Skip to content

feat: ChannelFeeOptimizerJob dynamic fee actuator (routing engine Phase 2b)#535

Closed
markettes wants to merge 1 commit into
feat/heuristic-fee-engine-phase2afrom
feat/heuristic-fee-engine-phase2b
Closed

feat: ChannelFeeOptimizerJob dynamic fee actuator (routing engine Phase 2b)#535
markettes wants to merge 1 commit into
feat/heuristic-fee-engine-phase2afrom
feat/heuristic-fee-engine-phase2b

Conversation

@markettes

Copy link
Copy Markdown
Contributor

Routing engine — Phase 2b (dynamic fee actuator)

Stacked on #534 (Phase 2a), which stacks on #533 (Phase 1). Base branch is feat/heuristic-fee-engine-phase2a, so this PR's diff shows only the Phase 2b layer. Merge #533#534 → this.

The Quartz actuator that drives FeeOptimizerService over live channels.

What's here

  • ChannelFeeOptimizerJob (~30 min, [DisallowConcurrentExecution], IDbContextFactory):
    • Eligibility: owned via ChannelOwnershipHelper, Open, IsDynamicFeeEnabled, capacity ≥ ROUTING_ENGINE_FEE_MIN_CHANNEL_SIZE_SATS, has routing-state signal, not circuit-broken.
    • Prioritization: recent 7-day organic fee revenue desc, then |deviation| desc, capped at ROUTING_ENGINE_FEE_MAX_UPDATES_PER_RUN.
    • Authority split: skips any channel with an in-flight rebalance on its source (never fights the rebalancer).
    • Safety: restart-surviving per-channel throttle, baseline snapshot before first write, consecutive-failure circuit breaker.
    • Dry-run gate: real LND writes only when neither the global ROUTING_ENGINE_DRY_RUN nor the per-node RoutingEngineDryRun is set — go live one node at a time.
    • Restore-on-disable: a node with DynamicFeeManagementEnabled=false restores each touched channel's baseline once (or freezes with an audit marker), then clears its snapshot (one-shot).
  • Repository queries: RebalanceRepository.HasInFlightRebalanceBySourceChannel (authority-split check) and ChannelFeeStateRepository.GetByManagedNodePubKey (per-node batch + restore enumeration).
  • Quartz wiring in Program.cs.

Tests

  • ChannelFeeOptimizerJobTests: live write applies computed policy, global dry-run writes nothing, in-flight rebalance skips the channel entirely, kill-switch short-circuits.
  • RebalanceRepositoryRoutingEngineTests: HasInFlightRebalanceBySourceChannel true only for Pending/InFlight source.

Full suite green (337 passed, 1 skipped). Job-level actuation is validated on a canary via dry-run per the plan.

🤖 Generated with Claude Code

Phase 2b of the heuristic routing engine (stacked on Phase 2a): the Quartz actuator
that drives the fee optimizer over live channels.

- ChannelFeeOptimizerJob (~30 min, DisallowConcurrentExecution, IDbContextFactory):
  reads ChannelRoutingState + raw ListChannels, filters eligibility (owned via
  ChannelOwnershipHelper, Open, IsDynamicFeeEnabled, capacity >= min, has signal,
  not circuit-broken), prioritizes by 7-day organic fee revenue then |deviation|,
  and under ROUTING_ENGINE_FEE_MAX_UPDATES_PER_RUN applies FeeOptimizerService's
  decision. Enforces the fee-vs-rebalance authority split (skips any channel with an
  in-flight rebalance), a restart-surviving per-channel throttle, baseline snapshot,
  and a consecutive-failure circuit breaker. Real LND writes only when neither the
  global nor per-node dry-run flag is set (go-live one node at a time).
- Restore-on-disable: a node with DynamicFeeManagementEnabled off restores each
  touched channel's baseline once (or freezes with an audit marker), then clears its
  snapshot so the action is one-shot.
- Repository queries: RebalanceRepository.HasInFlightRebalanceBySourceChannel (the
  authority-split check) and ChannelFeeStateRepository.GetByManagedNodePubKey (batch
  per-node state + restore-on-disable enumeration).
- Quartz wiring in Program.cs; job + repository tests (live write, dry-run gate,
  authority-split skip, kill-switch, in-flight-source query).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@markettes markettes closed this Jul 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant