Clocks and Temporal Ordering
In distributed systems, wall-clock time is unreliable. Network delays, clock skew, and unsynchronized hardware mean you cannot trust timestamps alone to determine event ordering. PyRapide provides a family of logical clocks that assign consistent, causally correct timestamps to events.
Clock Types
PyRapide offers three clock implementations, each suited to different system topologies:
SynchronousClock
The simplest clock type. It assigns monotonically increasing integers to events within a single thread of execution. Use it for single-process systems or when all events are recorded by one component.
from pyrapide import SynchronousClock, Event
clock = SynchronousClock()
e1 = Event(name="first", source="main")
e2 = Event(name="second", source="main")
t1 = clock.tick(e1) # 1
t2 = clock.tick(e2) # 2
assert t1 < t2
RegularClock
A clock that advances at a fixed interval, independent of event arrival. Useful for time-series systems where you need regular sampling periods. Events are assigned to the nearest tick.
from pyrapide import RegularClock
from datetime import timedelta
clock = RegularClock(interval=timedelta(milliseconds=100))
# Events are bucketed into 100ms intervals
t1 = clock.tick(e1) # Assigned to the current interval
t2 = clock.tick(e2) # Same or next interval, depending on wall time
RegularClock still respects causality. If event A caused event B, A's timestamp
is guaranteed to be less than or equal to B's, even if both fall within the same interval.SlavedClock
A clock that derives its time from a parent clock. Used in hierarchical systems where a sub-component needs its own local time but must stay consistent with a global reference.
from pyrapide import SynchronousClock, SlavedClock
master = SynchronousClock()
slave = SlavedClock(parent=master)
# Slave timestamps are derived from the master
t_master = master.tick(e1) # Master advances
t_slave = slave.tick(e2) # Slave timestamp is consistent with master
# The slave can also advance independently for local events
t_local = slave.tick(e3)
assert t_local >= t_slave
SlavedClock when modeling subsystems. For example, a database module can
have its own slaved clock while the overall architecture uses a master clock, keeping all
timestamps globally consistent.ClockManager
The ClockManager coordinates multiple clocks across an architecture. It ensures that causal ordering is preserved
across component boundaries. When an event from module A causes an event in module B, the
ClockManager synchronizes their clocks so timestamps reflect the causal relationship.
1from pyrapide import ClockManager, SynchronousClock
2
3manager = ClockManager()
4
5# Register clocks for each component
6api_clock = manager.register("api", SynchronousClock())
7db_clock = manager.register("db", SynchronousClock())
8cache_clock = manager.register("cache", SynchronousClock())
9
10# When an event crosses component boundaries, the manager synchronizes
11manager.sync("api", "db", caused_event=db_query)
12# db_clock is now guaranteed to be >= api_clock at the point of causation
How Clocks Integrate with Computations
When you use a Computation, clock management is handled
automatically. The computation creates a ClockManager internally and assigns
timestamps as events are recorded.
from pyrapide import Computation, Event
comp = Computation()
e1 = Event(name="start", source="main")
e2 = Event(name="process", source="worker")
comp.record(e1)
comp.record(e2, caused_by=[e1])
# Timestamps are assigned automatically
t1 = comp.timestamp(e1)
t2 = comp.timestamp(e2)
assert t1 < t2 # Causal ordering is preserved
Comparing Clock Types
| Clock | Best For | Ordering | Overhead |
|---|---|---|---|
SynchronousClock | Single-process, sequential systems | Total order within one component | Minimal |
RegularClock | Time-series, sampling, periodic tasks | Interval-bucketed with causal guarantees | Low |
SlavedClock | Hierarchical / nested subsystems | Locally independent, globally consistent | Low |
ClockManager | Multi-component architectures | Cross-component causal consistency | Moderate (sync on cross-boundary events) |
Clocks provide the temporal foundation that powers timed patterns and constraint monitoring. With events, posets, computations, interfaces, modules, architectures, patterns, constraints, and clocks, you now have the complete PyRapide core toolkit.