Class: Plutonium::Wizard::Store::ActiveRecord
- Defined in:
- lib/plutonium/wizard/store/active_record.rb
Overview
Shipped store, backed by the plutonium_wizard_sessions table via Plutonium::Wizard::Session.
Constant Summary collapse
- ENCRYPTED_ENVELOPE_KEY =
A
datablob encrypted at rest (§8.1, the wizard'sencrypt_dataopt-in) is stored as a SELF-DESCRIBING envelope INSIDE the jsonb column:{ "_enc" => "<ciphertext>" }. The row therefore decrypts based on its own shape, independent of the wizard's CURRENTencrypt_data?(which may have been toggled after the row was written). Onlydata(the step field values) is encrypted;tracked_recordsholds record GIDs and stays in clear. "_enc"
Instance Method Summary collapse
- #clear(instance_key) ⇒ Object
- #complete(instance_key) ⇒ Object
- #completed?(instance_key:) ⇒ Boolean
- #read(instance_key) ⇒ Object
-
#write(instance_key, state, cleanup_after:, &merge) ⇒ Object
Upsert the run's state.
Instance Method Details
#clear(instance_key) ⇒ Object
62 63 64 |
# File 'lib/plutonium/wizard/store/active_record.rb', line 62 def clear(instance_key) Session.where(instance_key: instance_key).delete_all end |
#complete(instance_key) ⇒ Object
57 58 59 60 |
# File 'lib/plutonium/wizard/store/active_record.rb', line 57 def complete(instance_key) row = Session.find_by!(instance_key: instance_key) row.update!(status: "completed", completed_at: Time.current, data: {}, tracked_records: {}, visited: []) end |
#completed?(instance_key:) ⇒ Boolean
66 67 68 |
# File 'lib/plutonium/wizard/store/active_record.rb', line 66 def completed?(instance_key:) Session.status_completed.where(instance_key: instance_key).exists? end |
#read(instance_key) ⇒ Object
16 17 18 19 |
# File 'lib/plutonium/wizard/store/active_record.rb', line 16 def read(instance_key) row = Session.find_by(instance_key: instance_key) row && to_state(row) end |
#write(instance_key, state, cleanup_after:, &merge) ⇒ Object
Upsert the run's state. Concurrency-safe (§6.2): two requests can write the same run (double-submit, two tabs, the first-step create race). A blind overwrite would let the later writer clobber the earlier one's staged data (last-writer-wins). Instead:
- Create (no row yet): insert. If a concurrent create won the unique
instance_keyindex (RecordNotUnique), fall through to the merge path. - Update (row exists): take a row lock, then compare the version the
caller's
statewas read at against the row's CURRENT version. Equal → no concurrent writer, writestateverbatim (honors this request's prunes/deletions). Differs (or the caller never read a version — it lost a create race) → a concurrent advance committed; call themergeblock with the latest committed Plutonium::Wizard::State so the two sides are combined, and write the result. The lock serializes writers; the version check keeps the merge off the normal single-writer path.
merge is optional — a blockless caller keeps last-writer-wins (the
store-contract callers that don't model concurrency). The runner always
passes one.
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/plutonium/wizard/store/active_record.rb', line 40 def write(instance_key, state, cleanup_after:, &merge) row = Session.find_or_initialize_by(instance_key: instance_key) if row.new_record? assign_row(row, state, cleanup_after) begin row.save! return to_state(row) rescue ::ActiveRecord::RecordNotUnique # Lost the create race — the row now exists. Re-fetch and merge. row = Session.find_by!(instance_key: instance_key) end end write_locked(row, state, cleanup_after, merge) end |