Class: Mistri::Stores::ActiveRecord

Inherits:
Object
  • Object
show all
Defined in:
lib/mistri/stores/active_record.rb

Overview

Entries in the host's own database, through a model class the host supplies, so sessions live where application data lives: MySQL, Postgres, whatever the app runs. Not auto-required; load it with require "mistri/stores/active_record".

The model needs three columns: session_id (string, indexed), position (integer), payload (text). A migration to copy:

create_table :mistri_entries do |t|
t.string :session_id, null: false, index: true
t.integer :position, null: false
t.text :payload, size: :medium, null: false
t.timestamps
end
add_index :mistri_entries, [:session_id, :position], unique: true

The unique index is load-bearing: a session's loop is serial, but other writers append alongside it by design (a steer from a web process, a background child's report from a job), so appends are optimistic. Two writers that pick the same position collide on the index and the loser retries at the next one; entry order stays intact without a lock.

Reads select only (position, payload) and sort in Ruby: ORDER BY over rows carrying multi-megabyte payloads exhausts MySQL's sort buffer.

Constant Summary collapse

APPEND_ATTEMPTS =

Concurrent writers converge in one or two retries; a session would need this many simultaneous appenders to exhaust them.

5

Instance Method Summary collapse

Constructor Details

#initialize(model) ⇒ ActiveRecord

Returns a new instance of ActiveRecord.



36
37
38
# File 'lib/mistri/stores/active_record.rb', line 36

def initialize(model)
  @model = model
end

Instance Method Details

#append(id, entry) ⇒ Object



40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/mistri/stores/active_record.rb', line 40

def append(id, entry)
  payload = JSON.generate(entry)
  attempts = 0
  begin
    position = @model.where(session_id: id).maximum(:position).to_i + 1
    @model.create!(session_id: id, position: position, payload: payload)
    nil
  rescue ::ActiveRecord::RecordNotUnique
    attempts += 1
    raise if attempts >= APPEND_ATTEMPTS

    retry
  end
end

#load(id) ⇒ Object



55
56
57
58
59
# File 'lib/mistri/stores/active_record.rb', line 55

def load(id)
  @model.where(session_id: id).pluck(:position, :payload)
        .sort_by(&:first)
        .map { |_position, payload| JSON.parse(payload) }
end