From df954a95546a2fb252f17d3c8aaac67c6ea2c78d Mon Sep 17 00:00:00 2001 From: Tobie Morgan Hitchcock Date: Sun, 18 Dec 2022 10:23:07 +0000 Subject: [PATCH] Ensure transaction cache is cleared when necessary Closes #1526 --- lib/src/kvs/cache.rs | 6 +- lib/src/kvs/tx.rs | 9 ++ lib/src/sql/statements/define.rs | 12 +++ lib/src/sql/statements/remove.rs | 9 ++ lib/tests/cache.rs | 137 +++++++++++++++++++++++++++++++ 5 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 lib/tests/cache.rs diff --git a/lib/src/kvs/cache.rs b/lib/src/kvs/cache.rs index 7fa295ec..fe102e9b 100644 --- a/lib/src/kvs/cache.rs +++ b/lib/src/kvs/cache.rs @@ -45,8 +45,12 @@ impl Cache { pub fn set(&mut self, key: Key, val: Entry) { self.0.insert(key, val); } - // get a key from the cache + // Get a key from the cache pub fn get(&mut self, key: &Key) -> Option { self.0.get(key).cloned() } + // Delete a key from the cache + pub fn del(&mut self, key: &Key) -> Option { + self.0.remove(key) + } } diff --git a/lib/src/kvs/tx.rs b/lib/src/kvs/tx.rs index 09c013bd..cf0652e7 100644 --- a/lib/src/kvs/tx.rs +++ b/lib/src/kvs/tx.rs @@ -642,6 +642,15 @@ impl Transaction { } Ok(()) } + /// Clear any cache entry for the specified key. + pub async fn clr(&mut self, key: K) -> Result<(), Error> + where + K: Into, + { + let key: Key = key.into(); + self.cache.del(&key); + Ok(()) + } /// Retrieve all namespace definitions in a datastore. pub async fn all_ns(&mut self) -> Result, Error> { let key = crate::key::ns::prefix(); diff --git a/lib/src/sql/statements/define.rs b/lib/src/sql/statements/define.rs index 4b0fcfdc..ec9c7fcb 100644 --- a/lib/src/sql/statements/define.rs +++ b/lib/src/sql/statements/define.rs @@ -609,6 +609,9 @@ impl DefineTableStatement { // Save the view config let key = crate::key::ft::new(opt.ns(), opt.db(), v, &self.name); run.set(key, self).await?; + // Clear the cache + let key = crate::key::ft::prefix(opt.ns(), opt.db(), v); + run.clr(key).await?; } // Release the transaction drop(run); @@ -775,6 +778,9 @@ impl DefineEventStatement { run.add_db(opt.ns(), opt.db(), opt.strict).await?; run.add_tb(opt.ns(), opt.db(), &self.what, opt.strict).await?; run.set(key, self).await?; + // Clear the cache + let key = crate::key::ev::prefix(opt.ns(), opt.db(), &self.what); + run.clr(key).await?; // Ok all good Ok(Value::None) } @@ -856,6 +862,9 @@ impl DefineFieldStatement { run.add_db(opt.ns(), opt.db(), opt.strict).await?; run.add_tb(opt.ns(), opt.db(), &self.what, opt.strict).await?; run.set(key, self).await?; + // Clear the cache + let key = crate::key::fd::prefix(opt.ns(), opt.db(), &self.what); + run.clr(key).await?; // Ok all good Ok(Value::None) } @@ -996,6 +1005,9 @@ impl DefineIndexStatement { run.add_db(opt.ns(), opt.db(), opt.strict).await?; run.add_tb(opt.ns(), opt.db(), &self.what, opt.strict).await?; run.set(key, self).await?; + // Clear the cache + let key = crate::key::ix::prefix(opt.ns(), opt.db(), &self.what); + run.clr(key).await?; // Remove the index data let beg = crate::key::index::prefix(opt.ns(), opt.db(), &self.what, &self.name); let end = crate::key::index::suffix(opt.ns(), opt.db(), &self.what, &self.name); diff --git a/lib/src/sql/statements/remove.rs b/lib/src/sql/statements/remove.rs index f1be1879..7d4d71d9 100644 --- a/lib/src/sql/statements/remove.rs +++ b/lib/src/sql/statements/remove.rs @@ -509,6 +509,9 @@ impl RemoveEventStatement { // Delete the definition let key = crate::key::ev::new(opt.ns(), opt.db(), &self.what, &self.name); run.del(key).await?; + // Clear the cache + let key = crate::key::ev::prefix(opt.ns(), opt.db(), &self.what); + run.clr(key).await?; // Ok all good Ok(Value::None) } @@ -569,6 +572,9 @@ impl RemoveFieldStatement { // Delete the definition let key = crate::key::fd::new(opt.ns(), opt.db(), &self.what, &self.name.to_string()); run.del(key).await?; + // Clear the cache + let key = crate::key::fd::prefix(opt.ns(), opt.db(), &self.what); + run.clr(key).await?; // Ok all good Ok(Value::None) } @@ -629,6 +635,9 @@ impl RemoveIndexStatement { // Delete the definition let key = crate::key::ix::new(opt.ns(), opt.db(), &self.what, &self.name); run.del(key).await?; + // Clear the cache + let key = crate::key::ix::prefix(opt.ns(), opt.db(), &self.what); + run.clr(key).await?; // Remove the resource data let beg = crate::key::index::prefix(opt.ns(), opt.db(), &self.what, &self.name); let end = crate::key::index::suffix(opt.ns(), opt.db(), &self.what, &self.name); diff --git a/lib/tests/cache.rs b/lib/tests/cache.rs new file mode 100644 index 00000000..b6827281 --- /dev/null +++ b/lib/tests/cache.rs @@ -0,0 +1,137 @@ +mod parse; +use parse::Parse; +use surrealdb::sql::Value; +use surrealdb::Datastore; +use surrealdb::Error; +use surrealdb::Session; + +#[tokio::test] +async fn clear_transaction_cache_table() -> Result<(), Error> { + let sql = " + BEGIN; + CREATE person:one CONTENT { x: 0 }; + SELECT * FROM person; + DEFINE TABLE other AS SELECT * FROM person; + COMMIT; + SELECT * FROM other; + "; + let dbs = Datastore::new("memory").await?; + let ses = Session::for_kv().with_ns("test").with_db("test"); + let res = &mut dbs.execute(&sql, &ses, None, false).await?; + assert_eq!(res.len(), 4); + // + let tmp = res.remove(0).result?; + let val = Value::parse( + "[ + { + id: person:one, + x: 0 + } + ]", + ); + assert_eq!(tmp, val); + // + let tmp = res.remove(0).result?; + let val = Value::parse( + "[ + { + id: person:one, + x: 0 + } + ]", + ); + assert_eq!(tmp, val); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result?; + let val = Value::parse( + "[ + { + id: other:one, + x: 0 + } + ]", + ); + assert_eq!(tmp, val); + // + Ok(()) +} + +#[tokio::test] +async fn clear_transaction_cache_field() -> Result<(), Error> { + let sql = " + DEFINE FIELD test ON person TYPE string VALUE 'test'; + BEGIN; + UPDATE person:one CONTENT { x: 0 }; + SELECT * FROM person; + REMOVE FIELD test ON person; + UPDATE person:two CONTENT { x: 0 }; + SELECT * FROM person; + COMMIT; + "; + let dbs = Datastore::new("memory").await?; + let ses = Session::for_kv().with_ns("test").with_db("test"); + let res = &mut dbs.execute(&sql, &ses, None, false).await?; + assert_eq!(res.len(), 6); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result?; + let val = Value::parse( + "[ + { + id: person:one, + test: 'test', + x: 0 + } + ]", + ); + assert_eq!(tmp, val); + // + let tmp = res.remove(0).result?; + let val = Value::parse( + "[ + { + id: person:one, + test: 'test', + x: 0 + } + ]", + ); + assert_eq!(tmp, val); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result?; + let val = Value::parse( + "[ + { + id: person:two, + x: 0 + } + ]", + ); + assert_eq!(tmp, val); + // + let tmp = res.remove(0).result?; + let val = Value::parse( + "[ + { + id: person:one, + test: 'test', + x: 0 + }, + { + id: person:two, + x: 0 + } + ]", + ); + assert_eq!(tmp, val); + // + Ok(()) +}