List: | Falcon Storage Engine | « Previous MessageNext Message » | |
From: | Jim Starkey | Date: | January 28 2009 7:30pm |
Subject: | Cycle Locking (was Problems with record visibility and how it is computed) | ||
View as plain text |
OK, let's look closely at cycle locking. Attached is the latest rev of
the cycle lock object. It's intended use is a stack local -- the
constructor takes the lock and the destructor releases it.
One issue is the interaction of cycle locks and long (i.e. transaction)
waits. One really doesn't want to block the cycle manager if we're
waiting for someone to come back from vacation to commit his updates (or
get rightly fired and the updates rolled back).
My solution is to release the cycle lock before a "long" wait and
reacquire it after waking up. The CycleLock object uses thread specific
data to register the address of the CycleLock object so it doesn't need
to be passed around. I believe this solves the first order problem.
The second order problems are the ramifications of releasing and
reacquiring the cycle lock. As long as the code didn't retain a pointer
acquired before the wait, everything should be just fine. I don't
think any of the candidates for cycle locking would be affected at all.
The immediate case -- transaction object lifetime control -- are OK as
long as we don't trust a transaction pointer acquired from before the
wait. In most cases we can just refetch it from where ever we got it
originally. If the pointer is now NULL, we can deal with it. Otherwise
the pointer is protected by the new cycle. The other candidate is
purging intermediate record versions. Since it tautological that these
versions of are no interest to anyone, there is no change that a
miscreant would retain a pointer to one over the wait.
I think the only serious problem with cycle locking is getting used to
the idea. I've been using it in Nimbus for some time now, and it's as
natural as SyncObjects.
So let's hear the issues and work through them. Maybe Kevin is right
and we'll find some messy and/or complicated cases; if not, then we've
got another tool for our toolbox. The more tools we have, the less that
all problems look like nails.
--
Jim Starkey
President, NimbusDB, Inc.
978 526-1376
#include "Cloud.h"
#include "CycleLock.h"
#include "MasterCatalog.h"
#include "Thread.h"
static int threadIndex = Thread::getThreadSpecificIndex();
CycleLock::CycleLock(MasterCatalog *masterCatalog)
{
syncObject = masterCatalog->currentCycle;
syncObject->lock(NULL, Shared);
locked = true;
Thread::setThreadSpecific(threadIndex, this);
}
CycleLock::~CycleLock(void)
{
if (locked)
syncObject->unlock(NULL, Shared);
Thread::setThreadSpecific(threadIndex, NULL);
}
void CycleLock::lock(void)
{
CycleLock *cycleLock = (CycleLock*) Thread::getThreadSpecific(threadIndex);
cycleLock->lockCycle();
}
void CycleLock::unlock(void)
{
CycleLock *cycleLock = (CycleLock*) Thread::getThreadSpecific(threadIndex);
cycleLock->unlockCycle();
}
void CycleLock::lockCycle(void)
{
if (!locked)
{
syncObject->lock(NULL, Shared);
locked = true;
}
}
void CycleLock::unlockCycle(void)
{
if (locked)
{
syncObject->unlock(NULL, Shared);
locked = false;
}
}
#ifndef _CYCLE_LOCK_H_
#define _CYCLE_LOCK_H_
class SyncObject;
class MasterCatalog;
class CycleLock
{
public:
CycleLock(MasterCatalog *masterCatalog);
~CycleLock(void);
void lockCycle(void);
void unlockCycle(void);
static void lock(void);
static void unlock(void);
SyncObject *syncObject;
bool locked;
};
#endif