LCOV - code coverage report
Current view: top level - src - forestdb.cc (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 1716 2036 84.3 %
Date: 2015-01-12 15:17:13 Functions: 57 57 100.0 %
Branches: 797 1132 70.4 %

           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                 :            : }

Generated by: LCOV version 1.11