mod io;
mod nodes;
mod predicates;
pub(super) mod records;
#[cfg(test)]
mod tests;
use std::io::{Read, Write};
use super::{DatabaseId, Edge, SerializableGraph, Transaction, VersionError, VersionResult};
use crate::read_control::StorageReadControl;
use io::{invalid, Decoder, Encoder};
const MAGIC: &[u8; 8] = b"UQASER03";
impl SerializableGraph {
pub fn checkpoint_changed(&self) -> bool {
self.checkpoint_changed
}
pub fn write_checkpoint(
&self,
output: &mut dyn Write,
control: &StorageReadControl,
) -> VersionResult<()> {
let mut encoder = Encoder::new(output, control);
encoder.bytes(MAGIC)?;
encoder.bytes(&self.database.as_bytes())?;
encoder.bytes(&self.coordinator)?;
for value in [self.last_allocation, self.clock, self.pending_finishes] {
encoder.number(value)?;
}
for count in [
self.transactions.len(),
self.outgoing.len(),
self.predicates.reads.len(),
self.predicates.writes.len(),
] {
encoder.count(count)?;
}
for &entry in &*self.transactions {
nodes::write(&mut encoder, entry)?;
}
for &Edge(reader, writer) in &*self.outgoing {
encoder.number(reader)?;
encoder.number(writer)?;
}
for entries in [&self.predicates.reads, &self.predicates.writes] {
for entry in &**entries {
predicates::write(&mut encoder, entry)?;
}
}
encoder.finish()
}
pub fn checkpoint_length(&self, control: &StorageReadControl) -> VersionResult<u64> {
let mut count = CountBytes(0);
self.write_checkpoint(&mut count, control)?;
Ok(count.0)
}
pub fn read_checkpoint(
database: DatabaseId,
coordinator: [u8; 16],
input: &mut dyn Read,
control: &StorageReadControl,
) -> VersionResult<Self> {
let mut decoder = Decoder::new(input, control);
let magic = decoder.array::<8>()?;
if magic != *MAGIC && magic != *b"UQASER02" && magic != *b"UQASER01" {
return Err(invalid());
}
if decoder.array::<16>()? != database.as_bytes() {
return Err(VersionError::WrongDatabase);
}
if decoder.array::<16>()? != coordinator {
return Err(VersionError::WrongSerializableCoordinator);
}
let mut graph = Self::new(database, coordinator, control.memory())?;
graph.last_allocation = decoder.number()?;
graph.clock = decoder.number()?;
graph.pending_finishes = decoder.number()?;
if graph.clock.checked_add(graph.pending_finishes).is_none()
|| (graph.clock == 0 && graph.last_allocation != 0)
{
return Err(invalid());
}
let transaction_count = decoder.count()?;
let edge_count = decoder.count()?;
let read_count = decoder.count()?;
let write_count = decoder.count()?;
nodes::restore(
&mut graph,
transaction_count,
magic != *b"UQASER01",
magic == *MAGIC,
&mut decoder,
)?;
graph.outgoing.reserve(edge_count)?;
graph.incoming.reserve(edge_count)?;
for _ in 0..edge_count {
let edge = Edge(decoder.number()?, decoder.number()?);
if edge.0 == edge.1
|| graph
.outgoing
.last()
.is_some_and(|previous| *previous >= edge)
{
return Err(invalid());
}
lookup(&graph, edge.0)?;
if lookup(&graph, edge.1)?.read_only {
return Err(invalid());
}
graph.outgoing.push(edge)?;
graph.incoming.push(Edge(edge.1, edge.0))?;
}
graph.incoming.sort_unstable();
for (writing, count) in [(false, read_count), (true, write_count)] {
let entries = if writing {
&mut graph.predicates.writes
} else {
&mut graph.predicates.reads
};
entries.reserve(count)?;
for _ in 0..count {
let mut observed = predicates::read(&mut decoder, writing)?;
observed.fingerprint =
records::SerializableCheckpointRecord::predicate(&graph, &observed, writing)
.fingerprint(control)?;
let owner = lookup(&graph, observed.owner)?;
if owner.aborted
|| (writing
&& (owner.read_only
|| observed.write == 0
|| observed.write > owner.writes))
|| (!writing && observed.write != 0)
{
return Err(invalid());
}
let entries = if writing {
&mut graph.predicates.writes
} else {
&mut graph.predicates.reads
};
if entries
.last()
.is_some_and(|previous| previous.predicate.object > observed.predicate.object)
{
return Err(invalid());
}
entries.push(observed)?;
}
}
decoder.finish()?;
for entries in [&mut graph.predicates.reads, &mut graph.predicates.writes] {
entries.sort_unstable_by_key(|entry| (entry.predicate.object, entry.fingerprint));
}
control.check()?;
graph.checkpoint_changed = false;
Ok(graph)
}
}
fn lookup(graph: &SerializableGraph, allocation: u64) -> VersionResult<&Transaction> {
let position = graph
.transactions
.binary_search_by_key(&allocation, |entry| entry.id)
.map_err(|_| invalid())?;
Ok(&graph.transactions[position])
}
struct CountBytes(u64);
impl Write for CountBytes {
fn write(&mut self, bytes: &[u8]) -> std::io::Result<usize> {
self.0 = self
.0
.checked_add(
u64::try_from(bytes.len())
.map_err(|_| std::io::Error::other("checkpoint length overflow"))?,
)
.ok_or_else(|| std::io::Error::other("checkpoint length overflow"))?;
Ok(bytes.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}