remove full_url, add migration to change scheme of node.monerodevs.org to http

This commit is contained in:
Binarybaron 2025-06-24 10:18:14 +02:00
parent 8741ac7619
commit dc21ab9cee
5 changed files with 62 additions and 24 deletions

View file

@ -0,0 +1,33 @@
-- Remove full_url column from monero_nodes table as it can be derived from scheme, host, and port
-- Drop the index first
DROP INDEX IF EXISTS idx_nodes_full_url;
-- Create a new table without the full_url column
CREATE TABLE monero_nodes_new (
id INTEGER PRIMARY KEY AUTOINCREMENT,
scheme TEXT NOT NULL,
host TEXT NOT NULL,
port INTEGER NOT NULL,
network TEXT NOT NULL, -- mainnet/stagenet/testnet - always known at insertion time
first_seen_at TEXT NOT NULL,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
-- Create a unique constraint on scheme, host, and port instead of full_url
UNIQUE(scheme, host, port)
);
-- Copy data from old table to new table
INSERT INTO monero_nodes_new (id, scheme, host, port, network, first_seen_at, created_at, updated_at)
SELECT id, scheme, host, port, network, first_seen_at, created_at, updated_at
FROM monero_nodes;
-- Drop the old table
DROP TABLE monero_nodes;
-- Rename the new table to the original name
ALTER TABLE monero_nodes_new RENAME TO monero_nodes;
-- Recreate the indexes (excluding the full_url index)
CREATE INDEX IF NOT EXISTS idx_nodes_network ON monero_nodes(network);
CREATE INDEX IF NOT EXISTS idx_nodes_scheme_host_port ON monero_nodes(scheme, host, port);

View file

@ -0,0 +1,12 @@
-- Fix monerodevs.org nodes: change from https to http
UPDATE monero_nodes
SET scheme = 'http'
WHERE host = 'node.monerodevs.org' AND network = 'stagenet';
UPDATE monero_nodes
SET scheme = 'http'
WHERE host = 'node2.monerodevs.org' AND network = 'stagenet';
UPDATE monero_nodes
SET scheme = 'http'
WHERE host = 'node3.monerodevs.org' AND network = 'stagenet';

View file

@ -12,7 +12,6 @@ pub struct MoneroNode {
pub scheme: String, // http or https
pub host: String,
pub port: i64,
pub full_url: String,
pub network: String, // mainnet, stagenet, or testnet - always known at insertion time
pub first_seen_at: String, // ISO 8601 timestamp when first discovered
// Computed fields from health_checks (not stored in monero_nodes table)
@ -49,14 +48,14 @@ pub struct HealthCheck {
impl MoneroNode {
pub fn new(scheme: String, host: String, port: i64, network: String) -> Self {
let full_url = format!("{}://{}:{}", scheme, host, port);
// TODO: Do this in the database
let now = chrono::Utc::now().to_rfc3339();
Self {
id: None,
scheme,
host,
port,
full_url,
network,
first_seen_at: now,
// These are computed from health_checks
@ -73,6 +72,10 @@ impl MoneroNode {
}
}
pub fn full_url(&self) -> String {
format!("{}://{}:{}", self.scheme, self.host, self.port)
}
pub fn success_rate(&self) -> f64 {
let total = self.success_count + self.failure_count;
if total == 0 {
@ -318,7 +321,6 @@ impl Database {
scheme: row.scheme,
host: row.host,
port: row.port,
full_url: row.full_url,
network: row.network,
first_seen_at: row.first_seen_at,
success_count: row.success_count,
@ -402,7 +404,6 @@ impl Database {
scheme: row.scheme,
host: row.host,
port: row.port,
full_url: row.full_url,
network: row.network,
first_seen_at: row.first_seen_at,
success_count: row.success_count,
@ -592,7 +593,6 @@ impl Database {
scheme: row.scheme,
host: row.host,
port: row.port,
full_url: row.full_url,
network: row.network,
first_seen_at: row.first_seen_at,
success_count: row.success_count,
@ -696,7 +696,6 @@ impl Database {
scheme: row.scheme,
host: row.host,
port: row.port,
full_url: row.full_url,
network: row.network,
first_seen_at: row.first_seen_at,
success_count: row.success_count,
@ -810,7 +809,6 @@ impl Database {
scheme: row.scheme,
host: row.host,
port: row.port,
full_url: row.full_url,
network: row.network,
first_seen_at: row.first_seen_at,
success_count: row.success_count,
@ -917,7 +915,6 @@ impl Database {
scheme: row.scheme,
host: row.host,
port: row.port,
full_url: row.full_url,
network: row.network,
first_seen_at: row.first_seen_at,
success_count: row.success_count,

View file

@ -54,7 +54,7 @@ impl NodePool {
}
if candidate_nodes.len() == 1 {
return Ok(Some(candidate_nodes[0].full_url.clone()));
return Ok(Some(candidate_nodes[0].full_url()));
}
// Power of Two Choices: pick 2 random nodes, select the better one
@ -71,9 +71,10 @@ impl NodePool {
debug!(
"Selected node using P2C for network {}: {}",
self.network, selected.full_url
self.network, selected.full_url()
);
Ok(Some(selected.full_url.clone()))
Ok(Some(selected.full_url()))
}
/// Calculate goodness score based on usage-based recency
@ -127,7 +128,7 @@ impl NodePool {
.into_iter()
.take(5)
.map(|node| ReliableNodeInfo {
url: node.full_url.clone(),
url: node.full_url(),
success_rate: node.success_rate(),
avg_latency_ms: node.avg_latency_ms,
})

View file

@ -5,7 +5,7 @@ use axum::{
response::Response,
};
use serde_json::json;
use std::{error::Error, time::Instant};
use std::time::Instant;
use tracing::{debug, error, info_span, Instrument};
use uuid::Uuid;
@ -69,7 +69,7 @@ async fn raw_http_request(
let client = reqwest::Client::builder()
.timeout(std::time::Duration::from_secs(30))
.build()
.map_err(|e| HandlerError::RequestError(e.to_string()))?;
.map_err(|e| HandlerError::RequestError(format!("{:#?}", e)))?;
let url = format!("{}{}", node_url, path);
@ -119,19 +119,14 @@ async fn raw_http_request(
let response = request_builder
.send()
.await
.map_err(|e| HandlerError::RequestError(e.to_string()))?;
.map_err(|e| HandlerError::RequestError(format!("{:#?}", e)))?;
// Convert to axum Response preserving everything
let status = response.status();
let response_headers = response.headers().clone();
let body_bytes = response.bytes().await.map_err(|e| {
let mut error_msg = format!("Failed to read response body: {}", e);
if let Some(source) = e.source() {
error_msg.push_str(&format!(" (source: {})", source));
}
HandlerError::RequestError(error_msg)
HandlerError::RequestError(format!("Failed to read response body: {:#?}", e))
})?;
let mut axum_response = Response::new(Body::from(body_bytes));
@ -189,7 +184,7 @@ async fn single_raw_request(
let (parts, body_stream) = response.into_parts();
let body_bytes = axum::body::to_bytes(body_stream, usize::MAX)
.await
.map_err(|e| HandlerError::RequestError(e.to_string()))?;
.map_err(|e| HandlerError::RequestError(format!("{:#?}", e)))?;
if is_jsonrpc_error(&body_bytes) {
record_failure(state, &node_url).await;
@ -253,7 +248,7 @@ async fn race_requests(
let pool: Vec<String> = reliable_nodes
.into_iter()
.map(|node| node.full_url)
.map(|node| node.full_url())
.collect();
pool