Goal: every smartbotic-database instance hosts multiple projects, each
with its own namespace of collections. Backward-compatible by construction
— a v2.2 client that doesn't address a project transparently uses
"default", which auto-migrates from the existing <dataDir>/env/ on
upgrade.
| Axis | Decision |
|---|---|
| Namespace name | project |
| Wire shape | Collection name encodes project: "my_app:users" parses to ("my_app","users"); bare "users" → ("default","users") |
| Separator | : (single, only one allowed) |
| Storage layout | One LMDB env per project at <dataDir>/projects/<name>/env/ |
| Upgrade path | First v2.3 boot moves <dataDir>/env/ → <dataDir>/projects/default/env/ atomically |
| Toggle / first-boot mode | None. Multi-project is always on. v2.2 clients without explicit project see identical behaviour via the default project. |
| Project codebase rename | Deferred. v2.x arc is supposed to freeze surface area. |
Parser semantics locked in by tests/test_project_addressing.cpp (23
assertions). Strict on edge cases — leading/trailing/double : all
throw std::invalid_argument.
default: always present, addressable. Idempotent on create._-prefix: reserved for system use (matches collection-name rules).^[a-zA-Z_][a-zA-Z0-9_-]{0,62}$.Each stage ends at a buildable, testable, committable state.
service/src/project_addressing.hpp — parseProjectCollection,
isValidProjectName, constants.tests/test_project_addressing.cpp — 23 assertions cover the wire-form
edge cases + the name-validation regex boundary.ProjectStore type wrapping (LmdbEnv, LmdbDocumentStore) per
project.DatabaseService holds std::map<std::string, std::unique_ptr<ProjectStore>>
instead of single env_/doc_store_.<X> creates
<dataDir>/projects/<X>/env/ and registers the store.<dataDir>/projects/*/env/, open each.Files touched: database_service.cpp/.hpp, possibly a new
storage/project_store.hpp. Test: new
tests/test_project_store.cpp exercising open/lazy-create/list.
<dataDir>/env/ exists AND <dataDir>/projects/default/env/
does not, atomically rename the directory.Files touched: database_service.cpp (boot path). Test: extend
test_project_store.cpp with a v2.2-layout fixture.
collection parses via
parseProjectCollection and dispatches against the resolved project's
store. Same change ~12 sites in database_grpc_impl.cpp."project:collection"
as its in-memory key, so concurrent reads see consistent state. The
LMDB-side mirror calls the resolved project's doc_store_->put.Files touched: database_grpc_impl.cpp (every handler), memory_store.cpp
(qualified-key handling), database_service.cpp (mirror routing).
Client::Config::project field (default "default"). When set,
the Client prepends project + ":" to every bare collection name in
outgoing requests. Explicit "other:users" overrides the sticky
default.client.hpp.client.find("other:users", ...) always works without
setting Config::project.Files touched: client/include/smartbotic/database/client.hpp,
client/src/client.cpp. Tests in the existing client tests (extend or
add a new file).
ListProjects() — returns the set of open project names. Filesystem
scan-backed; trivial.CreateProject(name) — validates name, ensures
<dataDir>/projects/<name>/env/ exists. Idempotent. Useful so admins
can pre-create empty projects before the first write.DropProject(name) — refuses if name == "default"; closes the env
and rm -rfs the dir. Operator-only.Files touched: proto/database.proto (3 new RPCs), service
implementations, client wrappers.
test_project_addressing ✅test_project_store — boot, lazy-create, list, drop, default-project
always present.test_v22_to_v23_migration — synthetic v2.2 dataDir, boot, verify the
env moves to projects/default/env/.smartbotic-db-cli projects) —
can wait for v2.3.x.projects.<name>.mapsize_mb
schema, or just take a global default? Global default for v2.3; per-project
tuning in v2.3.x if real pressure shows up.: in your collection name in
single-project mode"? Now moot since there's no single-project
mode — but if a v2.2 client accidentally sends "my_app:users", the
server will route to project "my_app", NOT to a collection named
literally "my_app:users". Could be surprising. Mitigation: every
v2.2 client should NOT be sending : in collection names today, so
the surprise window is "nobody actually does this." OK to ignore.Stages A-G total: ~1-2 days of focused work. The biggest two are Stage D (handler routing — many sites, all formulaic) and Stage F (proto + RPC wiring). Stage B + C are the meatiest design pieces.