2.0 KiB
2.0 KiB
Baseline Comparison Study — Design
Architecture
Pure backend/library feature. No UI. Three components added:
experimentmodule (tidal/src/experiment/) — types, assignment function, report builderdb/experiment.rs—TidalDbimpl block wiring experiment methods to existing infrastructurechronologicalbuiltin profile — registered inranking/builtins.rs
Module Layout
tidal/src/
├── experiment/
│ ├── mod.rs # ExperimentConfig, ExperimentGroup, ExperimentReport,
│ │ # GroupMetrics, MetricLift, assign_group()
│ └── report.rs # ReportBuilder: scans UserSignalIndex per group
├── db/
│ └── experiment.rs # TidalDb::experiment_group/profile/report
└── ranking/
└── builtins.rs # + chronological() profile
Assignment
FNV-1a hash over experiment_id.as_bytes() concatenated with user_id.to_le_bytes(). Bucket = hash % 10,000. Treatment if bucket < threshold. Pure function, no I/O, deterministic across platforms.
Report
ReportBuilder takes &Arc<SignalLedger> and &UserSignalIndex. Partitions users via assign_group, then for each group iterates users calling user_signal_count() (new method on UserSignalIndex that sums AllTime counts across all entities for a user+signal_type pair) and user_activity_split() for return rate.
Key Design Decisions
- No new storage — all metrics derived from existing
UserSignalIndexDashMap scans - New UserSignalIndex methods —
user_signal_count(user_id, type_id)anduser_activity_split(user_id, boundary_ns)added to support per-user aggregation across all entities - chronological profile — uses existing
Sort::New, no new sort variant needed - Thread safety — all types are
Send + Sync,assign_groupis pure, report takes&self
Error Handling
InvalidInput: treatment_fraction out of range, empty signal listsInternal: no schema/ledger available- No new error variants needed