perf: cache for players scraping
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
/target
|
/target
|
||||||
|
|
||||||
/*.txt
|
/*.txt
|
||||||
|
/players.redb
|
||||||
47
Cargo.lock
generated
47
Cargo.lock
generated
@@ -97,6 +97,26 @@ dependencies = [
|
|||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bincode"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740"
|
||||||
|
dependencies = [
|
||||||
|
"bincode_derive",
|
||||||
|
"serde",
|
||||||
|
"unty",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bincode_derive"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bf95709a440f45e986983918d0e8a1f30a9b1df04918fc828670606804ac3c09"
|
||||||
|
dependencies = [
|
||||||
|
"virtue",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.9.1"
|
version = "2.9.1"
|
||||||
@@ -553,7 +573,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
|
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.60.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1237,6 +1257,15 @@ dependencies = [
|
|||||||
"getrandom 0.3.3",
|
"getrandom 0.3.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redb"
|
||||||
|
version = "2.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fef838cd981b5c46e9e91e20e4623e43b29b5c251eb245b34da0cbd2da09ab27"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ref-cast"
|
name = "ref-cast"
|
||||||
version = "1.0.24"
|
version = "1.0.24"
|
||||||
@@ -1302,7 +1331,7 @@ dependencies = [
|
|||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.60.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1377,6 +1406,7 @@ name = "sdgb-api"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes",
|
"aes",
|
||||||
|
"bincode",
|
||||||
"cbc",
|
"cbc",
|
||||||
"chrono",
|
"chrono",
|
||||||
"cipher",
|
"cipher",
|
||||||
@@ -1401,6 +1431,7 @@ dependencies = [
|
|||||||
"futures-util",
|
"futures-util",
|
||||||
"nyquest-preset",
|
"nyquest-preset",
|
||||||
"palc",
|
"palc",
|
||||||
|
"redb",
|
||||||
"sdgb-api",
|
"sdgb-api",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"snafu",
|
"snafu",
|
||||||
@@ -1753,6 +1784,12 @@ version = "0.9.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unty"
|
||||||
|
version = "0.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vcpkg"
|
name = "vcpkg"
|
||||||
version = "0.2.15"
|
version = "0.2.15"
|
||||||
@@ -1765,6 +1802,12 @@ version = "0.9.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "virtue"
|
||||||
|
version = "0.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.11.1+wasi-snapshot-preview1"
|
version = "0.11.1+wasi-snapshot-preview1"
|
||||||
|
|||||||
@@ -6,9 +6,10 @@ edition = "2024"
|
|||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["compio"]
|
default = ["compio", "bincode"]
|
||||||
compio = ["dep:compio"]
|
compio = ["dep:compio"]
|
||||||
tokio = ["dep:tokio"]
|
tokio = ["dep:tokio"]
|
||||||
|
bincode = ["dep:bincode"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
snafu = { workspace = true }
|
snafu = { workspace = true }
|
||||||
@@ -36,3 +37,4 @@ flate2 = "1.1.2"
|
|||||||
cbc = "0.1.2"
|
cbc = "0.1.2"
|
||||||
aes = "0.8.4"
|
aes = "0.8.4"
|
||||||
cipher = { version = "0.4.4", features = ["block-padding"] }
|
cipher = { version = "0.4.4", features = ["block-padding"] }
|
||||||
|
bincode = { version = "2.0.1", optional = true }
|
||||||
|
|||||||
@@ -4,3 +4,5 @@ pub mod title;
|
|||||||
|
|
||||||
mod error;
|
mod error;
|
||||||
pub use error::ApiError;
|
pub use error::ApiError;
|
||||||
|
|
||||||
|
pub use bincode;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use bincode::{Decode, Encode};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
@@ -8,10 +9,10 @@ pub struct GetUserPreviewApi {
|
|||||||
pub user_id: u32,
|
pub user_id: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct GetUserPreviewApiResp {
|
pub struct GetUserPreviewApiResp {
|
||||||
pub user_id: i64,
|
pub user_id: u32,
|
||||||
pub user_name: String,
|
pub user_name: String,
|
||||||
pub is_login: bool,
|
pub is_login: bool,
|
||||||
pub last_rom_version: String,
|
pub last_rom_version: String,
|
||||||
|
|||||||
@@ -6,12 +6,15 @@ authors = ["mokurin000"]
|
|||||||
description = "CLI tool for SDGB protocol"
|
description = "CLI tool for SDGB protocol"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["compio"]
|
default = ["compio", "cache"]
|
||||||
|
|
||||||
compio = ["dep:compio", "sdgb-api/compio"]
|
compio = ["dep:compio", "sdgb-api/compio"]
|
||||||
tokio = ["dep:tokio", "sdgb-api/tokio"]
|
tokio = ["dep:tokio", "sdgb-api/tokio"]
|
||||||
|
|
||||||
|
cache = ["dep:redb"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
sdgb-api = { workspace = true }
|
sdgb-api = { workspace = true, features = ["bincode"] }
|
||||||
|
|
||||||
snafu = { workspace = true }
|
snafu = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
@@ -27,3 +30,4 @@ spdlog-rs = { version = "0.4.3", default-features = false, features = [
|
|||||||
"release-level-info",
|
"release-level-info",
|
||||||
] }
|
] }
|
||||||
futures-util = "0.3.31"
|
futures-util = "0.3.31"
|
||||||
|
redb = { version = "2.6.1", optional = true }
|
||||||
|
|||||||
25
sdgb-cli/src/cache/mod.rs
vendored
Normal file
25
sdgb-cli/src/cache/mod.rs
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
use std::sync::LazyLock;
|
||||||
|
|
||||||
|
use redb::{Table, TableDefinition, WriteTransaction};
|
||||||
|
|
||||||
|
static DATABASE: LazyLock<redb::Database> = LazyLock::new(|| {
|
||||||
|
redb::Database::builder()
|
||||||
|
.create("players.redb")
|
||||||
|
.expect("failed to open database")
|
||||||
|
});
|
||||||
|
const DIFINITION: TableDefinition<'_, u32, Vec<u8>> = redb::TableDefinition::new("players");
|
||||||
|
|
||||||
|
pub fn write_txn() -> Result<WriteTransaction, redb::Error> {
|
||||||
|
Ok(DATABASE.begin_write()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn open_table(write: &WriteTransaction) -> Result<Table<'_, u32, Vec<u8>>, redb::Error> {
|
||||||
|
Ok(write.open_table(DIFINITION)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_db() -> Result<(), redb::Error> {
|
||||||
|
let write_txn = DATABASE.begin_write()?;
|
||||||
|
write_txn.open_table(DIFINITION)?;
|
||||||
|
write_txn.commit()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ use std::{
|
|||||||
use futures_util::StreamExt;
|
use futures_util::StreamExt;
|
||||||
use nyquest_preset::nyquest::ClientBuilder;
|
use nyquest_preset::nyquest::ClientBuilder;
|
||||||
use palc::Parser;
|
use palc::Parser;
|
||||||
|
|
||||||
use sdgb_api::{
|
use sdgb_api::{
|
||||||
ApiError,
|
ApiError,
|
||||||
all_net::QRCode,
|
all_net::QRCode,
|
||||||
@@ -23,6 +24,8 @@ use spdlog::{error, info, warn};
|
|||||||
|
|
||||||
use crate::commands::Cli;
|
use crate::commands::Cli;
|
||||||
|
|
||||||
|
#[cfg(feature = "cache")]
|
||||||
|
mod cache;
|
||||||
mod commands;
|
mod commands;
|
||||||
|
|
||||||
#[cfg_attr(feature = "compio", compio::main)]
|
#[cfg_attr(feature = "compio", compio::main)]
|
||||||
@@ -102,9 +105,33 @@ async fn main() -> Result<(), Box<dyn snafu::Error>> {
|
|||||||
user_ids.push(user_id);
|
user_ids.push(user_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
let users = futures_util::stream::iter(user_ids)
|
#[cfg(feature = "cache")]
|
||||||
|
let _ = cache::init_db();
|
||||||
|
#[cfg(feature = "cache")]
|
||||||
|
let write = cache::write_txn()?;
|
||||||
|
#[cfg(feature = "cache")]
|
||||||
|
let config = sdgb_api::bincode::config::Configuration::<
|
||||||
|
sdgb_api::bincode::config::LittleEndian,
|
||||||
|
>::default()
|
||||||
|
.with_no_limit();
|
||||||
|
|
||||||
|
let players = futures_util::stream::iter(user_ids)
|
||||||
.map(async |user_id| {
|
.map(async |user_id| {
|
||||||
info!("preview: {user_id}");
|
#[cfg(feature = "cache")]
|
||||||
|
{
|
||||||
|
use redb::ReadableTable;
|
||||||
|
use sdgb_api::bincode::borrow_decode_from_slice;
|
||||||
|
|
||||||
|
let cache_table = cache::open_table(&write)?;
|
||||||
|
let data = cache_table.get(user_id)?;
|
||||||
|
if let Some(data) = data {
|
||||||
|
let decoded: (GetUserPreviewApiResp, _) =
|
||||||
|
borrow_decode_from_slice(&data.value(), config)?;
|
||||||
|
|
||||||
|
return Ok(decoded.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let resp = Sdgb1_50::request::<_, GetUserPreviewApiResp>(
|
let resp = Sdgb1_50::request::<_, GetUserPreviewApiResp>(
|
||||||
&client,
|
&client,
|
||||||
APIMethod::GetUserPreviewApi,
|
APIMethod::GetUserPreviewApi,
|
||||||
@@ -113,15 +140,30 @@ async fn main() -> Result<(), Box<dyn snafu::Error>> {
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
if let Err(e) = &resp {
|
match &resp {
|
||||||
if matches!(e, ApiError::JSON { .. }) {
|
Ok(resp) => {
|
||||||
|
info!("preview: {user_id} succeed");
|
||||||
|
|
||||||
|
#[cfg(feature = "cache")]
|
||||||
|
{
|
||||||
|
use sdgb_api::bincode::encode_to_vec;
|
||||||
|
|
||||||
|
if let Ok(mut table) = cache::open_table(&write)
|
||||||
|
&& let Ok(encoded) = encode_to_vec(resp, config)
|
||||||
|
{
|
||||||
|
_ = table.insert(resp.user_id, encoded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(ApiError::JSON { .. }) => {
|
||||||
warn!("account unregistered: {user_id}");
|
warn!("account unregistered: {user_id}");
|
||||||
} else {
|
}
|
||||||
|
Err(e) => {
|
||||||
error!("preview failed: {e}");
|
error!("preview failed: {e}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resp
|
Result::<_, Box<dyn snafu::Error>>::Ok(resp?)
|
||||||
})
|
})
|
||||||
.buffer_unordered(20)
|
.buffer_unordered(20)
|
||||||
.filter_map(async |r| r.ok())
|
.filter_map(async |r| r.ok())
|
||||||
@@ -132,8 +174,8 @@ async fn main() -> Result<(), Box<dyn snafu::Error>> {
|
|||||||
.write(true)
|
.write(true)
|
||||||
.truncate(true)
|
.truncate(true)
|
||||||
.create(true)
|
.create(true)
|
||||||
.open("users.json")?;
|
.open("players.json")?;
|
||||||
serde_json::to_writer_pretty(output, &users)?;
|
serde_json::to_writer_pretty(output, &players)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user