⚠️ DRAFT — This issue is under discussion and not yet ready for implementation.
Problem Statement
ChunkGraphBuilder stores raw chunk text as a graph property:
'chunk.value = params.text'
At retrieval time, chunk text is accessed at these specific locations:
keyword_vss_provider.py:81 — RETURN c.value AS content (explicit content fetch for keyword search)
statement_enhancement.py:103 — node.node.metadata['chunk']['value'] (LLM synthesis context)
traversal_based_base_retriever.py:136 — only when include_chunk_details=True (opt-in, defaults to False)
update_chunk_metadata.py:18 — chunk.metadata.pop('value', None) (moves value from metadata dict)
Note: The main traversal-based retriever does NOT fetch chunk text by default (include_chunk_details=False). The memory cost is incurred regardless — Neptune keeps all node properties in-memory even when not queried.
For 5M chunks averaging 500 tokens (~2KB text), this consumes ~10GB of Neptune Analytics graph memory for text that is only needed at final answer synthesis.
Key insight: The traversal-based retriever already sets value: NULL in its projection — the architecture already treats chunk text as a late-binding fetch. This confirms external storage is architecturally aligned.
Proposed Solution
ChunkStore protocol (backend-agnostic interface):
class ChunkStore(ABC):
@abstractmethod
def get(self, chunk_id: str) -> str: ...
@abstractmethod
def put(self, chunk_id: str, text: str) -> None: ...
@abstractmethod
def get_batch(self, chunk_ids: List[str]) -> Dict[str, str]: ...
-
Reference implementations:
S3ChunkStore — S3 objects keyed by chunk_id, batch get via ThreadPoolExecutor
InGraphChunkStore — current behavior (reads chunk.value), for backward compat
- Interface supports any object store (DynamoDB, Redis, local FS, third-party)
-
ChunkStoreFactory — registration pattern similar to GraphStoreFactory, allowing custom backends.
-
Write path change (ChunkGraphBuilder): when external store configured, skip chunk.value = params.text, call chunk_store.put(chunk_id, text).
-
Read path changes:
keyword_vss_provider.py:81: replace RETURN c.value AS content → ChunkStore.get_batch(chunk_ids)
update_chunk_metadata.py:18-21: resolve from ChunkStore
statement_enhancement.py:103: resolve from ChunkStore
get_chunks_query in chunk_utils.py:51: strip value from node_result, fetch from ChunkStore
Performance Impact
| Metric |
Current (in-graph) |
Proposed (S3 store) |
| Graph memory (5M chunks) |
+10GB |
+250MB (refs only) |
| Chunk retrieval latency |
<1ms (in-memory graph) |
5-15ms (S3 GET) |
| Monthly cost (NA memory) |
~$500/month for 10GB |
~$5/month S3 storage |
| Retrieval batch (20 chunks) |
<1ms |
15-30ms (parallel S3 GET) |
| Graph traversal speed |
Slower (large node properties evict cache) |
Faster (smaller nodes, better cache utilization) |
The 15-30ms retrieval overhead is negligible vs LLM synthesis latency (1-5s). Net effect: faster graph traversal + lower cost.
Why smaller nodes improve traversal: Neptune Analytics loads full node properties into memory pages. Large text values (~2KB per node) reduce the effective working set for graph traversal queries that never access the text. Removing text increases the ratio of structural data in cache, improving traversal query performance.
Acceptance Criteria
Alternatives Considered
- Neptune Analytics memory scaling — provision more memory. Doesn't solve the cost problem; linear cost increase for data that's rarely accessed.
- Compress chunk text in-graph — reduces memory but adds CPU overhead on every read and doesn't enable pluggable backends.
Problem Statement
ChunkGraphBuilderstores raw chunk text as a graph property:'chunk.value = params.text'At retrieval time, chunk text is accessed at these specific locations:
keyword_vss_provider.py:81—RETURN c.value AS content(explicit content fetch for keyword search)statement_enhancement.py:103—node.node.metadata['chunk']['value'](LLM synthesis context)traversal_based_base_retriever.py:136— only wheninclude_chunk_details=True(opt-in, defaults to False)update_chunk_metadata.py:18—chunk.metadata.pop('value', None)(moves value from metadata dict)Note: The main traversal-based retriever does NOT fetch chunk text by default (
include_chunk_details=False). The memory cost is incurred regardless — Neptune keeps all node properties in-memory even when not queried.For 5M chunks averaging 500 tokens (~2KB text), this consumes ~10GB of Neptune Analytics graph memory for text that is only needed at final answer synthesis.
Key insight: The traversal-based retriever already sets
value: NULLin its projection — the architecture already treats chunk text as a late-binding fetch. This confirms external storage is architecturally aligned.Proposed Solution
ChunkStoreprotocol (backend-agnostic interface):Reference implementations:
S3ChunkStore— S3 objects keyed by chunk_id, batch get via ThreadPoolExecutorInGraphChunkStore— current behavior (readschunk.value), for backward compatChunkStoreFactory— registration pattern similar toGraphStoreFactory, allowing custom backends.Write path change (
ChunkGraphBuilder): when external store configured, skipchunk.value = params.text, callchunk_store.put(chunk_id, text).Read path changes:
keyword_vss_provider.py:81: replaceRETURN c.value AS content→ChunkStore.get_batch(chunk_ids)update_chunk_metadata.py:18-21: resolve from ChunkStorestatement_enhancement.py:103: resolve from ChunkStoreget_chunks_queryinchunk_utils.py:51: stripvaluefromnode_result, fetch from ChunkStorePerformance Impact
The 15-30ms retrieval overhead is negligible vs LLM synthesis latency (1-5s). Net effect: faster graph traversal + lower cost.
Why smaller nodes improve traversal: Neptune Analytics loads full node properties into memory pages. Large text values (~2KB per node) reduce the effective working set for graph traversal queries that never access the text. Removing text increases the ratio of structural data in cache, improving traversal query performance.
Acceptance Criteria
ChunkStoreis a pluggable interface — supports any object store (S3, DynamoDB, Redis, local FS, third-party)ChunkStoreFactorywith registration pattern (likeGraphStoreFactory)InGraphChunkStoreas default — existing behavior unchangedS3ChunkStoresupports batch retrieval with configurable parallelismAlternatives Considered