Anon rpc accounting (#194)
* cargo generate migrations didnt work without this * anon rpc accounting * Revert "anon rpc accounting" This reverts commit f1b8cbcdeca0d5f9545da32f11dda6a90854f97a. * 0 for anon in mysql * only set approximate_balance_remaining on owned stats * only set self.approximate_balance_remaining when adding if some * todo complete
This commit is contained in:
parent
9b361cb849
commit
05e618f74c
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -3333,6 +3333,7 @@ name = "migration"
|
|||||||
version = "0.36.0"
|
version = "0.36.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
|
"sea-orm",
|
||||||
"sea-orm-migration",
|
"sea-orm-migration",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
@ -11,6 +11,7 @@ path = "src/lib.rs"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
tokio = { version = "1.29.1", features = ["full", "tracing"] }
|
tokio = { version = "1.29.1", features = ["full", "tracing"] }
|
||||||
chrono = "0.4.26"
|
chrono = "0.4.26"
|
||||||
|
sea-orm = { version = "0.11.3", features = ["with-chrono"]}
|
||||||
|
|
||||||
[dependencies.sea-orm-migration]
|
[dependencies.sea-orm-migration]
|
||||||
version = "0.11.3"
|
version = "0.11.3"
|
||||||
|
@ -133,8 +133,6 @@ pub enum AuthorizationType {
|
|||||||
#[derive(Clone, Debug, Default, From)]
|
#[derive(Clone, Debug, Default, From)]
|
||||||
pub struct AuthorizationChecks {
|
pub struct AuthorizationChecks {
|
||||||
/// database id of the primary user. 0 if anon
|
/// database id of the primary user. 0 if anon
|
||||||
/// TODO: do we need this? its on the authorization so probably not
|
|
||||||
/// TODO: `Option<NonZeroU64>`? they are actual zeroes some places in the db now
|
|
||||||
pub user_id: u64,
|
pub user_id: u64,
|
||||||
/// locally cached balance that may drift slightly if the user is on multiple servers
|
/// locally cached balance that may drift slightly if the user is on multiple servers
|
||||||
pub latest_balance: Arc<AsyncRwLock<Balance>>,
|
pub latest_balance: Arc<AsyncRwLock<Balance>>,
|
||||||
|
@ -26,7 +26,6 @@ use num_traits::ToPrimitive;
|
|||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::num::NonZeroU64;
|
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tracing::{error, instrument, trace, warn};
|
use tracing::{error, instrument, trace, warn};
|
||||||
@ -88,15 +87,15 @@ pub struct RpcQueryKey {
|
|||||||
user_error_response: bool,
|
user_error_response: bool,
|
||||||
/// the rpc method used.
|
/// the rpc method used.
|
||||||
method: Cow<'static, str>,
|
method: Cow<'static, str>,
|
||||||
/// None if the public url was used.
|
/// 0 if the public url was used.
|
||||||
rpc_secret_key_id: Option<NonZeroU64>,
|
rpc_secret_key_id: u64,
|
||||||
/// None if the public url was used.
|
/// 0 if the public url was used.
|
||||||
rpc_key_user_id: Option<NonZeroU64>,
|
rpc_key_user_id: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RpcQueryKey {
|
impl RpcQueryKey {
|
||||||
pub fn is_registered(&self) -> bool {
|
pub fn is_registered(&self) -> bool {
|
||||||
self.rpc_key_user_id.is_some()
|
self.rpc_key_user_id != 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,8 +112,15 @@ impl RpcQueryStats {
|
|||||||
fn accounting_key(&self, period_seconds: i64) -> RpcQueryKey {
|
fn accounting_key(&self, period_seconds: i64) -> RpcQueryKey {
|
||||||
let response_timestamp = round_timestamp(self.response_timestamp, period_seconds);
|
let response_timestamp = round_timestamp(self.response_timestamp, period_seconds);
|
||||||
|
|
||||||
// TODO: change this to use 0 for anonymous queries
|
// it is very important that for anonymous users, rpc_secret_key_id is 0 and not NULL in the database
|
||||||
let rpc_secret_key_id = self.authorization.checks.rpc_secret_key_id;
|
// for unique indexes, sql sees each NULL as a unique value!
|
||||||
|
// but we want them grouped!
|
||||||
|
let rpc_secret_key_id = self
|
||||||
|
.authorization
|
||||||
|
.checks
|
||||||
|
.rpc_secret_key_id
|
||||||
|
.map(u64::from)
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
let method = self.method.clone();
|
let method = self.method.clone();
|
||||||
|
|
||||||
@ -129,7 +135,7 @@ impl RpcQueryStats {
|
|||||||
error_response: self.error_response,
|
error_response: self.error_response,
|
||||||
method,
|
method,
|
||||||
rpc_secret_key_id,
|
rpc_secret_key_id,
|
||||||
rpc_key_user_id: self.authorization.checks.user_id.try_into().ok(),
|
rpc_key_user_id: self.authorization.checks.user_id,
|
||||||
user_error_response,
|
user_error_response,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -141,8 +147,8 @@ impl RpcQueryStats {
|
|||||||
let method = self.method.clone();
|
let method = self.method.clone();
|
||||||
|
|
||||||
// everyone gets grouped together
|
// everyone gets grouped together
|
||||||
let rpc_secret_key_id = None;
|
let rpc_secret_key_id = 0;
|
||||||
let rpc_key_user_id = None;
|
let rpc_key_user_id = 0;
|
||||||
|
|
||||||
RpcQueryKey {
|
RpcQueryKey {
|
||||||
response_timestamp: self.response_timestamp,
|
response_timestamp: self.response_timestamp,
|
||||||
@ -161,6 +167,13 @@ impl RpcQueryStats {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let rpc_secret_key_id = self
|
||||||
|
.authorization
|
||||||
|
.checks
|
||||||
|
.rpc_secret_key_id
|
||||||
|
.map(u64::from)
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
let method = self.method.clone();
|
let method = self.method.clone();
|
||||||
|
|
||||||
let key = RpcQueryKey {
|
let key = RpcQueryKey {
|
||||||
@ -168,8 +181,8 @@ impl RpcQueryStats {
|
|||||||
archive_needed: self.archive_request,
|
archive_needed: self.archive_request,
|
||||||
error_response: self.error_response,
|
error_response: self.error_response,
|
||||||
method,
|
method,
|
||||||
rpc_secret_key_id: self.authorization.checks.rpc_secret_key_id,
|
rpc_secret_key_id,
|
||||||
rpc_key_user_id: self.authorization.checks.user_id.try_into().ok(),
|
rpc_key_user_id: self.authorization.checks.user_id,
|
||||||
user_error_response: self.user_error_response,
|
user_error_response: self.user_error_response,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -187,7 +200,7 @@ pub enum AppStat {
|
|||||||
// TODO: move to stat_buffer.rs?
|
// TODO: move to stat_buffer.rs?
|
||||||
impl BufferedRpcQueryStats {
|
impl BufferedRpcQueryStats {
|
||||||
#[instrument(level = "trace")]
|
#[instrument(level = "trace")]
|
||||||
async fn add(&mut self, stat: RpcQueryStats, approximate_balance_remaining: Decimal) {
|
async fn add(&mut self, stat: RpcQueryStats, approximate_balance_remaining: Option<Decimal>) {
|
||||||
// a stat always come from just 1 frontend request
|
// a stat always come from just 1 frontend request
|
||||||
self.frontend_requests += 1;
|
self.frontend_requests += 1;
|
||||||
|
|
||||||
@ -214,7 +227,10 @@ impl BufferedRpcQueryStats {
|
|||||||
self.paid_credits_used += stat.compute_unit_cost;
|
self.paid_credits_used += stat.compute_unit_cost;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.approximate_balance_remaining = approximate_balance_remaining;
|
if approximate_balance_remaining.is_some() {
|
||||||
|
// notice that we overwrite. we intentionally do not increment!
|
||||||
|
self.approximate_balance_remaining = approximate_balance_remaining;
|
||||||
|
}
|
||||||
|
|
||||||
trace!("added");
|
trace!("added");
|
||||||
}
|
}
|
||||||
@ -232,7 +248,8 @@ impl BufferedRpcQueryStats {
|
|||||||
// =============================== //
|
// =============================== //
|
||||||
let accounting_entry = rpc_accounting_v2::ActiveModel {
|
let accounting_entry = rpc_accounting_v2::ActiveModel {
|
||||||
id: sea_orm::NotSet,
|
id: sea_orm::NotSet,
|
||||||
rpc_key_id: sea_orm::Set(key.rpc_secret_key_id.map(Into::into)),
|
// eventually rpc_key_id will be `NOT NULL`, but we have old data in the db to deal with
|
||||||
|
rpc_key_id: sea_orm::Set(Some(key.rpc_secret_key_id)),
|
||||||
chain_id: sea_orm::Set(chain_id),
|
chain_id: sea_orm::Set(chain_id),
|
||||||
period_datetime: sea_orm::Set(period_datetime),
|
period_datetime: sea_orm::Set(period_datetime),
|
||||||
archive_needed: sea_orm::Set(key.archive_needed),
|
archive_needed: sea_orm::Set(key.archive_needed),
|
||||||
@ -335,7 +352,7 @@ impl BufferedRpcQueryStats {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: rename to owner_id?
|
// TODO: rename to owner_id?
|
||||||
let sender_user_id = key.rpc_key_user_id.map_or(0, |x| x.get());
|
let sender_user_id = key.rpc_key_user_id;
|
||||||
|
|
||||||
// save the statistics to the database:
|
// save the statistics to the database:
|
||||||
self._save_db_stats(chain_id, db_conn, &key).await?;
|
self._save_db_stats(chain_id, db_conn, &key).await?;
|
||||||
@ -479,12 +496,6 @@ impl BufferedRpcQueryStats {
|
|||||||
.field("sum_request_bytes", self.sum_request_bytes as i64)
|
.field("sum_request_bytes", self.sum_request_bytes as i64)
|
||||||
.field("sum_response_bytes", self.sum_response_bytes as i64)
|
.field("sum_response_bytes", self.sum_response_bytes as i64)
|
||||||
.field("sum_response_millis", self.sum_response_millis as i64)
|
.field("sum_response_millis", self.sum_response_millis as i64)
|
||||||
.field(
|
|
||||||
"balance",
|
|
||||||
self.approximate_balance_remaining
|
|
||||||
.to_f64()
|
|
||||||
.context("balance is really (too) large")?,
|
|
||||||
)
|
|
||||||
.field(
|
.field(
|
||||||
"sum_credits_used",
|
"sum_credits_used",
|
||||||
self.paid_credits_used
|
self.paid_credits_used
|
||||||
@ -498,9 +509,16 @@ impl BufferedRpcQueryStats {
|
|||||||
.context("sum_credits_used is really (too) large")?,
|
.context("sum_credits_used is really (too) large")?,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if let Some(balance) = self.approximate_balance_remaining {
|
||||||
|
builder = builder.field(
|
||||||
|
"balance",
|
||||||
|
balance.to_f64().context("balance is really (too) large")?,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: set the rpc_secret_key_id tag to 0 when anon? will that make other queries easier?
|
// TODO: set the rpc_secret_key_id tag to 0 when anon? will that make other queries easier?
|
||||||
if let Some(rpc_secret_key_id) = key.rpc_secret_key_id {
|
if key.rpc_secret_key_id != 0 {
|
||||||
builder = builder.tag("rpc_secret_key_id", rpc_secret_key_id.to_string());
|
builder = builder.tag("rpc_secret_key_id", key.rpc_secret_key_id.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
// [add "uniq" to the timestamp](https://docs.influxdata.com/influxdb/v2.0/write-data/best-practices/duplicate-points/#increment-the-timestamp)
|
// [add "uniq" to the timestamp](https://docs.influxdata.com/influxdb/v2.0/write-data/best-practices/duplicate-points/#increment-the-timestamp)
|
||||||
|
@ -31,7 +31,7 @@ pub struct BufferedRpcQueryStats {
|
|||||||
/// The user's balance at this point in time.
|
/// The user's balance at this point in time.
|
||||||
/// Multiple queries might be modifying it at once, so this is a copy of it when received
|
/// Multiple queries might be modifying it at once, so this is a copy of it when received
|
||||||
/// None if this is an unauthenticated request
|
/// None if this is an unauthenticated request
|
||||||
pub approximate_balance_remaining: Decimal,
|
pub approximate_balance_remaining: Option<Decimal>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(From)]
|
#[derive(From)]
|
||||||
@ -297,19 +297,17 @@ impl StatBuffer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let accounting_key = stat.accounting_key(self.billing_period_seconds);
|
let accounting_key = stat.accounting_key(self.billing_period_seconds);
|
||||||
if accounting_key.is_registered() {
|
|
||||||
let span = tracing::trace_span!(
|
let span =
|
||||||
"accounting",
|
tracing::trace_span!("accounting", key = tracing::field::debug(&accounting_key))
|
||||||
key = tracing::field::debug(&accounting_key)
|
.or_current();
|
||||||
)
|
|
||||||
.or_current();
|
self.accounting_db_buffer
|
||||||
self.accounting_db_buffer
|
.entry(accounting_key)
|
||||||
.entry(accounting_key)
|
.or_default()
|
||||||
.or_default()
|
.add(stat.clone(), None)
|
||||||
.add(stat.clone(), approximate_balance_remaining)
|
.instrument(span)
|
||||||
.instrument(span)
|
.await;
|
||||||
.await;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.influxdb_client.is_some() {
|
if self.influxdb_client.is_some() {
|
||||||
@ -322,7 +320,7 @@ impl StatBuffer {
|
|||||||
self.opt_in_timeseries_buffer
|
self.opt_in_timeseries_buffer
|
||||||
.entry(opt_in_timeseries_key)
|
.entry(opt_in_timeseries_key)
|
||||||
.or_default()
|
.or_default()
|
||||||
.add(stat.clone(), approximate_balance_remaining)
|
.add(stat.clone(), Some(approximate_balance_remaining))
|
||||||
.instrument(span)
|
.instrument(span)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
@ -336,7 +334,7 @@ impl StatBuffer {
|
|||||||
self.global_timeseries_buffer
|
self.global_timeseries_buffer
|
||||||
.entry(global_timeseries_key)
|
.entry(global_timeseries_key)
|
||||||
.or_default()
|
.or_default()
|
||||||
.add(stat, approximate_balance_remaining)
|
.add(stat, None)
|
||||||
.instrument(span)
|
.instrument(span)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
@ -92,11 +92,11 @@ async fn test_sum_credits_used() {
|
|||||||
|
|
||||||
// flush stats
|
// flush stats
|
||||||
let flushed = x.flush_stats().await.unwrap();
|
let flushed = x.flush_stats().await.unwrap();
|
||||||
// TODO: this was 2 when we flushed stats for anon users. that was temporarily disabled. it should be turned back on once indexes are fixed
|
assert_eq!(flushed.relational, 2, "relational");
|
||||||
assert_eq!(flushed.relational, 1, "relational");
|
|
||||||
// TODO: how many should this actually be?
|
|
||||||
assert_eq!(flushed.timeseries, 1, "timeseries");
|
assert_eq!(flushed.timeseries, 1, "timeseries");
|
||||||
|
|
||||||
|
// TODO: sleep and then flush and make sure no more arrive
|
||||||
|
|
||||||
// Give user wallet $1000
|
// Give user wallet $1000
|
||||||
admin_increase_balance(&x, &r, &admin_login_response, &user_wallet, 1000.into()).await;
|
admin_increase_balance(&x, &r, &admin_login_response, &user_wallet, 1000.into()).await;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user