github.com/virtual-db/core
The Go framework behind VirtualDB. core is a transparent intercepting proxy engine — it routes every database operation through an ordered pipeline, maintains an in-memory delta store that overlays writes on top of source data, and manages an event bus and plugin system. It does not speak any wire protocol itself.
A driver implements the Server interface and calls back into DriverAPI as database events occur. A product wires a driver to core, attaches handlers, and calls Run.
- Go 1.23+
- Module:
github.com/virtual-db/core
app:=core.New(core.Config{PluginDir: "plugins"})
api:=app.DriverAPI()
srv:=mydriver.New(cfg, api)
app.
UseDriver(srv).
Attach("vdb.query.received.intercept", 5, func(ctxany, pany) (any, any, error) {
// inspect or rewrite queries before they hit the source DBreturnctx, p, nil
}).
Subscribe("vdb.record.inserted", func(ctxany, pany) error {
// react to inserts after they land in the deltareturnnil
})
log.Fatal(app.Run())Pipeline — Every database operation runs through a named vdb.* pipeline. Each pipeline has an ordered sequence of named points. Handlers attach to a point at a numeric priority; lower numbers run first. Built-in framework handlers reserve priority 10.
Event bus — After key operations complete the framework emits a named vdb.* event. Delivery is fire-and-forget; a subscriber error is logged but does not stop other subscribers.
Delta store — Writes (INSERT, UPDATE, DELETE) are recorded in memory without touching the source database. On every read, the delta is overlaid on top of source records. Transactions get a private staging delta (TxDelta) that is merged into the live delta on COMMIT and discarded on ROLLBACK.
Plugin system — Plugins are out-of-process executables discovered from PluginDir at startup. Each communicates over a Unix socket using JSON-RPC 2.0 and declares which pipeline points it handles and which events it subscribes to.
| Method | Description |
|---|---|
New(cfg Config) *App | Construct and initialise the framework. |
DriverAPI() DriverAPI | Get the framework's DriverAPI bridge. Call before UseDriver. |
UseDriver(s Server) *App | Register the database server. Must be called before Run. |
Attach(point string, priority int, fn PointFunc) *App | Register a handler at a pipeline point. Panics on unknown point or after Run. |
Subscribe(event string, fn EventFunc) *App | Register an event subscriber. Panics after Run. |
DeclareEvent(event string) | Declare a custom event (for extensions and plugins). |
DeclarePipeline(name string, points []string) | Declare a custom pipeline (for extensions and plugins). |
Emit(event string, payload any) | Dispatch a payload to all subscribers of the named event. |
Process(pipeline string, payload any) (any, error) | Run a pipeline manually. |
Run() error | Execute the startup sequence and block. May only be called once. |
Stop() | Graceful shutdown. Idempotent. Unblocks Run. |
// Implemented by the driver.typeServerinterface {
Run() errorStop() error
}
// Implemented by the framework. Called by the driver.typeDriverAPIinterface {
ConnectionOpened(iduint32, user, addrstring) errorConnectionClosed(iduint32, user, addrstring)
TransactionBegun(connIDuint32, readOnlybool) errorTransactionCommitted(connIDuint32) errorTransactionRolledBack(connIDuint32, savepointstring)
QueryReceived(connIDuint32, query, databasestring) (string, error)
QueryCompleted(connIDuint32, querystring, rowsAffectedint64, errerror)
RecordsSource(connIDuint32, tablestring, records []map[string]any) ([]map[string]any, error)
RecordsMerged(connIDuint32, tablestring, records []map[string]any) ([]map[string]any, error)
RecordInserted(connIDuint32, tablestring, recordmap[string]any) (map[string]any, error)
RecordUpdated(connIDuint32, tablestring, old, newmap[string]any) (map[string]any, error)
RecordDeleted(connIDuint32, tablestring, recordmap[string]any) errorSchemaLoaded(tablestring, columns []string, pkColstring)
SchemaInvalidated(tablestring)
}| Pipeline | Points |
|---|---|
vdb.context.create | build_context → contribute → seal → emit |
vdb.server.start | build_context → configure → launch → emit |
vdb.server.stop | build_context → drain → halt → emit |
vdb.connection.opened | build_context → accept → track → emit |
vdb.connection.closed | build_context → cleanup → release → emit |
vdb.transaction.begin | build_context → authorize → emit |
vdb.transaction.commit | build_context → apply → emit |
vdb.transaction.rollback | build_context → apply → emit |
vdb.query.received | build_context → intercept → emit |
vdb.records.source | build_context → transform → emit |
vdb.records.merged | build_context → transform → emit |
vdb.write.insert | build_context → apply → emit |
vdb.write.update | build_context → apply → emit |
vdb.write.delete | build_context → apply → emit |
Use <pipeline>.<point> as the point name when calling Attach (e.g. vdb.query.received.intercept).
| Event | Emitted after |
|---|---|
vdb.server.stopped | Graceful shutdown completes |
vdb.connection.opened | A client connection is accepted |
vdb.connection.closed | A client connection is released |
vdb.transaction.started | A transaction begins |
vdb.transaction.committed | A transaction commits |
vdb.transaction.rolledback | A transaction rolls back |
vdb.query.completed | A query finishes execution |
vdb.record.inserted | A record lands in the delta as an insert |
vdb.record.updated | A record overlay lands in the delta |
vdb.record.deleted | A tombstone lands in the delta |
vdb.schema.loaded | Table schema is loaded into the cache |
vdb.schema.invalidated | Cached table schema is invalidated |
See CONTRIBUTING.md.
Elastic License 2.0 (ELv2). See LICENSE.md.