Change balance to record total spend and total deposits (#109)

* lets test total deposit and total spent

* removed referrer from cache for performance reasons
This commit is contained in:
David 2023-06-07 23:45:57 +02:00 committed by GitHub
parent 4f7144abc6
commit de7d8919d3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 160 additions and 62 deletions

@ -1,19 +1,20 @@
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.10.6
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[sea_orm(table_name = "balance")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: i32,
#[sea_orm(column_type = "Decimal(Some((20, 10)))")]
pub available_balance: Decimal,
#[sea_orm(column_type = "Decimal(Some((20, 10)))")]
pub used_balance: Decimal,
#[sea_orm(unique)]
pub user_id: u64,
#[sea_orm(column_type = "Decimal(Some((20, 10)))")]
pub total_spent_including_free_tier: Decimal,
#[sea_orm(column_type = "Decimal(Some((20, 10)))")]
pub total_spent_outside_free_tier: Decimal,
#[sea_orm(column_type = "Decimal(Some((20, 10)))")]
pub total_deposits: Decimal,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]

@ -28,6 +28,7 @@ mod m20230422_172555_premium_downgrade_logic;
mod m20230511_161214_remove_columns_statsv2_origin_and_method;
mod m20230512_220213_allow_null_rpc_key_id_in_stats_v2;
mod m20230514_114803_admin_add_credits;
mod m20230607_221917_total_deposits;
pub struct Migrator;
@ -63,6 +64,7 @@ impl MigratorTrait for Migrator {
Box::new(m20230511_161214_remove_columns_statsv2_origin_and_method::Migration),
Box::new(m20230512_220213_allow_null_rpc_key_id_in_stats_v2::Migration),
Box::new(m20230514_114803_admin_add_credits::Migration),
Box::new(m20230607_221917_total_deposits::Migration),
]
}
}

@ -0,0 +1,76 @@
use sea_orm_migration::prelude::*;
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.alter_table(
Table::alter()
.table(Balance::Table)
.add_column(
ColumnDef::new(Balance::TotalSpentOutsideFreeTier)
.decimal_len(20, 10)
.not_null()
.default(0.0),
)
.add_column(
ColumnDef::new(Balance::TotalDeposits)
.decimal_len(20, 10)
.not_null()
.default(0.0),
)
.rename_column(Balance::UsedBalance, Balance::TotalSpentIncludingFreeTier)
.drop_column(Balance::AvailableBalance)
.to_owned(),
)
.await
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
// Remove the column again I suppose, but this will delete data, needless to say
manager
.alter_table(
Table::alter()
.table(Balance::Table)
.rename_column(Balance::TotalSpentIncludingFreeTier, Balance::UsedBalance)
.drop_column(Balance::TotalSpentOutsideFreeTier)
.drop_column(Balance::TotalDeposits)
.add_column(
ColumnDef::new(Balance::AvailableBalance)
.decimal_len(20, 10)
.not_null()
.default(0.0),
)
.add_column(
ColumnDef::new(Balance::UsedBalance)
.decimal_len(20, 10)
.not_null()
.default(0.0),
)
.to_owned(),
)
.await
}
}
/// Learn more at https://docs.rs/sea-query#iden
#[derive(Iden)]
enum User {
Table,
Id,
}
#[derive(Iden)]
enum Balance {
Table,
Id,
UserId,
TotalSpentIncludingFreeTier,
TotalSpentOutsideFreeTier,
TotalDeposits,
AvailableBalance,
UsedBalance,
}

@ -120,7 +120,7 @@ pub async fn admin_increase_balance(
// update balance
let balance_entry = balance::ActiveModel {
id: sea_orm::NotSet,
available_balance: sea_orm::Set(amount),
total_deposits: sea_orm::Set(amount),
user_id: sea_orm::Set(user_entry.id),
..Default::default()
};
@ -128,8 +128,8 @@ pub async fn admin_increase_balance(
.on_conflict(
OnConflict::new()
.values([(
balance::Column::AvailableBalance,
Expr::col(balance::Column::AvailableBalance).add(amount),
balance::Column::TotalDeposits,
Expr::col(balance::Column::TotalDeposits).add(amount),
)])
.to_owned(),
)

@ -7,7 +7,7 @@ use crate::jsonrpc::{JsonRpcForwardedResponse, JsonRpcRequest};
use crate::rpcs::one::Web3Rpc;
use crate::stats::{AppStat, BackendRequests, RpcQueryStats};
use crate::user_token::UserBearerToken;
use anyhow::{Context};
use anyhow::Context;
use axum::headers::authorization::Bearer;
use axum::headers::{Header, Origin, Referer, UserAgent};
use chrono::Utc;
@ -1151,7 +1151,7 @@ impl Web3ProxyApp {
.one(db_replica.as_ref())
.await?
{
Some(x) => x.available_balance,
Some(x) => x.total_deposits - x.total_spent_outside_free_tier,
None => Decimal::default(),
};
Ok(Arc::new(RwLock::new(balance)))
@ -1256,7 +1256,7 @@ impl Web3ProxyApp {
.filter(balance::Column::UserId.eq(user_model.id))
.one(db_replica.as_ref())
.await? {
Some(x) => x.available_balance,
Some(x) => x.total_deposits - x.total_spent_outside_free_tier,
None => Decimal::default()
};

@ -298,8 +298,6 @@ pub async fn user_login_post(
// We should also create the balance entry ...
let user_balance = balance::ActiveModel {
user_id: sea_orm::Set(caller.id),
available_balance: sea_orm::Set(Decimal::new(0, 0)),
used_balance: sea_orm::Set(Decimal::new(0, 0)),
..Default::default()
};
user_balance.insert(&txn).await?;

@ -49,7 +49,7 @@ pub async fn user_balance_get(
.filter(balance::Column::UserId.eq(_user.id))
.one(db_replica.as_ref())
.await?
.map(|x| x.available_balance)
.map(|x| x.total_deposits - x.total_spent_outside_free_tier)
.unwrap_or_default();
let response = json!({
@ -257,7 +257,7 @@ pub async fn user_balance_post(
// create or update the balance
let balance_entry = balance::ActiveModel {
id: sea_orm::NotSet,
available_balance: sea_orm::Set(payment_token_amount),
total_deposits: sea_orm::Set(payment_token_amount),
user_id: sea_orm::Set(recipient.id),
..Default::default()
};
@ -265,8 +265,8 @@ pub async fn user_balance_post(
.on_conflict(
OnConflict::new()
.values([(
balance::Column::AvailableBalance,
Expr::col(balance::Column::AvailableBalance).add(payment_token_amount),
balance::Column::TotalDeposits,
Expr::col(balance::Column::TotalDeposits).add(payment_token_amount),
)])
.to_owned(),
)

@ -294,8 +294,6 @@ pub async fn modify_subuser(
// We should also create the balance entry ...
let subuser_balance = balance::ActiveModel {
user_id: sea_orm::Set(subuser.id),
available_balance: sea_orm::Set(Decimal::new(0, 0)),
used_balance: sea_orm::Set(Decimal::new(0, 0)),
..Default::default()
};
subuser_balance.insert(&txn).await?;

@ -185,10 +185,11 @@ impl RpcQueryStats {
}
struct Deltas {
sender_available_balance_delta: Decimal,
sender_used_balance_delta: Decimal,
balance_used_outside_free_tier: Decimal,
balance_used_including_free_tier: Decimal,
sender_bonus_applied: bool,
referrer_available_balance_delta: Decimal,
referrer_deposit_delta: Decimal,
sender_bonus_balance_deposited: Decimal,
}
/// A stat that we aggregate and then store in a database.
@ -370,14 +371,16 @@ impl BufferedRpcQueryStats {
async fn _compute_balance_deltas(
&self,
sender_balance: balance::Model,
referral_objects: Option<(referee::Model, referrer::Model)>,
) -> Web3ProxyResult<(Deltas, Option<(referee::Model, referrer::Model)>)> {
// Calculate Balance Only
let mut deltas = Deltas {
sender_available_balance_delta: -self.sum_credits_used,
sender_used_balance_delta: self.sum_credits_used,
balance_used_outside_free_tier: Default::default(),
balance_used_including_free_tier: Default::default(),
sender_bonus_applied: false,
referrer_available_balance_delta: Decimal::from(0),
referrer_deposit_delta: Default::default(),
sender_bonus_balance_deposited: Default::default(),
};
// Calculate a bunch using referrals as well
@ -393,7 +396,7 @@ impl BufferedRpcQueryStats {
+ self.sum_credits_used)
>= Decimal::from(100)
{
deltas.sender_available_balance_delta += Decimal::from(100);
deltas.sender_bonus_balance_deposited += Decimal::from(10);
deltas.sender_bonus_applied = true;
}
@ -404,13 +407,23 @@ impl BufferedRpcQueryStats {
+ Months::new(12);
if now <= valid_until {
deltas.referrer_available_balance_delta +=
self.sum_credits_used / Decimal::new(10, 0);
deltas.referrer_deposit_delta += self.sum_credits_used / Decimal::new(10, 0);
}
return Ok((deltas, Some((referral_entity, referrer_code_entity))));
}
let user_balance = (sender_balance.total_deposits + deltas.sender_bonus_balance_deposited
- sender_balance.total_spent_outside_free_tier);
// Split up the component of into how much of the paid component was used, and how much of the free component was used (anything after "balance")
if user_balance >= Decimal::from(0) {
deltas.balance_used_outside_free_tier = self.sum_credits_used;
} else {
deltas.balance_used_outside_free_tier =
user_balance + deltas.sender_bonus_balance_deposited;
deltas.balance_used_including_free_tier = self.sum_credits_used;
}
Ok((deltas, None))
}
@ -425,8 +438,9 @@ impl BufferedRpcQueryStats {
// Do the user updates
let user_balance = balance::ActiveModel {
id: sea_orm::NotSet,
available_balance: sea_orm::Set(deltas.sender_available_balance_delta),
used_balance: sea_orm::Set(deltas.sender_used_balance_delta),
total_deposits: sea_orm::Set(deltas.sender_bonus_balance_deposited),
total_spent_including_free_tier: sea_orm::Set(deltas.balance_used_including_free_tier),
total_spent_outside_free_tier: sea_orm::Set(deltas.balance_used_outside_free_tier),
user_id: sea_orm::Set(sender_rpc_entity.user_id),
};
@ -435,14 +449,19 @@ impl BufferedRpcQueryStats {
OnConflict::new()
.values([
(
balance::Column::AvailableBalance,
Expr::col(balance::Column::AvailableBalance)
.add(deltas.sender_available_balance_delta),
balance::Column::TotalDeposits,
Expr::col(balance::Column::TotalDeposits)
.add(deltas.sender_bonus_balance_deposited),
),
(
balance::Column::UsedBalance,
Expr::col(balance::Column::UsedBalance)
.add(deltas.sender_used_balance_delta),
balance::Column::TotalSpentIncludingFreeTier,
Expr::col(balance::Column::TotalSpentIncludingFreeTier)
.add(deltas.balance_used_including_free_tier),
),
(
balance::Column::TotalSpentOutsideFreeTier,
Expr::col(balance::Column::TotalSpentOutsideFreeTier)
.add(deltas.balance_used_outside_free_tier),
),
])
.to_owned(),
@ -452,7 +471,7 @@ impl BufferedRpcQueryStats {
// Do the referrer_entry updates
if let Some((referral_entity, referrer_code_entity)) = referral_objects {
if deltas.referrer_available_balance_delta > Decimal::from(0) {
if deltas.referrer_deposit_delta > Decimal::from(0) {
let referee_entry = referee::ActiveModel {
id: sea_orm::Unchanged(referral_entity.id),
referral_start_date: sea_orm::Unchanged(referral_entity.referral_start_date),
@ -460,9 +479,7 @@ impl BufferedRpcQueryStats {
user_id: sea_orm::Unchanged(referral_entity.user_id),
credits_applied_for_referee: sea_orm::Set(deltas.sender_bonus_applied),
credits_applied_for_referrer: sea_orm::Set(
deltas.referrer_available_balance_delta,
),
credits_applied_for_referrer: sea_orm::Set(deltas.referrer_deposit_delta),
};
referee::Entity::insert(referee_entry)
.on_conflict(
@ -477,7 +494,7 @@ impl BufferedRpcQueryStats {
(
referee::Column::CreditsAppliedForReferrer,
Expr::col(referee::Column::CreditsAppliedForReferrer)
.add(deltas.referrer_available_balance_delta),
.add(deltas.referrer_deposit_delta),
),
])
.to_owned(),
@ -487,18 +504,18 @@ impl BufferedRpcQueryStats {
let user_balance = balance::ActiveModel {
id: sea_orm::NotSet,
available_balance: sea_orm::Set(deltas.referrer_available_balance_delta),
used_balance: sea_orm::Set(Decimal::from(0)),
total_deposits: sea_orm::Set(deltas.referrer_deposit_delta),
user_id: sea_orm::Set(referral_entity.user_id),
..Default::default()
};
let _ = balance::Entity::insert(user_balance)
.on_conflict(
OnConflict::new()
.values([(
balance::Column::AvailableBalance,
Expr::col(balance::Column::AvailableBalance)
.add(deltas.referrer_available_balance_delta),
balance::Column::TotalDeposits,
Expr::col(balance::Column::TotalDeposits)
.add(deltas.referrer_deposit_delta),
)])
.to_owned(),
)
@ -542,7 +559,9 @@ impl BufferedRpcQueryStats {
let mut latest_balance = sender_latest_balance.write().await;
let balance_before = *latest_balance;
// Now modify the balance
*latest_balance += deltas.sender_available_balance_delta;
// TODO: Double check this (perhaps while testing...)
*latest_balance = *latest_balance - deltas.balance_used_outside_free_tier
+ deltas.sender_bonus_balance_deposited;
if *latest_balance < Decimal::from(0) {
*latest_balance = Decimal::from(0);
}
@ -568,18 +587,21 @@ impl BufferedRpcQueryStats {
// ==================
// Modify referrer balance
// ==================
// If the referrer object is empty, we don't care about the cache, becase this will be fetched in a next request from the database
if let Some((referral_entity, _)) = referral_objects {
if let Ok(referrer_user_id) = NonZeroU64::try_from(referral_entity.user_id) {
// If the referrer object is in the cache, we just remove it from the balance cache; it will be reloaded next time
// Get all the RPC keys, delete them from cache
// In principle, do not remove the cache for the referrer; the next reload will trigger premium
// We don't touch the RPC keys at this stage for the refferer, a payment must be paid to reset those (we want to keep things simple here)
// Anyways, the RPC keys will be updated in 5 min (600 seconds)
user_balance_cache.remove(&referrer_user_id);
}
};
// We ignore this for performance reasons right now
// We would have to load all the RPC keys of the referrer to de-activate them
// Instead, it's fine if they wait for 60 seconds until their tier reloads
// // If the referrer object is empty, we don't care about the cache, becase this will be fetched in a next request from the database
// if let Some((referral_entity, _)) = referral_objects {
// if let Ok(referrer_user_id) = NonZeroU64::try_from(referral_entity.user_id) {
// // If the referrer object is in the cache, we just remove it from the balance cache; it will be reloaded next time
// // Get all the RPC keys, delete them from cache
//
// // In principle, do not remove the cache for the referrer; the next reload will trigger premium
// // We don't touch the RPC keys at this stage for the refferer, a payment must be paid to reset those (we want to keep things simple here)
// // Anyways, the RPC keys will be updated in 5 min (600 seconds)
// user_balance_cache.remove(&referrer_user_id);
// }
// };
Ok(())
}
@ -627,8 +649,9 @@ impl BufferedRpcQueryStats {
self._get_relevant_entities(rpc_secret_key_id, &txn).await?;
// Compute Changes in balance for user and referrer, incl. referral logic //
let (deltas, referral_objects): (Deltas, Option<(referee::Model, referrer::Model)>) =
self._compute_balance_deltas(referral_objects).await?;
let (deltas, referral_objects): (Deltas, Option<(referee::Model, referrer::Model)>) = self
._compute_balance_deltas(_sender_balance, referral_objects)
.await?;
// Update balances in the database
self._update_balances_in_db(&deltas, &txn, &sender_rpc_entity, &referral_objects)