LCOV - code coverage report
Current view: top level - gdk - gdk_tm.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 66 105 62.9 %
Date: 2021-09-14 19:48:19 Functions: 5 6 83.3 %

          Line data    Source code
       1             : /*
       2             :  * This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0.  If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       5             :  *
       6             :  * Copyright 1997 - July 2008 CWI, August 2008 - 2021 MonetDB B.V.
       7             :  */
       8             : 
       9             : /*
      10             :  * @a M. L. Kersten, P. Boncz, N. J. Nes
      11             :  *
      12             :  * @* Transaction management
      13             :  * The Transaction Manager maintains the buffer of (permanent) BATS
      14             :  * held resident.  Entries from the BAT buffer are always accessed by
      15             :  * BAT id.  A BAT becomes permanent by assigning a name with
      16             :  * @%BBPrename@.  Access to the transaction table is regulated by a
      17             :  * semaphore.
      18             :  */
      19             : #include "monetdb_config.h"
      20             : #include "gdk.h"
      21             : #include "gdk_private.h"
      22             : #include "gdk_tm.h"
      23             : 
      24             : /*
      25             :  * The physical (disk) commit protocol is handled mostly by
      26             :  * BBPsync. Once a commit succeeded, there is the task of removing
      27             :  * ex-persistent bats (those that still were persistent in the
      28             :  * previous commit, but were made transient in this transaction).
      29             :  * Notice that such ex- (i.e. non-) persistent bats are not backed up
      30             :  * by the BBPsync protocol, so we cannot start deleting after we know
      31             :  * the commit will succeed.
      32             :  *
      33             :  * Another hairy issue are the delta statuses in BATs. These provide a
      34             :  * fast way to perform a transaction abort (HOT-abort, instead of
      35             :  * COLD-abort, which is achieved by the BBP recovery in a database
      36             :  * restart). Hot-abort functionality has not been important in MonetDB
      37             :  * for now, so it is not well-tested. The problem here is that if a
      38             :  * commit fails in the physical part (BBPsync), we have not sufficient
      39             :  * information to roll back the delta statuses.
      40             :  *
      41             :  * So a 'feature' of the abort is that after a failed commit,
      42             :  * in-memory we *will* commit the transaction. Subsequent commits can
      43             :  * retry to achieve a physical commit. The only way to abort in such a
      44             :  * situation is COLD-abort: quit the server and restart, so you get
      45             :  * the recovered disk images.
      46             :  */
      47             : /* in the commit prelude, the delta status in the memory image of all
      48             :  * bats is commited */
      49             : static gdk_return
      50       16321 : prelude(int cnt, bat *restrict subcommit, BUN *restrict sizes)
      51             : {
      52             :         int i = 0;
      53             : 
      54     4455479 :         while (++i < cnt) {
      55     4439158 :                 bat bid = subcommit ? subcommit[i] : i;
      56             : 
      57     4439158 :                 if (BBP_status(bid) & BBPPERSISTENT) {
      58     4397913 :                         BAT *b = BBP_cache(bid);
      59             : 
      60     4397913 :                         if (b == NULL && (BBP_status(bid) & BBPSWAPPED)) {
      61           0 :                                 b = BBPquickdesc(bid);
      62           0 :                                 if (b == NULL)
      63             :                                         return GDK_FAIL;
      64             :                         }
      65     4397913 :                         if (b) {
      66     4179290 :                                 MT_lock_set(&b->theaplock);
      67     4179290 :                                 assert(!isVIEW(b));
      68     4179290 :                                 assert(b->batRole == PERSISTENT);
      69     4179290 :                                 BATcommit(b, sizes ? sizes[i] : BUN_NONE);
      70     4179290 :                                 MT_lock_unset(&b->theaplock);
      71             :                         }
      72             :                 }
      73             :         }
      74             :         return GDK_SUCCEED;
      75             : }
      76             : 
      77             : /* in the commit epilogue, the BBP-status of the bats is changed to
      78             :  * reflect their presence in the succeeded checkpoint.  Also bats from
      79             :  * the previous checkpoint that were deleted now are physically
      80             :  * destroyed.
      81             :  */
      82             : static void
      83       16321 : epilogue(int cnt, bat *subcommit, bool locked)
      84             : {
      85             :         int i = 0;
      86             : 
      87     4455479 :         while (++i < cnt) {
      88     4439158 :                 bat bid = subcommit ? subcommit[i] : i;
      89             : 
      90     4439158 :                 if (BBP_status(bid) & BBPPERSISTENT) {
      91     4397913 :                         BBP_status_on(bid, BBPEXISTING);
      92       41245 :                 } else if (BBP_status(bid) & BBPDELETED) {
      93             :                         /* check mmap modes of bats that are now
      94             :                          * transient. this has to be done after the
      95             :                          * commit succeeded, because the mmap modes
      96             :                          * allowed on transient bats would be
      97             :                          * dangerous on persistent bats. If the commit
      98             :                          * failed, the already processed bats that
      99             :                          * would become transient after the commit,
     100             :                          * but didn't due to the failure, would be a
     101             :                          * consistency risk.
     102             :                          */
     103       29972 :                         BAT *b = BBP_cache(bid);
     104       29972 :                         if (b) {
     105             :                                 /* check mmap modes */
     106       29568 :                                 if (BATcheckmodes(b, true) != GDK_SUCCEED)
     107           0 :                                         TRC_WARNING(GDK, "BATcheckmodes failed\n");
     108             :                         }
     109             :                 }
     110     4439158 :                 if (!locked)
     111     4425313 :                         MT_lock_set(&GDKswapLock(bid));
     112     4439158 :                 if ((BBP_status(bid) & BBPDELETED) && BBP_refs(bid) <= 0 && BBP_lrefs(bid) <= 0) {
     113         404 :                         BAT *b = BBPquickdesc(bid);
     114             : 
     115             :                         /* the unloaded ones are deleted without
     116             :                          * loading deleted disk images */
     117         404 :                         if (b) {
     118         404 :                                 BATdelete(b);
     119             :                         }
     120         404 :                         BBPclear(bid, false);
     121             :                 }
     122     4439158 :                 BBP_status_off(bid, BBPDELETED | BBPSWAPPED | BBPNEW);
     123     4439158 :                 if (!locked)
     124     4425313 :                         MT_lock_unset(&GDKswapLock(bid));
     125             :         }
     126       16321 :         GDKclrerr();
     127       16321 : }
     128             : 
     129             : /*
     130             :  * @- TMcommit
     131             :  * global commit without any multi-threaded access assumptions, thus
     132             :  * taking all BBP locks.  It creates a new database checkpoint.
     133             :  */
     134             : gdk_return
     135           8 : TMcommit(void)
     136             : {
     137             :         gdk_return ret = GDK_FAIL;
     138             : 
     139             :         /* commit with the BBP globally locked */
     140           8 :         BBPlock();
     141          16 :         if (prelude(getBBPsize(), NULL, NULL) == GDK_SUCCEED &&
     142           8 :             BBPsync(getBBPsize(), NULL, NULL, getBBPlogno(), getBBPtransid()) == GDK_SUCCEED) {
     143           8 :                 epilogue(getBBPsize(), NULL, true);
     144             :                 ret = GDK_SUCCEED;
     145             :         }
     146           8 :         BBPunlock();
     147           8 :         return ret;
     148             : }
     149             : 
     150             : /*
     151             :  * @- TMsubcommit
     152             :  *
     153             :  * Create a new checkpoint that is equal to the previous, with the
     154             :  * exception that for the passed list of batnames, the current state
     155             :  * will be reflected in the new checkpoint.
     156             :  *
     157             :  * On the bats in this list we assume exclusive access during the
     158             :  * operation.
     159             :  *
     160             :  * This operation is useful for e.g. adding a new XQuery document or
     161             :  * SQL table to the committed state (after bulk-load). Or for dropping
     162             :  * a table or doc, without forcing the total database to be clean,
     163             :  * which may require a lot of I/O.
     164             :  *
     165             :  * We expect the globally locked phase (BBPsync) to take little time
     166             :  * (<100ms) as only the BBP.dir is written out; and for the existing
     167             :  * bats that were modified, only some heap moves are done (moved from
     168             :  * BAKDIR to SUBDIR).  The atomic commit for sub-commit is the rename
     169             :  * of SUBDIR to DELDIR.
     170             :  *
     171             :  * As it does not take the BBP-locks (thanks to the assumption that
     172             :  * access is exclusive), the concurrency impact of subcommit is also
     173             :  * much lighter to ongoing concurrent query and update facilities than
     174             :  * a real global TMcommit.
     175             :  */
     176             : gdk_return
     177       16316 : TMsubcommit_list(bat *restrict subcommit, BUN *restrict sizes, int cnt, lng logno, lng transid)
     178             : {
     179             :         int xx;
     180             :         gdk_return ret = GDK_FAIL;
     181             : 
     182       16316 :         assert(cnt > 0);
     183       16316 :         assert(subcommit[0] == 0); /* BBP artifact: slot 0 in the array will be ignored */
     184             : 
     185       16316 :         if (GDKinmemory(0))
     186             :                 return GDK_SUCCEED;
     187             : 
     188             :         /* sort the list on BAT id */
     189       17123 :         GDKqsort(subcommit + 1, sizes ? sizes + 1 : NULL, NULL, cnt - 1, sizeof(bat), sizes ? sizeof(BUN) : 0, TYPE_bat, false, false);
     190             : 
     191       16313 :         assert(cnt == 1 || subcommit[1] > 0);  /* all values > 0 */
     192             :         /* de-duplication of BAT ids in subcommit list
     193             :          * this is needed because of legacy reasons (database
     194             :          * upgrade) */
     195     4425313 :         for (xx = 2; xx < cnt; xx++) {
     196     4409000 :                 if (subcommit[xx-1] == subcommit[xx]) {
     197             :                         int i;
     198           0 :                         cnt--;
     199           0 :                         for (i = xx; i < cnt; i++)
     200           0 :                                 subcommit[i] = subcommit[i+1];
     201           0 :                         if (sizes) {
     202           0 :                                 for (i = xx; i < cnt; i++)
     203           0 :                                         sizes[i] = sizes[i+1];
     204             :                         }
     205             :                 }
     206             :         }
     207       16313 :         if (prelude(cnt, subcommit, sizes) == GDK_SUCCEED) {    /* save the new bats outside the lock */
     208             :                 /* lock just prevents other global (sub-)commits */
     209       16313 :                 MT_lock_set(&GDKtmLock);
     210       16313 :                 if (BBPsync(cnt, subcommit, sizes, logno, transid) == GDK_SUCCEED) { /* write BBP.dir (++) */
     211       16313 :                         epilogue(cnt, subcommit, false);
     212             :                         ret = GDK_SUCCEED;
     213             :                 }
     214       16313 :                 MT_lock_unset(&GDKtmLock);
     215             :         }
     216             :         return ret;
     217             : }
     218             : 
     219             : gdk_return
     220           1 : TMsubcommit(BAT *b)
     221             : {
     222             :         int cnt = 1;
     223             :         gdk_return ret = GDK_FAIL;
     224             :         bat *subcommit;
     225             :         BUN p, q;
     226             : 
     227           1 :         subcommit = GDKmalloc((BATcount(b) + 1) * sizeof(bat));
     228           1 :         if (subcommit == NULL)
     229             :                 return GDK_FAIL;
     230             : 
     231           1 :         BATiter bi = bat_iterator(b);
     232           1 :         subcommit[0] = 0;       /* BBP artifact: slot 0 in the array will be ignored */
     233             :         /* collect the list and save the new bats outside any
     234             :          * locking */
     235          10 :         BATloop(b, p, q) {
     236           9 :                 bat bid = BBPindex((str) BUNtvar(bi, p));
     237             : 
     238           9 :                 if (bid)
     239           9 :                         subcommit[cnt++] = bid;
     240             :         }
     241           1 :         bat_iterator_end(&bi);
     242             : 
     243           1 :         ret = TMsubcommit_list(subcommit, NULL, cnt, getBBPlogno(), getBBPtransid());
     244           1 :         GDKfree(subcommit);
     245           1 :         return ret;
     246             : }
     247             : 
     248             : /*
     249             :  * @- TMabort
     250             :  * Transaction abort is cheap. We use the delta statuses to go back to
     251             :  * the previous version of each BAT. Also for BATs that are currently
     252             :  * swapped out. Persistent BATs that were made transient in this
     253             :  * transaction become persistent again.
     254             :  */
     255             : void
     256           0 : TMabort(void)
     257             : {
     258             :         int i;
     259             : 
     260           0 :         BBPlock();
     261           0 :         for (i = 1; i < getBBPsize(); i++) {
     262           0 :                 if (BBP_status(i) & BBPNEW) {
     263           0 :                         BAT *b = BBPquickdesc(i);
     264             : 
     265           0 :                         if (b) {
     266           0 :                                 if (!b->batTransient)
     267           0 :                                         BBPrelease(i);
     268           0 :                                 b->batTransient = true;
     269           0 :                                 b->batDirtydesc = true;
     270             :                         }
     271             :                 }
     272             :         }
     273           0 :         for (i = 1; i < getBBPsize(); i++) {
     274           0 :                 if (BBP_status(i) & (BBPPERSISTENT | BBPDELETED | BBPSWAPPED)) {
     275           0 :                         BAT *b = BBPquickdesc(i);
     276             : 
     277           0 :                         if (b == NULL)
     278           0 :                                 continue;
     279             : 
     280           0 :                         BBPfix(i);
     281           0 :                         if (BATdirty(b) || DELTAdirty(b)) {
     282             :                                 /* BUN move-backes need a real BAT! */
     283             :                                 /* Stefan:
     284             :                                  * Actually, in case DELTAdirty(b),
     285             :                                  * i.e., a BAT with differences that
     286             :                                  * is saved/swapped-out but not yet
     287             :                                  * committed, we (AFAIK) don't have to
     288             :                                  * load the BAT and apply the undo,
     289             :                                  * but rather could simply discard the
     290             :                                  * delta and revive the backup;
     291             :                                  * however, I don't know how to do
     292             :                                  * this (yet), hence we stick with
     293             :                                  * this solution for the time being
     294             :                                  * --- it should be correct though it
     295             :                                  * might not be the most efficient
     296             :                                  * way...
     297             :                                  */
     298           0 :                                 b = BBPdescriptor(i);
     299           0 :                                 BATundo(b);
     300             :                         }
     301           0 :                         if (BBP_status(i) & BBPDELETED) {
     302           0 :                                 BBP_status_on(i, BBPEXISTING);
     303           0 :                                 if (b->batTransient)
     304           0 :                                         BBPretain(i);
     305           0 :                                 b->batTransient = false;
     306           0 :                                 b->batDirtydesc = true;
     307             :                         }
     308           0 :                         BBPunfix(i);
     309             :                 }
     310           0 :                 BBP_status_off(i, BBPDELETED | BBPSWAPPED | BBPNEW);
     311             :         }
     312           0 :         BBPunlock();
     313           0 :         GDKclrerr();
     314           0 : }

Generated by: LCOV version 1.14