Class: Hecks::Adapters::Heki::SagaStore

Inherits:
Object
  • Object
show all
Includes:
Journal, Snapshot
Defined in:
lib/hecks/adapters/driven/heki/saga_store.rb

Overview

THE OPTIONAL saga-persistence capability (§2), Heki's own shape — a SIBLING snapshot+journal file pair, built the exact same way an aggregate's own persistence already is: Snapshot/Journal (heki/snapshot.rb, heki/journal.rb) operate generically on @path/@journal_path/@entry_mirrors and never touch @aggregate, so this reuses them unchanged rather than re-deriving the same binary framing and crash-recovery replay.

Reserved file name (hecks_saga_instances.heki, matching the hecks_-prefix convention every other new saga table in this work uses) avoids colliding with any real aggregate's own storage_name. Lives in the SAME directory an aggregate's own .heki file would (File.dirname(@path), Heki's own call below) — which, since Heki's resolve_path has no per-domain component at all, is typically shared across EVERY domain booted from the same root. domain is therefore carried inside each record and filtered on read, the same reason Postgres's own hecks_saga_instances keeps an explicit domain column under schema isolation (§3).

ONE flat records hash, keyed by a composite string (Heki's own snapshot format is id-keyed, not tuple-keyed) — never exposed outside this class; each_saga yields the four real fields a caller actually wants, not the internal key shape.

NO LOCKING OF ANY KIND, same as Heki's own aggregate persistence already has (confirmed: no flock anywhere in heki.rb/ heki/snapshot.rb/heki/journal.rb) — a saga gets exactly the durability and concurrency-safety this adapter already gives its aggregates, no better, no worse.

Instance Method Summary collapse

Methods included from Journal

#entries

Constructor Details

#initialize(dir) ⇒ SagaStore

Returns a new instance of SagaStore.



41
42
43
44
45
# File 'lib/hecks/adapters/driven/heki/saga_store.rb', line 41

def initialize(dir)
  @path         = File.join(dir, "hecks_saga_instances.heki")
  @journal_path = "#{@path}.journal"
  @entry_mirrors = nil
end

Instance Method Details

#delete_saga(domain, process_manager, correlation) ⇒ Object



59
60
61
62
63
64
65
66
67
# File 'lib/hecks/adapters/driven/heki/saga_store.rb', line 59

def delete_saga(domain, process_manager, correlation)
  key = key_for(domain, process_manager, correlation)

  append_entry("delete", key, nil)
  current = store
  current.delete(key)
  write(current)
  @store = current
end

#each_saga(domain) ⇒ Object



69
70
71
72
73
74
75
76
77
78
# File 'lib/hecks/adapters/driven/heki/saga_store.rb', line 69

def each_saga(domain)
  return enum_for(:each_saga, domain) unless block_given?

  store.each_value do |record|
    next unless record["domain"] == domain

    yield record["process_manager"], record["correlation"], record["state"],
          (record["memory"] || {}).transform_keys(&:to_sym)
  end
end

#save_saga(domain, process_manager, correlation, state, memory) ⇒ Object



47
48
49
50
51
52
53
54
55
56
57
# File 'lib/hecks/adapters/driven/heki/saga_store.rb', line 47

def save_saga(domain, process_manager, correlation, state, memory)
  key    = key_for(domain, process_manager, correlation)
  record = { "domain" => domain, "process_manager" => process_manager,
             "correlation" => correlation, "state" => state, "memory" => memory }

  append_entry("save", key, record)
  current = store
  current[key] = record
  write(current)
  @store = current
end