3.4 KiB
Design: Community Profile Sync
Overview
Community Profile Sync adds opt-in routing of user engagement signals to named community personalization aggregates. When a user joins a community, their future signal_with_context writes are forwarded to the community's aggregate — a named partition of the existing CohortSignalLedger — in addition to the global ledger.
The design reuses existing infrastructure wherever possible:
- Aggregate storage:
CohortSignalLedger(keyed by"community::<name>") - Persistence layer:
users_enginefjall partition (newTag::CommunityMembership = 0x0E) - Forwarding hook:
try_community_forwarding()called fromsignal_with_context
Storage Layout
Membership Records
Key: encode_key(EntityId::new(user_id), Tag::CommunityMembership, community_name.as_bytes())
Value: member_since_ns as [u8; 8] (little-endian u64)
Tag assignment: Tag::CommunityMembership = 0x0E
Community Signal Aggregates
Aggregates are stored in the existing CohortSignalLedger with the key prefix "community::<name>":
CohortSignalLedger key: ("community::jazz", entity_id, signal_type_id)
New Components
Tag::CommunityMembership = 0x0E
Added to tidal/src/storage/keys.rs.
CommunityMembershipIndex
New module: tidal/src/entities/community.rs
pub struct CommunityMembershipIndex {
memberships: DashMap<u64, Vec<String>>,
}
impl CommunityMembershipIndex {
pub fn add(&self, user_id: u64, community: &str);
pub fn get(&self, user_id: u64) -> Vec<String>;
pub fn contains(&self, user_id: u64, community: &str) -> bool;
pub fn entry_count(&self) -> usize;
}
db/community.rs
New module with public methods on TidalDb: join_community, get_community_memberships, is_community_member.
try_community_forwarding
Private method in db/signals.rs, called from signal_with_context after try_cohort_attribution:
fn try_community_forwarding(&self, signal_type, entity_id, weight, timestamp, user_id) {
let memberships = self.community_membership.get(user_id);
if memberships.is_empty() { return; }
let Ok(type_id) = self.cohort_ledger.resolve_signal_type(signal_type) else { return; };
let ts_ns = timestamp.as_nanos();
for name in &memberships {
self.cohort_ledger.record(&format!("community::{name}"), entity_id, type_id, weight, ts_ns);
}
}
rebuild_community_memberships
Added to tidal/src/db/state_rebuild.rs, called during open:
- Scans
Tag::CommunityMembershipkeys from users_engine - Populates
CommunityMembershipIndex
Changes by File
| File | Change |
|---|---|
tidal/src/storage/keys.rs |
Add Tag::CommunityMembership = 0x0E; update from_byte, tests |
tidal/src/entities/community.rs |
New: CommunityMembershipIndex |
tidal/src/entities/mod.rs |
pub mod community; + re-export |
tidal/src/db/mod.rs |
Add community_membership field; wire in constructors |
tidal/src/db/community.rs |
New: join_community, get_community_memberships, is_community_member |
tidal/src/db/signals.rs |
Call try_community_forwarding in signal_with_context |
tidal/src/db/state_rebuild.rs |
rebuild_community_memberships called during open |
tidal/tests/m9_community_sync.rs |
New: 10 integration tests |
Implementation Status
COMPLETE — All components implemented. 1299 lib tests pass. 10 integration tests pass. fmt/clippy clean.