36 PSpaceTxnGroupMgr

PSpace transaction groups

The PSpaces in a PersistStore are grouped according to whether they need to all be written to the LSS in a single transaction.

This grouping is transient in nature (i.e. it doesn't need to persist). Initially all PSpaces are independent (i.e. each PSpace is in its own group - with only one PSpace member). As transactions are performed, groups are merged as required. For example, when transfering objects between PSpaces it is necessary to merge groups to ensure atomicity of the changes. Without this (i.e. if PSpaces could independently commit their changes), we couldn't (for example) guarantee "conservation of matter" when objects are transferred across PSpace boundaries.

When the DOS writer processes a group (i.e. writes all the dirty objects to disk) it is then able to break the group up into the individual PSpaces again.

If transactions are always performed on individual PSpaces (as is commonly the case) then groups don't need to be merged. This is beneficial because it decouples the processing of PSpaces. More specifically, the DOS writer only needs to lock a single PSpace in order to write the dirty objects to the LSS.

Implementation

class PSpace
{
private:
    // Used to allow PSpaces to be partitioned into groups.  A group must have all the dirty
    // objects written to the LSS in a single transaction.
    PSpace* m_nextInTxnGrp;

    // Used to support an efficient implementation of RetrieveGroup().  Normally false.
    bool m_inGrp;
};

Each PSpace stores a pointer to the next in the group. This makes it easy to traverse a group : Simply walk around until getting back to the start position.

Testing whether two PSpaces belong to the same group : It is necessary to traverse one group and see whether we come across the other PSpace

PSpaceTxnGroupMgr.h

Source: Ceda/cxPersistStore/src/PSpaceTxnGroupMgr.h

PSpaceTxnGroupMgr.cpp

Source: Ceda/cxPersistStore/src/PSpaceTxnGroupMgr.cpp

Conservative two phase locking of PSpaces

Each PSpace is associlated with a CSpace and therefore protected by a single mutex and offers exclusive and shared read access modes.

An IObject can belong to at most one CSpace (and hence PSpace) at a time.

An object in one PSpace cannot pref an object in a different PSpace.

There is a general purpose mechanism for "simultaneously" locking any number of CSpaces. In particular, we have a means to reliably lock any pair of PSpaces in order to transfer objects from one to the other. Transfer of persistent objects can be very fast because there is no affinity between OIDs and PSpaces.

A tree of persistent objects is moved by reparenting. It is important that the original parent and new parent are changed atomically to "conserve matter". When moving objects across PSpace boundaries, we introduce a constraint that the dirty objects from all the PSpaces must be written to the LSS in a single transaction.

The PersistStore can maintain a bit-matrix that keeps track of sets of PSpaces that are tied together - in terms of being written to disk.

Consider PSpaces numbered 1 through 4. Initially the bit matrix is initialised as follows

      |  1   2   3   4
    --------------------
    1 |  1   0   0   0
      |
    2 |  0   1   0   0
      |
    3 |  0   0   1   0
      |
    4 |  0   0   0   1

This corresponds to sets {1}, {2}, {3}, {4}. I.e. dirty objects in each PSpace can be written to disk independently of the other PSpaces.

Consider that exclusive locks are gained on PSpaces 2 and 3. Then the bit matrix becomes

      |  1   2   3   4
    --------------------
    1 |  1   0   0   0
      |
    2 |  0   1   1   0
      |
    3 |  0   1   1   0
      |
    4 |  0   0   0   1

This corresponds to sets {1}, {2, 3}, {4}. Note that PSpaces 2,3 must be processed with a single LSS transaction.

In general, exclusive locking multiple PSpaces causes the associated sets to be merged. Writing a set to disk causes the set to be broken up again, i.e. back into individual PSpaces.

Let M(i,j) represent the above matrix. The following algorithm merges i,j.

void Merge(int i,int j)
{
    int ni = 0;
    int nj = 0;
    int Li[n];
    int Lj[n];
    for (int k=0 ; k < n ; ++k)
    {
        if (M(i,k)) Li[ni++] = k;
        if (M(j,k)) Lj[nj++] = k;
    }

    for (int vi = 0 ; vi < ni ; ++vi)
    {
        int ei = Li[vi];
        for (int vj = 0 ; vj < nj ; ++vj)
        {
            int ej = Lj[vj];
            M(ei,ej) = 1;
            M(ej,ei) = 1;
        }
    }
}