Roadmap
Quark is v1.2.2 on the stable v1.x line (v1.1.0 was the hardening minor, v1.2.0 the scaling minor). This page
separates what already ships from what is planned, so the docs do not read like a promise
that every idea is already in main.
Implemented core (shipping today)
Type-safe builder + composable query AST
- Type-safe
Query[T]builders, immutable composition (clone-per-method). - Typed expression AST (
Col/Lit/Func/And/Or/Not/In/Exists). - Subqueries composable through
AsSubquery. - CTEs (
With,WithRecursive). - Window functions (
OVER (PARTITION BY … ORDER BY …),RowNumber/Rank/Lag). - Set operators:
UNION,INTERSECT,EXCEPT. - Pessimistic locking:
ForUpdate,ForShare,SkipLocked,NoWait. - Optimistic locking:
quark:"version"tag +ErrStaleEntity. HavingAggregatefor HAVING over aggregates.- Nested
Preloadwith dotted paths ("Orders.Items.Product"). IN(...)chunking respecting dialect limits (Oracle 1000, MSSQL 2100).- Structured
JoinBuilderwithValidateJoinOn.
Six dialects + SQLGuard
- SQLite, PostgreSQL, MySQL, MariaDB, SQL Server, Oracle dialects.
- SQLGuard identifier, operator, raw-query, JSON path, and JOIN-ON validation.
- Dialect-specific upserts (
ON CONFLICT/ON DUPLICATE KEY/MERGE). - Custom dialects via
RegisterDialect.
Rich types
Nullable[T]generic.JSON[T]typed JSON column wrapper.Array[T]typed array wrapper (PG native, JSON-backed elsewhere).RegisterTypeMapperfor extensible mapping (decimal, UUID, etc. as opt-in).time.Durationmapped out of the box.- Per-column timezones via
quark:"tz=..."tag or Client-wideWithDefaultTZ; UTC-always wire contract.
CRUD + dirty tracking + soft delete
Create/Update/UpdateFields/Delete/HardDelete/UpsertBatch/CreateBatch/UpdateBatch/DeleteBatch.- Dirty tracking via
Tracked[T]: snapshot at load,Save()emits UPDATE only over changed fields (closes theisZeroValuetrap ofUpdate(entity)forfalse/0/""). - Soft delete scopes:
WithTrashed,OnlyTrashed,Restore.
Lifecycle hooks (transactional)
- Before/After hooks for Create / Update / Delete.
BeforeFind/AfterFind.After*fire post-commit underClient.Tx— undone work no longer fires its side-effects.Tx.OnCommit/Tx.OnRollback+quark.TxFromContextfor arbitrary commit/rollback side-effects.- Rolling back to a savepoint unwinds the hooks queued in that scope.
Migrations (schema-as-code)
Migrate,Sync,CreateIndex,AddForeignKey.- Neutral schema introspection (
Client.IntrospectSchema) across all six dialects. - Pure-Go schema diff (
Diff,PlanMigration,ApplyPlan) with round-trip identity:Migrate(model) → PlanMigration(model)returns emptyPlanon all six motors. - Transactional or resumable execution: PostgreSQL / SQL Server / SQLite run migrations transactionally; MySQL / MariaDB / Oracle run them resumably with checkpoint bookkeeping.
- Distributed migration lock: PostgreSQL
pg_advisory_lock, MySQL/MariaDBGET_LOCK, MSSQLsp_getapplock, OracleDBMS_LOCK(needsGRANT EXECUTE ON DBMS_LOCK). SQLite returnsErrUnsupportedFeature. - Orchestrated
Backfillwith primary-key-based batching and resume tokens. - Per-Client model registry (
Client.RegisterModeland friends). - Versioned Go migrations via
github.com/jcsvwinston/quark/migrate(versioned migration registry still global — see Known boundaries). quarkmigrateplan/verify/apply package (library, embeddable in your ownmigrations/main.go).
Multi-tenancy (four strategies)
DatabasePerTenantwith LRU of Clients.SchemaPerTenant.RowLevelSecurityClient— client-side WHERE injection (all six dialects; the tenant predicate composes correctly throughOr()groups).RowLevelSecurityNative— PostgreSQL engine-enforced RLS viaset_config('app.tenant_id', …, true)+CREATE POLICY. PostgreSQL only; other dialects fail-fast withErrUnsupportedFeature. The embeddablequarktenantlibrary generates the policy DDL —quarktenant.InstallRLSPolicies, orquarktenant.Runwired into your own CLI (e.g.go run ./cmd/tenant install-rls-policies). See Native RLS.- Tenant context propagation through association loads/saves.
L2 cache (memory + Redis)
- Pluggable
CacheStore(memory, Redis). - Stampede protection: the cache backing is wrapped so a hot key
never produces a database stampede on miss (singleflight +
probabilistic early refresh). Tunable via
WithCacheJitterandWithCacheXFetchBeta. - Per-row invalidation: a
<table>:<pk>tag on top of the table tag; mutations register the affected primary keys. - Deterministic, type-tagged, length-prefixed cache keys.
Observability
- OpenTelemetry traces (spans with
db.statement/db.operation). - OpenTelemetry metrics: counter
quark.queries.total, histogramsquark.queries.durationandquark.queries.rows. WithSpanRedactionkeeps bind values out of spans by default;IncludeArgsis opt-in for local debug.WithSlowQueryThresholdemits structured slow-query WARNs throughClient.logger.- Query observers and middleware.
Transactional resilience
Client.Tx(...)callback API; nested savepoint-style callbacks; explicit savepoints; isolation levels.WithDeadlockRetry(maxAttempts)onClient.Tx— re-runs the closure on PG 40P01 / MySQL 1213 / MSSQL 1205 / Oracle ORA-00060 with exponential backoff + jitter (opt-in, ctx-aware).
Audit log + event bus
Client.EnableAuditLog(ctx, AuditConfig)— records everyCreate/Update/Deleteinto thequark_audittable on the same connection/transaction as the write, atomically.Client.UseEventBus(bus)— anEventBuspublishingcreated/updated/deletedevents synchronously post-commit (at-least-once, no transactional outbox).
Code generation (opt-in)
cmd/quarkbinary with subcommands:gen,init,inspect,migrate,model,seed,sync,tenant,validate.quark genis generally available since v0.11.- Typed scanners on the read path (
List/First/Find). - Typed INSERT binder for single-integer-PK models (
Create). - Typed compile-time column accessors (
<Model>Columns+Query.WhereP) — pure compile-time sugar; column typos and wrong-typed values fail at build time. - Versioned generator contract (
//quark:gen vN) plus a model-hash drift check; files generated against an incompatible version fall back to reflection by design.
Stored routines
- Stored routine helpers (
routine_builder.go).
Horizontal scaling
- Read replicas (v0.13.0):
WithReplicas(replica1, …)routes reads to replicas; writes always go to the primary;Sticky(ctx)pins reads to the primary for read-your-writes. See Read replicas. - Primary failover (v0.13.0): a read to a replica that hits a transient connection error fails over to the primary, and the replica is taken out of rotation for a cooldown.
- Sharding (
ShardRouter): routes each query by shard key, with a runnable example inexamples/sharding/. Cross-shard scatter-gather (ScatterGather/ScatterCount) and deriving the shard key from the entity (WithShardKeyOf/ShardKeyer) are delivered (v1.2). - Stress/load harness (v0.13.0) in
benchmarks/stress/. - ent and sqlc in the benchmark harness (v1.0.0): the code-generation tier of the comparison harness, informational rather than a release gate. See Benchmarks.
- UPDATE / partial / batch binder codegen — deferred to v1.2+; the measured payoff was ~1%, so it is reopened only if motivated by type-safety, not speed.
v1.1.0 — delivered (hardening)
A systematic cross-engine pass over the whole public surface, plus the correctness fixes it surfaced:
CreateBatchchunking — large batches now chunk to each dialect's bind-parameter ceiling (SQL Server ~2100; others higher) instead of failing.- Versioned migrations on SQL Server — the bookkeeping-table DDL is now
per-dialect (the previous
CREATE TABLE IF NOT EXISTS … TIMESTAMPwas invalid on SQL Server). - MariaDB schema-diff false positive — a nullable, no-default column no
longer produces a phantom alter op in
PlanMigration. - Dialect-aware savepoints,
SchemaPerTenantwrite routing, and three eager-loading fixes. --rejected in raw queries underAllowRawQueries.- Automatic MariaDB detection and an inbound PostgreSQL
LISTEN/NOTIFYlistener.
No breaking changes. The release-candidate soak ran clean on the four testcontainers CI engines (PostgreSQL, MySQL, MariaDB, SQL Server); SQLite and Oracle hit only harness-level environment limits — not ORM bugs — which were hardened before the tag. Functional correctness still runs across all six dialects on every PR (see boundaries below).
Known current boundaries
- Oracle runs in the blocking
integrationCI matrix (all six dialects validated on every PR). The Oracle job bootsgvenzl/oracle-freeviadocker runinstead of testcontainers, whose lifecycle exited code 1 on hosted runners; the suite gets a DSN viaQUARK_TEST_ORACLE_DSN. - Code generation is a type-safety feature, not a speedup. Scan/bind
codegen measured ~2–5% (scan) / ~1% (INSERT) in the harness; per-op CPU
is dominated by
database/sqland the engine, not reflection. The codegen layer remains valuable for compile-time type-safety and forward compatibility, and v1.0 does not gate on a speedup target. - The versioned migration registry is still global. The model registry has been per-Client since v0.6; the versioned registry stays as documented debt.
LISTEN/NOTIFYis PostgreSQL-only. Both sides ship:Notify(outbound) and the inboundListenerFactory.CreateListenerlistener. Other dialects returnErrDialectNotSupported. Delivery is fire-and-forget — notifications emitted while the listener connection is down are lost (no durable replay); it is not a substitute for a queue.SelectandWherevalidate simple identifiers; dotted columns and SQL expressions require views or controlled raw SQL via theinternal/guardlayer.DeleteByandDeleteBatchare hard-delete APIs in the current implementation.- Bulk and WHERE-based methods (
CreateBatch,UpdateBatch,DeleteBatch,DeleteBy) do not fireAfter*hooks.Before*hooks run per entity inCreateBatch/UpdateBatchsince v1.1.4.
Long-term goals (post-v1.0)
- Schema-first workflow with reviewable migration generation from a declarative schema (Atlas/Prisma-style). The pure-Go schema diff shipped in v0.6 is the foundation; the schema-first DSL is the next layer if demand justifies it.
- Pluggable ID strategies (UUID v7, ULID, Snowflake) as built-ins.
- Outbound CRUD lifecycle events to NATS/Kafka/Redis Streams as
out-of-the-box
EventBusimplementations beyond the logger/OTel defaults. - More database-native features behind dialect-specific extension points as the user base demands them.