Skip to content

Commit

Permalink
The banishment of Address (#1880)
Browse files Browse the repository at this point in the history
Co-authored-by: Jeffrey Dallatezza <[email protected]>
  • Loading branch information
cloutiertyler and jsdt authored Oct 23, 2024
1 parent 9347573 commit 83fc5c3
Show file tree
Hide file tree
Showing 57 changed files with 739 additions and 667 deletions.
4 changes: 2 additions & 2 deletions crates/bench/benches/subscription.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ fn eval(c: &mut Criterion) {
let tx = raw.db.begin_tx();
let query = compile_read_only_query(&raw.db, &AuthCtx::for_testing(), &tx, sql).unwrap();
let query: ExecutionSet = query.into();
let ctx = &ExecutionContext::subscribe(raw.db.address());
let ctx = &ExecutionContext::subscribe(raw.db.database_identity());
b.iter(|| {
drop(black_box(query.eval::<BsatnFormat>(
ctx,
Expand Down Expand Up @@ -134,7 +134,7 @@ fn eval(c: &mut Criterion) {
);
bench_eval(c, "full-join", &name);

let ctx_incr = &ExecutionContext::incremental_update(raw.db.address());
let ctx_incr = &ExecutionContext::incremental_update(raw.db.database_identity());

// To profile this benchmark for 30s
// samply record -r 10000000 cargo bench --bench=subscription --profile=profiling -- incr-select --exact --profile-time=30
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ spacetimedb
│ │ └── syn (*)
│ ├── hex
│ ├── itertools (*)
│ ├── rand (*)
│ ├── spacetimedb_bindings_macro (*)
│ ├── spacetimedb_data_structures
│ │ ├── ahash
Expand Down
13 changes: 10 additions & 3 deletions crates/cli/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,28 @@ use serde_json::value::RawValue;
use spacetimedb_lib::db::raw_def::v9::RawModuleDefV9;
use spacetimedb_lib::de::serde::DeserializeWrapper;
use spacetimedb_lib::sats::ProductType;
use spacetimedb_lib::Address;
use spacetimedb_lib::Identity;

static APP_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"),);

#[derive(Debug, Clone)]
pub struct Connection {
pub(crate) host: String,
pub(crate) address: Address,
pub(crate) database_identity: Identity,
pub(crate) database: String,
pub(crate) auth_header: Option<String>,
}

impl Connection {
pub fn db_uri(&self, endpoint: &str) -> String {
[&self.host, "/database/", endpoint, "/", &self.address.to_hex()].concat()
[
&self.host,
"/database/",
endpoint,
"/",
&self.database_identity.to_hex(),
]
.concat()
}
}

Expand Down
39 changes: 20 additions & 19 deletions crates/cli/src/subcommands/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ use crate::common_args;
use crate::config::Config;
use crate::edit_distance::{edit_distance, find_best_match_for_name};
use crate::util;
use crate::util::{add_auth_header_opt, database_address, get_auth_header_only};
use crate::util::{add_auth_header_opt, database_identity, get_auth_header_only};
use anyhow::{bail, Context, Error};
use clap::{Arg, ArgMatches};
use itertools::Either;
use serde_json::Value;
use spacetimedb::Identity;
use spacetimedb_lib::de::serde::deserialize_from;
use spacetimedb_lib::sats::{AlgebraicType, AlgebraicTypeRef, Typespace};
use spacetimedb_lib::{Address, ProductTypeElement};
use spacetimedb_lib::ProductTypeElement;
use std::fmt::Write;
use std::iter;

Expand All @@ -19,7 +20,7 @@ pub fn cli() -> clap::Command {
.arg(
Arg::new("database")
.required(true)
.help("The database domain or address to use to invoke the call"),
.help("The database name or identity to use to invoke the call"),
)
.arg(
Arg::new("reducer_name")
Expand All @@ -46,19 +47,19 @@ pub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), Error> {
let identity = args.get_one::<String>("identity");
let anon_identity = args.get_flag("anon_identity");

let address = database_address(&config, database, server).await?;
let database_identity = database_identity(&config, database, server).await?;

let builder = reqwest::Client::new().post(format!(
"{}/database/call/{}/{}",
config.get_host_url(server)?,
address.clone(),
database_identity.clone(),
reducer_name
));
let auth_header = get_auth_header_only(&mut config, anon_identity, identity, server).await?;
let builder = add_auth_header_opt(builder, &auth_header);
let describe_reducer = util::describe_reducer(
&mut config,
address,
database_identity,
server.map(|x| x.to_string()),
reducer_name.clone(),
anon_identity,
Expand Down Expand Up @@ -90,11 +91,11 @@ pub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), Error> {
let error = Err(e).context(format!("Response text: {}", response_text));

let error_msg = if response_text.starts_with("no such reducer") {
no_such_reducer(config, &address, database, &auth_header, reducer_name, server).await
no_such_reducer(config, &database_identity, database, &auth_header, reducer_name, server).await
} else if response_text.starts_with("invalid arguments") {
invalid_arguments(
config,
&address,
&database_identity,
database,
&auth_header,
reducer_name,
Expand All @@ -115,16 +116,16 @@ pub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), Error> {
/// Returns an error message for when `reducer` is called with wrong arguments.
async fn invalid_arguments(
config: Config,
addr: &Address,
identity: &Identity,
db: &str,
auth_header: &Option<String>,
reducer: &str,
text: &str,
server: Option<&str>,
) -> String {
let mut error = format!(
"Invalid arguments provided for reducer `{}` for database `{}` resolving to address `{}`.",
reducer, db, addr
"Invalid arguments provided for reducer `{}` for database `{}` resolving to identity `{}`.",
reducer, db, identity
);

if let Some((actual, expected)) = find_actual_expected(text).filter(|(a, e)| a != e) {
Expand All @@ -136,7 +137,7 @@ async fn invalid_arguments(
.unwrap();
}

if let Some(sig) = schema_json(config, addr, auth_header, true, server)
if let Some(sig) = schema_json(config, identity, auth_header, true, server)
.await
.and_then(|schema| reducer_signature(schema, reducer))
{
Expand Down Expand Up @@ -198,18 +199,18 @@ fn reducer_signature(schema_json: Value, reducer_name: &str) -> Option<String> {
/// Returns an error message for when `reducer` does not exist in `db`.
async fn no_such_reducer(
config: Config,
addr: &Address,
database_identity: &Identity,
db: &str,
auth_header: &Option<String>,
reducer: &str,
server: Option<&str>,
) -> String {
let mut error = format!(
"No such reducer `{}` for database `{}` resolving to address `{}`.",
reducer, db, addr
"No such reducer `{}` for database `{}` resolving to identity `{}`.",
reducer, db, database_identity
);

if let Some(schema) = schema_json(config, addr, auth_header, false, server).await {
if let Some(schema) = schema_json(config, database_identity, auth_header, false, server).await {
add_reducer_ctx_to_err(&mut error, schema, reducer);
}

Expand Down Expand Up @@ -255,20 +256,20 @@ fn add_reducer_ctx_to_err(error: &mut String, schema_json: Value, reducer_name:
}
}

/// Fetch the schema as JSON for the database at `address`.
/// Fetch the schema as JSON for the database at `identity`.
///
/// The value of `expand` determines how detailed information to fetch.
async fn schema_json(
config: Config,
address: &Address,
identity: &Identity,
auth_header: &Option<String>,
expand: bool,
server: Option<&str>,
) -> Option<Value> {
let builder = reqwest::Client::new().get(format!(
"{}/database/schema/{}",
config.get_host_url(server).ok()?,
address
identity
));
let builder = add_auth_header_opt(builder, auth_header);

Expand Down
8 changes: 4 additions & 4 deletions crates/cli/src/subcommands/delete.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::common_args;
use crate::config::Config;
use crate::util::{add_auth_header_opt, database_address, get_auth_header_only};
use crate::util::{add_auth_header_opt, database_identity, get_auth_header_only};
use clap::{Arg, ArgMatches};

pub fn cli() -> clap::Command {
Expand All @@ -9,7 +9,7 @@ pub fn cli() -> clap::Command {
.arg(
Arg::new("database")
.required(true)
.help("The domain or address of the database to delete"),
.help("The name or identity of the database to delete"),
)
.arg(
common_args::identity()
Expand All @@ -28,9 +28,9 @@ pub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::E
let database = args.get_one::<String>("database").unwrap();
let identity_or_name = args.get_one::<String>("identity");

let address = database_address(&config, database, server).await?;
let identity = database_identity(&config, database, server).await?;

let builder = reqwest::Client::new().post(format!("{}/database/delete/{}", config.get_host_url(server)?, address));
let builder = reqwest::Client::new().post(format!("{}/database/delete/{}", config.get_host_url(server)?, identity));
let auth_header = get_auth_header_only(&mut config, false, identity_or_name, server).await?;
let builder = add_auth_header_opt(builder, &auth_header);
builder.send().await?.error_for_status()?;
Expand Down
10 changes: 5 additions & 5 deletions crates/cli/src/subcommands/describe.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::common_args;
use crate::config::Config;
use crate::util::{add_auth_header_opt, database_address, get_auth_header_only};
use crate::util::{add_auth_header_opt, database_identity, get_auth_header_only};
use clap::{Arg, ArgMatches};

pub fn cli() -> clap::Command {
Expand All @@ -9,7 +9,7 @@ pub fn cli() -> clap::Command {
.arg(
Arg::new("database")
.required(true)
.help("The domain or address of the database to describe"),
.help("The name or identity of the database to describe"),
)
.arg(
Arg::new("entity_type")
Expand Down Expand Up @@ -46,14 +46,14 @@ pub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::E
let identity = args.get_one::<String>("identity");
let anon_identity = args.get_flag("anon_identity");

let address = database_address(&config, database, server).await?;
let database_identity = database_identity(&config, database, server).await?;

let builder = reqwest::Client::new().get(match entity_name {
None => format!("{}/database/schema/{}", config.get_host_url(server)?, address),
None => format!("{}/database/schema/{}", config.get_host_url(server)?, database_identity),
Some(entity_name) => format!(
"{}/database/schema/{}/{}/{}",
config.get_host_url(server)?,
address,
database_identity,
entity_type.unwrap(),
entity_name
),
Expand Down
34 changes: 20 additions & 14 deletions crates/cli/src/subcommands/dns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,25 +40,25 @@ fn get_subcommands() -> Vec<Command> {
)
.after_help("Run `spacetime dns register-tld --help` for more detailed information.\n"),
Command::new("lookup")
.about("Resolves a domain to a database address")
.about("Resolves a domain to a database identity")
.arg(Arg::new("domain").required(true).help("The name of the domain to lookup"))
.arg(
common_args::server()
.help("The nickname, host name or URL of the server on which to look up the domain name"),
)
.after_help("Run `spacetime dns lookup --help` for more detailed information"),
Command::new("reverse-lookup")
.about("Returns the domains for the provided database address")
.arg(Arg::new("address").required(true).help("The address you would like to find all of the known domains for"))
.about("Returns the domains for the provided database identity")
.arg(Arg::new("database-identity").required(true).help("The database identity you would like to find all of the known domains for"))
.arg(
common_args::server()
.help("The nickname, host name or URL of the server on which to look up the address"),
.help("The nickname, host name or URL of the server on which to look up the database identity"),
)
.after_help("Run `spacetime dns reverse-lookup --help` for more detailed information.\n"),
Command::new("set-name")
.about("Sets the domain of the database")
.arg(Arg::new("domain").required(true).help("The domain you would like to assign or create"))
.arg(Arg::new("address").required(true).help("The database address to assign to the domain"))
.arg(Arg::new("database-identity").required(true).help("The database identity to assign to the domain"))
.arg(common_args::identity().long_help(
"The identity that owns the tld for this domain. If no identity is specified, the default identity is used.",
).help("The identity that owns the tld for this domain"))
Expand Down Expand Up @@ -107,8 +107,8 @@ pub async fn exec_dns_lookup(config: Config, args: &ArgMatches) -> Result<(), an

let response = spacetime_dns(&config, domain, server).await?;
match response {
DnsLookupResponse::Success { domain: _, address } => {
println!("{}", address);
DnsLookupResponse::Success { domain: _, identity } => {
println!("{}", identity);
}
DnsLookupResponse::Failure { domain } => {
println!("No such database: {}", domain);
Expand All @@ -118,11 +118,14 @@ pub async fn exec_dns_lookup(config: Config, args: &ArgMatches) -> Result<(), an
}

pub async fn exec_reverse_dns(config: Config, args: &ArgMatches) -> Result<(), anyhow::Error> {
let addr = args.get_one::<String>("address").unwrap();
let database_identity = args.get_one::<String>("database-identity").unwrap();
let server = args.get_one::<String>("server").map(|s| s.as_ref());
let response = spacetime_reverse_dns(&config, addr, server).await?;
let response = spacetime_reverse_dns(&config, database_identity, server).await?;
if response.names.is_empty() {
Err(anyhow::anyhow!("Could not find a name for the address: {}", addr))
Err(anyhow::anyhow!(
"Could not find a name for the identity: {}",
database_identity
))
} else {
for name in response.names {
println!("{}", name);
Expand All @@ -133,7 +136,7 @@ pub async fn exec_reverse_dns(config: Config, args: &ArgMatches) -> Result<(), a

pub async fn exec_set_name(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::Error> {
let domain = args.get_one::<String>("domain").unwrap();
let address = args.get_one::<String>("address").unwrap();
let database_identity = args.get_one::<String>("database-identity").unwrap();
let identity = args.get_one::<String>("identity");
let server = args.get_one::<String>("server").map(|s| s.as_ref());
let auth_header = get_auth_header_only(&mut config, false, identity, server).await?;
Expand All @@ -142,7 +145,7 @@ pub async fn exec_set_name(mut config: Config, args: &ArgMatches) -> Result<(),
format!("{}/database/set_name", config.get_host_url(server)?).as_str(),
[
("domain", domain.clone()),
("address", address.clone()),
("database_identity", database_identity.clone()),
("register_tld", "true".to_string()),
],
)?);
Expand All @@ -153,8 +156,11 @@ pub async fn exec_set_name(mut config: Config, args: &ArgMatches) -> Result<(),
println!("{}", String::from_utf8_lossy(&bytes[..]));
let result: InsertDomainResult = serde_json::from_slice(&bytes[..]).unwrap();
match result {
InsertDomainResult::Success { domain, address } => {
println!("Domain set to {} for address {}.", domain, address);
InsertDomainResult::Success {
domain,
database_identity,
} => {
println!("Domain set to {} for identity {}.", domain, database_identity);
}
InsertDomainResult::TldNotRegistered { domain } => {
return Err(anyhow::anyhow!(
Expand Down
14 changes: 7 additions & 7 deletions crates/cli/src/subcommands/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use anyhow::Context;
use clap::{ArgMatches, Command};
use reqwest::StatusCode;
use serde::Deserialize;
use spacetimedb_lib::Address;
use spacetimedb::Identity;
use tabled::{
settings::{object::Columns, Alignment, Modify, Style},
Table, Tabled,
Expand All @@ -23,13 +23,13 @@ pub fn cli() -> Command {

#[derive(Deserialize)]
struct DatabasesResult {
pub addresses: Vec<AddressRow>,
pub identities: Vec<IdentityRow>,
}

#[derive(Tabled, Deserialize)]
#[serde(transparent)]
struct AddressRow {
pub db_address: Address,
struct IdentityRow {
pub db_identity: Identity,
}

pub async fn exec(config: Config, args: &ArgMatches) -> Result<(), anyhow::Error> {
Expand Down Expand Up @@ -64,12 +64,12 @@ pub async fn exec(config: Config, args: &ArgMatches) -> Result<(), anyhow::Error
let result: DatabasesResult = res.json().await?;

let identity = identity_config.nick_or_identity();
if !result.addresses.is_empty() {
let mut table = Table::new(result.addresses);
if !result.identities.is_empty() {
let mut table = Table::new(result.identities);
table
.with(Style::psql())
.with(Modify::new(Columns::first()).with(Alignment::left()));
println!("Associated database addresses for {}:\n", identity);
println!("Associated database identities for {}:\n", identity);
println!("{}", table);
} else {
println!("No databases found for {}.", identity);
Expand Down
Loading

2 comments on commit 83fc5c3

@github-actions
Copy link

@github-actions github-actions bot commented on 83fc5c3 Oct 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmarking failed. Please check the workflow run for details.

@github-actions
Copy link

@github-actions github-actions bot commented on 83fc5c3 Oct 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Callgrind benchmark results

Callgrind Benchmark Report

These benchmarks were run using callgrind,
an instruction-level profiler. They allow comparisons between sqlite (sqlite), SpacetimeDB running through a module (stdb_module), and the underlying SpacetimeDB data storage engine (stdb_raw). Callgrind emulates a CPU to collect the below estimates.

Measurement changes larger than five percent are in bold.

In-memory benchmarks

callgrind: empty transaction

db total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw 6514 5392 20.81% 6548 5500 19.05%
sqlite 5579 5579 0.00% 5971 5963 0.13%

callgrind: filter

db schema indices count preload _column data_type total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw u32_u64_str no_index 64 128 1 u64 76680 75558 1.48% 77050 76060 1.30%
stdb_raw u32_u64_str no_index 64 128 2 string 118922 117800 0.95% 119406 118404 0.85%
stdb_raw u32_u64_str btree_each_column 64 128 2 string 25232 24107 4.67% 25676 24647 4.17%
stdb_raw u32_u64_str btree_each_column 64 128 1 u64 24197 23075 4.86% 24527 23489 4.42%
sqlite u32_u64_str no_index 64 128 2 string 144695 144695 0.00% 146107 146087 0.01%
sqlite u32_u64_str no_index 64 128 1 u64 124044 124044 0.00% 125126 125194 -0.05%
sqlite u32_u64_str btree_each_column 64 128 1 u64 131361 131361 0.00% 132691 132811 -0.09%
sqlite u32_u64_str btree_each_column 64 128 2 string 134494 134494 0.00% 136038 136170 -0.10%

callgrind: insert bulk

db schema indices count preload total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw u32_u64_str unique_0 64 128 903926 900365 0.40% 956316 948663 0.81%
stdb_raw u32_u64_str btree_each_column 64 128 1056047 1051489 0.43% 1126773 1106051 1.87%
sqlite u32_u64_str unique_0 64 128 398320 398320 0.00% 416972 417272 -0.07%
sqlite u32_u64_str btree_each_column 64 128 983637 983637 0.00% 1022363 1020257 0.21%

callgrind: iterate

db schema indices count total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw u32_u64_str unique_0 1024 153906 152784 0.73% 153944 152894 0.69%
stdb_raw u32_u64_str unique_0 64 16931 15809 7.10% 16969 15911 6.65%
sqlite u32_u64_str unique_0 1024 1067255 1067255 0.00% 1070643 1070565 0.01%
sqlite u32_u64_str unique_0 64 76201 76201 0.00% 77247 77249 -0.00%

callgrind: serialize_product_value

count format total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
64 json 47528 47528 0.00% 50252 50150 0.20%
64 bsatn 25509 25509 0.00% 27685 27685 0.00%
16 bsatn 8200 8200 0.00% 9492 9492 0.00%
16 json 12188 12188 0.00% 14160 14024 0.97%

callgrind: update bulk

db schema indices count preload total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw u32_u64_str unique_0 1024 1024 21208844 20995425 1.02% 21908516 21429733 2.23%
stdb_raw u32_u64_str unique_0 64 128 1316935 1310205 0.51% 1364043 1372259 -0.60%
sqlite u32_u64_str unique_0 1024 1024 1802128 1802146 -0.00% 1811398 1811606 -0.01%
sqlite u32_u64_str unique_0 64 128 128474 128474 0.00% 131252 131368 -0.09%
On-disk benchmarks

callgrind: empty transaction

db total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw 6519 5397 20.79% 6553 5501 19.12%
sqlite 5621 5621 0.00% 6081 6057 0.40%

callgrind: filter

db schema indices count preload _column data_type total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw u32_u64_str no_index 64 128 1 u64 76685 75563 1.48% 77059 76061 1.31%
stdb_raw u32_u64_str no_index 64 128 2 string 118927 117805 0.95% 119467 118377 0.92%
stdb_raw u32_u64_str btree_each_column 64 128 2 string 25235 24113 4.65% 25647 24673 3.95%
stdb_raw u32_u64_str btree_each_column 64 128 1 u64 24202 23080 4.86% 24528 23490 4.42%
sqlite u32_u64_str no_index 64 128 1 u64 125965 125965 0.00% 127391 127419 -0.02%
sqlite u32_u64_str no_index 64 128 2 string 146616 146616 0.00% 148372 148276 0.06%
sqlite u32_u64_str btree_each_column 64 128 2 string 136616 136634 -0.01% 138566 138644 -0.06%
sqlite u32_u64_str btree_each_column 64 128 1 u64 133457 133457 0.00% 135259 135331 -0.05%

callgrind: insert bulk

db schema indices count preload total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw u32_u64_str unique_0 64 128 853540 849554 0.47% 904488 896488 0.89%
stdb_raw u32_u64_str btree_each_column 64 128 1005759 1001024 0.47% 1074569 1054024 1.95%
sqlite u32_u64_str unique_0 64 128 415857 415875 -0.00% 433903 434133 -0.05%
sqlite u32_u64_str btree_each_column 64 128 1021898 1021898 0.00% 1059954 1057184 0.26%

callgrind: iterate

db schema indices count total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw u32_u64_str unique_0 1024 153911 152789 0.73% 153949 152891 0.69%
stdb_raw u32_u64_str unique_0 64 16936 15814 7.09% 16974 15916 6.65%
sqlite u32_u64_str unique_0 1024 1070323 1070323 0.00% 1074173 1074079 0.01%
sqlite u32_u64_str unique_0 64 77973 78006 -0.04% 79279 79270 0.01%

callgrind: serialize_product_value

count format total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
64 json 47528 47528 0.00% 50252 50150 0.20%
64 bsatn 25509 25509 0.00% 27685 27685 0.00%
16 bsatn 8200 8200 0.00% 9492 9492 0.00%
16 json 12188 12188 0.00% 14160 14024 0.97%

callgrind: update bulk

db schema indices count preload total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw u32_u64_str unique_0 1024 1024 19512358 19482498 0.15% 20246478 19958834 1.44%
stdb_raw u32_u64_str unique_0 64 128 1269770 1264864 0.39% 1346246 1325728 1.55%
sqlite u32_u64_str unique_0 1024 1024 1809689 1809689 0.00% 1818261 1818375 -0.01%
sqlite u32_u64_str unique_0 64 128 132618 132606 0.01% 135476 135652 -0.13%

Please sign in to comment.