-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* check duplicate opp before adding to db * handle count error * add .sqlx files * updates * address comments 2 * unncessary derefmut trait * fix the creation time setting * correct removal time heuristic
- Loading branch information
Showing
14 changed files
with
230 additions
and
84 deletions.
There are no files selected for viewing
31 changes: 0 additions & 31 deletions
31
...-server/.sqlx/query-0c6e459902a37a3d78256a096e52ca903521090ad82e60eb6efefa011ccacd8c.json
This file was deleted.
Oops, something went wrong.
32 changes: 32 additions & 0 deletions
32
...-server/.sqlx/query-56dc5a001c0b882716515a17a7da6f59183d35bbce11786e32100429c7655373.json
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
48 changes: 48 additions & 0 deletions
48
...-server/.sqlx/query-c08a28fafc4386e0406334e61dd2aba81d4c36dda279171bed39557f17358fa3.json
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
1 change: 1 addition & 0 deletions
1
auction-server/migrations/20241108172510_opportunity_last_creation_time.down.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
ALTER TABLE opportunity DROP COLUMN last_creation_time; |
3 changes: 3 additions & 0 deletions
3
auction-server/migrations/20241108172510_opportunity_last_creation_time.up.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
ALTER TABLE opportunity ADD COLUMN last_creation_time TIMESTAMP NOT NULL DEFAULT NOW(); | ||
ALTER TABLE opportunity ALTER COLUMN last_creation_time DROP DEFAULT; | ||
UPDATE opportunity SET last_creation_time = creation_time; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
134 changes: 115 additions & 19 deletions
134
auction-server/src/opportunity/repository/add_opportunity.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,66 +1,162 @@ | ||
use { | ||
super::{ | ||
models::OpportunityMetadata, | ||
models::{ | ||
self, | ||
OpportunityMetadata, | ||
OpportunityRemovalReason, | ||
}, | ||
InMemoryStore, | ||
Repository, | ||
}, | ||
crate::{ | ||
api::RestError, | ||
opportunity::{ | ||
entities, | ||
entities::Opportunity, | ||
opportunity::entities::{ | ||
self, | ||
Opportunity, | ||
}, | ||
}, | ||
sqlx::Postgres, | ||
sqlx::{ | ||
Postgres, | ||
QueryBuilder, | ||
}, | ||
time::{ | ||
OffsetDateTime, | ||
PrimitiveDateTime, | ||
}, | ||
uuid::Uuid, | ||
}; | ||
|
||
impl<T: InMemoryStore> Repository<T> { | ||
/// Add an opportunity to the system, in memory and in the database. | ||
/// | ||
/// The provided opportunity will be added to the in-memory store. If the opportunity already exists | ||
/// in the database, then it will be refreshed. Otherwise it will be added to the database. | ||
pub async fn add_opportunity( | ||
&self, | ||
db: &sqlx::Pool<Postgres>, | ||
opportunity: <T::Opportunity as entities::Opportunity>::OpportunityCreate, | ||
) -> Result<T::Opportunity, RestError> { | ||
let opportunity = self.get_or_add_db_opportunity(db, opportunity).await?; | ||
|
||
self.in_memory_store | ||
.opportunities | ||
.write() | ||
.await | ||
.entry(opportunity.get_key()) | ||
.or_insert_with(Vec::new) | ||
.push(opportunity.clone()); | ||
|
||
Ok(opportunity) | ||
} | ||
|
||
/// Gets the opportunity from the database if it exists, otherwise adds it. | ||
/// | ||
/// If the opportunity already exists in the database and hasn't become invalid in simulation, then | ||
/// it will be refreshed. Otherwise, the opportunity will be added to the database. | ||
async fn get_or_add_db_opportunity( | ||
&self, | ||
db: &sqlx::Pool<Postgres>, | ||
opportunity: <T::Opportunity as entities::Opportunity>::OpportunityCreate, | ||
) -> Result<T::Opportunity, RestError> { | ||
let chain_type = <T::Opportunity as entities::Opportunity>::ModelMetadata::get_chain_type(); | ||
let opportunity: T::Opportunity = | ||
<T::Opportunity as entities::Opportunity>::new_with_current_time(opportunity); | ||
let odt = OffsetDateTime::from_unix_timestamp_nanos(opportunity.creation_time * 1000) | ||
.expect("creation_time is valid"); | ||
let metadata = opportunity.get_models_metadata(); | ||
|
||
let result = sqlx::query!("SELECT id FROM opportunity WHERE permission_key = $1 AND chain_id = $2 AND chain_type = $3 AND sell_tokens = $4 AND buy_tokens = $5 AND metadata #- '{slot}' = $6 AND (removal_reason IS NULL OR removal_reason = $7) LIMIT 1", | ||
opportunity.permission_key.to_vec(), | ||
opportunity.chain_id, | ||
chain_type as _, | ||
serde_json::to_value(&opportunity.sell_tokens).expect("Failed to serialize sell_tokens"), | ||
serde_json::to_value(&opportunity.buy_tokens).expect("Failed to serialize buy_tokens"), | ||
self.remove_slot_field(serde_json::to_value(metadata).expect("Failed to serialize metadata")), | ||
OpportunityRemovalReason::Expired as _) | ||
.fetch_optional(db) | ||
.await | ||
.map_err(|e| { | ||
tracing::error!("DB: Failed to check duplicate opportunity: {}", e); | ||
RestError::TemporarilyUnavailable | ||
})?; | ||
|
||
match result { | ||
Some(record) => self.refresh_db_opportunity(db, record.id).await, | ||
None => self.add_db_opportunity(db, opportunity).await, | ||
} | ||
} | ||
|
||
fn remove_slot_field(&self, mut metadata: serde_json::Value) -> serde_json::Value { | ||
if let Some(obj) = metadata.as_object_mut() { | ||
obj.remove("slot"); | ||
} | ||
metadata | ||
} | ||
|
||
/// Refresh an opportunity that already exists in the database. | ||
/// | ||
/// This will update the last creation time of the opportunity and remove the removal reason and time. | ||
async fn refresh_db_opportunity( | ||
&self, | ||
db: &sqlx::Pool<Postgres>, | ||
id: Uuid, | ||
) -> Result<T::Opportunity, RestError> { | ||
let odt_creation = OffsetDateTime::now_utc(); | ||
|
||
let mut query = QueryBuilder::new("UPDATE opportunity SET removal_reason = NULL, removal_time = NULL, last_creation_time = "); | ||
query.push_bind(PrimitiveDateTime::new( | ||
odt_creation.date(), | ||
odt_creation.time(), | ||
)); | ||
query.push(" WHERE id = "); | ||
query.push_bind(id); | ||
query.push(" RETURNING *"); | ||
let opportunity: models::Opportunity< | ||
<T::Opportunity as entities::Opportunity>::ModelMetadata, | ||
> = query.build_query_as().fetch_one(db).await.map_err(|e| { | ||
tracing::error!("DB: Failed to refresh opportunity {}: {}", id, e); | ||
RestError::TemporarilyUnavailable | ||
})?; | ||
|
||
opportunity.clone().try_into().map_err(|_| { | ||
tracing::error!( | ||
"Failed to convert database opportunity to entity opportunity: {:?}", | ||
opportunity | ||
); | ||
RestError::TemporarilyUnavailable | ||
}) | ||
} | ||
|
||
async fn add_db_opportunity( | ||
&self, | ||
db: &sqlx::Pool<Postgres>, | ||
opportunity: <T as InMemoryStore>::Opportunity, | ||
) -> Result<T::Opportunity, RestError> { | ||
let chain_type = <T::Opportunity as entities::Opportunity>::ModelMetadata::get_chain_type(); | ||
let metadata = opportunity.get_models_metadata(); | ||
sqlx::query!("INSERT INTO opportunity (id, | ||
creation_time, | ||
last_creation_time, | ||
permission_key, | ||
chain_id, | ||
chain_type, | ||
metadata, | ||
sell_tokens, | ||
buy_tokens) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)", | ||
buy_tokens) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", | ||
opportunity.id, | ||
PrimitiveDateTime::new(odt.date(), odt.time()), | ||
PrimitiveDateTime::new(opportunity.creation_time.date(), opportunity.creation_time.time()), | ||
PrimitiveDateTime::new(opportunity.creation_time.date(), opportunity.creation_time.time()), | ||
opportunity.permission_key.to_vec(), | ||
opportunity.chain_id, | ||
chain_type as _, | ||
serde_json::to_value(metadata).expect("Failed to serialize metadata"), | ||
serde_json::to_value(&opportunity.sell_tokens).unwrap(), | ||
serde_json::to_value(&opportunity.buy_tokens).unwrap()) | ||
serde_json::to_value(&opportunity.sell_tokens).expect("Failed to serialize sell_tokens"), | ||
serde_json::to_value(&opportunity.buy_tokens).expect("Failed to serialize buy_tokens")) | ||
.execute(db) | ||
.await | ||
.map_err(|e| { | ||
tracing::error!("DB: Failed to insert opportunity: {}", e); | ||
RestError::TemporarilyUnavailable | ||
})?; | ||
|
||
self.in_memory_store | ||
.opportunities | ||
.write() | ||
.await | ||
.entry(opportunity.get_key()) | ||
.or_insert_with(Vec::new) | ||
.push(opportunity.clone()); | ||
|
||
Ok(opportunity) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.