1 /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 // vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
3 #ident "$Id$"
4 /*======
5 This file is part of PerconaFT.
6 
7 
8 Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
9 
10     PerconaFT is free software: you can redistribute it and/or modify
11     it under the terms of the GNU General Public License, version 2,
12     as published by the Free Software Foundation.
13 
14     PerconaFT is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17     GNU General Public License for more details.
18 
19     You should have received a copy of the GNU General Public License
20     along with PerconaFT.  If not, see <http://www.gnu.org/licenses/>.
21 
22 ----------------------------------------
23 
24     PerconaFT is free software: you can redistribute it and/or modify
25     it under the terms of the GNU Affero General Public License, version 3,
26     as published by the Free Software Foundation.
27 
28     PerconaFT is distributed in the hope that it will be useful,
29     but WITHOUT ANY WARRANTY; without even the implied warranty of
30     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
31     GNU Affero General Public License for more details.
32 
33     You should have received a copy of the GNU Affero General Public License
34     along with PerconaFT.  If not, see <http://www.gnu.org/licenses/>.
35 ======= */
36 
37 #ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
38 
39 #include "test.h"
40 #include "cachetable-test.h"
41 
42 bool clone_called;
43 bool check_flush;
44 bool flush_expected;
45 bool flush_called;
46 
47 static void
48 clone_callback(void* UU(value_data), void** cloned_value_data, long* clone_size, PAIR_ATTR* new_attr, bool UU(for_checkpoint), void* UU(write_extraargs))
49 {
50     *cloned_value_data = (void *)1;
51     new_attr->is_valid = false;
52     clone_called = true;
53     *clone_size = 8;
54 }
55 
56 static void
57 flush (
58     CACHEFILE f __attribute__((__unused__)),
59     int UU(fd),
60     CACHEKEY k  __attribute__((__unused__)),
61     void *v     __attribute__((__unused__)),
62     void** UU(dd),
63     void *e     __attribute__((__unused__)),
64     PAIR_ATTR s      __attribute__((__unused__)),
65     PAIR_ATTR* new_size      __attribute__((__unused__)),
66     bool w      __attribute__((__unused__)),
67     bool keep   __attribute__((__unused__)),
68     bool c      __attribute__((__unused__)),
69     bool UU(is_clone)
70     )
71 {
72     if (w) usleep(5*1024*1024);
73     if (w && check_flush) {
74         assert(flush_expected);
75         if (clone_called) assert(is_clone);
76     }
77     flush_called = true;
78     if (is_clone) assert(!keep);
79 }
80 
81 static uint64_t tdelta_usec(struct timeval *tend, struct timeval *tstart) {
82     uint64_t t = tend->tv_sec * 1000000 + tend->tv_usec;
83     t -= tstart->tv_sec * 1000000 + tstart->tv_usec;
84     return t;
85 }
86 
87 
88 //
89 // test the following things for simple cloning:
90 //  - if the pending pair is clean, nothing gets written
91 //  - if the pending pair is dirty and cloneable, then pair is written
92 //     in background and get_and_pin returns immedietely
93 //  - if the pending pair is dirty and not cloneable, then get_and_pin
94 //     blocks until the pair is written out
95 //
96 static void
97 test_clean (enum cachetable_dirty dirty, bool cloneable) {
98     const int test_limit = 12;
99     int r;
100     CACHETABLE ct;
101     toku_cachetable_create(&ct, test_limit, ZERO_LSN, nullptr);
102     const char *fname1 = TOKU_TEST_FILENAME;
103     unlink(fname1);
104     CACHEFILE f1;
105     r = toku_cachetable_openf(&f1, ct, fname1, O_RDWR|O_CREAT, S_IRWXU|S_IRWXG|S_IRWXO); assert(r == 0);
106     create_dummy_functions(f1);
107 
108     void* v1;
109     CACHETABLE_WRITE_CALLBACK wc = def_write_callback(NULL);
110     wc.clone_callback = cloneable ? clone_callback : NULL;
111     wc.flush_callback = flush;
112     r = toku_cachetable_get_and_pin(f1, make_blocknum(1), 1, &v1, wc, def_fetch, def_pf_req_callback, def_pf_callback, true, NULL);
113     r = toku_test_cachetable_unpin(f1, make_blocknum(1), 1, dirty, make_pair_attr(8));
114 
115     check_flush = true;
116     clone_called = false;
117     flush_expected = (dirty == CACHETABLE_DIRTY) ? true : false;
118     flush_called = false;
119     // begin checkpoint, since pair is clean, we should not
120     // have the clone called
121     CHECKPOINTER cp = toku_cachetable_get_checkpointer(ct);
122     toku_cachetable_begin_checkpoint(cp, NULL);
123     assert_zero(r);
124     struct timeval tstart;
125     struct timeval tend;
126     gettimeofday(&tstart, NULL);
127 
128     // test that having a pin that passes false for may_modify_value does not stall behind checkpoint
129     r = toku_cachetable_get_and_pin(f1, make_blocknum(1), 1, &v1, wc, def_fetch, def_pf_req_callback, def_pf_callback, false, NULL);
130     r = toku_test_cachetable_unpin(f1, make_blocknum(1), 1, CACHETABLE_CLEAN, make_pair_attr(8));
131     gettimeofday(&tend, NULL);
132     assert(tdelta_usec(&tend, &tstart) <= 2000000);
133     assert(!clone_called);
134 
135     r = toku_cachetable_get_and_pin(f1, make_blocknum(1), 1, &v1, wc, def_fetch, def_pf_req_callback, def_pf_callback, true, NULL);
136     gettimeofday(&tend, NULL);
137 
138     // we take 5 seconds for a write
139     // we check if time to pin is less than 2 seconds, if it is
140     // then we know act of cloning worked properly
141     if (cloneable || !dirty ) {
142         assert(tdelta_usec(&tend, &tstart) <= 2000000);
143     }
144     else {
145         assert(tdelta_usec(&tend, &tstart) >= 2000000);
146     }
147 
148 
149     if (dirty == CACHETABLE_DIRTY && cloneable) {
150         assert(clone_called);
151     }
152     else {
153         assert(!clone_called);
154     }
155 
156     // at this point, there should be no more dirty writes
157     r = toku_test_cachetable_unpin(f1, make_blocknum(1), 1, CACHETABLE_CLEAN, make_pair_attr(8));
158     gettimeofday(&tend, NULL);
159     if (cloneable || !dirty ) {
160         assert(tdelta_usec(&tend, &tstart) <= 2000000);
161     }
162     else {
163         assert(tdelta_usec(&tend, &tstart) >= 2000000);
164     }
165 
166     toku_cachetable_end_checkpoint(
167         cp,
168         NULL,
169         NULL,
170         NULL
171         );
172 
173     check_flush = false;
174 
175     toku_cachetable_verify(ct);
176     toku_cachefile_close(&f1, false, ZERO_LSN);
177     toku_cachetable_close(&ct);
178 }
179 
180 int
181 test_main(int argc, const char *argv[]) {
182   default_parse_args(argc, argv);
183   test_clean(CACHETABLE_CLEAN, true);
184   test_clean(CACHETABLE_DIRTY, true);
185   test_clean(CACHETABLE_CLEAN, false);
186   test_clean(CACHETABLE_DIRTY, false);
187   return 0;
188 }
189