• No results found

CHAPTER 4 ENABLING RELAXED CONSISTENCY TO REDUCE RTCES

4.6. Correctness and Efficiency Analysis

To demonstrate that our methods OBTAINLOCK and REASELOCK are sufficient to cover the activities that users perform within a CES, we identify a set of user actions within a CES and map these actions to events within our tree-based system. This

Figure 40: Promotion across multiple levels is permissible 2 2 2 n o p i j k m n o p i j k m REMOVELOCK(u1, i) u2 u1 u2

mapping is demonstrated in Table 5. Note that if a lock is requested (via the OBTAINLOCK event) and the user already owns the lock, then no server

request/communication is required. It is only when a user attempts to edit a section without having previously edited that section (i.e. the section is not owned by the user) that a request for the lock is required.

Note that these document tree events listed below support exclusive locking, but they also support multiple writers (where a lock request will never fail). In the case where multiple writers are allowed to own a section, care must be taken when the section is deleted, split, or two sections are combined where at least one of the sections to be combined are owned by other users. In these cases, coordination between the users can be enacted such that all users agree upon the action (delete, split, join), or a priority- based scheme could be adopted where a high-priority user may enact the action after the other lower-priority users’ locks are revoked and reestablished. The document tree structure changes would need to be broadcast to all affected users and their locks reestablished after the structural changes have been completed. In the case where operations were performed concurrent to the structural changes, these operations could be transformed and replayed once the structural changes were completed.

Table 5: Mapping User Actions to CES Document Tree Events

User Action CES Document Tree Events

Enter the CES Place user as a reader in the default section of the document Exit the CES Remove the user from the CES and flush the user cache Modify content

within section A

OBTAINLOCK for section A

If not successful, deny the edit Move from

section A to section B

RELEASELOCK on section A

Place user as a reader in section B Delete section A OBTAINLOCK for section A

If successful, remove section A from tree Create section A Create a new node A and insert it into the tree Combine section

A and section B

OBTAINLOCK on section A

OBTAINLOCK on section B

If either fail, release any successfully obtained lock and deny the request

Else merge sections A and B in the tree (removing section B and RELEASELOCK on B)

Split section A into sections A and A’

OBTAINLOCK on section A If not successful, deny the edit

Else create a new node A’ as a sibling of A, move specified content from A into A’

We designed the OBTAINLOCK and RELEASELOCK operations such that the

document tree is accessed only in a top-to-bottom, pipelined fashion; we do this to avoid race conditions. We enforce the policy that nodes must be accessed in a top-down manner such that we only access and modify the tree data structure in the following path:

Acquire a lock for the parent node Acquire a lock for the child node Release the lock for the parent node

This “handshake lock” technique, as employed by [111], ensures that a race condition on concurrent access to the tree data structure is avoided. As a result, our operations may be executed concurrently while maintaining their correctness.

The full presentation of the algorithms appears below in Figure 41 through Figure 43. Note that these algorithms are presented to show intent; the actual implementations feature an iterative/loop-based solution that employs a top-to-bottom, handshake-lock as the paths from the root to the desired nodes are traversed.

Figure 41: The OBTAINLOCK Algorithm OBTAINLOCK(w, ui)

ifw.ownerui

RECURSEOBTAINLOCK (ROOT, w, ui) RECURSEOBTAINLOCK(n, w, ui)

ifn.color = white

then SETLOCK(n, ui, w)

LINKSIBLINGS(n.parent, n, n.parent.firstBlackChild)

else if n ISATOMIC

then RECURSEREMOVELOCK (ROOT, w, ui)

return failure

elseifn.color = grey

then n.greyCount = n.greyCount + 1

RECURSEOBTAINLOCK(NEXTINPATH(n, w), w, ui)

else b = NEXTINPATH(n, w)

a = NEXTINPATH(n, n.originalRequest)

REMOVEFROMSIBLINGLIST(n)

SETLOCK(a, n.owner, n.originalRequest)

n.color = grey

n.greyCount = 2

ifab

then SETLOCK(b, ui, w)

LINKSIBLINGS(n, a, b)

Since the RECURSEOBTAINLOCK traverses from the root down to a leaf (or stops earlier if a white or black node is reached), this algorithm must traverse O(h) nodes, where h equals the height of the document tree. The work involved at each node is O(1) since the work in processing an individual node involves updating references/pointers, coloring, and grey count (integer) values. It is possible upon a lock request failure that the RECURSEREMOVELOCK function will be invoked, but this RECURSEREMOVELOCK (as discussed below) runs in O(h), thus it is not asymptotically greater than the existing O(h) work for the OBTAINLOCK algorithm. Thus the overall cost for the OBTAINLOCK

algorithm is O(h).

Similarly, the RECURSEREMOVELOCK traverses from the root down to a leaf (or

stops earlier if a grey or black node is reached), this algorithm must traverse O(h) nodes, where h equals the height of the document tree. The work involved at each node is O(1)

Figure 42: The REMOVELOCK Algorithm

REMOVELOCK(w, ui)

ifw.owner = ui

then RECURSEREMOVELOCK(ROOT, w, ui)

RECURSEREMOVELOCK(n, w, ui)

ifn.color = black and n.owner = ui then REMOVEFROMSIBLINGLIST(n)

UNSETLOCK(n)

elseifn.color = grey

then n.greyCount = n.greyCount – 1

ifn.greyCount = 1

then a = FINDELIGIBLEPROMOTION(n, w)

SETLOCK(n, a.owner, a.originalRequest)

LINKSIBLINGS(n.parent, n, n.parent.firstBlackChild)

else ifn.greyCount = 0 // removal occurs before delayed promotion

then UNSETLOCK(n)

since the work in processing an individual node involves updating references/pointers, coloring, and grey count (integer) values. Upon promotion, the FINDELIGIBLEPROMOTION function must be called, but it continues the traversal down the tree from the point where the promotion may occur, thus its work is also O(h). Thus the overall cost for the REMOVELOCK algorithm is O(h).

Figure 43: Supporting Functions REMOVEFROMSIBLINGLIST(n)

n.previousSibling.nextSibling = n.nextSibling n.nextSibling.previousSibling = n.previousSibling

ifn.previousSibling ≠ NIL

then n.parent.firstBlackChild = n.nextSibling

n.previousSibling = NIL

n.nextSibling = NIL

FINDELIGIBLEPROMOTION(n, w)

traverse from n to w until black node (a) is found

if a.nextSibling.color = black

thenreturna.nextSibling

else if a.previousSibling.color = black

thenreturna.previousSibling

else returna SETLOCK(w, ui, r) w.color = black w.owner = ui w.originalRequest = r LINKSIBLINGS(n, a, b) n.firstBlackChild = a a.previousSibling = NIL a.nextSibling = b b.previousSibling = a UNSETLOCK(w) w.color = white w.owner = NIL w.originalRequest = NIL

The supporting functions REMOVEFROMSIBLINGLIST, SETLOCK, LINKSIBLINGS, and UNSETLOCK are invoked by the RECURSEREMOVELOCK and RECURSEOBTAINLOCK

functions. We present them here in Figure 43 to show that they all run in O(1) since they only update attributes of the nodes. The supporting function FINDELIGIBLEPROMOTION requires traversing down the path to the desired node to find

the first black node along the path, thus it runs in O(h); but we note that this function is only invoked when a grey count is reduced to 1 when the RECURSERELEASELOCK

function is running; when this occurs, some number of nodes have already been traversed, and the FINDELIGIBLEPROMOTION function must only process the remaining nodes below the reached node whose grey count is now equal to one. Thus the total number of nodes visited in the combination of the RECURSERELEASELOCK and FINDELIGIBLEPROMOTION functions is ≤ h, where h is equal to the height of the

document tree.

It is important to note that nodes within the sub-trees not along the path from the root to the destination – shown as the sub-trees α and β in Figure 44 and as the sub-tree α Figure 45 – are unaffected by the OBTAINLOCK operation. This improves the concurrent operations that are able to be performed on the tree (i.e., pipelining the operations from the top/root of the tree down. This is critical in ensuring that the lock request and release operations may be executed efficiently on the server without significant delay in responding to the clients making the requests.

Further, in the case of demotion for OBTAINLOCK as shown in Figure 45, the only modification to leaves occurs in increasing the grey count along the path from t to v and moving the ownership of u2 to the sibling of the newly-acquired node (w in this

example) when resolving conflict. If OT is adopted at a node, then no demotion is required and all sub-trees within the document (α and β in the preceding figure examples) remain unaffected.

Figure 45: The OBTAINLOCK Operation with Demotion

t α w v OBTAINLOCK(w, u1) t α w v u2 u2 x x u1 Figure 44: The OBTAINLOCK Operation without Demotion

t α w v OBTAINLOCK(w, u1) t α w v β β u1