I have no idea why this is not working. My tests pass the earlier expiry stage, but when reading the expiry from the rdb file the same code is failing. Can anyone suggest how to fix this?
pub fn is_key_expired(created_timestamp: SystemTime, expiry: u128) -> bool {
SystemTime::now().duration_since(created_timestamp).unwrap_or(Duration::from_secs(0)) >= Duration::from_millis(expiry as u64)
}
//read expiry, if any
let mut expiry_indicator = [0; 1];
reader.read_exact(&mut expiry_indicator)?;
match expiry_indicator[0] {
0xFC => {
let mut expiry = [0; 8];
reader.read_exact(&mut expiry)?;
let expiry = u64::from_le_bytes(expiry) as u128;
//let expiry = reader.read_u64::<LittleEndian>()? as u128;
kv.expiry = Some(expiry);
}
0xFD => {
let mut expiry = [0; 4];
reader.read_exact(&mut expiry)?;
let expiry = u32::from_le_bytes(expiry) as u128;
//let expiry = reader.read_u64::<LittleEndian>()? as u128;
kv.expiry = Some(expiry * 1000);
}
_ => {
let _ = reader.seek(std::io::SeekFrom::Current(-1));
}
}
Full disclosure: I can barely read Rust, so I will try to help as best as I can.
I ran your code, and the first thing I noticed is that in the Expiry #yz1 stage, expiry values were way smaller than they should be:
The correct expiry values should be much bigger, like those in Read value with expiry #sm4
@paebanks Could you try to check the parsing logic for expiry, making sure they look like the big number in #sm4 but still pass #yz1?
The expiry values in $yz1 are read from command line and are supplied as values in milliseconds. Those value are exactly as passed in, with the “px” option. The values in #sm4 are timestamp values and thats where I’m not sure my parsing is correct
@paebanks Got it! I can confirm you’re parsing the expiry values in #sm4 correctly, but they’re probably not handled in the way intended.
Let’s look at this line:
# `expired_at` might not be a fitting name for the `px` option
if let Some(expired_at) = value.expiry
and this function:
pub fn is_key_expired(created_timestamp: SystemTime, expiry: u128) -> bool {
SystemTime::now().duration_since(created_timestamp).unwrap_or(Duration::from_secs(0)) >= Duration::from_millis(expiry as u64)
}
It can handle the case where px represents a duration (as a delta value, but not a timestamp).
So it won’t work for the expiry values in #sm4, which are timestamps and can be fittingly named expired_at.
Suggestion:
- Either transform
px durations into timestamps, and adjust is_key_expired accordingly,
- Or transform the
expiry timestamps in #sm4 into durations before storing them.
Thanks so much for your help. Still haven’t fixed it and I’m guessing it must be something simple. I converted the “px” values to timestamps with this function:
fn to_timestamp(expiry: u128) -> u128 {
let exp_timestamp = SystemTime::now() + Duration::from_millis(expiry as u64);
exp_timestamp.duration_since(UNIX_EPOCH).unwrap().as_millis() as u128
}
and my function to check expiry is this:
pub fn is_key_expired(expiry: u128) -> bool {
let current_time = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis() as u128;
current_time >= expiry
}
Passes #yz1, but still fails #sm4
How do the logs look like now?
I grabbed that expiry value and converted it with an online tool. I can confirm it is indeed expired. The date it represents is Saturday January 1, 2022.
@paebanks There may be multiple issues at play here, but here’s one fix you can try first:
The expiry values (if any) are located at the beginning of the key-value pairs, not at the end.
Namely, the expiry for orange: raspberry shouldn’t be None, but instead should be 1956528000000, in the example above.
In our instructions, the examples are grouped like this:
Feel free to use this snippet to print out the hex dump:
Click me
// Open the file
let mut file = File::open(&path)?;
let mut buffer = Vec::new();
// Read the file content into the buffer
file.read_to_end(&mut buffer)?;
// Process the file buffer to mimic `xxd` output with extra newlines for readability
for (i, chunk) in buffer.chunks(16).enumerate() {
// Print the offset at the beginning of each line
print!("{:08x}: ", i * 16);
// Print hex values for each byte in the chunk
for byte in chunk {
print!("{:02x} ", byte);
}
// Pad hex column if the last chunk is less than 16 bytes
if chunk.len() < 16 {
for _ in 0..(16 - chunk.len()) {
print!(" ");
}
}
// Print ASCII representation for each byte in the chunk
print!("|");
for byte in chunk {
let ch = if *byte == 0x0c || !byte.is_ascii_graphic() {
'.'
} else {
*byte as char
};
print!("{}", ch);
}
print!("|\n"); // Newline after each chunk for readability
}
This has been so helpful. Thanks! I was assuming expiry values came AFTER the kv, not before