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 // verify that the cache table checkpoint with prefetched blocks active works.
40 // the blocks in the reading state should be ignored.
41 
42 #include "test.h"
43 #include <stdio.h>
44 #include <unistd.h>
45 #include "cachetable-test.h"
46 
47 #include "cachetable/checkpoint.h"
48 
49 const int item_size = 1;
50 
51 int n_flush, n_write_me, n_keep_me, n_fetch;
52 
flush(CACHEFILE UU (cf),int UU (fd),CACHEKEY UU (key),void * UU (value),void ** UU (dd),void * UU (extraargs),PAIR_ATTR size,PAIR_ATTR * UU (new_size),bool write_me,bool keep_me,bool UU (for_checkpoint),bool UU (is_clone))53 static void flush(
54     CACHEFILE UU(cf),
55     int UU(fd),
56     CACHEKEY UU(key),
57     void *UU(value),
58     void** UU(dd),
59     void *UU(extraargs),
60     PAIR_ATTR size,
61     PAIR_ATTR* UU(new_size),
62     bool write_me,
63     bool keep_me,
64     bool UU(for_checkpoint),
65         bool UU(is_clone)
66     )
67 {
68     // assert(key == make_blocknum((long)value));
69     assert(size.size == item_size);
70     n_flush++;
71     if (write_me) n_write_me++;
72     if (keep_me) n_keep_me++;
73 }
74 
fetch(CACHEFILE UU (cf),PAIR UU (p),int UU (fd),CACHEKEY UU (key),uint32_t UU (fullhash),void ** UU (value),void ** UU (dd),PAIR_ATTR * UU (sizep),int * dirtyp,void * UU (extraargs))75 static int fetch(
76     CACHEFILE UU(cf),
77     PAIR UU(p),
78     int UU(fd),
79     CACHEKEY UU(key),
80     uint32_t UU(fullhash),
81     void **UU(value),
82     void** UU(dd),
83     PAIR_ATTR *UU(sizep),
84     int *dirtyp,
85     void *UU(extraargs)
86     )
87 {
88     n_fetch++;
89     sleep(10);
90     *value = 0;
91     *sizep = make_pair_attr(item_size);
92     *dirtyp = 0;
93     return 0;
94 }
95 
96 // put n items into the cachetable, maybe mark them dirty, do a checkpoint, and
97 // verify that all of the items have been written and are clean.
cachetable_prefetch_checkpoint_test(int n,enum cachetable_dirty dirty)98 static void cachetable_prefetch_checkpoint_test(int n, enum cachetable_dirty dirty) {
99     if (verbose) printf("%s:%d n=%d dirty=%d\n", __FUNCTION__, __LINE__, n, (int) dirty);
100     const int test_limit = n;
101     int r;
102     CACHETABLE ct;
103     CACHETABLE_WRITE_CALLBACK wc = def_write_callback(NULL);
104     wc.flush_callback = flush;
105     toku_cachetable_create(&ct, test_limit, ZERO_LSN, nullptr);
106     const char *fname1 = TOKU_TEST_FILENAME;
107     unlink(fname1);
108     CACHEFILE f1;
109     r = toku_cachetable_openf(&f1, ct, fname1, O_RDWR|O_CREAT, S_IRWXU|S_IRWXG|S_IRWXO); assert(r == 0);
110     create_dummy_functions(f1);
111 
112     // disable the eviction thread. this thread was written to assume
113     // evictions hapepn on the client thread, which is no longer true.
114     evictor_test_helpers::disable_ev_thread(&ct->ev);
115 
116     // prefetch block n+1. this will take 10 seconds.
117     {
118         CACHEKEY key = make_blocknum(n+1);
119         uint32_t fullhash = toku_cachetable_hash(f1, key);
120         r = toku_cachefile_prefetch(f1, key, fullhash, wc, fetch, def_pf_req_callback, def_pf_callback, 0, NULL);
121         toku_cachetable_verify(ct);
122     }
123 
124     // insert items into the cachetable. all should be dirty
125     int i;
126     for (i=0; i<n; i++) {
127         CACHEKEY key = make_blocknum(i);
128         uint32_t hi = toku_cachetable_hash(f1, key);
129         toku_cachetable_put(f1, key, hi, (void *)(long)i, make_pair_attr(1), wc, put_callback_nop);
130 
131         r = toku_test_cachetable_unpin(f1, key, hi, dirty, make_pair_attr(item_size));
132         assert(r == 0);
133 
134         void *v;
135         int its_dirty;
136         long long its_pin;
137         long its_size;
138         r = toku_cachetable_get_key_state(ct, key, f1, &v, &its_dirty, &its_pin, &its_size);
139         if (r != 0)
140             continue;
141         assert(its_dirty == CACHETABLE_DIRTY);
142         assert(its_pin == 0);
143         assert(its_size == item_size);
144     }
145 
146     // the checkpoint should cause n writes, but since n <= the cachetable size,
147     // all items should be kept in the cachetable
148     n_flush = n_write_me = n_keep_me = n_fetch = 0;
149     CHECKPOINTER cp = toku_cachetable_get_checkpointer(ct);
150     r = toku_checkpoint(cp, NULL, NULL, NULL, NULL, NULL, CLIENT_CHECKPOINT);
151     assert(r == 0);
152     assert(n_flush == n && n_write_me == n && n_keep_me == n);
153 
154     // after the checkpoint, all of the items should be clean
155     for (i=0; i<n; i++) {
156         CACHEKEY key = make_blocknum(i);
157         uint32_t hi = toku_cachetable_hash(f1, key);
158         void *v;
159         r = toku_cachetable_maybe_get_and_pin(f1, key, hi, PL_WRITE_EXPENSIVE, &v);
160         if (r != 0)
161             continue;
162         r = toku_test_cachetable_unpin(f1, key, hi, CACHETABLE_CLEAN, make_pair_attr(item_size));
163         assert(r == 0);
164 
165         int its_dirty;
166         long long its_pin;
167         long its_size;
168         r = toku_cachetable_get_key_state(ct, key, f1, &v, &its_dirty, &its_pin, &its_size);
169         if (r != 0)
170             continue;
171         assert(its_dirty == CACHETABLE_CLEAN);
172         assert(its_pin == 0);
173         assert(its_size == item_size);
174     }
175 
176     // a subsequent checkpoint should cause no flushes, or writes since all of the items are clean
177     n_flush = n_write_me = n_keep_me = n_fetch = 0;
178 
179     r = toku_checkpoint(cp, NULL, NULL, NULL, NULL, NULL, CLIENT_CHECKPOINT);
180     assert(r == 0);
181     assert(n_flush == 0 && n_write_me == 0 && n_keep_me == 0);
182 
183     toku_cachefile_close(&f1, false, ZERO_LSN);
184     toku_cachetable_close(&ct);
185 }
186 
187 int
test_main(int argc,const char * argv[])188 test_main(int argc, const char *argv[]) {
189     int i;
190     for (i=1; i<argc; i++) {
191         if (strcmp(argv[i], "-v") == 0) {
192             verbose++;
193             continue;
194         }
195     }
196     for (i=0; i<8; i++) {
197         cachetable_prefetch_checkpoint_test(i, CACHETABLE_CLEAN);
198         cachetable_prefetch_checkpoint_test(i, CACHETABLE_DIRTY);
199     }
200     return 0;
201 }
202