Skip to content

docs(rfc):memory capacity and sharding - #1387

Open
MaoMengww wants to merge 2 commits into
oceanbase:masterfrom
MaoMengww:rfc/memory-capacity-and-sharding
Open

docs(rfc):memory capacity and sharding#1387
MaoMengww wants to merge 2 commits into
oceanbase:masterfrom
MaoMengww:rfc/memory-capacity-and-sharding

Conversation

@MaoMengww

Copy link
Copy Markdown
Contributor

Which issue or RFC does this PR close?

Closes #1321

Rationale for this change

Currently, because the manifest persists a full snapshot on every write, storage and write costs grow super-linearly, and in extreme cases the system can run beyond its practical limits. RFC 0014 explicitly listed Memory splitting, inactive tombstone compaction, and a shared routing manifest as unresolved. To contain these extreme cases, this RFC caps unbounded growth while preserving the manifest semantics, offering a concise solution under the conditions it defines.

What changes are included in this PR?

  • Adds synchronized English and Chinese RFC documents.
  • Introduces entry-count-bounded shards (memory_manifest_max_entries, default 200) and a routing domain derived from the configured root ID through strict root.sNNNN suffixes; no schema migration, no entry migration, and no change to the flat-v1 persistence shape.
  • Defines write routing: unconditional pure adds can automatically split to the next-ordinal shard; writes carrying expected_revision, citation-driven revise/retire/reactivate, and organize retain their owner shard and return the stable memory_capacity_exceeded error when capacity is insufficient.
  • Extends flush to single-plan semantics covering the entire routing domain: one candidate extraction over the active union, global deduplication by canonical bytes, and per-shard commits submitted together with the Source cursor in the same outer transaction, protected by a routing-domain barrier.
  • Defines candidate identity as (memory_artifact_id, entry_id) to prevent cross-shard target misidentification.
  • Defines bounded reads through exact immutable per-shard Revision snapshots and cursors that cannot be replayed across routing domains, as well as tombstone compaction audited by drop changes with no cooldown period.
  • Fully documents capacity/compaction, flush transactions and concurrency, API semantics, compatibility, drawbacks, alternatives, prior art, unresolved questions, and acceptance criteria.

Are there any user-facing changes?

No released behavior changes.

How was this change tested?

make check

AI usage statement

Claude Code was used in this change to analyze the Memory service implementation and the related design discussions, and to assist in drafting and cross-checking the synchronized English and Chinese RFC documents. The proposal was submitted by the author for review and revision by maintainers within the RFC process.

@Teingi Teingi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for writing this up. Bounded shards and stable entry ownership are useful directions, but the RFC still has several correctness and liveness gaps that should be resolved before acceptance. Because this PR defines design rather than implementing the open bug, please also change Closes #1321 to Refs #1321, add both RFC pages to the English and Chinese navigation, and bring the Chinese text fully back into sync with the English document.

### Identity

- Memory's persistent identity remains `(family, artifact_id)`; the routing domain is only a discovery set derived from the configured root.
- Shard ID resolution first matches the exact root, then applies a strict `root.s([1-9][0-9]*)` full match; ordering uses the parsed integer, forbidding accidental prefix matches.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The generated names and this parser disagree: root.s0001 does not match root.s([1-9][0-9]*) under a full match (root.s1 does). The first automatic split would therefore create a shard that routing cannot rediscover. Please define one canonical suffix grammar and use it for both generation and parsing.


### Identity

- Memory's persistent identity remains `(family, artifact_id)`; the routing domain is only a discovery set derived from the configured root.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Deriving membership solely from Artifact ID spelling can absorb an existing valid Artifact. For example, memory.s0001 is already a valid Memory ID; after configuring memory as the root, it would suddenly join this routing domain even if it was created independently. Please persist explicit routing membership or ownership, including upgrade conflict handling, rather than treating the suffix as proof of ownership.

- when the root exists, lock the root head row right after the outer transaction begins, then read all legal shard heads by ordinal;
- when the root does not exist, protect bootstrap with a database-supported `SERIALIZABLE` or equivalent predicate-conflict mechanism; a non-existent row is not a lockable object;
- every writer that changes the routing domain — explicit shard writes, split, flush, organize, and new-shard creation — first acquires the same barrier;
- head reads, candidate generation, global deduplication seeding, per-shard plans, ordinal-ordered apply, and cursor save all use the same bound connection;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This keeps the database barrier and connection open across model candidate generation, whose latency and retry duration are unbounded. That can block every writer in the routing domain. Please read an immutable head vector, run extraction outside the transaction, then acquire a short barrier, re-enumerate and recheck every head, and commit atomically.


## Flush: single-shard semantics extended

Source flush first reads all current heads of the routing domain. The candidate pipeline receives the active entries of all shards at once; its input is not truncated by this RFC, and candidate rules do not change with shard count. When the model proposes a revise, it must specify both `memory_artifact_id` and `entry_id`; an add carries no target identity.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Sharding bounds each Artifact, but this call still receives an unbounded domain-wide union. Once it exceeds the model context, the same Source window will fail on every retry and the cursor cannot advance. Please make selection token-budgeted and bounded, with a progress-safe fallback; global exact deduplication can remain server-side through canonical hashes and bytes.


All write paths that construct a new Memory Revision apply the same final rule to the resulting manifest: first compact droppable inactive tombstones according to the threshold and capacity conditions, then check `len(final_manifest) <= memory_manifest_max_entries`. The capacity check happens before embedding/projection preparation; a failure writes no Artifact, entry, projection, or index.

`memory_tombstone_compaction_threshold` controls whether compaction is triggered by tombstone count, and `memory_compaction_max_drops` only bounds the internal drop batch of one Revision; it does not limit the number of remember or candidate inputs. Legitimate large batches are not implicitly truncated; only a final manifest over budget fails as a whole.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

max_drops can prevent compaction from making progress. With capacity 200, 200 existing entries, 10 droppable tombstones, max_drops=5, and six conditional adds, compaction leaves 195 entries, the adds produce 201, and the all-or-nothing rollback restores all ten tombstones. Retrying repeats forever even though enough entries are droppable. Under pressure, please allow enough drops to fit the request or define a separately committable compaction step.


### Manifest 与版本

每个 shard 继续使用 flat-v1:manifest 只保存 `entry_id`、`entry_version_id`、`entry_content_hash` 和 `state`,正文在不可变 `MemoryEntryVersion` 中。最终 manifest 的 entry 数不得超过 `memory_manifest_max_entries`(默认 200,`ge=1`);条目数是容量判断维度。

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The translations are not currently synchronized: the Chinese RFC jumps from the capacity limit directly to Candidate identity and omits the English rationale and measurements for the default of 200. Please carry the same normative rationale into both documents, and clean up the incomplete wording near the Summary (以及。) before acceptance.

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.

bug: Memory append storage and latency grow superlinearly with entry history

2 participants