Skip to content

feat(engine): data-plane embedder enablers (engine registry + type-routing resolver)#387

Merged
aparajon merged 2 commits into
mainfrom
aparajon/engine-registry
Jun 17, 2026
Merged

feat(engine): data-plane embedder enablers (engine registry + type-routing resolver)#387
aparajon merged 2 commits into
mainfrom
aparajon/engine-registry

Conversation

@aparajon

@aparajon aparajon commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

Why this matters

Running the data plane in an embedding service across more than one engine — including a database type this build does not ship — needs two extension points the core lacked: a way to supply a custom engine, and a way to compose per-engine resolvers behind the single resolver a TargetRouter takes. This adds both, behind the existing engine.Engine and inventory.Resolver interfaces, so no engine-specific package is pulled into core.

What it does

Engine registry — supply an engine for a database type without a built-in implementation:

  • tern.EngineFactory + LocalConfig.EngineFactories: NewLocalClient builds the engine for a non-built-in type from a registered factory, failing closed when such a type has no factory, the factory errors, or it returns nil. Built-in mysql/vitess are unchanged.
  • api.Service.RegisterEngine(databaseType, factory): the embedder hook, called during setup; registered factories flow into the local clients the service builds.

Type-routing resolver — compose multiple engines behind one resolver:

  • An engine resolver serves a single engine (it rejects a request whose type it doesn't serve). inventory.TypeRoutingResolver routes a request to the resolver registered for its database type, failing closed when the request carries no type or none is registered.
TargetRouter ─▶ TypeRoutingResolver ─┬─ mysql  ─▶ resolver A
                                     └─ vitess ─▶ resolver B
              LocalClientFactory ─▶ NewLocalClient ─▶ built-in or registered engine

How it moves toward the northstar

Completes the embedder extension surface for the engine-agnostic data plane: an embedding service can run any mix of engines — including ones this build does not ship — behind engine.Engine and inventory.Resolver, with engine-specific code supplied by the embedder rather than the core.


PR drafted by Claude Code (Opus 4.8).

Copilot AI review requested due to automatic review settings June 16, 2026 21:44
@aparajon aparajon marked this pull request as ready for review June 16, 2026 21:48
@aparajon aparajon requested review from Kiran01bm and morgo as code owners June 16, 2026 21:48

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an engine registry extension seam so embedders can supply engine.Engine implementations for database types that SchemaBot’s core build doesn’t ship, while keeping built-in mysql (Spirit) and vitess (PlanetScale) behavior unchanged.

Changes:

  • Introduces tern.EngineFactory and wires LocalConfig.EngineFactories into tern.NewLocalClient to build a custom engine for non-built-in database types.
  • Adds api.Service.RegisterEngine(databaseType, factory) to let embedders register factories that flow into service-built local Tern clients.
  • Adds unit tests covering registered/unregistered factories and failure-closed behavior.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.

File Description
pkg/tern/local_client.go Adds EngineFactory + config plumbing and a customEngine path for non-built-in database types.
pkg/tern/local_client_test.go Adds tests for registry-based engine construction and failure-closed behaviors.
pkg/api/service.go Stores engine factories on the service and injects them into local client construction; adds RegisterEngine.
pkg/api/service_test.go Adds a test validating that RegisterEngine enables local client creation for a custom database type.
Comments suppressed due to low confidence (1)

pkg/tern/local_client_test.go:2385

  • Now that NewLocalClient fails closed when a factory returns a nil engine, it should also fail closed (with a test) when the registry contains the key but the EngineFactory function itself is nil. Without this, a nil factory would currently panic at call time.
	require.Error(t, err)
	assert.Contains(t, err.Error(), "nil engine")
}


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pkg/tern/local_client.go
Comment thread pkg/tern/local_client.go
Comment thread pkg/tern/local_client.go
Comment thread pkg/tern/local_client.go
Comment thread pkg/api/service.go
@aparajon aparajon changed the title feat(tern): add an engine registry for embedder-supplied engines feat: data-plane embedder enablers (engine registry + type-routing resolver) Jun 16, 2026
@aparajon aparajon force-pushed the aparajon/engine-registry branch from 1ee45b1 to 18a36a3 Compare June 16, 2026 21:59
aparajon and others added 2 commits June 16, 2026 17:59
The data plane builds one engine per database type, but the set was hardcoded
(mysql -> Spirit, vitess -> PlanetScale), so an embedding service had no way to
drive a database type this build does not include.

Add an extension point behind the existing engine.Engine interface:
- tern.EngineFactory + LocalConfig.EngineFactories: NewLocalClient builds the
  engine for a type without a built-in engine from a registered factory, and
  fails closed when a type has no engine, the registered factory is nil, the
  factory errors, or it returns a nil engine. Built-in mysql/vitess unchanged.
- api.Service.RegisterEngine(databaseType, factory) is the embedder hook; it
  validates its inputs and returns an error so misconfiguration fails fast.
- protoEngine reports the engine actually backing the client (via its Name) so
  a registered engine is not misreported as the Spirit default.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…anes

An engine resolver serves a single engine — it rejects a request whose database
type it does not serve — so a data plane that serves more than one engine had no
way to compose them behind the one Resolver a TargetRouter takes.

TypeRoutingResolver routes a request to the resolver registered for its database
type, failing closed when the request carries no type or no resolver is
registered for it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@aparajon aparajon force-pushed the aparajon/engine-registry branch from 18a36a3 to c83395e Compare June 16, 2026 22:00
@aparajon aparajon changed the title feat: data-plane embedder enablers (engine registry + type-routing resolver) feat(engine): data-plane embedder enablers (engine registry + type-routing resolver) Jun 16, 2026
@aparajon aparajon merged commit 57a7e38 into main Jun 17, 2026
28 checks passed
@aparajon aparajon deleted the aparajon/engine-registry branch June 17, 2026 12:04
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.

3 participants