# Particle Hierarchy & Storage Organization ## The Question Particles form a recursive file-system-like hierarchy (streams contain folders, folders contain files, etc.). Should we align the logical hierarchy with physical storage paths? ``` Logical: stream_123 -> folder_456 -> file_789 Storage: gs://bucket/stream_123/folder_456/file_789/document.pdf ``` ## Two Approaches ### Adjacency List (Current) Each particle stores a reference to its parent: ```sql CREATE TABLE particles ( id TEXT PRIMARY KEY, parent_id TEXT REFERENCES particles(id), ... ); ``` **To get ancestors:** Recursive CTE query **To move a particle:** Update one `parent_id` **Storage path:** Independent, based on `object_id` ### Materialized Path Each particle stores its full path: ```sql CREATE TABLE particles ( id TEXT PRIMARY KEY, path TEXT UNIQUE, -- '/net_abc/stream_123/folder_456' ... ); ``` **To get ancestors:** Parse the path string **To get descendants:** `WHERE path LIKE '/net_abc/stream_123/%'` **To move a particle:** Update paths of particle AND all descendants ## Why Materialized Path is Tempting If `path` doubles as the storage URI: ``` Particle path: /net_abc/stream_123/folder_456/file_789 Storage URI: gs://bucket/net_abc/stream_123/folder_456/file_789/video.mp4 ``` - Single source of truth for "where things live" - No recursive queries for hierarchy - Elegant alignment between logical and physical structure ## Why It Breaks Down **Moves are expensive:** Moving `folder_456` under a different stream requires: 1. Update `folder_456.path` 2. Update paths of ALL descendants (could be thousands) 3. Move ALL storage objects to new GCS paths GCS "moves" are copy + delete operations: - Slow and costs money - Links/references break during move - Concurrent access during move is undefined - Failure mid-move leaves inconsistent state **Storage should be immutable:** Once a file is uploaded to `gs://bucket/obj_abc123`, that path should never change. This enables: - Stable URLs (even if signed) - CDN caching - No coordination during particle reorganization ## Decision: Keep Them Decoupled ``` Logical hierarchy: parent_id references (mutable, cheap to change) Physical storage: object_id (immutable, never moves) ``` | Operation | Adjacency List | Materialized Path | |-----------|---------------|-------------------| | Move particle | O(1) - update parent_id | O(n) - update all descendant paths + move storage | | Get ancestors | O(depth) recursive query | O(1) parse path | | Get descendants | O(n) recursive query | O(1) prefix match | | Storage move | Not needed | Required on every move | The read optimization of materialized paths doesn't justify the write complexity, especially when writes involve physical storage operations. ## Storage Organization Objects are stored with stable IDs, optionally prefixed by network for operational convenience: ``` gs://bucket/{network_id}/{object_id}/{original_filename} ``` This enables: - Bulk operations per network (audit, delete, lifecycle policies) - Preserved original filename for downloads - No coupling to particle hierarchy The particle stores `object_id` in its data. The depot service handles signed URL generation. The particle's logical position in the hierarchy is independent of where its assets physically live.