130
130
situation, which theoretically should not happen;
131
131
to set timeout equal to <T> seconds add
132
132
#define KEYCACHE_TIMEOUT <T>
133
- to enable the module traps and to send debug information from
134
key cache module to a special debug log add:
135
#define KEYCACHE_DEBUG
136
the name of this debug log file <LOG NAME> can be set through:
137
#define KEYCACHE_DEBUG_LOG <LOG NAME>
138
if the name is not defined, it's set by default;
139
if the KEYCACHE_DEBUG flag is not set up and we are in a debug
140
mode, i.e. when ! defined(DBUG_OFF), the debug information from the
141
module is sent to the regular debug log.
134
143
Example of the settings:
135
144
#define SERIALIZED_READ_FROM_CACHE
136
145
#define MAX_THREADS 100
137
146
#define KEYCACHE_TIMEOUT 1
147
#define KEYCACHE_DEBUG
148
#define KEYCACHE_DEBUG_LOG "my_key_cache_debug.log"
140
151
#define STRUCT_PTR(TYPE, MEMBER, a) \
193
204
*next_changed, **prev_changed; /* for lists of file dirty/clean blocks */
194
205
struct st_hash_link *hash_link; /* backward ptr to referring hash_link */
195
206
KEYCACHE_WQUEUE wqueue[2]; /* queues on waiting requests for new/old pages */
196
uint32_t requests; /* number of requests for the block */
197
unsigned char *buffer; /* buffer for the block page */
198
uint32_t offset; /* beginning of modified data in the buffer */
199
uint32_t length; /* end of data in the buffer */
200
uint32_t status; /* state of the block */
207
uint requests; /* number of requests for the block */
208
uchar *buffer; /* buffer for the block page */
209
uint offset; /* beginning of modified data in the buffer */
210
uint length; /* end of data in the buffer */
211
uint status; /* state of the block */
201
212
enum BLOCK_TEMPERATURE temperature; /* block temperature: cold, warm, hot */
202
uint32_t hits_left; /* number of hits left until promotion */
203
uint64_t last_hit_time; /* timestamp of the last hit */
213
uint hits_left; /* number of hits left until promotion */
214
ulonglong last_hit_time; /* timestamp of the last hit */
204
215
KEYCACHE_CONDVAR *condvar; /* condition variable for 'no readers' event */
210
221
#define FLUSH_CACHE 2000 /* sort this many blocks at once */
212
223
static int flush_all_key_blocks(KEY_CACHE *keycache);
213
225
static void wait_on_queue(KEYCACHE_WQUEUE *wqueue,
214
226
pthread_mutex_t *mutex);
215
227
static void release_whole_queue(KEYCACHE_WQUEUE *wqueue);
229
#define wait_on_queue(wqueue, mutex) do {} while (0)
230
#define release_whole_queue(wqueue) do {} while (0)
216
232
static void free_block(KEY_CACHE *keycache, BLOCK_LINK *block);
233
#if !defined(DBUG_OFF)
234
static void test_key_cache(KEY_CACHE *keycache,
235
const char *where, my_bool lock);
218
238
#define KEYCACHE_HASH(f, pos) \
219
(((uint32_t) ((pos) / keycache->key_cache_block_size) + \
220
(uint32_t) (f)) & (keycache->hash_entries-1))
239
(((ulong) ((pos) / keycache->key_cache_block_size) + \
240
(ulong) (f)) & (keycache->hash_entries-1))
221
241
#define FILE_HASH(f) ((uint) (f) & (CHANGED_BLOCKS_HASH-1))
243
#define DEFAULT_KEYCACHE_DEBUG_LOG "keycache_debug.log"
245
#if defined(KEYCACHE_DEBUG) && ! defined(KEYCACHE_DEBUG_LOG)
246
#define KEYCACHE_DEBUG_LOG DEFAULT_KEYCACHE_DEBUG_LOG
249
#if defined(KEYCACHE_DEBUG_LOG)
250
static FILE *keycache_debug_log=NULL;
251
static void keycache_debug_print _VARARGS((const char *fmt,...));
252
#define KEYCACHE_DEBUG_OPEN \
253
if (!keycache_debug_log) \
255
keycache_debug_log= fopen(KEYCACHE_DEBUG_LOG, "w"); \
256
(void) setvbuf(keycache_debug_log, NULL, _IOLBF, BUFSIZ); \
259
#define KEYCACHE_DEBUG_CLOSE \
260
if (keycache_debug_log) \
262
fclose(keycache_debug_log); \
263
keycache_debug_log= 0; \
266
#define KEYCACHE_DEBUG_OPEN
267
#define KEYCACHE_DEBUG_CLOSE
268
#endif /* defined(KEYCACHE_DEBUG_LOG) */
270
#if defined(KEYCACHE_DEBUG_LOG) && defined(KEYCACHE_DEBUG)
271
#define KEYCACHE_DBUG_PRINT(l, m) \
272
{ if (keycache_debug_log) fprintf(keycache_debug_log, "%s: ", l); \
273
keycache_debug_print m; }
275
#define KEYCACHE_DBUG_ASSERT(a) \
276
{ if (! (a) && keycache_debug_log) fclose(keycache_debug_log); \
279
#define KEYCACHE_DBUG_PRINT(l, m) DBUG_PRINT(l, m)
280
#define KEYCACHE_DBUG_ASSERT(a) DBUG_ASSERT(a)
281
#endif /* defined(KEYCACHE_DEBUG_LOG) && defined(KEYCACHE_DEBUG) */
283
#if defined(KEYCACHE_DEBUG) || !defined(DBUG_OFF)
285
static long keycache_thread_id;
286
#define KEYCACHE_THREAD_TRACE(l) \
287
KEYCACHE_DBUG_PRINT(l,("|thread %ld",keycache_thread_id))
289
#define KEYCACHE_THREAD_TRACE_BEGIN(l) \
290
{ struct st_my_thread_var *thread_var= my_thread_var; \
291
keycache_thread_id= thread_var->id; \
292
KEYCACHE_DBUG_PRINT(l,("[thread %ld",keycache_thread_id)) }
294
#define KEYCACHE_THREAD_TRACE_END(l) \
295
KEYCACHE_DBUG_PRINT(l,("]thread %ld",keycache_thread_id))
297
#define KEYCACHE_THREAD_TRACE(l) KEYCACHE_DBUG_PRINT(l,(""))
298
#define KEYCACHE_THREAD_TRACE_BEGIN(l) KEYCACHE_DBUG_PRINT(l,(""))
299
#define KEYCACHE_THREAD_TRACE_END(l) KEYCACHE_DBUG_PRINT(l,(""))
302
#define KEYCACHE_THREAD_TRACE_BEGIN(l)
303
#define KEYCACHE_THREAD_TRACE_END(l)
304
#define KEYCACHE_THREAD_TRACE(l)
305
#endif /* defined(KEYCACHE_DEBUG) || !defined(DBUG_OFF) */
223
307
#define BLOCK_NUMBER(b) \
224
308
((uint) (((char*)(b)-(char *) keycache->block_root)/sizeof(BLOCK_LINK)))
225
309
#define HASH_LINK_NUMBER(h) \
226
310
((uint) (((char*)(h)-(char *) keycache->hash_link_root)/sizeof(HASH_LINK)))
228
#ifdef KEYCACHE_TIMEOUT
312
#if (defined(KEYCACHE_TIMEOUT)) || defined(KEYCACHE_DEBUG)
229
313
static int keycache_pthread_cond_wait(pthread_cond_t *cond,
230
314
pthread_mutex_t *mutex);
232
316
#define keycache_pthread_cond_wait pthread_cond_wait
319
#if defined(KEYCACHE_DEBUG)
320
static int keycache_pthread_mutex_lock(pthread_mutex_t *mutex);
321
static void keycache_pthread_mutex_unlock(pthread_mutex_t *mutex);
322
static int keycache_pthread_cond_signal(pthread_cond_t *cond);
235
324
#define keycache_pthread_mutex_lock pthread_mutex_lock
236
325
#define keycache_pthread_mutex_unlock pthread_mutex_unlock
237
326
#define keycache_pthread_cond_signal pthread_cond_signal
239
static inline uint32_t next_power(uint32_t value)
327
#endif /* defined(KEYCACHE_DEBUG) */
329
#if !defined(DBUG_OFF)
333
#define inline /* disabled inline for easier debugging */
334
static int fail_block(BLOCK_LINK *block);
335
static int fail_hlink(HASH_LINK *hlink);
336
static int cache_empty(KEY_CACHE *keycache);
339
static inline uint next_power(uint value)
241
return (uint) my_round_up_to_next_power((uint32_t) value) << 1;
341
return (uint) my_round_up_to_next_power((uint32) value) << 1;
270
int init_key_cache(KEY_CACHE *keycache, uint32_t key_cache_block_size,
271
size_t use_mem, uint32_t division_limit,
272
uint32_t age_threshold)
370
int init_key_cache(KEY_CACHE *keycache, uint key_cache_block_size,
371
size_t use_mem, uint division_limit,
274
uint32_t blocks, hash_links;
374
ulong blocks, hash_links;
277
assert(key_cache_block_size >= 512);
377
DBUG_ENTER("init_key_cache");
378
DBUG_ASSERT(key_cache_block_size >= 512);
279
381
if (keycache->key_cache_inited && keycache->disk_blocks > 0)
383
DBUG_PRINT("warning",("key cache already in use"));
284
387
keycache->global_cache_w_requests= keycache->global_cache_r_requests= 0;
353
460
keycache->hash_link_root= (HASH_LINK*) ((char*) keycache->hash_root +
354
461
ALIGN_SIZE((sizeof(HASH_LINK*) *
355
462
keycache->hash_entries)));
356
memset(keycache->block_root, 0,
357
keycache->disk_blocks * sizeof(BLOCK_LINK));
358
memset(keycache->hash_root, 0,
359
keycache->hash_entries * sizeof(HASH_LINK*));
360
memset(keycache->hash_link_root, 0,
361
keycache->hash_links * sizeof(HASH_LINK));
463
bzero((uchar*) keycache->block_root,
464
keycache->disk_blocks * sizeof(BLOCK_LINK));
465
bzero((uchar*) keycache->hash_root,
466
keycache->hash_entries * sizeof(HASH_LINK*));
467
bzero((uchar*) keycache->hash_link_root,
468
keycache->hash_links * sizeof(HASH_LINK));
362
469
keycache->hash_links_used= 0;
363
470
keycache->free_hash_list= NULL;
364
471
keycache->blocks_used= keycache->blocks_changed= 0;
384
491
keycache->waiting_for_hash_link.last_thread= NULL;
385
492
keycache->waiting_for_block.last_thread= NULL;
386
memset(keycache->changed_blocks, 0,
387
sizeof(keycache->changed_blocks[0]) * CHANGED_BLOCKS_HASH);
388
memset(keycache->file_blocks, 0,
389
sizeof(keycache->file_blocks[0]) * CHANGED_BLOCKS_HASH);
494
("disk_blocks: %d block_root: 0x%lx hash_entries: %d\
495
hash_root: 0x%lx hash_links: %d hash_link_root: 0x%lx",
496
keycache->disk_blocks, (long) keycache->block_root,
497
keycache->hash_entries, (long) keycache->hash_root,
498
keycache->hash_links, (long) keycache->hash_link_root));
499
bzero((uchar*) keycache->changed_blocks,
500
sizeof(keycache->changed_blocks[0]) * CHANGED_BLOCKS_HASH);
501
bzero((uchar*) keycache->file_blocks,
502
sizeof(keycache->file_blocks[0]) * CHANGED_BLOCKS_HASH);
446
559
(when cnt_for_resize=0).
449
int resize_key_cache(KEY_CACHE *keycache, uint32_t key_cache_block_size,
450
size_t use_mem, uint32_t division_limit,
451
uint32_t age_threshold)
562
int resize_key_cache(KEY_CACHE *keycache, uint key_cache_block_size,
563
size_t use_mem, uint division_limit,
567
DBUG_ENTER("resize_key_cache");
455
569
if (!keycache->key_cache_inited)
456
return(keycache->disk_blocks);
570
DBUG_RETURN(keycache->disk_blocks);
458
572
if(key_cache_block_size == keycache->key_cache_block_size &&
459
573
use_mem == keycache->key_cache_mem_size)
461
575
change_key_cache_param(keycache, division_limit, age_threshold);
462
return(keycache->disk_blocks);
576
DBUG_RETURN(keycache->disk_blocks);
465
579
keycache_pthread_mutex_lock(&keycache->cache_lock);
468
583
We may need to wait for another thread which is doing a resize
469
584
already. This cannot happen in the MySQL server though. It allows
610
void end_key_cache(KEY_CACHE *keycache, bool cleanup)
733
void end_key_cache(KEY_CACHE *keycache, my_bool cleanup)
735
DBUG_ENTER("end_key_cache");
736
DBUG_PRINT("enter", ("key_cache: 0x%lx", (long) keycache));
612
738
if (!keycache->key_cache_inited)
615
741
if (keycache->disk_blocks > 0)
617
743
if (keycache->block_mem)
619
free(keycache->block_mem);
745
my_large_free((uchar*) keycache->block_mem, MYF(0));
620
746
keycache->block_mem= NULL;
621
free((unsigned char*) keycache->block_root);
747
my_free((uchar*) keycache->block_root, MYF(0));
622
748
keycache->block_root= NULL;
624
750
keycache->disk_blocks= -1;
955
1112
not linked in the LRU ring.
958
static void link_block(KEY_CACHE *keycache, BLOCK_LINK *block, bool hot,
1115
static void link_block(KEY_CACHE *keycache, BLOCK_LINK *block, my_bool hot,
961
1118
BLOCK_LINK *ins;
962
1119
BLOCK_LINK **pins;
964
assert((block->status & ~BLOCK_CHANGED) == (BLOCK_READ | BLOCK_IN_USE));
965
assert(block->hash_link); /*backptr to block NULL from free_block()*/
966
assert(!block->requests);
967
assert(block->prev_changed && *block->prev_changed == block);
968
assert(!block->next_used);
969
assert(!block->prev_used);
1121
DBUG_ASSERT((block->status & ~BLOCK_CHANGED) == (BLOCK_READ | BLOCK_IN_USE));
1122
DBUG_ASSERT(block->hash_link); /*backptr to block NULL from free_block()*/
1123
DBUG_ASSERT(!block->requests);
1124
DBUG_ASSERT(block->prev_changed && *block->prev_changed == block);
1125
DBUG_ASSERT(!block->next_used);
1126
DBUG_ASSERT(!block->prev_used);
970
1128
if (!hot && keycache->waiting_for_block.last_thread)
972
1130
/* Signal that in the LRU warm sub-chain an available block has appeared */
1200
1400
static void remove_reader(BLOCK_LINK *block)
1202
assert(block->status & (BLOCK_READ | BLOCK_IN_USE));
1203
assert(block->hash_link && block->hash_link->block == block);
1204
assert(block->prev_changed && *block->prev_changed == block);
1205
assert(!block->next_used);
1206
assert(!block->prev_used);
1207
assert(block->hash_link->requests);
1402
DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE));
1403
DBUG_ASSERT(block->hash_link && block->hash_link->block == block);
1404
DBUG_ASSERT(block->prev_changed && *block->prev_changed == block);
1405
DBUG_ASSERT(!block->next_used);
1406
DBUG_ASSERT(!block->prev_used);
1407
DBUG_ASSERT(block->hash_link->requests);
1208
1409
if (! --block->hash_link->requests && block->condvar)
1209
1410
keycache_pthread_cond_signal(block->condvar);
1412
--block->hash_link->requests;
1218
1422
static void wait_for_readers(KEY_CACHE *keycache,
1219
1423
BLOCK_LINK *block)
1221
1426
struct st_my_thread_var *thread= my_thread_var;
1222
assert(block->status & (BLOCK_READ | BLOCK_IN_USE));
1223
assert(!(block->status & (BLOCK_ERROR | BLOCK_IN_FLUSH |
1427
DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE));
1428
DBUG_ASSERT(!(block->status & (BLOCK_ERROR | BLOCK_IN_FLUSH |
1224
1429
BLOCK_CHANGED)));
1225
assert(block->hash_link);
1226
assert(block->hash_link->block == block);
1430
DBUG_ASSERT(block->hash_link);
1431
DBUG_ASSERT(block->hash_link->block == block);
1227
1432
/* Linked in file_blocks or changed_blocks hash. */
1228
assert(block->prev_changed && *block->prev_changed == block);
1433
DBUG_ASSERT(block->prev_changed && *block->prev_changed == block);
1229
1434
/* Not linked in LRU ring. */
1230
assert(!block->next_used);
1231
assert(!block->prev_used);
1435
DBUG_ASSERT(!block->next_used);
1436
DBUG_ASSERT(!block->prev_used);
1232
1437
while (block->hash_link->requests)
1439
KEYCACHE_DBUG_PRINT("wait_for_readers: wait",
1440
("suspend thread %ld block %u",
1441
thread->id, BLOCK_NUMBER(block)));
1234
1442
/* There must be no other waiter. We have no queue here. */
1235
assert(!block->condvar);
1443
DBUG_ASSERT(!block->condvar);
1236
1444
block->condvar= &thread->suspend;
1237
1445
keycache_pthread_cond_wait(&thread->suspend, &keycache->cache_lock);
1238
1446
block->condvar= NULL;
1449
KEYCACHE_DBUG_ASSERT(block->hash_link->requests == 0);
1319
1543
hash_link points to the first member of the list
1321
1545
hash_link= *(start= &keycache->hash_root[KEYCACHE_HASH(file, filepos)]);
1546
#if defined(KEYCACHE_DEBUG)
1322
1549
/* Look for an element for the pair (file, filepos) in the bucket chain */
1323
1550
while (hash_link &&
1324
1551
(hash_link->diskpos != filepos || hash_link->file != file))
1326
1553
hash_link= hash_link->next;
1554
#if defined(KEYCACHE_DEBUG)
1556
if (! (cnt <= keycache->hash_links_used))
1559
for (i=0, hash_link= *start ;
1560
i < cnt ; i++, hash_link= hash_link->next)
1562
KEYCACHE_DBUG_PRINT("get_hash_link", ("fd: %u pos: %lu",
1563
(uint) hash_link->file,(ulong) hash_link->diskpos));
1566
KEYCACHE_DBUG_ASSERT(cnt <= keycache->hash_links_used);
1328
1569
if (! hash_link)
1409
1657
int page_status;
1659
DBUG_ENTER("find_key_block");
1660
KEYCACHE_THREAD_TRACE("find_key_block:begin");
1661
DBUG_PRINT("enter", ("fd: %d pos: %lu wrmode: %d",
1662
file, (ulong) filepos, wrmode));
1663
KEYCACHE_DBUG_PRINT("find_key_block", ("fd: %d pos: %lu wrmode: %d",
1664
file, (ulong) filepos,
1666
#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG)
1667
DBUG_EXECUTE("check_keycache2",
1668
test_key_cache(keycache, "start of find_key_block", 0););
1413
1673
If the flush phase of a resize operation fails, the cache is left
1414
1674
unusable. This will be detected only after "goto restart".
1416
1676
if (!keycache->can_be_used)
1420
1680
Find the hash_link for the requested file block (file, filepos). We
1775
2047
/* There are some never used blocks, take first of them */
1776
assert(keycache->blocks_used <
1777
(uint32_t) keycache->disk_blocks);
2048
DBUG_ASSERT(keycache->blocks_used <
2049
(ulong) keycache->disk_blocks);
1778
2050
block= &keycache->block_root[keycache->blocks_used];
1779
2051
block->buffer= ADD_TO_PTR(keycache->block_mem,
1780
((uint32_t) keycache->blocks_used*
2052
((ulong) keycache->blocks_used*
1781
2053
keycache->key_cache_block_size),
1783
2055
keycache->blocks_used++;
1784
assert(!block->next_used);
2056
DBUG_ASSERT(!block->next_used);
1786
assert(!block->prev_used);
1787
assert(!block->next_changed);
1788
assert(!block->prev_changed);
1789
assert(!block->hash_link);
1790
assert(!block->status);
1791
assert(!block->requests);
2058
DBUG_ASSERT(!block->prev_used);
2059
DBUG_ASSERT(!block->next_changed);
2060
DBUG_ASSERT(!block->prev_changed);
2061
DBUG_ASSERT(!block->hash_link);
2062
DBUG_ASSERT(!block->status);
2063
DBUG_ASSERT(!block->requests);
1792
2064
keycache->blocks_unused--;
1793
2065
block->status= BLOCK_IN_USE;
1794
2066
block->length= 0;
1828
2104
link_into_queue(&keycache->waiting_for_block, thread);
2107
KEYCACHE_DBUG_PRINT("find_key_block: wait",
2108
("suspend thread %ld", thread->id));
1831
2109
keycache_pthread_cond_wait(&thread->suspend,
1832
2110
&keycache->cache_lock);
1834
2112
while (thread->next);
1835
2113
thread->opt_info= NULL;
1836
2114
/* Assert that block has a request registered. */
1837
assert(hash_link->block->requests);
2115
DBUG_ASSERT(hash_link->block->requests);
1838
2116
/* Assert that block is not in LRU ring. */
1839
assert(!hash_link->block->next_used);
1840
assert(!hash_link->block->prev_used);
2117
DBUG_ASSERT(!hash_link->block->next_used);
2118
DBUG_ASSERT(!hash_link->block->prev_used);
2121
KEYCACHE_DBUG_ASSERT(keycache->used_last);
1843
2124
If we waited above, hash_link->block has been assigned by
1844
2125
link_block(). Otherwise it is still NULL. In the latter case
1911
2196
BLOCK_IN_EVICTION may be true or not. Other flags must
1912
2197
have a fixed value.
1914
assert((block->status & ~BLOCK_IN_EVICTION) ==
2199
DBUG_ASSERT((block->status & ~BLOCK_IN_EVICTION) ==
1915
2200
(BLOCK_READ | BLOCK_IN_SWITCH |
1916
2201
BLOCK_IN_FLUSH | BLOCK_IN_FLUSHWRITE |
1917
2202
BLOCK_CHANGED | BLOCK_IN_USE));
1918
assert(block->hash_link);
2203
DBUG_ASSERT(block->hash_link);
1920
2205
keycache_pthread_mutex_unlock(&keycache->cache_lock);
1922
2207
The call is thread safe because only the current
1923
2208
thread might change the block->hash_link value
1925
error= (pwrite(block->hash_link->file,
1926
block->buffer+block->offset,
1927
block->length - block->offset,
1928
block->hash_link->diskpos+ block->offset) == 0);
2210
error= my_pwrite(block->hash_link->file,
2211
block->buffer+block->offset,
2212
block->length - block->offset,
2213
block->hash_link->diskpos+ block->offset,
2214
MYF(MY_NABP | MY_WAIT_IF_FULL));
1929
2215
keycache_pthread_mutex_lock(&keycache->cache_lock);
1931
2217
/* Block status must not have changed. */
1932
assert((block->status & ~BLOCK_IN_EVICTION) ==
2218
DBUG_ASSERT((block->status & ~BLOCK_IN_EVICTION) ==
1933
2219
(BLOCK_READ | BLOCK_IN_SWITCH |
1934
2220
BLOCK_IN_FLUSH | BLOCK_IN_FLUSHWRITE |
1935
BLOCK_CHANGED | BLOCK_IN_USE));
2221
BLOCK_CHANGED | BLOCK_IN_USE) || fail_block(block));
1936
2222
keycache->global_cache_write++;
2043
2333
Register a request on the block. This is another protection
2044
2334
against eviction.
2046
assert(((block->hash_link != hash_link) &&
2336
DBUG_ASSERT(((block->hash_link != hash_link) &&
2047
2337
(block->status & (BLOCK_IN_EVICTION | BLOCK_IN_SWITCH))) ||
2048
2338
((block->hash_link == hash_link) &&
2049
2339
!(block->status & BLOCK_READ)) ||
2050
2340
((block->status & BLOCK_READ) &&
2051
2341
!(block->status & (BLOCK_IN_EVICTION | BLOCK_IN_SWITCH))));
2052
2342
reg_requests(keycache, block, 1);
2343
KEYCACHE_DBUG_PRINT("find_key_block",
2344
("block->hash_link: %p hash_link: %p "
2345
"block->status: %u", block->hash_link,
2346
hash_link, block->status ));
2053
2347
page_status= (((block->hash_link == hash_link) &&
2054
2348
(block->status & BLOCK_READ)) ?
2055
2349
PAGE_READ : PAGE_WAIT_TO_BE_READ);
2059
assert(page_status != -1);
2353
KEYCACHE_DBUG_ASSERT(page_status != -1);
2060
2354
/* Same assert basically, but be very sure. */
2355
KEYCACHE_DBUG_ASSERT(block);
2062
2356
/* Assert that block has a request and is not in LRU ring. */
2063
assert(block->requests);
2064
assert(!block->next_used);
2065
assert(!block->prev_used);
2357
DBUG_ASSERT(block->requests);
2358
DBUG_ASSERT(!block->next_used);
2359
DBUG_ASSERT(!block->prev_used);
2066
2360
/* Assert that we return the correct block. */
2067
assert((page_status == PAGE_WAIT_TO_BE_READ) ||
2361
DBUG_ASSERT((page_status == PAGE_WAIT_TO_BE_READ) ||
2068
2362
((block->hash_link->file == file) &&
2069
2363
(block->hash_link->diskpos == filepos)));
2070
2364
*page_st=page_status;
2365
KEYCACHE_DBUG_PRINT("find_key_block",
2366
("fd: %d pos: %lu block->status: %u page_status: %d",
2367
file, (ulong) filepos, block->status,
2370
#if !defined(DBUG_OFF) && defined(EXTRA_DEBUG)
2371
DBUG_EXECUTE("check_keycache2",
2372
test_key_cache(keycache, "end of find_key_block",0););
2374
KEYCACHE_THREAD_TRACE("find_key_block:end");
2113
2417
request for the block become secondary requests. For a primary
2114
2418
request the block must be properly initialized.
2116
assert(((block->status & ~BLOCK_FOR_UPDATE) == BLOCK_IN_USE));
2117
assert((block->length == 0));
2118
assert((block->offset == keycache->key_cache_block_size));
2119
assert((block->requests > 0));
2420
DBUG_ASSERT(((block->status & ~BLOCK_FOR_UPDATE) == BLOCK_IN_USE) ||
2422
DBUG_ASSERT((block->length == 0) || fail_block(block));
2423
DBUG_ASSERT((block->offset == keycache->key_cache_block_size) ||
2425
DBUG_ASSERT((block->requests > 0) || fail_block(block));
2427
KEYCACHE_DBUG_PRINT("read_block",
2428
("page to be read by primary request"));
2121
2430
keycache->global_cache_read++;
2122
2431
/* Page is not in buffer yet, is to be read from disk */
2125
2434
Here other threads may step in and register as secondary readers.
2126
2435
They will register in block->wqueue[COND_FOR_REQUESTED].
2128
got_length= pread(block->hash_link->file, block->buffer, read_length, block->hash_link->diskpos);
2437
got_length= my_pread(block->hash_link->file, block->buffer,
2438
read_length, block->hash_link->diskpos, MYF(0));
2129
2439
keycache_pthread_mutex_lock(&keycache->cache_lock);
2131
2441
The block can now have been marked for free (in case of
2132
2442
FLUSH_RELEASE). Otherwise the state must be unchanged.
2134
assert(((block->status & ~(BLOCK_REASSIGNED |
2135
BLOCK_FOR_UPDATE)) == BLOCK_IN_USE));
2136
assert((block->length == 0));
2137
assert((block->offset == keycache->key_cache_block_size));
2138
assert((block->requests > 0));
2444
DBUG_ASSERT(((block->status & ~(BLOCK_REASSIGNED |
2445
BLOCK_FOR_UPDATE)) == BLOCK_IN_USE) ||
2447
DBUG_ASSERT((block->length == 0) || fail_block(block));
2448
DBUG_ASSERT((block->offset == keycache->key_cache_block_size) ||
2450
DBUG_ASSERT((block->requests > 0) || fail_block(block));
2140
2452
if (got_length < min_length)
2141
2453
block->status|= BLOCK_ERROR;
2191
2509
The function ensures that a block of data of size length from file
2192
2510
positioned at filepos is in the buffers for some key cache blocks.
2193
2511
Then the function either copies the data into the buffer buff, or,
2194
if return_buffer is true, it just returns the pointer to the key cache
2512
if return_buffer is TRUE, it just returns the pointer to the key cache
2195
2513
buffer with the data.
2196
2514
Filepos must be a multiple of 'block_length', but it doesn't
2197
2515
have to be a multiple of key_cache_block_size;
2200
unsigned char *key_cache_read(KEY_CACHE *keycache,
2518
uchar *key_cache_read(KEY_CACHE *keycache,
2201
2519
File file, my_off_t filepos, int level,
2202
unsigned char *buff, uint32_t length,
2203
uint32_t block_length __attribute__((unused)),
2520
uchar *buff, uint length,
2521
uint block_length __attribute__((unused)),
2204
2522
int return_buffer __attribute__((unused)))
2206
bool locked_and_incremented= false;
2524
my_bool locked_and_incremented= FALSE;
2208
unsigned char *start= buff;
2527
DBUG_ENTER("key_cache_read");
2528
DBUG_PRINT("enter", ("fd: %u pos: %lu length: %u",
2529
(uint) file, (ulong) filepos, length));
2210
2531
if (keycache->key_cache_inited)
2212
2533
/* Key cache is used */
2213
2534
register BLOCK_LINK *block;
2214
uint32_t read_length;
2281
2608
/* The requested page is to be read into the block buffer */
2282
2609
read_block(keycache, block,
2283
2610
keycache->key_cache_block_size, read_length+offset,
2284
(bool)(page_st == PAGE_TO_BE_READ));
2611
(my_bool)(page_st == PAGE_TO_BE_READ));
2286
2613
A secondary request must now have the block assigned to the
2287
2614
requested file block. It does not hurt to check it for
2288
2615
primary requests too.
2290
assert(keycache->can_be_used);
2291
assert(block->hash_link->file == file);
2292
assert(block->hash_link->diskpos == filepos);
2293
assert(block->status & (BLOCK_READ | BLOCK_IN_USE));
2617
DBUG_ASSERT(keycache->can_be_used);
2618
DBUG_ASSERT(block->hash_link->file == file);
2619
DBUG_ASSERT(block->hash_link->diskpos == filepos);
2620
DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE));
2295
2622
else if (block->length < read_length + offset)
2307
2634
/* block status may have added BLOCK_ERROR in the above 'if'. */
2308
2635
if (!((status= block->status) & BLOCK_ERROR))
2638
if (! return_buffer)
2311
assert(block->status & (BLOCK_READ | BLOCK_IN_USE));
2641
DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE));
2312
2642
#if !defined(SERIALIZED_READ_FROM_CACHE)
2313
2643
keycache_pthread_mutex_unlock(&keycache->cache_lock);
2316
2646
/* Copy data from the cache buffer */
2317
memcpy(buff, block->buffer+offset, (size_t) read_length);
2647
if (!(read_length & 511))
2648
bmove512(buff, block->buffer+offset, read_length);
2650
memcpy(buff, block->buffer+offset, (size_t) read_length);
2319
2652
#if !defined(SERIALIZED_READ_FROM_CACHE)
2320
2653
keycache_pthread_mutex_lock(&keycache->cache_lock);
2321
assert(block->status & (BLOCK_READ | BLOCK_IN_USE));
2654
DBUG_ASSERT(block->status & (BLOCK_READ | BLOCK_IN_USE));
2392
2730
int key_cache_insert(KEY_CACHE *keycache,
2393
2731
File file, my_off_t filepos, int level,
2394
unsigned char *buff, uint32_t length)
2732
uchar *buff, uint length)
2735
DBUG_ENTER("key_cache_insert");
2736
DBUG_PRINT("enter", ("fd: %u pos: %lu length: %u",
2737
(uint) file,(ulong) filepos, length));
2398
2739
if (keycache->key_cache_inited)
2400
2741
/* Key cache is used */
2401
2742
register BLOCK_LINK *block;
2402
uint32_t read_length;
2405
bool locked_and_incremented= false;
2746
my_bool locked_and_incremented= FALSE;
2408
2749
When the keycache is once initialized, we use the cache_lock to
2616
2962
The function copies the data of size length from buff into buffers
2617
2963
for key cache blocks that are assigned to contain the portion of
2618
2964
the file starting with position filepos.
2619
It ensures that this data is flushed to the file if dont_write is false.
2965
It ensures that this data is flushed to the file if dont_write is FALSE.
2620
2966
Filepos must be a multiple of 'block_length', but it doesn't
2621
2967
have to be a multiple of key_cache_block_size;
2623
dont_write is always true in the server (info->lock_type is never F_UNLCK).
2969
dont_write is always TRUE in the server (info->lock_type is never F_UNLCK).
2626
2972
int key_cache_write(KEY_CACHE *keycache,
2627
2973
File file, my_off_t filepos, int level,
2628
unsigned char *buff, uint32_t length,
2629
uint32_t block_length __attribute__((unused)),
2974
uchar *buff, uint length,
2975
uint block_length __attribute__((unused)),
2630
2976
int dont_write)
2632
bool locked_and_incremented= false;
2978
my_bool locked_and_incremented= FALSE;
2980
DBUG_ENTER("key_cache_write");
2982
("fd: %u pos: %lu length: %u block_length: %u"
2983
" key_block_length: %u",
2984
(uint) file, (ulong) filepos, length, block_length,
2985
keycache ? keycache->key_cache_block_size : 0));
2635
2987
if (!dont_write)
2925
3295
is registered in the hash_link and free_block() will wait for it
2928
assert((block->status & BLOCK_IN_USE) &&
3298
DBUG_ASSERT((block->status & BLOCK_IN_USE) &&
2929
3299
!(block->status & (BLOCK_IN_EVICTION | BLOCK_IN_SWITCH |
2930
3300
BLOCK_REASSIGNED | BLOCK_IN_FLUSH |
2931
3301
BLOCK_CHANGED | BLOCK_FOR_UPDATE)));
2932
3302
/* Assert that the block is in a file_blocks chain. */
2933
assert(block->prev_changed && *block->prev_changed == block);
3303
DBUG_ASSERT(block->prev_changed && *block->prev_changed == block);
2934
3304
/* Assert that the block is not in the LRU ring. */
2935
assert(!block->next_used && !block->prev_used);
3305
DBUG_ASSERT(!block->next_used && !block->prev_used);
2937
3307
IMHO the below condition (if()) makes no sense. I can't see how it
2938
3308
could be possible that free_block() is entered with a NULL hash_link
2939
3309
pointer. The only place where it can become NULL is in free_block()
2940
3310
(or before its first use ever, but for those blocks free_block() is
2941
3311
not called). I don't remove the conditional as it cannot harm, but
2942
place an assert to confirm my hypothesis. Eventually the
3312
place an DBUG_ASSERT to confirm my hypothesis. Eventually the
2943
3313
condition (if()) can be removed.
2945
assert(block->hash_link && block->hash_link->block == block);
3315
DBUG_ASSERT(block->hash_link && block->hash_link->block == block);
2946
3316
if (block->hash_link)
3079
3454
if (!(block->status & BLOCK_FOR_UPDATE))
3081
3456
/* Blocks coming here must have a certain status. */
3082
assert(block->hash_link);
3083
assert(block->hash_link->block == block);
3084
assert(block->hash_link->file == file);
3085
assert((block->status & ~BLOCK_IN_EVICTION) ==
3457
DBUG_ASSERT(block->hash_link);
3458
DBUG_ASSERT(block->hash_link->block == block);
3459
DBUG_ASSERT(block->hash_link->file == file);
3460
DBUG_ASSERT((block->status & ~BLOCK_IN_EVICTION) ==
3086
3461
(BLOCK_READ | BLOCK_IN_FLUSH | BLOCK_CHANGED | BLOCK_IN_USE));
3087
3462
block->status|= BLOCK_IN_FLUSHWRITE;
3088
3463
keycache_pthread_mutex_unlock(&keycache->cache_lock);
3089
error= (pwrite(file,
3090
block->buffer+block->offset,
3091
block->length - block->offset,
3092
block->hash_link->diskpos+ block->offset) == 0);
3464
error= my_pwrite(file,
3465
block->buffer+block->offset,
3466
block->length - block->offset,
3467
block->hash_link->diskpos+ block->offset,
3468
MYF(MY_NABP | MY_WAIT_IF_FULL));
3093
3469
keycache_pthread_mutex_lock(&keycache->cache_lock);
3094
3470
keycache->global_cache_write++;
3758
4173
int reset_key_cache_counters(const char *name __attribute__((unused)),
3759
4174
KEY_CACHE *key_cache)
4176
DBUG_ENTER("reset_key_cache_counters");
3761
4177
if (!key_cache->key_cache_inited)
4179
DBUG_PRINT("info", ("Key cache %s not initialized.", name));
4182
DBUG_PRINT("info", ("Resetting counters for key cache %s.", name));
3765
4184
key_cache->global_blocks_changed= 0; /* Key_blocks_not_flushed */
3766
4185
key_cache->global_cache_r_requests= 0; /* Key_read_requests */
3767
4186
key_cache->global_cache_read= 0; /* Key_reads */
3768
4187
key_cache->global_cache_w_requests= 0; /* Key_write_requests */
3769
4188
key_cache->global_cache_write= 0; /* Key_writes */
4195
Test if disk-cache is ok
4197
static void test_key_cache(KEY_CACHE *keycache __attribute__((unused)),
4198
const char *where __attribute__((unused)),
4199
my_bool lock __attribute__((unused)))
3773
4205
#if defined(KEYCACHE_TIMEOUT)
3884
4324
1 nanosecond = 1000 micro seconds
3886
4326
timeout.tv_nsec= now.tv_usec * 1000;
4327
KEYCACHE_THREAD_TRACE_END("started waiting");
4328
#if defined(KEYCACHE_DEBUG)
4331
fprintf(keycache_debug_log, "waiting...\n");
4332
fflush(keycache_debug_log);
3887
4334
rc= pthread_cond_timedwait(cond, mutex, &timeout);
4335
KEYCACHE_THREAD_TRACE_BEGIN("finished waiting");
3888
4336
if (rc == ETIMEDOUT || rc == ETIME)
4338
#if defined(KEYCACHE_DEBUG)
4339
fprintf(keycache_debug_log,"aborted by keycache timeout\n");
4340
fclose(keycache_debug_log);
3890
4343
keycache_dump();
4346
#if defined(KEYCACHE_DEBUG)
4347
KEYCACHE_DBUG_ASSERT(rc != ETIMEDOUT);
3893
4349
assert(rc != ETIMEDOUT);
4354
#if defined(KEYCACHE_DEBUG)
4355
static int keycache_pthread_cond_wait(pthread_cond_t *cond,
4356
pthread_mutex_t *mutex)
4359
KEYCACHE_THREAD_TRACE_END("started waiting");
4360
rc= pthread_cond_wait(cond, mutex);
4361
KEYCACHE_THREAD_TRACE_BEGIN("finished waiting");
3896
4365
#endif /* defined(KEYCACHE_TIMEOUT) */
4367
#if defined(KEYCACHE_DEBUG)
4370
static int keycache_pthread_mutex_lock(pthread_mutex_t *mutex)
4373
rc= pthread_mutex_lock(mutex);
4374
KEYCACHE_THREAD_TRACE_BEGIN("");
4379
static void keycache_pthread_mutex_unlock(pthread_mutex_t *mutex)
4381
KEYCACHE_THREAD_TRACE_END("");
4382
pthread_mutex_unlock(mutex);
4386
static int keycache_pthread_cond_signal(pthread_cond_t *cond)
4389
KEYCACHE_THREAD_TRACE("signal");
4390
rc= pthread_cond_signal(cond);
4395
#if defined(KEYCACHE_DEBUG_LOG)
4398
static void keycache_debug_print(const char * fmt,...)
4402
if (keycache_debug_log)
4404
VOID(vfprintf(keycache_debug_log, fmt, args));
4405
VOID(fputc('\n',keycache_debug_log));
4409
#endif /* defined(KEYCACHE_DEBUG_LOG) */
4411
#if defined(KEYCACHE_DEBUG_LOG)
4414
void keycache_debug_log_close(void)
4416
if (keycache_debug_log)
4417
fclose(keycache_debug_log);
4419
#endif /* defined(KEYCACHE_DEBUG_LOG) */
4421
#endif /* defined(KEYCACHE_DEBUG) */
4423
#if !defined(DBUG_OFF)
4424
#define F_B_PRT(_f_, _v_) DBUG_PRINT("assert_fail", (_f_, _v_))
4426
static int fail_block(BLOCK_LINK *block)
4428
F_B_PRT("block->next_used: %lx\n", (ulong) block->next_used);
4429
F_B_PRT("block->prev_used: %lx\n", (ulong) block->prev_used);
4430
F_B_PRT("block->next_changed: %lx\n", (ulong) block->next_changed);
4431
F_B_PRT("block->prev_changed: %lx\n", (ulong) block->prev_changed);
4432
F_B_PRT("block->hash_link: %lx\n", (ulong) block->hash_link);
4433
F_B_PRT("block->status: %u\n", block->status);
4434
F_B_PRT("block->length: %u\n", block->length);
4435
F_B_PRT("block->offset: %u\n", block->offset);
4436
F_B_PRT("block->requests: %u\n", block->requests);
4437
F_B_PRT("block->temperature: %u\n", block->temperature);
4438
return 0; /* Let the assert fail. */
4441
static int fail_hlink(HASH_LINK *hlink)
4443
F_B_PRT("hlink->next: %lx\n", (ulong) hlink->next);
4444
F_B_PRT("hlink->prev: %lx\n", (ulong) hlink->prev);
4445
F_B_PRT("hlink->block: %lx\n", (ulong) hlink->block);
4446
F_B_PRT("hlink->diskpos: %lu\n", (ulong) hlink->diskpos);
4447
F_B_PRT("hlink->file: %d\n", hlink->file);
4448
return 0; /* Let the assert fail. */
4451
static int cache_empty(KEY_CACHE *keycache)
4455
if (keycache->disk_blocks <= 0)
4457
for (idx= 0; idx < keycache->disk_blocks; idx++)
4459
BLOCK_LINK *block= keycache->block_root + idx;
4460
if (block->status || block->requests || block->hash_link)
4462
fprintf(stderr, "block index: %u\n", idx);
4467
for (idx= 0; idx < keycache->hash_links; idx++)
4469
HASH_LINK *hash_link= keycache->hash_link_root + idx;
4470
if (hash_link->requests || hash_link->block)
4472
fprintf(stderr, "hash_link index: %u\n", idx);
4473
fail_hlink(hash_link);
4479
fprintf(stderr, "blocks: %d used: %lu\n",
4480
keycache->disk_blocks, keycache->blocks_used);
4481
fprintf(stderr, "hash_links: %d used: %d\n",
4482
keycache->hash_links, keycache->hash_links_used);
4483
fprintf(stderr, "\n");