Skip to content

Dedup Schema Cache via registry and shared schemaclientbound - #514

Open
steiler wants to merge 3 commits into
mainfrom
schema-bound-registry
Open

steiler wants to merge 3 commits into
mainfrom
schema-bound-registry

Conversation

@steiler

@steiler steiler commented Sep 30, 2026

Copy link
Copy Markdown
Collaborator

Summary

Each datastore used to create its own schema-bound client (SchemaClientBoundImpl) with a private path-keyed lookup cache. Many datastores on the same YANG model (name, vendor, version) duplicated the same cached entries in memory. This PR pools one bound client per schema triplet, shared by all datastores that use it, and frees it when no datastore still holds a handle.

We also remove the remote schema client’s TTL cache (RemoteSchemaCache / ttlcache) so there is a single caching layer—the pooled bound client—not two.

How it works

  • Registry (pkg/datastore/clients/schema/registry.go): map (name, vendor, version) → pooled instance + refcount. GetOrCreate returns a handle that implements SchemaClientBound and adds Close() (not on the interface; existing mocks/tests unchanged).
  • Datastore: New acquires a handle from the registry; Delete calls Close() ( Stop does not—same shutdown vs delete split as before). Failed New closes the handle to avoid refcount leaks.
  • Server: one registry after local/remote schema.Client setup; both datastore.New call sites use it.
  • Remote client: passthrough RPCs unchanged; local caching removed. Config field schema-server.cache is gone.

SchemaClientBoundImpl internals (path map, coalescing) are unchanged. Per-path invalidation on reload is out of scope (whole triplet evicted when refcount hits zero).

Test plan

  • Registry unit tests (same triplet shares instance, different triplets don’t, refcount/eviction, idempotent Close)
  • go test ./...
  • Manual: two datastores, same schema → shared pool; delete last one → triplet evicted

Docs

  • ADR: docs/adr/0005-shared-refcounted-schema-bound-registry.md
  • Spec: .scratch/open/11-schema-bound-registry-spec.md
  • Glossary: CONTEXT.md (Schema client registry / schema-bound handle)

Spec for pooling SchemaClientBoundImpl per (name, vendor, version)
schema identity instead of one private cache per datastore. See
docs/adr/0005-shared-refcounted-schema-bound-registry.md.
.scratch/open/11-schema-bound-registry-spec.md
Implements the schema-bound registry per .scratch/open/11-schema-bound-registry-spec.md
and ADR 0005.

- Add pkg/datastore/clients/schema/registry.go: a process-wide Registry
  that pools *SchemaClientBoundImpl instances keyed by (name, vendor,
  version), refcounted per datastore via GetOrCreate/Handle.Close(). The
  pooled instance's internals (path-keyed lookup cache, per-entry
  coalescing) are untouched; only construction/lifecycle move from
  one-per-datastore to one-per-schema-identity. Release on an unknown or
  already-zero-refcount triple, and repeated Handle.Close(), are no-ops.
- Wire the registry into Datastore.New/Delete (pkg/datastore/datastore_rpc.go):
  New acquires a handle via registry.GetOrCreate and stores it with its
  concrete *schemaClient.Handle type so Close is directly callable without
  a type assertion; Delete calls Close; Stop does not (shutdown and
  explicit deletion remain distinct teardown paths). The handle is also
  closed if tree construction fails partway through New.
- Construct the registry once in pkg/server/schema.go alongside
  schema.Client construction (local or remote) and thread it into both
  datastore.New call sites (pkg/server/datastore.go, pkg/server/server.go)
  in place of the raw schema.Client.
- Remove remoteClient's ttlcache and the RemoteSchemaCache config it was
  driven by (pkg/schema/remote.go, pkg/config/config.go,
  pkg/config/defaults.go): the registry is now the single schema-caching
  mechanism in the process. NewRemoteClient drops the cacheConfig param.
  go.mod: jellydator/ttlcache/v3 is now an indirect dependency.
- SchemaClientBound is unchanged (no Close() added); NewSchemaClientBound
  remains directly usable by existing test call sites that bypass the
  registry.

Tests: new registry_test.go covers the primary seam per the spec's Testing
Decisions (shared pooled instance for the same triple, distinct instances
for different triples, refcount keeps an instance alive until every handle
closes, idempotent Close, no-op Release on an unknown triple). Existing
pkg/server and pkg/datastore lifecycle tests continue to pass with minor
wiring updates (newTestServer now also builds a schemaRegistry).
@steiler
steiler requested a review from a team as a code owner September 30, 2026 09:10
@steiler steiler changed the title Shared, refcounted schema-bound registry per schema identity Dedup Schema Cache via registry and shared schemaclientbound Sep 30, 2026
@codecov

codecov Bot commented Sep 30, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 61.90476% with 24 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
pkg/schema/remote.go 0.00% 12 Missing ⚠️
pkg/datastore/datastore_rpc.go 0.00% 7 Missing ⚠️
pkg/datastore/clients/schema/registry.go 95.00% 2 Missing ⚠️
pkg/server/schema.go 0.00% 2 Missing ⚠️
pkg/server/server.go 0.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant