reconcile

package
v0.0.0-...-6ed2b60 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 19, 2026 License: Apache-2.0 Imports: 12 Imported by: 0

Documentation

Overview

Package reconcile owns the periodic Neutron reconcile: a 5-minute safety net that re-fetches the full snapshot and applies only the rows that changed, so metadata staleness stays bounded by one interval even through a Kafka outage.

It shares kernelwriter.ApplyTrieDelta with the Kafka path, so the insert-then-delete ordering holds for both.

docs/architecture/trie-construction.md#incremental-updates

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DesiredMACs

func DesiredMACs(snap *neutron.Snapshot) map[uint64]metadata.TenantMeta

DesiredMACs maps each admitted VM port to its full attribution — the target state of the metadata map. Amphora ports resolve to their load balancer's owning project. Ports failing admission are skipped silently: a persistently malformed port must not log every pass.

Exported because it is the SINGLE definition of what the map should hold, and it has two callers — the reconciler every pass, cold start once before any packet. Two implementations would be a billing bug: if cold start applied the Amphora rewrite and the reconciler did not, the first pass after boot would settle every Amphora port's flows under the LB owner and re-attribute to the service project, and the next cold start would swing it back.

Diagnostics stay OUT — they belong to cold start's audit pass, which produces no attribution and so cannot mis-bill if it drifts.

docs/architecture/octavia.md

Types

type AmphoraGauge

type AmphoraGauge interface {
	SetAmphoraPorts(n int)
}

AmphoraGauge publishes how many Amphora ports the latest pass re-attributed to their load balancer's owner. Cold start sets it once, so without this seam the gauge would freeze at the boot value — a load balancer created later would never move it, and the very failure it exists to catch would read healthy. nil skips publishing.

docs/architecture/octavia.md

type FlowSettler

type FlowSettler interface {
	Settle(mode state.SettleMode, resolve func(bpf.FlowKey) (tenant, extNet, server string, ok bool)) int
	// PruneServerSettled releases server-settled buckets whose server is
	// no longer in the Nova server list — the server tier's lifecycle
	// rule. The reconciler owns the call: it is the one place a fresh,
	// successful Nova fetch is in hand.
	//
	// Settled bytes: docs/architecture/data-structures.md#settled-bytes
	PruneServerSettled(alive map[string]struct{}) int
	// PruneTenantSettled releases tenant-settled buckets whose project is
	// no longer in the Keystone project list, folding each into the
	// total-settled absorber — the tenant tier's lifecycle rule. Same
	// ownership rationale as PruneServerSettled.
	//
	// Settled bytes: docs/architecture/data-structures.md#settled-bytes
	PruneTenantSettled(alive map[string]struct{}) int
}

FlowSettler folds flow rows into the settled-bytes accumulator. The MAC reconcile calls it just before re-pointing a live MAC, so bytes earned under the old attribution settle there instead of re-binding wholesale at the next scrape.

docs/architecture/data-structures.md#settled-bytes

type MacWriter

type MacWriter interface {
	// value is the packed (Amphora flag ++ tenant id) the kernel stores,
	// built by [bpf.TenantValue] — not a bare tenant id.
	Update(mac uint64, value uint32) error
}

MacWriter writes one (mac → tenant id) binding into the kernel mac_tenant_map. Removals are deliberately not its job: a gone MAC becomes a lingering ghost and the GC sweeps the kernel entry after the grace window.

docs/architecture/data-structures.md#lingering-ghost

type MapGauge

type MapGauge interface {
	SetCurrent(mapName string, value float64)
}

MapGauge refreshes a kernel map's current-entry gauge (lachesis_bpf_map_current_entries). Cold-start sets it once; the incremental path must keep it current or it drifts as the reconcile rewrites the trie and mac_tenant_map. Consumer-defined seam; *bpf.Metrics satisfies it.

type MetadataSource

type MetadataSource interface {
	Sync(ctx context.Context) (neutron.SyncResult, error)
	Trie() []neutron.TrieEntry
	Commit(result neutron.SyncResult, at time.Time)
}

MetadataSource is the subset of neutron.Neutron the reconcile loop drives: a full list-and-build MetadataSource.Sync, the retained trie of the last committed sync (MetadataSource.Trie, the "old" side of the diff), and MetadataSource.Commit to publish the new state once the kernel has acknowledged it. Defined here as the consumer's minimal seam so the loop can be unit-tested with a fake; *neutron.Neutron satisfies it.

type Metrics

type Metrics struct {
	// contains filtered or unexported fields
}

Metrics holds the Prometheus instruments for the reconcile subsystem. It is the dedicated error sink for the runtime incremental-update path: a kernel trie-write failure during a reconcile surfaces as lachesis_reconcile_runs_total{result="apply_error"} rather than a generic counter.

Metric catalogue: docs/architecture/metrics.md

The instruments are:

  • lachesis_reconcile_runs_total{result} counter

func NewMetrics

func NewMetrics() *Metrics

NewMetrics constructs the bundle with all three result outcomes seeded at zero so each series exists before the first reconcile fires.

func (*Metrics) Collectors

func (m *Metrics) Collectors() []prometheus.Collector

Collectors returns the underlying prometheus.Collector values for registration by the agent.

func (*Metrics) RecordRun

func (m *Metrics) RecordRun(result string)

RecordRun increments lachesis_reconcile_runs_total for one terminal outcome — call exactly once per reconcile pass.

type Options

type Options struct {
	Source MetadataSource
	Trie   kernelwriter.MapUpdateDeleter
	// AmphoraIPs writes the Octavia base-address set
	// (docs/architecture/octavia.md). Optional: nil skips the write, as
	// the trie-only unit tests do.
	AmphoraIPs kernelwriter.MapUpdater
	Meta       *metadata.ShardedMetadataMap
	MacWriter  MacWriter
	// Settler folds a MAC's flow rows to its old tenant before a
	// tenant reassignment replaces the binding. Optional (nil skips
	// the fold — trie-only unit tests); the agent wires its
	// *state.GlobalState.
	Settler  FlowSettler
	Interner *metadata.TenantInterner
	Seq      *boot.Sequencer
	Metrics  *Metrics
	// Routers is the router-interface-MAC → external-network map the
	// pass rebuilds and swaps (per-flow external attribution).
	// Optional (nil skips — trie-only unit tests); the agent wires the
	// store its Resolver reads.
	//
	// Billing tiers: docs/architecture/billing.md
	Routers *metadata.RouterMACs
	// BPFGauge refreshes the kernel map-fill gauges after each pass.
	// Optional (nil skips); the agent wires its bpf metrics bundle.
	BPFGauge MapGauge
	// AmphoraGauge republishes the Amphora re-attribution count each
	// pass. Optional; nil skips it.
	AmphoraGauge AmphoraGauge
	// Tunables supplies the live reconcile interval and ghost grace
	// (hot-reload; the interval applies at the next tick). REQUIRED —
	// operational knobs have exactly one source; unit tests construct
	// a store with the values they exercise.
	Tunables *tunables.Store
}

Options bundles the inputs to New. Source, Trie, Interner, and Metrics are required. Meta and MacWriter enable the mac_tenant_map reconcile; both nil (the trie-only unit tests) skips it. Seq is optional (nil skips the boot barrier, used by reconcileOnce unit tests); Interval defaults to [defaultInterval] when zero.

type Reconciler

type Reconciler struct {
	// contains filtered or unexported fields
}

Reconciler periodically reconciles the kernel subnet_zone_trie against a fresh Neutron snapshot. Construct with New, then run Reconciler.Run on a long-lived goroutine.

func New

func New(opts Options) *Reconciler

New constructs a Reconciler from opts.

func (*Reconciler) Kick

func (r *Reconciler) Kick()

Kick requests an immediate reconcile pass out of band. The Kafka consumer calls it when a Neutron notification shows metadata changed, so the trie and mac_tenant_map refresh within one pass instead of waiting up to a full interval. It is non-blocking and coalescing — the buffered channel holds at most one pending kick, so a burst of events costs a single extra pass — and safe to call from any goroutine.

func (*Reconciler) Run

func (r *Reconciler) Run(ctx context.Context)

Run reconciles on each tick and on each Reconciler.Kick until ctx is cancelled — one applier goroutine for both, so passes never overlap. Blocks on boot.PhaseStateRestored first: a delta computed before cold-start committed the initial trie would be wrong. No immediate first pass; cold start already populated the trie.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL