Skip to content
lake
Browse this documentation section

Meta-server design

Status. docs/architecture.md is authoritative for the tier layout. lake is a three-tier system — stateless lake-query fan-out in front of a bounded stateful lake-metasrv authority over an HA KV. The earlier “no server / library mode is enough” conclusion in the later sections of this file is superseded: a real metadata authority is first-class (v1), and metadata HA via lease-election is on the roadmap (v2). What remains authoritative here is section 1 — the GreptimeDB metasrv study — and the deep-dive on which metasrv pieces (election, procedures, meta-client caching) lake lifts and when. Read this as the metasrv reference behind architecture.md, not as a competing design.

Direction for lake’s metadata layer (“metasrv”), derived from a study of GreptimeDB’s metasrv. goal.md says why lake exists; docs/architecture.md says how the three tiers fit together; this file is the GreptimeDB-grounded reference for what the metadata layer lifts from metasrv, and when.

GreptimeDB paths below are relative to the greptimedb repo root (studied at src/meta-srv/, src/meta-client/, src/common/meta/).

1. What GreptimeDB metasrv does

GreptimeDB runs a dedicated metadata service between all nodes and the backing KV store. Its pieces:

2. What lake actually needs

Gate each capability against goal.md:

GreptimeDB capabilityWhy greptime needs itDoes lake?
KV abstraction w/ CASpluggable etcd/RDS/localYes — already have it (MetaStore)
Rich KV surface (txn, batch, range-delete)many mutable keys per table (regions, routes)No — one mutable key per table; CAS is the whole model (“NOT a transaction engine”)
Electionexactly-one procedure runner / region balancerNot yet — no singleton role exists until background GC does
Heartbeat + region leasesdatanodes host data; failover must move regionsNo — lake nodes host nothing; data lives in object storage. A dead writer loses a CAS race and nothing else
Mailbox / push cache invalidationmutable metadata cached on frontendsNo — lake caches only immutable manifests; the one mutable pointer is TTL-polled, O(1) per version change
Procedure frameworkmulti-step DDL touching many datanodesLater, small — drop-table + data-file GC is the first real multi-step op
meta-client (leader discovery, retries)all nodes talk to metasrvNo for readers, ever — the read path must not gain a service hop

The structural difference: GreptimeDB’s metasrv scales with cluster metadata churn because datanodes are stateful and regions move. Lake has no regions and no stateful nodes; the only coordination point is the per-table version pointer, and its commit protocol already works from any client. Readers must be served from immutable cached artifacts — a meta-server on the read path is disqualified by goal.md (“NOT a metadata service”), not merely deferred.

So the question is confined to the write/control plane: commits, DDL (create/drop table), and eventually garbage collection of unreferenced data files.

3. Proposed design

3.1 Keep MetaStore as the KvBackend analog — stay narrow

crates/lake-meta/src/store.rs (get / cas / list_prefix) is the lake analog of KvBackend, deliberately ~10% of greptime’s surface. Both hide backend types behind one trait and treat conditional-put as the concurrency primitive (greptime’s compare_and_put; DynamoDB conditional put for lake). Differences we keep on purpose:

Extensions, added only when a consumer lands:

3.2 v0 is library-mode: no meta-server process

GreptimeDB itself proves the layering: standalone mode runs the full catalog + DDL stack directly on a local KvBackend with no metasrv and no meta-client (src/cmd/src/standalone.rs). Lake starts — and stays as long as possible — in the equivalent posture, including in production: writers embed lake-meta/lake-manifest and CAS-commit straight to DynamoDB. DynamoDB is already a managed, replicated, serialized CAS arbiter; putting a single-writer gRPC service in front of it would reduce availability and add an op to run, while providing serialization we already have.

What library-mode handles fine:

3.3 What eventually forces a control-plane component

Not commit throughput and not reader count — those never route through metadata. The real triggers are multi-step operations that outlive a client process:

  1. Drop table with data-file GC. Deleting the pointer is one CAS; deleting orphaned manifests/Parquet must survive the client dying mid-way. This needs greptime’s idea — persisted, idempotent, resumable steps (src/common/procedure/src/procedure.rs) — at perhaps 1/20 the machinery: a proc/<id> key in the metastore holding {op, step, args}, advanced by CAS, executed by whoever holds the janitor lease. We adapt the pattern (idempotent execute, persist-between-steps, resume-on-takeover) and skip the framework (loaders, poison store, rollback DAGs) until more than one procedure type exists.
  2. Orphan sweep / compaction coordination. A background janitor that must run as exactly-one instance. This is where election appears — as the KV-lease scheme from §3.1, a few dozen lines over cas, not an etcd dependency and not a new process: the janitor is a role a writer node (or a cron job) claims, greptime-RDS-style.

Only if lake later gains multi-tenant writers needing central policy, auth brokering, or audit does a standalone lake-metasrv process earn its keep. If that day comes, the greptime pieces worth lifting are the shape of ask_leader client failover (src/meta-client/src/client/ask_leader.rs) and leader-only mutation gating (src/meta-srv/src/state.rs) — over our existing KV-lease election, serving writers only. The read path never learns it exists.

4. Phasing

5. Rejected alternatives