Skip to main content

nautilus_infrastructure/sql/
cache.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16use std::{collections::VecDeque, fmt::Debug, ops::ControlFlow, pin::Pin, time::Duration};
17
18use ahash::AHashMap;
19use bytes::Bytes;
20use nautilus_common::{
21    cache::{
22        CacheConfig,
23        database::{CacheDatabaseAdapter, CacheDatabaseFactory, CacheMap},
24    },
25    live::get_runtime,
26    logging::{log_task_awaiting, log_task_started, log_task_stopped},
27    signal::Signal,
28};
29use nautilus_core::{UUID4, UnixNanos};
30use nautilus_model::{
31    accounts::AccountAny,
32    data::{Bar, CustomData, DataType, FundingRateUpdate, InstrumentClose, QuoteTick, TradeTick},
33    events::{
34        AccountState, OrderEventAny, OrderFilled, OrderInitialized, OrderSnapshot,
35        position::snapshot::PositionSnapshot,
36    },
37    identifiers::{
38        AccountId, ActorId, ClientId, ClientOrderId, InstrumentId, PositionId, StrategyId,
39        TraderId, VenueOrderId,
40    },
41    instruments::{Instrument, InstrumentAny, SyntheticInstrument},
42    orderbook::OrderBook,
43    orders::{Order, OrderAny},
44    position::Position,
45    types::{Currency, Money},
46};
47use serde::{Deserialize, Serialize};
48use sqlx::{PgPool, postgres::PgConnectOptions};
49use tokio::{time::Instant, try_join};
50use ustr::Ustr;
51
52use crate::sql::{
53    pg::{connect_pg, get_postgres_connect_options},
54    queries::DatabaseQueries,
55};
56
57// Task and connection names
58const CACHE_PROCESS: &str = "cache-process";
59const SCHEMA_MIGRATION_COMMAND: &str = "run `nautilus database init` to migrate";
60
61/// Configuration for a Postgres-backed cache database.
62///
63/// Missing fields are resolved from Postgres environment variables and then built-in defaults.
64#[cfg_attr(
65    feature = "python",
66    expect(
67        clippy::unsafe_derive_deserialize,
68        reason = "config deserializes plain fields; unsafe methods come from generated PyO3 integration"
69    )
70)]
71#[derive(Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
72#[serde(default, deny_unknown_fields)]
73#[cfg_attr(
74    feature = "python",
75    pyo3::pyclass(module = "nautilus_trader.infrastructure", from_py_object)
76)]
77#[cfg_attr(
78    feature = "python",
79    pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.infrastructure")
80)]
81pub struct PostgresCacheConfig {
82    /// The Postgres host address.
83    pub host: Option<String>,
84    /// The Postgres port.
85    pub port: Option<u16>,
86    /// The Postgres account username.
87    pub username: Option<String>,
88    /// The Postgres account password.
89    pub password: Option<String>,
90    /// The Postgres database name.
91    pub database: Option<String>,
92}
93
94impl Debug for PostgresCacheConfig {
95    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
96        let redacted = self.password.as_ref().map(|_| "***");
97        f.debug_struct(stringify!(PostgresCacheConfig))
98            .field("host", &self.host)
99            .field("port", &self.port)
100            .field("username", &self.username)
101            .field("password", &redacted)
102            .field("database", &self.database)
103            .finish()
104    }
105}
106
107#[cfg(test)]
108mod tests {
109    use rstest::rstest;
110    use serde_json::json;
111
112    use super::*;
113
114    #[rstest]
115    fn test_default_postgres_cache_config() {
116        let config = PostgresCacheConfig::default();
117
118        assert_eq!(config.host, None);
119        assert_eq!(config.port, None);
120        assert_eq!(config.username, None);
121        assert_eq!(config.password, None);
122        assert_eq!(config.database, None);
123    }
124
125    #[rstest]
126    fn test_deserialize_postgres_cache_config() {
127        let config_json = json!({
128            "host": "localhost",
129            "port": 5432,
130            "username": "user",
131            "password": "pass",
132            "database": "nautilus"
133        });
134
135        let config: PostgresCacheConfig = serde_json::from_value(config_json).unwrap();
136
137        assert_eq!(config.host, Some("localhost".to_string()));
138        assert_eq!(config.port, Some(5432));
139        assert_eq!(config.username, Some("user".to_string()));
140        assert_eq!(config.password, Some("pass".to_string()));
141        assert_eq!(config.database, Some("nautilus".to_string()));
142    }
143
144    #[rstest]
145    fn test_deserialize_postgres_cache_config_rejects_type_selector() {
146        let config_json = json!({
147            "type": "postgres",
148        });
149
150        let error = serde_json::from_value::<PostgresCacheConfig>(config_json).unwrap_err();
151
152        assert!(error.to_string().contains("unknown field `type`"));
153    }
154}
155
156#[async_trait::async_trait]
157impl CacheDatabaseFactory for PostgresCacheConfig {
158    async fn create(
159        &self,
160        _trader_id: TraderId,
161        _instance_id: UUID4,
162        _config: CacheConfig,
163    ) -> anyhow::Result<Box<dyn CacheDatabaseAdapter>> {
164        let database = PostgresCacheDatabase::connect(
165            self.host.clone(),
166            self.port,
167            self.username.clone(),
168            self.password.clone(),
169            self.database.clone(),
170        )
171        .await?;
172        Ok(Box::new(database))
173    }
174}
175
176#[derive(Debug)]
177#[cfg_attr(
178    feature = "python",
179    pyo3::pyclass(module = "nautilus_trader.infrastructure")
180)]
181pub struct PostgresCacheDatabase {
182    pub pool: PgPool,
183    tx: tokio::sync::mpsc::UnboundedSender<DatabaseQuery>,
184    handle: tokio::task::JoinHandle<()>,
185}
186
187#[allow(
188    clippy::large_enum_variant,
189    reason = "variant sizes vary with feature unification; allow stays silent when the lint does not fire"
190)]
191#[derive(Debug, Clone)]
192pub enum DatabaseQuery {
193    Close,
194    Add(String, Vec<u8>),
195    AddCurrency(Currency),
196    AddInstrument(InstrumentAny),
197    AddInstrumentClose(InstrumentClose),
198    AddOrder(OrderInitialized, Option<ClientId>),
199    AddOrderSnapshot(OrderSnapshot),
200    AddPosition(PositionId, OrderFilled),
201    AddPositionSnapshot(PositionSnapshot),
202    AddAccount(AccountState, bool),
203    AddSignal(Signal),
204    AddCustom(CustomData),
205    AddQuote(QuoteTick),
206    AddTrade(TradeTick),
207    AddBar(Bar),
208    UpdateOrder(OrderEventAny),
209    UpdatePosition(OrderFilled),
210    IndexOrderPosition(ClientOrderId, PositionId),
211    IndexOrderClients(Vec<(ClientOrderId, ClientId)>),
212}
213
214impl PostgresCacheDatabase {
215    /// Connects to the Postgres cache database using the provided connection parameters.
216    ///
217    /// # Errors
218    ///
219    /// Returns an error if establishing the database connection fails.
220    ///
221    /// # Panics
222    ///
223    /// Panics if the internal Postgres pool connection attempt (`connect_pg`) unwraps on error.
224    pub async fn connect(
225        host: Option<String>,
226        port: Option<u16>,
227        username: Option<String>,
228        password: Option<String>,
229        database: Option<String>,
230    ) -> Result<Self, sqlx::Error> {
231        let pg_connect_options =
232            get_postgres_connect_options(host, port, username, password, database);
233        let pool = connect_pg(pg_connect_options.clone().into()).await.unwrap();
234        check_schema_migrated(&pool).await?;
235        let (tx, rx) = tokio::sync::mpsc::unbounded_channel::<DatabaseQuery>();
236
237        let handle = get_runtime().spawn(async move {
238            Box::pin(Self::process_commands(
239                rx,
240                pg_connect_options.clone().into(),
241            ))
242            .await;
243        });
244        Ok(Self { pool, tx, handle })
245    }
246
247    async fn process_commands(
248        mut rx: tokio::sync::mpsc::UnboundedReceiver<DatabaseQuery>,
249        pg_connect_options: PgConnectOptions,
250    ) {
251        log_task_started(CACHE_PROCESS);
252
253        let pool = connect_pg(pg_connect_options).await.unwrap();
254
255        // Buffering
256        let mut buffer: VecDeque<DatabaseQuery> = VecDeque::new();
257
258        // TODO: expose this via configuration once tests are fixed
259        let buffer_interval = Duration::from_millis(0);
260
261        // A sleep used to trigger periodic flushing of the buffer.
262        // When `buffer_interval` is zero we skip using the timer and flush immediately
263        // after every message.
264        let flush_timer = tokio::time::sleep(buffer_interval);
265        tokio::pin!(flush_timer);
266
267        // Continue to receive and handle messages until channel is hung up
268        loop {
269            tokio::select! {
270                maybe_msg = rx.recv() => {
271                    let result = Box::pin(handle_query(
272                        maybe_msg,
273                        &mut buffer,
274                        buffer_interval,
275                        &pool,
276                    ))
277                    .await;
278
279                    if result.is_break() {
280                        break;
281                    }
282                }
283                () = &mut flush_timer, if !buffer_interval.is_zero() => {
284                    flush_buffer(&mut buffer, &pool, &mut flush_timer, buffer_interval).await;
285                }
286            }
287        }
288
289        if !buffer.is_empty() {
290            drain_buffer(&pool, &mut buffer).await;
291        }
292
293        log_task_stopped(CACHE_PROCESS);
294    }
295}
296
297// Fails fast when the connected database predates required cache schema.
298//
299// An absent instrument-close table otherwise fails only when first queried. Both directions of the
300// exact-average type mismatch are silent: `numeric -> double precision` is an implicit cast so
301// writes truncate, and the row readers use `.ok().flatten()` so reads degrade to `None`. Absent
302// order event columns are silent in both directions too: every insert names them so writes fail
303// and drop the event, while every decoder reads them so `load_orders` yields an empty cache.
304// Missing instrument metadata also breaks inserts and panics in the existing row dispatch on load.
305async fn check_schema_migrated(pool: &PgPool) -> Result<(), sqlx::Error> {
306    let has_instrument_close: bool = sqlx::query_scalar(
307        "SELECT EXISTS (
308            SELECT 1
309            FROM information_schema.tables
310            WHERE table_schema = current_schema()
311              AND table_name = 'instrument_close'
312        )",
313    )
314    .fetch_one(pool)
315    .await?;
316
317    if !has_instrument_close {
318        return Err(sqlx::Error::Configuration(
319            format!(
320                "Postgres schema is out of date, missing `instrument_close` table: {SCHEMA_MIGRATION_COMMAND}"
321            )
322            .into(),
323        ));
324    }
325
326    let stale: Vec<String> = sqlx::query_scalar(
327        "SELECT table_name || '.' || column_name || ' (' || data_type || ')'
328        FROM information_schema.columns
329        WHERE table_schema = current_schema()
330          AND (table_name, column_name) IN (('order', 'avg_px'), ('order', 'slippage'))
331          AND data_type <> 'numeric'
332        ORDER BY table_name, column_name",
333    )
334    .fetch_all(pool)
335    .await?;
336
337    if !stale.is_empty() {
338        return Err(sqlx::Error::Configuration(
339            format!(
340                "Postgres schema is out of date, {} should be `numeric`: {SCHEMA_MIGRATION_COMMAND}",
341                stale.join(", "),
342            )
343            .into(),
344        ));
345    }
346
347    let missing: Vec<String> = sqlx::query_scalar(
348        "SELECT required.table_name || '.' || required.column_name
349        FROM (VALUES
350            ('instrument', 'info'),
351            ('order_event', 'released_price'),
352            ('order_event', 'protection_price'),
353            ('order_event', 'due_post_only'),
354            ('order_event', 'correction_id'),
355            ('order_event', 'is_reopened'),
356            ('order_event', 'info'),
357            ('order_event', 'causation_id'),
358            ('position_event', 'reconciliation'),
359            ('position_event', 'info'),
360            ('position_event', 'causation_id')
361        ) AS required(table_name, column_name)
362        WHERE NOT EXISTS (
363            SELECT 1
364            FROM information_schema.columns
365            WHERE table_schema = current_schema()
366              AND table_name = required.table_name
367              AND column_name = required.column_name
368        )
369        ORDER BY 1",
370    )
371    .fetch_all(pool)
372    .await?;
373
374    if missing.is_empty() {
375        return Ok(());
376    }
377
378    Err(sqlx::Error::Configuration(
379        format!(
380            "Postgres schema is out of date, missing cache columns {}: {SCHEMA_MIGRATION_COMMAND}",
381            missing.join(", "),
382        )
383        .into(),
384    ))
385}
386
387async fn handle_query(
388    maybe_msg: Option<DatabaseQuery>,
389    buffer: &mut VecDeque<DatabaseQuery>,
390    buffer_interval: Duration,
391    pool: &PgPool,
392) -> ControlFlow<()> {
393    let Some(msg) = maybe_msg else {
394        log::debug!("Command channel closed");
395        return ControlFlow::Break(());
396    };
397
398    if matches!(msg, DatabaseQuery::Close) {
399        if !buffer.is_empty() {
400            drain_buffer(pool, buffer).await;
401        }
402        return ControlFlow::Break(());
403    }
404
405    buffer.push_back(msg);
406
407    if buffer_interval.is_zero() {
408        drain_buffer(pool, buffer).await;
409    }
410
411    ControlFlow::Continue(())
412}
413
414async fn flush_buffer(
415    buffer: &mut VecDeque<DatabaseQuery>,
416    pool: &PgPool,
417    flush_timer: &mut Pin<&mut tokio::time::Sleep>,
418    buffer_interval: Duration,
419) {
420    if !buffer.is_empty() {
421        drain_buffer(pool, buffer).await;
422    }
423    flush_timer.as_mut().reset(Instant::now() + buffer_interval);
424}
425
426/// Retrieves a `PostgresCacheDatabase` using default connection options.
427///
428/// # Errors
429///
430/// Returns an error if connecting to the database or initializing the cache adapter fails.
431pub async fn get_pg_cache_database() -> anyhow::Result<PostgresCacheDatabase> {
432    let connect_options = get_postgres_connect_options(None, None, None, None, None);
433    Ok(PostgresCacheDatabase::connect(
434        Some(connect_options.host),
435        Some(connect_options.port),
436        Some(connect_options.username),
437        Some(connect_options.password),
438        Some(connect_options.database),
439    )
440    .await?)
441}
442
443#[async_trait::async_trait]
444impl CacheDatabaseAdapter for PostgresCacheDatabase {
445    fn close(&mut self) -> anyhow::Result<()> {
446        let pool = self.pool.clone();
447        let (tx, rx) = std::sync::mpsc::channel();
448
449        // The close command follows all pending writes on the FIFO channel and drains the buffer
450        if let Err(e) = self.tx.send(DatabaseQuery::Close) {
451            log::warn!("Error sending close: {e:?}");
452        }
453
454        log_task_awaiting("cache-write");
455
456        tokio::task::block_in_place(|| {
457            if let Err(e) = get_runtime().block_on(&mut self.handle) {
458                log::error!("Error awaiting task 'cache-write': {e:?}");
459            }
460        });
461
462        log::debug!("Closing connection pool");
463
464        tokio::task::block_in_place(|| {
465            get_runtime().block_on(async {
466                pool.close().await;
467
468                if let Err(e) = tx.send(()) {
469                    log::error!("Error closing pool: {e:?}");
470                }
471            });
472        });
473
474        log::debug!("Closed");
475
476        Ok(rx.recv()?)
477    }
478
479    fn flush(&mut self) -> anyhow::Result<()> {
480        let pool = self.pool.clone();
481        let (tx, rx) = std::sync::mpsc::channel();
482
483        tokio::task::block_in_place(|| {
484            get_runtime().block_on(async {
485                if let Err(e) = DatabaseQueries::truncate(&pool).await {
486                    log::error!("Error flushing pool: {e:?}");
487                }
488
489                if let Err(e) = tx.send(()) {
490                    log::error!("Error sending flush result: {e:?}");
491                }
492            });
493        });
494
495        Ok(rx.recv()?)
496    }
497
498    async fn load_all(&self) -> anyhow::Result<CacheMap> {
499        let currencies = self.load_currencies().await?;
500        for currency in currencies.values() {
501            Currency::register(*currency, false)?;
502        }
503
504        let (instruments, instrument_closes, synthetics, accounts, orders, positions) = try_join!(
505            self.load_instruments(),
506            self.load_instrument_closes(),
507            self.load_synthetics(),
508            self.load_accounts(),
509            self.load_orders(),
510            self.load_positions()
511        )
512        .map_err(|e| anyhow::anyhow!("Error loading cache data: {e}"))?;
513
514        // For now, we don't load greeks and yield curves from the database
515        // This will be implemented in the future
516        let greeks = AHashMap::new();
517        let yield_curves = AHashMap::new();
518
519        Ok(CacheMap {
520            currencies,
521            instruments,
522            instrument_closes,
523            synthetics,
524            accounts,
525            orders,
526            positions,
527            greeks,
528            yield_curves,
529        })
530    }
531
532    fn load(&self) -> anyhow::Result<AHashMap<String, Bytes>> {
533        let pool = self.pool.clone();
534        let (tx, rx) = std::sync::mpsc::channel();
535
536        tokio::spawn(async move {
537            let result = DatabaseQueries::load(&pool).await;
538            match result {
539                Ok(items) => {
540                    let mapping = items
541                        .into_iter()
542                        .map(|(k, v)| (k, Bytes::from(v)))
543                        .collect();
544
545                    if let Err(e) = tx.send(mapping) {
546                        log::error!("Failed to send general items: {e:?}");
547                    }
548                }
549                Err(e) => {
550                    log::error!("Failed to load general items: {e:?}");
551                    if let Err(e) = tx.send(AHashMap::new()) {
552                        log::error!("Failed to send empty general items: {e:?}");
553                    }
554                }
555            }
556        });
557        Ok(rx.recv()?)
558    }
559
560    async fn load_currencies(&self) -> anyhow::Result<AHashMap<Ustr, Currency>> {
561        let pool = self.pool.clone();
562        let (tx, rx) = std::sync::mpsc::channel();
563
564        tokio::spawn(async move {
565            let result = DatabaseQueries::load_currencies(&pool).await;
566            match result {
567                Ok(currencies) => {
568                    let mapping = currencies
569                        .into_iter()
570                        .map(|currency| (currency.code, currency))
571                        .collect();
572
573                    if let Err(e) = tx.send(mapping) {
574                        log::error!("Failed to send currencies: {e:?}");
575                    }
576                }
577                Err(e) => {
578                    log::error!("Failed to load currencies: {e:?}");
579                    if let Err(e) = tx.send(AHashMap::new()) {
580                        log::error!("Failed to send empty currencies: {e:?}");
581                    }
582                }
583            }
584        });
585        Ok(rx.recv()?)
586    }
587
588    async fn load_instruments(&self) -> anyhow::Result<AHashMap<InstrumentId, InstrumentAny>> {
589        let pool = self.pool.clone();
590        let (tx, rx) = std::sync::mpsc::channel();
591
592        tokio::spawn(async move {
593            let result = DatabaseQueries::load_instruments(&pool).await;
594            match result {
595                Ok(instruments) => {
596                    let mapping = instruments
597                        .into_iter()
598                        .map(|instrument| (instrument.id(), instrument))
599                        .collect();
600
601                    if let Err(e) = tx.send(mapping) {
602                        log::error!("Failed to send instruments: {e:?}");
603                    }
604                }
605                Err(e) => {
606                    log::error!("Failed to load instruments: {e:?}");
607                    if let Err(e) = tx.send(AHashMap::new()) {
608                        log::error!("Failed to send empty instruments: {e:?}");
609                    }
610                }
611            }
612        });
613        Ok(rx.recv()?)
614    }
615
616    async fn load_instrument_closes(
617        &self,
618    ) -> anyhow::Result<AHashMap<InstrumentId, InstrumentClose>> {
619        Ok(DatabaseQueries::load_instrument_closes(&self.pool)
620            .await?
621            .into_iter()
622            .map(|close| (close.instrument_id, close))
623            .collect())
624    }
625
626    async fn load_synthetics(&self) -> anyhow::Result<AHashMap<InstrumentId, SyntheticInstrument>> {
627        Ok(AHashMap::new())
628    }
629
630    async fn load_accounts(&self) -> anyhow::Result<AHashMap<AccountId, AccountAny>> {
631        let pool = self.pool.clone();
632        let (tx, rx) = std::sync::mpsc::channel();
633
634        tokio::spawn(async move {
635            let result = DatabaseQueries::load_accounts(&pool).await;
636            match result {
637                Ok(accounts) => {
638                    let mapping = accounts
639                        .into_iter()
640                        .map(|account| (account.id(), account))
641                        .collect();
642
643                    if let Err(e) = tx.send(mapping) {
644                        log::error!("Failed to send accounts: {e:?}");
645                    }
646                }
647                Err(e) => {
648                    log::error!("Failed to load accounts: {e:?}");
649                    if let Err(e) = tx.send(AHashMap::new()) {
650                        log::error!("Failed to send empty accounts: {e:?}");
651                    }
652                }
653            }
654        });
655        Ok(rx.recv()?)
656    }
657
658    async fn load_orders(&self) -> anyhow::Result<AHashMap<ClientOrderId, OrderAny>> {
659        let pool = self.pool.clone();
660        let (tx, rx) = std::sync::mpsc::channel();
661
662        tokio::spawn(async move {
663            let result = DatabaseQueries::load_orders(&pool).await;
664            match result {
665                Ok(orders) => {
666                    let mapping = orders
667                        .into_iter()
668                        .map(|order| (order.client_order_id(), order))
669                        .collect();
670
671                    if let Err(e) = tx.send(mapping) {
672                        log::error!("Failed to send orders: {e:?}");
673                    }
674                }
675                Err(e) => {
676                    log::error!("Failed to load orders: {e:?}");
677                    if let Err(e) = tx.send(AHashMap::new()) {
678                        log::error!("Failed to send empty orders: {e:?}");
679                    }
680                }
681            }
682        });
683        Ok(rx.recv()?)
684    }
685
686    async fn load_positions(&self) -> anyhow::Result<AHashMap<PositionId, Position>> {
687        let pool = self.pool.clone();
688        let (tx, rx) = std::sync::mpsc::channel();
689
690        tokio::spawn(async move {
691            let result = DatabaseQueries::load_positions(&pool)
692                .await
693                .map(|positions| {
694                    positions
695                        .into_iter()
696                        .map(|position| (position.id, position))
697                        .collect()
698                });
699
700            if let Err(e) = tx.send(result) {
701                log::error!("Failed to send positions: {e:?}");
702            }
703        });
704        rx.recv()?
705    }
706
707    fn load_index_order_position(&self) -> anyhow::Result<AHashMap<ClientOrderId, PositionId>> {
708        let pool = self.pool.clone();
709        let (tx, rx) = std::sync::mpsc::channel();
710
711        tokio::spawn(async move {
712            let result = DatabaseQueries::load_index_order_position(&pool).await;
713            match result {
714                Ok(index) => {
715                    if let Err(e) = tx.send(index) {
716                        log::error!("Failed to send load_index_order_position result: {e:?}");
717                    }
718                }
719                Err(e) => {
720                    log::error!("Failed to run query load_index_order_position: {e:?}");
721                    if let Err(e) = tx.send(AHashMap::new()) {
722                        log::error!("Failed to send empty load_index_order_position result: {e:?}");
723                    }
724                }
725            }
726        });
727        Ok(rx.recv()?)
728    }
729
730    fn load_index_order_client(&self) -> anyhow::Result<AHashMap<ClientOrderId, ClientId>> {
731        let pool = self.pool.clone();
732        let (tx, rx) = std::sync::mpsc::channel();
733
734        tokio::spawn(async move {
735            let result = DatabaseQueries::load_distinct_order_event_client_ids(&pool).await;
736            match result {
737                Ok(currency) => {
738                    if let Err(e) = tx.send(currency) {
739                        log::error!("Failed to send load_index_order_client result: {e:?}");
740                    }
741                }
742                Err(e) => {
743                    log::error!("Failed to run query load_distinct_order_event_client_ids: {e:?}");
744                    if let Err(e) = tx.send(AHashMap::new()) {
745                        log::error!("Failed to send empty load_index_order_client result: {e:?}");
746                    }
747                }
748            }
749        });
750        Ok(rx.recv()?)
751    }
752
753    async fn load_currency(&self, code: &Ustr) -> anyhow::Result<Option<Currency>> {
754        let pool = self.pool.clone();
755        let code = code.to_owned(); // Clone the code
756        let (tx, rx) = std::sync::mpsc::channel();
757
758        tokio::spawn(async move {
759            let result = DatabaseQueries::load_currency(&pool, &code).await;
760            match result {
761                Ok(currency) => {
762                    if let Err(e) = tx.send(currency) {
763                        log::error!("Failed to send currency {code}: {e:?}");
764                    }
765                }
766                Err(e) => {
767                    log::error!("Failed to load currency {code}: {e:?}");
768                    if let Err(e) = tx.send(None) {
769                        log::error!("Failed to send None for currency {code}: {e:?}");
770                    }
771                }
772            }
773        });
774        Ok(rx.recv()?)
775    }
776
777    async fn load_instrument(
778        &self,
779        instrument_id: &InstrumentId,
780    ) -> anyhow::Result<Option<InstrumentAny>> {
781        let pool = self.pool.clone();
782        let instrument_id = instrument_id.to_owned(); // Clone the instrument_id
783        let (tx, rx) = std::sync::mpsc::channel();
784
785        tokio::spawn(async move {
786            let result = DatabaseQueries::load_instrument(&pool, &instrument_id).await;
787            match result {
788                Ok(instrument) => {
789                    if let Err(e) = tx.send(instrument) {
790                        log::error!("Failed to send instrument {instrument_id}: {e:?}");
791                    }
792                }
793                Err(e) => {
794                    log::error!("Failed to load instrument {instrument_id}: {e:?}");
795                    if let Err(e) = tx.send(None) {
796                        log::error!("Failed to send None for instrument {instrument_id}: {e:?}");
797                    }
798                }
799            }
800        });
801        Ok(rx.recv()?)
802    }
803
804    async fn load_synthetic(
805        &self,
806        instrument_id: &InstrumentId,
807    ) -> anyhow::Result<Option<SyntheticInstrument>> {
808        anyhow::bail!(
809            "load_synthetic not implemented for PostgreSQL cache adapter: {instrument_id}"
810        )
811    }
812
813    async fn load_account(&self, account_id: &AccountId) -> anyhow::Result<Option<AccountAny>> {
814        let pool = self.pool.clone();
815        let account_id = account_id.to_owned();
816        let (tx, rx) = std::sync::mpsc::channel();
817
818        tokio::spawn(async move {
819            let result = DatabaseQueries::load_account(&pool, &account_id).await;
820            match result {
821                Ok(account) => {
822                    if let Err(e) = tx.send(account) {
823                        log::error!("Failed to send account {account_id}: {e:?}");
824                    }
825                }
826                Err(e) => {
827                    log::error!("Failed to load account {account_id}: {e:?}");
828                    if let Err(e) = tx.send(None) {
829                        log::error!("Failed to send None for account {account_id}: {e:?}");
830                    }
831                }
832            }
833        });
834        Ok(rx.recv()?)
835    }
836
837    async fn load_order(
838        &self,
839        client_order_id: &ClientOrderId,
840    ) -> anyhow::Result<Option<OrderAny>> {
841        let pool = self.pool.clone();
842        let client_order_id = client_order_id.to_owned();
843        let (tx, rx) = std::sync::mpsc::channel();
844
845        tokio::spawn(async move {
846            let result = DatabaseQueries::load_order(&pool, &client_order_id).await;
847            match result {
848                Ok(order) => {
849                    if let Err(e) = tx.send(order) {
850                        log::error!("Failed to send order {client_order_id}: {e:?}");
851                    }
852                }
853                Err(e) => {
854                    log::error!("Failed to load order {client_order_id}: {e:?}");
855                    let _ = tx.send(None);
856                }
857            }
858        });
859        Ok(rx.recv()?)
860    }
861
862    async fn load_position(&self, position_id: &PositionId) -> anyhow::Result<Option<Position>> {
863        let pool = self.pool.clone();
864        let position_id = position_id.to_owned();
865        let (tx, rx) = std::sync::mpsc::channel();
866
867        tokio::spawn(async move {
868            let result = DatabaseQueries::load_position(&pool, &position_id).await;
869            if let Err(e) = tx.send(result) {
870                log::error!("Failed to send position {position_id}: {e:?}");
871            }
872        });
873        rx.recv()?
874    }
875
876    fn load_actor(&self, actor_id: &ActorId) -> anyhow::Result<AHashMap<String, Bytes>> {
877        anyhow::bail!("load_actor not implemented for PostgreSQL cache adapter: {actor_id}")
878    }
879
880    fn delete_actor(&self, _actor_id: &ActorId) -> anyhow::Result<()> {
881        todo!()
882    }
883
884    fn load_strategy(&self, strategy_id: &StrategyId) -> anyhow::Result<AHashMap<String, Bytes>> {
885        anyhow::bail!("load_strategy not implemented for PostgreSQL cache adapter: {strategy_id}")
886    }
887
888    fn delete_strategy(&self, _strategy_id: &StrategyId) -> anyhow::Result<()> {
889        todo!()
890    }
891
892    fn delete_order(&self, client_order_id: &ClientOrderId) -> anyhow::Result<()> {
893        anyhow::bail!(
894            "delete_order not implemented for PostgreSQL cache adapter: {client_order_id}"
895        )
896    }
897
898    fn delete_position(&self, position_id: &PositionId) -> anyhow::Result<()> {
899        anyhow::bail!("delete_position not implemented for PostgreSQL cache adapter: {position_id}")
900    }
901
902    fn delete_account_event(&self, account_id: &AccountId, event_id: &str) -> anyhow::Result<()> {
903        anyhow::bail!(
904            "delete_account_event not implemented for PostgreSQL cache adapter: {account_id}, {event_id}"
905        )
906    }
907
908    fn add(&self, key: String, value: Bytes) -> anyhow::Result<()> {
909        let query = DatabaseQuery::Add(key, value.into());
910        self.tx
911            .send(query)
912            .map_err(|e| anyhow::anyhow!("Failed to send query to database message handler: {e}"))
913    }
914
915    fn add_currency(&self, currency: &Currency) -> anyhow::Result<()> {
916        let query = DatabaseQuery::AddCurrency(*currency);
917        self.tx.send(query).map_err(|e| {
918            anyhow::anyhow!("Failed to query add_currency to database message handler: {e}")
919        })
920    }
921
922    fn add_instrument(&self, instrument: &InstrumentAny) -> anyhow::Result<()> {
923        let query = DatabaseQuery::AddInstrument(instrument.clone());
924        self.tx.send(query).map_err(|e| {
925            anyhow::anyhow!("Failed to send query add_instrument to database message handler: {e}")
926        })
927    }
928
929    fn add_instrument_close(&self, close: &InstrumentClose) -> anyhow::Result<()> {
930        self.tx
931            .send(DatabaseQuery::AddInstrumentClose(*close))
932            .map_err(|e| {
933                anyhow::anyhow!(
934                    "Failed to send query add_instrument_close to database message handler: {e}"
935                )
936            })
937    }
938
939    fn add_synthetic(&self, _synthetic: &SyntheticInstrument) -> anyhow::Result<()> {
940        todo!()
941    }
942
943    fn add_account(&self, account: &AccountAny) -> anyhow::Result<()> {
944        let query = DatabaseQuery::AddAccount(account_last_event(account)?, false);
945        self.tx.send(query).map_err(|e| {
946            anyhow::anyhow!("Failed to send query add_account to database message handler: {e}")
947        })
948    }
949
950    fn add_order(&self, order: &OrderAny, client_id: Option<ClientId>) -> anyhow::Result<()> {
951        let query = DatabaseQuery::AddOrder(order_initialized_event(order), client_id);
952        self.tx.send(query).map_err(|e| {
953            anyhow::anyhow!("Failed to send query add_order to database message handler: {e}")
954        })
955    }
956
957    fn add_order_snapshot(&self, snapshot: &OrderSnapshot) -> anyhow::Result<()> {
958        let query = DatabaseQuery::AddOrderSnapshot(snapshot.to_owned());
959        self.tx.send(query).map_err(|e| {
960            anyhow::anyhow!(
961                "Failed to send query add_order_snapshot to database message handler: {e}"
962            )
963        })
964    }
965
966    fn add_position(&self, position: &Position) -> anyhow::Result<()> {
967        let event = position_last_event(position)?;
968        let query = DatabaseQuery::AddPosition(position.id, event);
969        self.tx.send(query).map_err(|e| {
970            anyhow::anyhow!("Failed to send query add_position to database message handler: {e}")
971        })
972    }
973
974    fn add_position_snapshot(&self, snapshot: &PositionSnapshot) -> anyhow::Result<()> {
975        let query = DatabaseQuery::AddPositionSnapshot(snapshot.to_owned());
976        self.tx.send(query).map_err(|e| {
977            anyhow::anyhow!(
978                "Failed to send query add_position_snapshot to database message handler: {e}"
979            )
980        })
981    }
982
983    fn add_order_book(&self, _order_book: &OrderBook) -> anyhow::Result<()> {
984        todo!()
985    }
986
987    fn add_quote(&self, quote: &QuoteTick) -> anyhow::Result<()> {
988        let query = DatabaseQuery::AddQuote(quote.to_owned());
989        self.tx.send(query).map_err(|e| {
990            anyhow::anyhow!("Failed to send query add_quote to database message handler: {e}")
991        })
992    }
993
994    fn load_quotes(&self, instrument_id: &InstrumentId) -> anyhow::Result<Vec<QuoteTick>> {
995        let pool = self.pool.clone();
996        let instrument_id = instrument_id.to_owned();
997        let (tx, rx) = std::sync::mpsc::channel();
998
999        tokio::spawn(async move {
1000            let result = DatabaseQueries::load_quotes(&pool, &instrument_id).await;
1001            match result {
1002                Ok(quotes) => {
1003                    if let Err(e) = tx.send(quotes) {
1004                        log::error!("Failed to send quotes for instrument {instrument_id}: {e:?}");
1005                    }
1006                }
1007                Err(e) => {
1008                    log::error!("Failed to load quotes for instrument {instrument_id}: {e:?}");
1009                    if let Err(e) = tx.send(Vec::new()) {
1010                        log::error!(
1011                            "Failed to send empty quotes for instrument {instrument_id}: {e:?}"
1012                        );
1013                    }
1014                }
1015            }
1016        });
1017        Ok(rx.recv()?)
1018    }
1019
1020    fn add_trade(&self, trade: &TradeTick) -> anyhow::Result<()> {
1021        let query = DatabaseQuery::AddTrade(trade.to_owned());
1022        self.tx.send(query).map_err(|e| {
1023            anyhow::anyhow!("Failed to send query add_trade to database message handler: {e}")
1024        })
1025    }
1026
1027    fn load_trades(&self, instrument_id: &InstrumentId) -> anyhow::Result<Vec<TradeTick>> {
1028        let pool = self.pool.clone();
1029        let instrument_id = instrument_id.to_owned();
1030        let (tx, rx) = std::sync::mpsc::channel();
1031
1032        tokio::spawn(async move {
1033            let result = DatabaseQueries::load_trades(&pool, &instrument_id).await;
1034            match result {
1035                Ok(trades) => {
1036                    if let Err(e) = tx.send(trades) {
1037                        log::error!("Failed to send trades for instrument {instrument_id}: {e:?}");
1038                    }
1039                }
1040                Err(e) => {
1041                    log::error!("Failed to load trades for instrument {instrument_id}: {e:?}");
1042                    if let Err(e) = tx.send(Vec::new()) {
1043                        log::error!(
1044                            "Failed to send empty trades for instrument {instrument_id}: {e:?}"
1045                        );
1046                    }
1047                }
1048            }
1049        });
1050        Ok(rx.recv()?)
1051    }
1052
1053    fn add_funding_rate(&self, _funding_rate: &FundingRateUpdate) -> anyhow::Result<()> {
1054        anyhow::bail!("add_funding_rate not implemented for PostgreSQL cache adapter")
1055    }
1056
1057    fn load_funding_rates(
1058        &self,
1059        _instrument_id: &InstrumentId,
1060    ) -> anyhow::Result<Vec<FundingRateUpdate>> {
1061        anyhow::bail!("load_funding_rates not implemented for PostgreSQL cache adapter")
1062    }
1063
1064    fn add_bar(&self, bar: &Bar) -> anyhow::Result<()> {
1065        let query = DatabaseQuery::AddBar(bar.to_owned());
1066        self.tx.send(query).map_err(|e| {
1067            anyhow::anyhow!("Failed to send query add_bar to database message handler: {e}")
1068        })
1069    }
1070
1071    fn load_bars(&self, instrument_id: &InstrumentId) -> anyhow::Result<Vec<Bar>> {
1072        let pool = self.pool.clone();
1073        let instrument_id = instrument_id.to_owned();
1074        let (tx, rx) = std::sync::mpsc::channel();
1075
1076        tokio::spawn(async move {
1077            let result = DatabaseQueries::load_bars(&pool, &instrument_id).await;
1078            match result {
1079                Ok(bars) => {
1080                    if let Err(e) = tx.send(bars) {
1081                        log::error!("Failed to send bars for instrument {instrument_id}: {e:?}");
1082                    }
1083                }
1084                Err(e) => {
1085                    log::error!("Failed to load bars for instrument {instrument_id}: {e:?}");
1086                    if let Err(e) = tx.send(Vec::new()) {
1087                        log::error!(
1088                            "Failed to send empty bars for instrument {instrument_id}: {e:?}"
1089                        );
1090                    }
1091                }
1092            }
1093        });
1094        Ok(rx.recv()?)
1095    }
1096
1097    fn add_signal(&self, signal: &Signal) -> anyhow::Result<()> {
1098        let query = DatabaseQuery::AddSignal(signal.to_owned());
1099        self.tx.send(query).map_err(|e| {
1100            anyhow::anyhow!("Failed to send query add_signal to database message handler: {e}")
1101        })
1102    }
1103
1104    fn load_signals(&self, name: &str) -> anyhow::Result<Vec<Signal>> {
1105        let pool = self.pool.clone();
1106        let name = name.to_owned();
1107        let (tx, rx) = std::sync::mpsc::channel();
1108
1109        tokio::spawn(async move {
1110            let result = DatabaseQueries::load_signals(&pool, &name).await;
1111            match result {
1112                Ok(signals) => {
1113                    if let Err(e) = tx.send(signals) {
1114                        log::error!("Failed to send signals for '{name}': {e:?}");
1115                    }
1116                }
1117                Err(e) => {
1118                    log::error!("Failed to load signals for '{name}': {e:?}");
1119                    if let Err(e) = tx.send(Vec::new()) {
1120                        log::error!("Failed to send empty signals for '{name}': {e:?}");
1121                    }
1122                }
1123            }
1124        });
1125        Ok(rx.recv()?)
1126    }
1127
1128    fn add_custom_data(&self, data: &CustomData) -> anyhow::Result<()> {
1129        let query = DatabaseQuery::AddCustom(data.to_owned());
1130        self.tx.send(query).map_err(|e| {
1131            anyhow::anyhow!("Failed to send query add_signal to database message handler: {e}")
1132        })
1133    }
1134
1135    fn load_custom_data(&self, data_type: &DataType) -> anyhow::Result<Vec<CustomData>> {
1136        let pool = self.pool.clone();
1137        let data_type = data_type.to_owned();
1138        let (tx, rx) = std::sync::mpsc::channel();
1139
1140        tokio::spawn(async move {
1141            let result = DatabaseQueries::load_custom_data(&pool, &data_type).await;
1142            match result {
1143                Ok(signals) => {
1144                    if let Err(e) = tx.send(signals) {
1145                        log::error!("Failed to send custom data for '{data_type}': {e:?}");
1146                    }
1147                }
1148                Err(e) => {
1149                    log::error!("Failed to load custom data for '{data_type}': {e:?}");
1150                    if let Err(e) = tx.send(Vec::new()) {
1151                        log::error!("Failed to send empty custom data for '{data_type}': {e:?}");
1152                    }
1153                }
1154            }
1155        });
1156        Ok(rx.recv()?)
1157    }
1158
1159    fn load_order_snapshot(
1160        &self,
1161        client_order_id: &ClientOrderId,
1162    ) -> anyhow::Result<Option<OrderSnapshot>> {
1163        let pool = self.pool.clone();
1164        let client_order_id = client_order_id.to_owned();
1165        let (tx, rx) = std::sync::mpsc::channel();
1166
1167        tokio::spawn(async move {
1168            let result = DatabaseQueries::load_order_snapshot(&pool, &client_order_id).await;
1169            match result {
1170                Ok(snapshot) => {
1171                    if let Err(e) = tx.send(snapshot) {
1172                        log::error!("Failed to send order snapshot {client_order_id}: {e:?}");
1173                    }
1174                }
1175                Err(e) => {
1176                    log::error!("Failed to load order snapshot {client_order_id}: {e:?}");
1177                    if let Err(e) = tx.send(None) {
1178                        log::error!(
1179                            "Failed to send None for order snapshot {client_order_id}: {e:?}"
1180                        );
1181                    }
1182                }
1183            }
1184        });
1185        Ok(rx.recv()?)
1186    }
1187
1188    fn load_position_snapshot(
1189        &self,
1190        position_id: &PositionId,
1191    ) -> anyhow::Result<Option<PositionSnapshot>> {
1192        let pool = self.pool.clone();
1193        let position_id = position_id.to_owned();
1194        let (tx, rx) = std::sync::mpsc::channel();
1195
1196        tokio::spawn(async move {
1197            let result = DatabaseQueries::load_position_snapshot(&pool, &position_id).await;
1198            match result {
1199                Ok(snapshot) => {
1200                    if let Err(e) = tx.send(snapshot) {
1201                        log::error!("Failed to send position snapshot {position_id}: {e:?}");
1202                    }
1203                }
1204                Err(e) => {
1205                    log::error!("Failed to load position snapshot {position_id}: {e:?}");
1206                    if let Err(e) = tx.send(None) {
1207                        log::error!(
1208                            "Failed to send None for position snapshot {position_id}: {e:?}"
1209                        );
1210                    }
1211                }
1212            }
1213        });
1214        Ok(rx.recv()?)
1215    }
1216
1217    fn index_venue_order_id(
1218        &self,
1219        _client_order_id: ClientOrderId,
1220        _venue_order_id: VenueOrderId,
1221    ) -> anyhow::Result<()> {
1222        todo!()
1223    }
1224
1225    fn index_order_position(
1226        &self,
1227        client_order_id: ClientOrderId,
1228        position_id: PositionId,
1229    ) -> anyhow::Result<()> {
1230        let query = DatabaseQuery::IndexOrderPosition(client_order_id, position_id);
1231        self.tx.send(query).map_err(|e| {
1232            anyhow::anyhow!(
1233                "Failed to send query index_order_position to database message handler: {e}"
1234            )
1235        })
1236    }
1237
1238    fn index_order_clients(&self, claims: &[(ClientOrderId, ClientId)]) -> anyhow::Result<()> {
1239        if claims.is_empty() {
1240            return Ok(());
1241        }
1242
1243        let query = DatabaseQuery::IndexOrderClients(claims.to_vec());
1244        self.tx.send(query).map_err(|e| {
1245            anyhow::anyhow!(
1246                "Failed to send query index_order_clients to database message handler: {e}"
1247            )
1248        })
1249    }
1250
1251    fn update_actor(
1252        &self,
1253        actor_id: &ActorId,
1254        _state: &AHashMap<String, Bytes>,
1255    ) -> anyhow::Result<()> {
1256        anyhow::bail!("update_actor not implemented for PostgreSQL cache adapter: {actor_id}")
1257    }
1258
1259    fn update_strategy(
1260        &self,
1261        strategy_id: &StrategyId,
1262        _state: &AHashMap<String, Bytes>,
1263    ) -> anyhow::Result<()> {
1264        anyhow::bail!("update_strategy not implemented for PostgreSQL cache adapter: {strategy_id}")
1265    }
1266
1267    fn update_account(&self, account: &AccountAny) -> anyhow::Result<()> {
1268        let query = DatabaseQuery::AddAccount(account_last_event(account)?, true);
1269        self.tx.send(query).map_err(|e| {
1270            anyhow::anyhow!("Failed to send query add_account to database message handler: {e}")
1271        })
1272    }
1273
1274    fn update_order(&self, event: &OrderEventAny) -> anyhow::Result<()> {
1275        let query = DatabaseQuery::UpdateOrder(event.clone());
1276        self.tx.send(query).map_err(|e| {
1277            anyhow::anyhow!("Failed to send query update_order to database message handler: {e}")
1278        })
1279    }
1280
1281    fn update_position(&self, position: &Position) -> anyhow::Result<()> {
1282        let query = if position.fill_voids.is_empty() {
1283            DatabaseQuery::UpdatePosition(position_last_event(position)?)
1284        } else {
1285            DatabaseQuery::AddPositionSnapshot(PositionSnapshot::from_replay_state(position, None))
1286        };
1287        self.tx.send(query).map_err(|e| {
1288            anyhow::anyhow!("Failed to send query update_position to database message handler: {e}")
1289        })
1290    }
1291
1292    fn snapshot_order_state(&self, order: &OrderAny) -> anyhow::Result<()> {
1293        let snapshot = OrderSnapshot::from(order.clone());
1294        self.add_order_snapshot(&snapshot)
1295    }
1296
1297    fn snapshot_position_state(
1298        &self,
1299        position: &Position,
1300        ts_snapshot: UnixNanos,
1301        unrealized_pnl: Option<Money>,
1302    ) -> anyhow::Result<()> {
1303        let mut snapshot = if position.fill_voids.is_empty() {
1304            PositionSnapshot::from(position, unrealized_pnl)
1305        } else {
1306            PositionSnapshot::from_replay_state(position, unrealized_pnl)
1307        };
1308        snapshot.ts_init = ts_snapshot;
1309        self.add_position_snapshot(&snapshot)
1310    }
1311
1312    fn heartbeat(&self, _timestamp: UnixNanos) -> anyhow::Result<()> {
1313        todo!()
1314    }
1315}
1316
1317fn account_last_event(account: &AccountAny) -> anyhow::Result<AccountState> {
1318    account
1319        .last_event()
1320        .ok_or_else(|| anyhow::anyhow!("Cannot persist account with no events: {}", account.id()))
1321}
1322
1323fn order_initialized_event(order: &OrderAny) -> OrderInitialized {
1324    order.init_event().clone()
1325}
1326
1327fn position_last_event(position: &Position) -> anyhow::Result<OrderFilled> {
1328    position
1329        .last_event()
1330        .ok_or_else(|| anyhow::anyhow!("Cannot persist position with no events: {}", position.id))
1331}
1332
1333#[expect(
1334    clippy::too_many_lines,
1335    reason = "database command dispatch enumerates each cache query variant explicitly"
1336)]
1337async fn drain_buffer(pool: &PgPool, buffer: &mut VecDeque<DatabaseQuery>) {
1338    for cmd in buffer.drain(..) {
1339        let result: anyhow::Result<()> = match cmd {
1340            DatabaseQuery::Close => Ok(()),
1341            DatabaseQuery::Add(key, value) => DatabaseQueries::add(pool, key, value).await,
1342            DatabaseQuery::AddCurrency(currency) => {
1343                DatabaseQueries::add_currency(pool, currency).await
1344            }
1345            DatabaseQuery::AddInstrument(instrument_any) => match instrument_any {
1346                InstrumentAny::Betting(instrument) => {
1347                    DatabaseQueries::add_instrument(pool, "BETTING", Box::new(instrument)).await
1348                }
1349                InstrumentAny::BinaryOption(instrument) => {
1350                    DatabaseQueries::add_instrument(pool, "BINARY_OPTION", Box::new(instrument))
1351                        .await
1352                }
1353                InstrumentAny::CryptoFuture(instrument) => {
1354                    DatabaseQueries::add_instrument(pool, "CRYPTO_FUTURE", Box::new(instrument))
1355                        .await
1356                }
1357                InstrumentAny::CryptoFuturesSpread(instrument) => {
1358                    DatabaseQueries::add_instrument(
1359                        pool,
1360                        "CRYPTO_FUTURES_SPREAD",
1361                        Box::new(instrument),
1362                    )
1363                    .await
1364                }
1365                InstrumentAny::CryptoOption(instrument) => {
1366                    DatabaseQueries::add_instrument(pool, "CRYPTO_OPTION", Box::new(instrument))
1367                        .await
1368                }
1369                InstrumentAny::CryptoOptionSpread(instrument) => {
1370                    DatabaseQueries::add_instrument(
1371                        pool,
1372                        "CRYPTO_OPTION_SPREAD",
1373                        Box::new(instrument),
1374                    )
1375                    .await
1376                }
1377                InstrumentAny::CryptoPerpetual(instrument) => {
1378                    DatabaseQueries::add_instrument(pool, "CRYPTO_PERPETUAL", Box::new(instrument))
1379                        .await
1380                }
1381                InstrumentAny::CurrencyPair(instrument) => {
1382                    DatabaseQueries::add_instrument(pool, "CURRENCY_PAIR", Box::new(instrument))
1383                        .await
1384                }
1385                InstrumentAny::Equity(equity) => {
1386                    DatabaseQueries::add_instrument(pool, "EQUITY", Box::new(equity)).await
1387                }
1388                InstrumentAny::FuturesContract(instrument) => {
1389                    DatabaseQueries::add_instrument(pool, "FUTURES_CONTRACT", Box::new(instrument))
1390                        .await
1391                }
1392                InstrumentAny::FuturesSpread(instrument) => {
1393                    DatabaseQueries::add_instrument(pool, "FUTURES_SPREAD", Box::new(instrument))
1394                        .await
1395                }
1396                InstrumentAny::OptionContract(instrument) => {
1397                    DatabaseQueries::add_instrument(pool, "OPTION_CONTRACT", Box::new(instrument))
1398                        .await
1399                }
1400                InstrumentAny::Commodity(instrument) => {
1401                    DatabaseQueries::add_instrument(pool, "COMMODITY", Box::new(instrument)).await
1402                }
1403                InstrumentAny::IndexInstrument(instrument) => {
1404                    DatabaseQueries::add_instrument(pool, "INDEX_INSTRUMENT", Box::new(instrument))
1405                        .await
1406                }
1407                InstrumentAny::Cfd(instrument) => {
1408                    DatabaseQueries::add_instrument(pool, "CFD", Box::new(instrument)).await
1409                }
1410                InstrumentAny::OptionSpread(instrument) => {
1411                    DatabaseQueries::add_instrument(pool, "OPTION_SPREAD", Box::new(instrument))
1412                        .await
1413                }
1414                InstrumentAny::PerpetualContract(instrument) => {
1415                    DatabaseQueries::add_instrument(
1416                        pool,
1417                        "PERPETUAL_CONTRACT",
1418                        Box::new(instrument),
1419                    )
1420                    .await
1421                }
1422                InstrumentAny::TokenizedAsset(instrument) => {
1423                    DatabaseQueries::add_instrument(pool, "TOKENIZED_ASSET", Box::new(instrument))
1424                        .await
1425                }
1426            },
1427            DatabaseQuery::AddInstrumentClose(close) => {
1428                DatabaseQueries::add_instrument_close(pool, &close).await
1429            }
1430            DatabaseQuery::AddOrder(event, client_id) => {
1431                DatabaseQueries::add_order(pool, event, client_id).await
1432            }
1433            DatabaseQuery::AddOrderSnapshot(snapshot) => {
1434                DatabaseQueries::add_order_snapshot(pool, snapshot).await
1435            }
1436            DatabaseQuery::AddPosition(position_id, event) => {
1437                DatabaseQueries::add_position(pool, position_id, &event).await
1438            }
1439            DatabaseQuery::AddPositionSnapshot(snapshot) => {
1440                DatabaseQueries::add_position_snapshot(pool, snapshot).await
1441            }
1442            DatabaseQuery::AddAccount(event, updated) => {
1443                DatabaseQueries::add_account(pool, updated, event).await
1444            }
1445            DatabaseQuery::AddSignal(signal) => DatabaseQueries::add_signal(pool, &signal).await,
1446            DatabaseQuery::AddCustom(data) => DatabaseQueries::add_custom_data(pool, &data).await,
1447            DatabaseQuery::AddQuote(quote) => DatabaseQueries::add_quote(pool, &quote).await,
1448            DatabaseQuery::AddTrade(trade) => DatabaseQueries::add_trade(pool, &trade).await,
1449            DatabaseQuery::AddBar(bar) => DatabaseQueries::add_bar(pool, &bar).await,
1450            DatabaseQuery::UpdateOrder(event) => {
1451                DatabaseQueries::add_order_event(pool, event.into_boxed(), None).await
1452            }
1453            DatabaseQuery::UpdatePosition(event) => {
1454                DatabaseQueries::update_position(pool, &event).await
1455            }
1456            DatabaseQuery::IndexOrderPosition(client_order_id, position_id) => {
1457                DatabaseQueries::index_order_position(pool, client_order_id, position_id).await
1458            }
1459            DatabaseQuery::IndexOrderClients(claims) => {
1460                DatabaseQueries::index_order_clients(pool, &claims).await
1461            }
1462        };
1463
1464        if let Err(e) = result {
1465            log::error!("Error on query: {e:?}");
1466        }
1467    }
1468}