~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to storage/innobase/trx/trx0purge.c

  • Committer: brian
  • Date: 2008-06-25 05:29:13 UTC
  • Revision ID: brian@localhost.localdomain-20080625052913-6upwo0jsrl4lnapl
clean slate

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/******************************************************
 
2
Purge old versions
 
3
 
 
4
(c) 1996 Innobase Oy
 
5
 
 
6
Created 3/26/1996 Heikki Tuuri
 
7
*******************************************************/
 
8
 
 
9
#include "trx0purge.h"
 
10
 
 
11
#ifdef UNIV_NONINL
 
12
#include "trx0purge.ic"
 
13
#endif
 
14
 
 
15
#include "fsp0fsp.h"
 
16
#include "mach0data.h"
 
17
#include "trx0rseg.h"
 
18
#include "trx0trx.h"
 
19
#include "trx0roll.h"
 
20
#include "read0read.h"
 
21
#include "fut0fut.h"
 
22
#include "que0que.h"
 
23
#include "row0purge.h"
 
24
#include "row0upd.h"
 
25
#include "trx0rec.h"
 
26
#include "srv0que.h"
 
27
#include "os0thread.h"
 
28
 
 
29
/* The global data structure coordinating a purge */
 
30
trx_purge_t*    purge_sys = NULL;
 
31
 
 
32
/* A dummy undo record used as a return value when we have a whole undo log
 
33
which needs no purge */
 
34
trx_undo_rec_t  trx_purge_dummy_rec;
 
35
 
 
36
/*********************************************************************
 
37
Checks if trx_id is >= purge_view: then it is guaranteed that its update
 
38
undo log still exists in the system. */
 
39
 
 
40
ibool
 
41
trx_purge_update_undo_must_exist(
 
42
/*=============================*/
 
43
                        /* out: TRUE if is sure that it is preserved, also
 
44
                        if the function returns FALSE, it is possible that
 
45
                        the undo log still exists in the system */
 
46
        dulint  trx_id) /* in: transaction id */
 
47
{
 
48
#ifdef UNIV_SYNC_DEBUG
 
49
        ut_ad(rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED));
 
50
#endif /* UNIV_SYNC_DEBUG */
 
51
 
 
52
        if (!read_view_sees_trx_id(purge_sys->view, trx_id)) {
 
53
 
 
54
                return(TRUE);
 
55
        }
 
56
 
 
57
        return(FALSE);
 
58
}
 
59
 
 
60
/*=================== PURGE RECORD ARRAY =============================*/
 
61
 
 
62
/***********************************************************************
 
63
Stores info of an undo log record during a purge. */
 
64
static
 
65
trx_undo_inf_t*
 
66
trx_purge_arr_store_info(
 
67
/*=====================*/
 
68
                        /* out: pointer to the storage cell */
 
69
        dulint  trx_no, /* in: transaction number */
 
70
        dulint  undo_no)/* in: undo number */
 
71
{
 
72
        trx_undo_inf_t* cell;
 
73
        trx_undo_arr_t* arr;
 
74
        ulint           i;
 
75
 
 
76
        arr = purge_sys->arr;
 
77
 
 
78
        for (i = 0;; i++) {
 
79
                cell = trx_undo_arr_get_nth_info(arr, i);
 
80
 
 
81
                if (!(cell->in_use)) {
 
82
                        /* Not in use, we may store here */
 
83
                        cell->undo_no = undo_no;
 
84
                        cell->trx_no = trx_no;
 
85
                        cell->in_use = TRUE;
 
86
 
 
87
                        arr->n_used++;
 
88
 
 
89
                        return(cell);
 
90
                }
 
91
        }
 
92
}
 
93
 
 
94
/***********************************************************************
 
95
Removes info of an undo log record during a purge. */
 
96
UNIV_INLINE
 
97
void
 
98
trx_purge_arr_remove_info(
 
99
/*======================*/
 
100
        trx_undo_inf_t* cell)   /* in: pointer to the storage cell */
 
101
{
 
102
        trx_undo_arr_t* arr;
 
103
 
 
104
        arr = purge_sys->arr;
 
105
 
 
106
        cell->in_use = FALSE;
 
107
 
 
108
        ut_ad(arr->n_used > 0);
 
109
 
 
110
        arr->n_used--;
 
111
}
 
112
 
 
113
/***********************************************************************
 
114
Gets the biggest pair of a trx number and an undo number in a purge array. */
 
115
static
 
116
void
 
117
trx_purge_arr_get_biggest(
 
118
/*======================*/
 
119
        trx_undo_arr_t* arr,    /* in: purge array */
 
120
        dulint*         trx_no, /* out: transaction number: ut_dulint_zero
 
121
                                if array is empty */
 
122
        dulint*         undo_no)/* out: undo number */
 
123
{
 
124
        trx_undo_inf_t* cell;
 
125
        dulint          pair_trx_no;
 
126
        dulint          pair_undo_no;
 
127
        int             trx_cmp;
 
128
        ulint           n_used;
 
129
        ulint           i;
 
130
        ulint           n;
 
131
 
 
132
        n = 0;
 
133
        n_used = arr->n_used;
 
134
        pair_trx_no = ut_dulint_zero;
 
135
        pair_undo_no = ut_dulint_zero;
 
136
 
 
137
        for (i = 0;; i++) {
 
138
                cell = trx_undo_arr_get_nth_info(arr, i);
 
139
 
 
140
                if (cell->in_use) {
 
141
                        n++;
 
142
                        trx_cmp = ut_dulint_cmp(cell->trx_no, pair_trx_no);
 
143
 
 
144
                        if ((trx_cmp > 0)
 
145
                            || ((trx_cmp == 0)
 
146
                                && (ut_dulint_cmp(cell->undo_no,
 
147
                                                  pair_undo_no) >= 0))) {
 
148
 
 
149
                                pair_trx_no = cell->trx_no;
 
150
                                pair_undo_no = cell->undo_no;
 
151
                        }
 
152
                }
 
153
 
 
154
                if (n == n_used) {
 
155
                        *trx_no = pair_trx_no;
 
156
                        *undo_no = pair_undo_no;
 
157
 
 
158
                        return;
 
159
                }
 
160
        }
 
161
}
 
162
 
 
163
/********************************************************************
 
164
Builds a purge 'query' graph. The actual purge is performed by executing
 
165
this query graph. */
 
166
static
 
167
que_t*
 
168
trx_purge_graph_build(void)
 
169
/*=======================*/
 
170
                                /* out, own: the query graph */
 
171
{
 
172
        mem_heap_t*     heap;
 
173
        que_fork_t*     fork;
 
174
        que_thr_t*      thr;
 
175
        /*      que_thr_t*      thr2; */
 
176
 
 
177
        heap = mem_heap_create(512);
 
178
        fork = que_fork_create(NULL, NULL, QUE_FORK_PURGE, heap);
 
179
        fork->trx = purge_sys->trx;
 
180
 
 
181
        thr = que_thr_create(fork, heap);
 
182
 
 
183
        thr->child = row_purge_node_create(thr, heap);
 
184
 
 
185
        /*      thr2 = que_thr_create(fork, fork, heap);
 
186
 
 
187
        thr2->child = row_purge_node_create(fork, thr2, heap);   */
 
188
 
 
189
        return(fork);
 
190
}
 
191
 
 
192
/************************************************************************
 
193
Creates the global purge system control structure and inits the history
 
194
mutex. */
 
195
 
 
196
void
 
197
trx_purge_sys_create(void)
 
198
/*======================*/
 
199
{
 
200
        ut_ad(mutex_own(&kernel_mutex));
 
201
 
 
202
        purge_sys = mem_alloc(sizeof(trx_purge_t));
 
203
 
 
204
        purge_sys->state = TRX_STOP_PURGE;
 
205
 
 
206
        purge_sys->n_pages_handled = 0;
 
207
 
 
208
        purge_sys->purge_trx_no = ut_dulint_zero;
 
209
        purge_sys->purge_undo_no = ut_dulint_zero;
 
210
        purge_sys->next_stored = FALSE;
 
211
 
 
212
        rw_lock_create(&purge_sys->latch, SYNC_PURGE_LATCH);
 
213
 
 
214
        mutex_create(&purge_sys->mutex, SYNC_PURGE_SYS);
 
215
 
 
216
        purge_sys->heap = mem_heap_create(256);
 
217
 
 
218
        purge_sys->arr = trx_undo_arr_create();
 
219
 
 
220
        purge_sys->sess = sess_open();
 
221
 
 
222
        purge_sys->trx = purge_sys->sess->trx;
 
223
 
 
224
        purge_sys->trx->is_purge = 1;
 
225
 
 
226
        ut_a(trx_start_low(purge_sys->trx, ULINT_UNDEFINED));
 
227
 
 
228
        purge_sys->query = trx_purge_graph_build();
 
229
 
 
230
        purge_sys->view = read_view_oldest_copy_or_open_new(ut_dulint_zero,
 
231
                                                            purge_sys->heap);
 
232
}
 
233
 
 
234
/*================ UNDO LOG HISTORY LIST =============================*/
 
235
 
 
236
/************************************************************************
 
237
Adds the update undo log as the first log in the history list. Removes the
 
238
update undo log segment from the rseg slot if it is too big for reuse. */
 
239
 
 
240
void
 
241
trx_purge_add_update_undo_to_history(
 
242
/*=================================*/
 
243
        trx_t*  trx,            /* in: transaction */
 
244
        page_t* undo_page,      /* in: update undo log header page,
 
245
                                x-latched */
 
246
        mtr_t*  mtr)            /* in: mtr */
 
247
{
 
248
        trx_undo_t*     undo;
 
249
        trx_rseg_t*     rseg;
 
250
        trx_rsegf_t*    rseg_header;
 
251
        trx_usegf_t*    seg_header;
 
252
        trx_ulogf_t*    undo_header;
 
253
        trx_upagef_t*   page_header;
 
254
        ulint           hist_size;
 
255
 
 
256
        undo = trx->update_undo;
 
257
 
 
258
        ut_ad(undo);
 
259
 
 
260
        rseg = undo->rseg;
 
261
 
 
262
        ut_ad(mutex_own(&(rseg->mutex)));
 
263
 
 
264
        rseg_header = trx_rsegf_get(rseg->space, rseg->page_no, mtr);
 
265
 
 
266
        undo_header = undo_page + undo->hdr_offset;
 
267
        seg_header  = undo_page + TRX_UNDO_SEG_HDR;
 
268
        page_header = undo_page + TRX_UNDO_PAGE_HDR;
 
269
 
 
270
        if (undo->state != TRX_UNDO_CACHED) {
 
271
                /* The undo log segment will not be reused */
 
272
 
 
273
                if (undo->id >= TRX_RSEG_N_SLOTS) {
 
274
                        fprintf(stderr,
 
275
                                "InnoDB: Error: undo->id is %lu\n",
 
276
                                (ulong) undo->id);
 
277
                        ut_error;
 
278
                }
 
279
 
 
280
                trx_rsegf_set_nth_undo(rseg_header, undo->id, FIL_NULL, mtr);
 
281
 
 
282
                hist_size = mtr_read_ulint(rseg_header + TRX_RSEG_HISTORY_SIZE,
 
283
                                           MLOG_4BYTES, mtr);
 
284
                ut_ad(undo->size == flst_get_len(
 
285
                              seg_header + TRX_UNDO_PAGE_LIST, mtr));
 
286
 
 
287
                mlog_write_ulint(rseg_header + TRX_RSEG_HISTORY_SIZE,
 
288
                                 hist_size + undo->size, MLOG_4BYTES, mtr);
 
289
        }
 
290
 
 
291
        /* Add the log as the first in the history list */
 
292
        flst_add_first(rseg_header + TRX_RSEG_HISTORY,
 
293
                       undo_header + TRX_UNDO_HISTORY_NODE, mtr);
 
294
        mutex_enter(&kernel_mutex);
 
295
        trx_sys->rseg_history_len++;
 
296
        mutex_exit(&kernel_mutex);
 
297
 
 
298
        /* Write the trx number to the undo log header */
 
299
        mlog_write_dulint(undo_header + TRX_UNDO_TRX_NO, trx->no, mtr);
 
300
        /* Write information about delete markings to the undo log header */
 
301
 
 
302
        if (!undo->del_marks) {
 
303
                mlog_write_ulint(undo_header + TRX_UNDO_DEL_MARKS, FALSE,
 
304
                                 MLOG_2BYTES, mtr);
 
305
        }
 
306
 
 
307
        if (rseg->last_page_no == FIL_NULL) {
 
308
 
 
309
                rseg->last_page_no = undo->hdr_page_no;
 
310
                rseg->last_offset = undo->hdr_offset;
 
311
                rseg->last_trx_no = trx->no;
 
312
                rseg->last_del_marks = undo->del_marks;
 
313
        }
 
314
}
 
315
 
 
316
/**************************************************************************
 
317
Frees an undo log segment which is in the history list. Cuts the end of the
 
318
history list at the youngest undo log in this segment. */
 
319
static
 
320
void
 
321
trx_purge_free_segment(
 
322
/*===================*/
 
323
        trx_rseg_t*     rseg,           /* in: rollback segment */
 
324
        fil_addr_t      hdr_addr,       /* in: the file address of log_hdr */
 
325
        ulint           n_removed_logs) /* in: count of how many undo logs we
 
326
                                        will cut off from the end of the
 
327
                                        history list */
 
328
{
 
329
        page_t*         undo_page;
 
330
        trx_rsegf_t*    rseg_hdr;
 
331
        trx_ulogf_t*    log_hdr;
 
332
        trx_usegf_t*    seg_hdr;
 
333
        ibool           freed;
 
334
        ulint           seg_size;
 
335
        ulint           hist_size;
 
336
        ibool           marked          = FALSE;
 
337
        mtr_t           mtr;
 
338
 
 
339
        /*      fputs("Freeing an update undo log segment\n", stderr); */
 
340
 
 
341
        ut_ad(mutex_own(&(purge_sys->mutex)));
 
342
loop:
 
343
        mtr_start(&mtr);
 
344
        mutex_enter(&(rseg->mutex));
 
345
 
 
346
        rseg_hdr = trx_rsegf_get(rseg->space, rseg->page_no, &mtr);
 
347
 
 
348
        undo_page = trx_undo_page_get(rseg->space, hdr_addr.page, &mtr);
 
349
        seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
 
350
        log_hdr = undo_page + hdr_addr.boffset;
 
351
 
 
352
        /* Mark the last undo log totally purged, so that if the system
 
353
        crashes, the tail of the undo log will not get accessed again. The
 
354
        list of pages in the undo log tail gets inconsistent during the
 
355
        freeing of the segment, and therefore purge should not try to access
 
356
        them again. */
 
357
 
 
358
        if (!marked) {
 
359
                mlog_write_ulint(log_hdr + TRX_UNDO_DEL_MARKS, FALSE,
 
360
                                 MLOG_2BYTES, &mtr);
 
361
                marked = TRUE;
 
362
        }
 
363
 
 
364
        freed = fseg_free_step_not_header(seg_hdr + TRX_UNDO_FSEG_HEADER,
 
365
                                          &mtr);
 
366
        if (!freed) {
 
367
                mutex_exit(&(rseg->mutex));
 
368
                mtr_commit(&mtr);
 
369
 
 
370
                goto loop;
 
371
        }
 
372
 
 
373
        /* The page list may now be inconsistent, but the length field
 
374
        stored in the list base node tells us how big it was before we
 
375
        started the freeing. */
 
376
 
 
377
        seg_size = flst_get_len(seg_hdr + TRX_UNDO_PAGE_LIST, &mtr);
 
378
 
 
379
        /* We may free the undo log segment header page; it must be freed
 
380
        within the same mtr as the undo log header is removed from the
 
381
        history list: otherwise, in case of a database crash, the segment
 
382
        could become inaccessible garbage in the file space. */
 
383
 
 
384
        flst_cut_end(rseg_hdr + TRX_RSEG_HISTORY,
 
385
                     log_hdr + TRX_UNDO_HISTORY_NODE, n_removed_logs, &mtr);
 
386
 
 
387
        mutex_enter(&kernel_mutex);
 
388
        ut_ad(trx_sys->rseg_history_len >= n_removed_logs);
 
389
        trx_sys->rseg_history_len -= n_removed_logs;
 
390
        mutex_exit(&kernel_mutex);
 
391
 
 
392
        freed = FALSE;
 
393
 
 
394
        while (!freed) {
 
395
                /* Here we assume that a file segment with just the header
 
396
                page can be freed in a few steps, so that the buffer pool
 
397
                is not flooded with bufferfixed pages: see the note in
 
398
                fsp0fsp.c. */
 
399
 
 
400
                freed = fseg_free_step(seg_hdr + TRX_UNDO_FSEG_HEADER,
 
401
                                       &mtr);
 
402
        }
 
403
 
 
404
        hist_size = mtr_read_ulint(rseg_hdr + TRX_RSEG_HISTORY_SIZE,
 
405
                                   MLOG_4BYTES, &mtr);
 
406
        ut_ad(hist_size >= seg_size);
 
407
 
 
408
        mlog_write_ulint(rseg_hdr + TRX_RSEG_HISTORY_SIZE,
 
409
                         hist_size - seg_size, MLOG_4BYTES, &mtr);
 
410
 
 
411
        ut_ad(rseg->curr_size >= seg_size);
 
412
 
 
413
        rseg->curr_size -= seg_size;
 
414
 
 
415
        mutex_exit(&(rseg->mutex));
 
416
 
 
417
        mtr_commit(&mtr);
 
418
}
 
419
 
 
420
/************************************************************************
 
421
Removes unnecessary history data from a rollback segment. */
 
422
static
 
423
void
 
424
trx_purge_truncate_rseg_history(
 
425
/*============================*/
 
426
        trx_rseg_t*     rseg,           /* in: rollback segment */
 
427
        dulint          limit_trx_no,   /* in: remove update undo logs whose
 
428
                                        trx number is < limit_trx_no */
 
429
        dulint          limit_undo_no)  /* in: if transaction number is equal
 
430
                                        to limit_trx_no, truncate undo records
 
431
                                        with undo number < limit_undo_no */
 
432
{
 
433
        fil_addr_t      hdr_addr;
 
434
        fil_addr_t      prev_hdr_addr;
 
435
        trx_rsegf_t*    rseg_hdr;
 
436
        page_t*         undo_page;
 
437
        trx_ulogf_t*    log_hdr;
 
438
        trx_usegf_t*    seg_hdr;
 
439
        int             cmp;
 
440
        ulint           n_removed_logs  = 0;
 
441
        mtr_t           mtr;
 
442
 
 
443
        ut_ad(mutex_own(&(purge_sys->mutex)));
 
444
 
 
445
        mtr_start(&mtr);
 
446
        mutex_enter(&(rseg->mutex));
 
447
 
 
448
        rseg_hdr = trx_rsegf_get(rseg->space, rseg->page_no, &mtr);
 
449
 
 
450
        hdr_addr = trx_purge_get_log_from_hist(
 
451
                flst_get_last(rseg_hdr + TRX_RSEG_HISTORY, &mtr));
 
452
loop:
 
453
        if (hdr_addr.page == FIL_NULL) {
 
454
 
 
455
                mutex_exit(&(rseg->mutex));
 
456
 
 
457
                mtr_commit(&mtr);
 
458
 
 
459
                return;
 
460
        }
 
461
 
 
462
        undo_page = trx_undo_page_get(rseg->space, hdr_addr.page, &mtr);
 
463
 
 
464
        log_hdr = undo_page + hdr_addr.boffset;
 
465
 
 
466
        cmp = ut_dulint_cmp(mach_read_from_8(log_hdr + TRX_UNDO_TRX_NO),
 
467
                            limit_trx_no);
 
468
        if (cmp == 0) {
 
469
                trx_undo_truncate_start(rseg, rseg->space, hdr_addr.page,
 
470
                                        hdr_addr.boffset, limit_undo_no);
 
471
        }
 
472
 
 
473
        if (cmp >= 0) {
 
474
                mutex_enter(&kernel_mutex);
 
475
                ut_a(trx_sys->rseg_history_len >= n_removed_logs);
 
476
                trx_sys->rseg_history_len -= n_removed_logs;
 
477
                mutex_exit(&kernel_mutex);
 
478
 
 
479
                flst_truncate_end(rseg_hdr + TRX_RSEG_HISTORY,
 
480
                                  log_hdr + TRX_UNDO_HISTORY_NODE,
 
481
                                  n_removed_logs, &mtr);
 
482
 
 
483
                mutex_exit(&(rseg->mutex));
 
484
                mtr_commit(&mtr);
 
485
 
 
486
                return;
 
487
        }
 
488
 
 
489
        prev_hdr_addr = trx_purge_get_log_from_hist(
 
490
                flst_get_prev_addr(log_hdr + TRX_UNDO_HISTORY_NODE, &mtr));
 
491
        n_removed_logs++;
 
492
 
 
493
        seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
 
494
 
 
495
        if ((mach_read_from_2(seg_hdr + TRX_UNDO_STATE) == TRX_UNDO_TO_PURGE)
 
496
            && (mach_read_from_2(log_hdr + TRX_UNDO_NEXT_LOG) == 0)) {
 
497
 
 
498
                /* We can free the whole log segment */
 
499
 
 
500
                mutex_exit(&(rseg->mutex));
 
501
                mtr_commit(&mtr);
 
502
 
 
503
                trx_purge_free_segment(rseg, hdr_addr, n_removed_logs);
 
504
 
 
505
                n_removed_logs = 0;
 
506
        } else {
 
507
                mutex_exit(&(rseg->mutex));
 
508
                mtr_commit(&mtr);
 
509
        }
 
510
 
 
511
        mtr_start(&mtr);
 
512
        mutex_enter(&(rseg->mutex));
 
513
 
 
514
        rseg_hdr = trx_rsegf_get(rseg->space, rseg->page_no, &mtr);
 
515
 
 
516
        hdr_addr = prev_hdr_addr;
 
517
 
 
518
        goto loop;
 
519
}
 
520
 
 
521
/************************************************************************
 
522
Removes unnecessary history data from rollback segments. NOTE that when this
 
523
function is called, the caller must not have any latches on undo log pages! */
 
524
static
 
525
void
 
526
trx_purge_truncate_history(void)
 
527
/*============================*/
 
528
{
 
529
        trx_rseg_t*     rseg;
 
530
        dulint          limit_trx_no;
 
531
        dulint          limit_undo_no;
 
532
 
 
533
        ut_ad(mutex_own(&(purge_sys->mutex)));
 
534
 
 
535
        trx_purge_arr_get_biggest(purge_sys->arr, &limit_trx_no,
 
536
                                  &limit_undo_no);
 
537
 
 
538
        if (ut_dulint_cmp(limit_trx_no, ut_dulint_zero) == 0) {
 
539
 
 
540
                limit_trx_no = purge_sys->purge_trx_no;
 
541
                limit_undo_no = purge_sys->purge_undo_no;
 
542
        }
 
543
 
 
544
        /* We play safe and set the truncate limit at most to the purge view
 
545
        low_limit number, though this is not necessary */
 
546
 
 
547
        if (ut_dulint_cmp(limit_trx_no, purge_sys->view->low_limit_no) >= 0) {
 
548
                limit_trx_no = purge_sys->view->low_limit_no;
 
549
                limit_undo_no = ut_dulint_zero;
 
550
        }
 
551
 
 
552
        ut_ad((ut_dulint_cmp(limit_trx_no,
 
553
                             purge_sys->view->low_limit_no) <= 0));
 
554
 
 
555
        rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list);
 
556
 
 
557
        while (rseg) {
 
558
                trx_purge_truncate_rseg_history(rseg, limit_trx_no,
 
559
                                                limit_undo_no);
 
560
                rseg = UT_LIST_GET_NEXT(rseg_list, rseg);
 
561
        }
 
562
}
 
563
 
 
564
/************************************************************************
 
565
Does a truncate if the purge array is empty. NOTE that when this function is
 
566
called, the caller must not have any latches on undo log pages! */
 
567
UNIV_INLINE
 
568
ibool
 
569
trx_purge_truncate_if_arr_empty(void)
 
570
/*=================================*/
 
571
                        /* out: TRUE if array empty */
 
572
{
 
573
        ut_ad(mutex_own(&(purge_sys->mutex)));
 
574
 
 
575
        if (purge_sys->arr->n_used == 0) {
 
576
 
 
577
                trx_purge_truncate_history();
 
578
 
 
579
                return(TRUE);
 
580
        }
 
581
 
 
582
        return(FALSE);
 
583
}
 
584
 
 
585
/***************************************************************************
 
586
Updates the last not yet purged history log info in rseg when we have purged
 
587
a whole undo log. Advances also purge_sys->purge_trx_no past the purged log. */
 
588
static
 
589
void
 
590
trx_purge_rseg_get_next_history_log(
 
591
/*================================*/
 
592
        trx_rseg_t*     rseg)   /* in: rollback segment */
 
593
{
 
594
        page_t*         undo_page;
 
595
        trx_ulogf_t*    log_hdr;
 
596
        trx_usegf_t*    seg_hdr;
 
597
        fil_addr_t      prev_log_addr;
 
598
        dulint          trx_no;
 
599
        ibool           del_marks;
 
600
        mtr_t           mtr;
 
601
 
 
602
        ut_ad(mutex_own(&(purge_sys->mutex)));
 
603
 
 
604
        mutex_enter(&(rseg->mutex));
 
605
 
 
606
        ut_a(rseg->last_page_no != FIL_NULL);
 
607
 
 
608
        purge_sys->purge_trx_no = ut_dulint_add(rseg->last_trx_no, 1);
 
609
        purge_sys->purge_undo_no = ut_dulint_zero;
 
610
        purge_sys->next_stored = FALSE;
 
611
 
 
612
        mtr_start(&mtr);
 
613
 
 
614
        undo_page = trx_undo_page_get_s_latched(rseg->space,
 
615
                                                rseg->last_page_no, &mtr);
 
616
        log_hdr = undo_page + rseg->last_offset;
 
617
        seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
 
618
 
 
619
        /* Increase the purge page count by one for every handled log */
 
620
 
 
621
        purge_sys->n_pages_handled++;
 
622
 
 
623
        prev_log_addr = trx_purge_get_log_from_hist(
 
624
                flst_get_prev_addr(log_hdr + TRX_UNDO_HISTORY_NODE, &mtr));
 
625
        if (prev_log_addr.page == FIL_NULL) {
 
626
                /* No logs left in the history list */
 
627
 
 
628
                rseg->last_page_no = FIL_NULL;
 
629
 
 
630
                mutex_exit(&(rseg->mutex));
 
631
                mtr_commit(&mtr);
 
632
 
 
633
                mutex_enter(&kernel_mutex);
 
634
 
 
635
                /* Add debug code to track history list corruption reported
 
636
                on the MySQL mailing list on Nov 9, 2004. The fut0lst.c
 
637
                file-based list was corrupt. The prev node pointer was
 
638
                FIL_NULL, even though the list length was over 8 million nodes!
 
639
                We assume that purge truncates the history list in moderate
 
640
                size pieces, and if we here reach the head of the list, the
 
641
                list cannot be longer than 20 000 undo logs now. */
 
642
 
 
643
                if (trx_sys->rseg_history_len > 20000) {
 
644
                        ut_print_timestamp(stderr);
 
645
                        fprintf(stderr,
 
646
                                "  InnoDB: Warning: purge reached the"
 
647
                                " head of the history list,\n"
 
648
                                "InnoDB: but its length is still"
 
649
                                " reported as %lu! Make a detailed bug\n"
 
650
                                "InnoDB: report, and submit it"
 
651
                                " to http://bugs.mysql.com\n",
 
652
                                (ulong) trx_sys->rseg_history_len);
 
653
                }
 
654
 
 
655
                mutex_exit(&kernel_mutex);
 
656
 
 
657
                return;
 
658
        }
 
659
 
 
660
        mutex_exit(&(rseg->mutex));
 
661
        mtr_commit(&mtr);
 
662
 
 
663
        /* Read the trx number and del marks from the previous log header */
 
664
        mtr_start(&mtr);
 
665
 
 
666
        log_hdr = trx_undo_page_get_s_latched(rseg->space,
 
667
                                              prev_log_addr.page, &mtr)
 
668
                + prev_log_addr.boffset;
 
669
 
 
670
        trx_no = mach_read_from_8(log_hdr + TRX_UNDO_TRX_NO);
 
671
 
 
672
        del_marks = mach_read_from_2(log_hdr + TRX_UNDO_DEL_MARKS);
 
673
 
 
674
        mtr_commit(&mtr);
 
675
 
 
676
        mutex_enter(&(rseg->mutex));
 
677
 
 
678
        rseg->last_page_no = prev_log_addr.page;
 
679
        rseg->last_offset = prev_log_addr.boffset;
 
680
        rseg->last_trx_no = trx_no;
 
681
        rseg->last_del_marks = del_marks;
 
682
 
 
683
        mutex_exit(&(rseg->mutex));
 
684
}
 
685
 
 
686
/***************************************************************************
 
687
Chooses the next undo log to purge and updates the info in purge_sys. This
 
688
function is used to initialize purge_sys when the next record to purge is
 
689
not known, and also to update the purge system info on the next record when
 
690
purge has handled the whole undo log for a transaction. */
 
691
static
 
692
void
 
693
trx_purge_choose_next_log(void)
 
694
/*===========================*/
 
695
{
 
696
        trx_undo_rec_t* rec;
 
697
        trx_rseg_t*     rseg;
 
698
        trx_rseg_t*     min_rseg;
 
699
        dulint          min_trx_no;
 
700
        ulint           space = 0;   /* remove warning (??? bug ???) */
 
701
        ulint           page_no = 0; /* remove warning (??? bug ???) */
 
702
        ulint           offset = 0;  /* remove warning (??? bug ???) */
 
703
        mtr_t           mtr;
 
704
 
 
705
        ut_ad(mutex_own(&(purge_sys->mutex)));
 
706
        ut_ad(purge_sys->next_stored == FALSE);
 
707
 
 
708
        rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list);
 
709
 
 
710
        min_trx_no = ut_dulint_max;
 
711
 
 
712
        min_rseg = NULL;
 
713
 
 
714
        while (rseg) {
 
715
                mutex_enter(&(rseg->mutex));
 
716
 
 
717
                if (rseg->last_page_no != FIL_NULL) {
 
718
 
 
719
                        if ((min_rseg == NULL)
 
720
                            || (ut_dulint_cmp(min_trx_no,
 
721
                                              rseg->last_trx_no) > 0)) {
 
722
 
 
723
                                min_rseg = rseg;
 
724
                                min_trx_no = rseg->last_trx_no;
 
725
                                space = rseg->space;
 
726
                                ut_a(space == 0); /* We assume in purge of
 
727
                                                  externally stored fields
 
728
                                                  that space id == 0 */
 
729
                                page_no = rseg->last_page_no;
 
730
                                offset = rseg->last_offset;
 
731
                        }
 
732
                }
 
733
 
 
734
                mutex_exit(&(rseg->mutex));
 
735
 
 
736
                rseg = UT_LIST_GET_NEXT(rseg_list, rseg);
 
737
        }
 
738
 
 
739
        if (min_rseg == NULL) {
 
740
 
 
741
                return;
 
742
        }
 
743
 
 
744
        mtr_start(&mtr);
 
745
 
 
746
        if (!min_rseg->last_del_marks) {
 
747
                /* No need to purge this log */
 
748
 
 
749
                rec = &trx_purge_dummy_rec;
 
750
        } else {
 
751
                rec = trx_undo_get_first_rec(space, page_no, offset,
 
752
                                             RW_S_LATCH, &mtr);
 
753
                if (rec == NULL) {
 
754
                        /* Undo log empty */
 
755
 
 
756
                        rec = &trx_purge_dummy_rec;
 
757
                }
 
758
        }
 
759
 
 
760
        purge_sys->next_stored = TRUE;
 
761
        purge_sys->rseg = min_rseg;
 
762
 
 
763
        purge_sys->hdr_page_no = page_no;
 
764
        purge_sys->hdr_offset = offset;
 
765
 
 
766
        purge_sys->purge_trx_no = min_trx_no;
 
767
 
 
768
        if (rec == &trx_purge_dummy_rec) {
 
769
 
 
770
                purge_sys->purge_undo_no = ut_dulint_zero;
 
771
                purge_sys->page_no = page_no;
 
772
                purge_sys->offset = 0;
 
773
        } else {
 
774
                purge_sys->purge_undo_no = trx_undo_rec_get_undo_no(rec);
 
775
 
 
776
                purge_sys->page_no = buf_frame_get_page_no(rec);
 
777
                purge_sys->offset = rec - buf_frame_align(rec);
 
778
        }
 
779
 
 
780
        mtr_commit(&mtr);
 
781
}
 
782
 
 
783
/***************************************************************************
 
784
Gets the next record to purge and updates the info in the purge system. */
 
785
static
 
786
trx_undo_rec_t*
 
787
trx_purge_get_next_rec(
 
788
/*===================*/
 
789
                                /* out: copy of an undo log record or
 
790
                                pointer to the dummy undo log record */
 
791
        mem_heap_t*     heap)   /* in: memory heap where copied */
 
792
{
 
793
        trx_undo_rec_t* rec;
 
794
        trx_undo_rec_t* rec_copy;
 
795
        trx_undo_rec_t* rec2;
 
796
        trx_undo_rec_t* next_rec;
 
797
        page_t*         undo_page;
 
798
        page_t*         page;
 
799
        ulint           offset;
 
800
        ulint           page_no;
 
801
        ulint           space;
 
802
        ulint           type;
 
803
        ulint           cmpl_info;
 
804
        mtr_t           mtr;
 
805
 
 
806
        ut_ad(mutex_own(&(purge_sys->mutex)));
 
807
        ut_ad(purge_sys->next_stored);
 
808
 
 
809
        space = purge_sys->rseg->space;
 
810
        page_no = purge_sys->page_no;
 
811
        offset = purge_sys->offset;
 
812
 
 
813
        if (offset == 0) {
 
814
                /* It is the dummy undo log record, which means that there is
 
815
                no need to purge this undo log */
 
816
 
 
817
                trx_purge_rseg_get_next_history_log(purge_sys->rseg);
 
818
 
 
819
                /* Look for the next undo log and record to purge */
 
820
 
 
821
                trx_purge_choose_next_log();
 
822
 
 
823
                return(&trx_purge_dummy_rec);
 
824
        }
 
825
 
 
826
        mtr_start(&mtr);
 
827
 
 
828
        undo_page = trx_undo_page_get_s_latched(space, page_no, &mtr);
 
829
        rec = undo_page + offset;
 
830
 
 
831
        rec2 = rec;
 
832
 
 
833
        for (;;) {
 
834
                /* Try first to find the next record which requires a purge
 
835
                operation from the same page of the same undo log */
 
836
 
 
837
                next_rec = trx_undo_page_get_next_rec(rec2,
 
838
                                                      purge_sys->hdr_page_no,
 
839
                                                      purge_sys->hdr_offset);
 
840
                if (next_rec == NULL) {
 
841
                        rec2 = trx_undo_get_next_rec(
 
842
                                rec2, purge_sys->hdr_page_no,
 
843
                                purge_sys->hdr_offset, &mtr);
 
844
                        break;
 
845
                }
 
846
 
 
847
                rec2 = next_rec;
 
848
 
 
849
                type = trx_undo_rec_get_type(rec2);
 
850
 
 
851
                if (type == TRX_UNDO_DEL_MARK_REC) {
 
852
 
 
853
                        break;
 
854
                }
 
855
 
 
856
                cmpl_info = trx_undo_rec_get_cmpl_info(rec2);
 
857
 
 
858
                if (trx_undo_rec_get_extern_storage(rec2)) {
 
859
                        break;
 
860
                }
 
861
 
 
862
                if ((type == TRX_UNDO_UPD_EXIST_REC)
 
863
                    && !(cmpl_info & UPD_NODE_NO_ORD_CHANGE)) {
 
864
                        break;
 
865
                }
 
866
        }
 
867
 
 
868
        if (rec2 == NULL) {
 
869
                mtr_commit(&mtr);
 
870
 
 
871
                trx_purge_rseg_get_next_history_log(purge_sys->rseg);
 
872
 
 
873
                /* Look for the next undo log and record to purge */
 
874
 
 
875
                trx_purge_choose_next_log();
 
876
 
 
877
                mtr_start(&mtr);
 
878
 
 
879
                undo_page = trx_undo_page_get_s_latched(space, page_no, &mtr);
 
880
 
 
881
                rec = undo_page + offset;
 
882
        } else {
 
883
                page = buf_frame_align(rec2);
 
884
 
 
885
                purge_sys->purge_undo_no = trx_undo_rec_get_undo_no(rec2);
 
886
                purge_sys->page_no = buf_frame_get_page_no(page);
 
887
                purge_sys->offset = rec2 - page;
 
888
 
 
889
                if (undo_page != page) {
 
890
                        /* We advance to a new page of the undo log: */
 
891
                        purge_sys->n_pages_handled++;
 
892
                }
 
893
        }
 
894
 
 
895
        rec_copy = trx_undo_rec_copy(rec, heap);
 
896
 
 
897
        mtr_commit(&mtr);
 
898
 
 
899
        return(rec_copy);
 
900
}
 
901
 
 
902
/************************************************************************
 
903
Fetches the next undo log record from the history list to purge. It must be
 
904
released with the corresponding release function. */
 
905
 
 
906
trx_undo_rec_t*
 
907
trx_purge_fetch_next_rec(
 
908
/*=====================*/
 
909
                                /* out: copy of an undo log record or
 
910
                                pointer to the dummy undo log record
 
911
                                &trx_purge_dummy_rec, if the whole undo log
 
912
                                can skipped in purge; NULL if none left */
 
913
        dulint*         roll_ptr,/* out: roll pointer to undo record */
 
914
        trx_undo_inf_t** cell,  /* out: storage cell for the record in the
 
915
                                purge array */
 
916
        mem_heap_t*     heap)   /* in: memory heap where copied */
 
917
{
 
918
        trx_undo_rec_t* undo_rec;
 
919
 
 
920
        mutex_enter(&(purge_sys->mutex));
 
921
 
 
922
        if (purge_sys->state == TRX_STOP_PURGE) {
 
923
                trx_purge_truncate_if_arr_empty();
 
924
 
 
925
                mutex_exit(&(purge_sys->mutex));
 
926
 
 
927
                return(NULL);
 
928
        }
 
929
 
 
930
        if (!purge_sys->next_stored) {
 
931
                trx_purge_choose_next_log();
 
932
 
 
933
                if (!purge_sys->next_stored) {
 
934
                        purge_sys->state = TRX_STOP_PURGE;
 
935
 
 
936
                        trx_purge_truncate_if_arr_empty();
 
937
 
 
938
                        if (srv_print_thread_releases) {
 
939
                                fprintf(stderr,
 
940
                                        "Purge: No logs left in the"
 
941
                                        " history list; pages handled %lu\n",
 
942
                                        (ulong) purge_sys->n_pages_handled);
 
943
                        }
 
944
 
 
945
                        mutex_exit(&(purge_sys->mutex));
 
946
 
 
947
                        return(NULL);
 
948
                }
 
949
        }
 
950
 
 
951
        if (purge_sys->n_pages_handled >= purge_sys->handle_limit) {
 
952
 
 
953
                purge_sys->state = TRX_STOP_PURGE;
 
954
 
 
955
                trx_purge_truncate_if_arr_empty();
 
956
 
 
957
                mutex_exit(&(purge_sys->mutex));
 
958
 
 
959
                return(NULL);
 
960
        }
 
961
 
 
962
        if (ut_dulint_cmp(purge_sys->purge_trx_no,
 
963
                          purge_sys->view->low_limit_no) >= 0) {
 
964
                purge_sys->state = TRX_STOP_PURGE;
 
965
 
 
966
                trx_purge_truncate_if_arr_empty();
 
967
 
 
968
                mutex_exit(&(purge_sys->mutex));
 
969
 
 
970
                return(NULL);
 
971
        }
 
972
 
 
973
        /*      fprintf(stderr, "Thread %lu purging trx %lu undo record %lu\n",
 
974
        os_thread_get_curr_id(),
 
975
        ut_dulint_get_low(purge_sys->purge_trx_no),
 
976
        ut_dulint_get_low(purge_sys->purge_undo_no)); */
 
977
 
 
978
        *roll_ptr = trx_undo_build_roll_ptr(FALSE, (purge_sys->rseg)->id,
 
979
                                            purge_sys->page_no,
 
980
                                            purge_sys->offset);
 
981
 
 
982
        *cell = trx_purge_arr_store_info(purge_sys->purge_trx_no,
 
983
                                         purge_sys->purge_undo_no);
 
984
 
 
985
        ut_ad(ut_dulint_cmp(purge_sys->purge_trx_no,
 
986
                            (purge_sys->view)->low_limit_no) < 0);
 
987
 
 
988
        /* The following call will advance the stored values of purge_trx_no
 
989
        and purge_undo_no, therefore we had to store them first */
 
990
 
 
991
        undo_rec = trx_purge_get_next_rec(heap);
 
992
 
 
993
        mutex_exit(&(purge_sys->mutex));
 
994
 
 
995
        return(undo_rec);
 
996
}
 
997
 
 
998
/***********************************************************************
 
999
Releases a reserved purge undo record. */
 
1000
 
 
1001
void
 
1002
trx_purge_rec_release(
 
1003
/*==================*/
 
1004
        trx_undo_inf_t* cell)   /* in: storage cell */
 
1005
{
 
1006
        trx_undo_arr_t* arr;
 
1007
 
 
1008
        mutex_enter(&(purge_sys->mutex));
 
1009
 
 
1010
        arr = purge_sys->arr;
 
1011
 
 
1012
        trx_purge_arr_remove_info(cell);
 
1013
 
 
1014
        mutex_exit(&(purge_sys->mutex));
 
1015
}
 
1016
 
 
1017
/***********************************************************************
 
1018
This function runs a purge batch. */
 
1019
 
 
1020
ulint
 
1021
trx_purge(void)
 
1022
/*===========*/
 
1023
                                /* out: number of undo log pages handled in
 
1024
                                the batch */
 
1025
{
 
1026
        que_thr_t*      thr;
 
1027
        /*      que_thr_t*      thr2; */
 
1028
        ulint           old_pages_handled;
 
1029
 
 
1030
        mutex_enter(&(purge_sys->mutex));
 
1031
 
 
1032
        if (purge_sys->trx->n_active_thrs > 0) {
 
1033
 
 
1034
                mutex_exit(&(purge_sys->mutex));
 
1035
 
 
1036
                /* Should not happen */
 
1037
 
 
1038
                ut_error;
 
1039
 
 
1040
                return(0);
 
1041
        }
 
1042
 
 
1043
        rw_lock_x_lock(&(purge_sys->latch));
 
1044
 
 
1045
        mutex_enter(&kernel_mutex);
 
1046
 
 
1047
        /* Close and free the old purge view */
 
1048
 
 
1049
        read_view_close(purge_sys->view);
 
1050
        purge_sys->view = NULL;
 
1051
        mem_heap_empty(purge_sys->heap);
 
1052
 
 
1053
        /* Determine how much data manipulation language (DML) statements
 
1054
        need to be delayed in order to reduce the lagging of the purge
 
1055
        thread. */
 
1056
        srv_dml_needed_delay = 0; /* in microseconds; default: no delay */
 
1057
 
 
1058
        /* If we cannot advance the 'purge view' because of an old
 
1059
        'consistent read view', then the DML statements cannot be delayed.
 
1060
        Also, srv_max_purge_lag <= 0 means 'infinity'. */
 
1061
        if (srv_max_purge_lag > 0
 
1062
            && !UT_LIST_GET_LAST(trx_sys->view_list)) {
 
1063
                float   ratio = (float) trx_sys->rseg_history_len
 
1064
                        / srv_max_purge_lag;
 
1065
                if (ratio > ULINT_MAX / 10000) {
 
1066
                        /* Avoid overflow: maximum delay is 4295 seconds */
 
1067
                        srv_dml_needed_delay = ULINT_MAX;
 
1068
                } else if (ratio > 1) {
 
1069
                        /* If the history list length exceeds the
 
1070
                        innodb_max_purge_lag, the
 
1071
                        data manipulation statements are delayed
 
1072
                        by at least 5000 microseconds. */
 
1073
                        srv_dml_needed_delay = (ulint) ((ratio - .5) * 10000);
 
1074
                }
 
1075
        }
 
1076
 
 
1077
        purge_sys->view = read_view_oldest_copy_or_open_new(ut_dulint_zero,
 
1078
                                                            purge_sys->heap);
 
1079
        mutex_exit(&kernel_mutex);
 
1080
 
 
1081
        rw_lock_x_unlock(&(purge_sys->latch));
 
1082
 
 
1083
        purge_sys->state = TRX_PURGE_ON;
 
1084
 
 
1085
        /* Handle at most 20 undo log pages in one purge batch */
 
1086
 
 
1087
        purge_sys->handle_limit = purge_sys->n_pages_handled + 20;
 
1088
 
 
1089
        old_pages_handled = purge_sys->n_pages_handled;
 
1090
 
 
1091
        mutex_exit(&(purge_sys->mutex));
 
1092
 
 
1093
        mutex_enter(&kernel_mutex);
 
1094
 
 
1095
        thr = que_fork_start_command(purge_sys->query);
 
1096
 
 
1097
        ut_ad(thr);
 
1098
 
 
1099
        /*      thr2 = que_fork_start_command(purge_sys->query);
 
1100
 
 
1101
        ut_ad(thr2); */
 
1102
 
 
1103
 
 
1104
        mutex_exit(&kernel_mutex);
 
1105
 
 
1106
        /*      srv_que_task_enqueue(thr2); */
 
1107
 
 
1108
        if (srv_print_thread_releases) {
 
1109
 
 
1110
                fputs("Starting purge\n", stderr);
 
1111
        }
 
1112
 
 
1113
        que_run_threads(thr);
 
1114
 
 
1115
        if (srv_print_thread_releases) {
 
1116
 
 
1117
                fprintf(stderr,
 
1118
                        "Purge ends; pages handled %lu\n",
 
1119
                        (ulong) purge_sys->n_pages_handled);
 
1120
        }
 
1121
 
 
1122
        return(purge_sys->n_pages_handled - old_pages_handled);
 
1123
}
 
1124
 
 
1125
/**********************************************************************
 
1126
Prints information of the purge system to stderr. */
 
1127
 
 
1128
void
 
1129
trx_purge_sys_print(void)
 
1130
/*=====================*/
 
1131
{
 
1132
        fprintf(stderr, "InnoDB: Purge system view:\n");
 
1133
        read_view_print(purge_sys->view);
 
1134
 
 
1135
        fprintf(stderr, "InnoDB: Purge trx n:o %lu %lu, undo n_o %lu %lu\n",
 
1136
                (ulong) ut_dulint_get_high(purge_sys->purge_trx_no),
 
1137
                (ulong) ut_dulint_get_low(purge_sys->purge_trx_no),
 
1138
                (ulong) ut_dulint_get_high(purge_sys->purge_undo_no),
 
1139
                (ulong) ut_dulint_get_low(purge_sys->purge_undo_no));
 
1140
        fprintf(stderr,
 
1141
                "InnoDB: Purge next stored %lu, page_no %lu, offset %lu,\n"
 
1142
                "InnoDB: Purge hdr_page_no %lu, hdr_offset %lu\n",
 
1143
                (ulong) purge_sys->next_stored,
 
1144
                (ulong) purge_sys->page_no,
 
1145
                (ulong) purge_sys->offset,
 
1146
                (ulong) purge_sys->hdr_page_no,
 
1147
                (ulong) purge_sys->hdr_offset);
 
1148
}