Python Client API
All classes described here are part of the py_chronolog_client module.
import py_chronolog_client
ClientPortalServiceConf Class
Holds the connection parameters for the ChronoVisor portal service (used for chronicle and story management and event logging).
Constructor
ClientPortalServiceConf(proto, ip, port, provider_id)
| Parameter | Type | Description |
|---|---|---|
proto | str | Transport protocol (e.g. "ofi+sockets") |
ip | str | IP address of the ChronoVisor portal service |
port | int | Port number of the portal service |
provider_id | int | Margo/Thallium provider ID |
Default values (matching ClientConfiguration defaults):
| Field | Default |
|---|---|
proto | "ofi+sockets" |
ip | "127.0.0.1" |
port | 5555 |
provider_id | 55 |
ClientQueryServiceConf Class
Holds the connection parameters for the ChronoPlayer query service (used for story replay). Only needed when constructing a client in WRITER + READER mode.
Constructor
ClientQueryServiceConf(proto, ip, port, provider_id)
| Parameter | Type | Description |
|---|---|---|
proto | str | Transport protocol (e.g. "ofi+sockets") |
ip | str | IP address of the ChronoPlayer query service |
port | int | Port number of the query service |
provider_id | int | Margo/Thallium provider ID |
Default values:
| Field | Default |
|---|---|
proto | "ofi+sockets" |
ip | "127.0.0.1" |
port | 5557 |
provider_id | 57 |
Client Class
The Client class manages the connection to ChronoVisor and provides all chronicle, story, and event operations.
Constructors
# WRITER mode — event logging only
Client(portal_conf: ClientPortalServiceConf)
# WRITER + READER mode — event logging and story replay
Client(portal_conf: ClientPortalServiceConf, query_conf: ClientQueryServiceConf)
- The first constructor creates a write-only client connected to the ChronoVisor portal service.
- The second constructor additionally connects to the ChronoPlayer query service, enabling
ReplayStory. CallingReplayStoryon a write-only client returns-11(CL_ERR_NOT_READER_MODE).
Connection
Client.Connect() -> int
- Establish a session with ChronoVisor. The client advertises its wire-protocol version as part of the handshake.
- Returns
0(CL_SUCCESS) on success,-13(CL_ERR_PROTOCOL_VERSION_MISMATCH) if the server rejects the client's protocol version, or another error code on failure.
Client.Disconnect() -> int
- Close the active session. Any stories still acquired by this client are auto-released by the Visor as part of disconnect.
- Returns
0(CL_SUCCESS) on success, error code otherwise.
Chronicle Management
Client.CreateChronicle(chronicle_name: str) -> int
- Create a new chronicle named
chronicle_name. - Returns
0(CL_SUCCESS) on success,-6(CL_ERR_CHRONICLE_EXISTS) if a chronicle with that name already exists, or another error code on failure.
Client.DestroyChronicle(chronicle_name: str) -> int
- Permanently delete the chronicle, including all of its stories and any persisted archive data.
- Returns
0(CL_SUCCESS) on success,-4(CL_ERR_ACQUIRED) if any story within the chronicle is currently acquired (by any client), or another error code on failure.
Client.ShowChronicles() -> tuple[int, list[str]]
- Returns a tuple
(return_code, names). On success,return_codeis0andnamesis the list of all chronicles visible to this client. On failure,namesis empty.
Story Management
Client.AcquireStory(chronicle_name: str,
story_name: str) -> tuple[int, StoryHandle]
- Open the story
story_nameinsidechronicle_namefor data access. If the story does not exist it is created. - Returns a tuple
(return_code, handle). On success,return_codeis0andhandleis a validStoryHandle; on failure,handleisNone. - Returns
-3(CL_ERR_NOT_EXIST) if the chronicle does not exist.
Client.ReleaseStory(chronicle_name: str,
story_name: str) -> int
- Release the story handle obtained from
AcquireStory. Pending events buffered by this client are flushed before the call returns. - Returns
0(CL_SUCCESS) on success,-5(CL_ERR_NOT_ACQUIRED) if the story was not acquired by this client.
Client.DestroyStory(chronicle_name: str,
story_name: str) -> int
- Permanently delete the story, including any persisted archive data.
- Returns
0(CL_SUCCESS) on success,-4(CL_ERR_ACQUIRED) if the story is currently acquired (by any client), or another error code on failure.
Client.ShowStories(chronicle_name: str) -> tuple[int, list[str]]
- Returns a tuple
(return_code, names). On success,return_codeis0andnamesis the list of all stories inchronicle_name. On failure (e.g.-3CL_ERR_NOT_EXISTif the chronicle does not exist),namesis empty.
Event Replay
Client.ReplayStory(chronicle_name: str,
story_name: str,
start: int,
end: int,
event_series: EventList) -> int
- Retrieve all events in the story whose timestamps fall within the half-open range
[start, end). - Events are appended to
event_series(anEventListinstance that must be created by the caller) in chronological order. - Returns
0(CL_SUCCESS) on success,-11(CL_ERR_NOT_READER_MODE) if the client was constructed without aClientQueryServiceConf,-12(CL_ERR_QUERY_TIMED_OUT) if the query did not complete in time, or another error code on failure. - Returns
-18(CL_ERR_PARTIAL_RESULT) when ChronoPlayer answered but knows the series is short: a keeper of the story did not answer within its 5-second fetch deadline, a keeper's answer hit its per-query event cap, or an archive file in the range could not be read.event_seriesholds the events it did gather; retry the range to pick up the rest.
StoryHandle Class
StoryHandle is obtained from Client.AcquireStory and provides write access as well as low-latency on-demand tail reading for a story.
StoryHandle.log_event(event_string: str) -> int
- Append a new event with the log record
event_string. - Returns the
uint64_ttimestamp (as a Pythonint) assigned to the event by the client library. - The keeper's answer is discarded; a keeper crash before ChronoGrapher writes the event loses it.
Use the overload below with
Durability.Durablewhen the call must not return until the event is on the keeper's disk.
StoryHandle.log_event(log_string: str, options: LogEventOptions) -> tuple[int, int]
- Append a new event and wait until it has reached
options.durability. Returns a tuple(status, event_time).statusis0(CL_SUCCESS) once the event got there;event_timeis the event's timestamp on success and with-21, and0with every other status. - With
Durability.Accepted(the default) it takes the same path aslog_event(event_string)and returns the keeper's status instead of discarding it; a keeper that is shutting down returns-1and stores nothing. - With
Durability.Durableit returns0only after the keeper wrote the event to its write-ahead log (WAL) andfdatasyncreturned. A keeper that cannot make the event durable answers with an error and stores nothing, except that-21means it may be stored; the write is never downgraded to Accepted. The WAL is off by default on the keepers, and with it off every durable write returns-19(CL_ERR_DURABILITY_UNAVAILABLE). See Keeper WAL. - A durable call can block for up to
options.attempts × options.timeout_ms(30 s with the defaults). It releases the GIL while it waits, so other Python threads keep running. Do not change theoptionsobject from another thread during the call. - Retries, at-least-once storage and the return codes are the same as in C++: see
the C++ API and Error Codes.
In short, every retry carries the same
(time, client id, index), copies collapse on read, so a reader sees the event once;-2means a bad argument (an empty record, orattemptsortimeout_msof 0 on a durable write),-7no keepers, and-19,-20,-21are the durability codes.
options = py_chronolog_client.LogEventOptions()
options.durability = py_chronolog_client.Durability.Durable
status, event_time = story.log_event("sensor reading 42", options)
if status == 0:
pass # on the keeper's disk
elif status == -20:
pass # nothing stored; back off and write again
elif status == -21:
pass # may be stored; a new log_event creates a second event.
# event_time is the event's timestamp: replay the story
# around it to see whether the event is there
else:
print(f"durable write failed: {status}") # nothing stored
A Python subclass of StoryHandle that overrides log_event serves both forms with one method. To
handle options itself, the override accepts an optional options argument and, when given one,
returns the (status, event_time) pair. An override that takes only event_string, as written for
earlier releases, keeps working when the C++ side calls log_event(record, options)
(StoryHandle.log_event(handle, record, options) does too): the default body sends Accepted writes
through it and answers Durable ones with -19. Calling handle.log_event(record, options) on such a
subclass from Python still reaches its one-argument method directly and raises TypeError.
StoryHandle.playback(n: int, events: EventList) -> int
- On-Demand Tail Read: Retrieve the most recent
nevents of this story directly from ChronoKeeper in-memory buffers (KeeperChunkRetentionStore), bypassing the ChronoPlayer/HDF5 archive path. eventsis anEventListinstance populated in place with the returnedEventobjects in ascending order.- Returns
0(CL_SUCCESS) on success,-7(CL_ERR_NO_KEEPERS),-12(CL_ERR_QUERY_TIMED_OUT),-18(CL_ERR_PARTIAL_RESULT), or-1(CL_ERR_UNKNOWN). - Supported in writer-only client mode (
ClientPortalServiceConfonly).
Durability Enum
How far an event must get before StoryHandle.log_event(log_string, options) returns 0.
| Value | Meaning |
|---|---|
Durability.Accepted | In keeper memory; what log_event(event_string) gives. The default. |
Durability.Durable | Written to the keeper's WAL and fdatasync'd. |
LogEventOptions Class
options = py_chronolog_client.LogEventOptions()
| Field | Type | Default | Description |
|---|---|---|---|
durability | Durability | Durability.Accepted | Level the call waits for |
timeout_ms | int | 10000 | Durable writes only: deadline of each attempt, in milliseconds |
attempts | int | 3 | Durable writes only: attempts to send before giving up; an attempt that gets no answer is retried |
Event Class
Event is the smallest data unit in ChronoLog. Events are returned by Client.ReplayStory inside an EventList.
Constructor
Event(event_time: int = 0,
client_id: int = 0,
index: int = 0,
log_record: str = "")
Getters
Event.time() -> int # event timestamp (uint64_t)
Event.client_id() -> int # originating client ID
Event.index() -> int # per-client sequence index (uint32_t)
Event.log_record() -> str # the log record string
EventList Class
EventList is the container returned (by reference) by Client.ReplayStory. Create an empty instance before calling ReplayStory and pass it as the last argument.
event_series = py_chronolog_client.EventList()
ret = client.ReplayStory("MyChronicle", "MyStory", start, end, event_series)
print(len(event_series)) # number of events replayed
for event in event_series:
print(event.time(), event.client_id(), event.index(), event.log_record())
See Error Codes for the full list of integer return codes produced by the methods above.