fix: isolate on-chain provider outages
This commit is contained in:
+35
-24
@@ -158,9 +158,30 @@ def load_history():
|
||||
|
||||
# ── Background scraper ────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def _scrape_onchain_sources():
|
||||
"""Run independent on-chain providers so one outage cannot mask the other."""
|
||||
observations = {}
|
||||
errors = []
|
||||
successful_sources = 0
|
||||
providers = (
|
||||
("LookIntoBitcoin", "scrapers.lookintobitcoin"),
|
||||
("CheckOnChain", "scrapers.checkonchain"),
|
||||
)
|
||||
for display_name, module_name in providers:
|
||||
try:
|
||||
module = __import__(module_name, fromlist=["scrape_all"])
|
||||
observations.update(module.scrape_all())
|
||||
successful_sources += 1
|
||||
except Exception as exc:
|
||||
log.error("%s scraping failed: %s\n%s", display_name, exc, traceback.format_exc())
|
||||
errors.append(f"{display_name}: {exc}")
|
||||
return observations, errors, successful_sources
|
||||
|
||||
|
||||
def run_scrape(force_full=False):
|
||||
"""Run a scrape cycle and update cache.
|
||||
|
||||
|
||||
By default, only refreshes fast data (price, F&G) and reuses cached on-chain data.
|
||||
On-chain metrics (Playwright scrapes) only refresh if:
|
||||
- force_full=True (manual full refresh)
|
||||
@@ -262,30 +283,20 @@ def run_scrape(force_full=False):
|
||||
|
||||
if refresh_onchain:
|
||||
log.info("Refreshing on-chain metrics (forced, missing, or TTL expired)...")
|
||||
try:
|
||||
from scrapers import lookintobitcoin
|
||||
onchain = lookintobitcoin.scrape_all()
|
||||
try:
|
||||
from scrapers import checkonchain
|
||||
onchain.update(checkonchain.scrape_all())
|
||||
except Exception as e:
|
||||
log.error("CheckOnChain scraping failed: %s\n%s", e, traceback.format_exc())
|
||||
cycle_errors.append(f"CheckOnChain: {e}")
|
||||
checkonchain_keys = {"sopr", "sellside_risk", "active_address_momentum",
|
||||
"txcount_momentum", "nvt_price", "vdd_multiple"}
|
||||
for key in onchain_keys:
|
||||
source = "checkonchain" if key in checkonchain_keys else "lookintobitcoin"
|
||||
metrics[key] = merge_observation(
|
||||
existing_cache.get(key), onchain.get(key), source=source,
|
||||
error="metric missing from scrape",
|
||||
)
|
||||
onchain, onchain_errors, successful_sources = _scrape_onchain_sources()
|
||||
cycle_errors.extend(onchain_errors)
|
||||
checkonchain_keys = {"sopr", "sellside_risk", "active_address_momentum",
|
||||
"txcount_momentum", "nvt_price", "vdd_multiple"}
|
||||
for key in onchain_keys:
|
||||
source = "checkonchain" if key in checkonchain_keys else "lookintobitcoin"
|
||||
metrics[key] = merge_observation(
|
||||
existing_cache.get(key), onchain.get(key), source=source,
|
||||
error="metric missing from scrape",
|
||||
)
|
||||
if successful_sources:
|
||||
metrics["_onchain_timestamp"] = datetime.now(timezone.utc).isoformat()
|
||||
except Exception as e:
|
||||
log.error("LookIntoBitcoin scraping failed: %s\n%s", e, traceback.format_exc())
|
||||
_last_error = f"On-chain scraping failed: {e}"
|
||||
for k in onchain_keys:
|
||||
if k in existing_cache:
|
||||
metrics[k] = existing_cache[k]
|
||||
elif "_onchain_timestamp" in existing_cache:
|
||||
metrics["_onchain_timestamp"] = existing_cache["_onchain_timestamp"]
|
||||
else:
|
||||
# Reuse cached on-chain values — they're stored permanently
|
||||
log.info("Reusing cached on-chain data (use Full Refresh to re-scrape)")
|
||||
|
||||
Reference in New Issue
Block a user