Switch to uuidv7 (#864)
* Add uuid_v7 * Actually use the new uuid * Add a test to ensure all uuids are unique, even in a tight loop * Make clippy happy
This commit is contained in:
parent
301296fae5
commit
03dd3ddf8b
7 changed files with 70 additions and 7 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -152,6 +152,7 @@ name = "atuin-common"
|
||||||
version = "14.0.0"
|
version = "14.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
|
"rand",
|
||||||
"serde",
|
"serde",
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::env;
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use atuin_common::utils::uuid_v4;
|
use atuin_common::utils::uuid_v7;
|
||||||
|
|
||||||
// Any new fields MUST be Optional<>!
|
// Any new fields MUST be Optional<>!
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, sqlx::FromRow)]
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, sqlx::FromRow)]
|
||||||
|
@ -48,12 +48,12 @@ impl History {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let session = session
|
let session = session
|
||||||
.or_else(|| env::var("ATUIN_SESSION").ok())
|
.or_else(|| env::var("ATUIN_SESSION").ok())
|
||||||
.unwrap_or_else(uuid_v4);
|
.unwrap_or_else(|| uuid_v7().as_simple().to_string());
|
||||||
let hostname =
|
let hostname =
|
||||||
hostname.unwrap_or_else(|| format!("{}:{}", whoami::hostname(), whoami::username()));
|
hostname.unwrap_or_else(|| format!("{}:{}", whoami::hostname(), whoami::username()));
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
id: uuid_v4(),
|
id: uuid_v7().as_simple().to_string(),
|
||||||
timestamp,
|
timestamp,
|
||||||
command,
|
command,
|
||||||
cwd,
|
cwd,
|
||||||
|
|
|
@ -6,7 +6,7 @@ use directories::UserDirs;
|
||||||
use eyre::{eyre, Result};
|
use eyre::{eyre, Result};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use atuin_common::utils::uuid_v4;
|
use atuin_common::utils::uuid_v7;
|
||||||
|
|
||||||
use super::{get_histpath, unix_byte_lines, Importer, Loader};
|
use super::{get_histpath, unix_byte_lines, Importer, Loader};
|
||||||
use crate::history::History;
|
use crate::history::History;
|
||||||
|
@ -123,13 +123,13 @@ impl Importer for Resh {
|
||||||
};
|
};
|
||||||
|
|
||||||
h.push(History {
|
h.push(History {
|
||||||
id: uuid_v4(),
|
id: uuid_v7().as_simple().to_string(),
|
||||||
timestamp,
|
timestamp,
|
||||||
duration,
|
duration,
|
||||||
exit: entry.exit_code,
|
exit: entry.exit_code,
|
||||||
command: entry.cmd_line,
|
command: entry.cmd_line,
|
||||||
cwd: entry.pwd,
|
cwd: entry.pwd,
|
||||||
session: uuid_v4(),
|
session: uuid_v7().as_simple().to_string(),
|
||||||
hostname: entry.host,
|
hostname: entry.host,
|
||||||
deleted_at: None,
|
deleted_at: None,
|
||||||
})
|
})
|
||||||
|
|
5
atuin-client/src/message.rs
Normal file
5
atuin-client/src/message.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
|
||||||
|
pub struct Message {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub type: String,
|
||||||
|
}
|
|
@ -14,3 +14,4 @@ repository = "https://github.com/ellie/atuin"
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
serde = { version = "1.0.145", features = ["derive"] }
|
serde = { version = "1.0.145", features = ["derive"] }
|
||||||
uuid = { version = "1.2", features = ["v4"] }
|
uuid = { version = "1.2", features = ["v4"] }
|
||||||
|
rand = { version = "0.8.5", features = ["std"] }
|
||||||
|
|
|
@ -2,8 +2,46 @@ use std::env;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use chrono::{Months, NaiveDate};
|
use chrono::{Months, NaiveDate};
|
||||||
|
use rand::RngCore;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
pub fn random_bytes<const N: usize>() -> [u8; N] {
|
||||||
|
let mut ret = [0u8; N];
|
||||||
|
|
||||||
|
rand::thread_rng().fill_bytes(&mut ret);
|
||||||
|
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// basically just ripped from the uuid crate. they have it as unstable, but we can use it fine.
|
||||||
|
const fn encode_unix_timestamp_millis(millis: u64, random_bytes: &[u8; 10]) -> Uuid {
|
||||||
|
let millis_high = ((millis >> 16) & 0xFFFF_FFFF) as u32;
|
||||||
|
let millis_low = (millis & 0xFFFF) as u16;
|
||||||
|
|
||||||
|
let random_and_version =
|
||||||
|
(random_bytes[0] as u16 | ((random_bytes[1] as u16) << 8) & 0x0FFF) | (0x7 << 12);
|
||||||
|
|
||||||
|
let mut d4 = [0; 8];
|
||||||
|
|
||||||
|
d4[0] = (random_bytes[2] & 0x3F) | 0x80;
|
||||||
|
d4[1] = random_bytes[3];
|
||||||
|
d4[2] = random_bytes[4];
|
||||||
|
d4[3] = random_bytes[5];
|
||||||
|
d4[4] = random_bytes[6];
|
||||||
|
d4[5] = random_bytes[7];
|
||||||
|
d4[6] = random_bytes[8];
|
||||||
|
d4[7] = random_bytes[9];
|
||||||
|
|
||||||
|
Uuid::from_fields(millis_high, millis_low, random_and_version, &d4)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn uuid_v7() -> Uuid {
|
||||||
|
let bytes = random_bytes();
|
||||||
|
let now: u64 = chrono::Utc::now().timestamp_millis() as u64;
|
||||||
|
|
||||||
|
encode_unix_timestamp_millis(now, &bytes)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn uuid_v4() -> String {
|
pub fn uuid_v4() -> String {
|
||||||
Uuid::new_v4().as_simple().to_string()
|
Uuid::new_v4().as_simple().to_string()
|
||||||
}
|
}
|
||||||
|
@ -59,6 +97,8 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_dirs() {
|
fn test_dirs() {
|
||||||
// these tests need to be run sequentially to prevent race condition
|
// these tests need to be run sequentially to prevent race condition
|
||||||
|
@ -117,4 +157,20 @@ mod tests {
|
||||||
// leap years
|
// leap years
|
||||||
assert_eq!(get_days_from_month(2024, 2), 29);
|
assert_eq!(get_days_from_month(2024, 2), 29);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn uuid_is_unique() {
|
||||||
|
let how_many: usize = 1000000;
|
||||||
|
|
||||||
|
// for peace of mind
|
||||||
|
let mut uuids: HashSet<Uuid> = HashSet::with_capacity(how_many);
|
||||||
|
|
||||||
|
// there will be many in the same millisecond
|
||||||
|
for _ in 0..how_many {
|
||||||
|
let uuid = uuid_v7();
|
||||||
|
uuids.insert(uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(uuids.len(), how_many);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,7 +60,7 @@ impl AtuinCmd {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Self::Uuid => {
|
Self::Uuid => {
|
||||||
println!("{}", atuin_common::utils::uuid_v4());
|
println!("{}", atuin_common::utils::uuid_v7().as_simple());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Self::GenCompletions { shell, out_dir } => {
|
Self::GenCompletions { shell, out_dir } => {
|
||||||
|
|
Loading…
Reference in a new issue