dshackle overrides weave: host-local method/option overrides survive config regens
/root/rpc-local/dshackle-overrides.yaml (host-local, like rpc-local/compose.d) is re-woven into main_configs/*.yaml by reload_dshackle.sh on every config push. Method-disable decisions are local to each gateway's market (operator eviction rule: cost_ratio = time_share/call_share at flat pay); this makes them durable inputs instead of fragile output patches. Step 1 of moving dshackle config generation onto the gateway hosts (step 2: ssh forced-command upstreams fetch, generic dshackle.yaml include list derived from drpc-gateways.json). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
99
apply-dshackle-overrides.py
Executable file
99
apply-dshackle-overrides.py
Executable file
@@ -0,0 +1,99 @@
|
||||
#!/usr/bin/env python3
|
||||
"""apply-dshackle-overrides.py - weave HOST-LOCAL dshackle upstream overrides into
|
||||
the generated main_configs/*.yaml so they survive every configure-drpc regeneration.
|
||||
|
||||
Override file (NOT in the repo - host-local, like rpc-local/compose.d):
|
||||
/root/rpc-local/dshackle-overrides.yaml
|
||||
|
||||
Schema (match by upstream id OR by chain; id wins):
|
||||
upstreams:
|
||||
uk-4-base-mainnet-op-reth-pruned: # or e.g. "chain:base"
|
||||
method_groups_disabled: [trace, debug] # removed from enabled, added to disabled
|
||||
methods_disabled: [eth_getFilterLogs] # per-method disables (methods.disabled)
|
||||
options:
|
||||
disable-validation: true # merged into upstream options
|
||||
|
||||
Rationale: heavy methods at flat per-call pay poison the chain's latency scoring
|
||||
(operator method-eviction rule 2026-07-10; see method-economics.py). The disable
|
||||
decision is local to the gateway's market, so the config lives on the gateway.
|
||||
|
||||
Called by reload_dshackle.sh before signaling dshackle - i.e. re-applied on every
|
||||
ansible config push. NOTE: dshackle only re-reads upstream method lists on RESTART;
|
||||
this keeps the on-disk config permanently correct, the next restart activates it.
|
||||
Idempotent. Silent no-op when the override file is absent.
|
||||
"""
|
||||
import glob
|
||||
import os
|
||||
import sys
|
||||
|
||||
import yaml
|
||||
|
||||
OVERRIDES = '/root/rpc-local/dshackle-overrides.yaml'
|
||||
CONFIG_DIR = '/root/rpc/main_configs'
|
||||
|
||||
|
||||
def apply(upstream, ov):
|
||||
changed = False
|
||||
mg = upstream.setdefault('method-groups', {}) or {}
|
||||
for grp in ov.get('method_groups_disabled') or []:
|
||||
en = mg.get('enabled') or []
|
||||
if grp in en:
|
||||
en.remove(grp)
|
||||
mg['enabled'] = en
|
||||
changed = True
|
||||
dis = mg.get('disabled') or []
|
||||
if grp not in dis:
|
||||
dis.append(grp)
|
||||
mg['disabled'] = dis
|
||||
changed = True
|
||||
if mg:
|
||||
upstream['method-groups'] = mg
|
||||
for meth in ov.get('methods_disabled') or []:
|
||||
methods = upstream.setdefault('methods', {}) or {}
|
||||
dis = methods.get('disabled') or []
|
||||
if not any((d.get('name') if isinstance(d, dict) else d) == meth for d in dis):
|
||||
dis.append({'name': meth})
|
||||
methods['disabled'] = dis
|
||||
upstream['methods'] = methods
|
||||
changed = True
|
||||
for k, v in (ov.get('options') or {}).items():
|
||||
opts = upstream.setdefault('options', {}) or {}
|
||||
if opts.get(k) != v:
|
||||
opts[k] = v
|
||||
upstream['options'] = opts
|
||||
changed = True
|
||||
return changed
|
||||
|
||||
|
||||
def main():
|
||||
if not os.path.exists(OVERRIDES):
|
||||
return
|
||||
spec = (yaml.safe_load(open(OVERRIDES)) or {}).get('upstreams') or {}
|
||||
if not spec:
|
||||
return
|
||||
total = 0
|
||||
for path in glob.glob(os.path.join(CONFIG_DIR, '*.yaml')):
|
||||
try:
|
||||
doc = yaml.safe_load(open(path))
|
||||
except yaml.YAMLError:
|
||||
continue
|
||||
if not isinstance(doc, dict) or 'upstreams' not in doc:
|
||||
continue
|
||||
changed = False
|
||||
for up in doc['upstreams'] or []:
|
||||
if not isinstance(up, dict):
|
||||
continue
|
||||
ov = spec.get(up.get('id')) or spec.get(f"chain:{up.get('chain')}")
|
||||
if ov and apply(up, ov):
|
||||
changed = True
|
||||
if changed:
|
||||
with open(path, 'w') as f:
|
||||
yaml.safe_dump(doc, f, default_flow_style=False, sort_keys=False)
|
||||
total += 1
|
||||
print(f'dshackle-overrides: rewove {path}')
|
||||
if total:
|
||||
print(f'dshackle-overrides: {total} file(s) updated (restart dshackle to activate method changes)')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,4 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Re-weave host-local upstream overrides (method disables etc.) into the freshly
|
||||
# pushed configs BEFORE signaling dshackle - makes /root/rpc-local/dshackle-overrides.yaml
|
||||
# survive every configure-drpc regeneration. See apply-dshackle-overrides.py.
|
||||
[ -f /root/rpc/apply-dshackle-overrides.py ] && python3 /root/rpc/apply-dshackle-overrides.py
|
||||
|
||||
docker ps -q -f "name=dshackle" | xargs -r docker kill --signal=HUP
|
||||
docker ps -q -f "name=dshackle-free" | xargs -r docker kill --signal=HUP
|
||||
|
||||
Reference in New Issue
Block a user