String encoded as a hex string with Big Endian bytes.
Applications might use UUID native types like MySQL’s UUID field type, or C#’s Guid class. However, an event ID will not validate to a known UUID version.
If two events occur at the exact same microsecond, Evently increments the timestamp by one microsecond. Thus, no two events will ever occur at the exact same time. If the current time on the system is behind the last event’s time component, Evently will disregard current time and utilize the time value of the last event time, incrementing the microsecond to move past the previous event time. This will keep the event ledger in order if the clock is reset into the past.
NOTE: this is how Google’s TrueTime works; it does not duplicate timestamps but will increment ahead in case timestamps collide.
An Event ID can be traced to the event ledger it belongs to, and its place therein. It is a reference to a point in the ledger that marks the event. It also contains a verification checksum to validate the event contents matches the Event ID.
The timestamp component is a 64-bit microsecond (µsecond) value in the Unix Epoch. Note that 1 millisecond = 1,000 microseconds. When parsing the timestamp component in the event body to convert to a datetime, verify that your date parsing library can parse more than to the millisecond. Some will not be able to parse microseconds.
A Ledger ID is a 32-bit integer. It is computed as a partial checksum of the ledger’s genesis event, ANDed with the Ledger ID version. The genesis event is the ledger’s very first event and does not have a Ledger ID itself for the checksum. As a result, the Ledger ID is computed by following steps 1-4 of the Event checksum algorithm.
Here is an example genesis event:
{copyText = 'Copy'}, 300)" class="language-json">{"event":"Ledger Created","entities":{"ledger":"testing⑆preview"},"timestamp":"2021-10-03T16:32:12.816530Z","meta":{"actor":"matt"},"data":{"name":"My Ledger","description":"This ledger is for my example events."}}
Evently anticipates that future event IDs will need different data structures, so versioning is built into the ID.
00: 30 bit Ledger ID, 2 bit version
To apply the version to Ledger ID: ledgerId & 0xfffffffc where & is a bitwise AND operation.
The reader can examine the end of the Ledger ID to determine its version.
Versioning allows for future changes to the Ledger ID format to be identified and utilized. A ledger can contain events with Ledger IDs that have different versions over time.
The checksum calculation utilizes CRC32C and requires each event's attribute to be fed to the CRC32C calculator in this order. Note that ledger ID checksums omit the last two steps.
Event name
Sorted1 entities object as a compact2 JSON string
Sorted1 meta object as a compact2 JSON string
Sorted1 data value, if an object, as a compact2 JSON string
Timestamp as lower-case 16 byte hex string, left-padded with 0.
Skip the following steps for Ledger ID calculation
Ledger ID as lower-case, 8 byte hex string, left-padded with 0.
Previous Event ID as lower-case, 16 byte hex string.
If this is the genesis event, then this value is an Event ID with a checksum of 0. The timestamp portion is the ledger creation timestamp, and the ledger ID is the checksum computed from steps 1-4.
1 A “sorted” object has its keys sorted in case-sensitive, UCS Basic collated, ascending order. An object’s properties, if they are themselves objects, are sorted in the same way. This guarantees the objects are serialized to JSON consistently from the JSON parser’s results, which may be an unordered map. Array values are never sorted, but the object entries in an array are sorted in this manner.
2 A “compact” JSON string is represented as a single line with no whitespace between the braces, brackets, commas or other separators.
A collision is when two different events share the same event ID. While theoretically possible, a collision is unlikely for these reasons:
Monotonic timestamp means a ledger will never have a duplicate timestamp for any event.
Very rare checksum collisions: two different events will almost never have the same checksum.
If #1 does not hold, and a ledger's event timestamps could be the same, the checksum would also have to collide for the event id to point to two different events that occur at the same time in the ledger.
The checksum can be used to validate an event ledger. It can validate the entire ledger, or a subsection of the ledger if one trusts the starting point of the ledger section being validated. Validation follows these steps.