|
68 | 68 |
|
69 | 69 | ### UUID Primary Keys |
70 | 70 |
|
| 71 | +UUIDs (Universally Unique Identifiers) are 128-bit identifiers ideal for distributed systems where coordination between nodes isn't possible. CQL generates UUIDs automatically when creating records. |
| 72 | + |
| 73 | +**Schema Definition:** |
| 74 | + |
| 75 | +```crystal |
| 76 | +MyDB = CQL::Schema.define(:my_db, adapter: CQL::Adapter::SQLite, uri: "sqlite3://db.sqlite3") do |
| 77 | + table :sessions do |
| 78 | + primary :id, String, auto_increment: false # UUID stored as TEXT |
| 79 | + bigint :user_id |
| 80 | + text :token |
| 81 | + timestamps |
| 82 | + end |
| 83 | +end |
| 84 | +``` |
| 85 | + |
| 86 | +**Model Definition:** |
| 87 | + |
71 | 88 | ```crystal |
72 | 89 | class Session |
73 | 90 | include CQL::ActiveRecord::Model(UUID) |
74 | | - db_context AppDB, :sessions |
| 91 | + db_context MyDB, :sessions |
75 | 92 |
|
76 | 93 | property id : UUID? |
77 | | - property user_id : Int32 |
78 | | - property token : String |
79 | | - property expires_at : Time |
| 94 | + property user_id : Int64 = 0_i64 |
| 95 | + property token : String = "" |
| 96 | + property created_at : Time? |
| 97 | + property updated_at : Time? |
| 98 | +
|
| 99 | + def initialize |
| 100 | + end |
80 | 101 |
|
81 | | - def initialize(@user_id : Int32, @token : String, @expires_at : Time) |
| 102 | + def initialize(@token : String, @user_id : Int64) |
82 | 103 | end |
83 | 104 | end |
84 | 105 | ``` |
85 | 106 |
|
| 107 | +**Usage:** |
| 108 | + |
| 109 | +```crystal |
| 110 | +# Create a session - UUID is generated automatically |
| 111 | +session = Session.create!( |
| 112 | + token: "abc123", |
| 113 | + user_id: 1_i64, |
| 114 | + created_at: Time.utc, |
| 115 | + updated_at: Time.utc |
| 116 | +) |
| 117 | +
|
| 118 | +session.id # => UUID instance |
| 119 | +session.id!.to_s # => "550e8400-e29b-41d4-a716-446655440000" |
| 120 | +
|
| 121 | +# Find by UUID |
| 122 | +found = Session.find!(session.id!) |
| 123 | +
|
| 124 | +# UUIDs are unique across all records |
| 125 | +session1 = Session.create!(token: "token1", user_id: 1_i64, created_at: Time.utc, updated_at: Time.utc) |
| 126 | +session2 = Session.create!(token: "token2", user_id: 2_i64, created_at: Time.utc, updated_at: Time.utc) |
| 127 | +session1.id != session2.id # => true |
| 128 | +``` |
| 129 | + |
| 130 | +**UUID Characteristics:** |
| 131 | + |
| 132 | +* **Format:** 8-4-4-4-12 hex characters (e.g., `550e8400-e29b-41d4-a716-446655440000`) |
| 133 | +* **Size:** 36 characters as string (with hyphens) |
| 134 | +* **Uniqueness:** Globally unique without coordination |
| 135 | +* **Use cases:** Distributed systems, public-facing IDs, merge-safe records |
| 136 | + |
86 | 137 | ### ULID Primary Keys |
87 | 138 |
|
| 139 | +ULIDs (Universally Unique Lexicographically Sortable Identifiers) combine the benefits of UUIDs with lexicographic sortability. They're ideal for time-ordered data like events, logs, or audit trails. |
| 140 | + |
| 141 | +**Schema Definition:** |
| 142 | + |
| 143 | +```crystal |
| 144 | +EventDB = CQL::Schema.define(:event_db, adapter: CQL::Adapter::SQLite, uri: "sqlite3://events.db") do |
| 145 | + table :events do |
| 146 | + primary :id, String, auto_increment: false # ULID stored as TEXT |
| 147 | + text :event_type |
| 148 | + text :payload |
| 149 | + timestamps |
| 150 | + end |
| 151 | +end |
| 152 | +``` |
| 153 | + |
| 154 | +**Model Definition:** |
| 155 | + |
88 | 156 | ```crystal |
89 | 157 | class Event |
90 | | - include CQL::ActiveRecord::Model(ULID) |
| 158 | + include CQL::ActiveRecord::Model(String) # ULID uses String type |
91 | 159 | db_context EventDB, :events |
92 | 160 |
|
93 | | - property id : ULID? |
94 | | - property event_type : String |
95 | | - property payload : JSON::Any |
96 | | - property occurred_at : Time |
| 161 | + property id : String? |
| 162 | + property event_type : String = "" |
| 163 | + property payload : String = "" |
| 164 | + property created_at : Time? |
| 165 | + property updated_at : Time? |
97 | 166 |
|
98 | | - def initialize(@event_type : String, @payload : JSON::Any, @occurred_at : Time = Time.utc) |
| 167 | + def initialize |
| 168 | + end |
| 169 | +
|
| 170 | + def initialize(@event_type : String, @payload : String) |
99 | 171 | end |
100 | 172 | end |
101 | 173 | ``` |
102 | 174 |
|
| 175 | +**Usage:** |
| 176 | + |
| 177 | +```crystal |
| 178 | +# Create an event - ULID is generated automatically |
| 179 | +event = Event.create!( |
| 180 | + event_type: "user.created", |
| 181 | + payload: "{\"user_id\": 1}", |
| 182 | + created_at: Time.utc, |
| 183 | + updated_at: Time.utc |
| 184 | +) |
| 185 | +
|
| 186 | +event.id # => "01ARZ3NDEKTSV4RRFFQ69G5FAV" |
| 187 | +event.id!.size # => 26 characters |
| 188 | +
|
| 189 | +# Find by ULID |
| 190 | +found = Event.find!(event.id!) |
| 191 | +
|
| 192 | +# ULIDs are sortable by creation time |
| 193 | +event1 = Event.create!(event_type: "first", payload: "{}", created_at: Time.utc, updated_at: Time.utc) |
| 194 | +sleep 1.millisecond |
| 195 | +event2 = Event.create!(event_type: "second", payload: "{}", created_at: Time.utc, updated_at: Time.utc) |
| 196 | +
|
| 197 | +event2.id! > event1.id! # => true (lexicographically sortable) |
| 198 | +
|
| 199 | +# Query events in chronological order using ID |
| 200 | +events = Event.order(:id).all # Ordered by creation time |
| 201 | +``` |
| 202 | + |
| 203 | +**ULID Characteristics:** |
| 204 | + |
| 205 | +* **Format:** 26 Crockford Base32 characters (e.g., `01ARZ3NDEKTSV4RRFFQ69G5FAV`) |
| 206 | +* **Size:** 26 characters (more compact than UUID) |
| 207 | +* **Sortability:** Lexicographically sortable by creation time |
| 208 | +* **Uniqueness:** Unique with 80-bit randomness per millisecond |
| 209 | +* **Use cases:** Event sourcing, audit logs, time-series data, distributed systems needing time ordering |
| 210 | + |
| 211 | +### Choosing Between Primary Key Types |
| 212 | + |
| 213 | +| Type | Format | Sortable | Size | Best For | |
| 214 | +| --------------- | ------------------------ | -------- | --------- | -------------------------------- | |
| 215 | +| `Int32`/`Int64` | Sequential integer | Yes | 4-8 bytes | Simple apps, internal IDs | |
| 216 | +| `UUID` | Random hex | No | 36 chars | Distributed systems, public IDs | |
| 217 | +| `String` (ULID) | Base32 timestamp+random | Yes | 26 chars | Event logs, time-ordered data | |
| 218 | + |
103 | 219 | --- |
104 | 220 |
|
105 | 221 | ## Working with Attributes |
|
0 commit comments