mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-20 01:06:45 +00:00
[management] Skip full network map on Sync when peer state is unchanged
Introduce a peer-sync cache keyed by WireGuard pubkey that records the NetworkMap.Serial and meta hash the server last delivered to each peer. When a Sync request arrives from a non-Android peer whose cached serial matches the current account serial and whose meta hash matches the last delivery, short-circuit SyncAndMarkPeer and reply with a NetbirdConfig-only SyncResponse mirroring the shape TimeBasedAuthSecretsManager already pushes for TURN/Relay token rotation. The client keeps its existing network map state and refreshes only control-plane credentials. The fast path avoids GetAccountWithBackpressure, the full per-peer map assembly, posture-check recomputation and the large encrypted payload on every reconnect of a peer whose account is quiescent. Slow path remains the source of truth for any real state change; every full-map send (initial sync or streamed NetworkMap update) rewrites the cache, and every Login deletes it so a fresh map is guaranteed after SSH key rotation, approval changes or re-registration. Backend-only: no proto changes and no client changes. Compatibility is provided by the existing client handling of nil NetworkMap in handleSync (every version from v0.20.0 on). Android is gated out at the server because its readInitialSettings path calls GrpcClient.GetNetworkMap which errors on nil map. The cache is wired through BaseServer.CacheStore() so it shares the same Redis/in-memory backend as OneTimeTokenStore and PKCEVerifierStore. Test coverage lands in four layers: - Pure decision function (peer_serial_cache_decision_test.go) - Cache wrapper with TTL + concurrency (peer_serial_cache_test.go) - Response shape unit tests (sync_fast_path_response_test.go) - In-process gRPC behavioural tests covering first sync, reconnect skip, android never-skip, meta change, login invalidation, and serial advance (management/server/sync_fast_path_test.go) - Frozen SyncRequest wire-format fixtures for v0.20.0 / v0.40.0 / v0.60.0 / current / android replayed against the in-process server (management/server/sync_legacy_wire_test.go + testdata fixtures)
This commit is contained in:
28
management/server/testdata/sync_request_wire/README.md
vendored
Normal file
28
management/server/testdata/sync_request_wire/README.md
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
# SyncRequest wire-format fixtures
|
||||
|
||||
These files are the frozen byte-for-byte contents of the `SyncRequest` proto a
|
||||
netbird client of each listed version would put on the wire. `server_sync_legacy_wire_test.go`
|
||||
decodes each file, wraps it in the current `EncryptedMessage` envelope and
|
||||
replays it through the in-process gRPC server to prove that the peer-sync fast
|
||||
path does not break historical clients.
|
||||
|
||||
File | Client era | Notes
|
||||
-----|-----------|------
|
||||
`v0_20_0.bin` | v0.20.x | `message SyncRequest {}` — no fields on the wire. Main Sync loop in v0.20 gracefully skips nil `NetworkMap`, so the fixture is expected to get a full map (empty Sync payload → cache miss → slow path).
|
||||
`v0_40_0.bin` | v0.40.x | First release with `Meta` at tag 1. v0.40 calls `GrpcClient.GetNetworkMap` on every OS; fixture must continue to produce a full map.
|
||||
`v0_60_0.bin` | v0.60.x | Same SyncRequest shape as v0.40 but tagged with a newer `NetbirdVersion`.
|
||||
`current.bin` | latest | Fully-populated `PeerSystemMeta`.
|
||||
`android_current.bin` | latest, Android | Same shape as `current.bin` with `GoOS=android`; the server must never take the fast path even after the cache is primed.
|
||||
|
||||
## Regenerating
|
||||
|
||||
The generator is forward-compatible: it uses the current proto package with only
|
||||
the fields each era exposes. Re-run after an intentional proto change:
|
||||
|
||||
```
|
||||
go run ./management/server/testdata/sync_request_wire/generate.go
|
||||
```
|
||||
|
||||
and review the byte diff. An unexpected size change or diff indicates the wire
|
||||
format has drifted — either adjust the generator (if the drift is intentional
|
||||
and backwards-compatible) or revert the proto change (if it broke old clients).
|
||||
102
management/server/testdata/sync_request_wire/generate.go
vendored
Normal file
102
management/server/testdata/sync_request_wire/generate.go
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
//go:build ignore
|
||||
|
||||
// generate.go produces the frozen SyncRequest wire-format fixtures used by
|
||||
// server_sync_legacy_wire_test.go. Run with:
|
||||
//
|
||||
// go run ./management/server/testdata/sync_request_wire/generate.go
|
||||
//
|
||||
// Each fixture is the proto-serialised SyncRequest a client of the indicated
|
||||
// netbird version would put on the wire. protobuf3 is forward-compatible: an
|
||||
// old client's fields live at stable tag numbers, so marshalling a current
|
||||
// SyncRequest that sets only those fields produces bytes byte-for-byte
|
||||
// compatible with what the old client produced. The fixtures are checked in
|
||||
// so a future proto change that silently breaks the old wire format is caught
|
||||
// in CI.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/golang/protobuf/proto" //nolint:staticcheck // wire-format stability
|
||||
|
||||
mgmtProto "github.com/netbirdio/netbird/shared/management/proto"
|
||||
)
|
||||
|
||||
func main() {
|
||||
outDir := filepath.Join("management", "server", "testdata", "sync_request_wire")
|
||||
if err := os.MkdirAll(outDir, 0o755); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "mkdir %s: %v\n", outDir, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fixtures := map[string]*mgmtProto.SyncRequest{
|
||||
// v0.20.0: message SyncRequest {} — no fields on the wire.
|
||||
"v0_20_0.bin": {},
|
||||
|
||||
// v0.40.0: Meta added at tag 1. Older meta fields only.
|
||||
"v0_40_0.bin": {
|
||||
Meta: &mgmtProto.PeerSystemMeta{
|
||||
Hostname: "v40-host",
|
||||
GoOS: "linux",
|
||||
OS: "linux",
|
||||
Platform: "x86_64",
|
||||
Kernel: "4.15.0",
|
||||
NetbirdVersion: "0.40.0",
|
||||
},
|
||||
},
|
||||
|
||||
// v0.60.0: same wire shape as v0.40.0 for SyncRequest.
|
||||
"v0_60_0.bin": {
|
||||
Meta: &mgmtProto.PeerSystemMeta{
|
||||
Hostname: "v60-host",
|
||||
GoOS: "linux",
|
||||
OS: "linux",
|
||||
Platform: "x86_64",
|
||||
Kernel: "5.15.0",
|
||||
NetbirdVersion: "0.60.0",
|
||||
},
|
||||
},
|
||||
|
||||
// current: fully-populated meta a modern client would send.
|
||||
"current.bin": {
|
||||
Meta: &mgmtProto.PeerSystemMeta{
|
||||
Hostname: "modern-host",
|
||||
GoOS: "linux",
|
||||
OS: "linux",
|
||||
Platform: "x86_64",
|
||||
Kernel: "6.5.0",
|
||||
NetbirdVersion: "0.70.0",
|
||||
UiVersion: "0.70.0",
|
||||
KernelVersion: "6.5.0-rc1",
|
||||
},
|
||||
},
|
||||
|
||||
// android: exercises the never-skip branch regardless of cache state.
|
||||
"android_current.bin": {
|
||||
Meta: &mgmtProto.PeerSystemMeta{
|
||||
Hostname: "android-host",
|
||||
GoOS: "android",
|
||||
OS: "android",
|
||||
Platform: "arm64",
|
||||
Kernel: "4.19",
|
||||
NetbirdVersion: "0.70.0",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, msg := range fixtures {
|
||||
payload, err := proto.Marshal(msg)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "marshal %s: %v\n", name, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
path := filepath.Join(outDir, name)
|
||||
if err := os.WriteFile(path, payload, 0o644); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "write %s: %v\n", path, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("wrote %s (%d bytes)\n", path, len(payload))
|
||||
}
|
||||
}
|
||||
0
management/server/testdata/sync_request_wire/v0_20_0.bin
vendored
Normal file
0
management/server/testdata/sync_request_wire/v0_20_0.bin
vendored
Normal file
3
management/server/testdata/sync_request_wire/v0_40_0.bin
vendored
Normal file
3
management/server/testdata/sync_request_wire/v0_40_0.bin
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
|
||||
0
|
||||
v40-hostlinux4.15.0*x86_642linux:0.40.0
|
||||
3
management/server/testdata/sync_request_wire/v0_60_0.bin
vendored
Normal file
3
management/server/testdata/sync_request_wire/v0_60_0.bin
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
|
||||
0
|
||||
v60-hostlinux5.15.0*x86_642linux:0.60.0
|
||||
Reference in New Issue
Block a user