Skip to content

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.

sync_clock.py python
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.

regular_clock.py python
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
i Note
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.

slaved_clock.py python
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
💡 Tip
Use 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.

clock_manager.py python
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.

auto_clocks.py python
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

ClockBest ForOrderingOverhead
SynchronousClockSingle-process, sequential systemsTotal order within one componentMinimal
RegularClockTime-series, sampling, periodic tasksInterval-bucketed with causal guaranteesLow
SlavedClockHierarchical / nested subsystemsLocally independent, globally consistentLow
ClockManagerMulti-component architecturesCross-component causal consistencyModerate (sync on cross-boundary events)
★ Important
For most applications, you do not need to manage clocks directly. The Computation and Architecture abstractions handle clock coordination automatically. Use explicit clock management only when you need fine-grained control over temporal semantics.

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.