design and implementation of linear hash structures … · design and implementation of linear hash...

39
DESIGN AND IMPLEMENTATION OF LINEAR HASH STRUCTURES IN NESTED TRANSACTIONS ENVIRONMNET ABSTRACT 1. INTRODUCTION

Upload: nguyenhuong

Post on 15-Sep-2018

217 views

Category:

Documents


0 download

TRANSCRIPT

1

DESIGN AND IMPLEMENTATION OF LINEAR HASH STRUCTURES IN NESTEDTRANSACTIONS ENVIRONMNET

Sanjay Kumar Madria Malik Ayed TubaishatCentre for Advanced Information System

School of Applied ScienceNanyang Technological University

[email protected]

School of Computer SciencesUniversiti Sains Malaysia11800 Penang, Malaysia

[email protected]

ABSTRACTIn this paper, we present formalization and an implementation of a linear hashing algorithm in nested transactionenvironment to handle large amount of data with increased concurrency. Nested transactions allow parallel executionof transactions, and handle transaction aborts in our system. First, We formalize the nested transaction model in alinear has structure environment using well- known I/O automaton model. Next, we design a client/server model usinglayered system architecture. We have used object-oriented methodology in our system implementation that helps indesigning and implementing the programming components formalized using I/O automaton model independently. Inour model, buckets are modeled as automaton (later as objects) and linear hash operations are modeled as transactionautomaton (later as methods). These methods correspond to nested transactions and are implemented usingmultithreading paradigm.Keywords: linear hashing, nested transaction, concurrency, object-oriented methodology, multithreading.1. INTRODUCTIONNested transaction processing and concurrency control [1] issues play a major role in providing higher concurrencyand better handling of transaction�s abort and hence have been an important area of research in database systems. Themotivation for using nested transactions is to allow transactions to exploit parallelism that might naturally occur withinthemselves. The benefits of parallel execution of transactions include performance improvements and better controlover recovery.

Attention is being given to the designing of concurrency control algorithms which take advantage of theknowledge of particular data structures and the semantics of operations such as insert, delete, find, etc. to improveavailability and expedite accesses. Data structures that have been studied with above in mind are B-trees ([2], [3]) andhashing techniques ([4], [5], [6], [7]). Hashing is one of many addressing techniques used to find a record based on itsunique key value. When a record is to be updated, the system takes the value of the key and performs some type ofcalculations to derive a target address for the record. Among various hashing techniques, Linear Hashing ([8], [9]) isinterested because of its simplicity, ability to handle large amount of data with increased concurrency, and very fast inretrieving data.

Current database applications are increasingly using object-oriented techniques which necessitate the revaluationof the traditional concurrency control methods that are in use for more efficiency. When combining databasefunctionality with object-oriented concepts, object-oriented databases become the ideal repository of the informationthat is shared by multiple users and multiple applications on different platforms. Nested transaction is one of theimportant concepts associated with object-oriented database [10]. In object-oriented databases, if the execution ofmethods is seen as a transaction then the execution of methods generates a hierarchy that correspond to nestedtransactions. While it is essential that object-oriented database systems must support multiple concurrent users, to our

2

knowledge, no implementation of nested transactions accessing linear hash structure has been available in literature. In[11], B-tree algorithm in nested transaction environment has been presented to show its correctness but noimplementation issues have been discussed. In one of the author's earlier work [12], linear hashing using nestedtransactions has been studied only with the aim of proving the correctness of the algorithm using I/O automatonmodel [13].

In this paper, we first formalize the linear hash structures algorithm [8] in nested transaction environment usingI/O automaton model [13]. Further, we present a system implementation of a nested transaction version of the linearhash structure algorithm using object-oriented concepts and multithreading paradigm. In our formalization ofalgorithm using I/O automaton model, we model buckets as automata (later modeled as objects) and linear hashoperations find, insert, delete, split, and merge as transaction automata (later modeled as methods). The formalizationusing I/O automata helps in describing and understanding the complex system such as ours.

The transaction automata while implementing are modeled as methods which correspond to nested transactions.These methods are implemented as multithreads. Multithreading is a programming paradigm that allows theapplication processes to run concurrently. When the process performs multiple tasks at the same time, multithreadingsplit themselves into separate threads that run concurrently and independently. Each thread performs one atomicaction. The threads execute individually and are unaware of the other threads in a process. Thus, multithreading can beused very efficiently to implement the behaviors of nested transactions. Finally, We have designed and implementedour system using layered system architecture in a client/server environment, which allows more flexibility in term ofdecomposing of application programs whose modules may be designed and implemented independently.

Rest of the paper is organized as follows: Section 2 outlines some preliminary discussion on nested transactionsand an overview of linear hash structures, concurrency control, and locking. Section 3 discusses the linear hashstructure model in nested transaction environment. In section 4, we discuss the layered system architecture. Section 5explains the object-oriented design of our model. Section 6 describes the implementation of nested transactions usingmultithreading. We give a snapshot of our model in section 7. We conclude in section 8.2. PRELIMINARIES2.1 Nested TransactionsA nested transaction ([1], [13], [14], [15], [16]) has a hierarchical structure and is useful in decomposing activities intosmaller units. Each nested transaction consists of primitive operations (i.e., reads and writes) or subtransactions whichare also nested transactions. A transaction can request any number of children, either sequentially or concurrently. Thisparent-child relationship forms a nested transaction tree. The functions of internal nodes of the transaction tree are tocreate and manage subtransactions and not to access data directly. The access transactions are situated at a leaf node ofthe transaction tree. To other transactions only top-level transaction is visible and appears as a normal atomictransaction. Internally the subtransactions are run concurrently and their actions are synchronized by an internalconcurrency control mechanism. Nested transactions improve concurrency, since it is possible to run severalsubtransactions concurrently. Furthermore, it is possible to deal with a subtransaction�s failure without having to rollback the entire transaction. Concurrent transactions can be guaranteed to be serializable if, in any transaction, all locksprecede all unlocks. This is called two-phase locking protocol [1].

The commit of a transaction makes its results accessible only to the parent transaction. The subtransaction willfinally commit only if it is committed locally and all its ancestors up to the root have been committed. If asubtransaction at any level of nesting is rolled back, all of its subtransactions are rolled back too, independent of their

3

local commit status. All the changes made by a subtransaction become visible to the parent transaction on thesubtransaction�s commit. All the objects held by a parent transaction can be made accessible to its subtransactions.When a subtransaction commits, the changes are made visible to other related transactions. The parents acquire thelocks and pass them to the children as they spawn child transactions. A child may acquire new locks on its own. Thelocks are kept until the end of the subtransaction (which enables the isolation of parallel transactions). After thetermination of the subtransactions, their locks are inherited by the parent transaction and are released when the top-level transaction terminates.2.2 Overview of Linear HashingIn a linear hash structure [8], there are primary buckets where each holds some constant number b of records. Thefunction h0 : k Æ {0, 1, ..., N -1} is initially used to load the file. There exists a sequence of functions h1, h2, ..., hi, ...,such that for any key value k, either hi(k) = hi-1(k) or hi(k) = hi-1(k) + 2i-1N, where N is the initial number of buckets.The hash functions change as the hash structure grows or shrinks. The operations defined on a linear hash structure arefind, insert, delete, split, and merge.

A variable level is used to determine the appropriate hash function for find, insert, or delete operations. A pointercalled next is used to point to the bucket to be split or points to the bucket involved in the merge operation. Level andnext are both initially set to 0, and together they referred to as root variable. Each bucket keeps an additional fieldlocal-level that specifies the hash function appropriate to that bucket. The private variable lev keeps the value of levelat the time root variable is read. To access a bucket, the process checks whether lev value matches that bucket�s local-level, and if not, it increments lev value and recalculates the address hlev(key) until a match is found. This operation iscalled rehashing. A process in its search phase behaves as follows: the root variable is read and its value determines thehash function to be used initially. The bucket and hash function are updated as follows:

lev = levelbucket no. Å hlev(key)if bucket no. < next then{

lev Å lev + 1bucket no. Å hlev(key)

}where hlev(key) = key mod (2lev * N)

Attempting to insert into a full primary bucket leads to collision. The problem of collision is handled by creating achain of overflow bucket associated with that particular bucket address. If any bucket overflows, the bucket pointed bythe pointer next will be split by moving some keys from its original bucket to a new primary bucket to be appended atthe end of the hash file. The split operation is applied cyclically. Split operation increases the address space of primarybuckets in order to reduce the accumulation of overflow chains. Since the bucket to split is chosen in round-robinfashion, eventually all buckets are split, thereby redistributing the data entries in overflow chains before the chains getto be more than one or two pages long. All bucket chains with address less than the value of next, and buckets withaddresses greater than or equal to 2levelN have local-level values equal to level + 1. All of the buckets in between havelocal-level = level, which indicates that they have not efficiently been split in the current round of restructuring.

The next pointer travels from 0 to 2j-1*N with hash function hj. After the split, the next is updated as follows:next Å (next + 1) mod (2level * N)if next = 0 then level Å level + 1

4

When a primary bucket becomes empty, a merge operation is called. Here, the next bucket will be merged with itspartner (where partner = next + 2level * N). That is, keys will be moved from the partner bucket to the next bucket.After merge the following changes will be made:

if next = 0 then level Å level - 1next Å (next - 1) mod (2level * N)

2.3 Concurrency and Locking in Linear HashingIn linear hashing algorithm [8], the find operation can be performed concurrently with find, insert, delete, and splitoperations. Insert and delete operations may operate in parallel if they are accessing different bucket chains. A splitmay be performed in parallel with insert and delete operations that are not accessing the particular chain being split.No concurrency is possible between a merge and processes doing find, insert, or delete operations. That is, theseprocesses can neither access the two buckets being merged nor they can read the values of level and next whilemerging process is using them. At most one restructuring operation either merge or split is executable at a time.

Locking protocol in [8] uses three types of locks as shown in Table 1. The primary bucket, and all its overflowbuckets are locked as a unit. The find algorithm uses lock-coupled read-lock on the root variable and holds the lockuntil a read-lock is placed on the bucket. Lock-coupling provides the particular flow of locking in which nextcomponent is locked before releasing the lock on the current component. The insert and delete processes hold read-lock on root variable and selective-lock on buckets with lock-coupling. The split operation uses selective-lock on rootvariable as well as on the buckets. Exclusive-locks are used on root variable and on both the buckets involved in mergewhen old overflow buckets are deallocated. If rehashing is required, a lock is placed on the subsequent bucket beforethe lock is released on the current bucket. The read-lock on root variable held by the searching process prevents amerge from decreasing the size of the address space (by updating level and next) during the initial bucket access.

Lock Request Existing LocksRead-lock Selective-lock Exclusive-lock

Read-lock yes yes noSelective-lock yes no noExclusive-lock no no no

3. LINEAR HASH STRUCTURE IN NESTED TRANSACTION ENVIRONMENTOur model of the linear hash structure algorithm [8] in the nested transaction environment is as follows. The nestedtransaction tree of our model is shown in Figure 1. In the nested transaction tree, we have a level of transactionmanagers. Each transaction manager (TM) corresponds to an operation. The agent, as shown in Figure 1, will send theuser-transaction to the appropriate operation (find, insert, or delete). The requested operation then will pass user-transaction to the appropriate TM. The TMs can be thought of as separate, concurrently running processes. Each TMhandles transaction-related processing requests like when transactions are created, when they are to be committed oraborted. The find operation is performed logically by an equivalent read-TM whereas insert and delete are performedby write-TM. The search-read (for find) and search-write (for insert/delete) access subtransactions are situated at theleaf level. Search-read accesses read the keys present in a bucket whereas search-write accesses physically modify ordelete the key present in a bucket. For split and merge, a split-TM and a merge-TM are provided respectively. During

5

an insert operation, an overflow means a split is required whereas a delete operation may account for an underflow,which signals the need for merging. If the root transaction T intercepts an "overflow" message, it triggers a split-TMwhereas if it intercepts an "underflow" message, it invokes a merge-TM. These TMs invoke access subtransactions ofthe type split-write and merge-write respectively, to physically accomplish the split and merge operations.

On the system building side, when a user requests for an operation to be performed, the agent initiates thecorresponding TM. TM can further initiate various subtransactions, which are passed to the appropriate bucketcontaining the needed data. TM receives the data from the subtransaction and processes the collected data, and mayinitiate some more subtransactions if required. The scheduler as shown in Figure 1 determines the order in whichsubtransactions are to be executed. The scheduler receives two types of request; to create a transaction (REQUEST-CREATE) or request for committing a transaction (REQUEST-COMMIT). In return, scheduler responds by creating,committing, or aborting the transaction (Figure 2). The scheduler will control operations of the subtransactions, makesa decision about the completion of subtransaction and reports back to their parent (a report-commit or report-abort),and informs objects the fate of transactions. Transactions situated at one level above the leaf level hold lock on the root

ction

BMs

DELETEINSERTFIND

6

variable (i.e., level and next) that is managed by the scheduler. More details can be found in one of the author's earlierwork [12].

To synchronize the leaf node of the transaction tree accessing the buckets we use a bucket manager (BM). In ourmodel, each BM handles one bucket and all its overflow buckets. A BM provides the status and the lock type of eachsubtransaction accessing the bucket. BM receives the subtransactions, and determines the appropriate time to executeeach one, and whether to execute one subtransaction or more depending on each subtransaction�s lock mode. Further,BM provides the lock management on keys present in a bucket and in its overflow buckets to control accessing thekeys within the same bucket chain. Since a key k in the linear hash structure is a resilient object (A resilient object hasno capabilities for managing concurrency, rather it assumes that concurrency control is handled externally by bucketmanagers. Also, a resilient object has additional capabilities to undo operations of transactions that it discovers haveaborted), different transactions may see different versions of the same key. However, there is only one version of eachlinear hash structure. Therefore, at any time, the set of all keys contained in a linear hash structure is partitioned amongthe buckets, and each key in a bucket has a set of versions associated with it. When keys move from a bucket toanother as a result of split or merge, all its versions move with it.

In our model, transactions situated at one level above the leaf level also hold locks on the root variable beingprovided by the scheduler. The value of the root variable would be returned to the scheduler when a lock is granted toa transaction. Lock management on the root variable and on buckets is done using Moss�s two phase locking algorithm[1] and locking algorithm using lock-coupling protocols given in [8]. While the lock on the keys is done usingread/write lock algorithm.

According to the implementation technique, each access subtransaction can lock a single data object (a bucketincluding all its overflow buckets in our case) only and after its commit, it has to release the lock. However, thecompression process in the linear hash structure algorithm needs two buckets to be locked at a time before accessingany of the buckets. Therefore, the implementation of compression process is difficult with the existing technique ofrepresenting each object as an automaton. To implement a merge operation, a link manager (LM) automaton for thetwo merging buckets is introduced which provides locks on the buckets simultaneously. A link manager (LM)automaton is created dynamically by combining the two BM automata to be merged. That is, initially system does notcontain any LM automata. Since a merge operation may not be initiated at all, and therefore, the system requires nopre-existing LM automata. The components in a state of a LM automaton are derived from the correspondingcomponents of the two linked BM automata. The difference between a BM and a LM automaton is that former allowsa subtransaction to access a single bucket while the later allows a subtransaction to modify the two related bucketssimultaneously.

The lock-coupling technique is also difficult to model using I/O automaton model where each object automatonhas only two actions; one requesting the action (similar to the invocation of an operation) and the other returning aresult (representing the response to the invocation). There is no intermediate returned action before finally respondingto the request. Therefore, it is not possible for a subtransaction to inform its parent about acquiring of the lock on thebucket before its commit. In case the parent gets the information early, it can initiate the process to release the lockacquired on the previously accessed bucket as required by the lock-coupling technique of [8]. Therefore, to implementthe lock-coupling technique in a nested transaction environment, a minor modification is incorporated. We release(drop) the lock held on the penultimate bucket instead of the previously accessed bucket after a lock is acquired on thenext searched bucket with the help of another subtransaction, called �release access�. This is also because in a nested

7

transaction model, a subtransaction passes its lock to its parent subtransaction on commit. In our case, we want to dropthe lock (i.e., not to be retained by its parent) held by the search-access subtransaction on the penultimate bucket oncethe key k is not found in that bucket.3.1 LINEAR HASH SYSTEM AND AUTOMATAThe linear hash system (LH) described before can be framed as a system B = (tB, parentB, OB, VB) where tB is the setof transaction automata described above, parentB : tB ® tB is a mapping which describes the parent-child relationship.OB is the set of buckets and VB is the set of returned key values.Transaction AutomataEach transaction is modeled as an I/O automaton that is specified with the help of the following operations [13]:Input operations: CREATE(T)

REPORT-COMMIT(T',v), where T' Î children (T) and v is the return value REPORT-ABORT(T') where T' Î children (T)

Output operations: REQUEST-CREATE(T') where T' Î children (T) REQUEST-COMMIT(T,v) where v is the return value

The CREATE operation wakes up the transaction. The REQUEST-CREATE is a request by T to create particularchild transaction. The REPORT-COMMIT operation reports to T the successful completion of one of its children andreturns a value depending upon the operation performed. The REPORT-ABORT operation reports to T theunsuccessful completion of one of its children, without returning any value. The REQUEST-CREATE is a request byT to create a particular child transaction.Generic Object AutomataThe generic object automata [13] serves as the specifications of the concurrent behavior of the operations on the dataobjects. The operation for each object are the CREATE, REQUEST-COMMIT operations for all the correspondingaccess transactions. The CREATE operation is an invocation of an access to the object, while theREQUEST-COMMIT is a return of value in response to such an invocation. It has two additional input operationsINFORM-COMMIT and INFORM-ABORT for every transaction which inform about the fate of each transaction. Italso has an internal operation OBTAIN-LOCKTYPE(T) for each T, an access to b. Thus, an object b is modeled as anautomaton with the following operations :Input Operation: CREATE(T) INFORM-COMMIT-AT-(b) OF T, T ¹ T0 , b Î H INFORM-ABORT-AT-(b) OF T, b Î HOutput Operation: REQUEST-COMMIT(T,v) where T Î accesses(b)Internal Operation: OBTAIN-LOCKTYPE(T) for each T, an access to bGeneric Scheduler AutomatonThe generic scheduler [13] is modeled as an I/O automaton. It passes request for the creation of subtransactions to theappropriate recipient, makes decision about the completion of children and reports back to their parents, and informsobjects of the fate of transactions. The operations are as follows:Input operation: REQUEST-CREATE(T) REQUEST-COMMIT(T,v)Output operations: CREATE(T)

8

COMMIT(T) ABORT(T) REPORT-COMMIT(T,v) REPORT-ABORT(T) INFORM-COMMIT-AT-(b) OF T, b Î H INFORM-ABORT-AT-(b) OF T, b Î H

The REQUEST-CREATE and REQUEST-COMMIT inputs are identified with the corresponding outputoperations of transactions and object automata, and corresponding for the CREATE, REPORT-COMMIT andREPORT-ABORT output operations. The COMMIT and ABORT operations are internal, marking the point of timewhere the decision on the fate of the transaction is irreversible. The COMMIT(T) and ABORT(T) are called returnoperations. The INFORM-COMMIT and INFORM-ABORT informs the data object automaton about the fate oftransactions, for details see [13].

Each state of an automaton is defined in terms of a subset of the following components as shown in Table 2:No. I/O Component Specification1 created* Is a Boolean variable used to initiate a CREATE operation.2 p* Is a Boolean variable which controls the initiation of release access

subtransactions.3 i* Is a Boolean variable to control the initiation of release, search-read or

search-write access subtransactions.4 data Holds the return value. Initially, data is undefined.5 phase Can be of type "idle", "searching", "finished", "merge" or "split".6 nextBucket Keeps track of the hash function that gives the address of the nextBucket in

the next bucket chain to be searched during the search operation for thedesired key k starting from the first bucket chain of the linear hashstructure.

7 rv Contains the root variables next and level [8].8 lev Keeps the level value.9 count Is a control variable. Initially, it is zero.10 root (T) Is the initial hash function associated with the transaction T.11 key (T) Is the key associated with the transaction T.12 read-lockholders Is the set of all the transactions holding read-locks on the root variable rv.

Initially, read- lockholders = {T0}.13 selective-lockholders Is the set of all the transactions holding selective-locks on the root variable

rv. Initially, selective-lockholders = {T0}.14 exclusive-lockholders Is the set of all the transactions holding exclusive-locks on the root variable

rv. Initially, exclusive-lockholders = {T0}.15 putChain Gives the address of the new bucket in case of a split. Initially, putChain is

undefined.16 release-bucket_1 and Give the addresses of the previously accessed bucket and the penultimate

9

release-bucket_2 accessed bucket, respectively. Initially, they are undefined.17 merge-1 and merge-2 Give the addresses of the two buckets to be merged. Initially, both are

undefined.18 read-committed and

write-committedAre the sets of committed search-read and search-write accesssubtransactions, respectively. Initially, both the sets are empty.

19 r(1) Returns value of the type "found", "not found", "underflow", "overflow",and "merge", and r(2) either returns the address of the next bucket to besearched or it returns the value associated with the key. Initially, r(1) andr(2) are undefined.

20 H Denotes the linear hash structure.* All the Boolean variables are initialized to false.

Read-TMThe purpose of a read-TM is to initiate a read access to read one of the keys k Î H. The read-TM invokes a read accessto a bucket. There are bucket managers for each bucket b1,... ,bm Î H. The read-TM has a lev variable initially assignedthe value level read from the root variable rv. Let bm be the bucket whose local-level matches with the lev value of theread-TM, i.e., the bucket bm holds the key k if key k Î H. Accesses to buckets which led to bm are non-decisiveoperations and the read access to bm is a decisive operation.

A state of the read-TM uses the components data, phase, nextBucket, created, p, rv, lev and count to define preand post conditions. The components not appearing in postconditions with respect to state s remain same in pre andpostconditions. The scheduler maintains and provides read-lock on the root variable, and also provides an initial hashfunction.· CREATE(T) with root(T) = "initial hash function", key(T) = k, and rv = (level, next)Precondition: s'.created = false read-lockholders(rv) Í ancestors(T)Postcondition: s.created = true s.phase = "searching" key(T) = k s.data = nil read-lockholders(rv) = read-lockholders(rv) È {T} rv = (level,next) lev(T) = level { $ a sequence h1, h2,....hj ' h1 = root(T) hi = hlev(T) + i (k) " i £ j hi < next " i < j hj ³ next s.nextbucket = hj }

10

· REQUEST-CREATE(T') with type (T') = search-read and key(T') = kPrecondition: T' Î access (s'.nextbucket) s'.phase = "searching" s'.created = true key(T') = k lev(T') = lev(T) s'.i = false· REPORT-COMMIT(T',v) with type(T') = search-read and v = (r(1),r(2))Precondition: s'.phase = "searching"Postcondition: If T Î read-lockholders(rv) then read-lockholders(rv) = read-lockholders(rv) - T È {parent(T)} If r(1) = "found" then { s.phase = "finished", s.data = r(2) If s.count = 1 then s.p = true } If r(1) = "not found" then { s.release-bucket_2 = s'.release-bucket_1 s.release-bucket_1 = s'.nextbucket s.nextbucket = r(2) lev(T) = lev(T) + 1 s.count = s.count + 1 If s.count ³ 2 then { s.p = true , s.i = true } } s.read-committed = s'.read-committed È {T'}· REQUEST-CREATE(T') with type (T') = release accessPrecondition: s'.created = true

s'.phase = "searching" or "finished" s'.p = true s'.i = true s'.release-bucket-2 ¹ nil and r(1) ¹ "found" T' Î access (s'.release-bucket_2) last T" Î access (s'.release-bucket_2) Î s'.read-committedPrecondition: s'.created = true s'.phase = "searching" or "finished" s'.p = true s'.i = true s'.release-bucket_2 = nil or r(1) = "found" T' Î access (s'.release-bucket_1) last T" Î access (s'.release-bucket_1) Î s'.read-committed· REPORT-COMMIT(T',v) with type (T') = release accessPostcondition: s.i = false If r(1) = "found" then s.p = false· REPORT-ABORT(T')Postcondition: no change

11

· REQUEST-COMMIT(T,v)Precondition: s'.phase = "finished" s'.p = false v = s'.dataPostcondition: s.phase = "idle"Write-TMThe purpose of a write-TM is to store a new key and its value or to change the value of the existing keys in H. Eachwrite-TM has key(T) and data(T) as parameters. To delete a key, we set data(T) = nil. A write-TM invokes a search-write access where key(T) is stored or updated in the bucket b in a bucket chain. All the variables used to define preand post conditions are as in the case of read-TM. Hence, details are omitted here.Split-TMA split-TM is used to perform a split operation on receiving an overflow message. The root transaction T invokes asplit-TM. The split operation results in a splitting of a bucket into two new buckets. The bucket at the new address iswritten using the hash function before the new version replaces the target bucket. Its local-level indicates that thebucket has split and the new bucket has been incorporated into the hash structure. All the components used to definepre and post conditions are as before.· CREATE(T)Precondition: s'.created = false selective-lockholders (rv) Í ancestors(T)Postcondition: s.created = true s.phase = "split" selective-lockholders (rv) = selective-lockholders (rv) È {T} rv = (level,next) s.nextbucket = next· REQUEST-CREATE(T') with type (T') = split-writePrecondition: T' Î access (s'.nextbucket) s '.phase = "split" keys Î s'.keyset putchain(T') = next + N*2**level· REPORT-COMMIT(T',v) with type (T') = split-writePrecondition: s'.phase = "split" r(1) = overPostcondition: s.phase = "finished" next = (next + 1) MOD (N*2**level) if next = 0 then level = level + 1 selective-lockholders (rv) = selective-lockholders (rv) - {T} È {parent(T)}· REPORT-ABORT(T')Postcondition: no change· REQUEST-COMMIT(T,v)Precondition: s'.phase = "finished"

12

v = nilPostcondition: s.phase = "idle"Merge-TMWhen the root transaction T intercepts the underflow message, it triggers a merge-TM. The purpose of merge-TM is tomerge the two buckets. The variable next is updated after the merge operation. All the state variables used to define preand post conditions are as defined earlier.· CREATE(T)Precondition: s'.created = false exclusive-lockholders (rv) Í ancestors(T)Postcondition: s.created = true s.phase = "merge" exclusive-lockholders (rv) = exclusive-lockholders (rv) È {T} next = (next - 1) MOD (N*2**level) If next = 0 then level = level + 1 s.merge-1 = next s.merge-2 = next + N*2**level· REQUEST-CREATE(T') with type (T') = merge-writePrecondition: T' Î access (s'.merge-2, s'.merge-1) s'.phase = "merge" keys Î s'.keyset· REPORT-COMMIT(T',v) with type (T') = merge-writePrecondition: r(1) = "merge" next = next - 1Postcondition: s.phase = "finished" exclusive-lockholders (rv) = exclusive-lockholders (rv) - {T} È {parent(T)}· REPORT-ABORT(T')Postcondition: no change· REQUEST-COMMIT(T,v)Precondition: s'.phase = "finished" v = nilPostcondition: s.phase = "idle"3.2 BUCKET MANAGER AUTOMATONIn our system, we have a bucket manager for each bucket b Î H including all its overflow buckets. Each bucketmanager (BM) also maintains the keys and their associated values. We have locks on the buckets as well as on thekeys. Therefore, both buckets and keys are objects in system B. Each bucket (including all its overflow buckets) isrepresented by an I/O automaton but keys in the buckets are managed by the bucket manager automaton. We considerkeys as the user-visible objects and buckets as user-invisible objects.

The accesses to a bucket b are of the type search-write, search-read, split-write and release access. These accessesinteract with an automaton via the scheduler. A state variable called created contains accesses to b that has beeninitiated. Variable run contains accesses to the bucket b on whose behalf BM(b) has output REQUEST-COMMITs.

13

Variable lev is the local level value of b, keySet stores the keys that are in b, map is a variable which maps transactionsto contents(b), and mapSize maps the transactions to the number of non-nil keys in b. r(1) and r(2) are the returnedvalues. Other variables are as defined before. BM(b) has the following operations:· CREATE(T) with key(T) = k and lev(T) = levelPrecondition: T Î accesses (b)Postcondition: s.created = s'.created È {T}· OBTAIN-LOCKTYPE(T) where type (T) = search-readPrecondition: T Î s'.created - s'.run s'.exclusive-lockholders (b) Í ancestors (T)Postcondition: s.read-lockholders (b) = s'.read-lockholders (b) È {T}· OBTAIN-LOCKTYPE(T) where type (T) = search-writePrecondition: T Î s'.created - s'.run s'.selective-lockholders (b) È s'.exclusive-lockholders (b) Í ancestors (T)Postcondition: s.selective-lockholders (b) = s'.selective-lockholders (b) È {T}· OBTAIN-LOCKTYPE(T) where type (T) = split-writePrecondition: T Î s'.created - s'.run s'.selective-lockholders (b) È s'.exclusive-lockholders (b) Í ancestors(T)Postcondition: s.selective-lockholders (b) = s'.selective-lockholders(b) È {T}· REQUEST-COMMIT(T,v) with type(T) = search-read, key(T) = k, and v = (r(1),r(2))Precondition: T Î s'.created - s'.run k Î s'.keySet T Î s'.read-lockholders (b) s'.write-lockholders (k) Í ancestor (T) If s'.lev = lev(T) then { r(1) = "found" r(2) = s'.map ( least (s'.write-lockholders, k)) }Postcondition: s.read-lockholders(k) = s'.read-lockholders(k) È {T} s.run = s'.run È {T}· REQUEST-COMMIT(T,v) with type (T) = search-read and key (T) = k, and v = (r(1),r(2))Precondition: T Î s'.created - s'.run T Î s'.read-lockholders (b) If s'.lev ¹ lev (T), i.e., k Ï s'.keySet then r(1) = "not found" r(2) = +

Postcondition: s.run = s'.run È {T}· REQUEST-COMMIT(T,v) with type (T) = search-write, key (T) = k, data (T) = d and v = (r(1),r(2))Precondition: b is the bucket such that k Î s'.keySet T Î s'.created - s'.run T Î s'.selective-lockholders (b) s'.write-lockholders (k) È s'.read-lockholders (k) Í ancestors (T)

14

If s'.mapSize (T) = maxsize then r (1) = "found and overflow" else r (1) = "found" r (2) = nilPostcondition: if s.map (T,k) = nil then s.mapSize (T) = s'.mapSize (T) + 1 s.write-lockholders (k) = s'.write-lockholders (k) È {T} s.map (T) = s'.map (least (s'.write-lockholders, k)) s.map (T,k) = d s.run = s'.run È {T}· REQUEST-COMMIT(T,v) with type (T) = search-write, key (T) = k, data (T) = nil and v = (r(1),r(2))Precondition: b is the bucket where k Î s'.keySet T Î s'.created - s'.run T Î s'.selective-lockholders (b) s'.write-lockholders (k) È s'.read-lockholders (k) Í ancestors (T)Postcondition: s.write-lockholders (k) = s'.write-lockholders (k) È {T} s.mapSize (T) = s'.mapSize (T) - 1 If s.mapSize (T) = nil then r(1) = "found and underflow" else r(1) = "found" r (2) = nil s.map (T) = s'.map ( least ( s'.write-lockholders, k)) s.map (T,k) = nil s.run = s'.run È {T}· REQUEST-COMMIT(T,v) with type(T) = search-write, , key (T) = k, data (T) = nil and v = (r(1),r(2))Precondition: b is an intermediate bucket, i.e., k Ï s'.keySet s'.lev ¹ lev(T) r(2) = r(1) = "not found" T Î s'.selective-lockholders (b)· REQUEST-COMMIT(T,v) with type (T) = release accessPrecondition: b is the intermediate bucket parent (T) Î s'.selective-lockholders (b) or parent (T) Î s'.read-lockholders (b)Postcondition: if parent (T) Î s'.selective-lockholders (b) then s.selective-lockholders (b) = s'.selective-lockholders (b) - {parent(T)} If parent(T) Î s'.read-lockholders(b) then s.read-lockholders (b) = s'.read-lockholders (b) - {parent(T)}· REQUEST-COMMIT(T,v) with type (T) = split-writeIn the postconditions, a BM for a new bucket bí is constructed with initial states as . .keySet is the initial keySetof bucket bí which contains the keys on the basis of the hash function based on lev (local-level) value of bucket b.

.created and .run get values from s'.created and s'.run for transactions those access keys in .keySetPrecondition: T Î accesses (b) T Î s'.created - s'.run

15

b is the bucket to be split keys Î s'.keySet T Î s'.selective-lockholders(b) putChain (T) = next + N*2**levelPostcondition: .map (T',k') = .map (T',k') for all .keySet and for all T' .mapSize (T') = number of non-nil keys in .map (T') for all T' .run = .run È {T} s .lev = .lev + 1 The following are the changes in a state s of the bucket manager BM for the bucket b. s.keySet = s'.keySet - .keySet s.created = s'.created " T Î s.keySet, s.run = s'.run " T Î s.keySet s.map (T',k') = s'.map (T',k') for all k' Î s.keySet s.mapSize (T') = number of non-nil keys in s.map(T',k) for all T' s.lev = s'.lev + 1 r(1) = over· INFORM-COMMIT-AT-(b) OF TPrecondition: b is the bucket containing key k or is a split or intermediate bucketPostcondition: if TÎ s'.selective-lockholders(b) then s.selective-lockholders (b) = s'.selective-lockholders (b) - {T} È{parent(T)} If T Î s'.read-lockholders (b) then s.read-lockholders (b) = s'.read-lockholders (b) - {T} È {parent(T)} If T Î s'.write-lock (k) then { s.write-lock (k) = s'.write-lock (k) - {T} È {parent (T)} s.map (parent (T), k)) = s'.map (T,k) } If T Î s'.read-lockholders (k) then s.read-lockholders (k) = s'.read-lockholders (k) - {T} È {parent (T)}· INFORM-ABORT-AT-(b) OF TPrecondition: b is the bucket containing the key k or is a split or intermediate bucketPostcondition: s.selective-lockholders (b) = s'.selective-lockholders (b) - descendants (T) s.read-lockholders (b) = s'.read-lockholders (b) - descendants (T) s.write-lockholders (k) = s'.write-lockholders (k) - descendants (T) s.read-lockholders (k) = s'.read-lockholders (k) - descendants (T) s.map (U) = s'.map (U) for all U Î s.write-lockholders (k)3.3. LINK MANAGER AUTOMATONThe merge-write subtransactions access the dynamic link manager automaton (LM) corresponding to the two bucketmanager automata to be merged. The states of the link manager automaton are a crossproduct of the states of the tworespective bucket manager automata. The assertion s.created = sí.created È {T}, where s and sí are the states of thelink manager automata, has the following interpretation: If s = ( ) and sí = ( �, �) then .created =

�.created È {T} and .created = �.created È {T}. Other components in a state of a link manager automatonare derived similarly. All the state components used are as defined earlier. Accesses (b,bí) is the set of transactions

16

accessing the link manager automaton of buckets b and bí, and exclusive-lockholders (b,bí) is the set of transactionsholding an exclusive-lock on the link manager automaton of buckets b and bí.· CREATE(T)Precondition: T Î accesses(b,b')Postcondition: s.created = s'.created È {T}· OBTAIN-LOCKTYPE(T) where type(T) = merge-writePrecondition: T Î s'.created s'.exclusive-lockholders (b,b') È ancestors (T)Postcondition: s.exclusive-lockholders (b,b') = s'.exclusive-lockholders (b,b') È (T)· REQUEST-COMMIT(T,v) with type(T) = merge-write and T

Î

Î accesses(b,b')In the postconditions, and are the states of the bucket manager automata for b and bí, respectively afterREQUEST-COMMIT.Precondition: T Î s'.created - s'.run keys Î s'.keySet T Î s'.exclusive-lockholders (b,b')Postcondition: s.run = s'.run È {T} changes to the bucket manager BM for bucket b s1.keySet = s'1.keySet È s'2.keySet s1.created = s'1.created È s'2.created s1 .map (T',k') = s'1.map (T',k') for all keys k' in b s1.map (T',k") = s'2.map (T',k") for all keys k'' in b' s1.mapSize = s'1.mapSize È s'2.mapSize s1.run = s'1.run È s'2.run s1.exclusive-lockholders (b) = s1.exclusive-lockholders (b) È {T} r(1) = "merge" Changes at the deallocated bucket All the variables in the state s will be nil.· INFORM-COMMIT-AT- (b,b') OF TPrecondition: (b,b') is the pair of buckets mergedPostcondition: s.exclusive-lockholders (b,b') = s'.exclusive-lockholders (b,b') - T È {parent (T)}· INFORM-ABORT-AT- (b,b') OF TPrecondition: (b,b') is the bucket pairPostcondition: s.exclusive-lockholders (b,b') = s'.exclusive-lockholders (b,b') - descendants (T) s.read-lockholders (k) = s'.read-lockholders (k) - descendants (T) for all k in (b,b') s.write-lockholders (k) = s'.write-lockholders (k) - descendants (T) for all k in (b,b')4. LAYERED SYSTEM ARCHITECTUREIn this section, we discuss the layered system architecture of our model discussed in the previous section. Our systemdesign is based on client/server model as shown in Figure 2. Each module in the layered system is designed andimplemented separately. The communications among various components in our system are done via messages.

17

The architecture of our model consists of six layers distributed between the clients and the server. Each client sitehas one layer called user interface (UI) layer. The other layers are implemented at the server site. These layers areordered as operations layer, transaction manager (TM) layer, scheduler layer, bucket manager (BM) layer, and finallyfile manager (FM) layer respectively. All layers at the client and server site work in chronological order to achieve thetransaction�s computation.

The purpose of this layering system is to partition transaction into nested transactions. At each level,subtransactions can operate concurrently without leading to an inconsistency and hence a higher concurrency can beachieved in our system. A very important feature of our layered system architecture is that if a subtransaction fails atany level; the parent at a higher layer level can initiate another subtransaction without aborting the entire transaction.

¾ User Interface (UI) LayerThis is the only layer that has been implemented at the client site. It works as an interface between users and the server.UI layer�s task is to receive an operation request from the user and pass it to the operations layer at the server. In UIlayer, two components exist. They are user interface operations (i.e., find, insert, and delete) and the agent. When theuser initiates one of the operations, the agent will pass the message, containing the key requested by the user, to theappropriate operation at the server site. At the end of computations at the server site, the agent will receive a messagefrom the server site, which could be either "record is found", "record does not exist", or confirmation messages.

merge-TM

. . . .

Agent

Find Insert Delete

Agent

Find Insert Delete

Agent

Find Delete

DELETEINSERTFIND

18

¾ Operations LayerThis layer is considered to be the main layer in the server as it creates the transactions, depending on the operationrequired, and pass them to the appropriate TMs in the next layer. It contains find, insert, and delete operations. Theseoperations will receive different messages from the TM layer and in turn will pass an appropriate message to the clientas an indication of the operation�s completion. In case the message received from the TM layer is an �overflow� or�underflow� as a result for insert or delete, split and merge will be called. If the message is �overflow�, insertoperation will initiate split-TM to complete the operation. Whereas, if the message is �underflow�, delete operationwill initiate merge-TM to complete the operation.¾ Transaction Manager (TM) LayerThis layer receives different messages from the operations layer. TM layer contains the four TMs; read-TM, write-TM,split-TM, and merge-TM explained before. Each TM creates subtransactions to complete the required operation. TMswill work concurrently and independently. After a TM completes its execution, it reports back to the main operation, inthe previous layer, the result of the operation. Transactions created at this layer will be passed to the scheduler at thenext layer to synchronize various subtransactions created by TMs. Subtransactions created by TMs can also workconcurrently and only leaf level subtransactions will access the buckets directly.¾ Scheduler LayerAt this layer we have a scheduler that synchronize all transactions. Scheduler will determine when to create, commit,and abort a transaction. Scheduler will send the synchronized subtransactions to the BM layer to access the buckets.When a transaction completes, the scheduler will report back to the TMs the state of each transaction; whether it issuccessfully created, committed or aborted.¾ Bucket Manager (BM) LayerAt this layer, each BM handles a bucket and all its overflow buckets. Before a transaction accesses any bucket, thecorresponding BM will determine whether to permit the transaction to access the bucket or suspend it with the help ofthe lock compatibility-matrix shown in Table 1. If two transactions accessing the same bucket interfere with eachother, BM will permit one of the transactions to access the bucket while suspending the other until previous transactionfinishes. Thus, BMs will restrict accesses to the buckets that lead to an inconsistent database state. To insureserializability, BM also handles the keys� locks within the bucket chain. The subtransactions, which access the BMs,are called decisive subtransactions as they are the only subtransactions that access the buckets directly and anyinterference between transactions at this layer will lead to an inconsistency. BMs work independently of each other.¾ File Manager (FM) LayerThis is the last layer in our system. FM layer will insure the durability of the transaction. Any updates on the bucketswill be stored in the database file (i.e., DB File in Figure 2) which resides on a permanent disk space. Updating of thedatabase file will be performed after the commit of a top-level transaction to insure the consistency of our databasefile. FM is also responsible for loading the keys from the database file to the memory pages (i.e., buckets). It can alsorestore the current database state in case of a system crash.5. OBJECT-ORIENTED DESIGN

The most widely used programming languages nowadays are Visual Basic, Java, and Visual C++. As our model isbased on an object-oriented methodology, our choice will be either Java or Visual C++. We have done our systemusing Visual C++ for the following reasons: First, a compile program will always be faster than an interpreted

19

program. Second, execution speed of Visual C++ and the ability to call any Win32 function (our implementation willbe mainly depending on multithreads and sockets) at any time make it a better choice. Finally, Visual C++ is known tobe more efficient and flexible [17]. Further, Visual C++ introduces an MFC (Microsoft Foundation Class) that containa number of built-in classes ready to use. These classes can greatly simplify and decrease writing code.

We have implemented our layered system architecture discussed in the previous section using object-orientedmethodology since it provides the way for objects to communicate among themselves via methods. Each methodinvocation corresponds to the initiation of a subtransaction. Each time a method is called, a message is created which iseither to be sent to other object or used by the same object.

A variable of the class type is called an object. Any object can be created from a class, and an object is said to bean instance of a class. The main program and the subprograms that oversee the interaction among objects handle theaccesses. The control code directs an object to access its data by using one of its methods or operations. A senderpasses a message to a receiving object and asks the object to perform a task; the receiving object may return theinformation to the sender (output data) or pass messages to other objects requesting the execution of additional tasks.Each object is designated by an id, called object id (OID), to distinguish from other objects.

The classes in our implementation were demonstrated in Figure 3 and Figure 4. Figure 3 shows the structure of theclient side, whereas, Figure 4 shows the structure of the server side.5.1 SERVERThe server side (Figure 3) in our implementation consists of eight components (classes), namely, server, agent, serversocket, file manger (FM), user operations, socket file, bucket manager (BM), and linear hash operations (LH).Following, we will discuss the scenario of each class separately.¾ Server

Server, is the first class to be initiated when the server runs. It starts by initiating windows socket. Windowssockets specification defines a binary-compatible network programming for Microsoft Windows. After initiatingwindows socket, server class will instantiate an object from the agent class.

¾ AgentAgent class is considered to be the multithreads class, as it is responsible for creating a new thread for eachoperation received from the client through the socket file as shown in Figure 3 (multithreading is explained indetail in the next section). Agent starts by calling FM class to load the DB file before enabling windows sockets.After loading the DB file, agent will call server socket class to create windows sockets. When connecting client tothe server, agent will be ready to receive a user request, and then, to create the thread that will be passed to theuser operations class to perform this operation.

¾ File Manager (FM)When instantiating this class, the DB file will be loaded to memory. So data can be accessible any time needed.Three main member functions exist in this class. These functions are: Store, Load, and Update. Store functionstores a key from memory to the DB file. Load function fetches a key from the DB file to memory. Updatefunction is called whenever an update is occurred to the DB file. As shown in Figure 3, FM sends and receivesmessages, containing the target key, to the TM class as this class is directly connected to the buckets.

¾ Server SocketServer socket class is responsible for network connection. The main member functions in this class are: Create,Listen, and Accept socket. After creating the socket, Listen socket function is used to listen for incoming

20

connection requests. A backlog for incoming connections is specified with Listen, and then the connections areaccepted with Accept function. The connector shown in Figure 3 is to point out where the clients are initiallyconnected to the server. A CSocket, which is an MFC class, represents a Windows Socket. Class CSocketencapsulates the Windows Sockets, providing an object-oriented abstraction to use windows sockets inconjunction with MFC. Server socket communicates with socket file to serialize data.

¾ Socket FileSocket file is created using an MFC class called CSocketFile. CSocketFile object is used for sending and receivingdata across the network via windows sockets. This class contains two main functions. These functions are:Receive and Send. Receive function receives the data sent by the client, whereas, Send function send data to theclients. Another class called CArchive, is used here to simplify sending and receiving data using MFCserialization. Serialization allows objects to persist in memory between runs of the program. Serialization is theprocess of writing or reading an object to or from a persistent storage medium, such as disk file. To serialize(send) data, we insert the data into archive (see Figure 3), which calls CSocketFile member functions to write datato the CSocketFile object. To deserialize (receive) data, we extract the data from the archive. This causes thearchive to call CSocketFile member functions to read data from the CSocket object. CSocket and CSocketFilework analogously with each other in that, CSocketFile manages the data by saving or loading it from/to servermemory, and CSocket manages the network connections with the clients. After receiving message, socket file willsend the message to the Create thread method at the agent class so it can decide which operation is required forthis message.

¾ User OperationsUser operations class contains the three main operations for any database information retrieval. These operationsare: insert, delete and find. These operations initiated when the user operations class receives a thread from theagent. Multithreads work concurrently at this class. Each thread is responsible for performing an atomic task.Before the three operations insert, find, and delete pass the message to the TM to perform the required operation,they will call the BM class to set a lock on the target bucket before accessing it. When reading or writing the keyBM will be called again to set a lock for the key.

If insert operation is initiated, it will pass a message to the TM class to perform an insert operation. If after theinsertion, user operation class receives an �overflow� message from the TM class, insert function will sendanother message to the TM class to perform the split operation. In case an �underflow� message is received, deletefunction will send a message to the TM class to perform a merge operation. Each time user operations class callTM it will call BM first to set a lock depending on the operation required.

¾ Bucket ManagerBM is responsible for locking and unlocking the buckets and the keys. BM receives a message from the useroperations class to set a lock depending on the operation to be performed. After the operation is finish, BM willbe called again to unlock the bucket or the key. Locking the bucket and the key afterwards will insure theserializability. Locking the key is implemented using read/write lock technique in which a key is granted a read-lock while being read and a write-lock while updating it. The read-lock, selective-lock, and exclusive-lockvariables, for locking the buckets, are also declared in this class.

21

OODBMS may provide locking at the object and/or page level. We choose a physical page; a bucket in ourdesign. As each LH object has two buckets, holding lock on the object level will decrease the concurrency amongtransactions that access the buckets.

¾ Transaction Managers (TM)TMs are implemented using methods. ReadTM method will search for a key in bucket. WriteTM method willeither insert a key or delete a key depends on the message received. SplitTM method will split the next bucket andwill send a message to the user operations class to create a new object if needed for the new bucket. MergeTMmethod will merge the two buckets involved in the merge operation (next and partner buckets), and will send amessage to the user operations class to delete the object if it has empty buckets.

TMs� methods will communicate with the FM class to load or store keys and to update file after a writeoperation. Also, TM class will send messages to the socket file to serialize data. That is, sending data to theclients.

The implementation of buckets are done by using a built in class named CArray, in Visual C++ language,which has a number of methods for insert, delete, or search for a key, and other related methods to handle thearray. The overflow chain is implemented using a built in class named CList that has a number of methods, whichdeals with the linked list. CArray and CList classes contain the operation related to insert, delete, or search for akey. Buckets and overflow chains are private instance in the LH class because there is no need for the client toknow the details of the implementation. Local-level variable is also declared in the private section of this class.

In our model, each LH object contains two buckets. We have considered two buckets in each object ratherthan one to reduce the system's overhead during split and merge operations. This is due to the fact that when thefirst bucket of any object say O1 needs to be split, a new object say O2 will be created with two buckets.However, when the second bucket from the object O1 splits, no new object (bucket) has to be created as the keysfrom this bucket can move to the second bucket of the object O2. Similarly, no object needs to be deleted whenone of the buckets becomes empty after a merge operation as the other bucket may contain some data. An objectwill be deleted to free memory space, only if both the buckets become empty. On the other hand, having morebuckets in each object will waste memory space. Thus, having two buckets in an object is a compromise betweenspeediness and space utilization.

Whenever split operation is called, the agent will read the local-level of the last bucket of the last object. If thelocal-level is 0 (which indicates that this bucket has not been accessed yet), the keys of the split bucket will moveto this bucket. Otherwise, a new object will be created and the keys will move to the first bucket of this object inthis object. At each level of the hash function, we have a maximum of 2level objects. We find an object id (OID)using the following:

bucket no. = key mod (2level * N)OID = bucket no. / N.Where N is the number of buckets contained in each LH object (In our model, N = 2)

5.2 CLIENTThe client side (Figure 4) in our implementation consists of four main components (classes), namely, client, client

socket, agent, and socket file. The following classes work the same as in the server side so we will not discuss indetails.

22

Load File

Server Socket

Create Thread

DELETE

Figure 3. Server�s structure

Transaction Managers

insert mergesplitfinddelete

Bucket manager

Server

Load

Store

Update

FINDINSERT

Agent

File Manager

User Operations

Transaction Managers

Agent

1

Server Socket

Create Windows Socket

Listen Socket

Accept Socket

Socket File

Socket File

Send

ReceiveLock Bucket

read sel excl

Lock Keyread write

Bucket Manager

Instantiate an object Send message

CArchive

SERIALIZE

23

¾ ClientClient starts by initiating the client�s windows socket same as in the server. After initiating the socket, client willcall client socket for the network connection with the server. When connected to the server, client instantiate agentclass (different than server�s class) that works as a user interface.

¾ AgentAgent class works as an interface between the user and the system. Agent receives a request, to perform anoperation on a specified key, from the user and in turn will pass this request to the server. Agent will send amessage to the socket file to send the key to the server to perform the required operation. After the servercompletes the required operation, agent will receive a message from the socket file containing the result of theoperation.

¾ Client SocketAfter creating the socket at the server side, the socket in the client side will try to connect to the server. Clientsocket instantiates an object from the socket file to serialize the data. Server should be on listen mode, that is, theserver is waiting to accept connection from a client. Otherwise, the client can not run. In this case, client socketwill send a message to the client class, which in turn will pass this message to the agent. Agent will show amessage on the screen to inform the user about the result.

¾ Socket FileThis class works the same as the one at the server side. Socket file receives a message from the agent and then callCArchive to serialize the data (store in memory). CSocket, created by client socket, will then send this message tothe server. Further, socket file receives data from the server through the CArchive. This message will be sent tothe agent to show the user the result of the operation requested.

Create Windows Socket

Connect Client to Server

Create Socket File

INSERT

DELETE

FIND

Client Socket

Agent

1

Figure 4. Client�s structure

Socket File

Send

Receive

Client

Client Socket

Create UI Dialog

CArchive

SERIALIZE

24

SERVER SITE:1. Server class

a) Initiates windows sockets. The purpose of windows sockets is to abstract away the underlying network and sothe application can run on any network that supports sockets. MFC supports programming with the windowssockets API by supplying two classes. One of these classes, CSocket, provides a high level of abstraction tosimplify the network communications programming. The other class CAsyncSocket object represents awindows socket � an endpoint of network communication. Class CAsyncSocket encapsulates the windowssockets API, providing an object-oriented abstraction for programmers who want to use windows sockets inconjunction with MFC.

if (!AfxSocketInit()){

AfxMessageBox(�Failed to initiate socket�);return FALSE;

}

25

2. Agent classa) Load the DB file by instantiating an object from the FM class.

CFileManager* FM = new CFileManager;There will be just one FM object. As creating more objects will lead to inconsistency.

b) Instantiates an object from the server socket to create the windows sockets.

Figure 8. Server Info

Figure 7. Server Dialog base interface

26

CServerSocket* serverSocket = new CServerSocket;After creating a new object from the server socket, agent can access any public method in the server socket toestablish the network connection. Just one object of server socket will be created. Creating another socket willlose the connection with the clients.

c) Create a new thread to accomplish the user�s (client�s) request. Agent will create a new object from the useroperations class to handle this thread. Consequently, agent can access the public method of this object suchas, insert, delete, or find.

CUserOperations* UserOperations = new CUserOperatoins;The above code creates an object UserOperations that will allow the agent to access the insert function as

shown below.void CAgent::OnInsert(){

pInsertThread = AfxBeginThread(InsertThread, GetSafeHwnd(),THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);

}UINT InsertThread(LPVOID pParam){

UserOperations->insert();return 0;

}

AfxBeginThread() is an MFC member function used to create a thread. InsertThread is the function toaccomplish the insert thread. In the InsertThread function, agent sends a message to the user operations classto access the insert operation. After calling the insert operation, InsertThread function returns 0 value, whichmeans the function is terminated successfully. Hence, the thread will terminate. Meanwhile, the insertoperation will continue in another class, and OnInsert() method is ready to receive another request. Thisscenario will be seen as multithreads working simultaneously.

3. FM classWhen creating an object from the FM class, the following code will be executed:

File.Open(�C:\\DBFile.dat�, CFile::modeCreate | CFile::modeReadWrite);The above code creates a new file called �DBFile.dat�. CFile is an MFC class that contains a number of

methods, which can be used to access files. In the above code we have used modeCreate and modeReadWritefunctions for creating the file and mark it for both read and write using.a) Load the DB file.

Do step c until end of file.b) Store key into the DB file.

void CFileManager::Store(int key){

File.SeekToEnd();CArchive ar (&file, CArchive::Store);ar << key;ar.Close();

}

27

The above code will serialize the key to the file. As seen from the code ar << key.c) Load key from the DB file.

void CFileManager::Load(int& key){

CArchive ar (&file, CArchive::Load);ar >> key;ar.Close();

}The above code will serialize the key from the file. As seen from the code ar >> key.

4. Server socket classa) Create Socket

m_pSocket = new CServerSock(this);m_pSocketÆCreate();

b) Listen for incoming connection requestsm_pSocketÆ Listen();

c) Accept Clientif (m_pSocket->Accept(*pSocket)){

pSocket->Init();m_connectionList.AddTail(pSocket);

}If a new client is accepted, first, the new socket�s variable will be initialized for the new client. Second, thenew client will be added to the m_connectionList. M_connectionList contains a list of all the clientssuccessfully connected to the server.

d) Send/receive dataServer socket will create a socket file for sending and receiving data across the network. Server socket workswith CSocketFile and CArchive to manage sending and receiving of data.

5. Socket File Class

m_pFile = new CSocketFile(this);m_pArchiveIn = new CArchive(m_pFile,CArchive::load);m_pArchiveOut = new CArchive(m_pFile,CArchive::store);void CMsg::Serialize(CArchive& ar){

if (ar.IsStoring())ar << key;

elsear >> key;

}

28

c) Receive datavoid CServerSocket::ReceiveMsg(CMsg* pMsg){

pMsg->Serialize(*m_pArchiveIn); //load}

d) Send datavoid CServerSocket::SendMsg(CMsg* pMsg){

if (m_pArchiveOut != NULL){

pMsg->Serialize(*m_pArchiveOut); //storem_pArchiveOut->Flush();

}}

The member function Flush ensures that all data is transferred from the archive (memory) to the file.6. User Operations Class

For efficiency, we put all the BM and TM objects in a dynamic array using an MFC class called CTypedPtrArray.As CTypedPtrArray is inline, using this template does not significantly affect the size or speed of the code.

CTypedPtrArray < CObArray, CTransactionManager*> TMarray;CTypedPtrArray < CObArray, CBucketManager*> BMarray;The CObArray class supports arrays of CObject pointers. These object arrays are similar to C arrays, but they

can dynamically shrink and grow as necessary. TMarray and BMarray, are arrays of type CTransactionManagerand CBucketManager respectively.

For each TM object created it will be added to the TMarray, and for each BM object created it will be added tothe BMarray. This technique will allow our system to keep track of all the objects, and hence, make searching forthe objects faster and easier. CTypedPtrArray function contains built-in operations for managing the array such as,adding, deleting, searching, etc.

As we said earlier, each TM object contains two buckets and each bucket is handled by one BM object.In this class we have FindObject function (Figure 9). This function is called before each operation.

FindObject finds the TM and BM objects, and find the bucket where the key resides.

void CUserOperations::FindObject(int & bucketNo, int lockType, int & key){

rootLock = read;lev = level;int h = ((int) pow(2, level)) * N; // N = 2; no of bucketsbucketNo = key % h;if (bucketNo < next){

lev++;h = ((int) pow(2, lev)) * N;bucketNo = key % h;

}if (new bucket){

//create new TM and BM objectCTransactionManager* TM = new CTransactionManager;TMarray. Add(TM);CBucketManager* BM = new CBucketManager;BMarray.Add(BM);

29

}

objectNo = bucketNo / N;BM = BMarray.GetAt(bucketNo); //find BM for the target bucketBM->Bucket(lockType); //lock this bucketrootLock = unlock;while (bucket not found){

TM = TMarray.GetAt(objectNo); //get access to the target objectTM->Find(); //find bucket and key

}}

a) Insert operation is shown in Figure 10 below.

FindObject(bucketNo, selectiveLock, key); if (find)

SendToClient(�Key is already exists; Can not insert key�);else{

FM->Store(key);TM = TMarray.GetAt(objectNo);TM->Insert(); //insert-TMBM = BMarray.GetAt(next);BM->Bucket(unlock);if (overflow){

TM = TMarray.GetAt(objectNo);TM->Split(); //split-TMif ( new bucket){

CBucketManager* BM = new CBucketManager;BMarray.Add(BM);

}if (new bucket and last object is full){

CTransactionManager* TM = new CTransactionManager;TMarray.Add(TM);TM = TMarray.GetAt(objectNo);

}}next++;next = next % (N * (int) pow(2, level));if (next == 0)

level++;}overflow = false;

Figure 10. Insert operation

Figure 9. FindObject function

30

b) Delete operation is shown in Figure 11 below.

if (emptyFile)SendToClient("No records in the database file�);

else{

FindObject(bucketNo, selectiveLock, key);

{underflow = false;TM = TMarray.GetAt(objectNo);TM->Delete();if (find)

FM->UpDate(key);if (underflow){

TM = TMarray.GetAt(objectNo);TM->Merge();

} rootLock = unlock; BMarray.RemoveAt(lastBucket); lastBucket--;}

}

c) Find operationif (emptyFile){

SendToClients("No records in the database file");}else{

FindObject(bucketNo, readLock, key);if (find){

CString Msg;Msg.Format("Key %d is in bucket %d", key, currentBucket);CString foundKey = Msg;SendToClient(foundKey);

}else{

SendToClients(�Key not found�);}

}7. BM Class

a) Lock Bucketvoid CBucketManager::Bucket(BucketLock lockType){

bucket = lockType;}

Figure 11. Delete operation

31

BucketLock can be read, selective, exclusive, or unlock.b) Lock key

void CBucketManager::Key(KeyLock lockType){

bucket = lockType;}KeyLock can be read, write, or unlock.

8. TM ClassOperations at this level will manipulate the bucket directly.a) Insert-TM

if (!find){

BM->Key(write);if (Bucket[bucketNo].GetSize() < BucketSize)

Bucket[bucketNo].Add(key); //add key into the Bucketelse{

OverflowBucket[bucketNo].Add(key); //add key into the OverflowBucketoverflow = true;

}BM->Key(unlock);

}Bucket and OverflowBucket are CArray objects. Bucket is the primary bucket, whereas, OverflowBucket is theoverflow chain bucket. Bucket can be linked to many OverflowBucket. BucketSize is the capacity of the bucket(3 keys in our model).

b) Split-TM

rootLock = selective;lev = level;lev++;local_level[next] = lev;j = 0;while(j < Bucket[next].GetSize()) // (1){BM->Key(write);

key = Bucket[next].GetAt(j);Rehash(j);BM->Key(unlock);

}POSITION loc = OverflowBucket[next].GetHeadPosition(); // (2)while( loc != NULL ){

j = 0;while(j < OverflowBucket[next].GetSize()){

BM->Key(write);key = OverflowBucket [next].GetAt(j);Rehash(j);

BM->Key(unlock);}POSITION loc = OverflowBucket[next].GetNext(); // (3)

}

Figure 12. Split-TM algorithm

32

In step 1, split will rehash the primary bucket. In step 2, a pointer called loc will be placed at the head ofthe first overflow bucket, and then, this overflow bucket will be rehashed. After rehashing the overflowbucket, loc will move to the next overflow bucket as seen in step 3.

c) Delete-TMSearch(); //search for the key in the bucket chainif (key found){

if (key found in primary bucket){

BM->Key(write);Bucket[next].RemoveAt(keyPos);//remove the key from Bucket at position keyPos.if(Bucket[next].GetSize() == NULL)underflow = true;BM->Key(unlock);

}else //key found in overflow bucket

OverflowBucket[next].RemoveAt(keyPos);}else

key not found;d) Merge-TM

rootLock = exclusive;if (next == 0)

level--;next = next-- % (N * (int) pow(2, level)); //N no of buckets in one objectpartner = next + (N * (int) pow(2,level));rootLock = selective;

e) Find-TMrootLock = read;lev = level;int h = ((int) pow(2, level)) * NoOfBuckets;bucketNo = key % h;if (bucketNo < next){

lev++;h = ((int) pow(2, lev)) * NoOfBuckets;bucketNo = key % h;

}BM = BMarray.GetAt(currentBucket);BM->LockBucket(lockType);rootLock = unlock;while (!endSearch){

CTransactionManager* TM = new CTransactionManager;TM = LHarray.GetAt(objectNo);TM->Find();

}

CLIENT SITE:1. Client Class

33

a) Initiates windows sockets for the clientif (!AfxSocketInit()){

AfxMessageBox(�Failed to initiate socket�);return FALSE;

}

b) Call the client socketCClientSocket* clientSocket = new CClientSocket;

c) Create the client user interface by instantiating an object from the agent class. The client user interface is thesame one as shown in Figure 7.

CClientDlg dlg;m_pMainWnd = &dlg;int nResponse = dlg.DoModal();

2. Client Socketa) Create socket

m_pSocket = new CClientSock(this);if (!m_pSocket->Create()){

delete m_pSocket;m_pSocket = NULL;AfxMessageBox(�Failed to create socket�);return FALSE;

}b) Connect client

while (!m_pSocket->Connect(server address)){

if (AfxMessageBox(�Connection failed, do you want to retry ?�,MB_YESNO) == IDNO){

delete m_pSocket;m_pSocket = NULL;return FALSE;

}}The message box ask the user if he wants to retry to connect to the server if the connection failed first

time.c) Create Socket File

m_pFile = new CSocketFile(m_pSocket);m_pArchiveIn = new CArchive(m_pFile,CArchive::load);m_pArchiveOut = new CArchive(m_pFile,CArchive::store);

3. AgentAgent is the user interface, which receives request from the user and pass the request to the socket file.

4. Socket FileSocket file for the client site works the same as in the server site.

In the previous section we have seen how messages have been used to communicate among various methods andobjects. In this section, we will see how the multithreads have been used to implement nested transactions in oursystem.

34

In our system, multithreads are managed in such a way that they can not interfere with each other. Life cycle ofthe thread is hidden from clients. During a thread�s life cycle, many multithreads may be created. A thread may behalted, or an inconsistency may occur, however, these situations will not affect clients. For the clients, a creation and acommit of the top-level thread is important.

As clients operate concurrently, many multithreads will be created. To control these threads from interfering, alocking scheme explained earlier is implemented. The operations in our model will require multithreads to completetheir jobs such as searching for the object, inserting, deleting, etc. The general scenario of various threads andmultithreads to perform various operations is shown in Figure 13. The arrows represent the beginning and the end ofthe thread route. Each arrow is designated by a number to show the sequence of creation of the multithreads. Threadsat the leaf level will only access the data directly.

To follow a thread cycle, we will take an example of userA�s request as shown in Figure 13. Suppose userAinitiates a thread to insert a key. AgentA will send thread TA1 to the insert operation at the server side. Insert operationwill send another thread TA2 to the server�s agent which will decide the appropriate TMs to complete the insertoperation. The server�s agent will pass a new subthread TA3 to the write-TM. Write-TM will create a subthread TA4 tosearch for the key. If the key is not found, subthread TA4 will insert the key into the appropriate bucket. After insertingthe key, write-TM will report back a transaction-commit to agentA, which will inform userA the result of the insertion.In case an overflow occurs, write-TM will send a message to the server�s agent, which in turn will create a newsubthread TA5 and send it to the split-TM. Split-TM will create another subthread TA6 to access the split-writeA

AgentA AgentCAgentB

DELETE FINDINSERT

Agent

split-TM write-TM merge-TM read-TM

split-writeA search-writeBsearch-writeA search-readCmerge-writeB

TA1 TC1TB1

TA2

TA6 TC4TB6TB4TA4

TC3TB5TA3TA5

TB2

TC2

TB3

Figure 13. A multithreading scenario for the linear hash structure

35

operation. If any one of these threads (subtransactions) has been aborted, the parent thread can create another thread tocomplete the execution of aborted thread.7.1 User Interface OperationsIn this section, we will see how each user operation is processed and implemented.¾ FindFind operation searches for a record in the DB file. This operation is handled by read-TM. When a user initiates thefind operation, a request for creating a thread to search for a key will be sent to the read-TM. Read-TM will create asearch-read thread to perform a search for the key. Search-read's thread will acquire a read-lock on root variable andon the bucket being accessed. Further, before accessing the target key a read-lock is granted to it to insure that othertransactions can not update it. Holding a read-lock permits other multithreads to perform simultaneously a search,insert, delete, or split operations. When the search process finishes, a report "transaction-commit" will be issued toinform read-TM that the thread is committed along with a message which is either "key is found" or "key does notexists".¾ InsertInsert operation inserts a record into the DB file. This operation is handled by Write-TM. Write-TM will create a searchthread to search for the requested key. If the key does not exist, another subthread write will be created to insert thekey into its appropriate bucket. In the searching phase, the search thread will be granted a read-lock on the rootvariable so that other threads can also execute concurrently. After finding the bucket, a new write thread will becreated and will be granted a selective-lock on this bucket, so that no other operations can access the same bucket.Another type of locks also granted to the requested key, while reading the key a read-lock is granted and while writingit a write-lock is granted.

If an overflow occurs after insertion, agent will send a message to the split-TM to create a subthread split-writethat will perform the split operation. Split-write thread will hold a selective-lock on the root variable and on the bucketto be split so other operations can work concurrently with split but on different buckets. Before splitting the bucket aread-lock is granted to all the keys in the primary and in its overflow buckets as split will not update the keys.¾ DeleteDelete operation is called to delete a record from the DB file. It is handled by Write-TM. As before, write-TM willcreate a search thread to search for the requested key. If the key exists, another write thread will be created to deletethe key from the bucket. In the searching phase, the subthread search will grant a read-lock to the root variable so thatother threads can work concurrently. In the second phase, the write thread will hold a selective-lock on the bucketbeing accessed so no other insert and delete operations can access the same bucket.

If the bucket becomes empty after deletion, the agent will send a message to the merge-TM to create a subthreadthat will perform the merge operation. Merge-write thread will hold an exclusive-lock on the root variable and on bothbuckets to be merged. No other operations can work concurrently with merge operation. As merge operation will notpermit any other operation to execute concurrently, there will be no need to lock the keys in the buckets involved in themerge operation (next and partner bucket).

36

8. Snapshot of Our ModelHere we give a working example of our model. The example explains when the buckets/objects are created anddeleted. Locking scheme is also taken into consideration. In the example (Figure 14.a, 14.b, and 14.c), we considerthree operations that are running concurrently. These operations are search for key 32, insert key 16, and delete key 15.

Each bucket is handled by a BM, which contains a list of all transactions accessing the bucket with their lock typesand the status of the transaction (see Figure 14.a). The root variable resides in the scheduler and will be read by eachtransaction before accessing the bucket. The variables level and next are shown in the example to specify when theirvalues are changed. Transactions created by search-read, search-write, split-write, and merge-write will read the rootvariable's lock. If the lock is exclusive, scheduler will suspend the transaction until the root variable's lock becomesunlocked. Otherwise, if the root variable's lock is either read, selective, or unlock, transaction will grant its lock to theroot variable and proceed to access the bucket. Before accessing the bucket, BM of the accessed bucket will check thetransaction's lock to decide whether to suspend it until the appropriate time or to permit it to access the bucket.

In Figure 14.a, two objects have been created and each contains two buckets with local-level = 1. Consider thatsearch-read has created a transaction TB5 to find key 32 and search-write has created a transaction TC5 to insert key16 and a transaction TD5 to delete key 15. Initially, all the three transactions can acquire a read-lock on the rootvariable as the scheduler will permit the three transactions to proceed since there is no lock-conflict. Transactions TB5and TC5 will access bucket 0 whereas transaction TD5 will access bucket 3. Transaction TB5 will acquire a read-lockand transaction TC5 will acquire a selective-lock on the bucket 0. BM will permit both the transactions to access thebucket concurrently since read-lock and selective-lock are compatible locks. As find transaction TB5 will just read thekey it will grant a read-lock to key 32 (see Figure 14.a). Insert transaction TC5 will grant a write-lock to key 16 whileupdating bucket 0 (see Figure 14.a). When transaction TB5 finds key 32, it will report back a message �found� andthen commits. When TC5 inserts key 16, an overflow occurs which will call for split operation. Split-write will create a

Figure 14.a

37

subtransaction TC6 to perform the splitting operation. Split transaction TC6 will acquire a selective-lock on the rootvariable and on next bucket which is bucket 0 (see Figure 14.b). Before rehashing bucket 0 using h2 function, all thekeys in this bucket will be granted a read-lock (see section 2.2). As a result, some of the keys will be moved to bucket4 (see Figure 14.b). A new object (object 3) will be created and will contain bucket 4, which is appended at the end ofthe hash file. After splitting operation, next will be incremented by 1 (see Figure 14.b).

Deletion transaction TD5 can work concurrently with insertion transaction TC5 as they access different buckets.TD5 will access bucket 3 and will acquire a selective-lock before accessing the bucket and then a write-lock will be

Figure 14.b

Figure 14.c

38

granted to key 15 as shown in Figure 6.a. After deletion of key 15, bucket 3 becomes empty and so a merge operationis called. Merge-write will create a subtransaction TD6 to perform the merge operation. TD6 will grant an exclusive-lock on the root variable and on both buckets involved in merge operation. As we see from Figure 14.b, transactionTD6 is suspended because of the lock-conflict with the transaction's TC6's lock. When split transaction TC6 finishesits computation, transaction TD6 will proceed to perform the merge operation. Merge transaction TD6 will decrementnext by 1 and will merge the next bucket and its partner, here bucket 0 and 4. Notice that the keys in bucket 0 and 4 areunlocked as no other transactions can access these buckets during the merge operation. After the merge operation, nextbucket and the root variable will be unlocked. Object 3 will be deleted (Figure 14.c) to save memory space, as no keysare left in the bucket.9. ConclusionIn this paper, we have presented the linear hash structure algorithm [8] in nested transaction environment to expediteconcurrency and to handle transaction aborts. First, we have formalized our algorithm using I/O automaton model.Second, we have presented an object-oriented client/server model for the implementation of nested transactions thataccess linear hash structures. We have exploited nested transactions in concurrent linear hash structure to robust theconcurrency and handle transaction aborts. We have used object-oriented technology, which is suitable forclient/server systems for its speediness in accessing data at the server site, and the property to encapsulatecommunications from the user. In our model, buckets are modeled as objects whereas linear hash operations find,insert, delete, split, and merge are considered as methods. These methods correspond to nested transactions in theirbehavior and are implemented as threads and multithreads. As a future work, we would like to do the performanceevaluation of our algorithm to examine our claim of increase in concurrency and to measure the overheads associatedwith our system. Another direction of research is to study the model in a distributed environment [18] where a linearhash structure can be spread over a number of machines.References[1] Moss, J. E., Nested Transactions: An Approach to Reliable Distributed Computing, MIT Press Cambridge,

Massachusetts, 1985.[2] Sagiv, Y., Concurrent Operations on B-trees with Overtaking, in proceeding of the 3rd ACM SIGACT-SIGMOD

Symposium on Principles of Database Systems, Portland, Oregon, pp. 28-37, March 1985.[3] Lehman, P. L., Yao, S. B., Efficient Locking for Concurrent Operations on B-trees, ACM Transaction on

Database Systems, Vol. 6, No. 4, pp. 650-670, December 1981.[4] Enbody, R. J., Du, H. C., Dynamic Hashing Schemes, ACM Computing Surveys, Vol. 20, No. 2, pp. 85-113,

June 1988.[5] Kumar, V., Concurrency Control on Extendible Hashing, Information Processing Letters, Vol. 31, No. 1, pp. 35-

41, April 1989.[6] Larson, P. A., Linear Hashing with Separator: A Dynamic Hashing Scheme Achieving One-Access Retrieval,

ACM Transaction on Database Systems, Vol. 13, No. 3, pp. 366-388, September 1988.[7] Mohan, C., Aries/LHS: A Concurrency Control and Recovery Method Using Write-Ahead logging for Hashing

With Separators, in proceedings of the 9th IEEE International Conference on Data Engineering, Vienna, Austria,April 1993.

[8] Ellis, C. S., Concurrency in Linear Hashing, ACM Transactions on Database Systems, Vol. 12, No. 2, pp. 195-217, June 1987.

39

[9] Hsu, M., Tung, S. S., Yang, W.P., Concurrent Operations in Linear Hashing, Information Sciences, Vol. 51, No.2, pp. 193-211, 1990.

[10] Khoshafian, S., Object-Oriented Databases, Wiley Professional Computing, 1993.[11] Fu, A., Kameda, T., Concurrency control of Nested Transactions Accessing B-Trees, in Proceedings of the 8th

ACM Symposium on Principles of Database Systems, pp. 270-285, 1989.[12] Madria, S. K., Maheshwari, S. N., Chandra, B., Formalization of a Concurrent Linear Hash Structure Algorithm

Using nested Transactions and I/O Automation Model, International Workshop on Issues and Applications ofDatabase Technology (IADT `98), Berlin, Germany, July, 1998.

[13] Lynch, N., Merrit, M., Weihl, W., Fekete, A., Atomic Transactions, Morgan Kaufman Publishers, San Mateo,California, 1994.

[14] Madria, S. K., Maheshwari, S. N., Chandra, B., Bhargava, B., Crash Recovery in an Open and Safe NestedTransaction Model, Lecture Notes in Computer Science, Vol. 1308, Springer Verlag, pp. 440-451, 1997.

[15] Madria, S. K., A Study of the Concurrency Control and Recovery Algorithms in Nested TransactionEnvironment, The Computer Journal, Vol. 40, Issue 10, 1998.

[16] Madria, S. K., Maheshwari, S. N., Chandra, B., Formalization and Correctness of a Concurrency ControlAlgorithm for an Open and Safe Nested Transaction Model Using I/O Automaton Model, 8th Intl. Conference onManagement of Data (COMAD�97), Madras, India, December 1997.

[17] Kruglinski, D., J., Inside Visual C++ 4th ed., Microsoft Press, 1997.[18] Litwin, W., Neimat, M., Schneider, D., LH* - A Scalable, distributed Data Structure, ACM Transactions on

Database Systems, Vol. 21, No. 4, pp. 480-525, December 1996.