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 <stdint.h>
22#include <time.h>
23#if !defined(WIN32) && !defined(_WIN32)
24#include <unistd.h>
25#include <errno.h>
26#endif
27
28#include "libforestdb/forestdb.h"
29#include "test.h"
30#include "filemgr_anomalous_ops.h"
31#include "filemgr.h"
32#include "internal_types.h"
33
34void logCallbackFunc(int err_code,
35                     const char *err_msg,
36                     void *pCtxData) {
37    fprintf(stderr, "%s - error code: %d, error message: %s\n",
38            (char *) pCtxData, err_code, err_msg);
39}
40
41// callback context for test specific data
42typedef struct fail_ctx_t {
43    int num_fails;
44    int num_ops;
45    int start_failing_after;
46} fail_ctx_t;
47
48ssize_t pwrite_failure_cb(void *ctx, struct filemgr_ops *normal_ops,
49                          int fd, void *buf, size_t count, cs_off_t offset)
50{
51    fail_ctx_t *wctx = (fail_ctx_t *)ctx;
52    wctx->num_ops++;
53    if (wctx->num_ops > wctx->start_failing_after) {
54        wctx->num_fails++;
55        errno = -2;
56        return (ssize_t)FDB_RESULT_WRITE_FAIL;
57    }
58    return normal_ops->pwrite(fd, buf, count, offset);
59}
60
61void write_failure_test()
62{
63    TEST_INIT();
64
65    memleak_start();
66
67    int i, j, idx, r;
68    int n=300, m=20; // n: # prefixes, m: # postfixes
69    int keylen_limit;
70    fdb_file_handle *dbfile;
71    fdb_kvs_handle *db;
72    fdb_doc **doc = alca(fdb_doc*, n*m);
73    fdb_doc *rdoc;
74    fdb_status status;
75    int anomaly_hit = 0;
76
77    char *keybuf;
78    char metabuf[256], bodybuf[256], temp[256];
79    // Get the default callbacks which result in normal operation for other ops
80    struct anomalous_callbacks *write_fail_cb = get_default_anon_cbs();
81    fail_ctx_t fail_ctx;
82    memset(&fail_ctx, 0, sizeof(fail_ctx_t));
83    // Modify the pwrite callback to redirect to test-specific function
84    write_fail_cb->pwrite_cb = &pwrite_failure_cb;
85
86    // remove previous anomaly_test files
87    r = system(SHELL_DEL" anomaly_test* > errorlog.txt");
88    (void)r;
89
90    // Reset anomalous behavior stats..
91    filemgr_ops_anomalous_init(write_fail_cb, &fail_ctx);
92
93    // The number indicates the count after which all writes begin to fail
94    // This number is unique to this test suite and can cause a segmentation
95    // fault if the underlying fixed issue resurfaces
96    fail_ctx.start_failing_after = 10112;
97
98    fdb_config fconfig = fdb_get_default_config();
99    fdb_kvs_config kvs_config = fdb_get_default_kvs_config();
100    fconfig.buffercache_size = 0;
101    fconfig.flags = FDB_OPEN_FLAG_CREATE;
102    fconfig.purging_interval = 0;
103    fconfig.compaction_threshold = 0;
104
105    keylen_limit = fconfig.blocksize - 256;
106    keybuf = alca(char, keylen_limit);
107
108    // open db
109    fdb_open(&dbfile, "anomaly_test1", &fconfig);
110    fdb_kvs_open_default(dbfile, &db, &kvs_config);
111
112    // key structure:
113    // <------------ <keylen_limit> bytes ------------->
114    // <-- 8 bytes -->             <-- 8 bytes  -->< 1 >
115    // [prefix number]____ ... ____[postfix number][ \0]
116    // e.g.)
117    // 00000001____ ... ____00000013[\0]
118
119    // create docs
120    for (i=0;i<keylen_limit-1;++i){
121        keybuf[i] = '_';
122    }
123    keybuf[keylen_limit-1] = 0;
124
125    for (i=0;i<n;++i){
126        // set prefix
127        sprintf(temp, "%08d", i);
128        memcpy(keybuf, temp, 8);
129        for (j=0;j<m;++j){
130            idx = i*m + j;
131            // set postfix
132            sprintf(temp, "%08d", j);
133            memcpy(keybuf + (keylen_limit-1) - 8, temp, 8);
134            sprintf(metabuf, "meta%d", idx);
135            sprintf(bodybuf, "body%d", idx);
136            fdb_doc_create(&doc[idx], (void*)keybuf, strlen(keybuf)+1,
137                                    (void*)metabuf, strlen(metabuf)+1,
138                                    (void*)bodybuf, strlen(bodybuf)+1);
139        }
140    }
141
142    // insert docs
143    for (i=0;i<n*m;++i) {
144        fdb_set(db, doc[i]);
145    }
146    fdb_commit(dbfile, FDB_COMMIT_MANUAL_WAL_FLUSH);
147
148    // retrieval check
149    for (i=0;i<n*m;++i){
150        fdb_doc_create(&rdoc, doc[i]->key, doc[i]->keylen, NULL, 0, NULL, 0);
151        status = fdb_get(db, rdoc);
152        if (status != FDB_RESULT_SUCCESS) {
153            fdb_doc_free(rdoc);
154            anomaly_hit = 1;
155            break;
156        }
157        TEST_CHK(status == FDB_RESULT_SUCCESS);
158        TEST_CHK(!memcmp(rdoc->key, doc[i]->key, rdoc->keylen));
159        TEST_CHK(!memcmp(rdoc->meta, doc[i]->meta, rdoc->metalen));
160        TEST_CHK(!memcmp(rdoc->body, doc[i]->body, rdoc->bodylen));
161        fdb_doc_free(rdoc);
162    }
163    TEST_CHK(anomaly_hit);
164
165    fdb_close(dbfile);
166
167    // free all documents
168    for (i=0;i<n*m;++i){
169        fdb_doc_free(doc[i]);
170    }
171
172    // free all resources
173    fdb_shutdown();
174
175    memleak_end();
176
177    sprintf(temp, "write failure test: %d failures out of %d writes",
178            fail_ctx.num_fails, fail_ctx.num_ops);
179
180    TEST_RESULT(temp);
181}
182
183ssize_t pread_failure_cb(void *ctx, struct filemgr_ops *normal_ops,
184                         int fd, void *buf, size_t count, cs_off_t offset)
185{
186    fail_ctx_t *wctx = (fail_ctx_t *)ctx;
187    wctx->num_ops++;
188    if (wctx->num_ops > wctx->start_failing_after) {
189        wctx->num_fails++;
190        errno = -2;
191        return (ssize_t)FDB_RESULT_READ_FAIL;
192    }
193    return normal_ops->pread(fd, buf, count, offset);
194}
195
196void read_failure_test()
197{
198    TEST_INIT();
199
200    memleak_start();
201
202    int i, r;
203    int n=300;
204    int keylen_limit;
205    fdb_file_handle *dbfile;
206    fdb_kvs_handle *db;
207    fdb_doc **doc = alca(fdb_doc*, n);
208    fdb_doc *rdoc;
209    fdb_status status;
210
211    char *keybuf;
212    char metabuf[256], bodybuf[256], temp[256];
213    // Get the default callbacks which result in normal operation for other ops
214    struct anomalous_callbacks *read_fail_cb = get_default_anon_cbs();
215    fail_ctx_t fail_ctx;
216    memset(&fail_ctx, 0, sizeof(fail_ctx_t));
217
218    // Modify the pread callback to redirect to test-specific function
219    read_fail_cb->pread_cb = &pread_failure_cb;
220
221    // remove previous anomaly_test files
222    r = system(SHELL_DEL" anomaly_test* > errorlog.txt");
223    (void)r;
224
225    // Reset anomalous behavior stats..
226    filemgr_ops_anomalous_init(read_fail_cb, &fail_ctx);
227
228    fail_ctx.start_failing_after = 1000; // some large value
229
230    fdb_config fconfig = fdb_get_default_config();
231    fdb_kvs_config kvs_config = fdb_get_default_kvs_config();
232    fconfig.buffercache_size = 0;
233    fconfig.flags = FDB_OPEN_FLAG_CREATE;
234    fconfig.purging_interval = 0;
235    fconfig.compaction_threshold = 0;
236
237    keylen_limit = fconfig.blocksize - 256;
238    keybuf = alca(char, keylen_limit);
239
240    // open db
241    status = fdb_open(&dbfile, "./anomaly_test1", &fconfig);
242    TEST_CHK(status == FDB_RESULT_SUCCESS);
243    status = fdb_kvs_open_default(dbfile, &db, &kvs_config);
244    TEST_CHK(status == FDB_RESULT_SUCCESS);
245
246    status = fdb_set_log_callback(db, logCallbackFunc,
247                                  (void *) "read_failure_test");
248    TEST_CHK(status == FDB_RESULT_SUCCESS);
249
250    // insert documents
251    for (i=0;i<n;++i){
252        sprintf(keybuf, "key%d", i);
253        sprintf(metabuf, "meta%d", i);
254        sprintf(bodybuf, "body%d", i);
255        fdb_doc_create(&doc[i], (void*)keybuf, strlen(keybuf),
256            (void*)metabuf, strlen(metabuf), (void*)bodybuf, strlen(bodybuf));
257        status = fdb_set(db, doc[i]);
258        TEST_CHK(status == FDB_RESULT_SUCCESS);
259    }
260
261    // commit
262    status = fdb_commit(dbfile, FDB_COMMIT_NORMAL);
263    TEST_CHK(status == FDB_RESULT_SUCCESS);
264    fdb_kvs_close(db);
265    fdb_close(dbfile);
266
267    fail_ctx.start_failing_after = fail_ctx.num_ops; // immediately fail
268
269    status = fdb_open(&dbfile, "./anomaly_test1", &fconfig);
270    TEST_CHK(status == FDB_RESULT_READ_FAIL || status == FDB_RESULT_SB_READ_FAIL);
271
272    fail_ctx.start_failing_after = fail_ctx.num_ops+1000; //normal operation
273
274    status = fdb_open(&dbfile, "./anomaly_test1", &fconfig);
275    TEST_CHK(status == FDB_RESULT_SUCCESS);
276    status = fdb_kvs_open_default(dbfile, &db, &kvs_config);
277    TEST_CHK(status == FDB_RESULT_SUCCESS);
278
279    status = fdb_set_log_callback(db, logCallbackFunc,
280                                  (void *) "read_failure_test");
281    TEST_CHK(status == FDB_RESULT_SUCCESS);
282
283    fail_ctx.start_failing_after = fail_ctx.num_ops; // immediately fail
284    i = 0;
285    fdb_doc_create(&rdoc, doc[i]->key, doc[i]->keylen, NULL, 0, NULL, 0);
286    status = fdb_get(db, rdoc);
287
288    TEST_CHK(status == FDB_RESULT_READ_FAIL);
289    // free result document
290    fdb_doc_free(rdoc);
291
292    fail_ctx.start_failing_after = fail_ctx.num_ops+1000; //normal operation
293    // retrieve documents
294    for (i=0;i<n;++i){
295        // search by key
296        fdb_doc_create(&rdoc, doc[i]->key, doc[i]->keylen, NULL, 0, NULL, 0);
297        status = fdb_get(db, rdoc);
298
299        TEST_CHK(status == FDB_RESULT_SUCCESS);
300        TEST_CMP(rdoc->meta, doc[i]->meta, rdoc->metalen);
301        TEST_CMP(rdoc->body, doc[i]->body, rdoc->bodylen);
302
303        // free result document
304        fdb_doc_free(rdoc);
305    }
306
307    fdb_kvs_close(db);
308    fdb_close(dbfile);
309
310    // free all documents
311    for (i=0;i<n;++i){
312        fdb_doc_free(doc[i]);
313    }
314
315    // free all resources
316    fdb_shutdown();
317
318    memleak_end();
319
320    sprintf(temp, "read failure test: %d failures out of %d reads",
321            fail_ctx.num_fails, fail_ctx.num_ops);
322
323    TEST_RESULT(temp);
324}
325
326struct shared_data {
327    fdb_file_handle *dbfile;
328    fdb_kvs_handle *db;
329    fdb_iterator *iterator;
330    bool test_handle_busy;
331};
332
333void *bad_thread(void *voidargs) {
334    struct shared_data *data = (struct shared_data *)voidargs;
335    fdb_kvs_handle *db = data->db;
336    fdb_iterator *itr = data->iterator;
337    fdb_status s;
338    fdb_doc doc;
339    TEST_INIT();
340
341    memset(&doc, 0, sizeof(fdb_doc));
342    doc.key = &doc; // some non-null value
343    doc.keylen = 2; // some non-zero value
344    doc.body = &doc; // some non-null value
345    doc.bodylen = 2; // some non-zero value
346
347    if (!itr) {
348        // since the parent thread is hung in the fdb_set callback
349        // all the forestdb apis calls on the same handle must return failure
350        s = fdb_set(db, &doc);
351        TEST_CHK(s == FDB_RESULT_HANDLE_BUSY);
352        s = fdb_del(db, &doc);
353        TEST_CHK(s == FDB_RESULT_HANDLE_BUSY);
354        s = fdb_get(db, &doc);
355        TEST_CHK(s == FDB_RESULT_HANDLE_BUSY);
356        s = fdb_get_metaonly(db, &doc);
357        TEST_CHK(s == FDB_RESULT_HANDLE_BUSY);
358        s = fdb_get_byseq(db, &doc);
359        TEST_CHK(s == FDB_RESULT_HANDLE_BUSY);
360        s = fdb_get_metaonly_byseq(db, &doc);
361        TEST_CHK(s == FDB_RESULT_HANDLE_BUSY);
362        doc.offset = 5000; // some random non-zero value
363        s = fdb_get_byoffset(db, &doc);
364        TEST_CHK(s == FDB_RESULT_HANDLE_BUSY);
365    } else {
366        s = fdb_iterator_next(itr);
367        TEST_CHK(s == FDB_RESULT_HANDLE_BUSY);
368        s = fdb_iterator_prev(itr);
369        TEST_CHK(s == FDB_RESULT_HANDLE_BUSY);
370        s = fdb_iterator_seek_to_min(itr);
371        TEST_CHK(s == FDB_RESULT_HANDLE_BUSY);
372        s = fdb_iterator_seek_to_max(itr);
373        TEST_CHK(s == FDB_RESULT_HANDLE_BUSY);
374        s = fdb_iterator_seek(itr, doc.key, doc.keylen, 0);
375        TEST_CHK(s == FDB_RESULT_HANDLE_BUSY);
376    }
377
378    return NULL;
379}
380
381// Calling apis from a callback simulates concurrent access from multiple
382// threads
383ssize_t pwrite_hang_cb(void *ctx, struct filemgr_ops *normal_ops,
384                       int fd, void *buf, size_t count, cs_off_t offset)
385{
386    struct shared_data *data = (struct shared_data *)ctx;
387    if (data->test_handle_busy) {
388        bad_thread(ctx);
389    }
390    return normal_ops->pwrite(fd, buf, count, offset);
391}
392
393void handle_busy_test()
394{
395    TEST_INIT();
396
397    memleak_start();
398
399    int n = 32;
400    int i = 0, r;
401
402    char keybuf[16], metabuf[16], bodybuf[16];
403    fdb_doc **doc = alca(fdb_doc *, n);
404    struct shared_data data;
405    fdb_kvs_handle *db;
406    fdb_iterator *itr;
407    fdb_status status;
408
409    // Get the default callbacks which result in normal operation for other ops
410    struct anomalous_callbacks *write_hang_cb = get_default_anon_cbs();
411
412    // Modify the pwrite callback to redirect to test-specific function
413    write_hang_cb->pwrite_cb = &pwrite_hang_cb;
414
415    // remove previous anomaly_test files
416    r = system(SHELL_DEL" anomaly_test* > errorlog.txt");
417    (void)r;
418
419    memset(&data, 0, sizeof(struct shared_data));
420
421    // Create anomalous behavior with shared handle for the callback ctx
422    filemgr_ops_anomalous_init(write_hang_cb, &data);
423
424    fdb_config fconfig = fdb_get_default_config();
425    fdb_kvs_config kvs_config = fdb_get_default_kvs_config();
426    fconfig.buffercache_size = 0;
427    fconfig.seqtree_opt = FDB_SEQTREE_USE; // enable seqtree since get_byseq
428    fconfig.flags = FDB_OPEN_FLAG_CREATE;
429    fconfig.purging_interval = 0;
430    fconfig.compaction_threshold = 0;
431
432    // open db
433    status = fdb_open(&data.dbfile, "anomaly_test5", &fconfig);
434    TEST_CHK(status == FDB_RESULT_SUCCESS);
435    status = fdb_kvs_open_default(data.dbfile, &data.db, &kvs_config);
436    TEST_CHK(status == FDB_RESULT_SUCCESS);
437    db = data.db;
438
439    // insert documents
440    for (i=0;i<n;++i){
441        sprintf(keybuf, "key%d", i);
442        sprintf(metabuf, "meta%d", i);
443        sprintf(bodybuf, "body%d", i);
444        fdb_doc_create(&doc[i], (void*)keybuf, strlen(keybuf) + 1,
445            (void*)metabuf, strlen(metabuf) + 1, (void*)bodybuf,
446            strlen(bodybuf) + 1);
447        status = fdb_set(db, doc[i]);
448        TEST_CHK(status == FDB_RESULT_SUCCESS);
449    }
450
451    // commit
452    status = fdb_commit(data.dbfile, FDB_COMMIT_NORMAL);
453    TEST_CHK(status == FDB_RESULT_SUCCESS);
454
455    status = fdb_iterator_init(db, &itr, NULL, 0, NULL, 0,
456                               FDB_ITR_NONE);
457    TEST_CHK(status == FDB_RESULT_SUCCESS);
458
459    // Set callback context to call bad_thread() and do a set invoking callback
460    data.test_handle_busy = 1;
461    status = fdb_set(db, doc[0]);
462    TEST_CHK(status == FDB_RESULT_SUCCESS);
463
464    // Test iterator callbacks by attemping a set call on the iterator handle..
465    data.iterator = itr;
466    // TODO: remove if concurrent access on iterator handle can never happen
467    //status = fdb_set(itr->handle, doc[0]);
468    //TEST_CHK(status == FDB_RESULT_SUCCESS);
469
470    fdb_iterator_close(itr);
471    data.test_handle_busy = 0;
472    fdb_close(data.dbfile);
473
474    for (i = n - 1; i >=0; --i) {
475        fdb_doc_free(doc[i]);
476    }
477    // free all resources
478    fdb_shutdown();
479
480    memleak_end();
481
482    TEST_RESULT("Handle Busy Test");
483}
484
485int get_fs_type_cb(void *ctx, struct filemgr_ops *normal_ops, int srcfd)
486{
487    return FILEMGR_FS_EXT4_WITH_COW;
488}
489
490struct cb_cmp_args {
491    fdb_kvs_handle *handle;
492    int ndocs;
493    int nmoves;
494};
495
496static fdb_compact_decision cb_compact(fdb_file_handle *fhandle,
497                            fdb_compaction_status status, const char *kv_name,
498                            fdb_doc *doc, uint64_t old_offset,
499                            uint64_t new_offset, void *ctx)
500{
501    TEST_INIT();
502    struct cb_cmp_args *args = (struct cb_cmp_args *)ctx;
503    fdb_status fs;
504    fdb_compact_decision ret = FDB_CS_KEEP_DOC;
505    (void) fhandle;
506    (void) doc;
507    (void) old_offset;
508    (void) new_offset;
509
510    if (status == FDB_CS_MOVE_DOC) {
511        TEST_CHK(kv_name);
512        args->nmoves++;
513        if (doc->deleted) {
514            ret = FDB_CS_DROP_DOC;
515        }
516        if (args->nmoves == args->ndocs - 1) {
517            char key[256], value[256];
518            // phase 3 of compaction - uncommitted docs in old_file
519            sprintf(key, "key%250d", args->ndocs);
520            sprintf(value, "body%250d", args->ndocs);
521            fs = fdb_set_kv(args->handle, key, 253, value, 254);
522            TEST_CHK(fs == FDB_RESULT_SUCCESS);
523            fs = fdb_commit(args->handle->fhandle, FDB_COMMIT_NORMAL);
524            TEST_CHK(fs == FDB_RESULT_SUCCESS);
525
526            sprintf(key, "zzz%250d", args->ndocs);
527            fs = fdb_set_kv(args->handle, key, 253, value, 254);
528            TEST_CHK(fs == FDB_RESULT_SUCCESS);
529        }
530    }
531
532    return ret;
533}
534
535static void append_batch_delta(void)
536{
537    TEST_INIT();
538    fdb_file_handle *dbfile;
539    fdb_kvs_handle *db;
540    fdb_status status;
541    int N = 5000;
542    int start = N/2;
543    int i;
544
545    char key[256], value[256];
546    fdb_config fconfig = fdb_get_default_config();
547    fdb_kvs_config kvs_config = fdb_get_default_kvs_config();
548    fconfig.buffercache_size = 0;
549    // open db
550    status = fdb_open(&dbfile, "anomaly_test1a", &fconfig);
551    TEST_CHK(status == FDB_RESULT_SUCCESS);
552    status = fdb_kvs_open_default(dbfile, &db, &kvs_config);
553    TEST_CHK(status == FDB_RESULT_SUCCESS);
554
555    // phase 2 of compaction...
556    // insert docs
557    for (i=start;i<N;++i){
558        sprintf(key, "key%250d", i);
559        sprintf(value, "body%250d", i);
560        status = fdb_set_kv(db, key, 253, value, 254);
561        TEST_CHK(status == FDB_RESULT_SUCCESS);
562        if (i == start + start/2) {
563            status = fdb_commit(dbfile, FDB_COMMIT_NORMAL);
564            TEST_CHK(status == FDB_RESULT_SUCCESS);
565        }
566    }
567
568    status = fdb_commit(dbfile, FDB_COMMIT_NORMAL);
569    TEST_CHK(status == FDB_RESULT_SUCCESS);
570
571    status = fdb_close(dbfile);
572    TEST_CHK(status == FDB_RESULT_SUCCESS);
573}
574
575static int copy_file_range_cb(void *ctx, struct filemgr_ops *normal_ops,
576                              int fstype, int src, int dst,
577                              uint64_t src_off, uint64_t dst_off, uint64_t len)
578{
579    uint8_t *buf = alca(uint8_t, len);
580    bool *append_delta = (bool *)ctx;
581
582    TEST_INIT();
583    TEST_CHK(src_off % 4096 == 0);
584    TEST_CHK(dst_off % 4096 == 0);
585    TEST_CHK(len && len % 4096 == 0);
586    printf("File Range Copy src bid - %" _F64
587           " to dst bid = %" _F64 ", %" _F64" blocks\n",
588           src_off / 4096, dst_off / 4096, (len / 4096) + 1);
589    normal_ops->pread(src, buf, len, src_off);
590    normal_ops->pwrite(dst, buf, len, dst_off);
591    if (*append_delta) {
592        // While the compactor is stuck doing compaction append more documents
593        append_batch_delta();
594        *append_delta = false;
595    }
596    return FDB_RESULT_SUCCESS;
597}
598
599void copy_file_range_test()
600{
601    TEST_INIT();
602
603    memleak_start();
604
605    int i, r;
606    int N = 5000; // total docs after append batch delta
607    int n = N/2;
608    bool append_delta = true;
609    fdb_file_handle *dbfile;
610    fdb_kvs_handle *db;
611    fdb_status status;
612
613    char key[256], value[256];
614    void *value_out;
615    size_t valuelen;
616    struct cb_cmp_args cb_args;
617
618    // Get the default callbacks which result in normal operation for other ops
619    struct anomalous_callbacks *cow_compact_cb = get_default_anon_cbs();
620    cow_compact_cb->get_fs_type_cb = &get_fs_type_cb;
621    cow_compact_cb->copy_file_range_cb = &copy_file_range_cb;
622
623    memset(&cb_args, 0x0, sizeof(struct cb_cmp_args));
624
625    // remove previous dummy files
626    r = system(SHELL_DEL" anomaly_test1a anomaly_test1b > errorlog.txt");
627    (void)r;
628
629    fdb_config fconfig = fdb_get_default_config();
630    fconfig.compaction_cb = cb_compact;
631    fconfig.compaction_cb_ctx = &cb_args;
632    fconfig.compaction_cb_mask = FDB_CS_BEGIN |
633                                 FDB_CS_END | FDB_CS_MOVE_DOC;
634
635    fdb_kvs_config kvs_config = fdb_get_default_kvs_config();
636
637    // Create anomalous behavior with shared handle for the callback ctx
638    filemgr_ops_anomalous_init(cow_compact_cb, &append_delta);
639
640    // open db
641    status = fdb_open(&dbfile, "anomaly_test1a", &fconfig);
642    TEST_CHK(status == FDB_RESULT_SUCCESS);
643    status = fdb_kvs_open_default(dbfile, &db, &kvs_config);
644    TEST_CHK(status == FDB_RESULT_SUCCESS);
645
646    cb_args.handle = db;
647    cb_args.ndocs = N;
648
649    // insert docs
650    for (i=0;i<n;++i){
651        sprintf(key, "key%250d", i);
652        sprintf(value, "body%250d", i);
653        status = fdb_set_kv(db, key, 253, value, 254);
654        TEST_CHK(status == FDB_RESULT_SUCCESS);
655    }
656
657    status = fdb_commit(dbfile, FDB_COMMIT_NORMAL);
658    TEST_CHK(status == FDB_RESULT_SUCCESS);
659
660    // update docs making half the docs stale
661    for (i=n/2; i<n; ++i){
662        sprintf(key, "key%250d", i);
663        sprintf(value, "BODY%250d", i);
664        status = fdb_set_kv(db, key, 253, value, 254);
665        TEST_CHK(status == FDB_RESULT_SUCCESS);
666    }
667
668    status = fdb_commit(dbfile, FDB_COMMIT_NORMAL);
669    TEST_CHK(status == FDB_RESULT_SUCCESS);
670
671    status = fdb_compact_with_cow(dbfile, "anomaly_test1b");
672    TEST_CHK(status == FDB_RESULT_SUCCESS);
673
674    // retrieve check again
675    for (i=0; i<n; ++i){
676        sprintf(key, "key%250d", i);
677        if (i < n/2) {
678            sprintf(value, "body%250d", i);
679        } else {
680            sprintf(value, "BODY%250d", i);
681        }
682        status = fdb_get_kv(db, key, 253, &value_out, &valuelen);
683        TEST_CHK(status == FDB_RESULT_SUCCESS);
684        TEST_CMP(value_out, value, valuelen);
685        fdb_free_block(value_out);
686    }
687
688    // retrieve docs after append batched delta
689    for (; i<N; ++i){
690        sprintf(key, "key%250d", i);
691        sprintf(value, "body%250d", i);
692        status = fdb_get_kv(db, key, 253, &value_out, &valuelen);
693        TEST_CHK(status == FDB_RESULT_SUCCESS);
694        TEST_CMP(value_out, value, valuelen);
695        fdb_free_block(value_out);
696    }
697
698    // check on phase 3 inserted documents..
699    sprintf(key, "key%250d", i);
700    sprintf(value, "body%250d", i);
701    status = fdb_get_kv(db, key, 253, &value_out, &valuelen);
702    TEST_CHK(status == FDB_RESULT_SUCCESS);
703    TEST_CMP(value_out, value, valuelen);
704    fdb_free_block(value_out);
705
706    sprintf(key, "zzz%250d", i);
707    status = fdb_get_kv(db, key, 253, &value_out, &valuelen);
708    TEST_CHK(status == FDB_RESULT_SUCCESS);
709    TEST_CHK(status == FDB_RESULT_SUCCESS);
710    TEST_CMP(value_out, value, valuelen);
711    fdb_free_block(value_out);
712
713    // free all resources
714    status = fdb_close(dbfile);
715    TEST_CHK(status == FDB_RESULT_SUCCESS);
716    fdb_shutdown();
717
718    memleak_end();
719
720    TEST_RESULT("copy file range test");
721}
722
723void read_old_file()
724{
725    TEST_INIT();
726    int n=200, i, r;
727    fdb_file_handle *dbfile;
728    fdb_kvs_handle *db;
729    fdb_config config;
730    fdb_kvs_config kvs_config;
731    fdb_doc *doc;
732    fdb_status s; (void)s;
733    char keybuf[256], valuebuf[256];
734    void *normal_ops_ptr;
735
736    memleak_start();
737
738    // remove previous dummy files
739    r = system(SHELL_DEL" anomaly_test* > errorlog.txt");
740    (void)r;
741
742    config = fdb_get_default_config();
743    config.buffercache_size = 0;
744    kvs_config = fdb_get_default_kvs_config();
745
746    struct anomalous_callbacks *cbs = get_default_anon_cbs();
747    filemgr_ops_anomalous_init(cbs, NULL);
748
749    normal_ops_ptr = get_normal_ops_ptr();
750
751    // create a file
752    s = fdb_open(&dbfile, "anomaly_test1", &config);
753    TEST_CHK(s == FDB_RESULT_SUCCESS);
754
755    s = fdb_kvs_open(dbfile, &db, NULL, &kvs_config);
756    TEST_CHK(s == FDB_RESULT_SUCCESS);
757
758    for (i=0; i<n; ++i) {
759        sprintf(keybuf, "k%06d", i);
760        sprintf(valuebuf, "v%06d", i);
761        fdb_doc_create(&doc, keybuf, 8, NULL, 0, valuebuf, 8);
762        s = fdb_set(db, doc);
763        TEST_CHK(s == FDB_RESULT_SUCCESS);
764        fdb_doc_free(doc);
765    }
766
767    s = fdb_commit(dbfile, FDB_COMMIT_MANUAL_WAL_FLUSH);
768    TEST_CHK(s == FDB_RESULT_SUCCESS);
769
770    s = fdb_close(dbfile);
771    TEST_CHK(s == FDB_RESULT_SUCCESS);
772
773    // hack the last 9 bytes (magic number + block marker) in the file
774    int fd = cbs->open_cb(NULL, (struct filemgr_ops*)normal_ops_ptr,
775                          "anomaly_test1", O_RDWR, 0644);
776    uint64_t offset = cbs->file_size_cb(NULL, (struct filemgr_ops*)normal_ops_ptr,
777                                        "anomaly_test1");
778    uint8_t magic[10] = {0xde, 0xad, 0xca, 0xfe, 0xbe, 0xef, 0xbe, 0xef, 0xee};
779    cbs->pwrite_cb(NULL, (struct filemgr_ops*)normal_ops_ptr, fd,
780                   (void*)magic, 9, offset-9);
781    cbs->close_cb(NULL, (struct filemgr_ops*)normal_ops_ptr, fd);
782
783    // reopen
784    s = fdb_open(&dbfile, "anomaly_test1", &config);
785    // successfully read
786    TEST_CHK(s == FDB_RESULT_SUCCESS);
787
788    s = fdb_close(dbfile);
789    TEST_CHK(s == FDB_RESULT_SUCCESS);
790
791    fdb_shutdown();
792    memleak_end();
793
794    TEST_RESULT("read an old file test");
795}
796
797void corrupted_header_correct_superblock_test()
798{
799    TEST_INIT();
800    int n=200, n_commits=4, i, j, r;
801    fdb_file_handle *dbfile;
802    fdb_kvs_handle *db;
803    fdb_config config;
804    fdb_kvs_config kvs_config;
805    fdb_doc *doc;
806    fdb_status s; (void)s;
807    char keybuf[256], valuebuf[256];
808    struct filemgr_ops *normal_ops;
809
810    memleak_start();
811
812    // remove previous dummy files
813    r = system(SHELL_DEL" anomaly_test* > errorlog.txt");
814    (void)r;
815
816    config = fdb_get_default_config();
817    config.buffercache_size = 0;
818    kvs_config = fdb_get_default_kvs_config();
819
820    struct anomalous_callbacks *cbs = get_default_anon_cbs();
821    filemgr_ops_anomalous_init(cbs, NULL);
822
823    normal_ops = get_normal_ops_ptr();
824
825    // create a file
826    s = fdb_open(&dbfile, "anomaly_test1", &config);
827    TEST_CHK(s == FDB_RESULT_SUCCESS);
828
829    s = fdb_kvs_open(dbfile, &db, NULL, &kvs_config);
830    TEST_CHK(s == FDB_RESULT_SUCCESS);
831
832    for (j=0; j<n_commits; ++j) {
833        for (i=0; i<n; ++i) {
834            sprintf(keybuf, "k%06d", i);
835            sprintf(valuebuf, "v%d_%04d", j, i);
836            fdb_doc_create(&doc, keybuf, 8, NULL, 0, valuebuf, 8);
837            s = fdb_set(db, doc);
838            TEST_CHK(s == FDB_RESULT_SUCCESS);
839            fdb_doc_free(doc);
840        }
841        s = fdb_commit(dbfile, FDB_COMMIT_NORMAL);
842        TEST_CHK(s == FDB_RESULT_SUCCESS);
843    }
844
845    s = fdb_close(dbfile);
846    TEST_CHK(s == FDB_RESULT_SUCCESS);
847
848    // copy the data in the file except for the last (header) block
849    int fd = cbs->open_cb(NULL, normal_ops, "anomaly_test1", O_RDWR, 0644);
850    int fd_dst = cbs->open_cb(NULL, normal_ops,
851                              "anomaly_test2", O_CREAT | O_RDWR, 0644);
852    uint64_t offset = cbs->file_size_cb(NULL, normal_ops, "anomaly_test1");
853    uint8_t *filedata = (uint8_t*)malloc(offset);
854    cbs->pread_cb(NULL, normal_ops, fd, (void*)filedata, offset, 0);
855    cbs->pwrite_cb(NULL, normal_ops, fd_dst, (void*)filedata, offset - 4096, 0);
856    cbs->close_cb(NULL, normal_ops, fd);
857    cbs->close_cb(NULL, normal_ops, fd_dst);
858    free(filedata);
859
860    // open the corrupted file
861    s = fdb_open(&dbfile, "anomaly_test2", &config);
862    TEST_CHK(s == FDB_RESULT_SUCCESS);
863
864    s = fdb_kvs_open(dbfile, &db, NULL, &kvs_config);
865    TEST_CHK(s == FDB_RESULT_SUCCESS);
866
867    // data at the 3rd commit should be read
868    j = 2;
869    for (i=0; i<n; ++i) {
870        sprintf(keybuf, "k%06d", i);
871        sprintf(valuebuf, "v%d_%04d", j, i);
872        fdb_doc_create(&doc, keybuf, 8, NULL, 0, NULL, 0);
873        s = fdb_get(db, doc);
874        TEST_CHK(s == FDB_RESULT_SUCCESS);
875        TEST_CMP(doc->body, valuebuf, doc->bodylen);
876        fdb_doc_free(doc);
877    }
878
879    s = fdb_close(dbfile);
880    TEST_CHK(s == FDB_RESULT_SUCCESS);
881
882    fdb_shutdown();
883    memleak_end();
884
885    TEST_RESULT("corrupted DB header from correct superblock test");
886}
887
888int main(){
889
890    /**
891     * Commented out this test for now; it copies consecutive document blocks
892     * to other file but they are written in different BID compared to the source
893     * file, so that meta section at the end of each document block points to wrong
894     * block and consequently documents cannot be read correctly.
895     */
896    //copy_file_range_test();
897    write_failure_test();
898    read_failure_test();
899    handle_busy_test();
900    read_old_file();
901    corrupted_header_correct_superblock_test();
902
903    return 0;
904}
905