All APIs and their parameters are explained in header files:
- db_config.h: DB configurations.
- db_stats.h: DB statistics.
- iterator.h: Iterator operations.
- jungle.h: DB operations.
- keyvalue.h: Key-value pair.
- record.h: Record.
- sized_buf.h: Basic buffer.
- status.h: Operation result status.
The basic unit of indexing in Jungle is a record, which consists of key-value pair, sequence number, operation type, and custom metadata. Key, value, and metadata are based on variable size buffer (i.e., a memory buffer and its length), called SizedBuf in Jungle.
Jungle defines various global resources shared among different DB instances in the same process, such as cache and thread pool. You can explicitly initialize or release them.
Initialize:
GlobalConfig global_config;
// ... set your config ...
jungle::init(global_config);
Release:
jungle::shutdown();
Note that above APIs can be skipped. In such case, initialization will be done on the first DB open with the default configurations, and they will be release at the termination of the process.
Each Jungle instance is a single directory which contains multiple files such as DB logs, tables, and some debugging logs. To open a DB instance, a path to the DB should be given.
DBConfig db_config;
// ... set your config ...
DB* db = nullptr;
Status s = DB::open(&db, "./my_db", db_config);
Once opening DB is successful, s.ok()
will be true
. Otherwise, you can see the result value by calling s.getValue()
or s.toString()
.
If the given path does not exist or empty, Jungle will create a new one.
You can close the DB instance by calling close
API:
Status s = db->close();
There are four different ways to set (upsert) a record.
- Custom metadata will be empty, and sequence number will be automatically generated.
db->set(KV("key", "value"));
- Custom metadata will be empty, and sequence number will be set to the given number.
- Sequence number should be unique and increasing. If not, undefined behavior including system crash or data corruption will happen.
KV key_value;
db->setSN(100, KV("key", "value"));
- Custom metadata will be set to the given data, and sequence number will be automatically generated.
Record record;
// ... set record ...
db->setRecordByKey(record);
- Both custom metadata and sequence number will be set to the given values.
- Sequence number should be unique and increasing. If not, undefined behavior including system crash or data corruption will happen.
Record record;
// ... set record ...
record.seqNum = 100;
db->setRecord(record);
There are four different ways to get (point query) a record. User is responsible for the deallocation of memory returned by get operations.
- Only value part will be returned.
get
on deleted key will not succeed.
SizedBuf returned_value;
db->get(SizedBuf("key_to_find", returned_value);
returned_value.free();
- Key and value will be returned.
getSN
on deleted key will not succeed.- Note: currently we support this API only for log store mode.
KV returned_key_value;
db->getSN(100, returned_key_value);
returned_key_value.free();
- All fields in record will be returned.
- There is a flag to retrieve a deleted record.
- Below example will not succeed on deleted record:
Record returned_record;
db->getRecordByKey(SizedBuf("key_to_find", returned_record);
returned_record.free();
- With setting the flag to
true
, deleted record will be returned. Custom metadata will be retrieved if it was set before when the record was deleted. Value part of the returned record will be empty. - Only logically deleted records will be visible. Once a record is physically deleted by merge or compaction, it will not be retrieved anymore.
Record returned_record;
db->getRecordByKey(SizedBuf("key_to_find", returned_record, true);
returned_record.free();
- All fields in record will be returned.
getRecord
on deleted key will always succeed.- Note: currently we support this API only for log store mode.
Record returned_record;
db->getRecord(100, returned_record);
returned_record.free();
In Jungle, delete operation is the same as update operation, modifying the existing record as a deletion marker (i.e., tombstone). You can put your custom metadata for each tombstone and retrieve it later. Tombstones are physically purged later, during Table compaction. Note that deletion will succeed even though the given key does not exist.
There are three different ways to delete a record.
- Sequence number for this tombstone will be automatically generated.
db->del(SizedBuf("key_to_delete");
- Sequence number for this tombstone will be set to the given number.
- Sequence number should be unique and increasing. If not, undefined behavior including system crash or data corruption will happen.
db->delSN(100, SizedBuf("key_to_delete");
- Both custom metadata and sequence number for this tombstone will be set to the given values.
- Sequence number should be unique and increasing. If not, undefined behavior including system crash or data corruption will happen.
Record record;
record.key = ... // key to delete.
record.meta = ... // metadata for this tombstone
record.seqNum = 100;
db->setRecord(record);
- If you skip setting
record.seqNum
or set it toDB::NULL_SEQNUM
, the sequence number will be automatically generated.
Each iterator works as a snapshot, thus any mutations will not be applied to previous iterators already opened.
Jungle can create an iterator in two different orders: key and sequence number.
Iterator itr;
itr.init(db);
This iterator can access all keys in the given db
. You can also specify the range.
itr.init(db, SizedBuf("a"), SizedBuf("z")); // from a to z (inclusive)
or
itr.init(db, SizedBuf("a")); // from a to max
or
itr.init(db, SizedBuf(), SizedBuf("z")); // from min to z
Iterator itr;
itr.initSN(db);
This iterator can access all sequence numbers in the given db
. You can also specify the range.
itr.initSN(db, 100, 200); // from 100 to 200 (inclusive)
or
itr.initSN(db, 100); // from 100 to max
or
itr.initSN(db, DB::NULL_SEQNUM, 200); // from min to 200
Even though the DB instance is empty or there is no record within the given range, opening an iterator will succeed. But any following operations on that iterator will return error.
Both key and sequence number iterators can be closed using DB::Iterator::close
API.
itr.close();
All iterators should be closed before the closing the parent DB instance.
Record returned_record;
Status s = itr.get(returned_record);
returned_record.free();
If the iterator currently does not point to any record, s.ok()
will be false
.
Same as get
operations, user is responsible for the deallocation of the returned record.
Sequence number iterator will return tombstones, while key iterator will return live records only.
Status s = itr.next();
If the cursor is successfully moved, s.ok()
will be true
.
Status s = itr.prev();
If the cursor is successfully moved, s.ok()
will be true
.
Status s = itr.gotoBegin();
If the cursor is successfully moved, s.ok()
will be true
.
Status s = itr.gotoEnd();
If the cursor is successfully moved, s.ok()
will be true
.
Status s = itr.seek(SizedBuf("key_to_find"));
If the given key does not exist, this API will find the smallest but greater then the given key. If such key does not exist either, s.ok()
will be false
.
There is an option to choose the behavior when the exact match does not exist. If you explicitly set it as follows:
Status s = itr.seek(SizedBuf("key_to_find", Iterator::SMALLER));
then it will find the greatest but smaller than the given key.
Status s = itr.seekSN(100);
Other things are identical to those of key iterator.
There are two types of snapshot in Jungle:
- Persistent snapshot: user explicitly generates a snapshot using
DB::checkpoint
API. This snapshot will persist even after DB close, and will last until it is compacted. - Instant snapshot: user can always open a volatile snapshot based on the latest DB image, but this snapshot will not be available after DB restart.
uint64_t marker = 0;
db->checkpoint(marker);
Returned marker
is a sequence number corresponding to the snapshot.
DB* snapshot = nullptr;
db->openSnapshot(&snapshot, marker);
std::list<uint64_t> markers;
db->getCheckpoints(markers);
DB* snapshot = nullptr;
db->openSnapshot(&snapshot);
All snapshot handles should be closed prior than the parent DB handle.
DB::close(snapshot);