Branch data Line data Source code
1 : : /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 : : /*
3 : : * Copyright 2010 Couchbase, Inc
4 : : *
5 : : * Licensed under the Apache License, Version 2.0 (the "License");
6 : : * you may not use this file except in compliance with the License.
7 : : * You may obtain a copy of the License at
8 : : *
9 : : * http://www.apache.org/licenses/LICENSE-2.0
10 : : *
11 : : * Unless required by applicable law or agreed to in writing, software
12 : : * distributed under the License is distributed on an "AS IS" BASIS,
13 : : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : : * See the License for the specific language governing permissions and
15 : : * limitations under the License.
16 : : */
17 : :
18 : : #include <stdio.h>
19 : : #include <stdlib.h>
20 : : #include <string.h>
21 : : #include <fcntl.h>
22 : : #include <time.h>
23 : : #if !defined(WIN32) && !defined(_WIN32)
24 : : #include <sys/time.h>
25 : : #endif
26 : :
27 : : #include "libforestdb/forestdb.h"
28 : : #include "fdb_internal.h"
29 : : #include "filemgr.h"
30 : : #include "hbtrie.h"
31 : : #include "list.h"
32 : : #include "btree.h"
33 : : #include "btree_kv.h"
34 : : #include "btree_var_kv_ops.h"
35 : : #include "docio.h"
36 : : #include "btreeblock.h"
37 : : #include "common.h"
38 : : #include "wal.h"
39 : : #include "snapshot.h"
40 : : #include "filemgr_ops.h"
41 : : #include "configuration.h"
42 : : #include "internal_types.h"
43 : : #include "compactor.h"
44 : : #include "memleak.h"
45 : :
46 : : #ifdef __DEBUG
47 : : #ifndef __DEBUG_FDB
48 : : #undef DBG
49 : : #undef DBGCMD
50 : : #undef DBGSW
51 : : #define DBG(...)
52 : : #define DBGCMD(...)
53 : : #define DBGSW(n, ...)
54 : : #endif
55 : : #endif
56 : :
57 : : static volatile uint8_t fdb_initialized = 0;
58 : : static volatile uint8_t fdb_open_inprog = 0;
59 : : #ifdef SPIN_INITIALIZER
60 : : static spin_t initial_lock = SPIN_INITIALIZER;
61 : : #else
62 : : static volatile unsigned int initial_lock_status = 0;
63 : : static spin_t initial_lock;
64 : : #endif
65 : :
66 : : static fdb_status _fdb_wal_snapshot_func(void *handle, fdb_doc *doc,
67 : : uint64_t offset);
68 : :
69 : 7736 : INLINE int _cmp_uint64_t_endian_safe(void *key1, void *key2, void *aux)
70 : : {
71 : : (void) aux;
72 : : uint64_t a,b;
73 : 7736 : a = *(uint64_t*)key1;
74 : 7736 : b = *(uint64_t*)key2;
75 : 7736 : a = _endian_decode(a);
76 : 7736 : b = _endian_decode(b);
77 : 7736 : return _CMP_U64(a, b);
78 : : }
79 : :
80 : 1900787 : size_t _fdb_readkey_wrap(void *handle, uint64_t offset, void *buf)
81 : : {
82 : : keylen_t keylen;
83 : 1900787 : offset = _endian_decode(offset);
84 : 1900787 : docio_read_doc_key((struct docio_handle *)handle, offset, &keylen, buf);
85 : 1901966 : return keylen;
86 : : }
87 : :
88 : 184757 : size_t _fdb_readseq_wrap(void *handle, uint64_t offset, void *buf)
89 : : {
90 : : int size_id, size_seq;
91 : : fdb_seqnum_t _seqnum;
92 : : struct docio_object doc;
93 : :
94 : 184757 : size_id = sizeof(fdb_kvs_id_t);
95 : 184757 : size_seq = sizeof(fdb_seqnum_t);
96 : 184757 : memset(&doc, 0, sizeof(struct docio_object));
97 : :
98 : 184757 : offset = _endian_decode(offset);
99 : 184757 : docio_read_doc_key_meta((struct docio_handle *)handle, offset, &doc);
100 : 185288 : memcpy((uint8_t*)buf, doc.key, size_id);
101 : 185288 : _seqnum = _endian_encode(doc.seqnum);
102 : 185288 : memcpy((uint8_t*)buf + size_id, &_seqnum, size_seq);
103 : :
104 : 185288 : free(doc.key);
105 : 185288 : free(doc.meta);
106 : :
107 : 185288 : return size_id + size_seq;
108 : : }
109 : :
110 : 316451 : int _fdb_custom_cmp_wrap(void *key1, void *key2, void *aux)
111 : : {
112 : : int is_key1_inf, is_key2_inf;
113 : 316451 : uint8_t *keystr1 = alca(uint8_t, FDB_MAX_KEYLEN_INTERNAL);
114 : 316451 : uint8_t *keystr2 = alca(uint8_t, FDB_MAX_KEYLEN_INTERNAL);
115 : : size_t keylen1, keylen2;
116 : 316451 : fdb_custom_cmp_variable cmp = (fdb_custom_cmp_variable)aux;
117 : :
118 : 316451 : is_key1_inf = _is_inf_key(key1);
119 : 316451 : is_key2_inf = _is_inf_key(key2);
120 [ + + ][ - + ]: 316451 : if (is_key1_inf && is_key2_inf) { // both are infinite
121 : 0 : return 0;
122 [ + + ][ - + ]: 316451 : } else if (!is_key1_inf && is_key2_inf) { // key2 is infinite
123 : 0 : return -1;
124 [ + + ][ + - ]: 316451 : } else if (is_key1_inf && !is_key2_inf) { // key1 is infinite
125 : 6 : return 1;
126 : : }
127 : :
128 : 316445 : _get_var_key(key1, (void*)keystr1, &keylen1);
129 : 316445 : _get_var_key(key2, (void*)keystr2, &keylen2);
130 : :
131 [ + + ][ - + ]: 316445 : if (keylen1 == 0 && keylen2 == 0) {
132 : 0 : return 0;
133 [ + + ][ + - ]: 316445 : } else if (keylen1 ==0 && keylen2 > 0) {
134 : 36 : return -1;
135 [ + - ][ - + ]: 316409 : } else if (keylen1 > 0 && keylen2 == 0) {
136 : 0 : return 1;
137 : : }
138 : :
139 : 316451 : return cmp(keystr1, keylen1, keystr2, keylen2);
140 : : }
141 : :
142 : 3027 : void fdb_fetch_header(void *header_buf,
143 : : bid_t *trie_root_bid,
144 : : bid_t *seq_root_bid,
145 : : uint64_t *ndocs,
146 : : uint64_t *nlivenodes,
147 : : uint64_t *datasize,
148 : : uint64_t *last_wal_flush_hdr_bid,
149 : : uint64_t *kv_info_offset,
150 : : uint64_t *header_flags,
151 : : char **new_filename,
152 : : char **old_filename)
153 : : {
154 : 3027 : size_t offset = 0;
155 : : uint16_t new_filename_len;
156 : : uint16_t old_filename_len;
157 : :
158 : 3027 : seq_memcpy(trie_root_bid, (uint8_t *)header_buf + offset,
159 : 3027 : sizeof(bid_t), offset);
160 : 3027 : *trie_root_bid = _endian_decode(*trie_root_bid);
161 : :
162 : 3027 : seq_memcpy(seq_root_bid, (uint8_t *)header_buf + offset,
163 : 3027 : sizeof(bid_t), offset);
164 : 3027 : *seq_root_bid = _endian_decode(*seq_root_bid);
165 : :
166 : 3027 : seq_memcpy(ndocs, (uint8_t *)header_buf + offset,
167 : 3027 : sizeof(uint64_t), offset);
168 : 3027 : *ndocs = _endian_decode(*ndocs);
169 : :
170 : 3027 : seq_memcpy(nlivenodes, (uint8_t *)header_buf + offset,
171 : 3027 : sizeof(uint64_t), offset);
172 : 3027 : *nlivenodes = _endian_decode(*nlivenodes);
173 : :
174 : 3027 : seq_memcpy(datasize, (uint8_t *)header_buf + offset,
175 : 3027 : sizeof(uint64_t), offset);
176 : 3027 : *datasize = _endian_decode(*datasize);
177 : :
178 : 3027 : seq_memcpy(last_wal_flush_hdr_bid, (uint8_t *)header_buf + offset,
179 : 3027 : sizeof(uint64_t), offset);
180 : 3027 : *last_wal_flush_hdr_bid = _endian_decode(*last_wal_flush_hdr_bid);
181 : :
182 : 3027 : seq_memcpy(kv_info_offset, (uint8_t *)header_buf + offset,
183 : 3027 : sizeof(uint64_t), offset);
184 : 3027 : *kv_info_offset = _endian_decode(*kv_info_offset);
185 : :
186 : 3027 : seq_memcpy(header_flags, (uint8_t *)header_buf + offset,
187 : 3027 : sizeof(uint64_t), offset);
188 : 3027 : *header_flags = _endian_decode(*header_flags);
189 : :
190 : 3027 : seq_memcpy(&new_filename_len, (uint8_t *)header_buf + offset,
191 : 3027 : sizeof(new_filename_len), offset);
192 : 3027 : new_filename_len = _endian_decode(new_filename_len);
193 : 3027 : seq_memcpy(&old_filename_len, (uint8_t *)header_buf + offset,
194 : 3027 : sizeof(old_filename_len), offset);
195 : 3027 : old_filename_len = _endian_decode(old_filename_len);
196 [ + + ]: 3027 : if (new_filename_len) {
197 : 38 : *new_filename = (char*)((uint8_t *)header_buf + offset);
198 : : }
199 : 3027 : offset += new_filename_len;
200 [ + + ][ + + ]: 3027 : if (old_filename && old_filename_len) {
201 : 830 : *old_filename = (char *) malloc(old_filename_len);
202 : 830 : seq_memcpy(*old_filename,
203 : : (uint8_t *)header_buf + offset,
204 : 830 : old_filename_len, offset);
205 : : }
206 : 3027 : }
207 : :
208 : : INLINE size_t _fdb_get_docsize(struct docio_length len);
209 : :
210 : : typedef enum {
211 : : FDB_RESTORE_NORMAL,
212 : : FDB_RESTORE_KV_INS,
213 : : } fdb_restore_mode_t;
214 : :
215 : 519 : INLINE void _fdb_restore_wal(fdb_kvs_handle *handle,
216 : : fdb_restore_mode_t mode,
217 : : bid_t hdr_bid,
218 : : fdb_kvs_id_t kv_id_req)
219 : : {
220 : 519 : struct filemgr *file = handle->file;
221 : 519 : uint32_t blocksize = handle->file->blocksize;
222 : 519 : uint64_t last_wal_flush_hdr_bid = handle->last_wal_flush_hdr_bid;
223 : 519 : uint64_t hdr_off = hdr_bid * FDB_BLOCKSIZE;
224 : 519 : uint64_t offset = 0; //assume everything from first block needs restoration
225 : :
226 [ + + ]: 519 : if (!hdr_off) { // Nothing to do if we don't have a header block offset
227 : 128 : return;
228 : : }
229 : :
230 : 391 : filemgr_mutex_lock(file);
231 [ + + ]: 391 : if (last_wal_flush_hdr_bid != BLK_NOT_FOUND) {
232 : 260 : offset = (last_wal_flush_hdr_bid + 1) * blocksize;
233 : : }
234 : :
235 : : // If a valid last header was retrieved and it matches the current header
236 : : // OR if WAL already had entries populated, then no crash recovery needed
237 [ + + ]: 575 : if (hdr_off <= offset ||
[ + + + + ]
[ + + ][ + + ]
238 : 184 : (!handle->shandle && wal_get_size(file) &&
239 : : mode != FDB_RESTORE_KV_INS)) {
240 : 245 : filemgr_mutex_unlock(file);
241 : 245 : return;
242 : : }
243 : :
244 : : // Temporarily disable the error logging callback as there are false positive
245 : : // checksum errors in docio_read_doc.
246 : : // TODO: Need to adapt docio_read_doc to separate false checksum errors.
247 : 146 : err_log_callback *log_callback = handle->dhandle->log_callback;
248 : 146 : handle->dhandle->log_callback = NULL;
249 : :
250 [ + + ]: 1061 : for (; offset < hdr_off;
251 : : offset = ((offset / blocksize) + 1) * blocksize) { // next block's off
252 [ + + ]: 915 : if (!docio_check_buffer(handle->dhandle, offset / blocksize)) {
253 : 344 : continue;
254 : : } else {
255 [ + - ]: 8090 : do {
256 : : struct docio_object doc;
257 : : uint64_t _offset;
258 : : uint64_t doc_offset;
259 : 8661 : memset(&doc, 0, sizeof(doc));
260 : 8661 : _offset = docio_read_doc(handle->dhandle, offset, &doc);
261 [ + + ][ + + ]: 8661 : if (doc.key || (doc.length.flag & DOCIO_TXN_COMMITTED)) {
262 : : // check if the doc is transactional or not, and
263 : : // also check if the doc contains system info
264 [ + + ][ + + ]: 8090 : if (!(doc.length.flag & DOCIO_TXN_DIRTY) &&
265 : 6873 : !(doc.length.flag & DOCIO_SYSTEM)) {
266 [ + + ]: 6290 : if (doc.length.flag & DOCIO_TXN_COMMITTED) {
267 : : // commit mark .. read doc offset
268 : 1210 : doc_offset = doc.doc_offset;
269 : : // read the previously skipped doc
270 : 1210 : docio_read_doc(handle->dhandle, doc_offset, &doc);
271 [ - + ]: 1210 : if (doc.key == NULL) { // doc read error
272 : 0 : free(doc.meta);
273 : 0 : free(doc.body);
274 : 0 : offset = _offset;
275 : 0 : continue;
276 : : }
277 : : } else {
278 : 5080 : doc_offset = offset;
279 : : }
280 : :
281 : : // If say a snapshot is taken on a db handle after
282 : : // rollback, then skip WAL items after rollback point
283 [ + + ][ + + ]: 6290 : if ((mode == FDB_RESTORE_KV_INS || !handle->kvs) &&
[ + + ]
284 : : doc.seqnum > handle->seqnum) {
285 : 19 : free(doc.key);
286 : 19 : free(doc.meta);
287 : 19 : free(doc.body);
288 : 19 : offset = _offset;
289 : 19 : continue;
290 : : }
291 : :
292 : : // restore document
293 : : fdb_doc wal_doc;
294 : 6271 : wal_doc.keylen = doc.length.keylen;
295 : 6271 : wal_doc.bodylen = doc.length.bodylen;
296 : 6271 : wal_doc.key = doc.key;
297 : 6271 : wal_doc.seqnum = doc.seqnum;
298 : 6271 : wal_doc.deleted = doc.length.flag & DOCIO_DELETED;
299 : :
300 [ + + ]: 6271 : if (!handle->shandle) {
301 : 5654 : wal_doc.metalen = doc.length.metalen;
302 : 5654 : wal_doc.meta = doc.meta;
303 : 5654 : wal_doc.size_ondisk = _fdb_get_docsize(doc.length);
304 : :
305 [ + + ]: 5654 : if (handle->kvs) {
306 : : // check seqnum before insert
307 : : fdb_kvs_id_t *_kv_id, kv_id;
308 : : fdb_seqnum_t kv_seqnum;
309 : 5649 : _kv_id = (fdb_kvs_id_t*)wal_doc.key;
310 : 5649 : kv_id = _endian_decode(*_kv_id);
311 : :
312 [ + + ]: 5649 : if (handle->config.seqtree_opt == FDB_SEQTREE_USE) {
313 : 5639 : kv_seqnum = fdb_kvs_get_seqnum(handle->file, kv_id);
314 : : } else {
315 : 10 : kv_seqnum = SEQNUM_NOT_USED;
316 : : }
317 [ + + ][ + + ]: 5649 : if (doc.seqnum <= kv_seqnum &&
[ + + ][ + + ]
318 : : ((mode == FDB_RESTORE_KV_INS &&
319 : : kv_id == kv_id_req) ||
320 : : (mode == FDB_RESTORE_NORMAL)) ) {
321 : : // if mode is NORMAL, restore all items
322 : : // if mode is KV_INS, restore items matching ID
323 : : wal_insert(&file->global_txn, file,
324 : 3617 : &wal_doc, doc_offset);
325 : : }
326 : : } else {
327 : : wal_insert(&file->global_txn, file,
328 : 5 : &wal_doc, doc_offset);
329 : : }
330 [ + - ]: 5654 : if (doc.key) free(doc.key);
331 : : } else {
332 : : // snapshot
333 [ + - ]: 617 : if (handle->kvs) {
334 : : fdb_kvs_id_t *_kv_id, kv_id;
335 : 617 : _kv_id = (fdb_kvs_id_t*)wal_doc.key;
336 : 617 : kv_id = _endian_decode(*_kv_id);
337 [ + + ]: 617 : if (kv_id == handle->kvs->id) {
338 : : // snapshot: insert ID matched documents only
339 : : snap_insert(handle->shandle,
340 : 217 : &wal_doc, doc_offset);
341 : : } else {
342 : 400 : free(doc.key);
343 : : }
344 : : } else {
345 : 0 : snap_insert(handle->shandle, &wal_doc, doc_offset);
346 : : }
347 : : }
348 : 6271 : free(doc.meta);
349 : 6271 : free(doc.body);
350 : 6271 : offset = _offset;
351 : : } else {
352 : : // skip transactional document or system document
353 : 1800 : free(doc.key);
354 : 1800 : free(doc.meta);
355 : 1800 : free(doc.body);
356 : 1800 : offset = _offset;
357 : : // do not break.. read next doc
358 : 8071 : }
359 : : } else {
360 : 571 : free(doc.key);
361 : 571 : free(doc.meta);
362 : 571 : free(doc.body);
363 : 571 : offset = _offset;
364 : 571 : break;
365 : : }
366 : : } while (offset + sizeof(struct docio_length) < hdr_off);
367 : : }
368 : : }
369 : : // wal commit
370 [ + + ]: 146 : if (!handle->shandle) {
371 : 140 : wal_commit(&file->global_txn, file, NULL);
372 : : }
373 : 146 : filemgr_mutex_unlock(file);
374 : 519 : handle->dhandle->log_callback = log_callback;
375 : : }
376 : :
377 : : // restore the documents in NEW_FILENAME (corrupted file during compaction)
378 : : // into the file referred by HANDLE
379 : 1 : INLINE fdb_status _fdb_recover_compaction(fdb_kvs_handle *handle,
380 : : const char *new_filename)
381 : : {
382 : 1 : uint64_t offset = 0;
383 : 1 : uint32_t blocksize = handle->config.blocksize;
384 : : fdb_kvs_handle new_db;
385 : 1 : fdb_config config = handle->config;
386 : : struct filemgr *new_file;
387 : : struct docio_handle dhandle;
388 : :
389 : 1 : memset(&new_db, 0, sizeof(new_db));
390 : 1 : new_db.log_callback.callback = handle->log_callback.callback;
391 : 1 : new_db.log_callback.ctx_data = handle->log_callback.ctx_data;
392 : : // Disable the error logging callback as there are false positive
393 : : // checksum errors in docio_read_doc.
394 : : // TODO: Need to adapt docio_read_doc to separate false checksum errors.
395 : 1 : dhandle.log_callback = NULL;
396 : 1 : config.flags |= FDB_OPEN_FLAG_RDONLY;
397 : 1 : new_db.fhandle = handle->fhandle;
398 : 1 : new_db.kvs_config = handle->kvs_config;
399 : 1 : fdb_status status = _fdb_open(&new_db, new_filename, &config);
400 [ - + ]: 1 : if (status != FDB_RESULT_SUCCESS) {
401 : : return fdb_log(&handle->log_callback, status,
402 : : "Error in opening a partially compacted file '%s' for recovery.",
403 : 0 : new_filename);
404 : : }
405 : :
406 : 1 : new_file = new_db.file;
407 [ + - ][ + - ]: 1 : if (new_file->old_filename &&
408 : : !strncmp(new_file->old_filename, handle->file->filename,
409 : 1 : FDB_MAX_FILENAME_LEN)) {
410 : 1 : struct filemgr *old_file = handle->file;
411 : : // If new file has a recorded old_filename then it means that
412 : : // compaction has completed successfully. Mark self for deletion
413 : 1 : filemgr_mutex_lock(new_file);
414 : :
415 : 1 : status = btreeblk_end(handle->bhandle);
416 [ - + ]: 1 : if (status != FDB_RESULT_SUCCESS) {
417 : 0 : filemgr_mutex_unlock(new_file);
418 : 0 : _fdb_close(&new_db);
419 : 0 : return status;
420 : : }
421 : 1 : btreeblk_free(handle->bhandle);
422 : 1 : free(handle->bhandle);
423 : 1 : handle->bhandle = new_db.bhandle;
424 : :
425 : 1 : docio_free(handle->dhandle);
426 : 1 : free(handle->dhandle);
427 : 1 : handle->dhandle = new_db.dhandle;
428 : :
429 : 1 : hbtrie_free(handle->trie);
430 : 1 : free(handle->trie);
431 : 1 : handle->trie = new_db.trie;
432 : :
433 : 1 : wal_shutdown(handle->file);
434 : 1 : handle->file = new_file;
435 : :
436 [ + - ]: 1 : if (handle->config.seqtree_opt == FDB_SEQTREE_USE) {
437 [ + - ]: 1 : if (handle->kvs) {
438 : : // multi KV instance mode
439 : 1 : hbtrie_free(handle->seqtrie);
440 : 1 : free(handle->seqtrie);
441 [ + - ]: 1 : if (new_db.config.seqtree_opt == FDB_SEQTREE_USE) {
442 : 1 : handle->seqtrie = new_db.seqtrie;
443 : : }
444 : : } else {
445 : 0 : free(handle->seqtree->kv_ops);
446 : 0 : free(handle->seqtree);
447 [ # # ]: 0 : if (new_db.config.seqtree_opt == FDB_SEQTREE_USE) {
448 : 0 : handle->seqtree = new_db.seqtree;
449 : : }
450 : : }
451 : : }
452 : :
453 : 1 : filemgr_mutex_unlock(new_file);
454 [ + - ]: 1 : if (new_db.kvs) {
455 : 1 : fdb_kvs_info_free(&new_db);
456 : : }
457 : : // remove self: WARNING must not close this handle if snapshots
458 : : // are yet to open this file
459 : 1 : filemgr_remove_pending(old_file, new_db.file);
460 : 1 : filemgr_close(old_file, 0, handle->filename, &handle->log_callback);
461 : 1 : free(new_db.filename);
462 : 1 : return FDB_RESULT_FAIL_BY_COMPACTION;
463 : : }
464 : 0 : docio_init(&dhandle, new_file, config.compress_document_body);
465 : :
466 [ # # ]: 0 : for (offset = 0; offset < new_file->pos;
467 : : offset = ((offset/blocksize)+1) * blocksize) {
468 : :
469 [ # # ]: 0 : if (!docio_check_buffer(&dhandle, offset/blocksize)) {
470 : : // this block is not for documents
471 : 0 : continue;
472 : :
473 : : } else {
474 [ # # ]: 0 : do {
475 : : struct docio_object doc;
476 : : uint64_t _offset;
477 : : uint64_t doc_offset;
478 : 0 : memset(&doc, 0, sizeof(doc));
479 : 0 : _offset = docio_read_doc(&dhandle, offset, &doc);
480 [ # # ]: 0 : if ((doc.key || (doc.length.flag & DOCIO_TXN_COMMITTED)) &&
[ # # # # ]
[ # # ]
481 : 0 : docio_check_compact_doc(&dhandle, &doc)) {
482 : : // Check if the doc is transactional or contains system info.
483 [ # # ][ # # ]: 0 : if (!(doc.length.flag & DOCIO_TXN_DIRTY) &&
484 : 0 : !(doc.length.flag & DOCIO_SYSTEM)) {
485 [ # # ]: 0 : if (doc.length.flag & DOCIO_TXN_COMMITTED) {
486 : : // commit mark .. read doc offset
487 : 0 : doc_offset = doc.doc_offset;
488 : : // read the previously skipped doc
489 : 0 : docio_read_doc(handle->dhandle, doc_offset, &doc);
490 [ # # ]: 0 : if (doc.key == NULL) {
491 : : // doc read error
492 [ # # ]: 0 : if (doc.key) free(doc.key);
493 [ # # ]: 0 : if (doc.meta) free(doc.meta);
494 [ # # ]: 0 : if (doc.body) free(doc.body);
495 : 0 : offset = _offset;
496 : 0 : continue;
497 : : }
498 : : }
499 : :
500 : : // this document was interleaved during compaction
501 : : fdb_doc wal_doc;
502 : 0 : wal_doc.keylen = doc.length.keylen;
503 : 0 : wal_doc.metalen = doc.length.metalen;
504 : 0 : wal_doc.bodylen = doc.length.bodylen;
505 : 0 : wal_doc.key = doc.key;
506 : 0 : wal_doc.seqnum = doc.seqnum;
507 : :
508 : 0 : wal_doc.meta = doc.meta;
509 : 0 : wal_doc.body = doc.body;
510 : 0 : wal_doc.deleted = doc.length.flag & DOCIO_DELETED;
511 : :
512 : 0 : fdb_set(handle, &wal_doc);
513 : :
514 : 0 : free(doc.key);
515 : 0 : free(doc.meta);
516 : 0 : free(doc.body);
517 : 0 : offset = _offset;
518 : : } else {
519 [ # # ]: 0 : if (doc.length.flag & DOCIO_SYSTEM) {
520 : : // KV instances header
521 : : // free existing KV header of handle->file
522 [ # # ]: 0 : if (handle->file->kv_header) {
523 : 0 : handle->file->free_kv_header(handle->file);
524 : : }
525 : 0 : fdb_kvs_header_create(handle->file);
526 : : // read from 'dhandle' (new file),
527 : : // and import into 'handle->file' (old_file)
528 : 0 : fdb_kvs_header_read(handle->file, &dhandle, offset);
529 : : // write KV header in 'handle->file'
530 : : // using 'handle->dhandle'
531 : 0 : fdb_kvs_header_append(handle->file, handle->dhandle);
532 : : }
533 : : // otherwise, skip but do not break.. read next doc
534 : 0 : free(doc.key);
535 : 0 : free(doc.meta);
536 : 0 : free(doc.body);
537 : 0 : offset = _offset;
538 : : }
539 : : } else {
540 : 0 : free(doc.key);
541 : 0 : free(doc.meta);
542 : 0 : free(doc.body);
543 : 0 : offset = _offset;
544 : 0 : break;
545 : : }
546 : : } while (offset + sizeof(struct docio_length) < new_file->pos);
547 : : }
548 : : }
549 : :
550 : 0 : docio_free(&dhandle);
551 [ # # ]: 0 : if (new_db.kvs) {
552 : 0 : fdb_kvs_info_free(&new_db);
553 : : }
554 : 0 : _fdb_close(&new_db);
555 : 0 : _fdb_commit(handle, FDB_COMMIT_NORMAL);
556 : :
557 : 1 : return FDB_RESULT_SUCCESS;
558 : : }
559 : :
560 : : LIBFDB_API
561 : 277 : fdb_status fdb_init(fdb_config *config)
562 : : {
563 : : fdb_config _config;
564 : : compactor_config c_config;
565 : : struct filemgr_config f_config;
566 : :
567 [ + - ]: 277 : if (config) {
568 [ + - ]: 277 : if (validate_fdb_config(config)) {
569 : 277 : _config = *config;
570 : : } else {
571 : 0 : return FDB_RESULT_INVALID_CONFIG;
572 : : }
573 : : } else {
574 : 0 : _config = get_default_config();
575 : : }
576 : :
577 : : // global initialization
578 : : // initialized only once at first time
579 : 277 : if (!fdb_initialized) {
580 : : #ifndef SPIN_INITIALIZER
581 : : // Note that only Windows passes through this routine
582 : : if (InterlockedCompareExchange(&initial_lock_status, 1, 0) == 0) {
583 : : // atomically initialize spin lock only once
584 : : spin_init(&initial_lock);
585 : : initial_lock_status = 2;
586 : : } else {
587 : : // the others .. wait until initializing 'initial_lock' is done
588 : : while (initial_lock_status != 2) {
589 : : Sleep(1);
590 : : }
591 : : }
592 : : #endif
593 : :
594 : : }
595 : 277 : spin_lock(&initial_lock);
596 [ + + ]: 277 : if (!fdb_initialized) {
597 : : // initialize file manager and block cache
598 : 105 : f_config.blocksize = _config.blocksize;
599 : 105 : f_config.ncacheblock = _config.buffercache_size / _config.blocksize;
600 : 105 : filemgr_init(&f_config);
601 : :
602 : : // initialize compaction daemon
603 : 105 : c_config.sleep_duration = _config.compactor_sleep_duration;
604 : 105 : compactor_init(&c_config);
605 : :
606 : 105 : fdb_initialized = 1;
607 : : }
608 : 277 : fdb_open_inprog++;
609 : 277 : spin_unlock(&initial_lock);
610 : :
611 : 277 : return FDB_RESULT_SUCCESS;
612 : : }
613 : :
614 : : LIBFDB_API
615 : 123 : fdb_config fdb_get_default_config(void) {
616 : 123 : return get_default_config();
617 : : }
618 : :
619 : : LIBFDB_API
620 : 162 : fdb_kvs_config fdb_get_default_kvs_config(void) {
621 : 162 : return get_default_kvs_config();
622 : : }
623 : :
624 : : LIBFDB_API
625 : 264 : fdb_status fdb_open(fdb_file_handle **ptr_fhandle,
626 : : const char *filename,
627 : : fdb_config *fconfig)
628 : : {
629 : : #ifdef _MEMPOOL
630 : : mempool_init();
631 : : #endif
632 : :
633 : : fdb_config config;
634 : : fdb_file_handle *fhandle;
635 : : fdb_kvs_handle *handle;
636 : :
637 [ + - ]: 264 : if (fconfig) {
638 [ + + ]: 264 : if (validate_fdb_config(fconfig)) {
639 : 264 : config = *fconfig;
640 : : } else {
641 : 1 : return FDB_RESULT_INVALID_CONFIG;
642 : : }
643 : : } else {
644 : 0 : config = get_default_config();
645 : : }
646 : :
647 : 264 : fhandle = (fdb_file_handle*)calloc(1, sizeof(fdb_file_handle));
648 [ - + ]: 264 : if (!fhandle) {
649 : 0 : return FDB_RESULT_ALLOC_FAIL;
650 : : }
651 : :
652 : 264 : handle = (fdb_kvs_handle *) calloc(1, sizeof(fdb_kvs_handle));
653 [ - + ]: 264 : if (!handle) {
654 : 0 : free(fhandle);
655 : 0 : return FDB_RESULT_ALLOC_FAIL;
656 : : }
657 : :
658 : 264 : handle->shandle = NULL;
659 : 264 : handle->kvs_config = get_default_kvs_config();
660 : :
661 : 264 : fdb_init(fconfig);
662 : 264 : fdb_file_handle_init(fhandle, handle);
663 : :
664 : 264 : fdb_status fs = _fdb_open(handle, filename, &config);
665 [ + + ]: 264 : if (fs == FDB_RESULT_SUCCESS) {
666 : 251 : *ptr_fhandle = fhandle;
667 : : } else {
668 : 13 : *ptr_fhandle = NULL;
669 : 13 : free(handle);
670 : 13 : fdb_file_handle_free(fhandle);
671 : : }
672 : 264 : spin_lock(&initial_lock);
673 : 264 : fdb_open_inprog--;
674 : 264 : spin_unlock(&initial_lock);
675 : 265 : return fs;
676 : : }
677 : :
678 : : LIBFDB_API
679 : 13 : fdb_status fdb_open_custom_cmp(fdb_file_handle **ptr_fhandle,
680 : : const char *filename,
681 : : fdb_config *fconfig,
682 : : size_t num_functions,
683 : : char **kvs_names,
684 : : fdb_custom_cmp_variable *functions)
685 : : {
686 : : #ifdef _MEMPOOL
687 : : mempool_init();
688 : : #endif
689 : :
690 : : fdb_config config;
691 : : fdb_file_handle *fhandle;
692 : : fdb_kvs_handle *handle;
693 : :
694 [ + - ]: 13 : if (fconfig) {
695 [ + - ]: 13 : if (validate_fdb_config(fconfig)) {
696 : 13 : config = *fconfig;
697 : : } else {
698 : 0 : return FDB_RESULT_INVALID_CONFIG;
699 : : }
700 : : } else {
701 : 0 : config = get_default_config();
702 : : }
703 : :
704 [ - + ]: 13 : if (config.multi_kv_instances == false) {
705 : : // single KV instance mode does not support customized cmp function
706 : 0 : return FDB_RESULT_INVALID_CONFIG;
707 : : }
708 : :
709 : 13 : fhandle = (fdb_file_handle*)calloc(1, sizeof(fdb_file_handle));
710 [ - + ]: 13 : if (!fhandle) {
711 : 0 : return FDB_RESULT_ALLOC_FAIL;
712 : : }
713 : :
714 : 13 : handle = (fdb_kvs_handle *) calloc(1, sizeof(fdb_kvs_handle));
715 [ - + ]: 13 : if (!handle) {
716 : 0 : free(fhandle);
717 : 0 : return FDB_RESULT_ALLOC_FAIL;
718 : : }
719 : :
720 : 13 : handle->shandle = NULL;
721 : 13 : handle->kvs_config = get_default_kvs_config();
722 : :
723 : 13 : fdb_init(fconfig);
724 : 13 : fdb_file_handle_init(fhandle, handle);
725 : :
726 : : // insert kvs_names and functions into fhandle's list
727 : : fdb_file_handle_parse_cmp_func(fhandle, num_functions,
728 : 13 : kvs_names, functions);
729 : :
730 : 13 : fdb_status fs = _fdb_open(handle, filename, &config);
731 [ + + ]: 13 : if (fs == FDB_RESULT_SUCCESS) {
732 : 11 : *ptr_fhandle = fhandle;
733 : : } else {
734 : 2 : *ptr_fhandle = NULL;
735 : 2 : free(handle);
736 : 2 : fdb_file_handle_free(fhandle);
737 : : }
738 : 13 : spin_lock(&initial_lock);
739 : 13 : fdb_open_inprog--;
740 : 13 : spin_unlock(&initial_lock);
741 : 13 : return fs;
742 : : }
743 : :
744 : 13 : fdb_status fdb_open_for_compactor(fdb_file_handle **ptr_fhandle,
745 : : const char *filename,
746 : : fdb_config *fconfig)
747 : : {
748 : : #ifdef _MEMPOOL
749 : : mempool_init();
750 : : #endif
751 : :
752 : : fdb_file_handle *fhandle;
753 : : fdb_kvs_handle *handle;
754 : :
755 : 13 : fhandle = (fdb_file_handle*)calloc(1, sizeof(fdb_file_handle));
756 [ - + ]: 13 : if (!fhandle) {
757 : 0 : return FDB_RESULT_ALLOC_FAIL;
758 : : }
759 : :
760 : 13 : handle = (fdb_kvs_handle *) calloc(1, sizeof(fdb_kvs_handle));
761 [ - + ]: 13 : if (!handle) {
762 : 0 : free(fhandle);
763 : 0 : return FDB_RESULT_ALLOC_FAIL;
764 : : }
765 : 13 : handle->shandle = NULL;
766 : :
767 : 13 : fdb_file_handle_init(fhandle, handle);
768 : 13 : fdb_status fs = _fdb_open(handle, filename, fconfig);
769 [ + - ]: 13 : if (fs == FDB_RESULT_SUCCESS) {
770 : 13 : *ptr_fhandle = fhandle;
771 : : } else {
772 : 0 : *ptr_fhandle = NULL;
773 : 0 : free(handle);
774 : 0 : fdb_file_handle_free(fhandle);
775 : : }
776 : 13 : return fs;
777 : : }
778 : :
779 : : LIBFDB_API
780 : 40 : fdb_status fdb_snapshot_open(fdb_kvs_handle *handle_in, fdb_kvs_handle **ptr_handle,
781 : : fdb_seqnum_t seqnum)
782 : : {
783 : : #ifdef _MEMPOOL
784 : : mempool_init();
785 : : #endif
786 : :
787 : 40 : fdb_config config = handle_in->config;
788 : 40 : fdb_kvs_config kvs_config = handle_in->kvs_config;
789 : : fdb_kvs_handle *handle;
790 : : fdb_status fs;
791 : : filemgr *file;
792 [ + - ][ - + ]: 40 : if (!handle_in || !ptr_handle) {
793 : 0 : return FDB_RESULT_INVALID_ARGS;
794 : : }
795 : :
796 : : // Sequence trees are a must for snapshot creation
797 [ - + ]: 40 : if (handle_in->config.seqtree_opt != FDB_SEQTREE_USE) {
798 : 0 : return FDB_RESULT_INVALID_CONFIG;
799 : : }
800 : :
801 [ + + ]: 40 : if (!handle_in->shandle) {
802 : 38 : fdb_check_file_reopen(handle_in);
803 : 38 : fdb_link_new_file(handle_in);
804 : 38 : fdb_sync_db_header(handle_in);
805 [ + - ]: 38 : if (handle_in->new_file == NULL) {
806 : 38 : file = handle_in->file;
807 : : } else {
808 : 0 : file = handle_in->new_file;
809 : : }
810 [ + - ][ + + ]: 38 : if (handle_in->kvs && handle_in->kvs->type == KVS_SUB) {
811 : 5 : handle_in->seqnum = fdb_kvs_get_seqnum(file, handle_in->kvs->id);
812 : : } else {
813 : 38 : handle_in->seqnum = filemgr_get_seqnum(file);
814 : : }
815 : : } else {
816 : 2 : file = handle_in->file;
817 : : }
818 : :
819 : : // if the max sequence number seen by this handle is lower than the
820 : : // requested snapshot marker, it means the snapshot is not yet visible
821 : : // even via the current fdb_kvs_handle
822 [ + + ][ + + ]: 40 : if (seqnum != FDB_SNAPSHOT_INMEM && seqnum > handle_in->seqnum) {
823 : 5 : return FDB_RESULT_NO_DB_INSTANCE;
824 : : }
825 : :
826 : 35 : handle = (fdb_kvs_handle *) calloc(1, sizeof(fdb_kvs_handle));
827 [ - + ]: 35 : if (!handle) {
828 : 0 : return FDB_RESULT_ALLOC_FAIL;
829 : : }
830 : :
831 : 35 : handle->log_callback = handle_in->log_callback;
832 : 35 : handle->max_seqnum = seqnum;
833 : 35 : handle->fhandle = handle_in->fhandle;
834 : :
835 : 35 : config.flags |= FDB_OPEN_FLAG_RDONLY;
836 : : // do not perform compaction for snapshot
837 : 35 : config.compaction_mode = FDB_COMPACTION_MANUAL;
838 : :
839 : : // If cloning an existing snapshot handle, then rewind indexes
840 : : // to its last DB header and point its avl tree to existing snapshot's tree
841 [ + + ]: 35 : if (handle_in->shandle) {
842 : 2 : handle->last_hdr_bid = handle_in->last_hdr_bid;
843 [ + - ]: 2 : if (snap_clone(handle_in->shandle, handle_in->max_seqnum,
844 : 2 : &handle->shandle, seqnum) == FDB_RESULT_SUCCESS) {
845 : 2 : handle->max_seqnum = FDB_SNAPSHOT_INMEM; // temp value to skip WAL
846 : : }
847 : : }
848 : :
849 [ + + ]: 35 : if (!handle->shandle) {
850 : 33 : handle->shandle = (struct snap_handle *) calloc(1, sizeof(snap_handle));
851 [ - + ]: 33 : if (!handle->shandle) {
852 : 0 : free(handle);
853 : 0 : return FDB_RESULT_ALLOC_FAIL;
854 : : }
855 : 33 : snap_init(handle->shandle, handle_in);
856 : : }
857 : :
858 [ + - ]: 35 : if (handle_in->kvs) {
859 : : // sub-handle in multi KV instance mode
860 : : fs = _fdb_kvs_open(handle_in->kvs->root,
861 : : &config, &kvs_config, file,
862 : : _fdb_kvs_get_name(handle_in,
863 : 35 : file),
864 : 35 : handle);
865 : : } else {
866 : 0 : fs = _fdb_open(handle, file->filename, &config);
867 : : }
868 : :
869 [ + + ]: 35 : if (fs == FDB_RESULT_SUCCESS) {
870 [ + + ][ + + ]: 34 : if (seqnum == FDB_SNAPSHOT_INMEM && !handle_in->shandle) {
871 : : wal_snapshot(handle->file, (void *)handle->shandle,
872 : 2 : handle_in->txn, _fdb_wal_snapshot_func);
873 : : // set seqnum based on handle type (multikv or default)
874 [ + - ][ - + ]: 4 : if (handle_in->kvs && handle_in->kvs->id > 0) {
875 : : handle->max_seqnum = _fdb_kvs_get_seqnum(file->kv_header,
876 : 0 : handle_in->kvs->id);
877 : : } else {
878 : 2 : handle->max_seqnum = filemgr_get_seqnum(file);
879 : : }
880 [ + + ]: 32 : } else if (handle->max_seqnum == FDB_SNAPSHOT_INMEM) {
881 : 2 : handle->max_seqnum = handle_in->seqnum;
882 : : }
883 : 34 : *ptr_handle = handle;
884 : : } else {
885 : 1 : *ptr_handle = NULL;
886 : 1 : snap_close(handle->shandle);
887 : 1 : free(handle);
888 : : }
889 : 40 : return fs;
890 : : }
891 : :
892 : : LIBFDB_API
893 : 28 : fdb_status fdb_rollback(fdb_kvs_handle **handle_ptr, fdb_seqnum_t seqnum)
894 : : {
895 : : #ifdef _MEMPOOL
896 : : mempool_init();
897 : : #endif
898 : :
899 : : fdb_config config;
900 : : fdb_kvs_handle *handle_in, *handle;
901 : : fdb_status fs;
902 : : fdb_seqnum_t old_seqnum;
903 : :
904 [ + - ][ - + ]: 28 : if (!handle_ptr || !seqnum) {
905 : 0 : return FDB_RESULT_INVALID_ARGS;
906 : : }
907 : :
908 : 28 : handle_in = *handle_ptr;
909 : 28 : config = handle_in->config;
910 : :
911 [ + + ]: 28 : if (handle_in->kvs) {
912 : 25 : return fdb_kvs_rollback(handle_ptr, seqnum);
913 : : }
914 : :
915 : : // Sequence trees are a must for rollback
916 [ - + ]: 3 : if (handle_in->config.seqtree_opt != FDB_SEQTREE_USE) {
917 : 0 : return FDB_RESULT_INVALID_CONFIG;
918 : : }
919 : :
920 [ - + ]: 3 : if (handle_in->config.flags & FDB_OPEN_FLAG_RDONLY) {
921 : : return fdb_log(&handle_in->log_callback, FDB_RESULT_RONLY_VIOLATION,
922 : : "Warning: Rollback is not allowed on the read-only DB file '%s'.",
923 : 0 : handle_in->file->filename);
924 : : }
925 : :
926 : : // if the max sequence number seen by this handle is lower than the
927 : : // requested snapshot marker, it means the snapshot is not yet visible
928 : : // even via the current fdb_kvs_handle
929 [ + + ]: 3 : if (seqnum > handle_in->seqnum) {
930 : 1 : return FDB_RESULT_NO_DB_INSTANCE;
931 : : }
932 : :
933 : 2 : handle = (fdb_kvs_handle *) calloc(1, sizeof(fdb_kvs_handle));
934 [ - + ]: 2 : if (!handle) {
935 : 0 : return FDB_RESULT_ALLOC_FAIL;
936 : : }
937 : :
938 : 2 : filemgr_mutex_lock(handle_in->file);
939 : 2 : filemgr_set_rollback(handle_in->file, 1); // disallow writes operations
940 : : // All transactions should be closed before rollback
941 [ + + ]: 2 : if (wal_txn_exists(handle_in->file)) {
942 : 1 : filemgr_set_rollback(handle_in->file, 0);
943 : 1 : filemgr_mutex_unlock(handle_in->file);
944 : 1 : free(handle);
945 : 1 : return FDB_RESULT_FAIL_BY_TRANSACTION;
946 : : }
947 : : // There should be no compaction on the file
948 [ - + ]: 1 : if (filemgr_get_file_status(handle_in->file) != FILE_NORMAL) {
949 : 0 : filemgr_set_rollback(handle_in->file, 0);
950 : 0 : filemgr_mutex_unlock(handle_in->file);
951 : 0 : free(handle);
952 : 0 : return FDB_RESULT_FAIL_BY_COMPACTION;
953 : : }
954 : 1 : filemgr_mutex_unlock(handle_in->file);
955 : :
956 : 1 : handle->log_callback = handle_in->log_callback;
957 : 1 : handle->max_seqnum = seqnum;
958 : 1 : handle->fhandle = handle_in->fhandle;
959 : :
960 : 1 : fs = _fdb_open(handle, handle_in->file->filename, &config);
961 : 1 : filemgr_set_rollback(handle_in->file, 0); // allow mutations
962 : :
963 [ + - ]: 1 : if (fs == FDB_RESULT_SUCCESS) {
964 : : // rollback the file's sequence number
965 : 1 : filemgr_mutex_lock(handle_in->file);
966 : 1 : old_seqnum = filemgr_get_seqnum(handle_in->file);
967 : 1 : filemgr_set_seqnum(handle_in->file, seqnum);
968 : 1 : filemgr_mutex_unlock(handle_in->file);
969 : :
970 : 1 : fs = _fdb_commit(handle, FDB_COMMIT_NORMAL);
971 [ + - ]: 1 : if (fs == FDB_RESULT_SUCCESS) {
972 [ - + ]: 1 : if (handle_in->txn) {
973 : 0 : handle->txn = handle_in->txn;
974 : 0 : handle_in->txn = NULL;
975 : : }
976 : 1 : handle_in->fhandle->root = handle;
977 : 1 : _fdb_close_root(handle_in);
978 : 1 : handle->max_seqnum = 0;
979 : 1 : handle->seqnum = seqnum;
980 : 1 : *handle_ptr = handle;
981 : : } else {
982 : : // cancel the rolling-back of the sequence number
983 : 0 : filemgr_mutex_lock(handle_in->file);
984 : 0 : filemgr_set_seqnum(handle_in->file, old_seqnum);
985 : 0 : filemgr_mutex_unlock(handle_in->file);
986 : 0 : free(handle);
987 : : }
988 : : } else {
989 : 0 : free(handle);
990 : : }
991 : :
992 : 28 : return fs;
993 : : }
994 : :
995 : 537 : static void _fdb_init_file_config(const fdb_config *config,
996 : : struct filemgr_config *fconfig) {
997 : 537 : fconfig->blocksize = config->blocksize;
998 : 537 : fconfig->ncacheblock = config->buffercache_size / config->blocksize;
999 : :
1000 : 537 : fconfig->options = 0x0;
1001 [ + + ]: 537 : if (config->flags & FDB_OPEN_FLAG_CREATE) {
1002 : 457 : fconfig->options |= FILEMGR_CREATE;
1003 : : }
1004 [ + + ]: 537 : if (config->flags & FDB_OPEN_FLAG_RDONLY) {
1005 : 78 : fconfig->options |= FILEMGR_READONLY;
1006 : : }
1007 [ + + ]: 537 : if (!(config->durability_opt & FDB_DRB_ASYNC)) {
1008 : 535 : fconfig->options |= FILEMGR_SYNC;
1009 : : }
1010 : :
1011 : 537 : fconfig->flag = 0x0;
1012 [ - + ]: 537 : if (config->durability_opt & FDB_DRB_ODIRECT) {
1013 : 0 : fconfig->flag |= _ARCH_O_DIRECT;
1014 : : }
1015 : :
1016 : 537 : fconfig->prefetch_duration = config->prefetch_duration;
1017 : 537 : }
1018 : :
1019 : 534 : fdb_status _fdb_open(fdb_kvs_handle *handle,
1020 : : const char *filename,
1021 : : const fdb_config *config)
1022 : : {
1023 : : struct filemgr_config fconfig;
1024 : : struct kvs_stat stat, empty_stat;
1025 : 534 : bid_t trie_root_bid = BLK_NOT_FOUND;
1026 : 534 : bid_t seq_root_bid = BLK_NOT_FOUND;
1027 : 534 : fdb_seqnum_t seqnum = 0;
1028 : 534 : fdb_seqtree_opt_t seqtree_opt = config->seqtree_opt;
1029 : 534 : uint64_t ndocs = 0;
1030 : 534 : uint64_t datasize = 0;
1031 : 534 : uint64_t last_wal_flush_hdr_bid = BLK_NOT_FOUND;
1032 : 534 : uint64_t kv_info_offset = BLK_NOT_FOUND;
1033 : 534 : uint64_t header_flags = 0;
1034 : : uint8_t header_buf[FDB_BLOCKSIZE];
1035 : 534 : char *compacted_filename = NULL;
1036 : 534 : char *prev_filename = NULL;
1037 : 534 : size_t header_len = 0;
1038 : 534 : bool multi_kv_instances = config->multi_kv_instances;
1039 : :
1040 : 534 : uint64_t nlivenodes = 0;
1041 : 534 : bid_t hdr_bid = 0; // initialize to zero for in-memory snapshot
1042 : : char actual_filename[FDB_MAX_FILENAME_LEN];
1043 : : fdb_status status;
1044 : :
1045 [ - + ]: 534 : if (filename == NULL) {
1046 : 0 : return FDB_RESULT_INVALID_ARGS;
1047 : : }
1048 [ + + ]: 534 : if (strlen(filename) > (FDB_MAX_FILENAME_LEN - 8)) {
1049 : : // filename (including path) length is supported up to
1050 : : // (FDB_MAX_FILENAME_LEN - 8) bytes.
1051 : 1 : return FDB_RESULT_TOO_LONG_FILENAME;
1052 : : }
1053 : :
1054 [ + + ]: 533 : if (!compactor_is_valid_mode(filename, (fdb_config *)config)) {
1055 : 2 : return FDB_RESULT_INVALID_COMPACTION_MODE;
1056 : : }
1057 : :
1058 : 531 : _fdb_init_file_config(config, &fconfig);
1059 : :
1060 : : compactor_get_actual_filename(filename, actual_filename,
1061 : 529 : config->compaction_mode);
1062 [ - + ]: 532 : if (handle->filename) {
1063 : 0 : handle->filename = (char *)realloc(handle->filename, strlen(filename)+1);
1064 : : } else {
1065 : 532 : handle->filename = (char*)malloc(strlen(filename)+1);
1066 : : }
1067 : 532 : strcpy(handle->filename, filename);
1068 : :
1069 : 532 : handle->fileops = get_filemgr_ops();
1070 : : filemgr_open_result result = filemgr_open((char *)actual_filename,
1071 : : handle->fileops,
1072 : 532 : &fconfig, &handle->log_callback);
1073 [ + + ]: 532 : if (result.rv != FDB_RESULT_SUCCESS) {
1074 : 8 : free(handle->filename);
1075 : 8 : handle->filename = NULL;
1076 : 8 : return (fdb_status) result.rv;
1077 : : }
1078 : :
1079 : 524 : handle->file = result.file;
1080 : 524 : filemgr_mutex_lock(handle->file);
1081 : : // If cloning from a snapshot handle, fdb_snapshot_open would have already
1082 : : // set handle->last_hdr_bid to the block id of required header, so rewind..
1083 [ + + ][ + + ]: 524 : if (handle->shandle && handle->last_hdr_bid) {
1084 : : status = filemgr_fetch_header(handle->file, handle->last_hdr_bid,
1085 : : header_buf, &header_len,
1086 : 2 : &handle->log_callback);
1087 [ - + ]: 2 : if (status != FDB_RESULT_SUCCESS) {
1088 : 0 : free(handle->filename);
1089 : 0 : handle->filename = NULL;
1090 : : filemgr_close(handle->file, false, handle->filename,
1091 : 0 : &handle->log_callback);
1092 : 0 : return status;
1093 : : }
1094 : : } else { // Normal open
1095 : 522 : filemgr_get_header(handle->file, header_buf, &header_len);
1096 : 522 : handle->last_hdr_bid = filemgr_get_header_bid(handle->file);
1097 : : }
1098 : :
1099 [ + + ]: 524 : if (header_len > 0) {
1100 : : fdb_fetch_header(header_buf, &trie_root_bid,
1101 : : &seq_root_bid, &ndocs, &nlivenodes,
1102 : : &datasize, &last_wal_flush_hdr_bid, &kv_info_offset,
1103 : 397 : &header_flags, &compacted_filename, &prev_filename);
1104 : : // use existing setting for seqtree_opt
1105 [ + + ]: 397 : if (header_flags & FDB_FLAG_SEQTREE_USE) {
1106 : 396 : seqtree_opt = FDB_SEQTREE_USE;
1107 : : } else {
1108 : 1 : seqtree_opt = FDB_SEQTREE_NOT_USE;
1109 : : }
1110 : : // set seqnum based on handle type (multikv or default)
1111 [ + + ][ + + ]: 397 : if (handle->kvs && handle->kvs->id > 0) {
1112 : : seqnum = _fdb_kvs_get_seqnum(handle->file->kv_header,
1113 : 196 : handle->kvs->id);
1114 : : } else {
1115 : 201 : seqnum = filemgr_get_seqnum(handle->file);
1116 : : }
1117 : : // other flags
1118 [ + + ]: 397 : if (header_flags & FDB_FLAG_ROOT_INITIALIZED) {
1119 : 260 : handle->fhandle->flags |= FHANDLE_ROOT_INITIALIZED;
1120 : : }
1121 [ + + ]: 397 : if (header_flags & FDB_FLAG_ROOT_CUSTOM_CMP) {
1122 : 44 : handle->fhandle->flags |= FHANDLE_ROOT_CUSTOM_CMP;
1123 : : }
1124 : : // use existing setting for multi KV instance mode
1125 [ + + ]: 397 : if (kv_info_offset == BLK_NOT_FOUND) {
1126 : 3 : multi_kv_instances = false;
1127 : : } else {
1128 : 394 : multi_kv_instances = true;
1129 : : }
1130 : : }
1131 : :
1132 : 524 : handle->config = *config;
1133 : 524 : handle->config.seqtree_opt = seqtree_opt;
1134 : 524 : handle->config.multi_kv_instances = multi_kv_instances;
1135 : :
1136 : : handle->dhandle = (struct docio_handle *)
1137 : 524 : calloc(1, sizeof(struct docio_handle));
1138 : 524 : handle->dhandle->log_callback = &handle->log_callback;
1139 : 524 : handle->new_file = NULL;
1140 : 524 : handle->new_dhandle = NULL;
1141 : 524 : docio_init(handle->dhandle, handle->file, config->compress_document_body);
1142 : :
1143 [ + + ][ + + ]: 524 : if (handle->shandle && handle->max_seqnum == FDB_SNAPSHOT_INMEM) {
1144 : : // Either an in-memory snapshot or cloning from an existing snapshot..
1145 : 4 : filemgr_mutex_unlock(handle->file);
1146 : 4 : hdr_bid = 0; // This prevents _fdb_restore_wal() as incoming handle's
1147 : : // *_open() should have already restored it
1148 : : } else {
1149 : 520 : filemgr_mutex_unlock(handle->file);
1150 : :
1151 : 520 : hdr_bid = filemgr_get_pos(handle->file) / FDB_BLOCKSIZE;
1152 [ + + ]: 520 : hdr_bid = hdr_bid ? --hdr_bid : 0;
1153 [ + + ]: 520 : if (handle->max_seqnum) {
1154 : : struct kvs_stat stat_ori;
1155 : : // backup original stats
1156 [ + + ]: 51 : if (handle->kvs) {
1157 : 43 : _kvs_stat_get(handle->file, handle->kvs->id, &stat_ori);
1158 : : } else {
1159 : 8 : _kvs_stat_get(handle->file, 0, &stat_ori);
1160 : : }
1161 : :
1162 [ + + ][ + + ]: 51 : if (handle->max_seqnum == seqnum &&
1163 : : hdr_bid > handle->last_hdr_bid){
1164 : : // In case, snapshot_open is attempted with latest uncommitted
1165 : : // sequence number
1166 : 1 : header_len = 0;
1167 : : }
1168 : : // Reverse scan the file to locate the DB header with seqnum marker
1169 [ + + ][ + + ]: 189 : while (header_len && seqnum != handle->max_seqnum) {
[ + + ]
1170 : : hdr_bid = filemgr_fetch_prev_header(handle->file, hdr_bid,
1171 : : header_buf, &header_len, &seqnum,
1172 : 138 : &handle->log_callback);
1173 [ - + ]: 138 : if (header_len == 0) {
1174 : 0 : continue; // header doesn't exist
1175 : : }
1176 : : fdb_fetch_header(header_buf, &trie_root_bid,
1177 : : &seq_root_bid, &ndocs, &nlivenodes,
1178 : : &datasize, &last_wal_flush_hdr_bid,
1179 : : &kv_info_offset, &header_flags,
1180 : 138 : &compacted_filename, NULL);
1181 : 138 : handle->last_hdr_bid = hdr_bid;
1182 : :
1183 [ + + ][ + + ]: 138 : if (!handle->kvs || handle->kvs->id == 0) {
1184 : : // single KVS mode OR default KVS
1185 [ + + ]: 28 : if (handle->shandle) {
1186 : : // snapshot
1187 : : memset(&handle->shandle->stat, 0x0,
1188 : 14 : sizeof(handle->shandle->stat));
1189 : 14 : handle->shandle->stat.ndocs = ndocs;
1190 : 14 : handle->shandle->stat.datasize = datasize;
1191 : 14 : handle->shandle->stat.nlivenodes = nlivenodes;
1192 : : } else {
1193 : : // rollback
1194 : : struct kvs_stat stat_dst;
1195 : 14 : _kvs_stat_get(handle->file, 0, &stat_dst);
1196 : 14 : stat_dst.ndocs = ndocs;
1197 : 14 : stat_dst.datasize = datasize;
1198 : 14 : stat_dst.nlivenodes = nlivenodes;
1199 : 14 : _kvs_stat_set(handle->file, 0, stat_dst);
1200 : : }
1201 : 28 : continue;
1202 : : }
1203 : :
1204 : : uint64_t doc_offset;
1205 : : struct kvs_header *kv_header;
1206 : : struct docio_object doc;
1207 : :
1208 : 110 : _fdb_kvs_header_create(&kv_header);
1209 : 110 : memset(&doc, 0, sizeof(struct docio_object));
1210 : : doc_offset = docio_read_doc(handle->dhandle,
1211 : 110 : kv_info_offset, &doc);
1212 : :
1213 [ - + ]: 110 : if (doc_offset == kv_info_offset) {
1214 : 0 : header_len = 0; // fail
1215 : 0 : _fdb_kvs_header_free(kv_header);
1216 : : } else {
1217 : : _fdb_kvs_header_import(kv_header, doc.body,
1218 : 110 : doc.length.bodylen);
1219 : : // get local sequence number for the KV instance
1220 : : seqnum = _fdb_kvs_get_seqnum(kv_header,
1221 : 110 : handle->kvs->id);
1222 [ + + ]: 110 : if (handle->shandle) {
1223 : : // snapshot: store stats in shandle
1224 : : memset(&handle->shandle->stat, 0x0,
1225 : 6 : sizeof(handle->shandle->stat));
1226 : : _kvs_stat_get(handle->file,
1227 : : handle->kvs->id,
1228 : 6 : &handle->shandle->stat);
1229 : : } else {
1230 : : // rollback: replace kv_header stats
1231 : : // read from the current header's kv_header
1232 : : struct kvs_stat stat_src, stat_dst;
1233 : : _kvs_stat_get_kv_header(kv_header,
1234 : : handle->kvs->id,
1235 : 104 : &stat_src);
1236 : : _kvs_stat_get(handle->file,
1237 : : handle->kvs->id,
1238 : 104 : &stat_dst);
1239 : : // update ndocs, datasize, nlivenodes
1240 : : // into the current file's kv_header
1241 : : // Note: stats related to WAL should not be updated
1242 : : // at this time. They will be adjusted through
1243 : : // discard & restore routines below.
1244 : 104 : stat_dst.ndocs = stat_src.ndocs;
1245 : 104 : stat_dst.datasize = stat_src.datasize;
1246 : 104 : stat_dst.nlivenodes = stat_src.nlivenodes;
1247 : : _kvs_stat_set(handle->file,
1248 : : handle->kvs->id,
1249 : 104 : stat_dst);
1250 : : }
1251 : 110 : _fdb_kvs_header_free(kv_header);
1252 : 110 : free_docio_object(&doc, 1, 1, 1);
1253 : : }
1254 : : }
1255 [ + + ]: 51 : if (!header_len) { // Marker MUST match that of DB commit!
1256 : : // rollback original stats
1257 [ + - ]: 1 : if (handle->kvs) {
1258 : 1 : _kvs_stat_get(handle->file, handle->kvs->id, &stat_ori);
1259 : : } else {
1260 : 0 : _kvs_stat_get(handle->file, 0, &stat_ori);
1261 : : }
1262 : :
1263 : 1 : docio_free(handle->dhandle);
1264 : 1 : free(handle->dhandle);
1265 : 1 : free(handle->filename);
1266 : 1 : handle->filename = NULL;
1267 : : filemgr_close(handle->file, false, handle->filename,
1268 : 1 : &handle->log_callback);
1269 : 1 : return FDB_RESULT_NO_DB_INSTANCE;
1270 : : }
1271 : :
1272 [ + + ]: 50 : if (!handle->shandle) { // Rollback mode, destroy file WAL..
1273 [ + + ]: 23 : if (handle->config.multi_kv_instances) {
1274 : : // multi KV instance mode
1275 : : // clear only WAL items belonging to the instance
1276 : : wal_close_kv_ins(handle->file,
1277 [ + + ]: 22 : (handle->kvs)?(handle->kvs->id):(0));
1278 : : } else {
1279 : 1 : wal_shutdown(handle->file);
1280 : : }
1281 : : }
1282 : : } else {
1283 [ + + ]: 469 : if (handle->shandle) { // fdb_snapshot_open API call
1284 [ - + ]: 3 : if (seqnum) {
1285 : : // Database currently has a non-zero seq number,
1286 : : // but the snapshot was requested with a seq number zero.
1287 : 0 : docio_free(handle->dhandle);
1288 : 0 : free(handle->dhandle);
1289 : 0 : free(handle->filename);
1290 : 0 : handle->filename = NULL;
1291 : : filemgr_close(handle->file, false, handle->filename,
1292 : 0 : &handle->log_callback);
1293 : 0 : return FDB_RESULT_NO_DB_INSTANCE;
1294 : : }
1295 : : } // end of zero max_seqnum but non-rollback check
1296 : : } // end of zero max_seqnum check
1297 : : } // end of durable snapshot locating
1298 : :
1299 : 523 : handle->btreeblkops = btreeblk_get_ops();
1300 : : handle->bhandle = (struct btreeblk_handle *)
1301 : 523 : calloc(1, sizeof(struct btreeblk_handle));
1302 : 523 : handle->bhandle->log_callback = &handle->log_callback;
1303 : :
1304 : 523 : handle->dirty_updates = 0;
1305 : :
1306 [ - + ]: 523 : if (handle->config.compaction_buf_maxsize == 0) {
1307 : 0 : handle->config.compaction_buf_maxsize = FDB_COMP_BUF_MAXSIZE;
1308 : : }
1309 : :
1310 : 523 : btreeblk_init(handle->bhandle, handle->file, handle->file->blocksize);
1311 : :
1312 : 523 : handle->cur_header_revnum = filemgr_get_header_revnum(handle->file);
1313 : 523 : handle->last_wal_flush_hdr_bid = last_wal_flush_hdr_bid;
1314 : :
1315 : 523 : memset(&empty_stat, 0x0, sizeof(empty_stat));
1316 : 523 : _kvs_stat_get(handle->file, 0, &stat);
1317 [ + + ]: 523 : if (!memcmp(&stat, &empty_stat, sizeof(stat))) { // first open
1318 : : // sync (default) KVS stat with DB header
1319 : 351 : stat.nlivenodes = nlivenodes;
1320 : 351 : stat.ndocs = ndocs;
1321 : 351 : stat.datasize = datasize;
1322 : 351 : _kvs_stat_set(handle->file, 0, stat);
1323 : : }
1324 : :
1325 [ + + ]: 523 : if (handle->config.multi_kv_instances) {
1326 : : // multi KV instance mode
1327 [ + + ]: 518 : if (kv_info_offset == BLK_NOT_FOUND) {
1328 : : // there is no KV header .. create & initialize
1329 : 125 : filemgr_mutex_lock(handle->file);
1330 : 125 : fdb_kvs_header_create(handle->file);
1331 : 125 : kv_info_offset = fdb_kvs_header_append(handle->file, handle->dhandle);
1332 : 125 : filemgr_mutex_unlock(handle->file);
1333 [ + + ]: 393 : } else if (handle->file->kv_header == NULL) {
1334 : : // KV header already exists but not loaded .. read & import
1335 : 60 : fdb_kvs_header_create(handle->file);
1336 : 60 : fdb_kvs_header_read(handle->file, handle->dhandle, kv_info_offset);
1337 : : }
1338 : :
1339 : : // validation check for key order of all KV stores
1340 [ + + ]: 518 : if (handle == handle->fhandle->root) {
1341 : 282 : fdb_status fs = fdb_kvs_cmp_check(handle);
1342 [ + + ]: 282 : if (fs != FDB_RESULT_SUCCESS) { // cmp function mismatch
1343 : 4 : docio_free(handle->dhandle);
1344 : 4 : free(handle->dhandle);
1345 : 4 : btreeblk_free(handle->bhandle);
1346 : 4 : free(handle->bhandle);
1347 : 4 : free(handle->filename);
1348 : 4 : handle->filename = NULL;
1349 : : filemgr_close(handle->file, false, handle->filename,
1350 : 4 : &handle->log_callback);
1351 : 4 : return fs;
1352 : : }
1353 : : }
1354 : : }
1355 : 519 : handle->kv_info_offset = kv_info_offset;
1356 : :
1357 [ + + ][ + + ]: 519 : if (handle->kv_info_offset != BLK_NOT_FOUND &&
1358 : : handle->kvs == NULL) {
1359 : : // multi KV instance mode .. turn on config flag
1360 : 282 : handle->config.multi_kv_instances = true;
1361 : : // only super handle can be opened using fdb_open(...)
1362 : 282 : fdb_kvs_info_create(NULL, handle, handle->file, NULL);
1363 : : }
1364 : :
1365 : 519 : handle->trie = (struct hbtrie *)malloc(sizeof(struct hbtrie));
1366 : : hbtrie_init(handle->trie, config->chunksize, OFFSET_SIZE,
1367 : : handle->file->blocksize, trie_root_bid,
1368 : : (void *)handle->bhandle, handle->btreeblkops,
1369 : 519 : (void *)handle->dhandle, _fdb_readkey_wrap);
1370 : : // set aux for cmp wrapping function
1371 : 519 : handle->trie->aux = NULL;
1372 : 519 : hbtrie_set_leaf_height_limit(handle->trie, 0xff);
1373 : 519 : hbtrie_set_leaf_cmp(handle->trie, _fdb_custom_cmp_wrap);
1374 : :
1375 [ + + ]: 519 : if (handle->kvs) {
1376 : 514 : hbtrie_set_map_function(handle->trie, fdb_kvs_find_cmp_chunk);
1377 : : }
1378 : :
1379 [ + + ]: 519 : if (handle->config.seqtree_opt == FDB_SEQTREE_USE) {
1380 : 517 : handle->seqnum = seqnum;
1381 : :
1382 [ + + ]: 517 : if (handle->config.multi_kv_instances) {
1383 : : // multi KV instance mode .. HB+trie
1384 : 512 : handle->seqtrie = (struct hbtrie *)malloc(sizeof(struct hbtrie));
1385 : : hbtrie_init(handle->seqtrie, sizeof(fdb_kvs_id_t), OFFSET_SIZE,
1386 : : handle->file->blocksize, seq_root_bid,
1387 : : (void *)handle->bhandle, handle->btreeblkops,
1388 : 512 : (void *)handle->dhandle, _fdb_readseq_wrap);
1389 : 512 : handle->seqtrie->aux = NULL;
1390 : :
1391 : : } else {
1392 : : // single KV instance mode .. normal B+tree
1393 : : struct btree_kv_ops *seq_kv_ops =
1394 : 5 : (struct btree_kv_ops *)malloc(sizeof(struct btree_kv_ops));
1395 : 5 : seq_kv_ops = btree_kv_get_kb64_vb64(seq_kv_ops);
1396 : 5 : seq_kv_ops->cmp = _cmp_uint64_t_endian_safe;
1397 : :
1398 : 5 : handle->seqtree = (struct btree*)malloc(sizeof(struct btree));
1399 [ + + ]: 5 : if (seq_root_bid == BLK_NOT_FOUND) {
1400 : : btree_init(handle->seqtree, (void *)handle->bhandle,
1401 : : handle->btreeblkops, seq_kv_ops,
1402 : : handle->config.blocksize, sizeof(fdb_seqnum_t),
1403 : 2 : OFFSET_SIZE, 0x0, NULL);
1404 : : }else{
1405 : : btree_init_from_bid(handle->seqtree, (void *)handle->bhandle,
1406 : : handle->btreeblkops, seq_kv_ops,
1407 : 3 : handle->config.blocksize, seq_root_bid);
1408 : : }
1409 : : }
1410 : : }else{
1411 : 2 : handle->seqtree = NULL;
1412 : : }
1413 : :
1414 [ + + ][ + + ]: 519 : if (handle->config.multi_kv_instances && handle->max_seqnum) {
1415 : : // restore only docs belonging to the KV instance
1416 : : // handle->kvs should not be NULL
1417 : : _fdb_restore_wal(handle, FDB_RESTORE_KV_INS,
1418 [ + - ]: 53 : hdr_bid, (handle->kvs)?(handle->kvs->id):(0));
1419 : : } else {
1420 : : // normal restore
1421 : 466 : _fdb_restore_wal(handle, FDB_RESTORE_NORMAL, hdr_bid, 0);
1422 : : }
1423 : :
1424 [ + + + - ]: 520 : if (compacted_filename &&
[ + - ][ + + ]
1425 : 1 : filemgr_get_file_status(handle->file) == FILE_NORMAL &&
1426 : 1 : !handle->shandle) { // do not do compaction recovery on snapshots
1427 : 1 : _fdb_recover_compaction(handle, compacted_filename);
1428 : : }
1429 : :
1430 [ + + ][ + - ]: 519 : if (prev_filename && !handle->shandle) {
1431 [ + + ]: 40 : if (strcmp(prev_filename, handle->file->filename)) {
1432 : : // record the old filename into the file handle of current file
1433 : : // and REMOVE old file on the first open
1434 : : // WARNING: snapshots must have been opened before this call
1435 [ + + ]: 33 : if (filemgr_update_file_status(handle->file, handle->file->status,
1436 : 33 : prev_filename)) {
1437 : : // Open the old file with read-only mode.
1438 : 10 : fconfig.options = FILEMGR_READONLY;
1439 : : filemgr_open_result result = filemgr_open(prev_filename,
1440 : : handle->fileops,
1441 : : &fconfig,
1442 : 10 : &handle->log_callback);
1443 [ + + ]: 10 : if (result.file) {
1444 : 1 : filemgr_remove_pending(result.file, handle->file);
1445 : : filemgr_close(result.file, 0, handle->filename,
1446 : 1 : &handle->log_callback);
1447 : : }
1448 : : }
1449 : : } else {
1450 : 7 : free(prev_filename);
1451 : : }
1452 : : }
1453 : :
1454 : 519 : status = btreeblk_end(handle->bhandle);
1455 [ - + ]: 519 : assert(status == FDB_RESULT_SUCCESS);
1456 : :
1457 : : // do not register read-only handles
1458 [ + + ][ + + ]: 519 : if (!(config->flags & FDB_OPEN_FLAG_RDONLY) &&
1459 : : config->compaction_mode == FDB_COMPACTION_AUTO) {
1460 : 34 : status = compactor_register_file(handle->file, (fdb_config *)config);
1461 : : }
1462 : :
1463 : 535 : return status;
1464 : : }
1465 : :
1466 : : LIBFDB_API
1467 : 127 : fdb_status fdb_set_log_callback(fdb_kvs_handle *handle,
1468 : : fdb_log_callback log_callback,
1469 : : void *ctx_data)
1470 : : {
1471 : 127 : handle->log_callback.callback = log_callback;
1472 : 127 : handle->log_callback.ctx_data = ctx_data;
1473 : 127 : return FDB_RESULT_SUCCESS;
1474 : : }
1475 : :
1476 : : LIBFDB_API
1477 : 3796459 : fdb_status fdb_doc_create(fdb_doc **doc, const void *key, size_t keylen,
1478 : : const void *meta, size_t metalen,
1479 : : const void *body, size_t bodylen)
1480 : : {
1481 [ + ][ + ][ + ]: 3796459 : if (doc == NULL || keylen > FDB_MAX_KEYLEN ||
[ + + ][ - ]
[ - ][ - ]
1482 : : metalen > FDB_MAX_METALEN || bodylen > FDB_MAX_BODYLEN) {
1483 : 0 : return FDB_RESULT_INVALID_ARGS;
1484 : : }
1485 : :
1486 : 3796459 : *doc = (fdb_doc*)calloc(1, sizeof(fdb_doc));
1487 [ - + ]: 3796459 : if (*doc == NULL) {
1488 : 0 : return FDB_RESULT_ALLOC_FAIL;
1489 : : }
1490 : :
1491 : 3796459 : (*doc)->seqnum = 0;
1492 : :
1493 [ + + ][ + + ]: 3796459 : if (key && keylen > 0) {
1494 : 3554184 : (*doc)->key = (void *)malloc(keylen);
1495 [ - + ]: 3554184 : if ((*doc)->key == NULL) {
1496 : 0 : return FDB_RESULT_ALLOC_FAIL;
1497 : : }
1498 : 3554184 : memcpy((*doc)->key, key, keylen);
1499 : 3554184 : (*doc)->keylen = keylen;
1500 : : } else {
1501 : 242275 : (*doc)->key = NULL;
1502 : 242275 : (*doc)->keylen = 0;
1503 : : }
1504 : :
1505 [ + + ][ + + ]: 3796459 : if (meta && metalen > 0) {
1506 : 2107695 : (*doc)->meta = (void *)malloc(metalen);
1507 [ - + ]: 2107695 : if ((*doc)->meta == NULL) {
1508 : 0 : return FDB_RESULT_ALLOC_FAIL;
1509 : : }
1510 : 2107695 : memcpy((*doc)->meta, meta, metalen);
1511 : 2107695 : (*doc)->metalen = metalen;
1512 : : } else {
1513 : 1688764 : (*doc)->meta = NULL;
1514 : 1688764 : (*doc)->metalen = 0;
1515 : : }
1516 : :
1517 [ + + ][ + + ]: 3796459 : if (body && bodylen > 0) {
1518 : 2134472 : (*doc)->body = (void *)malloc(bodylen);
1519 [ - + ]: 2134472 : if ((*doc)->body == NULL) {
1520 : 0 : return FDB_RESULT_ALLOC_FAIL;
1521 : : }
1522 : 2134472 : memcpy((*doc)->body, body, bodylen);
1523 : 2134472 : (*doc)->bodylen = bodylen;
1524 : : } else {
1525 : 1661987 : (*doc)->body = NULL;
1526 : 1661987 : (*doc)->bodylen = 0;
1527 : : }
1528 : :
1529 : 3796459 : (*doc)->size_ondisk = 0;
1530 : 3796459 : (*doc)->deleted = false;
1531 : :
1532 : 3796459 : return FDB_RESULT_SUCCESS;
1533 : : }
1534 : :
1535 : : LIBFDB_API
1536 : 300316 : fdb_status fdb_doc_update(fdb_doc **doc,
1537 : : const void *meta, size_t metalen,
1538 : : const void *body, size_t bodylen)
1539 : : {
1540 [ + - ][ + - ]: 300316 : if (doc == NULL ||
[ - + ]
1541 : : metalen > FDB_MAX_METALEN || bodylen > FDB_MAX_BODYLEN) {
1542 : 0 : return FDB_RESULT_INVALID_ARGS;
1543 : : }
1544 [ - + ]: 300316 : if (*doc == NULL) {
1545 : 0 : return FDB_RESULT_INVALID_ARGS;
1546 : : }
1547 : :
1548 [ + + ][ + - ]: 300316 : if (meta && metalen > 0) {
1549 : : // free previous metadata
1550 : 300166 : free((*doc)->meta);
1551 : : // allocate new metadata
1552 : 300166 : (*doc)->meta = (void *)malloc(metalen);
1553 [ - + ]: 300166 : if ((*doc)->meta == NULL) {
1554 : 0 : return FDB_RESULT_ALLOC_FAIL;
1555 : : }
1556 : 300166 : memcpy((*doc)->meta, meta, metalen);
1557 : 300166 : (*doc)->metalen = metalen;
1558 : : }
1559 : :
1560 [ + - ][ + - ]: 300316 : if (body && bodylen > 0) {
1561 : : // free previous body
1562 : 300316 : free((*doc)->body);
1563 : : // allocate new body
1564 : 300316 : (*doc)->body = (void *)malloc(bodylen);
1565 [ - + ]: 300316 : if ((*doc)->body == NULL) {
1566 : 0 : return FDB_RESULT_ALLOC_FAIL;
1567 : : }
1568 : 300316 : memcpy((*doc)->body, body, bodylen);
1569 : 300316 : (*doc)->bodylen = bodylen;
1570 : : }
1571 : :
1572 : 300316 : return FDB_RESULT_SUCCESS;
1573 : : }
1574 : :
1575 : : // doc MUST BE allocated by malloc
1576 : : LIBFDB_API
1577 : 3781549 : fdb_status fdb_doc_free(fdb_doc *doc)
1578 : : {
1579 [ + - ]: 3781549 : if (doc) {
1580 : 3783470 : free(doc->key);
1581 : 3783470 : free(doc->meta);
1582 : 3783470 : free(doc->body);
1583 : 3783470 : free(doc);
1584 : : }
1585 : 3781549 : return FDB_RESULT_SUCCESS;
1586 : : }
1587 : :
1588 : 6252147 : INLINE size_t _fdb_get_docsize(struct docio_length len)
1589 : : {
1590 : : size_t ret =
1591 : : len.keylen +
1592 : : len.metalen +
1593 : : len.bodylen_ondisk +
1594 : 6252147 : sizeof(struct docio_length);
1595 : :
1596 : 6252147 : ret += sizeof(timestamp_t);
1597 : :
1598 : 6252147 : ret += sizeof(fdb_seqnum_t);
1599 : :
1600 : : #ifdef __CRC32
1601 : 6252147 : ret += sizeof(uint32_t);
1602 : : #endif
1603 : :
1604 : 6252147 : return ret;
1605 : : }
1606 : :
1607 : 4271114 : INLINE uint64_t _fdb_wal_get_old_offset(void *voidhandle,
1608 : : struct wal_item *item)
1609 : : {
1610 : 4271114 : fdb_kvs_handle *handle = (fdb_kvs_handle *)voidhandle;
1611 : 4271114 : uint64_t old_offset = 0;
1612 : :
1613 : : hbtrie_find_offset(handle->trie,
1614 : : item->header->key,
1615 : : item->header->keylen,
1616 : 4271114 : (void*)&old_offset);
1617 : 4271114 : btreeblk_end(handle->bhandle);
1618 : 4271114 : old_offset = _endian_decode(old_offset);
1619 : :
1620 : 4271114 : return old_offset;
1621 : : }
1622 : :
1623 : 10 : INLINE fdb_status _fdb_wal_snapshot_func(void *handle, fdb_doc *doc,
1624 : : uint64_t offset) {
1625 : :
1626 : 10 : return snap_insert((struct snap_handle *)handle, doc, offset);
1627 : : }
1628 : :
1629 : 4269030 : INLINE fdb_status _fdb_wal_flush_func(void *voidhandle, struct wal_item *item)
1630 : : {
1631 : : hbtrie_result hr;
1632 : 4269030 : fdb_kvs_handle *handle = (fdb_kvs_handle *)voidhandle;
1633 : : fdb_seqnum_t _seqnum;
1634 : : fdb_kvs_id_t kv_id, *_kv_id;
1635 : 4269030 : fdb_status fs = FDB_RESULT_SUCCESS;
1636 : 4269030 : uint8_t *var_key = alca(uint8_t, handle->config.chunksize);
1637 : : uint64_t old_offset, _offset;
1638 : : int delta, r;
1639 : 4269030 : struct filemgr *file = handle->dhandle->file;
1640 : : struct kvs_stat stat;
1641 : :
1642 : 4269030 : memset(var_key, 0, handle->config.chunksize);
1643 [ + + ]: 4269030 : if (handle->kvs) {
1644 : 4268015 : _kv_id = (fdb_kvs_id_t*)item->header->key;
1645 : 4268015 : kv_id = _endian_decode(*_kv_id);
1646 : : } else {
1647 : 1015 : kv_id = 0;
1648 : : }
1649 : :
1650 [ + + ][ + - ]: 4269030 : if (item->action == WAL_ACT_INSERT ||
1651 : : item->action == WAL_ACT_LOGICAL_REMOVE) {
1652 : 4269030 : _offset = _endian_encode(item->offset);
1653 : :
1654 : 4269030 : r = _kvs_stat_get(file, kv_id, &stat);
1655 [ + + ]: 4269031 : if (r != 0) {
1656 : : // KV store corresponding to kv_id is already removed
1657 : : // skip this item
1658 : 2 : return FDB_RESULT_SUCCESS;
1659 : : }
1660 : 4269029 : handle->bhandle->nlivenodes = stat.nlivenodes;
1661 : :
1662 : : hr = hbtrie_insert(handle->trie,
1663 : : item->header->key,
1664 : : item->header->keylen,
1665 : : (void *)&_offset,
1666 : 4269029 : (void *)&old_offset);
1667 : :
1668 : 4269030 : fs = btreeblk_end(handle->bhandle);
1669 [ + + ]: 4269027 : if (fs != FDB_RESULT_SUCCESS) {
1670 : 1 : return fs;
1671 : : }
1672 : 4269026 : old_offset = _endian_decode(old_offset);
1673 : :
1674 [ + - ]: 4269026 : if (handle->config.seqtree_opt == FDB_SEQTREE_USE) {
1675 : 4269026 : _seqnum = _endian_encode(item->seqnum);
1676 [ + + ]: 4269026 : if (handle->kvs) {
1677 : : // multi KV instance mode .. HB+trie
1678 : : int size_id, size_seq;
1679 : : uint8_t *kvid_seqnum;
1680 : : uint64_t old_offset_local;
1681 : :
1682 : 4268011 : size_id = sizeof(fdb_kvs_id_t);
1683 : 4268011 : size_seq = sizeof(fdb_seqnum_t);
1684 : 4268011 : kvid_seqnum = alca(uint8_t, size_id + size_seq);
1685 : 4268011 : memcpy(kvid_seqnum, item->header->key, size_id);
1686 : 4268011 : memcpy(kvid_seqnum + size_id, &_seqnum, size_seq);
1687 : : hbtrie_insert(handle->seqtrie, kvid_seqnum, size_id + size_seq,
1688 : 4268011 : (void *)&_offset, (void *)&old_offset_local);
1689 : : } else {
1690 : : btree_insert(handle->seqtree, (void *)&_seqnum,
1691 : 1015 : (void *)&_offset);
1692 : : }
1693 : 4269027 : fs = btreeblk_end(handle->bhandle);
1694 [ - + ]: 4269027 : if (fs != FDB_RESULT_SUCCESS) {
1695 : 0 : return fs;
1696 : : }
1697 : : }
1698 : :
1699 : 4269027 : delta = (int)handle->bhandle->nlivenodes - (int)stat.nlivenodes;
1700 : 4269027 : _kvs_stat_update_attr(file, kv_id, KVS_STAT_NLIVENODES, delta);
1701 : :
1702 [ + + ]: 4269029 : if (hr == HBTRIE_RESULT_SUCCESS) {
1703 [ + + ]: 3768561 : if (item->action == WAL_ACT_INSERT) {
1704 : 3768453 : _kvs_stat_update_attr(file, kv_id, KVS_STAT_NDOCS, 1);
1705 : : }
1706 : : _kvs_stat_update_attr(file, kv_id, KVS_STAT_DATASIZE,
1707 : 3768559 : item->doc_size);
1708 : : } else { // update or logical delete
1709 : : struct docio_length len;
1710 : : // This block is already cached when we call HBTRIE_INSERT.
1711 : : // No additional block access.
1712 : 500468 : len = docio_read_doc_length(handle->dhandle, old_offset);
1713 : :
1714 [ + - ]: 500468 : if (!(len.flag & DOCIO_DELETED)) {
1715 [ + + ]: 500468 : if (item->action == WAL_ACT_LOGICAL_REMOVE) {
1716 : 503 : _kvs_stat_update_attr(file, kv_id, KVS_STAT_NDOCS, -1);
1717 : : }
1718 : : } else {
1719 [ # # ]: 0 : if (item->action == WAL_ACT_INSERT) {
1720 : 0 : _kvs_stat_update_attr(file, kv_id, KVS_STAT_NDOCS, 1);
1721 : : }
1722 : : }
1723 : :
1724 : 500468 : delta = (int)item->doc_size - (int)_fdb_get_docsize(len);
1725 : 500468 : _kvs_stat_update_attr(file, kv_id, KVS_STAT_DATASIZE, delta);
1726 : 4269028 : }
1727 : : } else {
1728 : : // Immediate remove
1729 : : hr = hbtrie_remove(handle->trie, item->header->key,
1730 : 0 : item->header->keylen);
1731 : 0 : fs = btreeblk_end(handle->bhandle);
1732 [ # # ]: 0 : if (fs != FDB_RESULT_SUCCESS) {
1733 : 0 : return fs;
1734 : : }
1735 : :
1736 [ # # ]: 0 : if (handle->config.seqtree_opt == FDB_SEQTREE_USE) {
1737 : 0 : _seqnum = _endian_encode(item->seqnum);
1738 : 0 : btree_remove(handle->seqtree, (void*)&_seqnum);
1739 : 0 : fs = btreeblk_end(handle->bhandle);
1740 [ # # ]: 0 : if (fs != FDB_RESULT_SUCCESS) {
1741 : 0 : return fs;
1742 : : }
1743 : : }
1744 : :
1745 [ # # ]: 0 : if (hr == HBTRIE_RESULT_SUCCESS) {
1746 : 0 : _kvs_stat_update_attr(file, kv_id, KVS_STAT_NDOCS, -1);
1747 : 0 : delta = -(int)item->doc_size;
1748 : 0 : _kvs_stat_update_attr(file, kv_id, KVS_STAT_DATASIZE, delta);
1749 : : }
1750 : : }
1751 : 4269031 : return FDB_RESULT_SUCCESS;
1752 : : }
1753 : :
1754 : 3534980 : void fdb_sync_db_header(fdb_kvs_handle *handle)
1755 : : {
1756 : 3534980 : uint64_t cur_revnum = filemgr_get_header_revnum(handle->file);
1757 [ + + ]: 3535210 : if (handle->cur_header_revnum != cur_revnum) {
1758 : 2468 : void *header_buf = NULL;
1759 : : size_t header_len;
1760 : :
1761 : 2468 : handle->last_hdr_bid = filemgr_get_header_bid(handle->file);
1762 : 2468 : header_buf = filemgr_get_header(handle->file, NULL, &header_len);
1763 [ + - ]: 2468 : if (header_len > 0) {
1764 : : uint64_t header_flags, dummy64;
1765 : : bid_t idtree_root;
1766 : : bid_t new_seq_root;
1767 : : char *compacted_filename;
1768 : 2468 : char *prev_filename = NULL;
1769 : :
1770 : : fdb_fetch_header(header_buf, &idtree_root,
1771 : : &new_seq_root,
1772 : : &dummy64, &dummy64,
1773 : : &dummy64, &handle->last_wal_flush_hdr_bid,
1774 : : &handle->kv_info_offset, &header_flags,
1775 : 2468 : &compacted_filename, &prev_filename);
1776 : :
1777 [ + + ]: 2468 : if (handle->dirty_updates) {
1778 : : // discard all cached writable b+tree nodes
1779 : : // to avoid data inconsistency with other writers
1780 : 168 : btreeblk_discard_blocks(handle->bhandle);
1781 : : }
1782 : :
1783 : 2468 : handle->trie->root_bid = idtree_root;
1784 : :
1785 [ + - ]: 2468 : if (handle->config.seqtree_opt == FDB_SEQTREE_USE) {
1786 [ + + ]: 2468 : if (new_seq_root != handle->seqtree->root_bid) {
1787 [ + - ]: 199 : if (handle->config.multi_kv_instances) {
1788 : 199 : handle->seqtrie->root_bid = new_seq_root;
1789 : : } else {
1790 : : btree_init_from_bid(handle->seqtree,
1791 : : handle->seqtree->blk_handle,
1792 : : handle->seqtree->blk_ops,
1793 : : handle->seqtree->kv_ops,
1794 : : handle->seqtree->blksize,
1795 : 0 : new_seq_root);
1796 : : }
1797 : : }
1798 : : }
1799 : :
1800 [ + + ]: 2468 : if (prev_filename) {
1801 : 790 : free(prev_filename);
1802 : : }
1803 : :
1804 : 2468 : handle->cur_header_revnum = cur_revnum;
1805 : 2468 : handle->dirty_updates = 0;
1806 : : }
1807 [ + - ]: 2468 : if (header_buf) {
1808 : 2468 : free(header_buf);
1809 : : }
1810 : : }
1811 : 3535210 : }
1812 : :
1813 : 3534373 : fdb_status fdb_check_file_reopen(fdb_kvs_handle *handle)
1814 : : {
1815 : 3534373 : fdb_status fs = FDB_RESULT_SUCCESS;
1816 : : // check whether the compaction is done
1817 [ + + ]: 3534373 : if (filemgr_get_file_status(handle->file) == FILE_REMOVED_PENDING) {
1818 : : uint64_t ndocs, datasize, nlivenodes, last_wal_flush_hdr_bid;
1819 : : uint64_t kv_info_offset, header_flags;
1820 : : size_t header_len;
1821 : : char *new_filename;
1822 : 24 : uint8_t *buf = alca(uint8_t, handle->config.blocksize);
1823 : : bid_t trie_root_bid, seq_root_bid;
1824 : 24 : fdb_config config = handle->config;
1825 : :
1826 [ + + ]: 24 : if (handle->new_file) {
1827 : : // compacted new file is already opened
1828 : : // close the old file
1829 : : filemgr_close(handle->file, handle->config.cleanup_cache_onclose,
1830 : 14 : handle->filename, &handle->log_callback);
1831 : : // close old docio handle
1832 : 14 : docio_free(handle->dhandle);
1833 : 14 : free(handle->dhandle);
1834 : : // close btree block handle
1835 : 14 : fs = btreeblk_end(handle->bhandle);
1836 : 14 : btreeblk_free(handle->bhandle);
1837 : :
1838 : : // switch to new file & docio handle
1839 : 14 : handle->file = handle->new_file;
1840 : 14 : handle->new_file = NULL;
1841 : 14 : handle->dhandle = handle->new_dhandle;
1842 : 14 : handle->new_dhandle = NULL;
1843 : :
1844 : 14 : btreeblk_init(handle->bhandle, handle->file, handle->config.blocksize);
1845 : :
1846 : : // read new file's header
1847 : 14 : filemgr_get_header(handle->file, buf, &header_len);
1848 : : fdb_fetch_header(buf,
1849 : : &trie_root_bid, &seq_root_bid,
1850 : : &ndocs, &nlivenodes, &datasize, &last_wal_flush_hdr_bid,
1851 : : &kv_info_offset, &header_flags,
1852 : 14 : &new_filename, NULL);
1853 : :
1854 : : // reset trie (id-tree)
1855 : 14 : handle->trie->root_bid = trie_root_bid;
1856 : 14 : handle->trie->btreeblk_handle = handle->bhandle;
1857 : 14 : handle->trie->doc_handle = handle->dhandle;
1858 : :
1859 : : // reset seq tree
1860 [ + - ]: 14 : if (handle->config.seqtree_opt == FDB_SEQTREE_USE) {
1861 [ + - ]: 14 : if (handle->config.multi_kv_instances) {
1862 : : // multi KV instance mode
1863 : 14 : handle->seqtrie->root_bid = seq_root_bid;
1864 : 14 : handle->seqtrie->btreeblk_handle = handle->bhandle;
1865 : 14 : handle->seqtrie->doc_handle = handle->dhandle;
1866 : : } else {
1867 [ # # ]: 0 : if (seq_root_bid != BLK_NOT_FOUND) {
1868 : : btree_init_from_bid(handle->seqtree, (void *)handle->bhandle,
1869 : : handle->seqtree->blk_ops,
1870 : : handle->seqtree->kv_ops,
1871 : : handle->config.blocksize,
1872 : 0 : seq_root_bid);
1873 : : } else {
1874 : : btree_init(handle->seqtree, (void *)handle->bhandle,
1875 : : handle->seqtree->blk_ops,
1876 : : handle->seqtree->kv_ops,
1877 : : handle->config.blocksize,
1878 : : sizeof(fdb_seqnum_t),
1879 : 0 : OFFSET_SIZE, 0x0, NULL);
1880 : : }
1881 : : }
1882 : : }
1883 : :
1884 : : // the others
1885 : 14 : handle->cur_header_revnum = filemgr_get_header_revnum(handle->file);
1886 : 14 : handle->dirty_updates = 0;
1887 : :
1888 : : // note that we don't need to call 'compactor_deregister_file'
1889 : : // because the old file is already removed when compaction is complete.
1890 [ + - ][ + + ]: 14 : if (!(config.flags & FDB_OPEN_FLAG_RDONLY) &&
1891 : : config.compaction_mode == FDB_COMPACTION_AUTO) {
1892 : 7 : fs = compactor_register_file(handle->file, &config);
1893 : : }
1894 : :
1895 : : } else {
1896 : : // close the current file and newly open the new file
1897 [ - + ]: 10 : if (handle->config.compaction_mode == FDB_COMPACTION_AUTO) {
1898 : : // compaction daemon mode .. just close and then open
1899 : : char filename[FDB_MAX_FILENAME_LEN];
1900 : 0 : strcpy(filename, handle->filename);
1901 : 0 : _fdb_close(handle);
1902 : 0 : _fdb_open(handle, filename, &config);
1903 : :
1904 : : } else {
1905 : 10 : filemgr_get_header(handle->file, buf, &header_len);
1906 : : fdb_fetch_header(buf,
1907 : : &trie_root_bid, &seq_root_bid,
1908 : : &ndocs, &nlivenodes, &datasize, &last_wal_flush_hdr_bid,
1909 : : &kv_info_offset, &header_flags,
1910 : 10 : &new_filename, NULL);
1911 : 10 : _fdb_close(handle);
1912 : 10 : _fdb_open(handle, new_filename, &config);
1913 : : }
1914 : : }
1915 : : }
1916 : 3535479 : return fs;
1917 : : }
1918 : :
1919 : 3534862 : void fdb_link_new_file(fdb_kvs_handle *handle)
1920 : : {
1921 : : // check whether this file is being compacted
1922 [ + + + + ]: 6656970 : if (!handle->new_file &&
[ + + ]
1923 : 3121986 : filemgr_get_file_status(handle->file) == FILE_COMPACT_OLD) {
1924 [ - + ]: 27 : assert(handle->file->new_file);
1925 : :
1926 : : // open new file and new dhandle
1927 : : filemgr_open_result result = filemgr_open(handle->file->new_file->filename,
1928 : : handle->fileops, handle->file->config,
1929 : 27 : &handle->log_callback);
1930 : 27 : handle->new_file = result.file;
1931 : : handle->new_dhandle = (struct docio_handle *)
1932 : 27 : calloc(1, sizeof(struct docio_handle));
1933 : 27 : handle->new_dhandle->log_callback = &handle->log_callback;
1934 : : docio_init(handle->new_dhandle,
1935 : : handle->new_file,
1936 : 27 : handle->config.compress_document_body);
1937 : : }
1938 : 3534984 : }
1939 : :
1940 : : LIBFDB_API
1941 : 1417092 : fdb_status fdb_get(fdb_kvs_handle *handle, fdb_doc *doc)
1942 : : {
1943 : : uint64_t offset, _offset;
1944 : : struct docio_object _doc;
1945 : 1417092 : struct filemgr *wal_file = NULL;
1946 : : struct docio_handle *dhandle;
1947 : : fdb_status wr;
1948 : 1417092 : hbtrie_result hr = HBTRIE_RESULT_FAIL;
1949 : : fdb_txn *txn;
1950 : 1417092 : fdb_doc doc_kv = *doc;
1951 : :
1952 [ + + ][ + ]: 1417092 : if (doc->key == NULL || doc->keylen == 0 || doc->keylen > FDB_MAX_KEYLEN ||
[ + ][ + ][ - ]
[ - ][ - ]
1953 : : doc->keylen > handle->config.blocksize - 256) {
1954 : 0 : return FDB_RESULT_INVALID_ARGS;
1955 : : }
1956 : :
1957 [ + + ]: 1417092 : if (handle->kvs) {
1958 : : // multi KV instance mode
1959 : : fdb_kvs_id_t id;
1960 : 1415322 : doc_kv.keylen = doc->keylen + sizeof(fdb_kvs_id_t);
1961 : 1415322 : doc_kv.key = alca(uint8_t, doc_kv.keylen);
1962 : 1415322 : id = _endian_encode(handle->kvs->id);
1963 : 1415322 : memcpy(doc_kv.key, &id, sizeof(id));
1964 : 1415322 : memcpy((uint8_t*)doc_kv.key + sizeof(id), doc->key, doc->keylen);
1965 : : }
1966 : :
1967 [ + + ]: 1417092 : if (!handle->shandle) {
1968 : 1057283 : fdb_check_file_reopen(handle);
1969 : 1057459 : fdb_link_new_file(handle);
1970 : 1057449 : fdb_sync_db_header(handle);
1971 : :
1972 [ + + ]: 1057600 : if (handle->new_file == NULL) {
1973 : 695336 : wal_file = handle->file;
1974 : : }else{
1975 : 362264 : wal_file = handle->new_file;
1976 : : }
1977 : 1057600 : dhandle = handle->dhandle;
1978 : :
1979 : 1057600 : txn = handle->fhandle->root->txn;
1980 [ + + ]: 1057600 : if (!txn) {
1981 : 1056910 : txn = &wal_file->global_txn;
1982 : : }
1983 [ + + ]: 1057600 : if (handle->kvs) {
1984 : 1056600 : wr = wal_find(txn, wal_file, &doc_kv, &offset);
1985 : : } else {
1986 : 1000 : wr = wal_find(txn, wal_file, doc, &offset);
1987 : : }
1988 : : } else {
1989 [ + - ]: 359809 : if (handle->kvs) {
1990 : 359809 : wr = snap_find(handle->shandle, &doc_kv, &offset);
1991 : : } else {
1992 : 0 : wr = snap_find(handle->shandle, doc, &offset);
1993 : : }
1994 : 359644 : dhandle = handle->dhandle;
1995 : : }
1996 : :
1997 [ + + ]: 1417247 : if (wr == FDB_RESULT_KEY_NOT_FOUND) {
1998 : 1170522 : bool locked = false;
1999 : : bid_t dirty_idtree_root, dirty_seqtree_root;
2000 : :
2001 [ + + ]: 1170522 : if (handle->dirty_updates) {
2002 : : // grab lock for writer if there are dirty updates
2003 : 4320 : filemgr_mutex_lock(handle->file);
2004 : 4320 : locked = true;
2005 : :
2006 : : // get dirty root nodes
2007 : 4320 : filemgr_get_dirty_root(handle->file, &dirty_idtree_root, &dirty_seqtree_root);
2008 [ + + ]: 4320 : if (dirty_idtree_root != BLK_NOT_FOUND) {
2009 : 222 : handle->trie->root_bid = dirty_idtree_root;
2010 : : }
2011 [ + - ]: 4320 : if (handle->config.seqtree_opt == FDB_SEQTREE_USE) {
2012 [ + + ]: 4320 : if (dirty_seqtree_root != BLK_NOT_FOUND) {
2013 : 222 : handle->seqtree->root_bid = dirty_seqtree_root;
2014 : : }
2015 : : }
2016 : 4320 : btreeblk_discard_blocks(handle->bhandle);
2017 : : }
2018 : :
2019 [ + + ]: 1170460 : if (handle->kvs) {
2020 : : hr = hbtrie_find(handle->trie, doc_kv.key, doc_kv.keylen,
2021 : 1169460 : (void *)&offset);
2022 : : } else {
2023 : : hr = hbtrie_find(handle->trie, doc->key, doc->keylen,
2024 : 1000 : (void *)&offset);
2025 : : }
2026 : 1169967 : btreeblk_end(handle->bhandle);
2027 : 1169961 : offset = _endian_decode(offset);
2028 : :
2029 [ + + ]: 1169961 : if (locked) {
2030 : : // grab lock for writer if there are dirty updates
2031 : 4320 : filemgr_mutex_unlock(handle->file);
2032 : : }
2033 : : } else {
2034 [ + + ][ + + ]: 246725 : if (wal_file == handle->new_file && !handle->shandle) {
2035 : 226861 : dhandle = handle->new_dhandle;
2036 : : }
2037 : : }
2038 : :
2039 [ + + ][ + + ]: 1416596 : if (wr == FDB_RESULT_SUCCESS || hr != HBTRIE_RESULT_FAIL) {
2040 [ + + ]: 1416162 : if (handle->kvs) {
2041 : 1415162 : _doc.key = doc_kv.key;
2042 : 1415162 : _doc.length.keylen = doc_kv.keylen;
2043 : : } else {
2044 : 1000 : _doc.key = doc->key;
2045 : 1000 : _doc.length.keylen = doc->keylen;
2046 : : }
2047 : 1416162 : _doc.meta = doc->meta;
2048 : 1416162 : _doc.body = doc->body;
2049 : :
2050 [ + + ][ - + ]: 1416162 : if (wr == FDB_RESULT_SUCCESS && doc->deleted) {
2051 : 0 : return FDB_RESULT_KEY_NOT_FOUND;
2052 : : }
2053 : :
2054 : 1416162 : _offset = docio_read_doc(dhandle, offset, &_doc);
2055 [ + + ]: 1416452 : if (_offset == offset) {
2056 : 1 : return FDB_RESULT_KEY_NOT_FOUND;
2057 : : }
2058 : :
2059 : 1416451 : doc->seqnum = _doc.seqnum;
2060 : 1416451 : doc->metalen = _doc.length.metalen;
2061 : 1416451 : doc->bodylen = _doc.length.bodylen;
2062 : 1416451 : doc->meta = _doc.meta;
2063 : 1416451 : doc->body = _doc.body;
2064 : 1416451 : doc->deleted = _doc.length.flag & DOCIO_DELETED;
2065 : 1416451 : doc->size_ondisk = _fdb_get_docsize(_doc.length);
2066 : 1415957 : doc->offset = offset;
2067 : :
2068 [ + + ][ + - ]: 1415957 : if (_doc.length.keylen != doc_kv.keylen ||
2069 : : _doc.length.flag & DOCIO_DELETED) {
2070 : 4 : return FDB_RESULT_KEY_NOT_FOUND;
2071 : : }
2072 : :
2073 : 1415953 : return FDB_RESULT_SUCCESS;
2074 : : }
2075 : :
2076 : 1416392 : return FDB_RESULT_KEY_NOT_FOUND;
2077 : : }
2078 : :
2079 : : // search document metadata using key
2080 : : LIBFDB_API
2081 : 825 : fdb_status fdb_get_metaonly(fdb_kvs_handle *handle, fdb_doc *doc)
2082 : : {
2083 : : uint64_t offset;
2084 : : struct docio_object _doc;
2085 : : struct docio_handle *dhandle;
2086 : 825 : struct filemgr *wal_file = NULL;
2087 : : fdb_status wr;
2088 : 825 : hbtrie_result hr = HBTRIE_RESULT_FAIL;
2089 : : fdb_txn *txn;
2090 : 825 : fdb_doc doc_kv = *doc;
2091 : :
2092 [ + - ][ + - ]: 825 : if (handle == NULL || doc == NULL || doc->key == NULL ||
[ + - ][ + - ]
[ + - ][ - + ]
2093 : : doc->keylen == 0 || doc->keylen > FDB_MAX_KEYLEN ||
2094 : : doc->keylen > handle->config.blocksize - 256) {
2095 : 0 : return FDB_RESULT_INVALID_ARGS;
2096 : : }
2097 : :
2098 : :
2099 [ + - ]: 825 : if (handle->kvs) {
2100 : : // multi KV instance mode
2101 : : fdb_kvs_id_t id;
2102 : 825 : doc_kv.keylen = doc->keylen + sizeof(fdb_kvs_id_t);
2103 : 825 : doc_kv.key = alca(uint8_t, doc_kv.keylen);
2104 : 825 : id = _endian_encode(handle->kvs->id);
2105 : 825 : memcpy(doc_kv.key, &id, sizeof(id));
2106 : 825 : memcpy((uint8_t*)doc_kv.key + sizeof(id), doc->key, doc->keylen);
2107 : : }
2108 : :
2109 [ + + ]: 825 : if (!handle->shandle) {
2110 : 823 : fdb_check_file_reopen(handle);
2111 : 823 : fdb_link_new_file(handle);
2112 : 823 : fdb_sync_db_header(handle);
2113 : :
2114 [ + - ]: 823 : if (handle->new_file == NULL) {
2115 : 823 : wal_file = handle->file;
2116 : : }else{
2117 : 0 : wal_file = handle->new_file;
2118 : : }
2119 : 823 : dhandle = handle->dhandle;
2120 : :
2121 : 823 : txn = handle->fhandle->root->txn;
2122 [ + - ]: 823 : if (!txn) {
2123 : 823 : txn = &wal_file->global_txn;
2124 : : }
2125 [ + - ]: 823 : if (handle->kvs) {
2126 : 823 : wr = wal_find(txn, wal_file, &doc_kv, &offset);
2127 : : } else {
2128 : 0 : wr = wal_find(txn, wal_file, doc, &offset);
2129 : : }
2130 : : } else {
2131 [ + - ]: 2 : if (handle->kvs) {
2132 : 2 : wr = snap_find(handle->shandle, &doc_kv, &offset);
2133 : : } else {
2134 : 0 : wr = snap_find(handle->shandle, doc, &offset);
2135 : : }
2136 : 2 : dhandle = handle->dhandle;
2137 : : }
2138 : :
2139 [ + + ]: 825 : if (wr == FDB_RESULT_KEY_NOT_FOUND) {
2140 : 626 : bool locked = false;
2141 : : bid_t dirty_idtree_root, dirty_seqtree_root;
2142 : :
2143 [ + + ]: 626 : if (handle->dirty_updates) {
2144 : : // grab lock for writer if there are dirty updates
2145 : 204 : filemgr_mutex_lock(handle->file);
2146 : 204 : locked = true;
2147 : :
2148 : : // get dirty root nodes
2149 : : filemgr_get_dirty_root(handle->file, &dirty_idtree_root,
2150 : 204 : &dirty_seqtree_root);
2151 [ + - ]: 204 : if (dirty_idtree_root != BLK_NOT_FOUND) {
2152 : 204 : handle->trie->root_bid = dirty_idtree_root;
2153 : : }
2154 [ + - ]: 204 : if (handle->config.seqtree_opt == FDB_SEQTREE_USE) {
2155 [ + - ]: 204 : if (dirty_seqtree_root != BLK_NOT_FOUND) {
2156 : 204 : handle->seqtree->root_bid = dirty_seqtree_root;
2157 : : }
2158 : : }
2159 : 204 : btreeblk_discard_blocks(handle->bhandle);
2160 : : }
2161 : :
2162 [ + - ]: 626 : if (handle->kvs) {
2163 : : hr = hbtrie_find(handle->trie, doc_kv.key, doc_kv.keylen,
2164 : 626 : (void *)&offset);
2165 : : } else {
2166 : : hr = hbtrie_find(handle->trie, doc->key, doc->keylen,
2167 : 0 : (void *)&offset);
2168 : : }
2169 : 626 : btreeblk_end(handle->bhandle);
2170 : 626 : offset = _endian_decode(offset);
2171 : :
2172 [ + + ]: 626 : if (locked) {
2173 : 204 : filemgr_mutex_unlock(handle->file);
2174 : : }
2175 : : } else {
2176 [ + + ][ - + ]: 199 : if (wal_file == handle->new_file && !handle->shandle) {
2177 : 0 : dhandle = handle->new_dhandle;
2178 : : }
2179 : : }
2180 : :
2181 [ + + ][ + + ]: 825 : if (wr == FDB_RESULT_SUCCESS || hr != HBTRIE_RESULT_FAIL) {
2182 [ + - ]: 823 : if (handle->kvs) {
2183 : 823 : _doc.key = doc_kv.key;
2184 : 823 : _doc.length.keylen = doc_kv.keylen;
2185 : : } else {
2186 : 0 : _doc.key = doc->key;
2187 : 0 : _doc.length.keylen = doc->keylen;
2188 : : }
2189 : 823 : _doc.meta = doc->meta;
2190 : 823 : _doc.body = doc->body;
2191 : :
2192 : 823 : uint64_t body_offset = docio_read_doc_key_meta(dhandle, offset, &_doc);
2193 [ - + ]: 823 : if (body_offset == offset){
2194 : 0 : return FDB_RESULT_KEY_NOT_FOUND;
2195 : : }
2196 : :
2197 : 823 : doc->seqnum = _doc.seqnum;
2198 : 823 : doc->metalen = _doc.length.metalen;
2199 : 823 : doc->bodylen = _doc.length.bodylen;
2200 : 823 : doc->meta = _doc.meta;
2201 : 823 : doc->body = _doc.body;
2202 : 823 : doc->deleted = _doc.length.flag & DOCIO_DELETED;
2203 : 823 : doc->size_ondisk = _fdb_get_docsize(_doc.length);
2204 : 823 : doc->offset = offset;
2205 : :
2206 [ - + ]: 823 : if (_doc.length.keylen != doc_kv.keylen) {
2207 : 0 : return FDB_RESULT_KEY_NOT_FOUND;
2208 : : }
2209 : :
2210 : 823 : return FDB_RESULT_SUCCESS;
2211 : : }
2212 : :
2213 : 825 : return FDB_RESULT_KEY_NOT_FOUND;
2214 : : }
2215 : :
2216 : : // search document using sequence number
2217 : : LIBFDB_API
2218 : 825 : fdb_status fdb_get_byseq(fdb_kvs_handle *handle, fdb_doc *doc)
2219 : : {
2220 : : uint64_t offset, _offset;
2221 : : struct docio_object _doc;
2222 : : struct docio_handle *dhandle;
2223 : 825 : struct filemgr *wal_file = NULL;
2224 : : fdb_status wr;
2225 : 825 : btree_result br = BTREE_RESULT_FAIL;
2226 : : fdb_seqnum_t _seqnum;
2227 : : fdb_txn *txn;
2228 : :
2229 [ - + ]: 825 : if (doc->seqnum == SEQNUM_NOT_USED) {
2230 : 0 : return FDB_RESULT_INVALID_ARGS;
2231 : : }
2232 : :
2233 : : // Sequence trees are a must for byseq operations
2234 [ + + ]: 825 : if (handle->config.seqtree_opt != FDB_SEQTREE_USE) {
2235 : 1 : return FDB_RESULT_INVALID_CONFIG;
2236 : : }
2237 : :
2238 [ + + ]: 824 : if (!handle->shandle) {
2239 : 823 : fdb_check_file_reopen(handle);
2240 : 823 : fdb_link_new_file(handle);
2241 : 823 : fdb_sync_db_header(handle);
2242 : :
2243 [ + - ]: 823 : if (handle->new_file == NULL) {
2244 : 823 : wal_file = handle->file;
2245 : : }else{
2246 : 0 : wal_file = handle->new_file;
2247 : : }
2248 : 823 : dhandle = handle->dhandle;
2249 : :
2250 : 823 : txn = handle->fhandle->root->txn;
2251 [ + - ]: 823 : if (!txn) {
2252 : 823 : txn = &wal_file->global_txn;
2253 : : }
2254 : : // prevent searching by key in WAL if 'doc' is not empty
2255 : 823 : doc->keylen = 0;
2256 [ + - ]: 823 : if (handle->kvs) {
2257 : 823 : wr = wal_find_kv_id(txn, wal_file, handle->kvs->id, doc, &offset);
2258 : : } else {
2259 : 0 : wr = wal_find(txn, wal_file, doc, &offset);
2260 : : }
2261 : : } else {
2262 : 1 : wr = snap_find(handle->shandle, doc, &offset);
2263 : 1 : dhandle = handle->dhandle;
2264 : : }
2265 : :
2266 [ + + ]: 824 : if (wr == FDB_RESULT_KEY_NOT_FOUND) {
2267 : 617 : bool locked = false;
2268 : : bid_t dirty_idtree_root, dirty_seqtree_root;
2269 : :
2270 [ + + ]: 617 : if (handle->dirty_updates) {
2271 : : // grab lock for writer if there are dirty updates
2272 : 204 : filemgr_mutex_lock(handle->file);
2273 : 204 : locked = true;
2274 : :
2275 : : // get dirty root nodes
2276 : 204 : filemgr_get_dirty_root(handle->file, &dirty_idtree_root, &dirty_seqtree_root);
2277 [ + - ]: 204 : if (dirty_idtree_root != BLK_NOT_FOUND) {
2278 : 204 : handle->trie->root_bid = dirty_idtree_root;
2279 : : }
2280 [ + - ]: 204 : if (handle->config.seqtree_opt == FDB_SEQTREE_USE) {
2281 [ + - ]: 204 : if (dirty_seqtree_root != BLK_NOT_FOUND) {
2282 : 204 : handle->seqtree->root_bid = dirty_seqtree_root;
2283 : : }
2284 : : }
2285 : 204 : btreeblk_discard_blocks(handle->bhandle);
2286 : : }
2287 : :
2288 : 617 : _seqnum = _endian_encode(doc->seqnum);
2289 [ + - ]: 617 : if (handle->kvs) {
2290 : : int size_id, size_seq;
2291 : : uint8_t *kv_seqnum;
2292 : : hbtrie_result hr;
2293 : : fdb_kvs_id_t _kv_id;
2294 : :
2295 : 617 : _kv_id = _endian_encode(handle->kvs->id);
2296 : 617 : size_id = sizeof(fdb_kvs_id_t);
2297 : 617 : size_seq = sizeof(fdb_seqnum_t);
2298 : 617 : kv_seqnum = alca(uint8_t, size_id + size_seq);
2299 : 617 : memcpy(kv_seqnum, &_kv_id, size_id);
2300 : 617 : memcpy(kv_seqnum + size_id, &_seqnum, size_seq);
2301 : : hr = hbtrie_find(handle->seqtrie, (void *)kv_seqnum,
2302 : 617 : size_id + size_seq, (void *)&offset);
2303 [ + + ]: 617 : br = (hr == HBTRIE_RESULT_SUCCESS)?(BTREE_RESULT_SUCCESS):(br);
2304 : : } else {
2305 : 0 : br = btree_find(handle->seqtree, (void *)&_seqnum, (void *)&offset);
2306 : : }
2307 : 617 : btreeblk_end(handle->bhandle);
2308 : 617 : offset = _endian_decode(offset);
2309 : :
2310 [ + + ]: 617 : if (locked) {
2311 : 204 : filemgr_mutex_unlock(handle->file);
2312 : : }
2313 : : } else {
2314 [ + + ][ - + ]: 207 : if (wal_file == handle->new_file && !handle->shandle) {
2315 : 0 : dhandle = handle->new_dhandle;
2316 : : }
2317 : : }
2318 : :
2319 [ + + ][ + + ]: 824 : if (wr == FDB_RESULT_SUCCESS || br != BTREE_RESULT_FAIL) {
2320 : 820 : _doc.key = doc->key;
2321 : 820 : _doc.meta = doc->meta;
2322 : 820 : _doc.body = doc->body;
2323 : :
2324 [ + + ][ - + ]: 820 : if (wr == FDB_RESULT_SUCCESS && doc->deleted) {
2325 : 0 : return FDB_RESULT_KEY_NOT_FOUND;
2326 : : }
2327 : :
2328 : 820 : _offset = docio_read_doc(dhandle, offset, &_doc);
2329 [ - + ]: 820 : if (_offset == offset) {
2330 : 0 : return FDB_RESULT_KEY_NOT_FOUND;
2331 : : }
2332 : :
2333 : 820 : doc->seqnum = _doc.seqnum;
2334 [ + - ]: 820 : if (handle->kvs) {
2335 : 820 : int size_id = sizeof(fdb_kvs_id_t);
2336 : 820 : doc->keylen = _doc.length.keylen - size_id;
2337 : 820 : doc->key = _doc.key;
2338 : 820 : memmove(doc->key, (uint8_t*)doc->key + size_id, doc->keylen);
2339 : : } else {
2340 : 0 : doc->keylen = _doc.length.keylen;
2341 : 0 : doc->key = _doc.key;
2342 : : }
2343 : 820 : doc->metalen = _doc.length.metalen;
2344 : 820 : doc->bodylen = _doc.length.bodylen;
2345 : 820 : doc->meta = _doc.meta;
2346 : 820 : doc->body = _doc.body;
2347 : 820 : doc->deleted = _doc.length.flag & DOCIO_DELETED;
2348 : 820 : doc->size_ondisk = _fdb_get_docsize(_doc.length);
2349 : 820 : doc->offset = offset;
2350 : :
2351 [ - + ]: 820 : if (_doc.length.flag & DOCIO_DELETED) {
2352 : 0 : return FDB_RESULT_KEY_NOT_FOUND;
2353 : : }
2354 : :
2355 [ - + ]: 820 : assert(doc->seqnum == _doc.seqnum);
2356 : :
2357 : 820 : return FDB_RESULT_SUCCESS;
2358 : : }
2359 : :
2360 : 825 : return FDB_RESULT_KEY_NOT_FOUND;
2361 : : }
2362 : :
2363 : : // search document metadata using sequence number
2364 : : LIBFDB_API
2365 : 901 : fdb_status fdb_get_metaonly_byseq(fdb_kvs_handle *handle, fdb_doc *doc)
2366 : : {
2367 : : uint64_t offset;
2368 : : struct docio_object _doc;
2369 : : struct docio_handle *dhandle;
2370 : 901 : struct filemgr *wal_file = NULL;
2371 : : fdb_status wr;
2372 : 901 : btree_result br = BTREE_RESULT_FAIL;
2373 : : fdb_seqnum_t _seqnum;
2374 : 901 : fdb_txn *txn = handle->fhandle->root->txn;
2375 : :
2376 [ - + ]: 901 : if (doc->seqnum == SEQNUM_NOT_USED) {
2377 : 0 : return FDB_RESULT_INVALID_ARGS;
2378 : : }
2379 : :
2380 : : // Sequence trees are a must for byseq operations
2381 [ + + ]: 901 : if (handle->config.seqtree_opt != FDB_SEQTREE_USE) {
2382 : 1 : return FDB_RESULT_INVALID_CONFIG;
2383 : : }
2384 : :
2385 [ + - ]: 900 : if (!handle->shandle) {
2386 : 900 : fdb_check_file_reopen(handle);
2387 : 900 : fdb_link_new_file(handle);
2388 : 900 : fdb_sync_db_header(handle);
2389 : :
2390 [ + - ]: 900 : if (handle->new_file == NULL) {
2391 : 900 : wal_file = handle->file;
2392 : : } else {
2393 : 0 : wal_file = handle->new_file;
2394 : : }
2395 : 900 : dhandle = handle->dhandle;
2396 : :
2397 [ + - ]: 900 : if (!txn) {
2398 : 900 : txn = &wal_file->global_txn;
2399 : : }
2400 : : // prevent searching by key in WAL if 'doc' is not empty
2401 : 900 : doc->keylen = 0;
2402 [ + - ]: 900 : if (handle->kvs) {
2403 : 900 : wr = wal_find_kv_id(txn, wal_file, handle->kvs->id, doc, &offset);
2404 : : } else {
2405 : 0 : wr = wal_find(txn, wal_file, doc, &offset);
2406 : : }
2407 : : } else {
2408 : 0 : wr = snap_find(handle->shandle, doc, &offset);
2409 : 0 : dhandle = handle->dhandle;
2410 : : }
2411 : :
2412 [ + + ]: 900 : if (wr == FDB_RESULT_KEY_NOT_FOUND) {
2413 : 604 : bool locked = false;
2414 : : bid_t dirty_idtree_root, dirty_seqtree_root;
2415 : :
2416 [ + + ]: 604 : if (handle->dirty_updates) {
2417 : : // grab lock for writer if there are dirty updates
2418 : 204 : filemgr_mutex_lock(handle->file);
2419 : 204 : locked = true;
2420 : :
2421 : : // get dirty root nodes
2422 : 204 : filemgr_get_dirty_root(handle->file, &dirty_idtree_root, &dirty_seqtree_root);
2423 [ + - ]: 204 : if (dirty_idtree_root != BLK_NOT_FOUND) {
2424 : 204 : handle->trie->root_bid = dirty_idtree_root;
2425 : : }
2426 [ + - ]: 204 : if (handle->config.seqtree_opt == FDB_SEQTREE_USE) {
2427 [ + - ]: 204 : if (dirty_seqtree_root != BLK_NOT_FOUND) {
2428 : 204 : handle->seqtree->root_bid = dirty_seqtree_root;
2429 : : }
2430 : : }
2431 : 204 : btreeblk_discard_blocks(handle->bhandle);
2432 : : }
2433 : :
2434 : 604 : _seqnum = _endian_encode(doc->seqnum);
2435 [ + - ]: 604 : if (handle->kvs) {
2436 : : int size_id, size_seq;
2437 : : uint8_t *kv_seqnum;
2438 : : hbtrie_result hr;
2439 : : fdb_kvs_id_t _kv_id;
2440 : :
2441 : 604 : _kv_id = _endian_encode(handle->kvs->id);
2442 : 604 : size_id = sizeof(fdb_kvs_id_t);
2443 : 604 : size_seq = sizeof(fdb_seqnum_t);
2444 : 604 : kv_seqnum = alca(uint8_t, size_id + size_seq);
2445 : 604 : memcpy(kv_seqnum, &_kv_id, size_id);
2446 : 604 : memcpy(kv_seqnum + size_id, &_seqnum, size_seq);
2447 : : hr = hbtrie_find(handle->seqtrie, (void *)kv_seqnum,
2448 : 604 : size_id + size_seq, (void *)&offset);
2449 [ - + ]: 604 : br = (hr == HBTRIE_RESULT_SUCCESS)?(BTREE_RESULT_SUCCESS):(br);
2450 : : } else {
2451 : 0 : br = btree_find(handle->seqtree, (void *)&_seqnum, (void *)&offset);
2452 : : }
2453 : 604 : btreeblk_end(handle->bhandle);
2454 : 604 : offset = _endian_decode(offset);
2455 : :
2456 [ + + ]: 604 : if (locked) {
2457 : 204 : filemgr_mutex_unlock(handle->file);
2458 : : }
2459 : : } else {
2460 [ - + ][ # # ]: 296 : if (wal_file == handle->new_file && !handle->shandle) {
2461 : 0 : dhandle = handle->new_dhandle;
2462 : : }
2463 : : }
2464 : :
2465 [ + + ][ + - ]: 900 : if (wr == FDB_RESULT_SUCCESS || br != BTREE_RESULT_FAIL) {
2466 : 900 : _doc.key = doc->key;
2467 : 900 : _doc.meta = doc->meta;
2468 : 900 : _doc.body = doc->body;
2469 : :
2470 : 900 : uint64_t body_offset = docio_read_doc_key_meta(dhandle, offset, &_doc);
2471 [ - + ]: 900 : if (body_offset == offset) {
2472 : 0 : return FDB_RESULT_KEY_NOT_FOUND;
2473 : : }
2474 : :
2475 [ + - ]: 900 : if (handle->kvs) {
2476 : 900 : int size_id = sizeof(fdb_kvs_id_t);
2477 : 900 : doc->keylen = _doc.length.keylen - size_id;
2478 : 900 : doc->key = _doc.key;
2479 : 900 : memmove(doc->key, (uint8_t*)doc->key + size_id, doc->keylen);
2480 : : } else {
2481 : 0 : doc->keylen = _doc.length.keylen;
2482 : 0 : doc->key = _doc.key;
2483 : : }
2484 : 900 : doc->metalen = _doc.length.metalen;
2485 : 900 : doc->bodylen = _doc.length.bodylen;
2486 : 900 : doc->meta = _doc.meta;
2487 : 900 : doc->body = _doc.body;
2488 : 900 : doc->deleted = _doc.length.flag & DOCIO_DELETED;
2489 : 900 : doc->size_ondisk = _fdb_get_docsize(_doc.length);
2490 : 900 : doc->offset = offset;
2491 : :
2492 [ - + ]: 900 : assert(doc->seqnum == _doc.seqnum);
2493 : :
2494 : 900 : return FDB_RESULT_SUCCESS;
2495 : : }
2496 : :
2497 : 901 : return FDB_RESULT_KEY_NOT_FOUND;
2498 : : }
2499 : :
2500 : 402 : static uint8_t equal_docs(fdb_doc *doc, struct docio_object *_doc) {
2501 : 402 : uint8_t rv = 1;
2502 : : // Compare a seq num if seq tree is enabled.
2503 [ + - ]: 402 : if (doc->seqnum != SEQNUM_NOT_USED) {
2504 [ - + ]: 402 : if (doc->seqnum != _doc->seqnum) {
2505 : 0 : free(_doc->key);
2506 : 0 : free(_doc->meta);
2507 : 0 : free(_doc->body);
2508 : 0 : _doc->key = _doc->meta = _doc->body = NULL;
2509 : 0 : rv = 0;
2510 : : }
2511 : : } else { // Compare key and metadata
2512 [ # # ][ # # ]: 0 : if ((doc->key && memcmp(doc->key, _doc->key, doc->keylen)) ||
[ # # ][ # # ]
2513 : 0 : (doc->meta && memcmp(doc->meta, _doc->meta, doc->metalen))) {
2514 : 0 : free(_doc->key);
2515 : 0 : free(_doc->meta);
2516 : 0 : free(_doc->body);
2517 : 0 : _doc->key = _doc->meta = _doc->body = NULL;
2518 : 0 : rv = 0;
2519 : : }
2520 : : }
2521 : 402 : return rv;
2522 : : }
2523 : :
2524 : 402 : INLINE void _remove_kv_id(struct docio_object *doc)
2525 : : {
2526 : 402 : size_t size_id = sizeof(fdb_kvs_id_t);
2527 : 402 : doc->length.keylen -= size_id;
2528 : 402 : memmove(doc->key, (uint8_t*)doc->key + size_id, doc->length.keylen);
2529 : 402 : }
2530 : :
2531 : : // Retrieve a doc's metadata and body with a given doc offset in the database file.
2532 : : LIBFDB_API
2533 : 402 : fdb_status fdb_get_byoffset(fdb_kvs_handle *handle, fdb_doc *doc)
2534 : : {
2535 : 402 : uint64_t offset = doc->offset;
2536 : : struct docio_object _doc;
2537 : :
2538 [ - + ]: 402 : if (!offset) {
2539 : 0 : return FDB_RESULT_INVALID_ARGS;
2540 : : }
2541 : :
2542 : 402 : memset(&_doc, 0, sizeof(struct docio_object));
2543 : :
2544 : 402 : uint64_t _offset = docio_read_doc(handle->dhandle, offset, &_doc);
2545 [ - + ]: 402 : if (_offset == offset) {
2546 [ # # ][ # # ]: 0 : if (handle->new_dhandle && !handle->shandle) {
2547 : : // Look up the new file being compacted
2548 : 0 : _offset = docio_read_doc(handle->new_dhandle, offset, &_doc);
2549 [ # # ]: 0 : if (_offset == offset) {
2550 : 0 : return FDB_RESULT_KEY_NOT_FOUND;
2551 : : }
2552 [ # # ]: 0 : if (handle->kvs) {
2553 : 0 : _remove_kv_id(&_doc);
2554 : : }
2555 [ # # ]: 0 : if (!equal_docs(doc, &_doc)) {
2556 : 0 : free_docio_object(&_doc, 1, 1, 1);
2557 : 0 : return FDB_RESULT_KEY_NOT_FOUND;
2558 : : }
2559 : : } else {
2560 : 0 : return FDB_RESULT_KEY_NOT_FOUND;
2561 : : }
2562 : : } else {
2563 [ + - ]: 402 : if (handle->kvs) {
2564 : 402 : _remove_kv_id(&_doc);
2565 : : }
2566 [ - + ]: 402 : if (!equal_docs(doc, &_doc)) {
2567 : 0 : free_docio_object(&_doc, 1, 1, 1);
2568 [ # # ][ # # ]: 0 : if (handle->new_dhandle && !handle->shandle) {
2569 : : // Look up the new file being compacted
2570 : 0 : _offset = docio_read_doc(handle->new_dhandle, offset, &_doc);
2571 [ # # ]: 0 : if (_offset == offset) {
2572 : 0 : return FDB_RESULT_KEY_NOT_FOUND;
2573 : : }
2574 [ # # ]: 0 : if (handle->kvs) {
2575 : 0 : _remove_kv_id(&_doc);
2576 : : }
2577 [ # # ]: 0 : if (!equal_docs(doc, &_doc)) {
2578 : 0 : free_docio_object(&_doc, 1, 1, 1);
2579 : 0 : return FDB_RESULT_KEY_NOT_FOUND;
2580 : : }
2581 : : } else {
2582 : 0 : return FDB_RESULT_KEY_NOT_FOUND;
2583 : : }
2584 : : }
2585 : : }
2586 : :
2587 : 402 : doc->seqnum = _doc.seqnum;
2588 : 402 : doc->keylen = _doc.length.keylen;
2589 : 402 : doc->metalen = _doc.length.metalen;
2590 : 402 : doc->bodylen = _doc.length.bodylen;
2591 [ + - ]: 402 : if (doc->key) {
2592 : 402 : free(_doc.key);
2593 : : } else {
2594 : 0 : doc->key = _doc.key;
2595 : : }
2596 [ + - ]: 402 : if (doc->meta) {
2597 : 402 : free(_doc.meta);
2598 : : } else {
2599 : 0 : doc->meta = _doc.meta;
2600 : : }
2601 [ - + ]: 402 : if (doc->body) {
2602 [ # # ]: 0 : if (_doc.length.bodylen > 0) {
2603 : 0 : memcpy(doc->body, _doc.body, _doc.length.bodylen);
2604 : : }
2605 : 0 : free(_doc.body);
2606 : : } else {
2607 : 402 : doc->body = _doc.body;
2608 : : }
2609 : 402 : doc->deleted = _doc.length.flag & DOCIO_DELETED;
2610 : 402 : doc->size_ondisk = _fdb_get_docsize(_doc.length);
2611 : :
2612 [ + + ]: 402 : if (_doc.length.flag & DOCIO_DELETED) {
2613 : 1 : return FDB_RESULT_KEY_NOT_FOUND;
2614 : : }
2615 : :
2616 : 402 : return FDB_RESULT_SUCCESS;
2617 : : }
2618 : :
2619 : 2407508 : static uint64_t _fdb_get_wal_threshold(fdb_kvs_handle *handle)
2620 : : {
2621 [ - + ]: 2407508 : if (filemgr_get_file_status(handle->file) == FILE_COMPACT_NEW) {
2622 : 0 : return wal_get_size(handle->file);
2623 : : }
2624 : 2407508 : return handle->config.wal_threshold;
2625 : : }
2626 : :
2627 : : LIBFDB_API
2628 : 2452456 : fdb_status fdb_set(fdb_kvs_handle *handle, fdb_doc *doc)
2629 : : {
2630 : : uint64_t offset;
2631 : : struct docio_object _doc;
2632 : : struct filemgr *file;
2633 : : struct docio_handle *dhandle;
2634 : : struct timeval tv;
2635 : 2452456 : bool txn_enabled = false;
2636 : 2452456 : bool sub_handle = false;
2637 : 2452456 : bool wal_flushed = false;
2638 : 2452456 : fdb_txn *txn = handle->fhandle->root->txn;
2639 : 2452456 : fdb_status wr = FDB_RESULT_SUCCESS;
2640 : :
2641 [ + + ]: 2452456 : if (handle->config.flags & FDB_OPEN_FLAG_RDONLY) {
2642 : : return fdb_log(&handle->log_callback, FDB_RESULT_RONLY_VIOLATION,
2643 : : "Warning: SET is not allowed on the read-only DB file '%s'.",
2644 : 1 : handle->file->filename);
2645 : : }
2646 : :
2647 [ + - ][ + - ]: 2452455 : if ( doc->key == NULL || doc->keylen == 0 || doc->keylen > FDB_MAX_KEYLEN ||
[ + - ][ + - ]
[ + + ][ + - ]
[ + + ][ - + ]
2648 : : doc->keylen > handle->config.blocksize - 256 ||
2649 : : (doc->metalen > 0 && doc->meta == NULL) ||
2650 : : (doc->bodylen > 0 && doc->body == NULL)) {
2651 : 0 : return FDB_RESULT_INVALID_ARGS;
2652 : : }
2653 : :
2654 : 2452455 : _doc.length.keylen = doc->keylen;
2655 : 2452455 : _doc.length.metalen = doc->metalen;
2656 [ + + ]: 2452455 : _doc.length.bodylen = doc->deleted ? 0 : doc->bodylen;
2657 : 2452455 : _doc.key = doc->key;
2658 : 2452455 : _doc.meta = doc->meta;
2659 [ + + ]: 2452455 : _doc.body = doc->deleted ? NULL : doc->body;
2660 : :
2661 [ + + ]: 2452455 : if (handle->kvs) {
2662 : : // multi KV instance mode
2663 : : // allocate more (temporary) space for key, to store ID number
2664 : : fdb_kvs_id_t id;
2665 : 2451434 : _doc.length.keylen = doc->keylen + sizeof(fdb_kvs_id_t);
2666 : 2451434 : _doc.key = alca(uint8_t, _doc.length.keylen);
2667 : : // copy ID
2668 : 2451434 : id = _endian_encode(handle->kvs->id);
2669 : 2451434 : memcpy(_doc.key, &id, sizeof(id));
2670 : : // copy key
2671 : 2451434 : memcpy((uint8_t*)_doc.key + sizeof(id), doc->key, doc->keylen);
2672 : :
2673 [ + + ]: 2451434 : if (handle->kvs->type == KVS_SUB) {
2674 : 17321 : sub_handle = true;
2675 : : } else {
2676 : 2434113 : sub_handle = false;
2677 : : }
2678 : : }
2679 : :
2680 : : fdb_set_start:
2681 : 2452455 : fdb_check_file_reopen(handle);
2682 : 2452455 : filemgr_mutex_lock(handle->file);
2683 : 2452455 : fdb_sync_db_header(handle);
2684 : 2452455 : fdb_link_new_file(handle);
2685 : :
2686 [ - + ]: 2452455 : if (filemgr_is_rollback_on(handle->file)) {
2687 : 0 : filemgr_mutex_unlock(handle->file);
2688 : 0 : return FDB_RESULT_FAIL_BY_ROLLBACK;
2689 : : }
2690 : :
2691 [ + + ]: 2452455 : if (handle->new_file == NULL) {
2692 : 2406884 : file = handle->file;
2693 : 2406884 : dhandle = handle->dhandle;
2694 : : } else {
2695 : : // compaction is being performed and new file exists
2696 : : // relay lock
2697 : 45571 : filemgr_mutex_lock(handle->new_file);
2698 : 45571 : filemgr_mutex_unlock(handle->file);
2699 : 45571 : file = handle->new_file;
2700 : 45571 : dhandle = handle->new_dhandle;
2701 : : }
2702 : :
2703 [ + + ]: 2452455 : if (!(file->status == FILE_NORMAL ||
2704 [ - + ]: 45563 : file->status == FILE_COMPACT_NEW)) {
2705 : : // we must not write into this file
2706 : : // file status was changed by other thread .. start over
2707 : 0 : filemgr_mutex_unlock(file);
2708 : 0 : goto fdb_set_start;
2709 : : }
2710 : :
2711 [ + + ]: 2452455 : if (handle->config.seqtree_opt == FDB_SEQTREE_USE) {
2712 [ + + ]: 2452445 : if (sub_handle) {
2713 : : // multiple KV instnace mode AND sub handle
2714 : 17321 : handle->seqnum = fdb_kvs_get_seqnum(file, handle->kvs->id) + 1;
2715 : 17321 : fdb_kvs_set_seqnum(file, handle->kvs->id, handle->seqnum);
2716 : : } else {
2717 : : // super handle OR single KV instnace mode
2718 : 2435124 : handle->seqnum = filemgr_get_seqnum(file) + 1;
2719 : 2435124 : filemgr_set_seqnum(file, handle->seqnum);
2720 : : }
2721 : 2452445 : _doc.seqnum = doc->seqnum = handle->seqnum;
2722 : : } else{
2723 : 10 : _doc.seqnum = SEQNUM_NOT_USED;
2724 : : }
2725 : :
2726 [ + + ]: 2452455 : if (doc->deleted) {
2727 : : // set timestamp
2728 : 729 : gettimeofday(&tv, NULL);
2729 : 729 : _doc.timestamp = (timestamp_t)tv.tv_sec;
2730 : : } else {
2731 : 2451726 : _doc.timestamp = 0;
2732 : : }
2733 : :
2734 [ + + ]: 2452455 : if (txn) {
2735 : 121287 : txn_enabled = true;
2736 : : }
2737 [ + + ]: 2452455 : if (dhandle == handle->new_dhandle) {
2738 : 45571 : offset = docio_append_doc_compact(dhandle, &_doc, doc->deleted, txn_enabled);
2739 : : } else {
2740 : 2406884 : offset = docio_append_doc(dhandle, &_doc, doc->deleted, txn_enabled);
2741 : : }
2742 [ + + ]: 2452455 : if (offset == BLK_NOT_FOUND) {
2743 : 3918 : filemgr_mutex_unlock(file);
2744 : 3918 : return FDB_RESULT_WRITE_FAIL;
2745 : : }
2746 : :
2747 : 2448537 : doc->size_ondisk = _fdb_get_docsize(_doc.length);
2748 : 2448537 : doc->offset = offset;
2749 [ + + ]: 2448537 : if (!txn) {
2750 : 2327250 : txn = &file->global_txn;
2751 : : }
2752 [ + + ]: 2448537 : if (handle->kvs) {
2753 : : // multi KV instance mode
2754 : 2447516 : fdb_doc kv_ins_doc = *doc;
2755 : 2447516 : kv_ins_doc.key = _doc.key;
2756 : 2447516 : kv_ins_doc.keylen = _doc.length.keylen;
2757 : 2447516 : wal_insert(txn, file, &kv_ins_doc, offset);
2758 : : } else {
2759 : 1021 : wal_insert(txn, file, doc, offset);
2760 : : }
2761 : :
2762 [ + + ]: 2448537 : if (wal_get_dirty_status(file)== FDB_WAL_CLEAN) {
2763 : 355 : wal_set_dirty_status(file, FDB_WAL_DIRTY);
2764 : : }
2765 : :
2766 [ + + ]: 4888074 : if ((handle->config.wal_flush_before_commit ||
[ - + + + ]
[ + + ]
2767 : : handle->config.auto_commit) &&
2768 : 2439537 : filemgr_get_file_status(handle->file) == FILE_NORMAL) {
2769 : : bid_t dirty_idtree_root, dirty_seqtree_root;
2770 : :
2771 [ + + ]: 2393966 : if (!txn_enabled) {
2772 : 2294879 : handle->dirty_updates = 1;
2773 : : }
2774 : :
2775 : : // MUST ensure that 'file' is always 'handle->file',
2776 : : // because this routine will not be executed during compaction.
2777 : 2393966 : filemgr_get_dirty_root(file, &dirty_idtree_root, &dirty_seqtree_root);
2778 : :
2779 : : // other concurrent writer flushed WAL before commit,
2780 : : // sync root node of each tree
2781 [ + + ]: 2393966 : if (dirty_idtree_root != BLK_NOT_FOUND) {
2782 : 2054730 : handle->trie->root_bid = dirty_idtree_root;
2783 : : }
2784 [ + + ][ + + ]: 2393966 : if (handle->config.seqtree_opt == FDB_SEQTREE_USE &&
2785 : : dirty_seqtree_root != BLK_NOT_FOUND) {
2786 : 2054730 : handle->seqtree->root_bid = dirty_seqtree_root;
2787 : : }
2788 : :
2789 [ + + ]: 2393966 : if (wal_get_num_flushable(file) > _fdb_get_wal_threshold(handle)) {
2790 : : struct avl_tree flush_items;
2791 : :
2792 : : // discard all cached writable blocks
2793 : : // to avoid data inconsistency with other writers
2794 : 603 : btreeblk_discard_blocks(handle->bhandle);
2795 : :
2796 : : // commit only for non-transactional WAL entries
2797 : 603 : wr = wal_commit(&file->global_txn, file, NULL);
2798 [ - + ]: 603 : if (wr != FDB_RESULT_SUCCESS) {
2799 : 0 : filemgr_mutex_unlock(file);
2800 : 0 : return wr;
2801 : : }
2802 : : wr = wal_flush(file, (void *)handle,
2803 : : _fdb_wal_flush_func, _fdb_wal_get_old_offset,
2804 : 603 : &flush_items);
2805 [ - + ]: 603 : if (wr != FDB_RESULT_SUCCESS) {
2806 : 0 : filemgr_mutex_unlock(file);
2807 : 0 : return wr;
2808 : : }
2809 : 603 : wal_set_dirty_status(file, FDB_WAL_PENDING);
2810 : : // it is ok to release flushed items becuase
2811 : : // these items are not actually committed yet.
2812 : : // they become visible after fdb_commit is invoked.
2813 : 603 : wal_release_flushed_items(file, &flush_items);
2814 : :
2815 : : // sync new root node
2816 : 603 : dirty_idtree_root = handle->trie->root_bid;
2817 [ + - ]: 603 : if (handle->config.seqtree_opt == FDB_SEQTREE_USE) {
2818 : 603 : dirty_seqtree_root = handle->seqtree->root_bid;
2819 : : }
2820 : : filemgr_set_dirty_root(file,
2821 : : dirty_idtree_root,
2822 : 603 : dirty_seqtree_root);
2823 : :
2824 : 603 : wal_flushed = true;
2825 : : }
2826 : : }
2827 : :
2828 : 2448537 : filemgr_mutex_unlock(file);
2829 : :
2830 [ + + ][ + + ]: 2448537 : if (wal_flushed && handle->config.auto_commit) {
2831 : 1 : return fdb_commit(handle->fhandle, FDB_COMMIT_NORMAL);
2832 : : }
2833 : 2452456 : return FDB_RESULT_SUCCESS;
2834 : : }
2835 : :
2836 : : LIBFDB_API
2837 : 726 : fdb_status fdb_del(fdb_kvs_handle *handle, fdb_doc *doc)
2838 : : {
2839 [ - + ]: 726 : if (handle->config.flags & FDB_OPEN_FLAG_RDONLY) {
2840 : : return fdb_log(&handle->log_callback, FDB_RESULT_RONLY_VIOLATION,
2841 : : "Warning: DEL is not allowed on the read-only DB file '%s'.",
2842 : 0 : handle->file->filename);
2843 : : }
2844 : :
2845 [ + - ][ + - ]: 726 : if (doc->key == NULL || doc->keylen == 0 || doc->keylen > FDB_MAX_KEYLEN ||
[ + - ][ - + ]
2846 : : doc->keylen > handle->config.blocksize - 256) {
2847 : 0 : return FDB_RESULT_INVALID_ARGS;
2848 : : }
2849 : :
2850 : 726 : doc->deleted = true;
2851 : : fdb_doc _doc;
2852 : 726 : _doc = *doc;
2853 : 726 : _doc.bodylen = 0;
2854 : 726 : _doc.body = NULL;
2855 : 726 : return fdb_set(handle, &_doc);
2856 : : }
2857 : :
2858 : 13760 : uint64_t _fdb_export_header_flags(fdb_kvs_handle *handle)
2859 : : {
2860 : 13760 : uint64_t rv = 0;
2861 [ + + ]: 13760 : if (handle->config.seqtree_opt == FDB_SEQTREE_USE) {
2862 : : // seq tree is used
2863 : 13759 : rv |= FDB_FLAG_SEQTREE_USE;
2864 : : }
2865 [ + + ]: 13760 : if (handle->fhandle->flags & FHANDLE_ROOT_INITIALIZED) {
2866 : : // the default KVS is once opened
2867 : 13572 : rv |= FDB_FLAG_ROOT_INITIALIZED;
2868 : : }
2869 [ + + ]: 13760 : if (handle->fhandle->flags & FHANDLE_ROOT_CUSTOM_CMP) {
2870 : : // the default KVS is based on custom key order
2871 : 45 : rv |= FDB_FLAG_ROOT_CUSTOM_CMP;
2872 : : }
2873 : 13760 : return rv;
2874 : : }
2875 : :
2876 : 13760 : uint64_t fdb_set_file_header(fdb_kvs_handle *handle)
2877 : : {
2878 : : /*
2879 : : <ForestDB header>
2880 : : [offset]: (description)
2881 : : [ 0]: BID of root node of root B+Tree of HB+Trie: 8 bytes
2882 : : [ 8]: BID of root node of seq B+Tree: 8 bytes (0xFF.. if not used)
2883 : : [ 16]: # of live documents: 8 bytes
2884 : : [ 24]: # of live B+Tree nodes: 8 bytes
2885 : : [ 32]: Data size (byte): 8 bytes
2886 : : [ 40]: BID of the DB header created when last WAL flush: 8 bytes
2887 : : [ 48]: Offset of the document containing KV instances' info: 8 bytes
2888 : : [ 56]: Header flags: 8 bytes
2889 : : [ 64]: Size of newly compacted target file name : 2 bytes
2890 : : [ 66]: Size of old file name before compaction : 2 bytes
2891 : : [ 68]: File name of newly compacted file : x bytes
2892 : : [ 68+x]: File name of old file before compcation : y bytes
2893 : : [68+x+y]: CRC32: 4 bytes
2894 : : total size (header's length): 72+x+y bytes
2895 : :
2896 : : Note: the list of functions that need to be modified
2897 : : if the header structure is changed:
2898 : :
2899 : : filemgr_destory_file() in filemgr.cc
2900 : : */
2901 : 13760 : uint8_t *buf = alca(uint8_t, handle->config.blocksize);
2902 : 13760 : uint16_t new_filename_len = 0;
2903 : 13760 : uint16_t old_filename_len = 0;
2904 : : uint16_t _edn_safe_16;
2905 : : uint32_t crc;
2906 : : uint64_t _edn_safe_64;
2907 : 13760 : size_t offset = 0;
2908 : : struct filemgr *cur_file;
2909 : : struct kvs_stat stat;
2910 : :
2911 : 13760 : cur_file = handle->file;
2912 : :
2913 : : // hb+trie or idtree root bid
2914 : 13760 : _edn_safe_64 = _endian_encode(handle->trie->root_bid);
2915 : 13760 : seq_memcpy(buf + offset, &_edn_safe_64, sizeof(handle->trie->root_bid), offset);
2916 : :
2917 [ + + ]: 13760 : if (handle->config.seqtree_opt == FDB_SEQTREE_USE) {
2918 : : // b+tree root bid
2919 : 13759 : _edn_safe_64 = _endian_encode(handle->seqtree->root_bid);
2920 : 13759 : seq_memcpy(buf + offset, &_edn_safe_64,
2921 : 13759 : sizeof(handle->seqtree->root_bid), offset);
2922 : : } else {
2923 : 1 : memset(buf + offset, 0xff, sizeof(uint64_t));
2924 : 1 : offset += sizeof(uint64_t);
2925 : : }
2926 : :
2927 : : // get stat
2928 : 13760 : _kvs_stat_get(cur_file, 0, &stat);
2929 : :
2930 : : // # docs
2931 : 13760 : _edn_safe_64 = _endian_encode(stat.ndocs);
2932 : 13760 : seq_memcpy(buf + offset, &_edn_safe_64, sizeof(_edn_safe_64), offset);
2933 : : // # live nodes
2934 : 13760 : _edn_safe_64 = _endian_encode(stat.nlivenodes);
2935 : 13760 : seq_memcpy(buf + offset, &_edn_safe_64,
2936 : 13760 : sizeof(_edn_safe_64), offset);
2937 : : // data size
2938 : 13760 : _edn_safe_64 = _endian_encode(stat.datasize);
2939 : 13760 : seq_memcpy(buf + offset, &_edn_safe_64, sizeof(_edn_safe_64), offset);
2940 : : // last header bid
2941 : 13760 : _edn_safe_64 = _endian_encode(handle->last_wal_flush_hdr_bid);
2942 : 13760 : seq_memcpy(buf + offset, &_edn_safe_64,
2943 : 13760 : sizeof(handle->last_wal_flush_hdr_bid), offset);
2944 : : // kv info offset
2945 : 13760 : _edn_safe_64 = _endian_encode(handle->kv_info_offset);
2946 : 13760 : seq_memcpy(buf + offset, &_edn_safe_64,
2947 : 13760 : sizeof(handle->kv_info_offset), offset);
2948 : : // header flags
2949 : 13760 : _edn_safe_64 = _fdb_export_header_flags(handle);
2950 : 13760 : _edn_safe_64 = _endian_encode(_edn_safe_64);
2951 : 13760 : seq_memcpy(buf + offset, &_edn_safe_64,
2952 : 13760 : sizeof(_edn_safe_64), offset);
2953 : :
2954 : : // size of newly compacted target file name
2955 [ + + ]: 13760 : if (handle->file->new_file) {
2956 : 53 : new_filename_len = strlen(handle->file->new_file->filename) + 1;
2957 : : }
2958 : 13760 : _edn_safe_16 = _endian_encode(new_filename_len);
2959 : 13760 : seq_memcpy(buf + offset, &_edn_safe_16, sizeof(new_filename_len), offset);
2960 : :
2961 : : // size of old filename before compaction
2962 [ + + ]: 13760 : if (handle->file->old_filename) {
2963 : 2512 : old_filename_len = strlen(handle->file->old_filename) + 1;
2964 : : }
2965 : 13760 : _edn_safe_16 = _endian_encode(old_filename_len);
2966 : 13760 : seq_memcpy(buf + offset, &_edn_safe_16, sizeof(old_filename_len), offset);
2967 : :
2968 [ + + ]: 13760 : if (new_filename_len) {
2969 : 53 : seq_memcpy(buf + offset, handle->file->new_file->filename,
2970 : 53 : new_filename_len, offset);
2971 : : }
2972 : :
2973 [ + + ]: 13760 : if (old_filename_len) {
2974 : 2512 : seq_memcpy(buf + offset, handle->file->old_filename,
2975 : 2512 : old_filename_len, offset);
2976 : : }
2977 : :
2978 : : // crc32
2979 : 13760 : crc = chksum(buf, offset);
2980 : 13760 : crc = _endian_encode(crc);
2981 : 13760 : seq_memcpy(buf + offset, &crc, sizeof(crc), offset);
2982 : :
2983 : 13760 : return filemgr_update_header(handle->file, buf, offset);
2984 : : }
2985 : :
2986 : 120630 : static fdb_status _fdb_append_commit_mark(void *voidhandle, uint64_t offset)
2987 : : {
2988 : 120630 : fdb_kvs_handle *handle = (fdb_kvs_handle *)voidhandle;
2989 : : struct docio_handle *dhandle;
2990 : :
2991 [ + + ]: 120630 : if (handle->new_file) {
2992 : 22189 : dhandle = handle->new_dhandle;
2993 : : } else {
2994 : 98441 : dhandle = handle->dhandle;
2995 : : }
2996 [ - + ]: 120630 : if (docio_append_commit_mark(dhandle, offset) == BLK_NOT_FOUND) {
2997 : 0 : return FDB_RESULT_WRITE_FAIL;
2998 : : }
2999 : 120630 : return FDB_RESULT_SUCCESS;
3000 : : }
3001 : :
3002 : : LIBFDB_API
3003 : 15917 : fdb_status fdb_commit(fdb_file_handle *fhandle, fdb_commit_opt_t opt)
3004 : : {
3005 : 15917 : return _fdb_commit(fhandle->root, opt);
3006 : : }
3007 : :
3008 : 17158 : fdb_status _fdb_commit(fdb_kvs_handle *handle, fdb_commit_opt_t opt)
3009 : : {
3010 : 17158 : fdb_txn *txn = handle->fhandle->root->txn;
3011 : : fdb_txn *earliest_txn;
3012 : 17158 : fdb_status fs = FDB_RESULT_SUCCESS;
3013 : 17158 : bool wal_flushed = false;
3014 : : bid_t dirty_idtree_root, dirty_seqtree_root;
3015 : : struct avl_tree flush_items;
3016 : 17158 : fdb_status wr = FDB_RESULT_SUCCESS;
3017 : :
3018 [ + + ]: 17158 : if (handle->kvs) {
3019 [ - + ]: 17151 : if (handle->kvs->type == KVS_SUB) {
3020 : : // deny commit on sub handle
3021 : 0 : return FDB_RESULT_INVALID_HANDLE;
3022 : : }
3023 : : }
3024 [ + + ]: 17158 : if (handle->config.flags & FDB_OPEN_FLAG_RDONLY) {
3025 : : return fdb_log(&handle->log_callback, FDB_RESULT_RONLY_VIOLATION,
3026 : : "Warning: Commit is not allowed on the read-only DB file '%s'.",
3027 : 1 : handle->file->filename);
3028 : : }
3029 : :
3030 : 17157 : fdb_check_file_reopen(handle);
3031 : :
3032 : 17157 : filemgr_mutex_lock(handle->file);
3033 : 17157 : fdb_sync_db_header(handle);
3034 : 17157 : fdb_link_new_file(handle);
3035 : :
3036 [ - + ]: 17157 : if (filemgr_is_rollback_on(handle->file)) {
3037 : 0 : filemgr_mutex_unlock(handle->file);
3038 : 0 : return FDB_RESULT_FAIL_BY_ROLLBACK;
3039 : : }
3040 : :
3041 [ + + ]: 17157 : if (handle->new_file) {
3042 : : // HANDLE->FILE is undergoing compaction ..
3043 : : // just do fsync to HANDLE->NEW_FILE
3044 : :
3045 : : // relay lock
3046 : 3615 : filemgr_mutex_lock(handle->new_file);
3047 : 3615 : filemgr_mutex_unlock(handle->file);
3048 : :
3049 [ + + ]: 3615 : if (txn) {
3050 : : // transactional updates
3051 : 222 : wr = wal_commit(txn, handle->new_file, _fdb_append_commit_mark);
3052 [ - + ]: 222 : if (wr != FDB_RESULT_SUCCESS) {
3053 : 0 : filemgr_mutex_unlock(handle->new_file);
3054 : 0 : return wr;
3055 : : }
3056 : : } else {
3057 : : // non-transactional updates
3058 : 3393 : wal_commit(&handle->new_file->global_txn, handle->new_file, NULL);
3059 : : }
3060 : :
3061 : 3615 : fs = filemgr_sync(handle->new_file, &handle->log_callback);
3062 : :
3063 : 3615 : filemgr_mutex_unlock(handle->new_file);
3064 : : } else {
3065 : : // normal case
3066 : 13542 : fs = btreeblk_end(handle->bhandle);
3067 [ - + ]: 13542 : if (fs != FDB_RESULT_SUCCESS) {
3068 : 0 : filemgr_mutex_unlock(handle->file);
3069 : 0 : return fs;
3070 : : }
3071 : :
3072 : : // commit wal
3073 [ + + ]: 13542 : if (txn) {
3074 : : // transactional updates
3075 : 996 : wr = wal_commit(txn, handle->file, _fdb_append_commit_mark);
3076 [ - + ]: 996 : if (wr != FDB_RESULT_SUCCESS) {
3077 : 0 : filemgr_mutex_unlock(handle->file);
3078 : 0 : return wr;
3079 : : }
3080 [ - + ]: 996 : if (wal_get_dirty_status(handle->file)== FDB_WAL_CLEAN) {
3081 : 0 : wal_set_dirty_status(handle->file, FDB_WAL_DIRTY);
3082 : : }
3083 : : } else {
3084 : : // non-transactional updates
3085 : 12546 : wal_commit(&handle->file->global_txn, handle->file, NULL);
3086 : : }
3087 : :
3088 : : // sync dirty root nodes
3089 : : filemgr_get_dirty_root(handle->file, &dirty_idtree_root,
3090 : 13542 : &dirty_seqtree_root);
3091 [ + + ]: 13542 : if (dirty_idtree_root != BLK_NOT_FOUND) {
3092 : 64 : handle->trie->root_bid = dirty_idtree_root;
3093 : : }
3094 [ + + ][ + + ]: 13542 : if (handle->config.seqtree_opt == FDB_SEQTREE_USE &&
3095 : : dirty_seqtree_root != BLK_NOT_FOUND) {
3096 : 64 : handle->seqtree->root_bid = dirty_seqtree_root;
3097 : : }
3098 : :
3099 [ + + ]: 13542 : if (handle->dirty_updates) {
3100 : : // discard all cached writable b+tree nodes
3101 : : // to avoid data inconsistency with other writers
3102 : 12370 : btreeblk_discard_blocks(handle->bhandle);
3103 : : }
3104 : :
3105 [ + + + + ]: 27057 : if (wal_get_num_flushable(handle->file) > _fdb_get_wal_threshold(handle) ||
[ + + ][ + + ]
3106 : 13515 : wal_get_dirty_status(handle->file) == FDB_WAL_PENDING ||
3107 : : opt & FDB_COMMIT_MANUAL_WAL_FLUSH) {
3108 : : // wal flush when
3109 : : // 1. wal size exceeds threshold
3110 : : // 2. wal is already flushed before commit
3111 : : // (in this case, flush the rest of entries)
3112 : : // 3. user forces to manually flush wal
3113 : :
3114 : : wr = wal_flush(handle->file, (void *)handle,
3115 : : _fdb_wal_flush_func, _fdb_wal_get_old_offset,
3116 : 245 : &flush_items);
3117 [ + + ]: 245 : if (wr != FDB_RESULT_SUCCESS) {
3118 : 1 : filemgr_mutex_unlock(handle->file);
3119 : 1 : return wr;
3120 : : }
3121 : 244 : wal_set_dirty_status(handle->file, FDB_WAL_CLEAN);
3122 : 244 : wal_flushed = true;
3123 : : }
3124 : :
3125 : : // Note: Appending KVS header must be done after flushing WAL
3126 : : // because KVS stats info is updated during WAL flushing.
3127 [ + + ]: 13541 : if (handle->kvs) {
3128 : : // multi KV instance mode .. append up-to-date KV header
3129 : : handle->kv_info_offset = fdb_kvs_header_append(handle->file,
3130 : 13534 : handle->dhandle);
3131 : : }
3132 : :
3133 : : // Note: Getting header BID must be done after
3134 : : // all other data are written into the file!!
3135 : : // Or, header BID inconsistency will occur (it will
3136 : : // point to wrong block).
3137 : 13541 : handle->last_hdr_bid = filemgr_get_next_alloc_block(handle->file);
3138 [ + + ]: 13541 : if (wal_get_dirty_status(handle->file) == FDB_WAL_CLEAN) {
3139 : : earliest_txn = wal_earliest_txn(handle->file,
3140 [ + + ]: 260 : (txn)?(txn):(&handle->file->global_txn));
3141 [ + + ]: 260 : if (earliest_txn) {
3142 : : // there exists other transaction that is not committed yet
3143 [ + + ]: 7 : if (handle->last_wal_flush_hdr_bid < earliest_txn->prev_hdr_bid) {
3144 : 1 : handle->last_wal_flush_hdr_bid = earliest_txn->prev_hdr_bid;
3145 : : }
3146 : : } else {
3147 : : // there is no other transaction .. now WAL is empty
3148 : 253 : handle->last_wal_flush_hdr_bid = handle->last_hdr_bid;
3149 : : }
3150 : : }
3151 : :
3152 [ + + ]: 13541 : if (txn == NULL) {
3153 : : // update global_txn's previous header BID
3154 : 12545 : handle->file->global_txn.prev_hdr_bid = handle->last_hdr_bid;
3155 : : }
3156 : :
3157 : 13541 : handle->cur_header_revnum = fdb_set_file_header(handle);
3158 : 13541 : fs = filemgr_commit(handle->file, &handle->log_callback);
3159 [ + + ]: 13541 : if (wal_flushed) {
3160 : 244 : wal_release_flushed_items(handle->file, &flush_items);
3161 : : }
3162 : :
3163 : 13541 : handle->dirty_updates = 0;
3164 : 13541 : filemgr_mutex_unlock(handle->file);
3165 : : }
3166 : :
3167 : 17158 : return fs;
3168 : : }
3169 : :
3170 : 53 : static fdb_status _fdb_commit_and_remove_pending(fdb_kvs_handle *handle,
3171 : : struct filemgr *old_file,
3172 : : struct filemgr *new_file)
3173 : : {
3174 : : fdb_txn *earliest_txn;
3175 : 53 : bool wal_flushed = false;
3176 : : bid_t dirty_idtree_root, dirty_seqtree_root;
3177 : : struct avl_tree flush_items;
3178 : 53 : fdb_status status = FDB_RESULT_SUCCESS;
3179 : :
3180 : 53 : filemgr_mutex_lock(handle->file);
3181 : :
3182 : 53 : btreeblk_end(handle->bhandle);
3183 : :
3184 : : // sync dirty root nodes
3185 : 53 : filemgr_get_dirty_root(handle->file, &dirty_idtree_root, &dirty_seqtree_root);
3186 [ - + ]: 53 : if (dirty_idtree_root != BLK_NOT_FOUND) {
3187 : 0 : handle->trie->root_bid = dirty_idtree_root;
3188 : : }
3189 [ + - ][ - + ]: 53 : if (handle->config.seqtree_opt == FDB_SEQTREE_USE &&
3190 : : dirty_seqtree_root != BLK_NOT_FOUND) {
3191 : 0 : handle->seqtree->root_bid = dirty_seqtree_root;
3192 : : }
3193 : :
3194 : 53 : wal_commit(&handle->file->global_txn, handle->file, NULL);
3195 [ + + ]: 53 : if (wal_get_num_flushable(handle->file)) {
3196 : : // flush wal if not empty
3197 : : wal_flush(handle->file, (void *)handle,
3198 : 15 : _fdb_wal_flush_func, _fdb_wal_get_old_offset, &flush_items);
3199 : 15 : wal_set_dirty_status(handle->file, FDB_WAL_CLEAN);
3200 : 15 : wal_flushed = true;
3201 [ + + ]: 38 : } else if (wal_get_size(handle->file) == 0) {
3202 : : // empty WAL
3203 : 35 : wal_set_dirty_status(handle->file, FDB_WAL_CLEAN);
3204 : : }
3205 : :
3206 : : // Note: Appending KVS header must be done after flushing WAL
3207 : : // because KVS stats info is updated during WAL flushing.
3208 [ + - ]: 53 : if (handle->kvs) {
3209 : : // multi KV instance mode .. append up-to-date KV header
3210 : : handle->kv_info_offset = fdb_kvs_header_append(handle->file,
3211 : 53 : handle->dhandle);
3212 : : }
3213 : :
3214 : 53 : handle->last_hdr_bid = filemgr_get_next_alloc_block(handle->file);
3215 [ + + ]: 53 : if (wal_get_dirty_status(handle->file) == FDB_WAL_CLEAN) {
3216 : 50 : earliest_txn = wal_earliest_txn(handle->file, &handle->file->global_txn);
3217 [ - + ]: 50 : if (earliest_txn) {
3218 : : // there exists other transaction that is not committed yet
3219 [ # # ]: 0 : if (handle->last_wal_flush_hdr_bid < earliest_txn->prev_hdr_bid) {
3220 : 0 : handle->last_wal_flush_hdr_bid = earliest_txn->prev_hdr_bid;
3221 : : }
3222 : : } else {
3223 : : // there is no other transaction .. now WAL is empty
3224 : 50 : handle->last_wal_flush_hdr_bid = handle->last_hdr_bid;
3225 : : }
3226 : : }
3227 : :
3228 : : // update global_txn's previous header BID
3229 : 53 : handle->file->global_txn.prev_hdr_bid = handle->last_hdr_bid;
3230 : :
3231 : 53 : handle->cur_header_revnum = fdb_set_file_header(handle);
3232 : 53 : status = filemgr_commit(handle->file, &handle->log_callback);
3233 [ - + ]: 53 : if (status != FDB_RESULT_SUCCESS) {
3234 : 0 : filemgr_mutex_unlock(handle->file);
3235 : 0 : return status;
3236 : : }
3237 : :
3238 [ + + ]: 53 : if (wal_flushed) {
3239 : 15 : wal_release_flushed_items(handle->file, &flush_items);
3240 : : }
3241 : :
3242 : : // Mark the old file as "remove_pending".
3243 : : // Note that a file deletion will be pended until there is no handle
3244 : : // referring the file.
3245 : 53 : filemgr_remove_pending(old_file, new_file);
3246 : :
3247 : : // Don't clean up the buffer cache entries for the old file.
3248 : : // They will be cleaned up later.
3249 : 53 : filemgr_close(old_file, 0, handle->filename, &handle->log_callback);
3250 : :
3251 : 53 : filemgr_mutex_unlock(handle->file);
3252 : 53 : return status;
3253 : : }
3254 : :
3255 : 27767071 : INLINE int _fdb_cmp_uint64_t(const void *key1, const void *key2)
3256 : : {
3257 : : uint64_t a,b;
3258 : : // must ensure that key1 and key2 are pointers to uint64_t values
3259 : 27767071 : a = deref64(key1);
3260 : 27767071 : b = deref64(key2);
3261 : :
3262 : : #ifdef __BIT_CMP
3263 : 27767071 : return _CMP_U64(a, b);
3264 : :
3265 : : #else
3266 : : if (a < b) {
3267 : : return -1;
3268 : : } else if (a > b) {
3269 : : return 1;
3270 : : } else {
3271 : : return 0;
3272 : : }
3273 : : #endif
3274 : : }
3275 : :
3276 : 53 : INLINE fdb_status _fdb_compact_move_docs(fdb_kvs_handle *handle,
3277 : : struct filemgr *new_file,
3278 : : struct hbtrie *new_trie,
3279 : : struct btree *new_idtree,
3280 : : struct btree *new_seqtree,
3281 : : struct docio_handle *new_dhandle,
3282 : : struct btreeblk_handle *new_bhandle)
3283 : : {
3284 : : uint8_t deleted;
3285 : : uint64_t offset;
3286 : : uint64_t new_offset;
3287 : : uint64_t *offset_array;
3288 : : uint64_t n_moved_docs;
3289 : : size_t i, j, c, count;
3290 : : size_t offset_array_max;
3291 : : hbtrie_result hr;
3292 : : struct docio_object doc[FDB_COMPACTION_BATCHSIZE];
3293 : : struct hbtrie_iterator it;
3294 : : struct timeval tv;
3295 : : fdb_doc wal_doc;
3296 : : fdb_kvs_handle new_handle;
3297 : : timestamp_t cur_timestamp;
3298 : 53 : fdb_status fs = FDB_RESULT_SUCCESS;
3299 : :
3300 : 53 : gettimeofday(&tv, NULL);
3301 : 53 : cur_timestamp = tv.tv_sec;
3302 : :
3303 : 53 : new_handle = *handle;
3304 : 53 : new_handle.file = new_file;
3305 : 53 : new_handle.trie = new_trie;
3306 : 53 : new_handle.idtree = new_idtree;
3307 [ + - ]: 53 : if (handle->kvs) {
3308 : 53 : new_handle.seqtrie = (struct hbtrie *)new_seqtree;
3309 : : } else {
3310 : 0 : new_handle.seqtree = new_seqtree;
3311 : : }
3312 : 53 : new_handle.dhandle = new_dhandle;
3313 : 53 : new_handle.bhandle = new_bhandle;
3314 : :
3315 : : offset_array_max =
3316 : 53 : handle->config.compaction_buf_maxsize / sizeof(uint64_t);
3317 : 53 : offset_array = (uint64_t*)malloc(sizeof(uint64_t) * offset_array_max);
3318 : 53 : c = count = n_moved_docs = 0;
3319 : :
3320 : 53 : hr = hbtrie_iterator_init(handle->trie, &it, NULL, 0);
3321 : :
3322 [ + + ]: 1880552 : while( hr != HBTRIE_RESULT_FAIL ) {
3323 : :
3324 : 1880499 : hr = hbtrie_next_value_only(&it, (void*)&offset);
3325 : 1880499 : fs = btreeblk_end(handle->bhandle);
3326 [ - + ]: 1880499 : if (fs != FDB_RESULT_SUCCESS) {
3327 : 0 : hbtrie_iterator_free(&it);
3328 : 0 : free(offset_array);
3329 : 0 : return fs;
3330 : : }
3331 : 1880499 : offset = _endian_decode(offset);
3332 : :
3333 [ + + ]: 1880499 : if ( hr != HBTRIE_RESULT_FAIL ) {
3334 : : // add to offset array
3335 : 1880446 : offset_array[c] = offset;
3336 : 1880446 : c++;
3337 : : }
3338 : :
3339 : : // if array exceeds the threshold, OR
3340 : : // there's no next item (hr == HBTRIE_RESULT_FAIL),
3341 : : // sort and move the documents in the array
3342 [ + - ][ + - ]: 1880499 : if (c >= offset_array_max ||
[ + + ]
3343 : : (c > 0 && hr == HBTRIE_RESULT_FAIL)) {
3344 : : // quick sort
3345 : 53 : qsort(offset_array, c, sizeof(uint64_t), _fdb_cmp_uint64_t);
3346 : :
3347 [ + + ]: 14779 : for (i=0; i<c; i+=FDB_COMPACTION_BATCHSIZE) {
3348 [ + + ]: 1895172 : for(j=i; j<MIN(c, i+FDB_COMPACTION_BATCHSIZE); ++j){
3349 : 1880446 : offset = offset_array[j];
3350 : :
3351 : 1880446 : doc[j-i].key = NULL;
3352 : 1880446 : doc[j-i].meta = NULL;
3353 : 1880446 : doc[j-i].body = NULL;
3354 : 1880446 : docio_read_doc(handle->dhandle, offset, &doc[j-i]);
3355 : : }
3356 : :
3357 : 14726 : filemgr_mutex_lock(new_file);
3358 [ + + ]: 1895172 : for(j=i; j<MIN(c, i+FDB_COMPACTION_BATCHSIZE); ++j){
3359 : : // compare timestamp
3360 : 1880446 : deleted = doc[j-i].length.flag & DOCIO_DELETED;
3361 [ + + ][ + + ]: 1880446 : if (!deleted ||
[ + - ]
3362 : : (cur_timestamp < doc[j-i].timestamp +
3363 : : handle->config.purging_interval &&
3364 : : deleted)) {
3365 : : // re-write the document to new file when
3366 : : // 1. the document is not deleted
3367 : : // 2. the document is logically deleted but
3368 : : // its timestamp isn't overdue
3369 : 1880340 : new_offset = docio_append_doc(new_dhandle, &doc[j-i],
3370 : 1880340 : deleted, 0);
3371 : :
3372 : 1880340 : wal_doc.keylen = doc[j-i].length.keylen;
3373 : 1880340 : wal_doc.metalen = doc[j-i].length.metalen;
3374 : 1880340 : wal_doc.bodylen = doc[j-i].length.bodylen;
3375 : 1880340 : wal_doc.key = doc[j-i].key;
3376 : 1880340 : wal_doc.seqnum = doc[j-i].seqnum;
3377 : :
3378 : 1880340 : wal_doc.meta = doc[j-i].meta;
3379 : 1880340 : wal_doc.body = doc[j-i].body;
3380 : 1880340 : wal_doc.size_ondisk= _fdb_get_docsize(doc[j-i].length);
3381 : 1880340 : wal_doc.deleted = deleted;
3382 : :
3383 : : wal_insert_by_compactor(&new_file->global_txn,
3384 : 1880340 : new_file, &wal_doc, new_offset);
3385 : 1880340 : n_moved_docs++;
3386 : :
3387 : : }
3388 : 1880446 : free(doc[j-i].key);
3389 : 1880446 : free(doc[j-i].meta);
3390 : 1880446 : free(doc[j-i].body);
3391 : : }
3392 : 14726 : filemgr_mutex_unlock(new_file);
3393 : : }
3394 : : // reset to zero
3395 : 53 : c=0;
3396 : 53 : count++;
3397 : :
3398 : : // wal flush
3399 [ + + ]: 53 : if (wal_get_num_flushable(new_file) > 0) {
3400 : : struct avl_tree flush_items;
3401 : : wal_flush_by_compactor(new_file, (void*)&new_handle,
3402 : : _fdb_wal_flush_func,
3403 : : _fdb_wal_get_old_offset,
3404 : 52 : &flush_items);
3405 : 52 : wal_set_dirty_status(new_file, FDB_WAL_PENDING);
3406 : 52 : wal_release_flushed_items(new_file, &flush_items);
3407 : 52 : n_moved_docs = 0;
3408 : : }
3409 : : }
3410 : : }
3411 : :
3412 : 53 : hbtrie_iterator_free(&it);
3413 : 53 : free(offset_array);
3414 : 53 : return fs;
3415 : : }
3416 : :
3417 : 12 : static uint64_t _fdb_doc_move(void *dbhandle,
3418 : : void *void_new_dhandle,
3419 : : struct wal_item *item,
3420 : : fdb_doc *fdoc)
3421 : : {
3422 : : uint8_t deleted;
3423 : : uint64_t new_offset;
3424 : 12 : fdb_kvs_handle *handle = (fdb_kvs_handle*)dbhandle;
3425 : 12 : struct docio_handle *new_dhandle = (struct docio_handle*)void_new_dhandle;
3426 : : struct docio_object doc;
3427 : :
3428 : : // read doc from old file
3429 : 12 : doc.key = NULL;
3430 : 12 : doc.meta = NULL;
3431 : 12 : doc.body = NULL;
3432 : 12 : docio_read_doc(handle->dhandle, item->offset, &doc);
3433 : :
3434 : : // append doc into new file
3435 : 12 : deleted = doc.length.flag & DOCIO_DELETED;
3436 : 12 : fdoc->keylen = doc.length.keylen;
3437 : 12 : fdoc->metalen = doc.length.metalen;
3438 : 12 : fdoc->bodylen = doc.length.bodylen;
3439 : 12 : fdoc->key = doc.key;
3440 : 12 : fdoc->seqnum = doc.seqnum;
3441 : :
3442 : 12 : fdoc->meta = doc.meta;
3443 : 12 : fdoc->body = doc.body;
3444 : 12 : fdoc->size_ondisk= _fdb_get_docsize(doc.length);
3445 : 12 : fdoc->deleted = deleted;
3446 : :
3447 : 12 : new_offset = docio_append_doc(new_dhandle, &doc, deleted, 1);
3448 : 12 : return new_offset;
3449 : : }
3450 : :
3451 : 53 : fdb_status fdb_compact_file(fdb_file_handle *fhandle,
3452 : : const char *new_filename,
3453 : : bool in_place_compaction)
3454 : : {
3455 : : struct filemgr *new_file, *old_file;
3456 : : struct filemgr_config fconfig;
3457 : : struct btreeblk_handle *new_bhandle;
3458 : : struct docio_handle *new_dhandle;
3459 : 53 : struct hbtrie *new_trie = NULL;
3460 : 53 : struct btree *new_idtree = NULL;
3461 : 53 : struct btree *new_seqtree = NULL, *old_seqtree;
3462 : 53 : struct hbtrie *new_seqtrie = NULL;
3463 : : struct avl_tree flush_items;
3464 : 53 : char *old_filename = NULL;
3465 : 53 : size_t old_filename_len = 0;
3466 : 53 : fdb_kvs_handle *handle = fhandle->root;
3467 : : fdb_seqnum_t seqnum;
3468 : :
3469 : : // prevent update to the target file
3470 : 53 : filemgr_mutex_lock(handle->file);
3471 : :
3472 : : // if the file is already compacted by other thread
3473 [ + - ][ + - ]: 53 : if (filemgr_get_file_status(handle->file) != FILE_NORMAL ||
[ - + ][ - + ]
3474 : : handle->new_file || handle->file->new_file) {
3475 : : // update handle and return
3476 : 0 : filemgr_mutex_unlock(handle->file);
3477 : 0 : fdb_check_file_reopen(handle);
3478 : 0 : fdb_link_new_file(handle);
3479 : 0 : fdb_sync_db_header(handle);
3480 : :
3481 : 0 : return FDB_RESULT_COMPACTION_FAIL;
3482 : : }
3483 : :
3484 [ + - ]: 53 : if (handle->kvs) {
3485 [ - + ]: 53 : if (handle->kvs->type == KVS_SUB) {
3486 : : // deny compaction on sub handle
3487 : 0 : filemgr_mutex_unlock(handle->file);
3488 : 0 : return FDB_RESULT_INVALID_HANDLE;
3489 : : }
3490 : : }
3491 : :
3492 : : // invalid filename
3493 [ - + ]: 53 : if (!new_filename) {
3494 : 0 : filemgr_mutex_unlock(handle->file);
3495 : 0 : return FDB_RESULT_INVALID_ARGS;
3496 : : }
3497 [ - + ]: 53 : if (strlen(new_filename) > FDB_MAX_FILENAME_LEN - 8) {
3498 : 0 : filemgr_mutex_unlock(handle->file);
3499 : 0 : return FDB_RESULT_TOO_LONG_FILENAME;
3500 : : }
3501 [ - + ]: 53 : if (!strcmp(new_filename, handle->file->filename)) {
3502 : 0 : filemgr_mutex_unlock(handle->file);
3503 : 0 : return FDB_RESULT_INVALID_ARGS;
3504 : : }
3505 [ - + ]: 53 : if (filemgr_is_rollback_on(handle->file)) {
3506 : 0 : filemgr_mutex_unlock(handle->file);
3507 : 0 : return FDB_RESULT_FAIL_BY_ROLLBACK;
3508 : : }
3509 : :
3510 : : // sync handle
3511 : 53 : fdb_sync_db_header(handle);
3512 : :
3513 : : // set filemgr configuration
3514 : 53 : fconfig.blocksize = handle->config.blocksize;
3515 : 53 : fconfig.ncacheblock = handle->config.buffercache_size / handle->config.blocksize;
3516 : 53 : fconfig.options = FILEMGR_CREATE;
3517 : 53 : fconfig.flag = 0x0;
3518 [ - + ]: 53 : if (handle->config.durability_opt & FDB_DRB_ODIRECT) {
3519 : 0 : fconfig.flag |= _ARCH_O_DIRECT;
3520 : : }
3521 [ + - ]: 53 : if (!(handle->config.durability_opt & FDB_DRB_ASYNC)) {
3522 : 53 : fconfig.options |= FILEMGR_SYNC;
3523 : : }
3524 : :
3525 : : // open new file
3526 : : filemgr_open_result result = filemgr_open((char *)new_filename,
3527 : : handle->fileops,
3528 : : &fconfig,
3529 : 53 : &handle->log_callback);
3530 [ - + ]: 53 : if (result.rv != FDB_RESULT_SUCCESS) {
3531 : 0 : filemgr_mutex_unlock(handle->file);
3532 : 0 : return (fdb_status) result.rv;
3533 : : }
3534 : :
3535 : 53 : new_file = result.file;
3536 [ - + ]: 53 : assert(new_file);
3537 : :
3538 : 53 : filemgr_set_in_place_compaction(new_file, in_place_compaction);
3539 : : // prevent update to the new_file
3540 : 53 : filemgr_mutex_lock(new_file);
3541 : :
3542 : : // sync dirty root nodes
3543 : : bid_t dirty_idtree_root, dirty_seqtree_root;
3544 : 53 : filemgr_get_dirty_root(handle->file, &dirty_idtree_root, &dirty_seqtree_root);
3545 [ + + ]: 53 : if (dirty_idtree_root != BLK_NOT_FOUND) {
3546 : 1 : handle->trie->root_bid = dirty_idtree_root;
3547 : : }
3548 [ + - ][ + + ]: 53 : if (handle->config.seqtree_opt == FDB_SEQTREE_USE &&
3549 : : dirty_seqtree_root != BLK_NOT_FOUND) {
3550 : 1 : handle->seqtree->root_bid = dirty_seqtree_root;
3551 : : }
3552 : :
3553 : : // create new hb-trie and related handles
3554 : 53 : new_bhandle = (struct btreeblk_handle *)calloc(1, sizeof(struct btreeblk_handle));
3555 : 53 : new_bhandle->log_callback = &handle->log_callback;
3556 : 53 : new_dhandle = (struct docio_handle *)calloc(1, sizeof(struct docio_handle));
3557 : 53 : new_dhandle->log_callback = &handle->log_callback;
3558 : :
3559 : 53 : docio_init(new_dhandle, new_file, handle->config.compress_document_body);
3560 : 53 : btreeblk_init(new_bhandle, new_file, new_file->blocksize);
3561 : :
3562 : 53 : new_trie = (struct hbtrie *)malloc(sizeof(struct hbtrie));
3563 : : hbtrie_init(new_trie, handle->trie->chunksize, handle->trie->valuelen,
3564 : : new_file->blocksize, BLK_NOT_FOUND,
3565 : : (void *)new_bhandle, handle->btreeblkops,
3566 : 53 : (void*)new_dhandle, _fdb_readkey_wrap);
3567 : :
3568 : 53 : hbtrie_set_leaf_cmp(new_trie, _fdb_custom_cmp_wrap);
3569 : : // set aux
3570 : 53 : new_trie->aux = handle->trie->aux;
3571 : 53 : new_trie->flag = handle->trie->flag;
3572 : 53 : new_trie->leaf_height_limit = handle->trie->leaf_height_limit;
3573 : 53 : new_trie->map = handle->trie->map;
3574 : :
3575 [ + - ]: 53 : if (handle->config.seqtree_opt == FDB_SEQTREE_USE) {
3576 : : // if we use sequence number tree
3577 [ + - ]: 53 : if (handle->kvs) { // multi KV instance mode
3578 : 53 : new_seqtrie = (struct hbtrie *)calloc(1, sizeof(struct hbtrie));
3579 : :
3580 : : hbtrie_init(new_seqtrie, sizeof(fdb_kvs_id_t),
3581 : : OFFSET_SIZE, new_file->blocksize, BLK_NOT_FOUND,
3582 : : (void *)new_bhandle, handle->btreeblkops,
3583 : 53 : (void *)new_dhandle, _fdb_readseq_wrap);
3584 : : } else {
3585 : 0 : new_seqtree = (struct btree *)calloc(1, sizeof(struct btree));
3586 : 0 : old_seqtree = handle->seqtree;
3587 : :
3588 : : btree_init(new_seqtree, (void *)new_bhandle,
3589 : : old_seqtree->blk_ops, old_seqtree->kv_ops,
3590 : : old_seqtree->blksize, old_seqtree->ksize,
3591 : 0 : old_seqtree->vsize, 0x0, NULL);
3592 : : }
3593 : : // copy old file's seqnum to new file
3594 : : // (KV instances' seq numbers will be copied along with KV header)
3595 : 53 : seqnum = filemgr_get_seqnum(handle->file);
3596 : 53 : filemgr_set_seqnum(new_file, seqnum);
3597 : : }
3598 : :
3599 [ + - ]: 53 : if (handle->kvs) {
3600 : : // multi KV instance mode .. copy KV header data to new file
3601 : 53 : fdb_kvs_header_copy(handle, new_file, new_dhandle);
3602 : : }
3603 : :
3604 : : // flush WAL and set DB header
3605 : 53 : wal_commit(&handle->file->global_txn, handle->file, NULL);
3606 : : wal_flush(handle->file, (void*)handle,
3607 : 53 : _fdb_wal_flush_func, _fdb_wal_get_old_offset, &flush_items);
3608 : 53 : wal_set_dirty_status(handle->file, FDB_WAL_CLEAN);
3609 : :
3610 : : // migrate uncommitted transaction items to new file
3611 : : wal_txn_migration((void*)handle, (void*)new_dhandle,
3612 : 53 : handle->file, new_file, _fdb_doc_move);
3613 : :
3614 : : // mark name of new file in old file
3615 : 53 : filemgr_set_compaction_old(handle->file, new_file);
3616 : :
3617 : 53 : handle->last_hdr_bid = (handle->file->pos) / handle->file->blocksize;
3618 : 53 : handle->last_wal_flush_hdr_bid = handle->last_hdr_bid;
3619 : :
3620 : 53 : handle->cur_header_revnum = fdb_set_file_header(handle);
3621 : 53 : btreeblk_end(handle->bhandle);
3622 : :
3623 : : // Commit the current file handle to record the compaction filename
3624 : 53 : fdb_status fs = filemgr_commit(handle->file, &handle->log_callback);
3625 : 53 : wal_release_flushed_items(handle->file, &flush_items);
3626 [ - + ]: 53 : if (fs != FDB_RESULT_SUCCESS) {
3627 : 0 : filemgr_mutex_unlock(handle->file);
3628 : 0 : filemgr_mutex_unlock(new_file);
3629 : 0 : return fs;
3630 : : }
3631 : :
3632 : : // reset last_wal_flush_hdr_bid
3633 : 53 : handle->last_wal_flush_hdr_bid = BLK_NOT_FOUND;
3634 : :
3635 : : // Mark new file as newly compacted
3636 : 53 : filemgr_update_file_status(new_file, FILE_COMPACT_NEW, NULL);
3637 : 53 : filemgr_mutex_unlock(handle->file);
3638 : 53 : filemgr_mutex_unlock(new_file);
3639 : : // now compactor & another writer can be interleaved
3640 : :
3641 [ + - ]: 53 : if (handle->kvs) {
3642 : : _fdb_compact_move_docs(handle, new_file, new_trie, new_idtree,
3643 : : (struct btree*)new_seqtrie, new_dhandle,
3644 : 53 : new_bhandle);
3645 : : } else {
3646 : : _fdb_compact_move_docs(handle, new_file, new_trie, new_idtree, new_seqtree,
3647 : 0 : new_dhandle, new_bhandle);
3648 : : }
3649 : :
3650 : 53 : filemgr_mutex_lock(new_file);
3651 : :
3652 : 53 : old_file = handle->file;
3653 : 53 : compactor_switch_file(old_file, new_file);
3654 : 53 : handle->file = new_file;
3655 : :
3656 : 53 : btreeblk_free(handle->bhandle);
3657 : 53 : free(handle->bhandle);
3658 : 53 : handle->bhandle = new_bhandle;
3659 : :
3660 : 53 : docio_free(handle->dhandle);
3661 : 53 : free(handle->dhandle);
3662 : 53 : handle->dhandle = new_dhandle;
3663 : :
3664 : 53 : hbtrie_free(handle->trie);
3665 : 53 : free(handle->trie);
3666 : 53 : handle->trie = new_trie;
3667 : :
3668 [ + - ]: 53 : if (handle->config.seqtree_opt == FDB_SEQTREE_USE) {
3669 [ + - ]: 53 : if (handle->kvs) {
3670 : 53 : hbtrie_free(handle->seqtrie);
3671 : 53 : free(handle->seqtrie);
3672 : 53 : handle->seqtrie = new_seqtrie;
3673 : : } else {
3674 : 0 : free(handle->seqtree);
3675 : 0 : handle->seqtree = new_seqtree;
3676 : : }
3677 : : }
3678 : :
3679 : 53 : old_filename_len = strlen(old_file->filename) + 1;
3680 : 53 : old_filename = (char *) malloc(old_filename_len);
3681 : 53 : strncpy(old_filename, old_file->filename, old_filename_len);
3682 : 53 : filemgr_update_file_status(new_file, FILE_NORMAL, old_filename);
3683 : :
3684 : : // allow update to new_file
3685 : 53 : filemgr_mutex_unlock(new_file);
3686 : :
3687 : : // atomically perform
3688 : : // 1) commit new file
3689 : : // 2) set remove pending flag of the old file
3690 : : // 3) close the old file
3691 : 53 : return _fdb_commit_and_remove_pending(handle, old_file, new_file);
3692 : : }
3693 : :
3694 : : LIBFDB_API
3695 : 40 : fdb_status fdb_compact(fdb_file_handle *fhandle,
3696 : : const char *new_filename)
3697 : : {
3698 : 40 : fdb_kvs_handle *handle = fhandle->root;
3699 : :
3700 [ + + ]: 40 : if (handle->config.compaction_mode == FDB_COMPACTION_MANUAL) {
3701 : : // manual compaction
3702 : 39 : bool in_place_compaction = false;
3703 : : char nextfile[FDB_MAX_FILENAME_LEN];
3704 [ + + ]: 39 : if (!new_filename) { // In-place compaction.
3705 : 6 : in_place_compaction = true;
3706 : 6 : compactor_get_next_filename(handle->file->filename, nextfile);
3707 : 6 : new_filename = nextfile;
3708 : : }
3709 : 39 : return fdb_compact_file(fhandle, new_filename, in_place_compaction);
3710 : :
3711 [ + - ]: 1 : } else if (handle->config.compaction_mode == FDB_COMPACTION_AUTO) {
3712 : : // auto compaction
3713 : : bool ret;
3714 : : char nextfile[FDB_MAX_FILENAME_LEN];
3715 : : fdb_status fs;
3716 : : // set compaction flag
3717 : 1 : ret = compactor_switch_compaction_flag(handle->file, true);
3718 [ - + ]: 1 : if (!ret) {
3719 : : // the file is already being compacted by other thread
3720 : 0 : return FDB_RESULT_FILE_IS_BUSY;
3721 : : }
3722 : : // get next filename
3723 : 1 : compactor_get_next_filename(handle->file->filename, nextfile);
3724 : 1 : fs = fdb_compact_file(fhandle, nextfile, false);
3725 [ + - ]: 1 : if (fs == FDB_RESULT_SUCCESS) {
3726 : : // compaction succeeded
3727 : : // clear compaction flag
3728 : 1 : ret = compactor_switch_compaction_flag(handle->file, false);
3729 : : (void)ret;
3730 : 1 : return fs;
3731 : : }
3732 : : // compaction failed
3733 : : // clear compaction flag
3734 : 0 : ret = compactor_switch_compaction_flag(handle->file, false);
3735 : : (void)ret;
3736 : 0 : return fs;
3737 : : }
3738 : 40 : return FDB_RESULT_MANUAL_COMPACTION_FAIL;
3739 : : }
3740 : :
3741 : : LIBFDB_API
3742 : 4 : fdb_status fdb_switch_compaction_mode(fdb_file_handle *fhandle,
3743 : : fdb_compaction_mode_t mode,
3744 : : size_t new_threshold)
3745 : : {
3746 : : int ret;
3747 : : fdb_status fs;
3748 : 4 : fdb_kvs_handle *handle = fhandle->root;
3749 : : fdb_config config;
3750 : : char vfilename[FDB_MAX_FILENAME_LEN];
3751 : : char filename[FDB_MAX_FILENAME_LEN];
3752 : : char metafile[FDB_MAX_FILENAME_LEN];
3753 : :
3754 [ + - ][ - + ]: 4 : if (!handle || new_threshold > 100) {
3755 : 0 : return FDB_RESULT_INVALID_ARGS;
3756 : : }
3757 : :
3758 : 4 : config = handle->config;
3759 [ + + ]: 4 : if (handle->config.compaction_mode != mode) {
3760 [ + + ]: 3 : if (filemgr_get_ref_count(handle->file) > 1) {
3761 : : // all the other handles referring this file should be closed
3762 : 1 : return FDB_RESULT_FILE_IS_BUSY;
3763 : : }
3764 : : /* TODO: In current code, we assume that all the other handles referring
3765 : : * the same database file should be closed before calling this API and
3766 : : * any open API calls should not be made until the completion of this API.
3767 : : */
3768 : :
3769 [ + + ]: 2 : if (handle->config.compaction_mode == FDB_COMPACTION_AUTO) {
3770 : : // 1. deregieter from compactor (by calling fdb_close)
3771 : : // 2. remove [filename].meta
3772 : : // 3. rename [filename].[n] as [filename]
3773 : :
3774 : : // set compaction flag to avoid auto compaction.
3775 : : // we will not clear this flag again becuase this file will be
3776 : : // deregistered by calling _fdb_close().
3777 [ - + ]: 1 : if (compactor_switch_compaction_flag(handle->file, true) == false) {
3778 : 0 : return FDB_RESULT_FILE_IS_BUSY;
3779 : : }
3780 : :
3781 : 1 : strcpy(vfilename, handle->filename);
3782 : 1 : strcpy(filename, handle->file->filename);
3783 : 1 : fs = _fdb_close(handle);
3784 [ - + ]: 1 : if (fs != FDB_RESULT_SUCCESS) {
3785 : 0 : return fs;
3786 : : }
3787 : 1 : sprintf(metafile, "%s.meta", vfilename);
3788 [ - + ]: 1 : if ((ret = remove(metafile)) < 0) {
3789 : 0 : return FDB_RESULT_FILE_REMOVE_FAIL;
3790 : : }
3791 [ - + ]: 1 : if ((ret = rename(filename, vfilename)) < 0) {
3792 : 0 : return FDB_RESULT_FILE_RENAME_FAIL;
3793 : : }
3794 : 1 : config.compaction_mode = FDB_COMPACTION_MANUAL;
3795 : 1 : fs = _fdb_open(handle, vfilename, &config);
3796 [ - + ]: 1 : if (fs != FDB_RESULT_SUCCESS) {
3797 : 0 : return fs;
3798 : : }
3799 [ + - ]: 1 : } else if (handle->config.compaction_mode == FDB_COMPACTION_MANUAL) {
3800 : : // 1. rename [filename] as [filename].rev_num
3801 : 1 : strcpy(vfilename, handle->file->filename);
3802 : 1 : compactor_get_next_filename(handle->file->filename, filename);
3803 : 1 : fs = _fdb_close(handle);
3804 [ - + ]: 1 : if (fs != FDB_RESULT_SUCCESS) {
3805 : 0 : return fs;
3806 : : }
3807 [ - + ]: 1 : if ((ret = rename(vfilename, filename) < 0)) {
3808 : 0 : return FDB_RESULT_FILE_RENAME_FAIL;
3809 : : }
3810 : 1 : config.compaction_mode = FDB_COMPACTION_AUTO;
3811 : 1 : config.compaction_threshold = new_threshold;
3812 : 1 : fs = _fdb_open(handle, vfilename, &config);
3813 [ - + ]: 1 : if (fs != FDB_RESULT_SUCCESS) {
3814 : 0 : return fs;
3815 : : }
3816 : :
3817 : : } else {
3818 : 0 : return FDB_RESULT_INVALID_ARGS;
3819 : : }
3820 : : } else {
3821 [ + - ]: 1 : if (handle->config.compaction_mode == FDB_COMPACTION_AUTO) {
3822 : : // change compaction threshold of the existing file
3823 : 1 : compactor_change_threshold(handle->file, new_threshold);
3824 : : }
3825 : : }
3826 : 4 : return FDB_RESULT_SUCCESS;
3827 : : }
3828 : :
3829 : : LIBFDB_API
3830 : 274 : fdb_status fdb_close(fdb_file_handle *fhandle)
3831 : : {
3832 : : fdb_status fs;
3833 : :
3834 [ + + + - ]: 276 : if (fhandle->root->config.auto_commit &&
[ + + ]
3835 : 2 : filemgr_get_ref_count(fhandle->root->file) == 1) {
3836 : : // auto commit mode & the last handle referring the file
3837 : : // commit file before close
3838 : 2 : fs = fdb_commit(fhandle, FDB_COMMIT_NORMAL);
3839 [ - + ]: 2 : if (fs != FDB_RESULT_SUCCESS) {
3840 : 0 : return fs;
3841 : : }
3842 : : }
3843 : :
3844 : 274 : fs = _fdb_close_root(fhandle->root);
3845 [ + - ]: 274 : if (fs == FDB_RESULT_SUCCESS) {
3846 : 274 : fdb_file_handle_close_all(fhandle);
3847 : 274 : fdb_file_handle_free(fhandle);
3848 : : }
3849 : 274 : return fs;
3850 : : }
3851 : :
3852 : 275 : fdb_status _fdb_close_root(fdb_kvs_handle *handle)
3853 : : {
3854 : : fdb_status fs;
3855 : :
3856 [ - + ]: 275 : if (!handle) {
3857 : 0 : return FDB_RESULT_SUCCESS;
3858 : : }
3859 [ + + ]: 275 : if (handle->kvs) {
3860 [ - + ]: 270 : if (handle->kvs->type == KVS_SUB) {
3861 : 0 : return fdb_kvs_close(handle);
3862 [ + - ]: 270 : } else if (handle->kvs->type == KVS_ROOT) {
3863 : : // close all sub-handles
3864 : 270 : fs = fdb_kvs_close_all(handle);
3865 [ - + ]: 270 : if (fs != FDB_RESULT_SUCCESS) {
3866 : 0 : return fs;
3867 : : }
3868 : : }
3869 : : }
3870 [ + + ]: 275 : if (handle->txn) {
3871 : 2 : _fdb_abort_transaction(handle);
3872 : : }
3873 : :
3874 : 275 : fs = _fdb_close(handle);
3875 [ + - ]: 275 : if (fs == FDB_RESULT_SUCCESS) {
3876 : 275 : fdb_kvs_info_free(handle);
3877 : 275 : free(handle);
3878 : : }
3879 : 275 : return fs;
3880 : : }
3881 : :
3882 : 517 : fdb_status _fdb_close(fdb_kvs_handle *handle)
3883 : : {
3884 : : fdb_status fs;
3885 [ + + ][ + + ]: 517 : if (!(handle->config.flags & FDB_OPEN_FLAG_RDONLY) &&
3886 : : handle->config.compaction_mode == FDB_COMPACTION_AUTO) {
3887 : : // read-only file is not registered in compactor
3888 : 34 : compactor_deregister_file(handle->file);
3889 : : }
3890 : :
3891 : 517 : btreeblk_end(handle->bhandle);
3892 : 517 : btreeblk_free(handle->bhandle);
3893 : :
3894 : : fs = filemgr_close(handle->file, handle->config.cleanup_cache_onclose,
3895 : 517 : handle->filename, &handle->log_callback);
3896 [ - + ]: 517 : if (fs != FDB_RESULT_SUCCESS) {
3897 : 0 : return fs;
3898 : : }
3899 : 517 : docio_free(handle->dhandle);
3900 [ + + ]: 517 : if (handle->new_file) {
3901 : : fs = filemgr_close(handle->new_file,
3902 : : handle->config.cleanup_cache_onclose,
3903 : 13 : handle->filename, &handle->log_callback);
3904 [ - + ]: 13 : if (fs != FDB_RESULT_SUCCESS) {
3905 : 0 : return fs;
3906 : : }
3907 : 13 : docio_free(handle->new_dhandle);
3908 : 13 : free(handle->new_dhandle);
3909 : 13 : handle->new_file = NULL;
3910 : 13 : handle->new_dhandle = NULL;
3911 : : }
3912 : :
3913 : 517 : hbtrie_free(handle->trie);
3914 : 517 : free(handle->trie);
3915 : :
3916 [ + + ]: 517 : if (handle->config.seqtree_opt == FDB_SEQTREE_USE) {
3917 [ + + ]: 515 : if (handle->kvs) {
3918 : : // multi KV instance mode
3919 : 510 : hbtrie_free(handle->seqtrie);
3920 : 510 : free(handle->seqtrie);
3921 : : } else {
3922 : 5 : free(handle->seqtree->kv_ops);
3923 : 5 : free(handle->seqtree);
3924 : : }
3925 : : }
3926 : :
3927 : 517 : free(handle->bhandle);
3928 : 517 : free(handle->dhandle);
3929 [ + + ]: 517 : if (handle->shandle) {
3930 : 34 : snap_close(handle->shandle);
3931 : : }
3932 [ + - ]: 517 : if (handle->filename) {
3933 : 517 : free(handle->filename);
3934 : 517 : handle->filename = NULL;
3935 : : }
3936 : 517 : return fs;
3937 : : }
3938 : :
3939 : : LIBFDB_API
3940 : 7 : fdb_status fdb_destroy(const char *fname,
3941 : : fdb_config *fdbconfig)
3942 : : {
3943 : : #ifdef _MEMPOOL
3944 : : mempool_init();
3945 : : #endif
3946 : :
3947 : : fdb_config config;
3948 : : struct filemgr_config fconfig;
3949 : 7 : fdb_status status = FDB_RESULT_SUCCESS;
3950 : 7 : char *filename = (char *)alca(uint8_t, FDB_MAX_FILENAME_LEN);
3951 : :
3952 [ + - ]: 7 : if (fdbconfig) {
3953 [ + - ]: 7 : if (validate_fdb_config(fdbconfig)) {
3954 : 7 : config = *fdbconfig;
3955 : : } else {
3956 : 0 : return FDB_RESULT_INVALID_CONFIG;
3957 : : }
3958 : : } else {
3959 : 0 : config = get_default_config();
3960 : : }
3961 : :
3962 : 7 : strncpy(filename, fname, FDB_MAX_FILENAME_LEN);
3963 : :
3964 [ + + ]: 7 : if (!compactor_is_valid_mode(filename, &config)) {
3965 : 2 : status = FDB_RESULT_INVALID_COMPACTION_MODE;
3966 : 2 : return status;
3967 : : }
3968 : :
3969 : 5 : _fdb_init_file_config(&config, &fconfig);
3970 : :
3971 : 5 : filemgr_mutex_openlock(&fconfig);
3972 : :
3973 : 5 : status = filemgr_destroy_file(filename, &fconfig, NULL);
3974 [ + + ]: 5 : if (status != FDB_RESULT_SUCCESS) {
3975 : 2 : filemgr_mutex_openunlock();
3976 : 2 : return status;
3977 : : }
3978 : :
3979 [ + + ]: 3 : if (config.compaction_mode == FDB_COMPACTION_AUTO) {
3980 : 1 : status = compactor_destroy_file(filename, &config);
3981 [ - + ]: 1 : if (status != FDB_RESULT_SUCCESS) {
3982 : 0 : filemgr_mutex_openunlock();
3983 : 0 : return status;
3984 : : }
3985 : : }
3986 : :
3987 : 3 : filemgr_mutex_openunlock();
3988 : :
3989 : 7 : return status;
3990 : : }
3991 : :
3992 : : // roughly estimate the space occupied db handle HANDLE
3993 : : LIBFDB_API
3994 : 1091 : size_t fdb_estimate_space_used(fdb_file_handle *fhandle)
3995 : : {
3996 : 1091 : size_t ret = 0;
3997 : : size_t datasize;
3998 : : size_t nlivenodes;
3999 : 1091 : fdb_kvs_handle *handle = NULL;
4000 : : struct filemgr *file;
4001 : :
4002 [ - + ]: 1091 : if (!fhandle) {
4003 : 0 : return FDB_RESULT_INVALID_ARGS;
4004 : : }
4005 : :
4006 : 1091 : handle = fhandle->root;
4007 : :
4008 : 1091 : fdb_check_file_reopen(handle);
4009 : 1091 : fdb_link_new_file(handle);
4010 : 1091 : fdb_sync_db_header(handle);
4011 : :
4012 [ + + ]: 1091 : file = (handle->new_file)?(handle->new_file):(handle->file);
4013 : :
4014 : 1091 : datasize = _kvs_stat_get_sum(file, KVS_STAT_DATASIZE);
4015 : 1091 : nlivenodes = _kvs_stat_get_sum(file, KVS_STAT_NLIVENODES);
4016 : :
4017 : 1091 : ret = datasize;
4018 : 1091 : ret += nlivenodes * handle->config.blocksize;
4019 : 1091 : ret += wal_get_datasize(handle->file);
4020 : :
4021 : 1091 : return ret;
4022 : : }
4023 : :
4024 : : LIBFDB_API
4025 : 1091 : fdb_status fdb_get_file_info(fdb_file_handle *fhandle, fdb_file_info *info)
4026 : : {
4027 : : uint64_t ndocs;
4028 : : fdb_kvs_handle *handle;
4029 : :
4030 [ + - ][ - + ]: 1091 : if (!fhandle || !info) {
4031 : 0 : return FDB_RESULT_INVALID_ARGS;
4032 : : }
4033 : 1091 : handle = fhandle->root;
4034 : :
4035 : 1091 : fdb_check_file_reopen(handle);
4036 : 1091 : fdb_link_new_file(handle);
4037 : 1091 : fdb_sync_db_header(handle);
4038 : :
4039 [ + + ]: 1091 : if (handle->config.compaction_mode == FDB_COMPACTION_AUTO) {
4040 : : // compaction daemon mode
4041 : 1 : info->filename = handle->filename;
4042 : : } else {
4043 : 1090 : info->filename = handle->file->filename;
4044 : : }
4045 : :
4046 [ + - ]: 1091 : if (handle->shandle) {
4047 : : // handle for snapshot
4048 : : } else {
4049 [ + + ]: 1091 : if (handle->new_file) {
4050 : 658 : info->new_filename = handle->new_file->filename;
4051 : : } else {
4052 : 433 : info->new_filename = NULL;
4053 : : }
4054 : : }
4055 : :
4056 : : // Note that doc_count includes the number of WAL entries, which might
4057 : : // incur an incorrect estimation. However, after the WAL flush, the doc
4058 : : // counter becomes consistent. We plan to devise a new way of tracking
4059 : : // the number of docs in a database instance.
4060 : 1091 : size_t wal_docs = wal_get_num_docs(handle->file);
4061 : 1091 : size_t wal_deletes = wal_get_num_deletes(handle->file);
4062 : 1091 : size_t wal_n_inserts = wal_docs - wal_deletes;
4063 : :
4064 : 1091 : ndocs = _kvs_stat_get_sum(handle->file, KVS_STAT_NDOCS);
4065 : :
4066 [ - + ]: 1091 : if (ndocs + wal_n_inserts < wal_deletes) {
4067 : 0 : info->doc_count = 0;
4068 : : } else {
4069 [ + + ]: 1091 : if (ndocs) {
4070 : 1090 : info->doc_count = ndocs + wal_n_inserts - wal_deletes;
4071 : : } else {
4072 : 1 : info->doc_count = wal_n_inserts;
4073 : : }
4074 : : }
4075 : :
4076 : 1091 : info->space_used = fdb_estimate_space_used(fhandle);
4077 : 1091 : info->file_size = filemgr_get_pos(handle->file);
4078 : :
4079 : 1091 : return FDB_RESULT_SUCCESS;
4080 : : }
4081 : :
4082 : : LIBFDB_API
4083 : 106 : fdb_status fdb_shutdown()
4084 : : {
4085 [ + - ]: 106 : if (fdb_initialized) {
4086 : 106 : spin_lock(&initial_lock);
4087 [ + + ]: 106 : if (fdb_open_inprog) {
4088 : 1 : spin_unlock(&initial_lock);
4089 : 1 : return FDB_RESULT_FILE_IS_BUSY;
4090 : : }
4091 : 105 : compactor_shutdown();
4092 : 105 : filemgr_shutdown();
4093 : : #ifdef _MEMPOOL
4094 : : mempool_shutdown();
4095 : : #endif
4096 : :
4097 : 105 : fdb_initialized = 0;
4098 : 105 : spin_unlock(&initial_lock);
4099 : : }
4100 : 106 : return FDB_RESULT_SUCCESS;
4101 : : }
|