/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>
100 lines
3.6 KiB
Python
Executable File
100 lines
3.6 KiB
Python
Executable File
#!/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()
|