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/*======
5This file is part of PerconaFT.
6
7
8Copyright (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.
51static struct serialized_rollback_log_node cloned_rollback;
52
53// Cleanup the rollback memory
54static void
55rollback_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
62static 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
81static void
82toku_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.
111void 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
166int 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
180void 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
194int 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
208bool 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)
215int 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
221int 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
232void 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