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 "portability/memory.h"
40 #include "portability/toku_portability.h"
41 
42 #include "ft/serialize/block_table.h"
43 #include "ft/ft-internal.h"
44 #include "ft/serialize/ft_node-serialize.h"
45 #include "ft/txn/rollback.h"
46 #include "ft/txn/rollback-ct-callbacks.h"
47 
48 #include "util/memarena.h"
49 
50 // Address used as a sentinel. Otherwise unused.
51 static struct serialized_rollback_log_node cloned_rollback;
52 
53 // Cleanup the rollback memory
54 static void
rollback_log_destroy(ROLLBACK_LOG_NODE log)55 rollback_log_destroy(ROLLBACK_LOG_NODE log) {
56     make_rollback_log_empty(log);
57     toku_free(log);
58 }
59 
60 // flush an ununused log to disk, by allocating a size 0 blocknum in
61 // the blocktable
toku_rollback_flush_unused_log(ROLLBACK_LOG_NODE log,BLOCKNUM logname,int fd,FT ft,bool write_me,bool keep_me,bool for_checkpoint,bool is_clone)62 static void toku_rollback_flush_unused_log(ROLLBACK_LOG_NODE log,
63                                            BLOCKNUM logname,
64                                            int fd,
65                                            FT ft,
66                                            bool write_me,
67                                            bool keep_me,
68                                            bool for_checkpoint,
69                                            bool is_clone) {
70     if (write_me) {
71         DISKOFF offset;
72         ft->blocktable.realloc_on_disk(
73             logname, 0, &offset, ft, fd, for_checkpoint);
74     }
75     if (!keep_me && !is_clone) {
76         toku_free(log);
77     }
78 }
79 
80 // flush a used log to disk by serializing and writing the node out
81 static void
toku_rollback_flush_used_log(ROLLBACK_LOG_NODE log,SERIALIZED_ROLLBACK_LOG_NODE serialized,int fd,FT ft,bool write_me,bool keep_me,bool for_checkpoint,bool is_clone)82 toku_rollback_flush_used_log (
83     ROLLBACK_LOG_NODE log,
84     SERIALIZED_ROLLBACK_LOG_NODE serialized,
85     int fd,
86     FT ft,
87     bool write_me,
88     bool keep_me,
89     bool for_checkpoint,
90     bool is_clone
91     )
92 {
93 
94     if (write_me) {
95         int r = toku_serialize_rollback_log_to(fd, log, serialized, is_clone, ft, for_checkpoint);
96         assert(r == 0);
97     }
98     if (!keep_me) {
99         if (is_clone) {
100             toku_serialized_rollback_log_destroy(serialized);
101         }
102         else {
103             rollback_log_destroy(log);
104         }
105     }
106 }
107 
108 // Write something out.  Keep trying even if partial writes occur.
109 // On error: Return negative with errno set.
110 // On success return nbytes.
toku_rollback_flush_callback(CACHEFILE UU (cachefile),int fd,BLOCKNUM logname,void * rollback_v,void ** UU (disk_data),void * extraargs,PAIR_ATTR size,PAIR_ATTR * new_size,bool write_me,bool keep_me,bool for_checkpoint,bool is_clone)111 void toku_rollback_flush_callback (
112     CACHEFILE UU(cachefile),
113     int fd,
114     BLOCKNUM logname,
115     void *rollback_v,
116     void** UU(disk_data),
117     void *extraargs,
118     PAIR_ATTR size,
119     PAIR_ATTR* new_size,
120     bool write_me,
121     bool keep_me,
122     bool for_checkpoint,
123     bool is_clone
124     )
125 {
126     ROLLBACK_LOG_NODE log = nullptr;
127     SERIALIZED_ROLLBACK_LOG_NODE serialized = nullptr;
128     bool is_unused = false;
129     if (is_clone) {
130         is_unused = (rollback_v == &cloned_rollback);
131         CAST_FROM_VOIDP(serialized, rollback_v);
132     }
133     else {
134         CAST_FROM_VOIDP(log, rollback_v);
135         is_unused = rollback_log_is_unused(log);
136     }
137     *new_size = size;
138     FT ft;
139     CAST_FROM_VOIDP(ft, extraargs);
140     if (is_unused) {
141         toku_rollback_flush_unused_log(
142             log,
143             logname,
144             fd,
145             ft,
146             write_me,
147             keep_me,
148             for_checkpoint,
149             is_clone
150             );
151     }
152     else {
153         toku_rollback_flush_used_log(
154             log,
155             serialized,
156             fd,
157             ft,
158             write_me,
159             keep_me,
160             for_checkpoint,
161             is_clone
162             );
163     }
164 }
165 
toku_rollback_fetch_callback(CACHEFILE cachefile,PAIR p,int fd,BLOCKNUM logname,uint32_t fullhash UU (),void ** rollback_pv,void ** UU (disk_data),PAIR_ATTR * sizep,int * UU (dirtyp),void * extraargs)166 int toku_rollback_fetch_callback (CACHEFILE cachefile, PAIR p, int fd, BLOCKNUM logname, uint32_t fullhash UU(),
167                                  void **rollback_pv,  void** UU(disk_data), PAIR_ATTR *sizep, int * UU(dirtyp), void *extraargs) {
168     int r;
169     FT CAST_FROM_VOIDP(h, extraargs);
170     assert(h->cf == cachefile);
171     ROLLBACK_LOG_NODE *result = (ROLLBACK_LOG_NODE*)rollback_pv;
172     r = toku_deserialize_rollback_log_from(fd, logname, result, h);
173     if (r==0) {
174         (*result)->ct_pair = p;
175         *sizep = rollback_memory_size(*result);
176     }
177     return r;
178 }
179 
toku_rollback_pe_est_callback(void * rollback_v,void * UU (disk_data),long * bytes_freed_estimate,enum partial_eviction_cost * cost,void * UU (write_extraargs))180 void toku_rollback_pe_est_callback(
181     void* rollback_v,
182     void* UU(disk_data),
183     long* bytes_freed_estimate,
184     enum partial_eviction_cost *cost,
185     void* UU(write_extraargs)
186     )
187 {
188     assert(rollback_v != NULL);
189     *bytes_freed_estimate = 0;
190     *cost = PE_CHEAP;
191 }
192 
193 // callback for partially evicting a cachetable entry
toku_rollback_pe_callback(void * rollback_v,PAIR_ATTR old_attr,void * UU (extraargs),void (* finalize)(PAIR_ATTR new_attr,void * extra),void * finalize_extra)194 int toku_rollback_pe_callback (
195     void *rollback_v,
196     PAIR_ATTR old_attr,
197     void* UU(extraargs),
198     void (*finalize)(PAIR_ATTR new_attr, void * extra),
199     void *finalize_extra
200     )
201 {
202     assert(rollback_v != NULL);
203     finalize(old_attr, finalize_extra);
204     return 0;
205 }
206 
207 // partial fetch is never required for a rollback log node
toku_rollback_pf_req_callback(void * UU (ftnode_pv),void * UU (read_extraargs))208 bool toku_rollback_pf_req_callback(void* UU(ftnode_pv), void* UU(read_extraargs)) {
209     return false;
210 }
211 
212 // a rollback node should never be partial fetched,
213 // because we always say it is not required.
214 // (pf req callback always returns false)
toku_rollback_pf_callback(void * UU (ftnode_pv),void * UU (disk_data),void * UU (read_extraargs),int UU (fd),PAIR_ATTR * UU (sizep))215 int toku_rollback_pf_callback(void* UU(ftnode_pv),  void* UU(disk_data), void* UU(read_extraargs), int UU(fd), PAIR_ATTR* UU(sizep)) {
216     assert(false);
217     return 0;
218 }
219 
220 // the cleaner thread should never choose a rollback node for cleaning
toku_rollback_cleaner_callback(void * UU (ftnode_pv),BLOCKNUM UU (blocknum),uint32_t UU (fullhash),void * UU (extraargs))221 int toku_rollback_cleaner_callback (
222     void* UU(ftnode_pv),
223     BLOCKNUM UU(blocknum),
224     uint32_t UU(fullhash),
225     void* UU(extraargs)
226     )
227 {
228     assert(false);
229     return 0;
230 }
231 
toku_rollback_clone_callback(void * value_data,void ** cloned_value_data,long * clone_size,PAIR_ATTR * new_attr,bool UU (for_checkpoint),void * UU (write_extraargs))232 void toku_rollback_clone_callback(
233     void* value_data,
234     void** cloned_value_data,
235     long* clone_size,
236     PAIR_ATTR* new_attr,
237     bool UU(for_checkpoint),
238     void* UU(write_extraargs)
239     )
240 {
241     ROLLBACK_LOG_NODE CAST_FROM_VOIDP(log, value_data);
242     SERIALIZED_ROLLBACK_LOG_NODE serialized = nullptr;
243     if (!rollback_log_is_unused(log)) {
244         XMALLOC(serialized);
245         toku_serialize_rollback_log_to_memory_uncompressed(log, serialized);
246         *cloned_value_data = serialized;
247         *clone_size = sizeof(struct serialized_rollback_log_node) + serialized->len;
248     }
249     else {
250         *cloned_value_data = &cloned_rollback;
251         *clone_size = sizeof(cloned_rollback);
252     }
253     // clear the dirty bit, because the node has been cloned
254     log->dirty = 0;
255     new_attr->is_valid = false;
256 }
257 
258