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 
41 CACHEFILE f1;
42 CACHEFILE f2;
43 
44 bool check_flush;
45 bool dirty_flush_called;
46 bool check_pe_callback;
47 bool pe_callback_called;
48 bool enable_partial_eviction;
49 
50 CACHETABLE ct;
51 
52 static int
pe_callback(void * ftnode_pv,PAIR_ATTR bytes_to_free,void * extraargs,void (* finalize)(PAIR_ATTR bytes_freed,void * extra),void * finalize_extra)53 pe_callback (
54     void *ftnode_pv __attribute__((__unused__)),
55     PAIR_ATTR bytes_to_free __attribute__((__unused__)),
56     void* extraargs __attribute__((__unused__)),
57     void (*finalize)(PAIR_ATTR bytes_freed, void *extra),
58     void *finalize_extra
59     )
60 {
61     if (check_pe_callback) {
62         pe_callback_called = true;
63     }
64     usleep(4*1024*1024);
65     finalize(make_pair_attr(1), finalize_extra);
66     return 0;
67 }
68 
69 static void
flush(CACHEFILE f,int UU (fd),CACHEKEY k,void * v,void ** dd,void * e,PAIR_ATTR s,PAIR_ATTR * new_size,bool w,bool keep,bool c,bool UU (is_clone))70 flush (CACHEFILE f __attribute__((__unused__)),
71        int UU(fd),
72        CACHEKEY k  __attribute__((__unused__)),
73        void *v     __attribute__((__unused__)),
74        void **dd     __attribute__((__unused__)),
75        void *e     __attribute__((__unused__)),
76        PAIR_ATTR s      __attribute__((__unused__)),
77        PAIR_ATTR* new_size      __attribute__((__unused__)),
78        bool w      __attribute__((__unused__)),
79        bool keep   __attribute__((__unused__)),
80        bool c      __attribute__((__unused__)),
81        bool UU(is_clone)
82        ) {
83     if (check_flush && w) {
84         dirty_flush_called = true;
85     }
86 }
87 
f2_pin(void * arg)88 static void *f2_pin(void *arg) {
89     int r;
90     void* v1;
91     CACHETABLE_WRITE_CALLBACK wc = def_write_callback(NULL);
92     //
93     // these booleans for pe_callback just ensure that the
94     // test is working as we expect it to. We expect the get_and_pin to
95     // cause a partial eviction of f1's PAIR, reducing its size from 8 to 1
96     // and we expect that to be enough so that the unpin does not invoke a partial eviction
97     // This is just to ensure that the bug is being exercised
98     //
99     check_pe_callback = true;
100     r = toku_cachetable_get_and_pin(f2, make_blocknum(1), 1, &v1, wc, def_fetch, def_pf_req_callback, def_pf_callback, true, NULL);
101     assert(r == 0);
102     ct->ev.signal_eviction_thread();
103     usleep(1*1024*1024);
104     if (enable_partial_eviction)
105         assert(pe_callback_called);
106     else
107         assert(!pe_callback_called);
108     pe_callback_called = false;
109     r = toku_test_cachetable_unpin(f2, make_blocknum(1), 1, CACHETABLE_CLEAN, make_pair_attr(8));
110     check_pe_callback = false;
111     assert(!pe_callback_called);
112     assert(r == 0);
113 
114     return arg;
115 }
116 
117 static void
cachetable_test(void)118 cachetable_test (void) {
119     const int test_limit = 12;
120     int r;
121     check_flush = false;
122     dirty_flush_called = false;
123 
124     toku_cachetable_create(&ct, test_limit, ZERO_LSN, nullptr);
125     evictor_test_helpers::disable_ev_thread(&ct->ev); // disable eviction thread
126 
127     toku_set_enable_partial_eviction(ct, enable_partial_eviction);
128 
129     toku_os_recursive_delete(TOKU_TEST_FILENAME);
130     r = toku_os_mkdir(TOKU_TEST_FILENAME, S_IRWXU);
131     assert_zero(r);
132     char fname1[TOKU_PATH_MAX + 1];
133     unlink(toku_path_join(fname1, 2, TOKU_TEST_FILENAME, "test1.dat"));
134     char fname2[TOKU_PATH_MAX + 1];
135     unlink(toku_path_join(fname2, 2, TOKU_TEST_FILENAME, "test2.dat"));
136 
137     r = toku_cachetable_openf(&f1, ct, fname1, O_RDWR|O_CREAT, S_IRWXU|S_IRWXG|S_IRWXO);
138     assert(r == 0);
139     r = toku_cachetable_openf(&f2, ct, fname2, O_RDWR|O_CREAT, S_IRWXU|S_IRWXG|S_IRWXO);
140     assert(r == 0);
141 
142     void* v1;
143     CACHETABLE_WRITE_CALLBACK wc = def_write_callback(NULL);
144     wc.pe_callback = pe_callback;
145     wc.flush_callback = flush;
146     // pin and unpin a node 20 times, just to get clock count up
147     for (int i = 0; i < 20; i++) {
148         r = toku_cachetable_get_and_pin(f1, make_blocknum(1), 1, &v1, wc, def_fetch, def_pf_req_callback, def_pf_callback, true, NULL);
149         assert(r == 0);
150         r = toku_test_cachetable_unpin(f1, make_blocknum(1), 1, CACHETABLE_DIRTY, make_pair_attr(8));
151         assert(r == 0);
152     }
153 
154     // at this point, we have a dirty PAIR in the cachetable associated with
155     // cachefile f1
156     // launch a thread that will put another PAIR in the cachetable, and get
157     // partial eviction started
158     toku_pthread_t tid;
159     r = toku_pthread_create(
160         toku_uninstrumented, &tid, nullptr, f2_pin, nullptr);
161     assert_zero(r);
162 
163     usleep(2 * 1024 * 1024);
164     check_flush = true;
165     toku_cachefile_close(&f1, false, ZERO_LSN);
166     if (enable_partial_eviction)
167         assert(dirty_flush_called);
168     else
169         assert(!dirty_flush_called);
170     check_flush = false;
171 
172     void *ret;
173     r = toku_pthread_join(tid, &ret);
174     assert_zero(r);
175 
176 
177     toku_cachetable_verify(ct);
178     toku_cachefile_close(&f2, false, ZERO_LSN);
179     toku_cachetable_close(&ct);
180 }
181 
182 int
test_main(int argc,const char * argv[])183 test_main(int argc, const char *argv[]) {
184     if (argc!=2) { printf("argcount should be 2.\n");  exit(1); }
185     if (strcmp(argv[1], "enable_pe") == 0) {
186         enable_partial_eviction = true;
187     } else {
188         enable_partial_eviction = false;
189     }
190     cachetable_test();
191     return 0;
192 }
193