Class: ZeroRuby::LmidStores::ActiveRecordStore
- Inherits:
-
ZeroRuby::LmidStore
- Object
- ZeroRuby::LmidStore
- ZeroRuby::LmidStores::ActiveRecordStore
- Defined in:
- lib/zero_ruby/lmid_stores/active_record_store.rb
Overview
ActiveRecord-based LMID store using Zero’s zero_0.clients table. This store provides proper transaction support with atomic LMID updates for concurrent access in production environments.
Uses the same atomic increment pattern as Zero’s TypeScript implementation.
Instance Attribute Summary collapse
-
#model_class ⇒ Object
readonly
The model class to use for client records.
Instance Method Summary collapse
-
#delete_mutation_results(args) ⇒ Object
Delete mutation results from the zero_0.mutations table.
-
#fetch_and_increment(client_group_id, client_id) ⇒ Integer
Atomically increment and return the last mutation ID for a client.
-
#initialize(model_class: nil) ⇒ ActiveRecordStore
constructor
A new instance of ActiveRecordStore.
-
#transaction { ... } ⇒ Object
Execute a block within an ActiveRecord transaction.
-
#write_mutation_result(client_group_id, client_id, mutation_id, result) ⇒ Object
Write a mutation result to the zero_0.mutations table.
Constructor Details
#initialize(model_class: nil) ⇒ ActiveRecordStore
Returns a new instance of ActiveRecordStore.
23 24 25 |
# File 'lib/zero_ruby/lmid_stores/active_record_store.rb', line 23 def initialize(model_class: nil) @model_class = model_class || default_model_class end |
Instance Attribute Details
#model_class ⇒ Object (readonly)
The model class to use for client records. Defaults to ZeroRuby::ZeroClient.
21 22 23 |
# File 'lib/zero_ruby/lmid_stores/active_record_store.rb', line 21 def model_class @model_class end |
Instance Method Details
#delete_mutation_results(args) ⇒ Object
Delete mutation results from the zero_0.mutations table.
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/zero_ruby/lmid_stores/active_record_store.rb', line 78 def delete_mutation_results(args) client_group_id = args["clientGroupID"] sql = if args["type"] == "bulk" client_ids = args["clientIDs"] model_class.sanitize_sql_array([<<~SQL.squish, {client_group_id:}]) DELETE FROM zero_0.mutations WHERE "clientGroupID" = :client_group_id AND "clientID" = ANY(ARRAY[#{client_ids.map { |id| model_class.connection.quote(id) }.join(",")}]) SQL else client_id = args["clientID"] up_to_mutation_id = args["upToMutationID"] model_class.sanitize_sql_array([<<~SQL.squish, {client_group_id:, client_id:, up_to_mutation_id:}]) DELETE FROM zero_0.mutations WHERE "clientGroupID" = :client_group_id AND "clientID" = :client_id AND "mutationID" <= :up_to_mutation_id SQL end model_class.connection.execute(sql) end |
#fetch_and_increment(client_group_id, client_id) ⇒ Integer
Atomically increment and return the last mutation ID for a client. Uses INSERT … ON CONFLICT to handle both new and existing clients in a single atomic operation, minimizing lock duration.
34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/zero_ruby/lmid_stores/active_record_store.rb', line 34 def fetch_and_increment(client_group_id, client_id) table = model_class.quoted_table_name sql = model_class.sanitize_sql_array([<<~SQL.squish, {client_group_id:, client_id:}]) INSERT INTO #{table} ("clientGroupID", "clientID", "lastMutationID") VALUES (:client_group_id, :client_id, 1) ON CONFLICT ("clientGroupID", "clientID") DO UPDATE SET "lastMutationID" = #{table}."lastMutationID" + 1 RETURNING "lastMutationID" SQL model_class.connection.select_value(sql) end |
#transaction { ... } ⇒ Object
Execute a block within an ActiveRecord transaction.
51 52 53 |
# File 'lib/zero_ruby/lmid_stores/active_record_store.rb', line 51 def transaction(&block) model_class.transaction(&block) end |
#write_mutation_result(client_group_id, client_id, mutation_id, result) ⇒ Object
Write a mutation result to the zero_0.mutations table.
61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/zero_ruby/lmid_stores/active_record_store.rb', line 61 def write_mutation_result(client_group_id, client_id, mutation_id, result) result_json = begin result.is_a?(String) ? result : result.to_json rescue JSON::GeneratorError, Encoding::UndefinedConversionError {error: "app", message: "Error result could not be serialized"}.to_json end sql = model_class.sanitize_sql_array([<<~SQL.squish, {client_group_id:, client_id:, mutation_id:, result: result_json}]) INSERT INTO zero_0.mutations ("clientGroupID", "clientID", "mutationID", "result") VALUES (:client_group_id, :client_id, :mutation_id, :result::text::json) SQL model_class.connection.execute(sql) end |