documentId generation within the database transaction
This commit is contained in:
@@ -21,24 +21,28 @@
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
use roaring::RoaringBitmap;
|
||||
use rusqlite::{params, OptionalExtension, TransactionBehavior};
|
||||
|
||||
use crate::{
|
||||
write::{Batch, BitmapClass, Operation, ValueClass, ValueOp},
|
||||
BitmapKey, IndexKey, Key, LogKey, ValueKey, SUBSPACE_COUNTERS,
|
||||
write::{
|
||||
key::DeserializeBigEndian, AssignedIds, Batch, BitmapClass, Operation, RandomAvailableId,
|
||||
ValueOp,
|
||||
},
|
||||
BitmapKey, IndexKey, Key, LogKey, SUBSPACE_COUNTERS, U32_LEN,
|
||||
};
|
||||
|
||||
use super::SqliteStore;
|
||||
|
||||
impl SqliteStore {
|
||||
pub(crate) async fn write(&self, batch: Batch) -> crate::Result<Option<i64>> {
|
||||
pub(crate) async fn write(&self, batch: Batch) -> crate::Result<AssignedIds> {
|
||||
let mut conn = self.conn_pool.get()?;
|
||||
self.spawn_worker(move || {
|
||||
let mut account_id = u32::MAX;
|
||||
let mut collection = u8::MAX;
|
||||
let mut document_id = u32::MAX;
|
||||
let trx = conn.transaction_with_behavior(TransactionBehavior::Immediate)?;
|
||||
let mut result = None;
|
||||
let mut result = AssignedIds::default();
|
||||
|
||||
for op in &batch.ops {
|
||||
match op {
|
||||
@@ -58,14 +62,14 @@ impl SqliteStore {
|
||||
document_id = *document_id_;
|
||||
}
|
||||
Operation::Value { class, op } => {
|
||||
let key = ValueKey {
|
||||
let key = class.serialize(
|
||||
account_id,
|
||||
collection,
|
||||
document_id,
|
||||
class,
|
||||
};
|
||||
let table = char::from(key.subspace());
|
||||
let key = key.serialize(0);
|
||||
0,
|
||||
(&result).into(),
|
||||
);
|
||||
let table = char::from(class.subspace(collection));
|
||||
|
||||
match op {
|
||||
ValueOp::Set(value) => {
|
||||
@@ -73,27 +77,7 @@ impl SqliteStore {
|
||||
"INSERT OR REPLACE INTO {} (k, v) VALUES (?, ?)",
|
||||
table
|
||||
))?
|
||||
.execute([&key, value])?;
|
||||
|
||||
if matches!(class, ValueClass::ReservedId) {
|
||||
// Make sure the reserved id is not already in use
|
||||
let key = BitmapKey {
|
||||
account_id,
|
||||
collection,
|
||||
class: BitmapClass::DocumentIds,
|
||||
block_num: document_id,
|
||||
}
|
||||
.serialize(0);
|
||||
if trx
|
||||
.prepare_cached("SELECT 1 FROM b WHERE k = ?")?
|
||||
.query_row([&key], |_| Ok(true))
|
||||
.optional()?
|
||||
.unwrap_or(false)
|
||||
{
|
||||
trx.rollback()?;
|
||||
return Err(crate::Error::AssertValueFailed);
|
||||
}
|
||||
}
|
||||
.execute([&key, value.resolve(&result)?.as_ref()])?;
|
||||
}
|
||||
ValueOp::AtomicAdd(by) => {
|
||||
if *by >= 0 {
|
||||
@@ -108,13 +92,14 @@ impl SqliteStore {
|
||||
}
|
||||
}
|
||||
ValueOp::AddAndGet(by) => {
|
||||
result = trx
|
||||
.prepare_cached(concat!(
|
||||
result.push_counter_id(
|
||||
trx.prepare_cached(concat!(
|
||||
"INSERT INTO c (k, v) VALUES (?, ?) ",
|
||||
"ON CONFLICT(k) DO UPDATE SET v = v + excluded.v RETURNING v"
|
||||
"ON CONFLICT(k) DO UPDATE SET v = v + ",
|
||||
"excluded.v RETURNING v"
|
||||
))?
|
||||
.query_row(params![&key, &by], |row| row.get::<_, i64>(0))?
|
||||
.into();
|
||||
.query_row(params![&key, &by], |row| row.get::<_, i64>(0))?,
|
||||
);
|
||||
}
|
||||
ValueOp::Clear => {
|
||||
trx.prepare_cached(&format!("DELETE FROM {} WHERE k = ?", table))?
|
||||
@@ -141,49 +126,83 @@ impl SqliteStore {
|
||||
}
|
||||
}
|
||||
Operation::Bitmap { class, set } => {
|
||||
let key = BitmapKey {
|
||||
// Find the next available document id
|
||||
let is_document_id = matches!(class, BitmapClass::DocumentIds);
|
||||
if *set && is_document_id && document_id == u32::MAX {
|
||||
let begin = BitmapKey {
|
||||
account_id,
|
||||
collection,
|
||||
class: BitmapClass::DocumentIds,
|
||||
document_id: 0,
|
||||
}
|
||||
.serialize(0);
|
||||
let end = BitmapKey {
|
||||
account_id,
|
||||
collection,
|
||||
class: BitmapClass::DocumentIds,
|
||||
document_id: u32::MAX,
|
||||
}
|
||||
.serialize(0);
|
||||
let key_len = begin.len();
|
||||
|
||||
let mut query =
|
||||
trx.prepare_cached("SELECT k FROM b WHERE k >= ? AND k <= ?")?;
|
||||
let mut rows = query.query([&begin, &end])?;
|
||||
let mut found_ids = RoaringBitmap::new();
|
||||
while let Some(row) = rows.next()? {
|
||||
let key = row.get_ref(0)?.as_bytes()?;
|
||||
if key.len() == key_len {
|
||||
found_ids.insert(key.deserialize_be_u32(key.len() - U32_LEN)?);
|
||||
}
|
||||
}
|
||||
|
||||
document_id = found_ids.random_available_id();
|
||||
result.push_document_id(document_id);
|
||||
}
|
||||
let key = class.serialize(
|
||||
account_id,
|
||||
collection,
|
||||
class,
|
||||
block_num: document_id,
|
||||
}
|
||||
.serialize(0);
|
||||
document_id,
|
||||
0,
|
||||
(&result).into(),
|
||||
);
|
||||
|
||||
if *set {
|
||||
trx.prepare_cached("INSERT OR IGNORE INTO b (k) VALUES (?)")?
|
||||
.execute(params![&key])?;
|
||||
if is_document_id {
|
||||
trx.prepare_cached("INSERT INTO b (k) VALUES (?)")?
|
||||
.execute(params![&key])?;
|
||||
} else {
|
||||
trx.prepare_cached("INSERT OR IGNORE INTO b (k) VALUES (?)")?
|
||||
.execute(params![&key])?;
|
||||
}
|
||||
} else {
|
||||
trx.prepare_cached("DELETE FROM b WHERE k = ?")?
|
||||
.execute(params![&key])?;
|
||||
};
|
||||
}
|
||||
Operation::Log {
|
||||
collection,
|
||||
change_id,
|
||||
set,
|
||||
} => {
|
||||
Operation::Log { set } => {
|
||||
let key = LogKey {
|
||||
account_id,
|
||||
collection: *collection,
|
||||
change_id: *change_id,
|
||||
collection,
|
||||
change_id: batch.change_id,
|
||||
}
|
||||
.serialize(0);
|
||||
|
||||
trx.prepare_cached("INSERT OR REPLACE INTO l (k, v) VALUES (?, ?)")?
|
||||
.execute([&key, set])?;
|
||||
.execute([&key, set.resolve(&result)?.as_ref()])?;
|
||||
}
|
||||
Operation::AssertValue {
|
||||
class,
|
||||
assert_value,
|
||||
} => {
|
||||
let key = ValueKey {
|
||||
let key = class.serialize(
|
||||
account_id,
|
||||
collection,
|
||||
document_id,
|
||||
class,
|
||||
};
|
||||
let table = char::from(key.subspace());
|
||||
let key = key.serialize(0);
|
||||
0,
|
||||
(&result).into(),
|
||||
);
|
||||
let table = char::from(class.subspace(collection));
|
||||
|
||||
let matches = trx
|
||||
.prepare_cached(&format!("SELECT v FROM {} WHERE k = ?", table))?
|
||||
|
||||
Reference in New Issue
Block a user