Repository: p2panda/aquadoggo (language: rust)

Issue:
Broken next entry args implementation
Next entry args selects log 2 as the next log as soon as 10 documents have been published by one author. This is because log IDs have now become stored as strings instead of integers, which causes SQL's `SORT BY` clause not to produce the correct order when querying sorted log ids.


Discussion / hints:

Fix patch:
diff --git a/.gitignore b/.gitignore
index f380ac095..808f047e7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,5 @@
 target/
 debug/
+
+# IDE
+.vscode
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 142df6697..b4fb95cda 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ### Changed
 
+- Fixed bamboo log selection for authors with more than 10 documents [#66](https://github.com/p2panda/aquadoggo/pull/66)
+
+### Changed
+
 - Replace schema logs with document logs, changing the behavior the `nextEntryArgs` and `publishEntry` RPC methods, invalidating and deleting all previously published entries [#44](https://github.com/p2panda/aquadoggo/pull/44)
 - Rename `Message` to `Operation` everywhere [#48](https://github.com/p2panda/aquadoggo/pull/48)
 - Make JSON RPC methods compatible with new document logs flow [#47](https://github.com/p2panda/aquadoggo/pull/47)
diff --git a/Cargo.lock b/Cargo.lock
index e083f54f1..887f0fac1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2203,7 +2203,7 @@ dependencies = [
 [[package]]
 name = "p2panda-rs"
 version = "0.3.0"
-source = "git+https://github.com/p2panda/p2panda?tag=v0.3.0#714cc5c9f517c8c06d02c9b9184941cd69afff7e"
+source = "git+https://github.com/p2panda/p2panda?branch=main#7ccc65b5cd5d22690e357cabe7be00f9fdd92a7b"
 dependencies = [
  "aquamarine",
  "arrayvec 0.5.2",
diff --git a/aquadoggo/Cargo.toml b/aquadoggo/Cargo.toml
index 217196229..49186ef6f 100644
--- a/aquadoggo/Cargo.toml
+++ b/aquadoggo/Cargo.toml
@@ -29,7 +29,7 @@ jsonrpc-v2 = { version = "0.10.1", features = [
 ], default-features = false }
 log = "0.4.14"
 openssl-probe = "0.1.4"
-p2panda-rs = { git = "https://github.com/p2panda/p2panda", tag = "v0.3.0" }
+p2panda-rs = { git = "https://github.com/p2panda/p2panda", branch = "main" }
 rand = "0.8.4"
 serde = { version = "1.0.130", features = ["derive"] }
 serde_json = "1.0.67"
diff --git a/aquadoggo/migrations/20201230002752_create-logs.sql b/aquadoggo/migrations/20201230002752_create-logs.sql
index d404a62b0..9412b2fe8 100644
--- a/aquadoggo/migrations/20201230002752_create-logs.sql
+++ b/aquadoggo/migrations/20201230002752_create-logs.sql
@@ -6,7 +6,8 @@ CREATE TABLE IF NOT EXISTS logs (
     -- Store u64 integer as 20 character string
     log_id            VARCHAR(20)       NOT NULL,
     schema            VARCHAR(68)       NOT NULL,
-    PRIMARY KEY (author, document, log_id)
+    PRIMARY KEY (author, document, log_id),
+    UNIQUE(author, log_id)
 );
 
 -- Create an index for querying by schema
diff --git a/aquadoggo/src/db/models/log.rs b/aquadoggo/src/db/models/log.rs
index 320fea324..2d2180673 100644
--- a/aquadoggo/src/db/models/log.rs
+++ b/aquadoggo/src/db/models/log.rs
@@ -71,8 +71,6 @@ impl Log {
                 logs
             WHERE
                 author = $1
-            ORDER BY
-                log_id ASC
             ",
         )
         .bind(author.as_str())
@@ -80,11 +78,17 @@ impl Log {
         .await?;
 
         // Convert all strings representing u64 integers to `LogId` instances
-        let log_ids: Vec<LogId> = result
+        let mut log_ids: Vec<LogId> = result
             .iter_mut()
             .map(|str| str.parse().expect("Corrupt u64 integer found in database"))
             .collect();
 
+        // The log id selection below expects log ids in sorted order. We can't easily use SQL
+        // for this because log IDs are stored as `VARCHAR`, which doesn't sort numbers correctly.
+        // A good solution would not require reading all existing log ids to find the next
+        // available one. See this issue: https://github.com/p2panda/aquadoggo/issues/67
+        log_ids.sort();
+
         // Find next unused document log by comparing the sequence of known log ids with an
         // sequence of subsequent log ids until we find a gap.
         let mut next_log_id = LogId::default();
@@ -234,6 +238,37 @@ mod tests {
         );
     }
 
+    #[async_std::test]
+    async fn selecting_next_log_id() {
+        let pool = initialize_db().await;
+        let key_pair = KeyPair::new();
+        let author = Author::try_from(*key_pair.public_key()).unwrap();
+        let schema = Hash::new_from_bytes(vec![1, 2, 3]).unwrap();
+        let document = Hash::new_from_bytes(vec![1, 2, 3]).unwrap();
+
+        // We expect to be given the next log id when asking for a possible log id for a new
+        // document by the same author
+        assert_eq!(
+            Log::find_document_log_id(&pool, &author, Some(&document))
+                .await
+                .unwrap(),
+            LogId::default()
+        );
+
+        // Starting with an empty db, we expect to be able to count up from 1 and expect each
+        // inserted document's log id to be euqal to the count index
+        for n in 1..12 {
+            let doc = Hash::new_from_bytes(vec![1,2,n]).unwrap();
+            let log_id = Log::find_document_log_id(&pool, &author, None)
+                .await
+                .unwrap();
+            assert_eq!(LogId::new(n.into()), log_id);
+            Log::insert(&pool, &author, &doc, &schema, &log_id)
+                .await
+                .unwrap();
+        }
+    }
+
     #[async_std::test]
     async fn document_log_id() {
         let pool = initialize_db().await;
@@ -299,15 +334,6 @@ mod tests {
                 .unwrap(),
             LogId::default()
         );
-
-        // We expect to be given the next log id when asking for a possible log id for a new
-        // document by the same author
-        assert_eq!(
-            Log::find_document_log_id(&pool, &author, None)
-                .await
-                .unwrap(),
-            LogId::default().next().unwrap()
-        );
     }
 
     #[async_std::test]


Test patch: