~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to plugin/pbms/src/trans_cache_ms.cc

  • Committer: Mark Atwood
  • Date: 2011-12-20 02:32:53 UTC
  • mfrom: (2469.1.1 drizzle-build)
  • Revision ID: me@mark.atwood.name-20111220023253-bvu0kr14kwsdvz7g
mergeĀ lp:~brianaker/drizzle/deprecate-pbms

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* Copyright (C) 2009 PrimeBase Technologies GmbH, Germany
2
 
 *
3
 
 * PrimeBase Media Stream for MySQL
4
 
 *
5
 
 * This program is free software; you can redistribute it and/or modify
6
 
 * it under the terms of the GNU General Public License as published by
7
 
 * the Free Software Foundation; either version 2 of the License, or
8
 
 * (at your option) any later version.
9
 
 *
10
 
 * This program is distributed in the hope that it will be useful,
11
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 
 * GNU General Public License for more details.
14
 
 *
15
 
 * You should have received a copy of the GNU General Public License
16
 
 * along with this program; if not, write to the Free Software
17
 
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
 
 *
19
 
 * Barry Leslie
20
 
 *
21
 
 * 2009-06-10
22
 
 *
23
 
 * H&G2JCtL
24
 
 *
25
 
 * PBMS transaction cache.
26
 
 *
27
 
 */
28
 
 
29
 
#include "cslib/CSConfig.h"
30
 
#include <inttypes.h>
31
 
 
32
 
#include "cslib/CSGlobal.h"
33
 
 
34
 
#include "trans_cache_ms.h"
35
 
 
36
 
#define LIST_INC_SIZE 256       // If the list starts to grow it is probably because a backup is in progress so it could get quite large.
37
 
#define MIN_LIST_SIZE 32        // A list size that should be able to handle a normal transaction load.
38
 
#define MIN_CACHE_RECORDS 2     
39
 
 
40
 
typedef struct myTrans {
41
 
        uint8_t tc_type;                // The transaction type. If the first bit is set then the transaction is an autocommit.
42
 
        uint32_t        tc_db_id;               // The database ID for the operation.
43
 
        uint32_t        tc_tab_id;              // The table ID for the operation.
44
 
        bool    tc_rolled_back; // 'true' if this action has been rolled back.
45
 
        uint64_t        tc_blob_id;             // The blob ID for the operation.
46
 
        uint64_t        tc_blob_ref_id; // The blob reference id.
47
 
        uint64_t        tc_position;    // The log position of the record.
48
 
} myTransRec, *myTransPtr;
49
 
 
50
 
#define BAD_LOG_POSITION ((uint64_t) -1)
51
 
typedef struct TransList {
52
 
#ifdef DEBUG
53
 
        uint32_t                old_tid;
54
 
#endif
55
 
        uint32_t                tid;
56
 
        uint64_t                log_position;   // The transaction log position of the start of the transaction.
57
 
        MS_TxnState     terminated;             // 
58
 
        size_t          size;                   // The allocated size of the list.
59
 
        size_t          len;                    // The number of records in the list that are being used.
60
 
        myTransPtr      list;
61
 
} TransListRec, *TransListPtr;
62
 
 
63
 
MSTransCache::MSTransCache(): CSSharedRefObject(),
64
 
        tc_List(NULL),
65
 
        tc_OverFlow(NULL),
66
 
        tc_Size(0),
67
 
        tc_EOL(0),
68
 
        tc_First(0),
69
 
        tc_Used(0),
70
 
        tc_TotalTransCount(0),
71
 
        tc_TotalCacheCount(0),
72
 
        tc_ReLoadingThread(NULL),
73
 
        tc_OverFlowTID(0),
74
 
        tc_Full(false),
75
 
        tc_CacheVersion(0),
76
 
        tc_Recovering(false)
77
 
        {}
78
 
 
79
 
MSTransCache::~MSTransCache() 
80
 
{
81
 
        if (tc_List) {
82
 
                for (uint32_t i = 0; i < tc_Size; i++) {
83
 
                        if (tc_List[i].list)
84
 
                                cs_free(tc_List[i].list);
85
 
                }
86
 
                cs_free(tc_List);
87
 
        }
88
 
}
89
 
 
90
 
MSTransCache *MSTransCache::newMSTransCache(uint32_t min_size)
91
 
{
92
 
        MSTransCache *tl = NULL;
93
 
        enter_();
94
 
        
95
 
        new_(tl, MSTransCache());
96
 
        push_(tl);
97
 
        
98
 
        if (MIN_LIST_SIZE > min_size)
99
 
                min_size = MIN_LIST_SIZE;
100
 
                
101
 
        tl->tc_Initialize(min_size);
102
 
                
103
 
        pop_(tl);
104
 
        
105
 
        return_(tl);
106
 
}
107
 
        
108
 
 
109
 
void MSTransCache::tc_Initialize(uint32_t size)
110
 
{
111
 
        enter_();
112
 
                
113
 
        tc_Size = size;
114
 
        size++; // Add an extra for the overflow
115
 
        tc_List = (TransListPtr) cs_malloc(size * sizeof(TransListRec));
116
 
        
117
 
        // Give each new transaction list record a short list of transaction records
118
 
        for (uint32_t i = 0; i < tc_Size; i++) {
119
 
                tc_List[i].list = (myTransPtr) cs_malloc(MIN_CACHE_RECORDS * sizeof(myTransRec));
120
 
                tc_List[i].size = MIN_CACHE_RECORDS;
121
 
                tc_List[i].len = 0;
122
 
                tc_List[i].tid = 0;
123
 
                tc_List[i].log_position = 0;
124
 
                tc_List[i].terminated = MS_Running;
125
 
        }
126
 
 
127
 
        tc_OverFlow = tc_List + tc_Size;
128
 
        
129
 
        tc_OverFlow->list = NULL;
130
 
        tc_OverFlow->size = 0;
131
 
        tc_OverFlow->len = 0;
132
 
        tc_OverFlow->tid = 0;
133
 
        tc_OverFlow->log_position = 0;
134
 
        tc_OverFlow->terminated = MS_Running;
135
 
        exit_();
136
 
}
137
 
 
138
 
//--------------------
139
 
void MSTransCache::tc_SetSize(uint32_t cache_size)
140
 
{
141
 
        enter_();
142
 
 
143
 
        lock_(this);
144
 
        
145
 
        if (cache_size < MIN_LIST_SIZE)
146
 
                cache_size = MIN_LIST_SIZE;
147
 
        
148
 
        // If the cache is being reduced then free the record 
149
 
        // lists if the transactions about to be removed.
150
 
        for (uint32_t i = cache_size +1; i < tc_Size; i++) {
151
 
                if (tc_List[i].list)
152
 
                        cs_free(tc_List[i].list);
153
 
        }
154
 
 
155
 
        // Add one to cache_size for overflow.  
156
 
        cs_realloc((void **) &tc_List, (cache_size +1) * sizeof(TransListRec));
157
 
        
158
 
        if (cache_size > tc_Size) {
159
 
                // Move the overflow record.
160
 
                memcpy(tc_List + cache_size, tc_List + tc_Size, sizeof(TransListRec));
161
 
                
162
 
                for (uint32_t i = tc_Size; i < cache_size; i++) {
163
 
                        tc_List[i].list = (myTransPtr) cs_malloc(MIN_CACHE_RECORDS * sizeof(myTransRec));
164
 
                        tc_List[i].size = MIN_CACHE_RECORDS;
165
 
                        tc_List[i].len = 0;
166
 
                        tc_List[i].tid = 0;
167
 
                        tc_List[i].log_position = 0;
168
 
                        tc_List[i].terminated = MS_Running;
169
 
                }
170
 
                
171
 
        }
172
 
        
173
 
        
174
 
        tc_Size = cache_size;
175
 
        tc_OverFlow = tc_List + tc_Size;
176
 
        
177
 
        unlock_(this);
178
 
 
179
 
        exit_();
180
 
}
181
 
 
182
 
bool MSTransCache::tc_ShoulReloadCache()
183
 
{
184
 
        return (((tc_Used +1) < tc_Size) && tc_Full);
185
 
}
186
 
 
187
 
uint64_t MSTransCache::tc_StartCacheReload(bool startup)
188
 
{
189
 
        enter_();
190
 
        
191
 
        (void) startup;
192
 
        
193
 
        ASSERT((startup) || tc_Full);
194
 
        tc_ReLoadingThread = self;
195
 
        tc_OverFlowTID = tc_OverFlow->tid;
196
 
        
197
 
        self->myTID = 0;
198
 
        self->myTransRef = 0;
199
 
#ifdef DEBUG
200
 
                tc_ReloadCnt =0;
201
 
#endif          
202
 
 
203
 
        return_(tc_OverFlow->log_position);
204
 
}
205
 
 
206
 
bool MSTransCache::tc_ContinueCacheReload()
207
 
{
208
 
        // Reload should continue until the list is full again and the termination records 
209
 
        // for the first and overflow transactions have been found.
210
 
        //
211
 
        // It is assumed the reload will also stop if there are no more records to
212
 
        // be read in from the log.
213
 
        
214
 
        return ((tc_List[tc_First].terminated == MS_Running) || // Keep searching for the terminator for the first txn.
215
 
                        (tc_OverFlow->tid == tc_OverFlowTID) || // The old overflow txn has not yet been loaded.
216
 
                        (tc_OverFlow->terminated == MS_Running) // If the overflow tnx is terminated then the cache is also full. 
217
 
                        );
218
 
}
219
 
 
220
 
 
221
 
void MSTransCache::tc_CompleteCacheReload()
222
 
{
223
 
        enter_();
224
 
        
225
 
        tc_ReLoadingThread = NULL;
226
 
        if (tc_OverFlowTID) { // Clear the overflow condition;
227
 
                tc_OverFlow->tid = 0;
228
 
                tc_OverFlowTID = 0;
229
 
                tc_Full = false;
230
 
        }
231
 
        
232
 
        exit_();
233
 
}
234
 
 
235
 
#define OVERFLOW_TREF (tc_Size)
236
 
#define MAX_TREF (OVERFLOW_TREF +1)
237
 
 
238
 
// Create a new transaction record for the specified 
239
 
// transaction.
240
 
TRef MSTransCache::tc_NewTransaction(uint32_t tid)
241
 
{
242
 
        TRef ref;
243
 
        enter_();
244
 
        
245
 
        ASSERT(tid);
246
 
        
247
 
        if (self != tc_ReLoadingThread) {
248
 
                tc_TotalTransCount++;                   
249
 
        }
250
 
                
251
 
        // Once we have entered an overflow state we remain in it until
252
 
        // the cache has been reloaded even if there is now space in the cache.
253
 
        // This is to ensure that the transactions are loaded into the cache
254
 
        // in the correct order.
255
 
        // While reloading, make sure that any attempt to add a transaction by any thread
256
 
        // other than tc_ReLoadingThread recieves an overflow condition. 
257
 
        
258
 
        if (tc_Full) {
259
 
                if (tc_ReLoadingThread != self) {
260
 
                        ref = MAX_TREF;
261
 
                        goto done;
262
 
                }
263
 
 
264
 
#ifdef DEBUG
265
 
                if (!tc_ReloadCnt) {
266
 
                        ASSERT(tc_OverFlow->tid == tid); // The first txn reloaded should be the overflow txn
267
 
                }
268
 
                tc_ReloadCnt++;
269
 
#endif  
270
 
                if (tid == tc_OverFlowTID) {
271
 
#ifdef DEBUG
272
 
                        tc_OverFlow->old_tid = tid;
273
 
#endif  
274
 
                        tc_OverFlow->tid = 0;
275
 
                        tc_OverFlow->terminated = MS_Running;
276
 
                        ASSERT((tc_Used +1) < tc_Size); // There should be room in the list for the old everflow txn.
277
 
                } else if (tc_OverFlowTID == 0) {
278
 
                        // We are seaching for the end of the overflow txn
279
 
                        // and found the start of another txn.
280
 
                        ref = MAX_TREF;
281
 
                        goto done;                      
282
 
                }
283
 
        }
284
 
 
285
 
        if ((tc_Used +1) == tc_Size){ 
286
 
                // The cache is full.
287
 
                tc_OverFlowTID = 0;
288
 
                tc_OverFlow->tid = tid; // save the tid of the first transaction to overflow.
289
 
                tc_OverFlow->log_position = BAD_LOG_POSITION;
290
 
                tc_OverFlow->len = 0; 
291
 
                tc_OverFlow->terminated = MS_Running; 
292
 
                ref = OVERFLOW_TREF;
293
 
                tc_Full = true;
294
 
#ifdef DEBUG
295
 
                tc_ReloadCnt++;
296
 
#endif  
297
 
                                
298
 
                goto done;
299
 
        }
300
 
        
301
 
        if (self != tc_ReLoadingThread) {
302
 
                tc_TotalCacheCount++;                   
303
 
        }
304
 
 
305
 
        ref = tc_EOL;
306
 
        
307
 
#ifdef CHECK_TIDS
308
 
{
309
 
static uint32_t last_tid = 0;
310
 
static bool last_state = false;
311
 
if (tc_Recovering != last_state)
312
 
        last_tid = 0;
313
 
        
314
 
last_state = tc_Recovering;
315
 
if (!( ((last_tid + 1) == tid) || !last_tid))
316
 
        printf("Expected tid %"PRIu32"\n", last_tid + 1);
317
 
ASSERT( ((last_tid + 1) == tid) || !last_tid);
318
 
last_tid = tid;
319
 
}
320
 
#endif
321
 
                
322
 
        tc_List[ref].tid = tid;
323
 
        tc_List[ref].len = 0;
324
 
        tc_List[ref].log_position = BAD_LOG_POSITION;
325
 
        tc_List[ref].terminated = MS_Running;
326
 
 
327
 
        // Update these after initializing the structure because
328
 
        // the reader thread may read it as soon as tc_EOL is updated.
329
 
        tc_Used++;
330
 
        tc_EOL++;
331
 
 
332
 
        if (tc_EOL == tc_Size)
333
 
                tc_EOL = 0;
334
 
 
335
 
done:   
336
 
        self->myTID = tid;
337
 
        self->myTransRef = ref;
338
 
        self->myCacheVersion = tc_CacheVersion;
339
 
        return_(ref);
340
 
}
341
 
 
342
 
void MSTransCache::tc_FindTXNRef(uint32_t tid, TRef *tref)
343
 
{
344
 
        uint32_t i = tc_First;
345
 
        enter_();
346
 
        
347
 
        // Search for the record
348
 
        if (tc_First > tc_EOL) {
349
 
                for (; i < OVERFLOW_TREF && *tref >= MAX_TREF; i++) {
350
 
                        if (tc_List[i].tid == tid)
351
 
                                *tref = i;
352
 
                }
353
 
                i = 0;
354
 
        }
355
 
        
356
 
        for (; i < tc_EOL && *tref >= MAX_TREF; i++) {
357
 
                if (tc_List[i].tid == tid)
358
 
                        *tref = i;
359
 
        }
360
 
 
361
 
        // Do not return the overflow reference if the tid = tc_OverFlowTID.
362
 
        // This may seem a bit strange but it is needed so that the overflow txn
363
 
        // will get a new non-overflow cache slot when it is reloaded.  
364
 
        if ((*tref >= MAX_TREF) && (tid == tc_OverFlow->tid) && (tid != tc_OverFlowTID))
365
 
                *tref = OVERFLOW_TREF;
366
 
                
367
 
        self->myTID = tid;
368
 
        self->myTransRef = *tref;
369
 
        self->myCacheVersion = tc_CacheVersion;
370
 
        exit_();
371
 
}
372
 
 
373
 
// Add a transaction record to an already existing transaction
374
 
// or possible creating a new one. Depending on the record added this may
375
 
// also commit or rollback the transaction.
376
 
void MSTransCache::tc_AddRec(uint64_t log_position, MSTransPtr rec, TRef tref)
377
 
{
378
 
        TransListPtr lrec;
379
 
        enter_();
380
 
        
381
 
        lock_(this);
382
 
 
383
 
        //---------
384
 
        if (tref == TRANS_CACHE_UNKNOWN_REF) { // It is coming from a reload
385
 
                ASSERT(tc_ReLoadingThread == self); // Sanity check here
386
 
 
387
 
                if ((self->myTID == rec->tr_id) && (self->myTransRef != TRANS_CACHE_UNKNOWN_REF))
388
 
                        tref = self->myTransRef;
389
 
                else {
390
 
                        tc_FindTXNRef(rec->tr_id, &tref);
391
 
                        if (tref == TRANS_CACHE_UNKNOWN_REF) {
392
 
                                if (!TRANS_IS_START(rec->tr_type)) 
393
 
                                        goto done; // Ignore partial tansaction reloads.
394
 
                                        
395
 
                                tref = tc_NewTransaction(rec->tr_id);
396
 
                        }
397
 
                }
398
 
        }
399
 
        
400
 
        ASSERT((tref <= MAX_TREF) || (tref == TRANS_CACHE_NEW_REF));
401
 
        ASSERT(self->myTID == rec->tr_id);
402
 
        
403
 
        //---------
404
 
        if (tref >= OVERFLOW_TREF) {
405
 
                if (tref == TRANS_CACHE_NEW_REF) {
406
 
                        ASSERT(TRANS_IS_START(rec->tr_type));
407
 
                        tref = tc_NewTransaction(rec->tr_id);
408
 
                } else if (self->myCacheVersion != tc_CacheVersion) {
409
 
                        // Check to see if the transaction if now in the cache
410
 
                        tc_FindTXNRef(rec->tr_id, &tref);
411
 
                }
412
 
                
413
 
                if (tref >= OVERFLOW_TREF){ // Overflow.
414
 
                        if (tref == OVERFLOW_TREF) {
415
 
                                if (!tc_OverFlow->len)
416
 
                                        tc_OverFlow->log_position = log_position;
417
 
                                        
418
 
                                tc_OverFlow->len++;
419
 
                                if (TRANS_IS_TERMINATED(rec->tr_type)) {
420
 
                                        if (rec->tr_type == MS_RollBackTxn)
421
 
                                                tc_OverFlow->terminated = MS_RolledBack;
422
 
                                        else if (rec->tr_type == MS_RecoveredTxn)
423
 
                                                tc_OverFlow->terminated = MS_Recovered;
424
 
                                        else
425
 
                                                tc_OverFlow->terminated = MS_Committed;
426
 
                                }
427
 
                        }
428
 
                        
429
 
                        goto done;
430
 
                }
431
 
        }
432
 
 
433
 
        lrec = tc_List + tref;
434
 
        
435
 
        ASSERT(lrec->tid);
436
 
        ASSERT(lrec->tid == rec->tr_id);
437
 
        
438
 
        if (!lrec->len) { // The first record in the transaction
439
 
                lrec->log_position = log_position;
440
 
        } else if (( (TRANS_TYPE(rec->tr_type) == MS_ReferenceTxn) || (TRANS_TYPE(rec->tr_type) == MS_DereferenceTxn)) && !tc_Recovering) { 
441
 
                // Make sure the record isn't already in the list.
442
 
                // This can happen during cache reload.
443
 
                for (uint32_t i = 0; i < lrec->len; i++) {
444
 
                        if (lrec->list[i].tc_position == log_position)
445
 
                                goto done;
446
 
                }
447
 
        }
448
 
        
449
 
        // During recovery there is no need to cache the records.
450
 
        if (!tc_Recovering) {
451
 
                switch (TRANS_TYPE(rec->tr_type)) {
452
 
                        case MS_RollBackTxn:
453
 
                        case MS_Committed:
454
 
                        case MS_RecoveredTxn:
455
 
                                // This is handled below;
456
 
                                break;
457
 
                                
458
 
                        case MS_PartialRollBackTxn:
459
 
                        {
460
 
                                // The rollback position is stored in the place for the database id.
461
 
                                for (uint32_t i = rec->tr_db_id;i < lrec->len; i++)
462
 
                                        lrec->list[i].tc_rolled_back = true;
463
 
                                        
464
 
                                break;
465
 
                        }
466
 
 
467
 
                        case MS_ReferenceTxn:
468
 
                        case MS_DereferenceTxn:
469
 
                        {
470
 
                                myTransPtr my_rec;
471
 
                                
472
 
                                if (lrec->len == lrec->size) { //Grow the list if required
473
 
                                        cs_realloc((void **) &(lrec->list), (lrec->size + 10)* sizeof(myTransRec));
474
 
                                        lrec->size += 10;               
475
 
                                }
476
 
                        
477
 
                                my_rec = lrec->list + lrec->len;
478
 
                                my_rec->tc_type = rec->tr_type;
479
 
                                my_rec->tc_db_id = rec->tr_db_id;
480
 
                                my_rec->tc_tab_id = rec->tr_tab_id;
481
 
                                my_rec->tc_blob_id = rec->tr_blob_id;
482
 
                                my_rec->tc_blob_ref_id = rec->tr_blob_ref_id;
483
 
                                my_rec->tc_position = log_position;
484
 
                                my_rec->tc_rolled_back = false;
485
 
                                
486
 
                                lrec->len++;                            
487
 
                                break;
488
 
                        }
489
 
                        
490
 
                }
491
 
        } else if ( (TRANS_TYPE(rec->tr_type) == MS_ReferenceTxn) || (TRANS_TYPE(rec->tr_type) == MS_DereferenceTxn))
492
 
                lrec->len++;
493
 
        
494
 
        
495
 
        // Check to see if this is a commit or rollback 
496
 
        // Do this last because as soon as it is marked as terminated
497
 
        // the reader thread may start processing it.
498
 
        if (TRANS_IS_TERMINATED(rec->tr_type)) {
499
 
                if (rec->tr_type == MS_RollBackTxn)
500
 
                        lrec->terminated = MS_RolledBack;
501
 
                else if (rec->tr_type == MS_RecoveredTxn)
502
 
                        lrec->terminated = MS_Recovered;
503
 
                else
504
 
                        lrec->terminated = MS_Committed;
505
 
        }
506
 
        
507
 
done:
508
 
        unlock_(this);          
509
 
        exit_();
510
 
}
511
 
 
512
 
// Get the transaction ref of the first transaction in the list.
513
 
// Sets committed to true or false depending on if the transaction is terminated.
514
 
// If there is no trsansaction then false is returned.
515
 
bool MSTransCache::tc_GetTransaction(TRef *ref, bool *terminated)
516
 
{
517
 
        if (!tc_Used)
518
 
                return false;
519
 
        
520
 
        ASSERT(tc_List[tc_First].tid);
521
 
        
522
 
        *ref =  tc_First;
523
 
        *terminated = (tc_List[tc_First].terminated != MS_Running);
524
 
        
525
 
        return true;
526
 
}       
527
 
 
528
 
//----------
529
 
bool MSTransCache::tc_GetTransactionStartPosition(uint64_t *log_position)
530
 
{
531
 
        if ((!tc_Used) || (tc_List[tc_First].len == 0))
532
 
                return false;
533
 
                
534
 
        *log_position = tc_List[tc_First].log_position;
535
 
        return true;
536
 
}
537
 
 
538
 
//----------
539
 
MS_TxnState MSTransCache::tc_TransactionState(TRef ref)
540
 
{
541
 
        ASSERT((ref < tc_Size) && tc_List[ref].tid);
542
 
        
543
 
        return tc_List[ref].terminated;
544
 
}       
545
 
 
546
 
uint32_t MSTransCache::tc_GetTransactionID(TRef ref)
547
 
{
548
 
        ASSERT((ref < tc_Size) && tc_List[ref].tid);
549
 
        
550
 
        return (tc_List[ref].tid);
551
 
}       
552
 
        
553
 
// Remove the transaction and all record associated with it.
554
 
void MSTransCache::tc_FreeTransaction(TRef tref)
555
 
{
556
 
        TransListPtr lrec;
557
 
        enter_();
558
 
        ASSERT(tc_Used && (tref < tc_Size) && tc_List[tref].tid);
559
 
        
560
 
#ifdef CHECK_TIDS
561
 
{
562
 
static uint32_t last_tid = 0;
563
 
static bool last_state = false;
564
 
(void)last_tid;
565
 
if (tc_Recovering != last_state)
566
 
        last_tid = 0;
567
 
        
568
 
last_state = tc_Recovering;
569
 
ASSERT( ((last_tid + 1) == tc_List[tref].tid) || !last_tid);
570
 
last_tid = tc_List[tref].tid;
571
 
}
572
 
#endif
573
 
 
574
 
        lrec = tc_List + tref;
575
 
#ifdef DEBUG
576
 
        lrec->old_tid = lrec->tid;
577
 
#endif
578
 
        lrec->tid = 0;
579
 
        lrec->len = 0;
580
 
        
581
 
        if (lrec->size > 10) { // Free up some excess records.
582
 
                cs_realloc((void **) &(lrec->list), 10* sizeof(myTransRec));
583
 
                lrec->size = 10;                
584
 
        }
585
 
 
586
 
        lock_(this);
587
 
        tc_Used--;
588
 
        
589
 
        if (tref == tc_First) { // Reset the start of the list.
590
 
                TRef eol = tc_EOL; // cache this incase it changes 
591
 
                
592
 
                // Skip any unused records indicated by a zero tid.
593
 
                if (tc_First > eol) {
594
 
                        for (; tc_First < tc_Size && !tc_List[tc_First].tid; tc_First++) ;
595
 
                        
596
 
                        if (tc_First == tc_Size)
597
 
                                tc_First = 0;
598
 
                }
599
 
                
600
 
                for (; tc_First < eol && !tc_List[tc_First].tid; tc_First++) ;
601
 
        }
602
 
        
603
 
        ASSERT( (tc_Used == 0 && tc_First == tc_EOL) || (tc_Used != 0 && tc_First != tc_EOL)); 
604
 
 
605
 
        unlock_(this);
606
 
 
607
 
        exit_();
608
 
}
609
 
 
610
 
//--------------------
611
 
bool MSTransCache::tc_GetRecAt(TRef tref, size_t index, MSTransPtr rec, MS_TxnState *state)
612
 
{
613
 
        TransListPtr lrec;
614
 
        bool found = false;
615
 
 
616
 
        ASSERT(tc_Used && (tref < tc_Size) && tc_List[tref].tid);
617
 
#ifdef CHECK_TIDS
618
 
{
619
 
        static uint32_t last_tid = 0;
620
 
        (void)last_tid;
621
 
        ASSERT( ((last_tid + 1) == tc_List[tref].tid) || (last_tid  == tc_List[tref].tid) || !last_tid);
622
 
        last_tid = tc_List[tref].tid;
623
 
}
624
 
#endif
625
 
        
626
 
        lrec = tc_List + tref;
627
 
        if (index < lrec->len) {
628
 
                myTransPtr my_rec = lrec->list + index;
629
 
                
630
 
                rec->tr_type = my_rec->tc_type;
631
 
                rec->tr_db_id = my_rec->tc_db_id;
632
 
                rec->tr_tab_id = my_rec->tc_tab_id;
633
 
                rec->tr_blob_id = my_rec->tc_blob_id;
634
 
                rec->tr_blob_ref_id = my_rec->tc_blob_ref_id;
635
 
                rec->tr_id = lrec->tid;
636
 
                rec->tr_check = 0;
637
 
                if (my_rec->tc_rolled_back)
638
 
                        *state = MS_RolledBack;
639
 
                else
640
 
                        *state = lrec->terminated;
641
 
                        
642
 
                found = true;
643
 
        }
644
 
        
645
 
        return found;
646
 
}
647
 
 
648
 
//--------------------
649
 
void MSTransCache::tc_dropDatabase(uint32_t db_id)
650
 
{
651
 
        enter_();
652
 
        lock_(this);
653
 
        if (tc_List) {
654
 
                for (uint32_t i = 0; i < tc_Size; i++) {
655
 
                        myTransPtr rec = tc_List[i].list;
656
 
                        if (rec) {
657
 
                                uint32_t list_len = tc_List[i].len;                     
658
 
                                while (list_len) {
659
 
                                        if (rec->tc_db_id == db_id)
660
 
                                                rec->tc_db_id = 0;
661
 
                                        list_len--; 
662
 
                                        rec++;
663
 
                                }
664
 
                        }
665
 
                }               
666
 
        }
667
 
        
668
 
        unlock_(this);
669
 
        exit_();
670
 
}