In-memory, LSM-inspired, time-indexed multimap for Python (C17 core + CPython extension).
Timelog is built for timestamp-first workloads where the core operation is "everything in [t1, t2)".
It provides a native in-memory index with snapshot-consistent reads, out-of-order ingestion support, and sequenced range deletes.
At a high level, writes flow through mutable ingest state into immutable layers (memrun, L0, L1), while reads merge across layers with tombstone-aware filtering.
The design is LSM-inspired, but explicitly scoped to an embedded in-memory engine.
Install from PyPI:
Or with uv:
Distribution name is timelog-lib, import namespace stays timelog:
from timelog import Timelogfrom timelog import Timelog log = Timelog.for_streaming(time_unit="ms") # Auto-timestamp append
log.append({"event": "boot"}) # Operator-style explicit timestamp append
log[1_700_000_000_000] = {"event": "tick"} # Half-open range query [t1, t2)
rows = list(log[1_700_000_000_000:1_700_000_000_001])
print(rows) log.close() # optional explicit cleanupfrom timelog import Timelog log = Timelog(time_unit="ms")
log[10] = "A"
del log[5:15] # delete [5, 15)
log[10] = "B" # later insert at same ts print(log[10]) # ['B']
print(list(log[0:20])) # [(10, 'B')] log.close() # optional explicit cleanupTimelog uses sequenced tombstones, so later inserts are not hidden by earlier deletes.
- Time ranges are half-open:
[t1, t2). - Reads are snapshot-consistent.
- Concurrency model is single writer plus concurrent readers.
- Duplicate timestamps are allowed (multimap semantics).
- Write-path backpressure (
TimelogBusyError) indicates the write was accepted; do not blind-retry the same write.
Timelog is:
- an embedded, in-memory timestamp index,
- optimized for append-heavy ingest and time-range retrieval,
- implemented in C17 with first-party CPython bindings.
Timelog is not:
- a durable storage engine,
- a distributed TSDB,
- a SQL query engine.
close() does not materialize unflushed writes. Call flush() first if you need all pending data materialized into immutable segments before shutdown.
Core Python facade surface:
- Constructors:
Timelog(...)Timelog.for_streaming(...)Timelog.for_bulk_ingest(...)Timelog.for_low_latency(...)
- Writes:
append(...)extend(...)log[ts] = objdelete(t1, t2)/delete(ts)cutoff(ts)
- Reads:
log[t1:t2],log[t1:],log[:t2],log[:]log[ts]/at(ts)
- Introspection and views:
See docs/python-api.md for the full behavior contract.
- Writes and lifecycle operations must be externally serialized.
- Snapshot iterators are safe for concurrent reads.
- Background maintenance can run automatically (
maintenance="background") or be controlled manually. TimelogBusyErroron write operations means accepted write + pressure signal, not "write lost".- Do not call
close()concurrently with other operations on the same instance.
Write Path Read Path
---------- ---------
append/extend/delete snapshot + query([t1, t2))
| |
v v
Memtable (mutable) <-------------------- Snapshot view
| seal
v
Memrun (immutable)
| flush
v
L0 Segments (overlap)
| compact
v
L1 Segments (windowed, non-overlap)
Reads plan sources across active + immutable layers, then run k-way merge with tombstone filtering based on sequencing/watermark state. Flush and compaction bound read fan-out over time.
Deletes are logical tombstones; physical cleanup is deferred to maintenance.