inserts 10k rows in 6 seconds
This commit is contained in:
parent
825db88039
commit
e93e43786f
5 changed files with 74 additions and 9 deletions
13
Cargo.lock
generated
13
Cargo.lock
generated
|
@ -2080,6 +2080,17 @@ dependencies = [
|
||||||
"syn 2.0.18",
|
"syn 2.0.18",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-retry"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f57eb36ecbe0fc510036adff84824dd3c24bb781e21bfa67b69d556aa85214f"
|
||||||
|
dependencies = [
|
||||||
|
"pin-project",
|
||||||
|
"rand",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-rustls"
|
name = "tokio-rustls"
|
||||||
version = "0.23.4"
|
version = "0.23.4"
|
||||||
|
@ -2560,6 +2571,8 @@ dependencies = [
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-retry",
|
||||||
|
"tokio-stream",
|
||||||
"tower",
|
"tower",
|
||||||
"tower-http 0.4.1",
|
"tower-http 0.4.1",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
|
|
@ -31,6 +31,8 @@ optional_optional_user = {path = "optional_optional_user"}
|
||||||
chrono = { version = "0.4", default-features = false, features = ["std", "clock"] }
|
chrono = { version = "0.4", default-features = false, features = ["std", "clock"] }
|
||||||
clap = { version = "4.3.10", features = ["derive", "env", "unicode", "suggestions", "usage"] }
|
clap = { version = "4.3.10", features = ["derive", "env", "unicode", "suggestions", "usage"] }
|
||||||
futures-util = { version = "0.3.28", features = ["tokio-io"] }
|
futures-util = { version = "0.3.28", features = ["tokio-io"] }
|
||||||
|
tokio-retry = "0.3.0"
|
||||||
|
tokio-stream = "0.1.14"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
axum-test = "9.0.0"
|
axum-test = "9.0.0"
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
use std::{ffi::OsString, time::Duration};
|
use std::{ffi::OsString, pin::Pin, time::Duration};
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use futures_util::stream::TryStreamExt;
|
use futures_util::Stream;
|
||||||
|
//use futures_util::stream::TryStreamExt;
|
||||||
use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions};
|
use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions};
|
||||||
|
use tokio::task::JoinSet;
|
||||||
|
use tokio_retry::Retry;
|
||||||
|
use tokio_stream::StreamExt;
|
||||||
use witch_watch::{
|
use witch_watch::{
|
||||||
get_db_pool,
|
get_db_pool,
|
||||||
import_utils::{add_watch_omega, ensure_omega},
|
import_utils::{add_watch_omega, ensure_omega, ImportMovieOmega},
|
||||||
};
|
};
|
||||||
|
|
||||||
const MOVIE_QUERY: &str = "select * from movies order by random() limit 10000";
|
const MOVIE_QUERY: &str = "select * from movies order by random() limit 10000";
|
||||||
|
@ -30,11 +34,35 @@ async fn main() {
|
||||||
|
|
||||||
let ww_db = get_db_pool().await;
|
let ww_db = get_db_pool().await;
|
||||||
|
|
||||||
let mut movies = sqlx::query_as(MOVIE_QUERY).fetch(&movie_db);
|
let mut movies: Pin<Box<dyn Stream<Item = Result<ImportMovieOmega, _>> + Send>> =
|
||||||
|
sqlx::query_as(MOVIE_QUERY).fetch(&movie_db);
|
||||||
|
|
||||||
ensure_omega(&ww_db).await;
|
ensure_omega(&ww_db).await;
|
||||||
|
|
||||||
|
let mut set = JoinSet::new();
|
||||||
|
|
||||||
|
let retry_strategy = tokio_retry::strategy::ExponentialBackoff::from_millis(100)
|
||||||
|
.map(tokio_retry::strategy::jitter)
|
||||||
|
.take(4);
|
||||||
|
|
||||||
while let Ok(Some(movie)) = movies.try_next().await {
|
while let Ok(Some(movie)) = movies.try_next().await {
|
||||||
add_watch_omega(&ww_db, movie).await;
|
let db = ww_db.clone();
|
||||||
|
let title = movie.title.as_str();
|
||||||
|
let year = movie.year.clone().unwrap();
|
||||||
|
let len = movie.length.clone().unwrap();
|
||||||
|
let retry_strategy = retry_strategy.clone();
|
||||||
|
|
||||||
|
let key = format!("{title}{year}{len}");
|
||||||
|
set.spawn(async move {
|
||||||
|
(
|
||||||
|
key,
|
||||||
|
Retry::spawn(retry_strategy, || async {
|
||||||
|
add_watch_omega(&db, &movie).await
|
||||||
|
})
|
||||||
|
.await,
|
||||||
|
)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
// stragglers
|
||||||
|
while (set.join_next().await).is_some() {}
|
||||||
}
|
}
|
||||||
|
|
12
src/db.rs
12
src/db.rs
|
@ -8,8 +8,8 @@ use axum_login::{
|
||||||
use session_store::SqliteSessionStore;
|
use session_store::SqliteSessionStore;
|
||||||
use sqlx::{
|
use sqlx::{
|
||||||
migrate::Migrator,
|
migrate::Migrator,
|
||||||
sqlite::{SqliteConnectOptions, SqlitePoolOptions},
|
sqlite::{SqliteConnectOptions, SqliteJournalMode, SqlitePoolOptions},
|
||||||
SqlitePool,
|
Executor, SqlitePool,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{db_id::DbId, User};
|
use crate::{db_id::DbId, User};
|
||||||
|
@ -45,7 +45,9 @@ pub async fn get_db_pool() -> SqlitePool {
|
||||||
|
|
||||||
let conn_opts = SqliteConnectOptions::new()
|
let conn_opts = SqliteConnectOptions::new()
|
||||||
.foreign_keys(true)
|
.foreign_keys(true)
|
||||||
.auto_vacuum(sqlx::sqlite::SqliteAutoVacuum::Incremental)
|
//.auto_vacuum(sqlx::sqlite::SqliteAutoVacuum::Incremental)
|
||||||
|
.journal_mode(SqliteJournalMode::Memory)
|
||||||
|
.synchronous(sqlx::sqlite::SqliteSynchronous::Off)
|
||||||
.filename(&db_filename)
|
.filename(&db_filename)
|
||||||
.busy_timeout(Duration::from_secs(TIMEOUT))
|
.busy_timeout(Duration::from_secs(TIMEOUT))
|
||||||
.create_if_missing(true);
|
.create_if_missing(true);
|
||||||
|
@ -59,6 +61,10 @@ pub async fn get_db_pool() -> SqlitePool {
|
||||||
.await
|
.await
|
||||||
.expect("can't connect to database");
|
.expect("can't connect to database");
|
||||||
|
|
||||||
|
let mut conn = pool.acquire().await.unwrap();
|
||||||
|
conn.execute("PRAGMA cache_size = 1000000;").await.unwrap();
|
||||||
|
conn.execute("PRAGMA temp_store = MEMORY;").await.unwrap();
|
||||||
|
|
||||||
// let the filesystem settle before trying anything
|
// let the filesystem settle before trying anything
|
||||||
// possibly not effective?
|
// possibly not effective?
|
||||||
tokio::time::sleep(Duration::from_millis(500)).await;
|
tokio::time::sleep(Duration::from_millis(500)).await;
|
||||||
|
|
|
@ -29,15 +29,31 @@ impl From<ImportMovieOmega> for Watch {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&ImportMovieOmega> for Watch {
|
||||||
|
fn from(value: &ImportMovieOmega) -> Self {
|
||||||
|
Watch {
|
||||||
|
title: value.title.to_string(),
|
||||||
|
release_date: year_to_epoch(value.year.as_deref()),
|
||||||
|
length: value.length.as_ref().and_then(|v| v.parse::<i64>().ok()),
|
||||||
|
id: DbId::new(),
|
||||||
|
kind: ShowKind::Movie,
|
||||||
|
metadata_url: None,
|
||||||
|
added_by: OMEGA_ID.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//-************************************************************************
|
//-************************************************************************
|
||||||
// utility functions for building CLI tools, currently just for benchmarking
|
// utility functions for building CLI tools, currently just for benchmarking
|
||||||
//-************************************************************************
|
//-************************************************************************
|
||||||
pub async fn add_watch_omega(db_pool: &SqlitePool, movie: ImportMovieOmega) {
|
pub async fn add_watch_omega(db_pool: &SqlitePool, movie: &ImportMovieOmega) -> Result<(), ()> {
|
||||||
let watch: Watch = movie.into();
|
let watch: Watch = movie.into();
|
||||||
if add_new_watch_impl(db_pool, &watch, None).await.is_ok() {
|
if add_new_watch_impl(db_pool, &watch, None).await.is_ok() {
|
||||||
println!("{}", watch.id);
|
println!("{}", watch.id);
|
||||||
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
eprintln!("failed to add \"{}\"", watch.title);
|
eprintln!("failed to add \"{}\"", watch.title);
|
||||||
|
Err(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue