Class: Insika::FunnelFold
- Inherits:
-
Object
- Object
- Insika::FunnelFold
- Defined in:
- lib/insika/funnel_fold.rb
Overview
the tick-driven fold of WS7 outcome records into the pack-declared stages (the tick's cadence). One pass per claim window; the fold is idempotent across crashes via the per-pair ids cursor (D3) and per-pair transactions. Pairs whose declaration is absent or malformed are skipped (D8), as are outcome kinds the declaration does not map — the funnel shows the hole. Nothing else: no attribution (D4), no stage vocabulary of its own (D1).
Constant Summary collapse
- SCOPE =
"funnel_fold"- KEY =
"claim"- DEFAULT_WINDOW =
seconds; one folding worker per window
300
Instance Method Summary collapse
-
#initialize(outcome_store:, funnel_store:, profiles:, store:, window: DEFAULT_WINDOW, now: nil) ⇒ FunnelFold
constructor
A new instance of FunnelFold.
-
#recompute(tenant:, agent:, declaration:) ⇒ Object
From scratch (E1's "recompute matches the incremental fold", and the repair path for backfilled outcomes): wipes the pair's day cells and rebuilds them from every outcome record.
-
#run ⇒ Object
One pass.
Constructor Details
#initialize(outcome_store:, funnel_store:, profiles:, store:, window: DEFAULT_WINDOW, now: nil) ⇒ FunnelFold
Returns a new instance of FunnelFold.
19 20 21 22 23 24 25 26 27 |
# File 'lib/insika/funnel_fold.rb', line 19 def initialize(outcome_store:, funnel_store:, profiles:, store:, window: DEFAULT_WINDOW, now: nil) @outcome_store = outcome_store @funnel_store = funnel_store @profiles = profiles @store = store @window = window @now = now end |
Instance Method Details
#recompute(tenant:, agent:, declaration:) ⇒ Object
From scratch (E1's "recompute matches the incremental fold", and the
repair path for backfilled outcomes): wipes the pair's day cells and
rebuilds them from every outcome record. tenant: takes the store's
spellings alike (nil/""/"platform" = the no-tenant pair) — for_pair and
delete_days normalize at their own key boundaries, so no tenant is
ever mixed with another (a "platform" recompute never folds "acme"'s
records). -> folded count.
66 67 68 69 |
# File 'lib/insika/funnel_fold.rb', line 66 def recompute(tenant:, agent:, declaration:) @funnel_store.delete_days(tenant: tenant, agent: agent) fold_pair(tenant: tenant, agent: agent, declaration: declaration, full: true) end |
#run ⇒ Object
One pass. -> { claimed: true, folded: Integer, skipped: Integer, pairs: Integer } | { claimed: false } (another worker holds the window — the retention.rb:74 shape, so the tick summary reads uniformly)
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/insika/funnel_fold.rb', line 32 def run return { claimed: false } unless claim folded = 0 skipped = 0 pairs = 0 # The pairs come from the OUTCOME keys (prefix scan, no record reads); # each pair's records are read by `for_pair`, so a pair without a # declaration is never even read (a full `all` would read every record # of every pair — the O(n) the key shape is there to avoid). @outcome_store.pairs.each do |pair| begin declaration = declaration_for(pair[:agent]) next unless declaration folded += fold_pair(tenant: pair[:tenant], agent: pair[:agent], declaration: declaration, skipped: -> { skipped += 1 }) pairs += 1 rescue StandardError # a broken pair must not hold every other store's funnel hostage — # its own transaction already rolled back; the pass keeps folding. next end end { claimed: true, folded: folded, skipped: skipped, pairs: pairs } end |