Thread: SSI implementation question

SSI implementation question

From
Tom Lane
Date:
Is it really necessary for GetSerializableTransactionSnapshotInt to
acquire an empty SERIALIZABLEXACT before it acquires a snapshot?
If so, why?  The proposed synchronized-snapshots feature will mean
that the allegedly-new snapshot actually was taken some time before,
so it seems to me that either this is not necessary or we cannot use
a synchronized snapshot in a serializable xact.

In the same vein, why is it necessary to be holding
SerializableXactHashLock (exclusively, yet) while acquiring the
snapshot?  That seems rather bad from a concurrency standpoint, and
again it's going to be pretty meaningless if we're just installing a
pre-existing snapshot.

The reason these things came to mind is that I want to refactor the code
so that the SSI-specific work in GetSerializableTransactionSnapshotInt
is done by a function that is handed an already-taken snapshot, because
I cannot stomach what Joachim did to the APIs of GetSnapshotData and
allied functions.  But refactor or no refactor, it seems like installing
a pre-existing snapshot may be breaking some assumptions here.
        regards, tom lane


Re: SSI implementation question

From
"Kevin Grittner"
Date:
Tom Lane <tgl@sss.pgh.pa.us> wrote:
> Is it really necessary for GetSerializableTransactionSnapshotInt
> to acquire an empty SERIALIZABLEXACT before it acquires a
> snapshot?  If so, why?  The proposed synchronized-snapshots
> feature will mean that the allegedly-new snapshot actually was
> taken some time before, so it seems to me that either this is not
> necessary or we cannot use a synchronized snapshot in a
> serializable xact.
Hmm.  If the intent is that each serializable transaction sharing
the snapshot is a separate logical transaction, it *might* hold -- I
have to think about that a bit.  If the intent is that the work of
one logical transaction is being split across processes, then SSI
doesn't hold up without somehow tying all of the processes to a
single SERIALIZABLEXACT; and then the direct access to
MySerializableXact falls apart.
> In the same vein, why is it necessary to be holding
> SerializableXactHashLock (exclusively, yet) while acquiring the
> snapshot?  That seems rather bad from a concurrency standpoint,
> and again it's going to be pretty meaningless if we're just
> installing a pre-existing snapshot.
That seems like something which probably could and should be fixed,
especially since SerializableXactHashLock can become a bottleneck in
9.1 and will be much more of a problem with the elimination of other
bottlenecks in 9.2.
> The reason these things came to mind is that I want to refactor
> the code so that the SSI-specific work in
> GetSerializableTransactionSnapshotInt is done by a function that
> is handed an already-taken snapshot, because I cannot stomach what
> Joachim did to the APIs of GetSnapshotData and allied functions. 
> But refactor or no refactor, it seems like installing a
> pre-existing snapshot may be breaking some assumptions here.
I guess the other thing to look out for is whether it could possibly
move PredXact->SxactGlobalXmin backwards.  I would have to check for
other possible problems.
-Kevin


Re: SSI implementation question

From
"Kevin Grittner"
Date:
"Kevin Grittner" <Kevin.Grittner@wicourts.gov> wrote:
> If the intent is that each serializable transaction sharing
> the snapshot is a separate logical transaction, it *might* hold --
I think the rules have to be that the snapshot provided to a
serializable transaction must be provided by an active serializable
transaction.  That prevents the serializable global xmin from moving
backwards; which is not allowed except during recovery processing of
prepared transactions.  Each transaction using the snapshot is a
logically separate transaction -- they just have a shared view of
the state of the data.
> If the intent is that the work of one logical transaction is being
> split across processes, then SSI doesn't hold up without somehow
> tying all of the processes to a single SERIALIZABLEXACT; and then
> the direct access to MySerializableXact falls apart.
Except, as discussed on a separate, concurrent thread, that a READ
ONLY transaction might find its snapshot to be safe -- at which
point it no longer uses a SERIALIZABLEXACT.
-Kevin


Re: SSI implementation question

From
Tom Lane
Date:
"Kevin Grittner" <Kevin.Grittner@wicourts.gov> writes:
> Tom Lane <tgl@sss.pgh.pa.us> wrote:
>> Is it really necessary for GetSerializableTransactionSnapshotInt
>> to acquire an empty SERIALIZABLEXACT before it acquires a
>> snapshot?  If so, why?  The proposed synchronized-snapshots
>> feature will mean that the allegedly-new snapshot actually was
>> taken some time before, so it seems to me that either this is not
>> necessary or we cannot use a synchronized snapshot in a
>> serializable xact.
> Hmm.  If the intent is that each serializable transaction sharing
> the snapshot is a separate logical transaction, it *might* hold -- I
> have to think about that a bit.  If the intent is that the work of
> one logical transaction is being split across processes, then SSI
> doesn't hold up without somehow tying all of the processes to a
> single SERIALIZABLEXACT; and then the direct access to
> MySerializableXact falls apart.

No, the intention of the synchronized-snapshots feature is just to be
able to start multiple transactions using exactly the same snapshot.
They're independent after that.  The aspect of it that is worrying me
is that if xact A starts, gets a snapshot and publishes it, and then
xact B starts and wants to adopt that snapshot, then

(1) other transactions may have started or ended meanwhile; does that
break any of SSI's assumptions?

(2) as things stand, xact A need not be running in serializable mode ---
if B is serializable, does *that* break any assumptions?

We already have to have an interlock to ensure that GlobalXmin doesn't
go backwards, by means of requiring A to still be running at the instant
B adopts the snapshot and sets its MyProc->xmin accordingly.  However,
there is not currently anything that guarantees that A is still running
by the time B does GetSerializableTransactionSnapshotInt, shortly later.
So if your answer to question (1) involves an assumption that A is still
running, we're going to have to figure out how to arrange that without
deadlocking on ProcArrayLock vs SerializableXactHashLock.  Which might
be another good reason for changing predicate.c so that we don't hold
the latter while taking a snapshot ...
        regards, tom lane


Re: SSI implementation question

From
Dan Ports
Date:
On Wed, Oct 19, 2011 at 12:56:58PM -0400, Tom Lane wrote:
> Is it really necessary for GetSerializableTransactionSnapshotInt to
> acquire an empty SERIALIZABLEXACT before it acquires a snapshot?
> If so, why? 

*That* isn't necessary, no. It is necessary, however, to acquire the
snapshot while SerializableXactHashLock is held. There are a couple
reasons for this: the sxact's lastCommitBeforeSnapshot needs to match
the snapshot, SxactGlobalXmin needs to be set to the correct value,
etc. That's why the call to GetSnapshotData happens from where it does

> The proposed synchronized-snapshots feature will mean
> that the allegedly-new snapshot actually was taken some time before,
> so it seems to me that either this is not necessary or we cannot use
> a synchronized snapshot in a serializable xact.

There are definitely potential problems here. If the original snapshot
doesn't belong to an active serializable transaction, we may have
discarded the state we need to do SSI, e.g. we might have already
cleaned up SIREAD locks from concurrent committed transactions.

I assume the answer here is going to have to be to either refuse to
start a serializable transaction if that's the case, or make saving a
snapshot inhibit some of the SSI cleanup.

> In the same vein, why is it necessary to be holding
> SerializableXactHashLock (exclusively, yet) while acquiring the
> snapshot?  That seems rather bad from a concurrency standpoint, and
> again it's going to be pretty meaningless if we're just installing a
> pre-existing snapshot.

Yes, it's bad. I'm working on a design to address
SerializableXactHashLock contention, but there needs to be some locking
here for the reasons I mentioned above. I think the best we can do here
is to acquire a lock in shared mode when registering a serializable
transaction and in exclusive mode when committing. (Which is what you'd
expect, I guess; it's the same story as ProcArrayLock, and for most of
the same reasons.) Obviously, we'll also want to minimize the amount of
work we're doing while holding that lock.

Dan

-- 
Dan R. K. Ports              MIT CSAIL                http://drkp.net/


Re: SSI implementation question

From
Tom Lane
Date:
Dan Ports <drkp@csail.mit.edu> writes:
> On Wed, Oct 19, 2011 at 12:56:58PM -0400, Tom Lane wrote:
>> The proposed synchronized-snapshots feature will mean
>> that the allegedly-new snapshot actually was taken some time before,
>> so it seems to me that either this is not necessary or we cannot use
>> a synchronized snapshot in a serializable xact.

> There are definitely potential problems here. If the original snapshot
> doesn't belong to an active serializable transaction, we may have
> discarded the state we need to do SSI, e.g. we might have already
> cleaned up SIREAD locks from concurrent committed transactions.

> I assume the answer here is going to have to be to either refuse to
> start a serializable transaction if that's the case, or make saving a
> snapshot inhibit some of the SSI cleanup.

We can easily mark an exported snapshot with the IsoLevel of the
transaction that made it, and then for example refuse to adopt a
less-than-serializable snapshot into a serializable transaction.
So that aspect can be had if we need it.  But we still have a race
condition with the patch as it stands, because there is a window for the
original xact to terminate before GetSerializableTransactionSnapshotInt
runs.  It sounds like we have to fix that.

>> In the same vein, why is it necessary to be holding
>> SerializableXactHashLock (exclusively, yet) while acquiring the
>> snapshot?  That seems rather bad from a concurrency standpoint, and
>> again it's going to be pretty meaningless if we're just installing a
>> pre-existing snapshot.

> Yes, it's bad. I'm working on a design to address
> SerializableXactHashLock contention, but there needs to be some locking
> here for the reasons I mentioned above. I think the best we can do here
> is to acquire a lock in shared mode when registering a serializable
> transaction and in exclusive mode when committing. (Which is what you'd
> expect, I guess; it's the same story as ProcArrayLock, and for most of
> the same reasons.) Obviously, we'll also want to minimize the amount of
> work we're doing while holding that lock.

I wonder whether it would be prudent to set the synchronized-snapshots
patch aside until you've finished that work (assuming you're actively
working on it).  It's evidently going to require some nontrivial changes
in predicate.c, and I don't think the feature should take precedence
over SSI performance improvement.
        regards, tom lane


Re: SSI implementation question

From
Dan Ports
Date:
On Wed, Oct 19, 2011 at 04:36:41PM -0400, Tom Lane wrote:
> No, the intention of the synchronized-snapshots feature is just to be
> able to start multiple transactions using exactly the same snapshot.
> They're independent after that.  The aspect of it that is worrying me
> is that if xact A starts, gets a snapshot and publishes it, and then
> xact B starts and wants to adopt that snapshot, then
> 
> (2) as things stand, xact A need not be running in serializable mode ---
> if B is serializable, does *that* break any assumptions?

[taking these in opposite order]

Yes, I think that's going to be a problem. The obvious case where it's
clearly not going to work is if A is older than the oldest active
serializable xact (i.e. SxactGlobalXmin would have to move backwards).
It's probably possible to make it work when that's not the case, but I
think it's better to require A to be serializable -- if nothing else,
it's a far simpler rule to document!

There is another case that could be problematic, if A was READ ONLY,
and B isn't. It sounds to me like that would also be a reasonable thing
to forbid.

> (1) other transactions may have started or ended meanwhile; does that
> break any of SSI's assumptions?

Mostly, no, if A is still running. There's one case that needs to be
handled a bit carefully, but shouldn't be a problem: if A was
SERIALIZABLE READ ONLY, and its snapshot was found to be safe, then
it's actually running (safely) at REPEATABLE READ. If we start a new
read-only transaction at the same snapshot, we need to make it run at
REPEATABLE READ if it requests SERIALIZABLE.

> We already have to have an interlock to ensure that GlobalXmin doesn't
> go backwards, by means of requiring A to still be running at the instant
> B adopts the snapshot and sets its MyProc->xmin accordingly.  However,
> there is not currently anything that guarantees that A is still running
> by the time B does GetSerializableTransactionSnapshotInt, shortly later.
> So if your answer to question (1) involves an assumption that A is still
> running, we're going to have to figure out how to arrange that without
> deadlocking on ProcArrayLock vs SerializableXactHashLock.

Yep, I think we're going to have to do that. I haven't had a chance to
look at the synchronized snapshots patch yet, so I can't (yet) offer
any suggestions about how to implement it.

> Which might
> be another good reason for changing predicate.c so that we don't hold
> the latter while taking a snapshot ...

It'd be great if we could do that, but I don't really see it being
possible...

Dan

-- 
Dan R. K. Ports              MIT CSAIL                http://drkp.net/


Re: SSI implementation question

From
"Kevin Grittner"
Date:
Dan Ports <drkp@csail.mit.edu> wrote:
> the sxact's lastCommitBeforeSnapshot needs to match the snapshot,
> SxactGlobalXmin needs to be set to the correct value, etc. That's
> why the call to GetSnapshotData happens from where it does
Oh, right.  I knew I was forgetting something.  What if that was
captured as part of building a snapshot?  That seems like it would
be a trivial cost compared to other snapshot-building activity, and
might give us a way to get this out from under the
SerializableXactHashLock locking.
-Kevin


Re: SSI implementation question

From
Tom Lane
Date:
"Kevin Grittner" <Kevin.Grittner@wicourts.gov> writes:
> Dan Ports <drkp@csail.mit.edu> wrote:
>> the sxact's lastCommitBeforeSnapshot needs to match the snapshot,
>> SxactGlobalXmin needs to be set to the correct value, etc. That's
>> why the call to GetSnapshotData happens from where it does
> Oh, right.  I knew I was forgetting something.  What if that was
> captured as part of building a snapshot?  That seems like it would
> be a trivial cost compared to other snapshot-building activity, and
> might give us a way to get this out from under the
> SerializableXactHashLock locking.

But aren't the values you need to fetch protected by
SerializableXactHashLock?  Having to take an additional LWLock is
*not* a "trivial cost".
        regards, tom lane


Re: SSI implementation question

From
"Kevin Grittner"
Date:
Tom Lane <tgl@sss.pgh.pa.us> wrote:
> "Kevin Grittner" <Kevin.Grittner@wicourts.gov> writes:
>> Dan Ports <drkp@csail.mit.edu> wrote:
>>> the sxact's lastCommitBeforeSnapshot needs to match the
>>> snapshot, SxactGlobalXmin needs to be set to the correct value,
>>> etc. That's why the call to GetSnapshotData happens from where
>>> it does
>  
>> Oh, right.  I knew I was forgetting something.  What if that was
>> captured as part of building a snapshot?  That seems like it
>> would be a trivial cost compared to other snapshot-building
>> activity, and might give us a way to get this out from under the
>> SerializableXactHashLock locking.
> 
> But aren't the values you need to fetch protected by
> SerializableXactHashLock?  Having to take an additional LWLock is
> *not* a "trivial cost".
I was thinking that this would become a more general "commit
sequence number" and could be bundled into the snapshot.  It would
also allow cleaning up the funny double-increment we did as a
workaround for this not being incremented at the actual moment of
commit.  There was actually a patch floated to do it that way near
the end of the 9.1 development cycle.  I imagine that's probably
suffered major bitrot because of Robert's 9.2 work, but the general
idea is the same.
I agree that if it can't fit under existing locks at commit and
snapshot build times, it isn't feasible.
-Kevin


Re: SSI implementation question

From
Dan Ports
Date:
On Wed, Oct 19, 2011 at 05:04:52PM -0400, Tom Lane wrote:
> I wonder whether it would be prudent to set the synchronized-snapshots
> patch aside until you've finished that work (assuming you're actively
> working on it).  It's evidently going to require some nontrivial changes
> in predicate.c, and I don't think the feature should take precedence
> over SSI performance improvement.

I wouldn't hold the patch up on my account. Improving the SSI locking
situation looks to be a fairly substantial project. I've been drawing
up a plan to fix it, but I'm also travelling for most of the next two
weeks and probably won't be able to do any serious hacking on it until
I'm back to the office.

Dan

-- 
Dan R. K. Ports              MIT CSAIL                http://drkp.net/


Re: SSI implementation question

From
Dan Ports
Date:
I think it would be fairly sensible to push some of this into
ProcArray, actually. The commit sequence numbers just involve assigning/
incrementing a global counter when taking a snapshot and finishing a
transaction -- that's not too much work, doesn't require any additional
locking beyond ProcArrayLock, and isn't too tied to SSI. (I could
imagine it being useful for other purposes, though I'm not going to
make that argument too seriously without actually having one in mind.)

SxactGlobalXmin and WritableSxactCount are obviously more SSI-specific,
but I think we can come up with something reasonable to do with them.

The part that's harder is building the list of potential conflicts
that's used to identify safe snapshots for r/o transactions. That
(currently) has to happen atomically taking the snapshot. We'll
probably have to do this in some significantly different way, but I
haven't quite worked out what it is yet.

On the bright side, if we can address these three issues, we shouldn't
need to take SerializableXactHashLock at all when starting a
transaction. (Realistically, we might have to take it or some other
lock shared to handle one of them -- but I really want starting a
serializable xact to not take any exclusive locks.)

Dan

-- 
Dan R. K. Ports              MIT CSAIL                http://drkp.net/


Re: SSI implementation question

From
"Kevin Grittner"
Date:
Dan Ports  wrote:
> I think it would be fairly sensible to push some of this into
> ProcArray, actually. The commit sequence numbers just involve
> assigning/incrementing a global counter when taking a snapshot and
> finishing a transaction -- that's not too much work, doesn't
> require any additional locking beyond ProcArrayLock, and isn't too
> tied to SSI. (I could imagine it being useful for other purposes,
> though I'm not going to make that argument too seriously without
> actually having one in mind.)
So far it sounds like we're on the same page.
> SxactGlobalXmin and WritableSxactCount are obviously more
> SSI-specific, but I think we can come up with something reasonable
> to do with them.
Agreed.
> The part that's harder is building the list of potential conflicts
> that's used to identify safe snapshots for r/o transactions. That
> (currently) has to happen atomically taking the snapshot.
That's not obvious to me; could you elaborate on the reasons?  If the
commit sequence number is incremented under cover of an existing
ProcArrayLock, and the current value is assigned to a snapshot under
cover of same, acquiring SerializableXactHashLock after we get the
snapshot seems to work for SxactGlobalXmin and WritableSxactCount,
AFAICS.
> We'll probably have to do this in some significantly different way,
> but I haven't quite worked out what it is yet.
Well, transaction startup needs some rearrangement, clearly.  So far
nothing fundamental has caught my attention beyond the commit
sequence number changes.
> On the bright side, if we can address these three issues, we
> shouldn't need to take SerializableXactHashLock at all when
> starting a transaction. (Realistically, we might have to take it or
> some other lock shared to handle one of them -- but I really want
> starting a serializable xact to not take any exclusive locks.)
Yeah, I don't see how we can avoid taking a LW lock to get a
serializable transaction which needs a SERIALIZABLEXACT set up, but
it might be possible and worthwhile to split the uses of
SerializableXactHashLock so that it's not such a hotspot.
-Kevin


Re: SSI implementation question

From
Dan Ports
Date:
On Thu, Oct 20, 2011 at 07:33:59AM -0500, Kevin Grittner wrote:
> Dan Ports  wrote:
> > The part that's harder is building the list of potential conflicts
> > that's used to identify safe snapshots for r/o transactions. That
> > (currently) has to happen atomically taking the snapshot.
>  
> That's not obvious to me; could you elaborate on the reasons?  If the
> commit sequence number is incremented under cover of an existing
> ProcArrayLock, and the current value is assigned to a snapshot under
> cover of same, acquiring SerializableXactHashLock after we get the
> snapshot seems to work for SxactGlobalXmin and WritableSxactCount,
> AFAICS.

Well, whenever a r/w transaction commits or aborts, we need to check
whether that caused any concurrent r/o transactions to have a
known-safe or unsafe snapshot. The way that happens now is that, when a
r/o transaction starts, it scans the list of r/w transactions and adds
a pointer to itself in their sxact->possibleUnsafeConflicts. When one
of them commits, it scans the list of possible conflicts and does the
appropriate thing.

That's not ideal because:
 - it requires modifing another transaction's sxact when registering a   serializable transaction, so that means taking
 SerializableXactHashLock exclusive.
 
 - the set of concurrent transactions used to identify the possible   conflicts needs to match the one used to build
thesnapshot.   Otherwise, a transaction might commit between when the snapshot is   take and when we find possible
conflicts.(Holding   SerializableXactHashLock prevents this.)
 

> Yeah, I don't see how we can avoid taking a LW lock to get a
> serializable transaction which needs a SERIALIZABLEXACT set up, but
> it might be possible and worthwhile to split the uses of
> SerializableXactHashLock so that it's not such a hotspot.

Oh, right, one other problem is that the sxact free list is also
protected by SerializableXactHashLock, so allocating from it requires
locking. That one could be fixed by protecting it with its own lock, or
(better yet) eventually moving to a lock-free implementation.

In general, the contention problem is that SerializableXactHashLock
basically protects all SSI state except the predicate locks themselves
(notably, the dependency graph). This isn't partitioned at all, so
looking at or modifying a single sxact requires locking the whole
graph. I'd like to replace this with something finer-grained.

Dan

-- 
Dan R. K. Ports              MIT CSAIL                http://drkp.net/


Re: SSI implementation question

From
Tom Lane
Date:
Dan Ports <drkp@csail.mit.edu> writes:
> On Wed, Oct 19, 2011 at 04:36:41PM -0400, Tom Lane wrote:
>> (2) as things stand, xact A need not be running in serializable mode ---
>> if B is serializable, does *that* break any assumptions?

> [taking these in opposite order]

> Yes, I think that's going to be a problem. The obvious case where it's
> clearly not going to work is if A is older than the oldest active
> serializable xact (i.e. SxactGlobalXmin would have to move backwards).
> It's probably possible to make it work when that's not the case, but I
> think it's better to require A to be serializable -- if nothing else,
> it's a far simpler rule to document!

> There is another case that could be problematic, if A was READ ONLY,
> and B isn't. It sounds to me like that would also be a reasonable thing
> to forbid.

I've committed the synchronized-snapshots patch with those two
restrictions, ie, to import a snapshot into a serializable transaction
(1) the source transaction must be serializable (and must still be
running, of course);
(2) you can't import a read-only transaction's snapshot into a
read-write serializable transaction.

I don't understand the SSI code well enough to tell if this is
sufficient or not, so I hope you guys will take a closer look at the
issue when you have time.
        regards, tom lane


Re: SSI implementation question

From
"Kevin Grittner"
Date:
Tom Lane <tgl@sss.pgh.pa.us> wrote:
> I don't understand the SSI code well enough to tell if this is
> sufficient or not, so I hope you guys will take a closer look at
> the issue when you have time.
I will definitely give it a look.  Right now we have a "perfect
storm" of time demands due to some recently-passed legislation
combined with the need to wrap up some "Annual Plan" items.  Within
a few weeks I should find time for a more careful review and
testing.
-Kevin