Class: Nexo::RunStore::Disk
- Inherits:
-
Object
- Object
- Nexo::RunStore::Disk
- Defined in:
- lib/nexo/run_store.rb
Overview
File-backed backend for a host with no database: one JSON document per run
under dir, rewritten whenever the run changes. This is what makes durability
available outside Rails — checkpoint, suspend/resume and a run's recorded
artifacts all read state back from the store, and Memory dies with the process.
Nexo.config.run_store = Nexo::RunStore::Disk.new(dir: "~/.local/state/myapp/runs")
The Run is Memory's, subclassed: the shape and every read helper are shared, so
a host written against one store behaves identically on the other. Only the
save_* hooks differ — they write the document. push_event/push_artifact
deliberately do NOT, because Workflow always calls the matching save_* right
after, and persisting twice would double the writes for nothing.
Scope, honestly: rewriting the whole document per change is fine for the run
sizes this is meant for (a CLI's own history) and wrong for a busy multi-worker
queue — that is what the ActiveRecord backend is for. claim_for_resume! is
atomic within a process but NOT across processes; two machines resuming the
same run concurrently is out of scope. Runs are never pruned here: retention is
the host's policy, and dir is a plain directory it can manage.
Defined Under Namespace
Classes: Run
Constant Summary collapse
- MUTEX =
Serializes writes within the process, mirroring Memory's store-wide lock.
Mutex.new
Instance Attribute Summary collapse
-
#dir ⇒ Object
readonly
The directory runs are written to.
Instance Method Summary collapse
-
#all ⇒ Object
Every run in the directory, newest-written first.
-
#claim_for_resume!(run) ⇒ Object
Atomically claims a
"suspended"run for resume, re-reading from disk so a stale in-memory copy cannot win. -
#create(workflow_class:, payload:) ⇒ Object
Builds a fresh
"pending"Run, writes it, and returns it. -
#find(id) ⇒ Object
Reads a run back by id.
-
#initialize(dir:) ⇒ Disk
constructor
diris created on demand;~is expanded.
Constructor Details
#initialize(dir:) ⇒ Disk
dir is created on demand; ~ is expanded.
232 233 234 |
# File 'lib/nexo/run_store.rb', line 232 def initialize(dir:) @dir = ::File.(dir) end |
Instance Attribute Details
#dir ⇒ Object (readonly)
The directory runs are written to.
237 238 239 |
# File 'lib/nexo/run_store.rb', line 237 def dir @dir end |
Instance Method Details
#all ⇒ Object
Every run in the directory, newest-written first. Not part of the store contract Workflow calls — it is here because a host with a run directory inevitably wants to list it ("what did the last run produce?").
262 263 264 265 266 267 268 269 270 271 272 273 |
# File 'lib/nexo/run_store.rb', line 262 def all return [] unless ::File.directory?(@dir) Dir.glob(::File.join(@dir, "*.json")) .sort_by { |p| -::File.mtime(p).to_f } .filter_map do |p| hydrate(JSON.parse(::File.read(p)), p) rescue JSON::ParserError, SystemCallError # A half-written or hand-edited document must not break the listing. nil end end |
#claim_for_resume!(run) ⇒ Object
Atomically claims a "suspended" run for resume, re-reading from disk so a
stale in-memory copy cannot win. Within one process only — see the class
note.
278 279 280 281 282 283 284 285 286 287 288 |
# File 'lib/nexo/run_store.rb', line 278 def claim_for_resume!(run) MUTEX.synchronize do current = find(run.id) return false unless current.status == "suspended" run.status = "running" run.path ||= path_for(run.id) run.persist! true end end |
#create(workflow_class:, payload:) ⇒ Object
Builds a fresh "pending" Run, writes it, and returns it.
240 241 242 243 244 245 246 247 248 249 |
# File 'lib/nexo/run_store.rb', line 240 def create(workflow_class:, payload:) run = Run.new( id: Nexo.generate_run_id, workflow_class: workflow_class, status: "pending", payload: payload, result: nil, error: nil, events: [], artifacts: [], state: {} ) ::FileUtils.mkdir_p(@dir) run.path = path_for(run.id) run.persist! run end |
#find(id) ⇒ Object
Reads a run back by id. A miss raises KeyError, matching Memory.
252 253 254 255 256 257 |
# File 'lib/nexo/run_store.rb', line 252 def find(id) path = path_for(id) raise KeyError, "run not found: #{id}" unless ::File.exist?(path) hydrate(JSON.parse(::File.read(path)), path) end |