mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-01 14:29:01 -07:00
server: cache fixes
This commit is contained in:
@@ -30,7 +30,7 @@ impl AddrRoutes for ApiRouter<AppState> {
|
||||
Path(path): Path<AddrParam>,
|
||||
State(state): State<AppState>
|
||||
| {
|
||||
let strategy = state.addr_cache(Version::ONE, &path.addr);
|
||||
let strategy = state.addr_cache(Version::ONE, &path.addr, false);
|
||||
state.cached_json(&headers, strategy, &uri, move |q| q.addr(path.addr)).await
|
||||
}, |op| op
|
||||
.id("get_address")
|
||||
@@ -53,7 +53,7 @@ impl AddrRoutes for ApiRouter<AppState> {
|
||||
Query(params): Query<AddrTxidsParam>,
|
||||
State(state): State<AppState>
|
||||
| {
|
||||
let strategy = state.addr_cache(Version::ONE, &path.addr);
|
||||
let strategy = state.addr_cache(Version::ONE, &path.addr, false);
|
||||
state.cached_json(&headers, strategy, &uri, move |q| q.addr_txs(path.addr, params.after_txid, 50)).await
|
||||
}, |op| op
|
||||
.id("get_address_txs")
|
||||
@@ -76,7 +76,7 @@ impl AddrRoutes for ApiRouter<AppState> {
|
||||
Query(params): Query<AddrTxidsParam>,
|
||||
State(state): State<AppState>
|
||||
| {
|
||||
let strategy = state.addr_cache(Version::ONE, &path.addr);
|
||||
let strategy = state.addr_cache(Version::ONE, &path.addr, true);
|
||||
state.cached_json(&headers, strategy, &uri, move |q| q.addr_txs(path.addr, params.after_txid, 25)).await
|
||||
}, |op| op
|
||||
.id("get_address_confirmed_txs")
|
||||
@@ -119,7 +119,7 @@ impl AddrRoutes for ApiRouter<AppState> {
|
||||
Path(path): Path<AddrParam>,
|
||||
State(state): State<AppState>
|
||||
| {
|
||||
let strategy = state.addr_cache(Version::ONE, &path.addr);
|
||||
let strategy = state.addr_cache(Version::ONE, &path.addr, false);
|
||||
state.cached_json(&headers, strategy, &uri, move |q| q.addr_utxos(path.addr)).await
|
||||
}, |op| op
|
||||
.id("get_address_utxos")
|
||||
|
||||
@@ -117,7 +117,7 @@ impl BlockRoutes for ApiRouter<AppState> {
|
||||
headers: HeaderMap,
|
||||
Path(path): Path<TimestampParam>,
|
||||
State(state): State<AppState>| {
|
||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.block_by_timestamp(path.timestamp)).await
|
||||
state.cached_json(&headers, state.timestamp_cache(Version::ONE, path.timestamp), &uri, move |q| q.block_by_timestamp(path.timestamp)).await
|
||||
},
|
||||
|op| {
|
||||
op.id("get_block_by_timestamp")
|
||||
|
||||
@@ -3,7 +3,7 @@ use axum::{
|
||||
extract::{Query, State},
|
||||
http::{HeaderMap, Uri},
|
||||
};
|
||||
use brk_types::{DifficultyAdjustment, HistoricalPrice, Prices, Timestamp};
|
||||
use brk_types::{DifficultyAdjustment, HistoricalPrice, Prices, Timestamp, Version};
|
||||
|
||||
use crate::{
|
||||
AppState, CacheStrategy, extended::TransformResponseExtended, params::OptionalTimestampParam,
|
||||
@@ -66,8 +66,12 @@ impl GeneralRoutes for ApiRouter<AppState> {
|
||||
headers: HeaderMap,
|
||||
Query(params): Query<OptionalTimestampParam>,
|
||||
State(state): State<AppState>| {
|
||||
let strategy = params
|
||||
.timestamp
|
||||
.map(|ts| state.timestamp_cache(Version::ONE, ts))
|
||||
.unwrap_or(CacheStrategy::Tip);
|
||||
state
|
||||
.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| {
|
||||
.cached_json(&headers, strategy, &uri, move |q| {
|
||||
q.historical_price(params.timestamp)
|
||||
})
|
||||
.await
|
||||
|
||||
@@ -8,7 +8,7 @@ use axum::{
|
||||
use brk_types::{
|
||||
BlockFeeRatesEntry, BlockFeesEntry, BlockInfoV1, BlockRewardsEntry, BlockSizesWeights,
|
||||
DifficultyAdjustmentEntry, HashrateSummary, PoolDetail, PoolHashrateEntry, PoolInfo,
|
||||
PoolsSummary, RewardStats,
|
||||
PoolsSummary, RewardStats, Version,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -154,7 +154,7 @@ impl MiningRoutes for ApiRouter<AppState> {
|
||||
"/api/v1/mining/pool/{slug}/blocks/{height}",
|
||||
get_with(
|
||||
async |uri: Uri, headers: HeaderMap, Path(PoolSlugAndHeightParam {slug, height}): Path<PoolSlugAndHeightParam>, State(state): State<AppState>| {
|
||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.pool_blocks(slug, Some(height))).await
|
||||
state.cached_json(&headers, state.height_cache(Version::ONE, height), &uri, move |q| q.pool_blocks(slug, Some(height))).await
|
||||
},
|
||||
|op| {
|
||||
op.id("get_pool_blocks_from")
|
||||
|
||||
@@ -10,7 +10,8 @@ use brk_types::{CpfpInfo, MerkleProof, Transaction, TxOutspend, TxStatus, Txid,
|
||||
|
||||
use crate::{
|
||||
AppState, CacheStrategy,
|
||||
extended::TransformResponseExtended,
|
||||
cache::CacheParams,
|
||||
extended::{ResponseExtended, TransformResponseExtended},
|
||||
params::{TxidParam, TxidVout, TxidsParam},
|
||||
};
|
||||
|
||||
@@ -132,7 +133,16 @@ impl TxRoutes for ApiRouter<AppState> {
|
||||
Path(path): Path<TxidVout>,
|
||||
State(state): State<AppState>
|
||||
| {
|
||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.outspend(&path.txid, path.vout)).await
|
||||
let v = Version::ONE;
|
||||
let immutable = CacheParams::immutable(v);
|
||||
if immutable.matches_etag(&headers) {
|
||||
return ResponseExtended::new_not_modified_with(&immutable);
|
||||
}
|
||||
let outspend = state.run(move |q| q.outspend(&path.txid, path.vout)).await;
|
||||
let height = state.sync(|q| q.height());
|
||||
let is_deep = outspend.as_ref().is_ok_and(|o| o.is_deeply_spent(height));
|
||||
let strategy = if is_deep { CacheStrategy::Immutable(v) } else { CacheStrategy::Tip };
|
||||
state.cached_json(&headers, strategy, &uri, move |_| outspend).await
|
||||
},
|
||||
|op| op
|
||||
.id("get_tx_outspend")
|
||||
@@ -157,7 +167,16 @@ impl TxRoutes for ApiRouter<AppState> {
|
||||
Path(param): Path<TxidParam>,
|
||||
State(state): State<AppState>
|
||||
| {
|
||||
state.cached_json(&headers, CacheStrategy::Tip, &uri, move |q| q.outspends(¶m.txid)).await
|
||||
let v = Version::ONE;
|
||||
let immutable = CacheParams::immutable(v);
|
||||
if immutable.matches_etag(&headers) {
|
||||
return ResponseExtended::new_not_modified_with(&immutable);
|
||||
}
|
||||
let outspends = state.run(move |q| q.outspends(¶m.txid)).await;
|
||||
let height = state.sync(|q| q.height());
|
||||
let all_deep = outspends.as_ref().is_ok_and(|os| os.iter().all(|o| o.is_deeply_spent(height)));
|
||||
let strategy = if all_deep { CacheStrategy::Immutable(v) } else { CacheStrategy::Tip };
|
||||
state.cached_json(&headers, strategy, &uri, move |_| outspends).await
|
||||
},
|
||||
|op| op
|
||||
.id("get_tx_outspends")
|
||||
@@ -237,7 +256,8 @@ impl TxRoutes for ApiRouter<AppState> {
|
||||
post_with(
|
||||
async |State(state): State<AppState>, body: String| {
|
||||
let hex = body.trim().to_string();
|
||||
state.sync(|q| q.broadcast_transaction(&hex))
|
||||
state.run(move |q| q.broadcast_transaction(&hex))
|
||||
.await
|
||||
.map(|txid| txid.to_string())
|
||||
.map_err(crate::Error::from)
|
||||
},
|
||||
|
||||
@@ -6,7 +6,7 @@ use aide::{
|
||||
};
|
||||
use axum::{
|
||||
Extension,
|
||||
http::{HeaderMap, header},
|
||||
http::HeaderMap,
|
||||
response::{Html, Redirect, Response},
|
||||
routing::get,
|
||||
};
|
||||
@@ -78,13 +78,12 @@ impl ApiRoutes for ApiRouter<AppState> {
|
||||
)
|
||||
.route("/api", get(Html::from(include_str!("./scalar.html"))))
|
||||
// Pre-compressed with: brotli -c -q 11 scalar.js > scalar.js.br
|
||||
.route("/scalar.js", get(|| async {
|
||||
(
|
||||
[
|
||||
(header::CONTENT_TYPE, "application/javascript"),
|
||||
(header::CONTENT_ENCODING, "br"),
|
||||
],
|
||||
.route("/scalar.js", get(|headers: HeaderMap| async move {
|
||||
Response::static_bytes(
|
||||
&headers,
|
||||
include_bytes!("./scalar.js.br").as_slice(),
|
||||
"application/javascript",
|
||||
"br",
|
||||
)
|
||||
}))
|
||||
.route(
|
||||
|
||||
@@ -4,15 +4,15 @@ use axum::{
|
||||
Extension,
|
||||
body::{Body, Bytes},
|
||||
extract::{Query, State},
|
||||
http::{HeaderMap, StatusCode, Uri},
|
||||
response::{IntoResponse, Response},
|
||||
http::{HeaderMap, Uri},
|
||||
response::Response,
|
||||
};
|
||||
use brk_types::{Format, Output, SeriesSelection};
|
||||
|
||||
use crate::{
|
||||
Result,
|
||||
api::series::{CACHE_CONTROL, max_weight},
|
||||
extended::{ContentEncoding, HeaderMapExtended},
|
||||
extended::{ContentEncoding, HeaderMapExtended, ResponseExtended},
|
||||
};
|
||||
|
||||
use super::AppState;
|
||||
@@ -34,7 +34,7 @@ pub async fn handler(
|
||||
let csv_filename = resolved.csv_filename();
|
||||
|
||||
if headers.has_etag(etag.as_str()) {
|
||||
return Ok((StatusCode::NOT_MODIFIED, "").into_response());
|
||||
return Ok(Response::new_not_modified(&etag, CACHE_CONTROL));
|
||||
}
|
||||
|
||||
// Phase 2: Format (expensive, server-side cached)
|
||||
|
||||
@@ -4,8 +4,8 @@ use axum::{
|
||||
Extension,
|
||||
body::{Body, Bytes},
|
||||
extract::{Query, State},
|
||||
http::{HeaderMap, StatusCode, Uri},
|
||||
response::{IntoResponse, Response},
|
||||
http::{HeaderMap, Uri},
|
||||
response::Response,
|
||||
};
|
||||
use brk_error::Result as BrkResult;
|
||||
use brk_query::{Query as BrkQuery, ResolvedQuery};
|
||||
@@ -14,7 +14,7 @@ use brk_types::{Format, Output, SeriesOutput, SeriesSelection};
|
||||
use crate::{
|
||||
Result,
|
||||
api::series::{CACHE_CONTROL, max_weight},
|
||||
extended::{ContentEncoding, HeaderMapExtended},
|
||||
extended::{ContentEncoding, HeaderMapExtended, ResponseExtended},
|
||||
};
|
||||
|
||||
use super::AppState;
|
||||
@@ -57,7 +57,7 @@ async fn format_and_respond(
|
||||
let csv_filename = resolved.csv_filename();
|
||||
|
||||
if headers.has_etag(etag.as_str()) {
|
||||
return Ok((StatusCode::NOT_MODIFIED, "").into_response());
|
||||
return Ok(Response::new_not_modified(&etag, CACHE_CONTROL));
|
||||
}
|
||||
|
||||
// Phase 2: Format (expensive, server-side cached)
|
||||
|
||||
@@ -4,15 +4,15 @@ use axum::{
|
||||
Extension,
|
||||
body::{Body, Bytes},
|
||||
extract::{Query, State},
|
||||
http::{HeaderMap, StatusCode, Uri},
|
||||
response::{IntoResponse, Response},
|
||||
http::{HeaderMap, Uri},
|
||||
response::Response,
|
||||
};
|
||||
use brk_types::{Format, OutputLegacy, SeriesSelection};
|
||||
|
||||
use crate::{
|
||||
Result,
|
||||
api::series::{CACHE_CONTROL, max_weight},
|
||||
extended::{ContentEncoding, HeaderMapExtended},
|
||||
extended::{ContentEncoding, HeaderMapExtended, ResponseExtended},
|
||||
};
|
||||
|
||||
pub const SUNSET: &str = "2027-01-01T00:00:00Z";
|
||||
@@ -36,7 +36,7 @@ pub async fn handler(
|
||||
let csv_filename = resolved.csv_filename();
|
||||
|
||||
if headers.has_etag(etag.as_str()) {
|
||||
return Ok((StatusCode::NOT_MODIFIED, "").into_response());
|
||||
return Ok(Response::new_not_modified(&etag, CACHE_CONTROL));
|
||||
}
|
||||
|
||||
// Phase 2: Format (expensive, server-side cached)
|
||||
|
||||
Reference in New Issue
Block a user