Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

MB-9661: API to count changes between two sequence numbers

Change-Id: Icf62266bf29b5981d1e9dd6d33aa784a5a90d04f
Reviewed-on: http://review.couchbase.org/32412
Reviewed-by: Michael Wiederhold <mike@couchbase.com>
Tested-by: Michael Wiederhold <mike@couchbase.com>
  • Loading branch information...
commit a48827f1b5cb5553d55c46b66d4db9fde172751c 1 parent 5f28318
@apage43 apage43 authored mikewied committed
View
1  CMakeLists.txt
@@ -149,6 +149,7 @@ ENDMACRO()
M_ADD_PYTHON_TEST(couchstore-purge tests/purge.py)
M_ADD_PYTHON_TEST(couchstore-rewind tests/rewind.py)
+M_ADD_PYTHON_TEST(couchstore-changecount tests/changecount.py)
ADD_EXECUTABLE(couchstore_testapp tests/testapp.c
View
15 include/libcouchstore/couch_db.h
@@ -705,6 +705,21 @@ extern "C" {
char* buf,
size_t size);
+ /**
+ * Counts the number of changes between two sequence numbers, inclusive.
+ *
+ * @param db The db to count changes in
+ * @param min_seq The minimum sequence to count
+ * @param max_seq The maximum sequence to count
+ * @param count Pointer to uint64_t to store count in
+ * @return COUCHSTORE_SUCCESS on success
+ */
+ LIBCOUCHSTORE_API
+ couchstore_error_t couchstore_changes_count(Db* db,
+ uint64_t min_seq,
+ uint64_t max_seq,
+ uint64_t *count);
+
#ifdef __cplusplus
}
#endif
View
9 python/couchstore.py
@@ -108,6 +108,8 @@ class DbInfoStruct (Structure):
("space_used", c_ulonglong),
("header_position", c_ulonglong) ]
+class CounterStruct (Structure):
+ _fields_ = [("count", c_ulonglong)]
### DOCUMENT INFO CLASS:
@@ -393,6 +395,13 @@ def callback (dbPtr, docInfoPtr, context):
_check(_lib.couchstore_docinfos_by_id(self, ids, c_uint(numIDs), c_uint64(1), \
CouchStore.ITERATORFUNC(callback), c_void_p(0)))
+ def changesCount(self, minimum, maximum):
+ cstruct = CounterStruct()
+ err = _lib.couchstore_changes_count(self, c_uint64(minimum), \
+ c_uint64(maximum), pointer(cstruct))
+ _check(err)
+ return cstruct.count
+
@property
def localDocs(self):
"""A simple dictionary-like object that accesses the CouchStore's local documents."""
View
74 src/couch_db.cc
@@ -1192,3 +1192,77 @@ couchstore_error_t couchstore_last_os_error(const Db *db,
return COUCHSTORE_SUCCESS;
}
+
+static couchstore_error_t btree_eval_seq_reduce(Db *db,
+ uint64_t *accum,
+ sized_buf *left,
+ sized_buf *right,
+ bool past_left_edge,
+ uint64_t diskpos) {
+ couchstore_error_t errcode = COUCHSTORE_SUCCESS;
+ int bufpos = 1, nodebuflen = 0;
+ int node_type;
+ char *nodebuf = NULL;
+ nodebuflen = pread_compressed(&db->file, diskpos, &nodebuf);
+ error_unless(nodebuflen >= 0, (static_cast<couchstore_error_t>(nodebuflen))); // if negative, it's an error code
+
+ node_type = nodebuf[0];
+ while(bufpos < nodebuflen) {
+ sized_buf k, v;
+ bufpos += read_kv(nodebuf + bufpos, &k, &v);
+ int left_cmp = seq_cmp(&k, left);
+ int right_cmp = seq_cmp(&k, right);
+ if(left_cmp < 0) {
+ continue;
+ }
+ if(node_type == KP_NODE) {
+ // In-range Item in a KP Node
+ const raw_node_pointer *raw = (const raw_node_pointer*)v.buf;
+ const raw_by_seq_reduce *rawreduce = (const raw_by_seq_reduce*) (v.buf + sizeof(raw_node_pointer));
+ uint64_t subcount = decode_raw40(rawreduce->count);
+ uint64_t pointer = decode_raw48(raw->pointer);
+ if((left_cmp >= 0 && !past_left_edge) || right_cmp >= 0) {
+ error_pass(btree_eval_seq_reduce(db, accum, left, right, past_left_edge, pointer));
+ if(right_cmp >= 0) {
+ break;
+ } else {
+ past_left_edge = true;
+ }
+ } else {
+ *accum += subcount;
+ }
+ } else {
+ if(right_cmp > 0) {
+ break;
+ }
+ // In-range Item in a KV Node
+ *accum += 1;
+ }
+ }
+cleanup:
+ return errcode;
+}
+
+LIBCOUCHSTORE_API
+couchstore_error_t couchstore_changes_count(Db* db,
+ uint64_t min_seq,
+ uint64_t max_seq,
+ uint64_t *count) {
+ couchstore_error_t errcode = COUCHSTORE_SUCCESS;
+ raw_48 leftkr, rightkr;
+ sized_buf leftk, rightk;
+ leftk.buf = (char*) &leftkr;
+ rightk.buf = (char*) &rightkr;
+ leftk.size = 6;
+ rightk.size = 6;
+ leftkr = encode_raw48(min_seq);
+ rightkr = encode_raw48(max_seq);
+
+ *count = 0;
+ if(db->header.by_seq_root) {
+ error_pass(btree_eval_seq_reduce(db, count, &leftk, &rightk, false,
+ db->header.by_seq_root->pointer));
+ }
+cleanup:
+ return errcode;
+}
View
63 tests/changecount.py
@@ -0,0 +1,63 @@
+from couchstore import CouchStore, DocumentInfo
+from tempfile import mkdtemp
+import os
+import struct
+import unittest
+
+class ChangeCountTest(unittest.TestCase):
+ def setUp(self):
+ self.tmpdir = mkdtemp()
+ self.dbname = self.tmpdir + "testing.couch"
+ self.db = CouchStore(self.dbname, 'c');
+
+ def tearDown(self):
+ try:
+ self.db.commit()
+ self.db.close()
+ except:
+ pass
+ try:
+ os.remove(self.dbname)
+ except:
+ pass
+ try:
+ os.rmdir(self.tmpdir)
+ except:
+ pass
+
+ def bulkSet(self, prefix, n):
+ ids = [prefix + str(x) for x in xrange(n)]
+ datas = ["val" + str(x) for x in xrange(n)]
+ self.db.saveMultiple(ids, datas)
+
+ def testRewind(self):
+ # Save some docs
+ self.db.save("foo1", "bar")
+ self.db.save("foo2", "baz")
+ self.db.save("foo3", "bell")
+ self.db.save("foo4", "a")
+ self.assertEqual(self.db.changesCount(0,100), 4)
+
+ self.db.save("foo1", "new_bar")
+ self.db.save("foo2", "new_baz")
+ self.db.save("foo3", "new_bell")
+ self.db.save("foo4", "new_a")
+ self.assertEqual(self.db.changesCount(0,100), 4)
+
+ self.bulkSet("foo", 100)
+ self.assertEqual(self.db.changesCount(0, 108), 100)
+ self.assertEqual(self.db.changesCount(0, 100), 92)
+ self.assertEqual(self.db.changesCount(1, 100), 92)
+ self.assertNotEqual(self.db.changesCount(12, 100), 92)
+ self.assertEqual(self.db.changesCount(50, 99), 50)
+ self.assertEqual(self.db.changesCount(50, 100), 51)
+ self.assertEqual(self.db.changesCount(50, 108), 59)
+ self.assertEqual(self.db.changesCount(51, 100), 50)
+ self.assertEqual(self.db.changesCount(91, 1000), 18)
+ self.db.save("foo88", "tval")
+ self.assertEqual(self.db.changesCount(50, 108), 58)
+ self.assertEqual(self.db.changesCount(50, 109), 59)
+
+
+if __name__ == '__main__':
+ unittest.main()
Please sign in to comment.
Something went wrong with that request. Please try again.