Bump actions/setup-go from 5 to 7 - #3
Closed
dependabot[bot] wants to merge 1 commit into
Closed
Conversation
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5 to 7. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](actions/setup-go@v5...v7) --- updated-dependencies: - dependency-name: actions/setup-go dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
Author
|
Looks like actions/setup-go is up-to-date now, so this is no longer needed. |
dependabot
Bot
deleted the
dependabot/github_actions/actions/setup-go-7
branch
August 26, 2026 16:05
luynrs
added a commit
that referenced
this pull request
Aug 30, 2026
> **Параллельно выполнять работу — можно. Параллельно изменять состояние
системы — нельзя.**
Это даст одновременно производительность, современную архитектуру и
сильно меньшую поверхность для гонок/рассинхронизации.
```text
CLI / TUI
│
│ RPC
▼
┌───────────────────────────────┐
│ Daemon │
│ │
│ ┌─────────────┐ │
│ │ Core │ │
│ │ single owner│ │
│ │ of State │ │
│ └──────┬──────┘ │
│ │ │
│ ┌────────┼─────────┐ │
│ ▼ ▼ ▼ │
│ Engine Store Workers │
│ │ │
│ HTTP / Probe │
│ DNS / Refresh │
│ │
└───────────────────────────────┘
```
Сам `Core` не обязан быть одним большим goroutine-actor с сотней
сообщений. Я бы сделал обычный объект с одним `opMu` для mutations и
`RWMutex` либо atomic snapshot для чтения. Это проще actor model и легче
отлаживается.
Ключевое — **никакой другой компонент не меняет business state
самостоятельно**.
Например:
```go
type Core struct {
opMu sync.Mutex
state atomic.Pointer[Snapshot]
engine Engine
store Store
}
```
Для чтений:
```go
func (c *Core) Snapshot() Snapshot {
return *c.state.Load()
}
```
Для mutations:
```go
func (c *Core) Connect(ctx context.Context, ref NodeRef) (Snapshot,
error) {
c.opMu.Lock()
defer c.opMu.Unlock()
// transition
}
```
Это очень быстрый и при этом скучный concurrency design.
---
Я бы перестал распределять authoritative состояние по:
```text
connection.Service
subscription.Service
store
watch
TUI
```
и определил один объект:
```go
type Snapshot struct {
Revision uint64
Settings Settings
Subscriptions []Subscription
Nodes []Node
Connection ConnectionState
}
```
Daemon всегда знает:
> текущее подтверждённое состояние системы — вот этот Snapshot.
После успешного изменения:
```text
old Snapshot
↓
compute desired
↓
perform external work
↓
persist
↓
publish new Snapshot
```
Снаружи Snapshot меняется **целиком**.
Это сильно упрощает reasoning.
---
Я бы не давал Core напрямую оркестрировать:
```text
Start
Swap
TunAdd
TunRemove
Restart
...
```
Его API должен быть примерно таким:
```go
type Engine interface {
Apply(context.Context, SessionSpec) error
Stop(context.Context) error
}
```
А внутри engine уже решает:
```text
можно hot swap?
→ swap
нужно только переключить TUN?
→ tun transition
нужен полный restart?
→ restart
```
Таким образом оптимизации остаются.
Но Core видит одну операцию:
```go
engine.Apply(ctx, desired)
```
Это важный уровень абстракции.
Тебе не приходится через два года помнить:
> «после Swap обязательно сделать X, если TUN поменялся, но перед Y,
кроме случая elevation».
Такое знание живёт внутри одного компонента.
---
Например:
```go
type SessionSpec struct {
Node NodeRef
Tun bool
ListenPort int
DNS DNSSettings
Routing RoutingSettings
}
```
Тогда engine state является функцией:
```text
SessionSpec → running engine
```
А не результатом истории:
```text
Start(A)
TunAdd()
SetPort()
Swap(B)
TunRemove()
...
```
Это огромное упрощение.
История операций перестаёт иметь значение.
---
Я бы убрал API вида:
```go
SetTun()
SetActive()
SetSettings()
SetWhatever()
```
и оставил:
```go
type Store interface {
Load() (PersistentState, error)
Save(PersistentState) error
}
```
То есть один atomic persistence snapshot.
Например:
```go
type PersistentState struct {
Settings Settings
Subscriptions []Subscription
Desired *SessionSpec
}
```
Это очень сильно уменьшит partial commit bugs.
Вместо:
```text
SetTun success
SetActive fail
SetSettings success
```
есть:
```text
Save(nextState)
```
одна transactional boundary.
С YAML это особенно удобно: temp file → fsync → rename.
---
Важно различать:
Последовательная.
Параллельная.
Refresh:
```text
┌── fetch #1
├── fetch #2
Core ─ job ───┼── fetch #3
├── ...
└── fetch #8
│
▼
results
│
▼
Core
commit
```
Probe:
```text
32 concurrent probes
↓
ProbeReport
```
DNS:
```text
cache + singleflight
```
Парсинг subscriptions тоже можно делать worker'ами.
Получается важный принцип:
> **Workers возвращают данные. Workers никогда не меняют State.**
Это как раз очень современная и хорошо масштабируемая модель.
---
Не нужно городить generations для этого случая.
Просто:
```text
refresh sub A already running
↓
second request joins existing job
```
То есть `singleflight`.
Это одновременно:
* быстрее;
* проще;
* меньше HTTP;
* нет stale commits;
* нет generation bookkeeping.
Для `RefreshAll` тот же worker pool может переиспользоваться.
---
Probe вообще не обязательно смешивать с глобальным состоянием daemon.
```go
Probe(ctx, refs) (ProbeReport, error)
```
Он может работать на 32/64 goroutines.
Результат получает TUI.
Если пользователь запускает новый Probe:
```text
cancel previous
start new
```
Очень простая модель.
Не надо:
```text
probe generations
probe revisions
probe persisted state
probe events
```
---
Не polling.
Не полноценный event bus.
Не поток состояний.
А:
```go
type Changed struct {
Revision uint64
}
```
Daemon говорит только:
> состояние изменилось, теперь revision 42.
TUI:
```text
имею 40
получил 42
↓
Snapshot()
```
Всё.
Потерял event 41?
Вообще неважно.
Watch отвалился?
Reconnect → Snapshot.
Notification пришёл после ответа Connect?
Revision уже та же → игнорируем.
Channel переполнился?
Пускай теряет события — главное, чтобы когда-нибудь пришёл новый
revision.
Это практически идеальная semantics для такой программы.
---
Я бы стремился примерно к этому набору:
```text
Hello
Snapshot
Connect(NodeRef)
Disconnect
SetSettings(Settings)
AddSubscription(...)
RemoveSubscription(...)
Refresh(...)
Probe(...)
Watch
Shutdown
```
И каждая state-changing операция возвращает:
```go
type MutationResult struct {
Snapshot Snapshot
}
```
Не надо отдельного `Status()` после операции.
---
Очень важное правило.
Любая операция:
```text
request
↓
work
↓
commit
↓
response
```
либо:
```text
request
↓
error
```
Никакого:
```text
вернули клиенту
а daemon там ещё что-то доделывает
```
кроме явно background вещей вроде AutoRefresh.
Даже background jobs должны иметь owner и context.
---
```text
daemonCtx
│
├── rpcRequestCtx
│ └── engine / HTTP / DNS
│
├── autoRefreshCtx
│
└── watchCtx
```
`context.Background()` появляется только в `main`.
После `main` он исчезает из application code.
Очень простое правило для code review:
> увидел `context.Background()` ниже composition root — почти наверняка
неправильно.
---
```text
cancel daemonCtx
↓
stop accepting RPC
↓
workers receive cancellation
↓
engine.Stop(ctx)
↓
bounded wait
↓
exit
```
Например:
```text
graceful deadline: 5–10 sec
```
После него force close.
Не должно существовать ни одной daemon goroutine без владельца.
---
Примерно:
```text
internal/
core/
core.go
state.go
connection.go
subscriptions.go
engine/
engine.go
singbox/
store/
store.go
yaml.go
jobs/
refresh.go
probe.go
daemon/
rpc/
lifecycle/
client/
rpc/
cli/
tui/
domain/
node.go
subscription.go
settings.go
```
Причём:
Владеет semantics.
Ничего не знает про UI/RPC/store.
Ничего не знает про engine.
Делают I/O и возвращают результаты.
Просто transport.
Просто отображает Snapshot и запускает commands.
Вот это хороший уровень разделения.
---
Не стал бы добавлять:
* event sourcing;
* полноценную actor system;
* CQRS;
* несколько command queues;
* database;
* сложный transaction framework;
* distributed-style retry logic;
* десятки mutex'ов;
* универсальный generic job scheduler;
* огромный internal event bus.
Для JustRay это будет архитектурная инфляция.
Тебе не нужна система, способная обслуживать миллион requests в секунду.
Тебе нужна система, которая очень быстро управляет одним локальным
networking engine и при этом **остаётся очевидной через три года**.
---
В hot path:
```text
Snapshot read → atomic load
```
Практически бесплатно.
Параллельный I/O:
```text
bounded goroutines
```
Быстро.
Mutation:
```text
один opMu
```
Просто.
Persistence:
```text
один atomic snapshot save
```
Надёжно.
Engine:
```text
one desired SessionSpec
```
Предсказуемо.
UI synchronization:
```text
revision notification + Snapshot
```
Минимум гонок.
---
Для любого нового feature можно задать четыре вопроса:
```text
1. Это меняет authoritative State?
→ только через Core.
2. Это тяжёлый I/O?
→ worker/context, результат обратно Core.
3. Клиенту нужно узнать об изменении?
→ revision changed.
4. Это должно пережить restart?
→ поле PersistentState.
```
Если разработчик не может определить, куда относится новая вещь, значит
abstraction, скорее всего, неправильная.
Вот **такую** архитектуру я бы рекомендовал тебе как конечную: не
«простую игрушечную», а **boring high-performance architecture** —
минимум механизмов, сильные invariants, параллельность только там, где
она реально даёт скорость, и очень мало возможностей создать гонку
случайным новым кодом.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Bumps actions/setup-go from 5 to 7.
Release notes
Sourced from actions/setup-go's releases.
... (truncated)
Commits
b7ad1dachore(deps): bump@actions/cacheto 6.2.0 (#771)0778a10Migrate to ESM and upgrade dependencies (#763)924ae3achore: bump version to 6.5.0 in package.json and package-lock.json (#762)e91cc3bBump@actions/cacheto 5.1.0, log cache write denied (#758)4a2405echore: update@types/nodeand@typescript-eslintdependencies to latest versi...78961f6chore: update@actionsdependencies and refresh license cache (#744)4a36011docs: fix Microsoft build of Go link (#734)8f19afcfeat: add go-download-base-url input for custom Go distributions (#721)27fdb26Bump minimatch from 3.1.2 to 3.1.5 (#727)def8c39Rearrange README.md, add advanced-usage.md (#724)Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting
@dependabot rebase.Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
@dependabot rebasewill rebase this PR@dependabot recreatewill recreate this PR, overwriting any edits that have been made to it@dependabot show <dependency name> ignore conditionswill show all of the ignore conditions of the specified dependency@dependabot ignore this major versionwill close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)@dependabot ignore this minor versionwill close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)@dependabot ignore this dependencywill close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)