Merge lp:~akopytov/percona-server/bug1131189-5.6 into lp:percona-server/5.6

Proposed by Alexey Kopytov on 2013-05-24
Status: Merged
Approved by: Laurynas Biveinis on 2013-05-26
Approved revision: 356
Merged at revision: 356
Proposed branch: lp:~akopytov/percona-server/bug1131189-5.6
Merge into: lp:percona-server/5.6
Prerequisite: lp:~laurynas-biveinis/percona-server/ci-fixes-5.6.11
Diff against target: 1545 lines (+617/-233)
21 files modified
Percona-Server/sql/handler.cc (+7/-17)
Percona-Server/sql/sql_base.cc (+9/-3)
Percona-Server/sql/sql_class.cc (+1/-3)
Percona-Server/sql/sql_connect.cc (+9/-6)
Percona-Server/sql/sql_parse.cc (+6/-2)
Percona-Server/sql/sql_prepare.cc (+26/-10)
Percona-Server/storage/innobase/include/read0read.h (+7/-4)
Percona-Server/storage/innobase/include/read0read.ic (+11/-28)
Percona-Server/storage/innobase/include/trx0sys.h (+58/-10)
Percona-Server/storage/innobase/include/trx0sys.ic (+66/-13)
Percona-Server/storage/innobase/include/trx0trx.h (+24/-0)
Percona-Server/storage/innobase/lock/lock0lock.cc (+13/-3)
Percona-Server/storage/innobase/read/read0read.cc (+82/-114)
Percona-Server/storage/innobase/row/row0sel.cc (+2/-2)
Percona-Server/storage/innobase/row/row0vers.cc (+5/-8)
Percona-Server/storage/innobase/trx/trx0sys.cc (+10/-0)
Percona-Server/storage/innobase/trx/trx0trx.cc (+177/-10)
policy/apparmor/usr.sbin.mysqld (+61/-0)
policy/apparmor/usr.sbin.mysqld.local (+2/-0)
policy/selinux/percona-server.fc (+6/-0)
policy/selinux/percona-server.te (+35/-0)
To merge this branch: bzr merge lp:~akopytov/percona-server/bug1131189-5.6
Reviewer Review Type Date Requested Status
Laurynas Biveinis (community) 2013-05-24 Approve on 2013-05-26
Review via email: mp+165584@code.launchpad.net
To post a comment you must log in.

    - The patch would be OK, but I did find one issue which IMHO
      requires recommitting (although the test cycle could be
      skipped) or follow-up addressing (I don't have a strong opinion
      on recomitting vs follow-up): trx_rw_is_active_low is a
      bool-returning function that returns NULL in one of the code
      paths.

    - If we are recommitting, then in_trx_serial_list should be bool,
      not ibool as well, and true/false used instead of 0/1 for
      setting it, spurious newline at diff line 278. The row0sel.cc
      change is spurious as well?

    Random remark:

    - trx_descr_cmp() should be defined in trx0trx.ic, not .c? So
      then compiler can resolve the function pointer to inlining. It
      is also possible to use std::binary_search() in the brave new
      C++ world.

    Last and least absolutely random remarks:

    - I don't fully understand the header comment for
      trx_rw_get_active_trx_by_id() stating that the returned pointer
      must not be dereferenced unless lock_sys->mutex is held. But
      the function itself dereferences the pointer in
      trx_state_eq()? I see that all callers hold the
      lock_sys->mutex, thus it does not seem to be a real issue
      though.

    - Upstream started making heavy use of __attribute__ function
      annotations to cause extra warnings on misuse, especially
      nonnull, warn_unused_result. Maybe not bad idea for us well
      where it applies.

review: Needs Fixing

Will merge as-is and handle the minor issues in a follow-up.

review: Approve
Alexey Kopytov (akopytov) wrote :

Hi Laurynas,

On Fri, 24 May 2013 14:22:31 -0000, Laurynas Biveinis wrote:
> Review: Needs Fixing
>
> - The patch would be OK, but I did find one issue which IMHO
> requires recommitting (although the test cycle could be
> skipped) or follow-up addressing (I don't have a strong opinion
> on recomitting vs follow-up): trx_rw_is_active_low is a
> bool-returning function that returns NULL in one of the code
> paths.

Right, good catch! Thanks for fixing it as a followup.

>
> - If we are recommitting, then in_trx_serial_list should be bool,
> not ibool as well, and true/false used instead of 0/1 for
> setting it, spurious newline at diff line 278. The row0sel.cc
> change is spurious as well?
>

I deliberately made it ibool to be consistent with in_ro_trx_list /
in_rw_trx_list (which is also new 5.6 code BTW). But OK.

> Random remark:
>
> - trx_descr_cmp() should be defined in trx0trx.ic, not .c? So
> then compiler can resolve the function pointer to inlining. It
> is also possible to use std::binary_search() in the brave new
> C++ world.
>

bsearch() is a library function, so no inlining is possible. Is it
different with std::binary_search()?

> Last and least absolutely random remarks:
>
> - I don't fully understand the header comment for
> trx_rw_get_active_trx_by_id() stating that the returned pointer
> must not be dereferenced unless lock_sys->mutex is held. But
> the function itself dereferences the pointer in
> trx_state_eq()? I see that all callers hold the
> lock_sys->mutex, thus it does not seem to be a real issue
> though.
>

There's actually an upstream problem with that code, but it only applies
to debug builds. Will report it separately.

> - Upstream started making heavy use of __attribute__ function
> annotations to cause extra warnings on misuse, especially
> nonnull, warn_unused_result. Maybe not bad idea for us well
> where it applies.
>

Yes, that's good stuff.

/Alexey

Ah, indeed trx_descr_cmp() is just a wrapper around bsearch(), thus removing the indirection would not help there.

std::binary_search() 3rd optional arg is a C++ callable object for performing element comparisons. If a function pointer is passed there, then it's the same as with bsearch(), but if a template function object is passed instead, then it's all inlineable.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Percona-Server/sql/handler.cc'
2--- Percona-Server/sql/handler.cc 2013-05-12 06:24:46 +0000
3+++ Percona-Server/sql/handler.cc 2013-05-24 12:02:59 +0000
4@@ -2516,8 +2516,13 @@
5 dup_ref=ref+ALIGN_SIZE(ref_length);
6 cached_table_flags= table_flags();
7 }
8- rows_read= rows_changed= 0;
9- memset(index_rows_read, 0, sizeof(index_rows_read));
10+
11+ if (unlikely(opt_userstat))
12+ {
13+ rows_read= rows_changed= 0;
14+ memset(index_rows_read, 0, sizeof(index_rows_read));
15+ }
16+
17 DBUG_RETURN(error);
18 }
19
20@@ -4667,12 +4672,6 @@
21 // Updates the global table stats with the TABLE this handler represents.
22 void handler::update_global_table_stats()
23 {
24- if (!opt_userstat)
25- {
26- rows_read= rows_changed= 0;
27- return;
28- }
29-
30 if (!rows_read && !rows_changed)
31 return; // Nothing to update.
32 // table_cache_key is db_name + '\0' + table_name + '\0'.
33@@ -4730,15 +4729,6 @@
34 !table->s->table_name.str)
35 return;
36
37- if (!opt_userstat)
38- {
39- for (uint x= 0; x < table->s->keys; ++x)
40- {
41- index_rows_read[x]= 0;
42- }
43- return;
44- }
45-
46 for (uint x = 0; x < table->s->keys; ++x)
47 {
48 if (index_rows_read[x])
49
50=== modified file 'Percona-Server/sql/sql_base.cc'
51--- Percona-Server/sql/sql_base.cc 2013-05-13 04:25:56 +0000
52+++ Percona-Server/sql/sql_base.cc 2013-05-24 12:02:59 +0000
53@@ -1490,11 +1490,13 @@
54 table->mdl_ticket= NULL;
55
56 mysql_mutex_lock(&thd->LOCK_thd_data);
57- if(table->file)
58+
59+ if(unlikely(opt_userstat && table->file))
60 {
61 table->file->update_global_table_stats();
62 table->file->update_global_index_stats();
63 }
64+
65 *table_ptr=table->next;
66 mysql_mutex_unlock(&thd->LOCK_thd_data);
67
68@@ -2152,8 +2154,12 @@
69 DBUG_PRINT("tmptable", ("closing table: '%s'.'%s'",
70 table->s->db.str, table->s->table_name.str));
71
72- table->file->update_global_table_stats();
73- table->file->update_global_index_stats();
74+ if (unlikely(opt_userstat))
75+ {
76+ table->file->update_global_table_stats();
77+ table->file->update_global_index_stats();
78+ }
79+
80 free_io_cache(table);
81 closefrm(table, 0);
82 if (delete_table)
83
84=== modified file 'Percona-Server/sql/sql_class.cc'
85--- Percona-Server/sql/sql_class.cc 2013-05-22 10:51:07 +0000
86+++ Percona-Server/sql/sql_class.cc 2013-05-24 12:02:59 +0000
87@@ -1520,8 +1520,6 @@
88 // Updates 'diff' stats of a THD.
89 void THD::update_stats(bool ran_command)
90 {
91- if (opt_userstat)
92- {
93 diff_total_busy_time+= busy_time;
94 diff_total_cpu_time+= cpu_time;
95 diff_total_bytes_received+= bytes_received;
96@@ -1566,7 +1564,7 @@
97 /* reset counters to zero to avoid double-counting since values
98 are already store in diff_total_*.
99 */
100- }
101+
102 busy_time= 0;
103 cpu_time= 0;
104 bytes_received= 0;
105
106=== modified file 'Percona-Server/sql/sql_connect.cc'
107--- Percona-Server/sql/sql_connect.cc 2013-05-22 10:51:07 +0000
108+++ Percona-Server/sql/sql_connect.cc 2013-05-24 12:02:59 +0000
109@@ -566,9 +566,6 @@
110 const char* client_string= get_client_host(thd);
111 int return_value= 0;
112
113- if (!opt_userstat)
114- return return_value;
115-
116 if (acl_is_utility_user(thd->security_ctx->user, thd->security_ctx->host,
117 thd->security_ctx->ip))
118 return return_value;
119@@ -1383,8 +1380,9 @@
120 my_net_set_write_timeout(net, thd->variables.net_write_timeout);
121
122 thd->reset_stats();
123+
124 // Updates global user connection stats.
125- if (increment_connection_count(thd, true))
126+ if (opt_userstat && increment_connection_count(thd, true))
127 DBUG_RETURN(1);
128
129 DBUG_RETURN(0);
130@@ -1621,8 +1619,13 @@
131
132 end_thread:
133 close_connection(thd);
134- thd->update_stats(false);
135- update_global_user_stats(thd, create_user, time(NULL));
136+
137+ if (unlikely(opt_userstat))
138+ {
139+ thd->update_stats(false);
140+ update_global_user_stats(thd, create_user, time(NULL));
141+ }
142+
143 if (MYSQL_CALLBACK_ELSE(thd->scheduler, end_thread, (thd, 1), 0))
144 return; // Probably no-threads
145
146
147=== modified file 'Percona-Server/sql/sql_parse.cc'
148--- Percona-Server/sql/sql_parse.cc 2013-05-22 10:51:07 +0000
149+++ Percona-Server/sql/sql_parse.cc 2013-05-24 12:02:59 +0000
150@@ -6545,11 +6545,15 @@
151 else
152 thd->cpu_time = 0;
153 }
154+
155 // Updates THD stats and the global user stats.
156- thd->update_stats(true);
157+ if (unlikely(opt_userstat))
158+ {
159+ thd->update_stats(true);
160 #ifndef EMBEDDED_LIBRARY
161- update_global_user_stats(thd, true, time(NULL));
162+ update_global_user_stats(thd, true, time(NULL));
163 #endif
164+ }
165
166 DBUG_VOID_RETURN;
167 }
168
169=== modified file 'Percona-Server/sql/sql_prepare.cc'
170--- Percona-Server/sql/sql_prepare.cc 2013-05-12 06:24:46 +0000
171+++ Percona-Server/sql/sql_prepare.cc 2013-05-24 12:02:59 +0000
172@@ -2267,7 +2267,7 @@
173 double start_cpu_nsecs= 0;
174 double end_cpu_nsecs= 0;
175
176- if (opt_userstat)
177+ if (unlikely(opt_userstat))
178 {
179 #ifdef HAVE_CLOCK_GETTIME
180 /* get start cputime */
181@@ -2309,7 +2309,7 @@
182
183 /* check_prepared_statemnt sends the metadata packet in case of success */
184 end:
185- if (opt_userstat)
186+ if (unlikely(opt_userstat))
187 {
188 // Gets the end time.
189 if (!(end_time_error= gettimeofday(&end_time, NULL)))
190@@ -2351,11 +2351,15 @@
191 else
192 thd->cpu_time = 0;
193 }
194+
195 // Updates THD stats and the global user stats.
196- thd->update_stats(true);
197+ if (unlikely(opt_userstat))
198+ {
199+ thd->update_stats(true);
200 #ifndef EMBEDDED_LIBRARY
201- update_global_user_stats(thd, true, time(NULL));
202+ update_global_user_stats(thd, true, time(NULL));
203 #endif
204+ }
205
206 DBUG_VOID_RETURN;
207 }
208@@ -2813,11 +2817,15 @@
209 else
210 thd->cpu_time = 0;
211 }
212+
213 // Updates THD stats and the global user stats.
214- thd->update_stats(true);
215+ if (unlikely(opt_userstat))
216+ {
217+ thd->update_stats(true);
218 #ifndef EMBEDDED_LIBRARY
219- update_global_user_stats(thd, true, time(NULL));
220+ update_global_user_stats(thd, true, time(NULL));
221 #endif
222+ }
223
224 DBUG_VOID_RETURN;
225 }
226@@ -2990,11 +2998,15 @@
227 } else
228 thd->cpu_time= 0;
229 }
230+
231 // Updates THD stats and the global user stats.
232- thd->update_stats(true);
233+ if (unlikely(opt_userstat))
234+ {
235+ thd->update_stats(true);
236 #ifndef EMBEDDED_LIBRARY
237- update_global_user_stats(thd, true, time(NULL));
238+ update_global_user_stats(thd, true, time(NULL));
239 #endif
240+ }
241
242 DBUG_VOID_RETURN;
243 }
244@@ -3118,11 +3130,15 @@
245 else
246 thd->cpu_time= 0;
247 }
248+
249 // Updates THD stats and the global user stats.
250- thd->update_stats(true);
251+ if (unlikely(opt_userstat))
252+ {
253+ thd->update_stats(true);
254 #ifndef EMBEDDED_LIBRARY
255- update_global_user_stats(thd, true, time(NULL));
256+ update_global_user_stats(thd, true, time(NULL));
257 #endif
258+ }
259
260 DBUG_VOID_RETURN;
261 }
262
263=== modified file 'Percona-Server/storage/innobase/include/read0read.h'
264--- Percona-Server/storage/innobase/include/read0read.h 2013-05-23 06:51:50 +0000
265+++ Percona-Server/storage/innobase/include/read0read.h 2013-05-24 12:02:59 +0000
266@@ -32,6 +32,7 @@
267 #include "ut0byte.h"
268 #include "ut0lst.h"
269 #include "trx0trx.h"
270+#include "trx0sys.h"
271 #include "read0types.h"
272
273 /*********************************************************************//**
274@@ -46,6 +47,7 @@
275 transaction, or 0 used in purge */
276 read_view_t*& view); /*!< in,out: pre-allocated view array or
277 NULL if a new one needs to be created */
278+
279 /*********************************************************************//**
280 Makes a copy of the oldest existing read view, or opens a new. The view
281 must be closed with ..._close.
282@@ -154,17 +156,18 @@
283 are strictly smaller (<) than this value.
284 In other words,
285 this is the "low water mark". */
286- ulint n_trx_ids;
287+ ulint n_descr;
288 /*!< Number of cells in the trx_ids array */
289- ulint max_trx_ids;
290+ ulint max_descr;
291 /*!< Maximum number of cells in the trx_ids
292 array */
293- trx_id_t* trx_ids;/*!< Additional trx ids which the read should
294+ trx_id_t* descriptors;
295+ /*!< Additional trx ids which the read should
296 not see: typically, these are the read-write
297 active transactions at the time when the read
298 is serialized, except the reading transaction
299 itself; the trx ids in this array are in a
300- descending order. These trx_ids should be
301+ ascending order. These trx_ids should be
302 between the "low" and "high" water marks,
303 that is, up_limit_id and low_limit_id. */
304 trx_id_t creator_trx_id;
305
306=== modified file 'Percona-Server/storage/innobase/include/read0read.ic'
307--- Percona-Server/storage/innobase/include/read0read.ic 2012-08-22 01:40:20 +0000
308+++ Percona-Server/storage/innobase/include/read0read.ic 2013-05-24 12:02:59 +0000
309@@ -35,11 +35,13 @@
310 const read_view_t* view) /*!< in: view to validate */
311 {
312 ut_ad(mutex_own(&trx_sys->mutex));
313-
314- /* Check that the view->trx_ids array is in descending order. */
315- for (ulint i = 1; i < view->n_trx_ids; ++i) {
316-
317- ut_a(view->trx_ids[i] < view->trx_ids[i - 1]);
318+ ut_ad(view->max_descr >= view->n_descr);
319+ ut_ad(view->descriptors == NULL || view->max_descr > 0);
320+
321+ /* Check that the view->descriptors array is in ascending order. */
322+ for (ulint i = 1; i < view->n_descr; ++i) {
323+
324+ ut_a(view->descriptors[i] > view->descriptors[i - 1]);
325 }
326
327 return(true);
328@@ -92,31 +94,12 @@
329 } else if (trx_id >= view->low_limit_id) {
330
331 return(false);
332- } else {
333- ulint lower = 0;
334- ulint upper = view->n_trx_ids - 1;
335-
336- ut_a(view->n_trx_ids > 0);
337-
338- do {
339- ulint mid = (lower + upper) >> 1;
340- trx_id_t mid_id = view->trx_ids[mid];
341-
342- if (mid_id == trx_id) {
343- return(FALSE);
344- } else if (mid_id < trx_id) {
345- if (mid > 0) {
346- upper = mid - 1;
347- } else {
348- break;
349- }
350- } else {
351- lower = mid + 1;
352- }
353- } while (lower <= upper);
354 }
355
356- return(true);
357+ /* Do a binary search over this view's descriptors array */
358+
359+ return(trx_find_descriptor(view->descriptors, view->n_descr,
360+ trx_id) == NULL);
361 }
362
363 /*********************************************************************//**
364
365=== modified file 'Percona-Server/storage/innobase/include/trx0sys.h'
366--- Percona-Server/storage/innobase/include/trx0sys.h 2013-05-10 09:39:17 +0000
367+++ Percona-Server/storage/innobase/include/trx0sys.h 2013-05-24 12:02:59 +0000
368@@ -184,6 +184,17 @@
369 trx_sys_get_max_trx_id(void);
370 /*========================*/
371
372+/*************************************************************//**
373+Find a slot for a given trx ID in a descriptors array.
374+@return: slot pointer */
375+UNIV_INLINE
376+trx_id_t*
377+trx_find_descriptor(
378+/*================*/
379+ const trx_id_t* descriptors, /*!< in: descriptors array */
380+ ulint n_descr, /*!< in: array size */
381+ trx_id_t trx_id); /*!< in: trx pointer */
382+
383 #ifdef UNIV_DEBUG
384 /* Flag to control TRX_RSEG_N_SLOTS behavior debugging. */
385 extern uint trx_rseg_n_slots_debug;
386@@ -231,14 +242,26 @@
387 trx_rw_min_trx_id(void);
388 /*===================*/
389 /****************************************************************//**
390+Returns pointer to a transaction instance if a rw transaction with the given id
391+is active. Caller must hold trx_sys->mutex. If the caller is not holding
392+lock_sys->mutex, the transaction may already have been committed.
393+@return transaction instance if active, or NULL;
394+the pointer must not be dereferenced unless lock_sys->mutex was
395+acquired before calling this function and is still being held */
396+UNIV_INLINE
397+trx_t*
398+trx_rw_get_active_trx_by_id(
399+/*========================*/
400+ trx_id_t trx_id, /*!< in: trx id of the transaction */
401+ ibool* corrupt); /*!< in: NULL or pointer to a flag
402+ that will be set if corrupt */
403+/****************************************************************//**
404 Checks if a rw transaction with the given id is active. Caller must hold
405-trx_sys->mutex in shared mode. If the caller is not holding
406-lock_sys->mutex, the transaction may already have been committed.
407-@return transaction instance if active, or NULL;
408-the pointer must not be dereferenced unless lock_sys->mutex was
409-acquired before calling this function and is still being held */
410+trx_sys->mutex. If the caller is not holding lock_sys->mutex, the
411+transaction may already have been committed.
412+@return true if rw transaction it with a given id is active. */
413 UNIV_INLINE
414-trx_t*
415+bool
416 trx_rw_is_active_low(
417 /*=================*/
418 trx_id_t trx_id, /*!< in: trx id of the transaction */
419@@ -248,11 +271,9 @@
420 Checks if a rw transaction with the given id is active. If the caller is
421 not holding lock_sys->mutex, the transaction may already have been
422 committed.
423-@return transaction instance if active, or NULL;
424-the pointer must not be dereferenced unless lock_sys->mutex was
425-acquired before calling this function and is still being held */
426+@return true if rw transaction it with a given id is active. */
427 UNIV_INLINE
428-trx_t*
429+bool
430 trx_rw_is_active(
431 /*=============*/
432 trx_id_t trx_id, /*!< in: trx id of the transaction */
433@@ -598,6 +619,8 @@
434 | TRX_SYS_FILE_FORMAT_TAG_MAGIC_N_LOW)
435 /* @} */
436
437+#define TRX_DESCR_ARRAY_INITIAL_SIZE 1000
438+
439 #ifndef UNIV_HOTBACKUP
440 /** The transaction system central memory data structure. */
441 struct trx_sys_t{
442@@ -618,6 +641,18 @@
443 trx_id_t max_trx_id; /*!< The smallest number not yet
444 assigned as a transaction id or
445 transaction number */
446+ char pad1[64]; /*!< Ensure max_trx_id does not share
447+ cache line with other fields. */
448+ trx_id_t* descriptors; /*!< Array of trx descriptors */
449+ ulint descr_n_max; /*!< The current size of the descriptors
450+ array. */
451+ char pad2[64]; /*!< Ensure static descriptor fields
452+ do not share cache lines with
453+ descr_n_used */
454+ ulint descr_n_used; /*!< Number of used elements in the
455+ descriptors array. */
456+ char pad3[64]; /*!< Ensure descriptors do not share
457+ cache line with other fields */
458 #ifdef UNIV_DEBUG
459 trx_id_t rw_max_trx_id; /*!< Max trx id of read-write transactions
460 which exist or existed */
461@@ -626,6 +661,8 @@
462 memory read-write transactions, sorted
463 on trx id, biggest first. Recovered
464 transactions are always on this list. */
465+ char pad4[64]; /*!< Ensure list base nodes do not
466+ share cache line with other fields */
467 trx_list_t ro_trx_list; /*!< List of active and committed in
468 memory read-only transactions, sorted
469 on trx id, biggest first. NOTE:
470@@ -633,6 +670,8 @@
471 is not necessary. We should exploit
472 this and increase concurrency during
473 add/remove. */
474+ char pad5[64]; /*!< Ensure list base nodes do not
475+ share cache line with other fields */
476 trx_list_t mysql_trx_list; /*!< List of transactions created
477 for MySQL. All transactions on
478 ro_trx_list are on mysql_trx_list. The
479@@ -645,6 +684,15 @@
480 mysql_trx_list may additionally contain
481 transactions that have not yet been
482 started in InnoDB. */
483+ char pad6[64]; /*!< Ensure list base nodes do not
484+ share cache line with other fields */
485+ trx_list_t trx_serial_list;
486+ /*!< trx->no ordered List of
487+ transactions in either TRX_PREPARED or
488+ TRX_ACTIVE which have already been
489+ assigned a serialization number */
490+ char pad7[64]; /*!< Ensure list base nodes do not
491+ share cache line with other fields */
492 trx_rseg_t* const rseg_array[TRX_SYS_N_RSEGS];
493 /*!< Pointer array to rollback
494 segments; NULL if slot not in use;
495
496=== modified file 'Percona-Server/storage/innobase/include/trx0sys.ic'
497--- Percona-Server/storage/innobase/include/trx0sys.ic 2013-05-10 09:39:17 +0000
498+++ Percona-Server/storage/innobase/include/trx0sys.ic 2013-05-24 12:02:59 +0000
499@@ -368,16 +368,16 @@
500 }
501
502 /****************************************************************//**
503-Checks if a rw transaction with the given id is active. Caller must hold
504-trx_sys->mutex. If the caller is not holding lock_sys->mutex, the
505-transaction may already have been committed.
506-@return transaction instance if active, or NULL;
507+Returns pointer to a transaction instance if a rw transaction with the given id
508+is active. Caller must hold trx_sys->mutex. If the caller is not holding
509+lock_sys->mutex, the transaction may already have been committed.
510+@return transaction instance if active, or NULL;
511 the pointer must not be dereferenced unless lock_sys->mutex was
512 acquired before calling this function and is still being held */
513 UNIV_INLINE
514 trx_t*
515-trx_rw_is_active_low(
516-/*=================*/
517+trx_rw_get_active_trx_by_id(
518+/*========================*/
519 trx_id_t trx_id, /*!< in: trx id of the transaction */
520 ibool* corrupt) /*!< in: NULL or pointer to a flag
521 that will be set if corrupt */
522@@ -412,29 +412,58 @@
523 }
524
525 /****************************************************************//**
526+Checks if a rw transaction with the given id is active. Caller must hold
527+trx_sys->mutex. If the caller is not holding lock_sys->mutex, the
528+transaction may already have been committed.
529+@return true if rw transaction it with a given id is active. */
530+UNIV_INLINE
531+bool
532+trx_rw_is_active_low(
533+/*=================*/
534+ trx_id_t trx_id, /*!< in: trx id of the transaction */
535+ ibool* corrupt) /*!< in: NULL or pointer to a flag
536+ that will be set if corrupt */
537+{
538+ ut_ad(mutex_own(&trx_sys->mutex));
539+
540+ if (UNIV_UNLIKELY(trx_id >= trx_sys->max_trx_id)) {
541+
542+ /* There must be corruption: we let the caller handle the
543+ diagnostic prints in this case. */
544+
545+ if (corrupt != NULL) {
546+ *corrupt = TRUE;
547+ }
548+
549+ return(NULL);
550+ }
551+
552+ return(trx_find_descriptor(trx_sys->descriptors, trx_sys->descr_n_used,
553+ trx_id) != NULL);
554+}
555+
556+/****************************************************************//**
557 Checks if a rw transaction with the given id is active. If the caller is
558 not holding lock_sys->mutex, the transaction may already have been
559 committed.
560-@return transaction instance if active, or NULL;
561-the pointer must not be dereferenced unless lock_sys->mutex was
562-acquired before calling this function and is still being held */
563+@return true if rw transaction it with a given id is active. */
564 UNIV_INLINE
565-trx_t*
566+bool
567 trx_rw_is_active(
568 /*=============*/
569 trx_id_t trx_id, /*!< in: trx id of the transaction */
570 ibool* corrupt) /*!< in: NULL or pointer to a flag
571 that will be set if corrupt */
572 {
573- trx_t* trx;
574+ bool res;
575
576 mutex_enter(&trx_sys->mutex);
577
578- trx = trx_rw_is_active_low(trx_id, corrupt);
579+ res = trx_rw_is_active_low(trx_id, corrupt);
580
581 mutex_exit(&trx_sys->mutex);
582
583- return(trx);
584+ return(res);
585 }
586
587 /*****************************************************************//**
588@@ -509,4 +538,28 @@
589
590 return(n_trx);
591 }
592+
593+
594+/*************************************************************//**
595+Find a slot for a given trx ID in a descriptors array.
596+@return: slot pointer */
597+UNIV_INLINE
598+trx_id_t*
599+trx_find_descriptor(
600+/*================*/
601+ const trx_id_t* descriptors, /*!< in: descriptors array */
602+ ulint n_descr, /*!< in: array size */
603+ trx_id_t trx_id) /*!< in: trx id */
604+{
605+ ut_ad(descriptors != trx_sys->descriptors ||
606+ mutex_own(&trx_sys->mutex));
607+
608+ if (UNIV_UNLIKELY(n_descr == 0)) {
609+
610+ return(NULL);
611+ }
612+
613+ return((trx_id_t *) bsearch(&trx_id, descriptors, n_descr,
614+ sizeof(trx_id_t), trx_descr_cmp));
615+}
616 #endif /* !UNIV_HOTBACKUP */
617
618=== modified file 'Percona-Server/storage/innobase/include/trx0trx.h'
619--- Percona-Server/storage/innobase/include/trx0trx.h 2013-05-23 06:51:50 +0000
620+++ Percona-Server/storage/innobase/include/trx0trx.h 2013-05-24 12:02:59 +0000
621@@ -459,6 +459,24 @@
622 /*============*/
623 trx_t* trx); /*!< A read-only transaction that
624 needs to be assigned a RBS. */
625+
626+/*************************************************************//**
627+Callback function for trx_find_descriptor() to compare trx IDs. */
628+UNIV_INTERN
629+int
630+trx_descr_cmp(
631+/*==========*/
632+ const void *a, /*!< in: pointer to first comparison argument */
633+ const void *b); /*!< in: pointer to second comparison argument */
634+
635+/*************************************************************//**
636+Release a slot for a given trx in the global descriptors array. */
637+UNIV_INTERN
638+void
639+trx_release_descriptor(
640+/*===================*/
641+ trx_t* trx); /*!< in: trx pointer */
642+
643 /*******************************************************************//**
644 Transactions that aren't started by the MySQL server don't set
645 the trx_t::mysql_thd field. For such transactions we set the lock
646@@ -892,6 +910,12 @@
647 /*!< TRUE if in
648 trx_sys->mysql_trx_list */
649 #endif /* UNIV_DEBUG */
650+ UT_LIST_NODE_T(trx_t)
651+ trx_serial_list;/*!< list node for
652+ trx_sys->trx_serial_list */
653+ ibool in_trx_serial_list;
654+ /* Set when transaction is in the
655+ trx_serial_list */
656 /*------------------------------*/
657 dberr_t error_state; /*!< 0 if no error, otherwise error
658 number; NOTE That ONLY the thread
659
660=== modified file 'Percona-Server/storage/innobase/lock/lock0lock.cc'
661--- Percona-Server/storage/innobase/lock/lock0lock.cc 2013-05-12 09:13:00 +0000
662+++ Percona-Server/storage/innobase/lock/lock0lock.cc 2013-05-24 12:02:59 +0000
663@@ -5556,7 +5556,7 @@
664 if the check and assertion are covered by the lock mutex. */
665
666 trx_id = lock_clust_rec_some_has_impl(rec, index, offsets);
667- impl_trx = trx_rw_is_active_low(trx_id, NULL);
668+ impl_trx = trx_rw_get_active_trx_by_id(trx_id, NULL);
669
670 ut_ad(lock_mutex_own());
671 /* impl_trx cannot be committed until lock_mutex_exit()
672@@ -6055,7 +6055,9 @@
673 /* If the transaction is still active and has no
674 explicit x-lock set on the record, set one for it */
675
676- impl_trx = trx_rw_is_active(trx_id, NULL);
677+ mutex_enter(&trx_sys->mutex);
678+ impl_trx = trx_rw_get_active_trx_by_id(trx_id, NULL);
679+ mutex_exit(&trx_sys->mutex);
680
681 /* impl_trx cannot be committed until lock_mutex_exit()
682 because lock_trx_release_locks() acquires lock_sys->mutex */
683@@ -6843,8 +6845,12 @@
684 }
685
686 /* The transition of trx->state to TRX_STATE_COMMITTED_IN_MEMORY
687- is protected by both the lock_sys->mutex and the trx->mutex. */
688+ is protected by both the lock_sys->mutex and the trx->mutex.
689+ We also lock trx_sys->mutex, because state transition to
690+ TRX_STATE_COMMITTED_IN_MEMORY must be atomic with removing trx
691+ from the descriptors array. */
692 lock_mutex_enter();
693+ mutex_enter(&trx_sys->mutex);
694 trx_mutex_enter(trx);
695
696 /* The following assignment makes the transaction committed in memory
697@@ -6863,6 +6869,8 @@
698
699 /*--------------------------------------*/
700 trx->state = TRX_STATE_COMMITTED_IN_MEMORY;
701+ /* The following also removes trx from trx_serial_list */
702+ trx_release_descriptor(trx);
703 /*--------------------------------------*/
704
705 /* If the background thread trx_rollback_or_clean_recovered()
706@@ -6880,6 +6888,8 @@
707
708 trx_mutex_exit(trx);
709
710+ mutex_exit(&trx_sys->mutex);
711+
712 lock_release(trx);
713
714 lock_mutex_exit();
715
716=== modified file 'Percona-Server/storage/innobase/read/read0read.cc'
717--- Percona-Server/storage/innobase/read/read0read.cc 2013-05-23 06:51:50 +0000
718+++ Percona-Server/storage/innobase/read/read0read.cc 2013-05-24 12:02:59 +0000
719@@ -189,23 +189,23 @@
720 if (view == NULL) {
721 view = static_cast<read_view_t*>(
722 ut_malloc(sizeof(read_view_t)));
723- view->max_trx_ids = 0;
724- view->trx_ids = NULL;
725+ view->max_descr = 0;
726+ view->descriptors = NULL;
727 }
728
729- if (UNIV_UNLIKELY(view->max_trx_ids < n)) {
730+ if (UNIV_UNLIKELY(view->max_descr < n)) {
731
732- /* avoid frequent reallocations by extending the array to the
733+ /* avoid frequent re-allocations by extending the array to the
734 desired size + 10% */
735
736- view->max_trx_ids = n + n / 10;
737- view->trx_ids = static_cast<trx_id_t*>(
738- ut_realloc(view->trx_ids,
739- view->max_trx_ids *
740- sizeof *view->trx_ids));
741+ view->max_descr = n + n / 10;
742+ view->descriptors = static_cast<trx_id_t*>(
743+ ut_realloc(view->descriptors,
744+ view->max_descr *
745+ sizeof *view->descriptors));
746 }
747
748- view->n_trx_ids = n;
749+ view->n_descr = n;
750
751 return(view);
752 }
753@@ -225,24 +225,24 @@
754 NULL */
755 {
756 read_view_t* clone;
757- trx_id_t* old_trx_ids;
758- ulint old_max_trx_ids;
759+ trx_id_t* old_descriptors;
760+ ulint old_max_descr;
761
762 ut_ad(mutex_own(&trx_sys->mutex));
763
764- clone = read_view_create_low(view->n_trx_ids, prebuilt_clone);
765+ clone = read_view_create_low(view->n_descr, prebuilt_clone);
766
767- old_trx_ids = clone->trx_ids;
768- old_max_trx_ids = clone->max_trx_ids;
769+ old_descriptors = clone->descriptors;
770+ old_max_descr = clone->max_descr;
771
772 memcpy(clone, view, sizeof(*view));
773
774- clone->trx_ids = old_trx_ids;
775- clone->max_trx_ids = old_max_trx_ids;
776+ clone->descriptors = old_descriptors;
777+ clone->max_descr = old_max_descr;
778
779- if (view->n_trx_ids) {
780- memcpy(clone->trx_ids, view->trx_ids,
781- view->n_trx_ids * sizeof(trx_id_t));
782+ if (view->n_descr) {
783+ memcpy(clone->descriptors, view->descriptors,
784+ view->n_descr * sizeof(trx_id_t));
785 }
786
787 return(clone);
788@@ -280,53 +280,6 @@
789 ut_ad(read_view_list_validate());
790 }
791
792-/** Functor to create thew view trx_ids array. */
793-struct CreateView {
794-
795- CreateView(read_view_t* view)
796- : m_view(view)
797- {
798- m_n_trx = m_view->n_trx_ids;
799- m_view->n_trx_ids = 0;
800- }
801-
802- void operator()(const trx_t* trx)
803- {
804- ut_ad(mutex_own(&trx_sys->mutex));
805- ut_ad(trx->in_rw_trx_list);
806-
807- /* trx->state cannot change from or to NOT_STARTED
808- while we are holding the trx_sys->mutex. It may change
809- from ACTIVE to PREPARED or COMMITTED. */
810-
811- if (trx->id != m_view->creator_trx_id
812- && !trx_state_eq(trx, TRX_STATE_COMMITTED_IN_MEMORY)) {
813-
814- ut_ad(m_n_trx > m_view->n_trx_ids);
815-
816- m_view->trx_ids[m_view->n_trx_ids++] = trx->id;
817-
818- /* NOTE that a transaction whose trx number is <
819- trx_sys->max_trx_id can still be active, if it is
820- in the middle of its commit! Note that when a
821- transaction starts, we initialize trx->no to
822- IB_ULONGLONG_MAX. */
823-
824- /* trx->no is protected by trx_sys->mutex, which
825- we are holding. It is assigned by trx_commit()
826- before lock_trx_release_locks() assigns
827- trx->state = TRX_STATE_COMMITTED_IN_MEMORY. */
828-
829- if (m_view->low_limit_no > trx->no) {
830- m_view->low_limit_no = trx->no;
831- }
832- }
833- }
834-
835- read_view_t* m_view;
836- ulint m_n_trx;
837-};
838-
839 /*********************************************************************//**
840 Opens a read view where exactly the transactions serialized before this
841 point in time are seen in the view.
842@@ -340,11 +293,12 @@
843 read_view_t*& view) /*!< in,out: pre-allocated view array or
844 NULL if a new one needs to be created */
845 {
846- ulint n_trx = UT_LIST_GET_LEN(trx_sys->rw_trx_list);
847+ trx_id_t* descr;
848+ ulint i;
849
850 ut_ad(mutex_own(&trx_sys->mutex));
851
852- view = read_view_create_low(n_trx, view);
853+ view = read_view_create_low(trx_sys->descr_n_used, view);
854
855 view->undo_no = 0;
856 view->type = VIEW_NORMAL;
857@@ -355,13 +309,52 @@
858 view->low_limit_no = trx_sys->max_trx_id;
859 view->low_limit_id = view->low_limit_no;
860
861- /* No active transaction should be visible, except cr_trx */
862-
863- ut_list_map(trx_sys->rw_trx_list, &trx_t::trx_list, CreateView(view));
864-
865- if (view->n_trx_ids > 0) {
866+ descr = trx_find_descriptor(trx_sys->descriptors,
867+ trx_sys->descr_n_used,
868+ cr_trx_id);
869+ if (UNIV_LIKELY(descr != NULL)) {
870+ ut_ad(trx_sys->descr_n_used > 0);
871+ ut_ad(view->n_descr > 0);
872+
873+ view->n_descr--;
874+
875+ i = descr - trx_sys->descriptors;
876+ } else {
877+ i = trx_sys->descr_n_used;
878+ }
879+
880+ if (UNIV_LIKELY(i > 0)) {
881+ /* Copy the [0; i-1] range */
882+ memcpy(view->descriptors, trx_sys->descriptors,
883+ i * sizeof(trx_id_t));
884+ }
885+
886+ if (UNIV_UNLIKELY(i + 1 < trx_sys->descr_n_used)) {
887+ /* Copy the [i+1; descr_n_used-1] range */
888+ memcpy(view->descriptors + i,
889+ trx_sys->descriptors + i + 1,
890+ (trx_sys->descr_n_used - i - 1) *
891+ sizeof(trx_id_t));
892+ }
893+
894+ /* NOTE that a transaction whose trx number is < trx_sys->max_trx_id can
895+ still be active, if it is in the middle of its commit! Note that when a
896+ transaction starts, we initialize trx->no to IB_ULONGLONG_MAX. */
897+
898+ if (UT_LIST_GET_LEN(trx_sys->trx_serial_list) > 0) {
899+
900+ trx_id_t trx_no;
901+
902+ trx_no = UT_LIST_GET_FIRST(trx_sys->trx_serial_list)->no;
903+
904+ if (trx_no < view->low_limit_no) {
905+ view->low_limit_no = trx_no;
906+ }
907+ }
908+
909+ if (UNIV_LIKELY(view->n_descr > 0)) {
910 /* The last active transaction has the smallest id: */
911- view->up_limit_id = view->trx_ids[view->n_trx_ids - 1];
912+ view->up_limit_id = view->descriptors[0];
913 } else {
914 view->up_limit_id = view->low_limit_id;
915 }
916@@ -442,29 +435,29 @@
917 ut_a(oldest_view->creator_trx_id > 0);
918 creator_trx_id = oldest_view->creator_trx_id;
919
920- view = read_view_create_low(oldest_view->n_trx_ids + 1, prebuilt_view);
921+ view = read_view_create_low(oldest_view->n_descr + 1, prebuilt_view);
922
923 /* Add the creator transaction id in the trx_ids array in the
924 correct slot. */
925
926- for (i = 0; i < oldest_view->n_trx_ids; ++i) {
927+ for (i = 0; i < oldest_view->n_descr; ++i) {
928 trx_id_t id;
929
930- id = oldest_view->trx_ids[i - insert_done];
931+ id = oldest_view->descriptors[i - insert_done];
932
933- if (insert_done == 0 && creator_trx_id > id) {
934+ if (insert_done == 0 && creator_trx_id < id) {
935 id = creator_trx_id;
936 insert_done = 1;
937 }
938
939- view->trx_ids[i] = id;
940+ view->descriptors[i] = id;
941 }
942
943 if (insert_done == 0) {
944- view->trx_ids[i] = creator_trx_id;
945+ view->descriptors[i] = creator_trx_id;
946 } else {
947 ut_a(i > 0);
948- view->trx_ids[i] = oldest_view->trx_ids[i - 1];
949+ view->descriptors[i] = oldest_view->descriptors[i - 1];
950 }
951
952 view->creator_trx_id = 0;
953@@ -472,10 +465,10 @@
954 view->low_limit_no = oldest_view->low_limit_no;
955 view->low_limit_id = oldest_view->low_limit_id;
956
957- if (view->n_trx_ids > 0) {
958+ if (view->n_descr > 0) {
959 /* The last active transaction has the smallest id: */
960
961- view->up_limit_id = view->trx_ids[view->n_trx_ids - 1];
962+ view->up_limit_id = view->descriptors[0];
963 } else {
964 view->up_limit_id = oldest_view->up_limit_id;
965 }
966@@ -531,11 +524,11 @@
967
968 fprintf(file, "Read view individually stored trx ids:\n");
969
970- n_ids = view->n_trx_ids;
971+ n_ids = view->n_descr;
972
973 for (i = 0; i < n_ids; i++) {
974 fprintf(file, "Read view trx id " TRX_ID_FMT "\n",
975- view->trx_ids[i]);
976+ view->descriptors[i]);
977 }
978 }
979
980@@ -582,8 +575,8 @@
981 return;
982 }
983
984- if (view->trx_ids != NULL) {
985- ut_free(view->trx_ids);
986+ if (view->descriptors != NULL) {
987+ ut_free(view->descriptors);
988 }
989
990 ut_free(view);
991@@ -604,7 +597,6 @@
992 {
993 read_view_t* view;
994 mem_heap_t* heap;
995- ulint n_trx;
996 cursor_view_t* curview;
997
998 /* Use larger heap than in trx_create when creating a read_view
999@@ -625,36 +617,12 @@
1000
1001 mutex_enter(&trx_sys->mutex);
1002
1003- n_trx = UT_LIST_GET_LEN(trx_sys->rw_trx_list);
1004-
1005 curview->read_view = NULL;
1006- read_view_create_low(n_trx, curview->read_view);
1007+ read_view_open_now_low(UINT64_UNDEFINED, curview->read_view);
1008
1009 view = curview->read_view;
1010 view->undo_no = cr_trx->undo_no;
1011 view->type = VIEW_HIGH_GRANULARITY;
1012- view->creator_trx_id = UINT64_UNDEFINED;
1013-
1014- /* No future transactions should be visible in the view */
1015-
1016- view->low_limit_no = trx_sys->max_trx_id;
1017- view->low_limit_id = view->low_limit_no;
1018-
1019- /* No active transaction should be visible */
1020-
1021- ut_list_map(trx_sys->rw_trx_list, &trx_t::trx_list, CreateView(view));
1022-
1023- view->creator_trx_id = cr_trx->id;
1024-
1025- if (view->n_trx_ids > 0) {
1026- /* The last active transaction has the smallest id: */
1027-
1028- view->up_limit_id = view->trx_ids[view->n_trx_ids - 1];
1029- } else {
1030- view->up_limit_id = view->low_limit_id;
1031- }
1032-
1033- read_view_add(view);
1034
1035 mutex_exit(&trx_sys->mutex);
1036
1037
1038=== modified file 'Percona-Server/storage/innobase/row/row0sel.cc'
1039--- Percona-Server/storage/innobase/row/row0sel.cc 2013-05-23 06:51:50 +0000
1040+++ Percona-Server/storage/innobase/row/row0sel.cc 2013-05-24 12:02:59 +0000
1041@@ -5205,8 +5205,8 @@
1042 if (trx->isolation_level >= TRX_ISO_REPEATABLE_READ
1043 && !trx->read_view) {
1044
1045- trx->read_view = read_view_open_now(
1046- trx->id, trx->prebuilt_view);
1047+ trx->read_view = read_view_open_now(trx->id,
1048+ trx->prebuilt_view);
1049
1050 trx->global_read_view = trx->read_view;
1051 }
1052
1053=== modified file 'Percona-Server/storage/innobase/row/row0vers.cc'
1054--- Percona-Server/storage/innobase/row/row0vers.cc 2013-05-10 09:39:17 +0000
1055+++ Percona-Server/storage/innobase/row/row0vers.cc 2013-05-24 12:02:59 +0000
1056@@ -646,7 +646,7 @@
1057 version = rec;
1058
1059 for (;;) {
1060- const trx_t* version_trx;
1061+ trx_id_t* version_trx_descr;
1062 mem_heap_t* heap2;
1063 rec_t* prev_version;
1064 trx_id_t version_trx_id;
1065@@ -657,19 +657,16 @@
1066 }
1067
1068 mutex_enter(&trx_sys->mutex);
1069- version_trx = trx_get_rw_trx_by_id(version_trx_id);
1070+ version_trx_descr = trx_find_descriptor(trx_sys->descriptors,
1071+ trx_sys->descr_n_used,
1072+ version_trx_id);
1073 /* Because version_trx is a read-write transaction,
1074 its state cannot change from or to NOT_STARTED while
1075 we are holding the trx_sys->mutex. It may change from
1076 ACTIVE to PREPARED or COMMITTED. */
1077- if (version_trx
1078- && trx_state_eq(version_trx,
1079- TRX_STATE_COMMITTED_IN_MEMORY)) {
1080- version_trx = NULL;
1081- }
1082 mutex_exit(&trx_sys->mutex);
1083
1084- if (!version_trx) {
1085+ if (!version_trx_descr) {
1086 committed_version_trx:
1087 /* We found a version that belongs to a
1088 committed transaction: return it. */
1089
1090=== modified file 'Percona-Server/storage/innobase/trx/trx0sys.cc'
1091--- Percona-Server/storage/innobase/trx/trx0sys.cc 2013-05-12 06:24:46 +0000
1092+++ Percona-Server/storage/innobase/trx/trx0sys.cc 2013-05-24 12:02:59 +0000
1093@@ -506,6 +506,13 @@
1094
1095 mtr_start(&mtr);
1096
1097+ /* Allocate the trx descriptors array */
1098+ trx_sys->descriptors = static_cast<trx_id_t*>(
1099+ ut_malloc(sizeof(trx_id_t) *
1100+ TRX_DESCR_ARRAY_INITIAL_SIZE));
1101+ trx_sys->descr_n_max = TRX_DESCR_ARRAY_INITIAL_SIZE;
1102+ trx_sys->descr_n_used = 0;
1103+
1104 sys_header = trx_sysf_get(&mtr);
1105
1106 if (srv_force_recovery < SRV_FORCE_NO_UNDO_LOG_SCAN) {
1107@@ -1232,6 +1239,9 @@
1108
1109 mutex_free(&trx_sys->mutex);
1110
1111+ ut_ad(trx_sys->descr_n_used == 0);
1112+ ut_free(trx_sys->descriptors);
1113+
1114 mem_free(trx_sys);
1115
1116 trx_sys = NULL;
1117
1118=== modified file 'Percona-Server/storage/innobase/trx/trx0trx.cc'
1119--- Percona-Server/storage/innobase/trx/trx0trx.cc 2013-05-23 06:51:50 +0000
1120+++ Percona-Server/storage/innobase/trx/trx0trx.cc 2013-05-24 12:02:59 +0000
1121@@ -83,6 +83,127 @@
1122 sizeof(trx->detailed_error));
1123 }
1124
1125+/*************************************************************//**
1126+Callback function for trx_find_descriptor() to compare trx IDs. */
1127+UNIV_INTERN
1128+int
1129+trx_descr_cmp(
1130+/*==========*/
1131+ const void *a, /*!< in: pointer to first comparison argument */
1132+ const void *b) /*!< in: pointer to second comparison argument */
1133+{
1134+ const trx_id_t* da = (const trx_id_t*) a;
1135+ const trx_id_t* db = (const trx_id_t*) b;
1136+
1137+ if (*da < *db) {
1138+ return -1;
1139+ } else if (*da > *db) {
1140+ return 1;
1141+ }
1142+
1143+ return 0;
1144+}
1145+
1146+/*************************************************************//**
1147+Reserve a slot for a given trx in the global descriptors array. */
1148+UNIV_INLINE
1149+void
1150+trx_reserve_descriptor(
1151+/*===================*/
1152+ const trx_t* trx) /*!< in: trx pointer */
1153+{
1154+ ulint n_used;
1155+ ulint n_max;
1156+ trx_id_t* descr;
1157+
1158+ ut_ad(mutex_own(&trx_sys->mutex) || srv_is_being_started);
1159+ ut_ad(srv_is_being_started ||
1160+ !trx_find_descriptor(trx_sys->descriptors,
1161+ trx_sys->descr_n_used,
1162+ trx->id));
1163+
1164+ n_used = trx_sys->descr_n_used + 1;
1165+ n_max = trx_sys->descr_n_max;
1166+
1167+ if (UNIV_UNLIKELY(n_used > n_max)) {
1168+
1169+ n_max = n_max * 2;
1170+
1171+ trx_sys->descriptors = static_cast<trx_id_t*>(
1172+ ut_realloc(trx_sys->descriptors,
1173+ n_max * sizeof(trx_id_t)));
1174+
1175+ trx_sys->descr_n_max = n_max;
1176+ }
1177+
1178+ descr = trx_sys->descriptors + n_used - 1;
1179+
1180+ if (UNIV_UNLIKELY(n_used > 1 && trx->id < descr[-1])) {
1181+
1182+ /* Find the slot where it should be inserted. We could use a
1183+ binary search, but in reality linear search should be faster,
1184+ because the slot we are looking for is near the array end. */
1185+
1186+ trx_id_t* tdescr;
1187+
1188+ for (tdescr = descr - 1;
1189+ tdescr >= trx_sys->descriptors && *tdescr > trx->id;
1190+ tdescr--) {
1191+ }
1192+
1193+ tdescr++;
1194+
1195+ ut_memmove(tdescr + 1, tdescr, (descr - tdescr) *
1196+ sizeof(trx_id_t));
1197+
1198+ descr = tdescr;
1199+ }
1200+
1201+ *descr = trx->id;
1202+
1203+ trx_sys->descr_n_used = n_used;
1204+}
1205+
1206+/*************************************************************//**
1207+Release a slot for a given trx in the global descriptors array. */
1208+UNIV_INTERN
1209+void
1210+trx_release_descriptor(
1211+/*===================*/
1212+ trx_t* trx) /*!< in: trx pointer */
1213+{
1214+ ulint size;
1215+ trx_id_t* descr;
1216+
1217+ ut_ad(mutex_own(&trx_sys->mutex));
1218+
1219+ if (UNIV_LIKELY(trx->in_trx_serial_list)) {
1220+
1221+ UT_LIST_REMOVE(trx_serial_list, trx_sys->trx_serial_list,
1222+ trx);
1223+ trx->in_trx_serial_list = 0;
1224+ }
1225+
1226+ descr = trx_find_descriptor(trx_sys->descriptors,
1227+ trx_sys->descr_n_used,
1228+ trx->id);
1229+
1230+ if (UNIV_UNLIKELY(descr == NULL)) {
1231+
1232+ return;
1233+ }
1234+
1235+ size = (trx_sys->descriptors + trx_sys->descr_n_used - 1 - descr) *
1236+ sizeof(trx_id_t);
1237+
1238+ if (UNIV_LIKELY(size > 0)) {
1239+
1240+ ut_memmove(descr, descr + 1, size);
1241+ }
1242+
1243+ trx_sys->descr_n_used--;
1244+}
1245+
1246 /****************************************************************//**
1247 Creates and initializes a transaction object. It must be explicitly
1248 started with trx_start_if_not_started() before using it. The default
1249@@ -108,6 +229,7 @@
1250 trx->isolation_level = TRX_ISO_REPEATABLE_READ;
1251
1252 trx->no = IB_ULONGLONG_MAX;
1253+ trx->in_trx_serial_list = 0;
1254
1255 trx->support_xa = TRUE;
1256
1257@@ -207,11 +329,12 @@
1258 }
1259
1260 /********************************************************************//**
1261-Frees a transaction object. */
1262+Frees a transaction object without releasing the corresponding descriptor.
1263+Should be used by callers that already own trx_sys->mutex. */
1264 static
1265 void
1266-trx_free(
1267-/*=====*/
1268+trx_free_low(
1269+/*=========*/
1270 trx_t* trx) /*!< in, own: trx object */
1271 {
1272 ut_a(trx->magic_n == TRX_MAGIC_N);
1273@@ -255,6 +378,21 @@
1274 }
1275
1276 /********************************************************************//**
1277+Frees a transaction object. */
1278+static
1279+void
1280+trx_free(
1281+/*=========*/
1282+ trx_t* trx) /*!< in, own: trx object */
1283+{
1284+ mutex_enter(&trx_sys->mutex);
1285+ trx_release_descriptor(trx);
1286+ mutex_exit(&trx_sys->mutex);
1287+
1288+ trx_free_low(trx);
1289+}
1290+
1291+/********************************************************************//**
1292 Frees a transaction object of a background operation of the master thread. */
1293 UNIV_INTERN
1294 void
1295@@ -328,7 +466,11 @@
1296 UT_LIST_REMOVE(trx_list, trx_sys->rw_trx_list, trx);
1297 ut_d(trx->in_rw_trx_list = FALSE);
1298
1299- trx_free(trx);
1300+ trx_release_descriptor(trx);
1301+
1302+ trx_free_low(trx);
1303+
1304+ ut_ad(trx_sys->descr_n_used <= UT_LIST_GET_LEN(trx_sys->rw_trx_list));
1305 }
1306
1307 /********************************************************************//**
1308@@ -595,6 +737,7 @@
1309
1310 UT_LIST_INIT(trx_sys->ro_trx_list);
1311 UT_LIST_INIT(trx_sys->rw_trx_list);
1312+ UT_LIST_INIT(trx_sys->trx_serial_list);
1313
1314 /* Look from the rollback segments if there exist undo logs for
1315 transactions */
1316@@ -617,6 +760,11 @@
1317
1318 trx = trx_resurrect_insert(undo, rseg);
1319
1320+ if (trx->state == TRX_STATE_ACTIVE ||
1321+ trx->state == TRX_STATE_PREPARED) {
1322+
1323+ trx_reserve_descriptor(trx);
1324+ }
1325 trx_list_rw_insert_ordered(trx);
1326 }
1327
1328@@ -642,6 +790,11 @@
1329 trx_resurrect_update(trx, undo, rseg);
1330
1331 if (trx_created) {
1332+ if (trx->state == TRX_STATE_ACTIVE ||
1333+ trx->state == TRX_STATE_PREPARED) {
1334+
1335+ trx_reserve_descriptor(trx);
1336+ }
1337 trx_list_rw_insert_ordered(trx);
1338 }
1339 }
1340@@ -791,6 +944,8 @@
1341 UT_LIST_ADD_FIRST(trx_list, trx_sys->rw_trx_list, trx);
1342 ut_d(trx->in_rw_trx_list = TRUE);
1343 ut_d(trx_sys->rw_max_trx_id = trx->id);
1344+
1345+ trx_reserve_descriptor(trx);
1346 }
1347
1348 ut_ad(trx_sys_validate_trx_list());
1349@@ -820,6 +975,14 @@
1350
1351 trx->no = trx_sys_get_new_trx_id();
1352
1353+ if (UNIV_LIKELY(trx->in_trx_serial_list == 0)) {
1354+
1355+ UT_LIST_ADD_LAST(trx_serial_list, trx_sys->trx_serial_list,
1356+ trx);
1357+
1358+ trx->in_trx_serial_list = 1;
1359+ }
1360+
1361 /* If the rollack segment is not empty then the
1362 new trx_t::no can't be less than any trx_t::no
1363 already in the rollback segment. User threads only
1364@@ -1104,6 +1267,8 @@
1365 } else {
1366 UT_LIST_REMOVE(trx_list, trx_sys->rw_trx_list, trx);
1367 ut_d(trx->in_rw_trx_list = FALSE);
1368+ ut_ad(trx_sys->descr_n_used <=
1369+ UT_LIST_GET_LEN(trx_sys->rw_trx_list));
1370 MONITOR_INC(MONITOR_TRX_RW_COMMIT);
1371 }
1372
1373@@ -1220,6 +1385,8 @@
1374
1375 trx->dict_operation = TRX_DICT_OP_NONE;
1376
1377+ ut_ad(trx_sys->descr_n_used <= UT_LIST_GET_LEN(trx_sys->rw_trx_list));
1378+
1379 trx->error_state = DB_SUCCESS;
1380
1381 /* trx->in_mysql_trx_list would hold between
1382@@ -1343,10 +1510,14 @@
1383 ut_a(!trx->read_only);
1384
1385 UT_LIST_REMOVE(trx_list, trx_sys->rw_trx_list, trx);
1386+ ut_ad(trx_sys->descr_n_used <= UT_LIST_GET_LEN(trx_sys->rw_trx_list));
1387
1388 assert_trx_in_rw_list(trx);
1389 ut_d(trx->in_rw_trx_list = FALSE);
1390
1391+ trx->state = TRX_STATE_NOT_STARTED;
1392+ trx_release_descriptor(trx);
1393+
1394 mutex_exit(&trx_sys->mutex);
1395
1396 /* Change the transaction state without mutex protection, now
1397@@ -1356,7 +1527,6 @@
1398 ut_ad(!trx->in_ro_trx_list);
1399 ut_ad(!trx->in_rw_trx_list);
1400 ut_ad(!trx->in_mysql_trx_list);
1401- trx->state = TRX_STATE_NOT_STARTED;
1402 }
1403
1404 /********************************************************************//**
1405@@ -1376,11 +1546,8 @@
1406 return(trx->read_view);
1407 }
1408
1409- if (!trx->read_view) {
1410- trx->read_view = read_view_open_now(trx->id,
1411- trx->prebuilt_view);
1412- trx->global_read_view = trx->read_view;
1413- }
1414+ trx->read_view = read_view_open_now(trx->id, trx->prebuilt_view);
1415+ trx->global_read_view = trx->read_view;
1416
1417 return(trx->read_view);
1418 }
1419
1420=== added directory 'policy'
1421=== added directory 'policy/apparmor'
1422=== added file 'policy/apparmor/usr.sbin.mysqld'
1423--- policy/apparmor/usr.sbin.mysqld 1970-01-01 00:00:00 +0000
1424+++ policy/apparmor/usr.sbin.mysqld 2013-05-24 12:02:59 +0000
1425@@ -0,0 +1,61 @@
1426+# Last Modified: Thu Mar 7 21:58:51 2013
1427+# Based on usr.sbin.mysqld packaged in mysql-server in Ubuntu.
1428+# For Percona Server and Percona XtraDB Cluster
1429+
1430+#include <tunables/global>
1431+
1432+/usr/sbin/mysqld flags=(complain) {
1433+ #include <abstractions/base>
1434+ #include <abstractions/mysql>
1435+ #include <abstractions/nameservice>
1436+ #include <abstractions/user-tmp>
1437+ #include <abstractions/winbind>
1438+ #include <local/usr.sbin.mysqld>
1439+
1440+
1441+ capability chown,
1442+ capability dac_override,
1443+ capability setgid,
1444+ capability setuid,
1445+ capability sys_rawio,
1446+ capability sys_resource,
1447+
1448+ network tcp,
1449+
1450+
1451+ /dev/dm-0 r,
1452+ /etc/group r,
1453+ /etc/gai.conf r,
1454+ /etc/hosts.allow r,
1455+ /etc/hosts.deny r,
1456+ /etc/ld.so.cache r,
1457+ /etc/mtab r,
1458+ /etc/my.cnf r,
1459+ /etc/mysql/*.cnf r,
1460+ /etc/mysql/*.pem r,
1461+ /etc/mysql/conf.d/ r,
1462+ /etc/mysql/conf.d/* r,
1463+ /etc/nsswitch.conf r,
1464+ /etc/passwd r,
1465+ /etc/services r,
1466+ /run/mysqld/mysqld.pid w,
1467+ /run/mysqld/mysqld.sock w,
1468+ /sys/devices/system/cpu/ r,
1469+ owner /tmp/** lk,
1470+ /tmp/** rw,
1471+ /usr/lib/mysql/plugin/ r,
1472+ /usr/lib/mysql/plugin/*.so* mr,
1473+ /usr/sbin/mysqld mr,
1474+ /usr/share/mysql/** r,
1475+ /var/lib/mysql/ r,
1476+ /var/lib/mysql/** rwk,
1477+ /var/log/mysql.err rw,
1478+ /var/log/mysql.log rw,
1479+ /var/log/mysql/ r,
1480+ /var/log/mysql/* rw,
1481+ /var/run/mysqld/mysqld.pid w,
1482+ /var/run/mysqld/mysqld.sock w,
1483+
1484+ # Site-specific additions and overrides. See local/README for details.
1485+ #include <local/usr.sbin.mysqld>
1486+}
1487
1488=== added file 'policy/apparmor/usr.sbin.mysqld.local'
1489--- policy/apparmor/usr.sbin.mysqld.local 1970-01-01 00:00:00 +0000
1490+++ policy/apparmor/usr.sbin.mysqld.local 2013-05-24 12:02:59 +0000
1491@@ -0,0 +1,2 @@
1492+# Site-specific additions and overrides for usr.sbin.mysqld..
1493+# For more details, please see /etc/apparmor.d/local/README.
1494
1495=== added directory 'policy/selinux'
1496=== added file 'policy/selinux/percona-server.fc'
1497--- policy/selinux/percona-server.fc 1970-01-01 00:00:00 +0000
1498+++ policy/selinux/percona-server.fc 2013-05-24 12:02:59 +0000
1499@@ -0,0 +1,6 @@
1500+/etc/init\.d/rc\.d/mysql -- gen_context(system_u:object_r:mysqld_initrc_exec_t,s0)
1501+/var/lib/mysql/.*\.log -- gen_context(system_u:object_r:mysqld_log_t,s0)
1502+/var/lib/mysql/.*\.err -- gen_context(system_u:object_r:mysqld_log_t,s0)
1503+/var/lib/mysql/.*\.pid -- gen_context(system_u:object_r:mysqld_var_run_t,s0)
1504+/var/lib/mysql/.*\.cnf -- gen_context(system_u:object_r:mysqld_etc_t,s0)
1505+/usr/bin/xtrabackup.* -- gen_context(system_u:object_r:mysqld_exec_t,s0)
1506
1507=== added file 'policy/selinux/percona-server.te'
1508--- policy/selinux/percona-server.te 1970-01-01 00:00:00 +0000
1509+++ policy/selinux/percona-server.te 2013-05-24 12:02:59 +0000
1510@@ -0,0 +1,35 @@
1511+# This adds few more rules in addition to mysql.pp in selinux-policy-targeted
1512+module percona-server 1.0;
1513+
1514+require {
1515+ type user_tmp_t;
1516+ type mysqld_safe_t;
1517+ type tmp_t;
1518+ type fixed_disk_device_t;
1519+ type mysqld_t;
1520+ type tmpfs_t;
1521+ class sock_file { getattr unlink create };
1522+ class capability { sys_nice sys_resource };
1523+ class blk_file { read write open };
1524+ class file { write getattr read create unlink open };
1525+ class dir { search read write remove_name open add_name };
1526+}
1527+
1528+#============= mysqld_safe_t ==============
1529+allow mysqld_safe_t self:capability { sys_nice sys_resource };
1530+
1531+allow mysqld_safe_t tmp_t:dir { write remove_name };
1532+allow mysqld_safe_t tmp_t:sock_file { getattr unlink };
1533+allow mysqld_safe_t user_tmp_t:sock_file { getattr unlink };
1534+
1535+#============= mysqld_t ==============
1536+allow mysqld_t fixed_disk_device_t:blk_file { read write open };
1537+allow mysqld_t tmp_t:sock_file { create unlink };
1538+
1539+allow mysqld_t tmpfs_t:dir { write search read remove_name open add_name };
1540+allow mysqld_t tmpfs_t:file { write getattr read create unlink open };
1541+
1542+allow mysqld_t user_tmp_t:dir { write add_name };
1543+allow mysqld_t user_tmp_t:file create;
1544+
1545+allow mysqld_t tmp_t:file { append create read write open getattr unlink setattr };

Subscribers

People subscribed via source and target branches