~drizzle-trunk/drizzle/development

« back to all changes in this revision

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

Tags: innodb-plugin-1.0.1
Imported 1.0.1 with clean - with no changes.

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
UNIV_INTERN 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
UNIV_INTERN 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
UNIV_INTERN
 
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
UNIV_INTERN
 
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
UNIV_INTERN
 
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->zip_size,
 
265
                                    rseg->page_no, mtr);
 
266
 
 
267
        undo_header = undo_page + undo->hdr_offset;
 
268
        seg_header  = undo_page + TRX_UNDO_SEG_HDR;
 
269
        page_header = undo_page + TRX_UNDO_PAGE_HDR;
 
270
 
 
271
        if (undo->state != TRX_UNDO_CACHED) {
 
272
                /* The undo log segment will not be reused */
 
273
 
 
274
                if (undo->id >= TRX_RSEG_N_SLOTS) {
 
275
                        fprintf(stderr,
 
276
                                "InnoDB: Error: undo->id is %lu\n",
 
277
                                (ulong) undo->id);
 
278
                        ut_error;
 
279
                }
 
280
 
 
281
                trx_rsegf_set_nth_undo(rseg_header, undo->id, FIL_NULL, mtr);
 
282
 
 
283
                hist_size = mtr_read_ulint(rseg_header + TRX_RSEG_HISTORY_SIZE,
 
284
                                           MLOG_4BYTES, mtr);
 
285
                ut_ad(undo->size == flst_get_len(
 
286
                              seg_header + TRX_UNDO_PAGE_LIST, mtr));
 
287
 
 
288
                mlog_write_ulint(rseg_header + TRX_RSEG_HISTORY_SIZE,
 
289
                                 hist_size + undo->size, MLOG_4BYTES, mtr);
 
290
        }
 
291
 
 
292
        /* Add the log as the first in the history list */
 
293
        flst_add_first(rseg_header + TRX_RSEG_HISTORY,
 
294
                       undo_header + TRX_UNDO_HISTORY_NODE, mtr);
 
295
        mutex_enter(&kernel_mutex);
 
296
        trx_sys->rseg_history_len++;
 
297
        mutex_exit(&kernel_mutex);
 
298
 
 
299
        /* Write the trx number to the undo log header */
 
300
        mlog_write_dulint(undo_header + TRX_UNDO_TRX_NO, trx->no, mtr);
 
301
        /* Write information about delete markings to the undo log header */
 
302
 
 
303
        if (!undo->del_marks) {
 
304
                mlog_write_ulint(undo_header + TRX_UNDO_DEL_MARKS, FALSE,
 
305
                                 MLOG_2BYTES, mtr);
 
306
        }
 
307
 
 
308
        if (rseg->last_page_no == FIL_NULL) {
 
309
 
 
310
                rseg->last_page_no = undo->hdr_page_no;
 
311
                rseg->last_offset = undo->hdr_offset;
 
312
                rseg->last_trx_no = trx->no;
 
313
                rseg->last_del_marks = undo->del_marks;
 
314
        }
 
315
}
 
316
 
 
317
/**************************************************************************
 
318
Frees an undo log segment which is in the history list. Cuts the end of the
 
319
history list at the youngest undo log in this segment. */
 
320
static
 
321
void
 
322
trx_purge_free_segment(
 
323
/*===================*/
 
324
        trx_rseg_t*     rseg,           /* in: rollback segment */
 
325
        fil_addr_t      hdr_addr,       /* in: the file address of log_hdr */
 
326
        ulint           n_removed_logs) /* in: count of how many undo logs we
 
327
                                        will cut off from the end of the
 
328
                                        history list */
 
329
{
 
330
        page_t*         undo_page;
 
331
        trx_rsegf_t*    rseg_hdr;
 
332
        trx_ulogf_t*    log_hdr;
 
333
        trx_usegf_t*    seg_hdr;
 
334
        ibool           freed;
 
335
        ulint           seg_size;
 
336
        ulint           hist_size;
 
337
        ibool           marked          = FALSE;
 
338
        mtr_t           mtr;
 
339
 
 
340
        /*      fputs("Freeing an update undo log segment\n", stderr); */
 
341
 
 
342
        ut_ad(mutex_own(&(purge_sys->mutex)));
 
343
loop:
 
344
        mtr_start(&mtr);
 
345
        mutex_enter(&(rseg->mutex));
 
346
 
 
347
        rseg_hdr = trx_rsegf_get(rseg->space, rseg->zip_size,
 
348
                                 rseg->page_no, &mtr);
 
349
 
 
350
        undo_page = trx_undo_page_get(rseg->space, rseg->zip_size,
 
351
                                      hdr_addr.page, &mtr);
 
352
        seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
 
353
        log_hdr = undo_page + hdr_addr.boffset;
 
354
 
 
355
        /* Mark the last undo log totally purged, so that if the system
 
356
        crashes, the tail of the undo log will not get accessed again. The
 
357
        list of pages in the undo log tail gets inconsistent during the
 
358
        freeing of the segment, and therefore purge should not try to access
 
359
        them again. */
 
360
 
 
361
        if (!marked) {
 
362
                mlog_write_ulint(log_hdr + TRX_UNDO_DEL_MARKS, FALSE,
 
363
                                 MLOG_2BYTES, &mtr);
 
364
                marked = TRUE;
 
365
        }
 
366
 
 
367
        freed = fseg_free_step_not_header(seg_hdr + TRX_UNDO_FSEG_HEADER,
 
368
                                          &mtr);
 
369
        if (!freed) {
 
370
                mutex_exit(&(rseg->mutex));
 
371
                mtr_commit(&mtr);
 
372
 
 
373
                goto loop;
 
374
        }
 
375
 
 
376
        /* The page list may now be inconsistent, but the length field
 
377
        stored in the list base node tells us how big it was before we
 
378
        started the freeing. */
 
379
 
 
380
        seg_size = flst_get_len(seg_hdr + TRX_UNDO_PAGE_LIST, &mtr);
 
381
 
 
382
        /* We may free the undo log segment header page; it must be freed
 
383
        within the same mtr as the undo log header is removed from the
 
384
        history list: otherwise, in case of a database crash, the segment
 
385
        could become inaccessible garbage in the file space. */
 
386
 
 
387
        flst_cut_end(rseg_hdr + TRX_RSEG_HISTORY,
 
388
                     log_hdr + TRX_UNDO_HISTORY_NODE, n_removed_logs, &mtr);
 
389
 
 
390
        mutex_enter(&kernel_mutex);
 
391
        ut_ad(trx_sys->rseg_history_len >= n_removed_logs);
 
392
        trx_sys->rseg_history_len -= n_removed_logs;
 
393
        mutex_exit(&kernel_mutex);
 
394
 
 
395
        freed = FALSE;
 
396
 
 
397
        while (!freed) {
 
398
                /* Here we assume that a file segment with just the header
 
399
                page can be freed in a few steps, so that the buffer pool
 
400
                is not flooded with bufferfixed pages: see the note in
 
401
                fsp0fsp.c. */
 
402
 
 
403
                freed = fseg_free_step(seg_hdr + TRX_UNDO_FSEG_HEADER,
 
404
                                       &mtr);
 
405
        }
 
406
 
 
407
        hist_size = mtr_read_ulint(rseg_hdr + TRX_RSEG_HISTORY_SIZE,
 
408
                                   MLOG_4BYTES, &mtr);
 
409
        ut_ad(hist_size >= seg_size);
 
410
 
 
411
        mlog_write_ulint(rseg_hdr + TRX_RSEG_HISTORY_SIZE,
 
412
                         hist_size - seg_size, MLOG_4BYTES, &mtr);
 
413
 
 
414
        ut_ad(rseg->curr_size >= seg_size);
 
415
 
 
416
        rseg->curr_size -= seg_size;
 
417
 
 
418
        mutex_exit(&(rseg->mutex));
 
419
 
 
420
        mtr_commit(&mtr);
 
421
}
 
422
 
 
423
/************************************************************************
 
424
Removes unnecessary history data from a rollback segment. */
 
425
static
 
426
void
 
427
trx_purge_truncate_rseg_history(
 
428
/*============================*/
 
429
        trx_rseg_t*     rseg,           /* in: rollback segment */
 
430
        dulint          limit_trx_no,   /* in: remove update undo logs whose
 
431
                                        trx number is < limit_trx_no */
 
432
        dulint          limit_undo_no)  /* in: if transaction number is equal
 
433
                                        to limit_trx_no, truncate undo records
 
434
                                        with undo number < limit_undo_no */
 
435
{
 
436
        fil_addr_t      hdr_addr;
 
437
        fil_addr_t      prev_hdr_addr;
 
438
        trx_rsegf_t*    rseg_hdr;
 
439
        page_t*         undo_page;
 
440
        trx_ulogf_t*    log_hdr;
 
441
        trx_usegf_t*    seg_hdr;
 
442
        int             cmp;
 
443
        ulint           n_removed_logs  = 0;
 
444
        mtr_t           mtr;
 
445
 
 
446
        ut_ad(mutex_own(&(purge_sys->mutex)));
 
447
 
 
448
        mtr_start(&mtr);
 
449
        mutex_enter(&(rseg->mutex));
 
450
 
 
451
        rseg_hdr = trx_rsegf_get(rseg->space, rseg->zip_size,
 
452
                                 rseg->page_no, &mtr);
 
453
 
 
454
        hdr_addr = trx_purge_get_log_from_hist(
 
455
                flst_get_last(rseg_hdr + TRX_RSEG_HISTORY, &mtr));
 
456
loop:
 
457
        if (hdr_addr.page == FIL_NULL) {
 
458
 
 
459
                mutex_exit(&(rseg->mutex));
 
460
 
 
461
                mtr_commit(&mtr);
 
462
 
 
463
                return;
 
464
        }
 
465
 
 
466
        undo_page = trx_undo_page_get(rseg->space, rseg->zip_size,
 
467
                                      hdr_addr.page, &mtr);
 
468
 
 
469
        log_hdr = undo_page + hdr_addr.boffset;
 
470
 
 
471
        cmp = ut_dulint_cmp(mach_read_from_8(log_hdr + TRX_UNDO_TRX_NO),
 
472
                            limit_trx_no);
 
473
        if (cmp == 0) {
 
474
                trx_undo_truncate_start(rseg, rseg->space, hdr_addr.page,
 
475
                                        hdr_addr.boffset, limit_undo_no);
 
476
        }
 
477
 
 
478
        if (cmp >= 0) {
 
479
                mutex_enter(&kernel_mutex);
 
480
                ut_a(trx_sys->rseg_history_len >= n_removed_logs);
 
481
                trx_sys->rseg_history_len -= n_removed_logs;
 
482
                mutex_exit(&kernel_mutex);
 
483
 
 
484
                flst_truncate_end(rseg_hdr + TRX_RSEG_HISTORY,
 
485
                                  log_hdr + TRX_UNDO_HISTORY_NODE,
 
486
                                  n_removed_logs, &mtr);
 
487
 
 
488
                mutex_exit(&(rseg->mutex));
 
489
                mtr_commit(&mtr);
 
490
 
 
491
                return;
 
492
        }
 
493
 
 
494
        prev_hdr_addr = trx_purge_get_log_from_hist(
 
495
                flst_get_prev_addr(log_hdr + TRX_UNDO_HISTORY_NODE, &mtr));
 
496
        n_removed_logs++;
 
497
 
 
498
        seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
 
499
 
 
500
        if ((mach_read_from_2(seg_hdr + TRX_UNDO_STATE) == TRX_UNDO_TO_PURGE)
 
501
            && (mach_read_from_2(log_hdr + TRX_UNDO_NEXT_LOG) == 0)) {
 
502
 
 
503
                /* We can free the whole log segment */
 
504
 
 
505
                mutex_exit(&(rseg->mutex));
 
506
                mtr_commit(&mtr);
 
507
 
 
508
                trx_purge_free_segment(rseg, hdr_addr, n_removed_logs);
 
509
 
 
510
                n_removed_logs = 0;
 
511
        } else {
 
512
                mutex_exit(&(rseg->mutex));
 
513
                mtr_commit(&mtr);
 
514
        }
 
515
 
 
516
        mtr_start(&mtr);
 
517
        mutex_enter(&(rseg->mutex));
 
518
 
 
519
        rseg_hdr = trx_rsegf_get(rseg->space, rseg->zip_size,
 
520
                                 rseg->page_no, &mtr);
 
521
 
 
522
        hdr_addr = prev_hdr_addr;
 
523
 
 
524
        goto loop;
 
525
}
 
526
 
 
527
/************************************************************************
 
528
Removes unnecessary history data from rollback segments. NOTE that when this
 
529
function is called, the caller must not have any latches on undo log pages! */
 
530
static
 
531
void
 
532
trx_purge_truncate_history(void)
 
533
/*============================*/
 
534
{
 
535
        trx_rseg_t*     rseg;
 
536
        dulint          limit_trx_no;
 
537
        dulint          limit_undo_no;
 
538
 
 
539
        ut_ad(mutex_own(&(purge_sys->mutex)));
 
540
 
 
541
        trx_purge_arr_get_biggest(purge_sys->arr, &limit_trx_no,
 
542
                                  &limit_undo_no);
 
543
 
 
544
        if (ut_dulint_is_zero(limit_trx_no)) {
 
545
 
 
546
                limit_trx_no = purge_sys->purge_trx_no;
 
547
                limit_undo_no = purge_sys->purge_undo_no;
 
548
        }
 
549
 
 
550
        /* We play safe and set the truncate limit at most to the purge view
 
551
        low_limit number, though this is not necessary */
 
552
 
 
553
        if (ut_dulint_cmp(limit_trx_no, purge_sys->view->low_limit_no) >= 0) {
 
554
                limit_trx_no = purge_sys->view->low_limit_no;
 
555
                limit_undo_no = ut_dulint_zero;
 
556
        }
 
557
 
 
558
        ut_ad((ut_dulint_cmp(limit_trx_no,
 
559
                             purge_sys->view->low_limit_no) <= 0));
 
560
 
 
561
        rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list);
 
562
 
 
563
        while (rseg) {
 
564
                trx_purge_truncate_rseg_history(rseg, limit_trx_no,
 
565
                                                limit_undo_no);
 
566
                rseg = UT_LIST_GET_NEXT(rseg_list, rseg);
 
567
        }
 
568
}
 
569
 
 
570
/************************************************************************
 
571
Does a truncate if the purge array is empty. NOTE that when this function is
 
572
called, the caller must not have any latches on undo log pages! */
 
573
UNIV_INLINE
 
574
ibool
 
575
trx_purge_truncate_if_arr_empty(void)
 
576
/*=================================*/
 
577
                        /* out: TRUE if array empty */
 
578
{
 
579
        ut_ad(mutex_own(&(purge_sys->mutex)));
 
580
 
 
581
        if (purge_sys->arr->n_used == 0) {
 
582
 
 
583
                trx_purge_truncate_history();
 
584
 
 
585
                return(TRUE);
 
586
        }
 
587
 
 
588
        return(FALSE);
 
589
}
 
590
 
 
591
/***************************************************************************
 
592
Updates the last not yet purged history log info in rseg when we have purged
 
593
a whole undo log. Advances also purge_sys->purge_trx_no past the purged log. */
 
594
static
 
595
void
 
596
trx_purge_rseg_get_next_history_log(
 
597
/*================================*/
 
598
        trx_rseg_t*     rseg)   /* in: rollback segment */
 
599
{
 
600
        page_t*         undo_page;
 
601
        trx_ulogf_t*    log_hdr;
 
602
        trx_usegf_t*    seg_hdr;
 
603
        fil_addr_t      prev_log_addr;
 
604
        dulint          trx_no;
 
605
        ibool           del_marks;
 
606
        mtr_t           mtr;
 
607
 
 
608
        ut_ad(mutex_own(&(purge_sys->mutex)));
 
609
 
 
610
        mutex_enter(&(rseg->mutex));
 
611
 
 
612
        ut_a(rseg->last_page_no != FIL_NULL);
 
613
 
 
614
        purge_sys->purge_trx_no = ut_dulint_add(rseg->last_trx_no, 1);
 
615
        purge_sys->purge_undo_no = ut_dulint_zero;
 
616
        purge_sys->next_stored = FALSE;
 
617
 
 
618
        mtr_start(&mtr);
 
619
 
 
620
        undo_page = trx_undo_page_get_s_latched(rseg->space, rseg->zip_size,
 
621
                                                rseg->last_page_no, &mtr);
 
622
        log_hdr = undo_page + rseg->last_offset;
 
623
        seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
 
624
 
 
625
        /* Increase the purge page count by one for every handled log */
 
626
 
 
627
        purge_sys->n_pages_handled++;
 
628
 
 
629
        prev_log_addr = trx_purge_get_log_from_hist(
 
630
                flst_get_prev_addr(log_hdr + TRX_UNDO_HISTORY_NODE, &mtr));
 
631
        if (prev_log_addr.page == FIL_NULL) {
 
632
                /* No logs left in the history list */
 
633
 
 
634
                rseg->last_page_no = FIL_NULL;
 
635
 
 
636
                mutex_exit(&(rseg->mutex));
 
637
                mtr_commit(&mtr);
 
638
 
 
639
                mutex_enter(&kernel_mutex);
 
640
 
 
641
                /* Add debug code to track history list corruption reported
 
642
                on the MySQL mailing list on Nov 9, 2004. The fut0lst.c
 
643
                file-based list was corrupt. The prev node pointer was
 
644
                FIL_NULL, even though the list length was over 8 million nodes!
 
645
                We assume that purge truncates the history list in moderate
 
646
                size pieces, and if we here reach the head of the list, the
 
647
                list cannot be longer than 20 000 undo logs now. */
 
648
 
 
649
                if (trx_sys->rseg_history_len > 20000) {
 
650
                        ut_print_timestamp(stderr);
 
651
                        fprintf(stderr,
 
652
                                "  InnoDB: Warning: purge reached the"
 
653
                                " head of the history list,\n"
 
654
                                "InnoDB: but its length is still"
 
655
                                " reported as %lu! Make a detailed bug\n"
 
656
                                "InnoDB: report, and submit it"
 
657
                                " to http://bugs.mysql.com\n",
 
658
                                (ulong) trx_sys->rseg_history_len);
 
659
                }
 
660
 
 
661
                mutex_exit(&kernel_mutex);
 
662
 
 
663
                return;
 
664
        }
 
665
 
 
666
        mutex_exit(&(rseg->mutex));
 
667
        mtr_commit(&mtr);
 
668
 
 
669
        /* Read the trx number and del marks from the previous log header */
 
670
        mtr_start(&mtr);
 
671
 
 
672
        log_hdr = trx_undo_page_get_s_latched(rseg->space, rseg->zip_size,
 
673
                                              prev_log_addr.page, &mtr)
 
674
                + prev_log_addr.boffset;
 
675
 
 
676
        trx_no = mach_read_from_8(log_hdr + TRX_UNDO_TRX_NO);
 
677
 
 
678
        del_marks = mach_read_from_2(log_hdr + TRX_UNDO_DEL_MARKS);
 
679
 
 
680
        mtr_commit(&mtr);
 
681
 
 
682
        mutex_enter(&(rseg->mutex));
 
683
 
 
684
        rseg->last_page_no = prev_log_addr.page;
 
685
        rseg->last_offset = prev_log_addr.boffset;
 
686
        rseg->last_trx_no = trx_no;
 
687
        rseg->last_del_marks = del_marks;
 
688
 
 
689
        mutex_exit(&(rseg->mutex));
 
690
}
 
691
 
 
692
/***************************************************************************
 
693
Chooses the next undo log to purge and updates the info in purge_sys. This
 
694
function is used to initialize purge_sys when the next record to purge is
 
695
not known, and also to update the purge system info on the next record when
 
696
purge has handled the whole undo log for a transaction. */
 
697
static
 
698
void
 
699
trx_purge_choose_next_log(void)
 
700
/*===========================*/
 
701
{
 
702
        trx_undo_rec_t* rec;
 
703
        trx_rseg_t*     rseg;
 
704
        trx_rseg_t*     min_rseg;
 
705
        dulint          min_trx_no;
 
706
        ulint           space = 0;   /* remove warning (??? bug ???) */
 
707
        ulint           zip_size = 0;
 
708
        ulint           page_no = 0; /* remove warning (??? bug ???) */
 
709
        ulint           offset = 0;  /* remove warning (??? bug ???) */
 
710
        mtr_t           mtr;
 
711
 
 
712
        ut_ad(mutex_own(&(purge_sys->mutex)));
 
713
        ut_ad(purge_sys->next_stored == FALSE);
 
714
 
 
715
        rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list);
 
716
 
 
717
        min_trx_no = ut_dulint_max;
 
718
 
 
719
        min_rseg = NULL;
 
720
 
 
721
        while (rseg) {
 
722
                mutex_enter(&(rseg->mutex));
 
723
 
 
724
                if (rseg->last_page_no != FIL_NULL) {
 
725
 
 
726
                        if ((min_rseg == NULL)
 
727
                            || (ut_dulint_cmp(min_trx_no,
 
728
                                              rseg->last_trx_no) > 0)) {
 
729
 
 
730
                                min_rseg = rseg;
 
731
                                min_trx_no = rseg->last_trx_no;
 
732
                                space = rseg->space;
 
733
                                zip_size = rseg->zip_size;
 
734
                                ut_a(space == 0); /* We assume in purge of
 
735
                                                  externally stored fields
 
736
                                                  that space id == 0 */
 
737
                                page_no = rseg->last_page_no;
 
738
                                offset = rseg->last_offset;
 
739
                        }
 
740
                }
 
741
 
 
742
                mutex_exit(&(rseg->mutex));
 
743
 
 
744
                rseg = UT_LIST_GET_NEXT(rseg_list, rseg);
 
745
        }
 
746
 
 
747
        if (min_rseg == NULL) {
 
748
 
 
749
                return;
 
750
        }
 
751
 
 
752
        mtr_start(&mtr);
 
753
 
 
754
        if (!min_rseg->last_del_marks) {
 
755
                /* No need to purge this log */
 
756
 
 
757
                rec = &trx_purge_dummy_rec;
 
758
        } else {
 
759
                rec = trx_undo_get_first_rec(space, zip_size, page_no, offset,
 
760
                                             RW_S_LATCH, &mtr);
 
761
                if (rec == NULL) {
 
762
                        /* Undo log empty */
 
763
 
 
764
                        rec = &trx_purge_dummy_rec;
 
765
                }
 
766
        }
 
767
 
 
768
        purge_sys->next_stored = TRUE;
 
769
        purge_sys->rseg = min_rseg;
 
770
 
 
771
        purge_sys->hdr_page_no = page_no;
 
772
        purge_sys->hdr_offset = offset;
 
773
 
 
774
        purge_sys->purge_trx_no = min_trx_no;
 
775
 
 
776
        if (rec == &trx_purge_dummy_rec) {
 
777
 
 
778
                purge_sys->purge_undo_no = ut_dulint_zero;
 
779
                purge_sys->page_no = page_no;
 
780
                purge_sys->offset = 0;
 
781
        } else {
 
782
                purge_sys->purge_undo_no = trx_undo_rec_get_undo_no(rec);
 
783
 
 
784
                purge_sys->page_no = page_get_page_no(page_align(rec));
 
785
                purge_sys->offset = page_offset(rec);
 
786
        }
 
787
 
 
788
        mtr_commit(&mtr);
 
789
}
 
790
 
 
791
/***************************************************************************
 
792
Gets the next record to purge and updates the info in the purge system. */
 
793
static
 
794
trx_undo_rec_t*
 
795
trx_purge_get_next_rec(
 
796
/*===================*/
 
797
                                /* out: copy of an undo log record or
 
798
                                pointer to the dummy undo log record */
 
799
        mem_heap_t*     heap)   /* in: memory heap where copied */
 
800
{
 
801
        trx_undo_rec_t* rec;
 
802
        trx_undo_rec_t* rec_copy;
 
803
        trx_undo_rec_t* rec2;
 
804
        trx_undo_rec_t* next_rec;
 
805
        page_t*         undo_page;
 
806
        page_t*         page;
 
807
        ulint           offset;
 
808
        ulint           page_no;
 
809
        ulint           space;
 
810
        ulint           zip_size;
 
811
        ulint           type;
 
812
        ulint           cmpl_info;
 
813
        mtr_t           mtr;
 
814
 
 
815
        ut_ad(mutex_own(&(purge_sys->mutex)));
 
816
        ut_ad(purge_sys->next_stored);
 
817
 
 
818
        space = purge_sys->rseg->space;
 
819
        zip_size = purge_sys->rseg->zip_size;
 
820
        page_no = purge_sys->page_no;
 
821
        offset = purge_sys->offset;
 
822
 
 
823
        if (offset == 0) {
 
824
                /* It is the dummy undo log record, which means that there is
 
825
                no need to purge this undo log */
 
826
 
 
827
                trx_purge_rseg_get_next_history_log(purge_sys->rseg);
 
828
 
 
829
                /* Look for the next undo log and record to purge */
 
830
 
 
831
                trx_purge_choose_next_log();
 
832
 
 
833
                return(&trx_purge_dummy_rec);
 
834
        }
 
835
 
 
836
        mtr_start(&mtr);
 
837
 
 
838
        undo_page = trx_undo_page_get_s_latched(space, zip_size,
 
839
                                                page_no, &mtr);
 
840
        rec = undo_page + offset;
 
841
 
 
842
        rec2 = rec;
 
843
 
 
844
        for (;;) {
 
845
                /* Try first to find the next record which requires a purge
 
846
                operation from the same page of the same undo log */
 
847
 
 
848
                next_rec = trx_undo_page_get_next_rec(rec2,
 
849
                                                      purge_sys->hdr_page_no,
 
850
                                                      purge_sys->hdr_offset);
 
851
                if (next_rec == NULL) {
 
852
                        rec2 = trx_undo_get_next_rec(
 
853
                                rec2, purge_sys->hdr_page_no,
 
854
                                purge_sys->hdr_offset, &mtr);
 
855
                        break;
 
856
                }
 
857
 
 
858
                rec2 = next_rec;
 
859
 
 
860
                type = trx_undo_rec_get_type(rec2);
 
861
 
 
862
                if (type == TRX_UNDO_DEL_MARK_REC) {
 
863
 
 
864
                        break;
 
865
                }
 
866
 
 
867
                cmpl_info = trx_undo_rec_get_cmpl_info(rec2);
 
868
 
 
869
                if (trx_undo_rec_get_extern_storage(rec2)) {
 
870
                        break;
 
871
                }
 
872
 
 
873
                if ((type == TRX_UNDO_UPD_EXIST_REC)
 
874
                    && !(cmpl_info & UPD_NODE_NO_ORD_CHANGE)) {
 
875
                        break;
 
876
                }
 
877
        }
 
878
 
 
879
        if (rec2 == NULL) {
 
880
                mtr_commit(&mtr);
 
881
 
 
882
                trx_purge_rseg_get_next_history_log(purge_sys->rseg);
 
883
 
 
884
                /* Look for the next undo log and record to purge */
 
885
 
 
886
                trx_purge_choose_next_log();
 
887
 
 
888
                mtr_start(&mtr);
 
889
 
 
890
                undo_page = trx_undo_page_get_s_latched(space, zip_size,
 
891
                                                        page_no, &mtr);
 
892
 
 
893
                rec = undo_page + offset;
 
894
        } else {
 
895
                page = page_align(rec2);
 
896
 
 
897
                purge_sys->purge_undo_no = trx_undo_rec_get_undo_no(rec2);
 
898
                purge_sys->page_no = page_get_page_no(page);
 
899
                purge_sys->offset = rec2 - page;
 
900
 
 
901
                if (undo_page != page) {
 
902
                        /* We advance to a new page of the undo log: */
 
903
                        purge_sys->n_pages_handled++;
 
904
                }
 
905
        }
 
906
 
 
907
        rec_copy = trx_undo_rec_copy(rec, heap);
 
908
 
 
909
        mtr_commit(&mtr);
 
910
 
 
911
        return(rec_copy);
 
912
}
 
913
 
 
914
/************************************************************************
 
915
Fetches the next undo log record from the history list to purge. It must be
 
916
released with the corresponding release function. */
 
917
UNIV_INTERN
 
918
trx_undo_rec_t*
 
919
trx_purge_fetch_next_rec(
 
920
/*=====================*/
 
921
                                /* out: copy of an undo log record or
 
922
                                pointer to the dummy undo log record
 
923
                                &trx_purge_dummy_rec, if the whole undo log
 
924
                                can skipped in purge; NULL if none left */
 
925
        dulint*         roll_ptr,/* out: roll pointer to undo record */
 
926
        trx_undo_inf_t** cell,  /* out: storage cell for the record in the
 
927
                                purge array */
 
928
        mem_heap_t*     heap)   /* in: memory heap where copied */
 
929
{
 
930
        trx_undo_rec_t* undo_rec;
 
931
 
 
932
        mutex_enter(&(purge_sys->mutex));
 
933
 
 
934
        if (purge_sys->state == TRX_STOP_PURGE) {
 
935
                trx_purge_truncate_if_arr_empty();
 
936
 
 
937
                mutex_exit(&(purge_sys->mutex));
 
938
 
 
939
                return(NULL);
 
940
        }
 
941
 
 
942
        if (!purge_sys->next_stored) {
 
943
                trx_purge_choose_next_log();
 
944
 
 
945
                if (!purge_sys->next_stored) {
 
946
                        purge_sys->state = TRX_STOP_PURGE;
 
947
 
 
948
                        trx_purge_truncate_if_arr_empty();
 
949
 
 
950
                        if (srv_print_thread_releases) {
 
951
                                fprintf(stderr,
 
952
                                        "Purge: No logs left in the"
 
953
                                        " history list; pages handled %lu\n",
 
954
                                        (ulong) purge_sys->n_pages_handled);
 
955
                        }
 
956
 
 
957
                        mutex_exit(&(purge_sys->mutex));
 
958
 
 
959
                        return(NULL);
 
960
                }
 
961
        }
 
962
 
 
963
        if (purge_sys->n_pages_handled >= purge_sys->handle_limit) {
 
964
 
 
965
                purge_sys->state = TRX_STOP_PURGE;
 
966
 
 
967
                trx_purge_truncate_if_arr_empty();
 
968
 
 
969
                mutex_exit(&(purge_sys->mutex));
 
970
 
 
971
                return(NULL);
 
972
        }
 
973
 
 
974
        if (ut_dulint_cmp(purge_sys->purge_trx_no,
 
975
                          purge_sys->view->low_limit_no) >= 0) {
 
976
                purge_sys->state = TRX_STOP_PURGE;
 
977
 
 
978
                trx_purge_truncate_if_arr_empty();
 
979
 
 
980
                mutex_exit(&(purge_sys->mutex));
 
981
 
 
982
                return(NULL);
 
983
        }
 
984
 
 
985
        /*      fprintf(stderr, "Thread %lu purging trx %lu undo record %lu\n",
 
986
        os_thread_get_curr_id(),
 
987
        ut_dulint_get_low(purge_sys->purge_trx_no),
 
988
        ut_dulint_get_low(purge_sys->purge_undo_no)); */
 
989
 
 
990
        *roll_ptr = trx_undo_build_roll_ptr(FALSE, (purge_sys->rseg)->id,
 
991
                                            purge_sys->page_no,
 
992
                                            purge_sys->offset);
 
993
 
 
994
        *cell = trx_purge_arr_store_info(purge_sys->purge_trx_no,
 
995
                                         purge_sys->purge_undo_no);
 
996
 
 
997
        ut_ad(ut_dulint_cmp(purge_sys->purge_trx_no,
 
998
                            (purge_sys->view)->low_limit_no) < 0);
 
999
 
 
1000
        /* The following call will advance the stored values of purge_trx_no
 
1001
        and purge_undo_no, therefore we had to store them first */
 
1002
 
 
1003
        undo_rec = trx_purge_get_next_rec(heap);
 
1004
 
 
1005
        mutex_exit(&(purge_sys->mutex));
 
1006
 
 
1007
        return(undo_rec);
 
1008
}
 
1009
 
 
1010
/***********************************************************************
 
1011
Releases a reserved purge undo record. */
 
1012
UNIV_INTERN
 
1013
void
 
1014
trx_purge_rec_release(
 
1015
/*==================*/
 
1016
        trx_undo_inf_t* cell)   /* in: storage cell */
 
1017
{
 
1018
        trx_undo_arr_t* arr;
 
1019
 
 
1020
        mutex_enter(&(purge_sys->mutex));
 
1021
 
 
1022
        arr = purge_sys->arr;
 
1023
 
 
1024
        trx_purge_arr_remove_info(cell);
 
1025
 
 
1026
        mutex_exit(&(purge_sys->mutex));
 
1027
}
 
1028
 
 
1029
/***********************************************************************
 
1030
This function runs a purge batch. */
 
1031
UNIV_INTERN
 
1032
ulint
 
1033
trx_purge(void)
 
1034
/*===========*/
 
1035
                                /* out: number of undo log pages handled in
 
1036
                                the batch */
 
1037
{
 
1038
        que_thr_t*      thr;
 
1039
        /*      que_thr_t*      thr2; */
 
1040
        ulint           old_pages_handled;
 
1041
 
 
1042
        mutex_enter(&(purge_sys->mutex));
 
1043
 
 
1044
        if (purge_sys->trx->n_active_thrs > 0) {
 
1045
 
 
1046
                mutex_exit(&(purge_sys->mutex));
 
1047
 
 
1048
                /* Should not happen */
 
1049
 
 
1050
                ut_error;
 
1051
 
 
1052
                return(0);
 
1053
        }
 
1054
 
 
1055
        rw_lock_x_lock(&(purge_sys->latch));
 
1056
 
 
1057
        mutex_enter(&kernel_mutex);
 
1058
 
 
1059
        /* Close and free the old purge view */
 
1060
 
 
1061
        read_view_close(purge_sys->view);
 
1062
        purge_sys->view = NULL;
 
1063
        mem_heap_empty(purge_sys->heap);
 
1064
 
 
1065
        /* Determine how much data manipulation language (DML) statements
 
1066
        need to be delayed in order to reduce the lagging of the purge
 
1067
        thread. */
 
1068
        srv_dml_needed_delay = 0; /* in microseconds; default: no delay */
 
1069
 
 
1070
        /* If we cannot advance the 'purge view' because of an old
 
1071
        'consistent read view', then the DML statements cannot be delayed.
 
1072
        Also, srv_max_purge_lag <= 0 means 'infinity'. */
 
1073
        if (srv_max_purge_lag > 0
 
1074
            && !UT_LIST_GET_LAST(trx_sys->view_list)) {
 
1075
                float   ratio = (float) trx_sys->rseg_history_len
 
1076
                        / srv_max_purge_lag;
 
1077
                if (ratio > ULINT_MAX / 10000) {
 
1078
                        /* Avoid overflow: maximum delay is 4295 seconds */
 
1079
                        srv_dml_needed_delay = ULINT_MAX;
 
1080
                } else if (ratio > 1) {
 
1081
                        /* If the history list length exceeds the
 
1082
                        innodb_max_purge_lag, the
 
1083
                        data manipulation statements are delayed
 
1084
                        by at least 5000 microseconds. */
 
1085
                        srv_dml_needed_delay = (ulint) ((ratio - .5) * 10000);
 
1086
                }
 
1087
        }
 
1088
 
 
1089
        purge_sys->view = read_view_oldest_copy_or_open_new(ut_dulint_zero,
 
1090
                                                            purge_sys->heap);
 
1091
        mutex_exit(&kernel_mutex);
 
1092
 
 
1093
        rw_lock_x_unlock(&(purge_sys->latch));
 
1094
 
 
1095
        purge_sys->state = TRX_PURGE_ON;
 
1096
 
 
1097
        /* Handle at most 20 undo log pages in one purge batch */
 
1098
 
 
1099
        purge_sys->handle_limit = purge_sys->n_pages_handled + 20;
 
1100
 
 
1101
        old_pages_handled = purge_sys->n_pages_handled;
 
1102
 
 
1103
        mutex_exit(&(purge_sys->mutex));
 
1104
 
 
1105
        mutex_enter(&kernel_mutex);
 
1106
 
 
1107
        thr = que_fork_start_command(purge_sys->query);
 
1108
 
 
1109
        ut_ad(thr);
 
1110
 
 
1111
        /*      thr2 = que_fork_start_command(purge_sys->query);
 
1112
 
 
1113
        ut_ad(thr2); */
 
1114
 
 
1115
 
 
1116
        mutex_exit(&kernel_mutex);
 
1117
 
 
1118
        /*      srv_que_task_enqueue(thr2); */
 
1119
 
 
1120
        if (srv_print_thread_releases) {
 
1121
 
 
1122
                fputs("Starting purge\n", stderr);
 
1123
        }
 
1124
 
 
1125
        que_run_threads(thr);
 
1126
 
 
1127
        if (srv_print_thread_releases) {
 
1128
 
 
1129
                fprintf(stderr,
 
1130
                        "Purge ends; pages handled %lu\n",
 
1131
                        (ulong) purge_sys->n_pages_handled);
 
1132
        }
 
1133
 
 
1134
        return(purge_sys->n_pages_handled - old_pages_handled);
 
1135
}
 
1136
 
 
1137
/**********************************************************************
 
1138
Prints information of the purge system to stderr. */
 
1139
UNIV_INTERN
 
1140
void
 
1141
trx_purge_sys_print(void)
 
1142
/*=====================*/
 
1143
{
 
1144
        fprintf(stderr, "InnoDB: Purge system view:\n");
 
1145
        read_view_print(purge_sys->view);
 
1146
 
 
1147
        fprintf(stderr, "InnoDB: Purge trx n:o " TRX_ID_FMT
 
1148
                ", undo n:o " TRX_ID_FMT "\n",
 
1149
                TRX_ID_PREP_PRINTF(purge_sys->purge_trx_no),
 
1150
                TRX_ID_PREP_PRINTF(purge_sys->purge_undo_no));
 
1151
        fprintf(stderr,
 
1152
                "InnoDB: Purge next stored %lu, page_no %lu, offset %lu,\n"
 
1153
                "InnoDB: Purge hdr_page_no %lu, hdr_offset %lu\n",
 
1154
                (ulong) purge_sys->next_stored,
 
1155
                (ulong) purge_sys->page_no,
 
1156
                (ulong) purge_sys->offset,
 
1157
                (ulong) purge_sys->hdr_page_no,
 
1158
                (ulong) purge_sys->hdr_offset);
 
1159
}