Skip to main content
benchmarkedInspired by Tree rings (dendrochronology)

Dendrite Ring

Counterpart to Fixed-interval time-series retention (Prometheus, InfluxDB)

Key Properties

  • O(1) amortized ingest via Welford streaming statistics
  • Variance-proportional encoding: thin rings (48 bytes) for boring periods, wide rings (196-292 bytes) for incidents
  • CUSUM + EWMV change-point detection drives ring boundaries automatically
  • 8-class shape classification (Flat, RampUp, RampDown, Spike, SpikeRecover, Oscillating, StepUp, StepDown)
  • Sapwood/heartwood lifecycle: recent rings mutable, aged rings immutable with summarized statistics
  • O(log a) anomaly range queries via sorted anomaly index

Operation Complexity

Side-by-side comparison with the classical counterpart.

Operation complexity comparison
OperationDendrite RingFixed-interval time-series retention (Prometheus, InfluxDB)
ingestO(1) amortizedO(1)
ring_atO(log r)O(log n) (B-tree)
rings_in_rangeO(log r + k)O(log n + k)
anomalies_in_rangeO(log a + k)N/A
rings_by_shapeO(r)N/A
tick (heartwood transition)O(t)N/A
space (per ring)48-292 bytesFixed per interval

Interface Preview

dendrite_ring.rs
rust
1pub struct DendriteStore { /* ... */ }
2
3impl DendriteStore {
4    pub fn new(config: DendriteConfig) -> Self;
5    pub fn with_preset(preset: MetricPreset) -> Self;
6
7    // Ingest
8    pub fn ingest(&mut self, timestamp: u64, value: f64) -> Result<Option<&DendriteRing>, DendriteError>;
9    pub fn flush(&mut self) -> Option<&DendriteRing>;
10
11    // Queries
12    pub fn ring_at(&self, timestamp: u64) -> Option<&DendriteRing>;
13    pub fn rings_in_range(&self, start: u64, end: u64) -> &[DendriteRing];
14    pub fn anomalies(&self) -> &[AnomalyEntry];
15    pub fn anomalies_in_range(&self, start: u64, end: u64) -> &[AnomalyEntry];
16    pub fn rings_by_shape(&self, shape: ShapeClass) -> Vec<&DendriteRing>;
17
18    // Maintenance
19    pub fn tick(&mut self) -> usize;
20
21    // Stats
22    pub fn ring_count(&self) -> usize;
23    pub fn memory_usage(&self) -> usize;
24    pub fn storage_profile(&self) -> StorageProfile;
25    pub fn sapwood_rings(&self) -> &[DendriteRing];
26    pub fn heartwood_rings(&self) -> &[DendriteRing];
27    pub fn accumulator_stats(&self) -> Option<AccumulatorStats>;
28}
29
dendrite_ring.ts
typescript
1class DendriteRing {
2  constructor(options?: DendriteRingOptions);
3  async init(): Promise<DendriteRing>;
4
5  // Ingest
6  ingest(timestampNs: number, value: number): RingSnapshot | null;
7  flush(): RingSnapshot | null;
8
9  // Queries
10  ringAt(timestampNs: number): RingSnapshot | null;
11  ringsInRange(startNs: number, endNs: number): RingSnapshot[];
12  anomalies(): AnomalySnapshot[];
13  anomaliesInRange(startNs: number, endNs: number): AnomalySnapshot[];
14  ringsByShape(shape: string): RingSnapshot[];
15
16  // Maintenance
17  tick(): number;
18
19  // Stats
20  readonly ringCount: number;
21  readonly memoryUsage: number;
22  storageProfile(): StorageProfile;
23  sapwoodRings(): RingSnapshot[];
24  heartwoodRings(): RingSnapshot[];
25  currentAccumulatorStats(): AccumulatorStats | null;
26}
27

Where This Matters

Prometheus

TSDB Block Compaction

Improvement comparison
TodayWith Dendrite RingHow
Fixed 2-hour blocks regardless of signalVariance-proportional ring widthsCUSUM/EWMV detects regime changes; thin rings for quiet periods, wide for incidents
Uniform storage cost for all time ranges48 bytes for boring periods, 292 bytes for incidentsRing summary fidelity scales with signal variance

Interactive Simulation

Simulation coming soon

An interactive visualization of Dendrite Ring behavior will be available here.

View all simulations

Discussion

Questions, ideas, or experiences with Dendrite Ring? Join the discussion.