Specification for locking of moin resources

status
Describe the different classes, their roles and usage, without going into api details.

Creating a locked resource

Any mutable shared resource must be locked.

Example: using a resource

from MoinMoin import lock
resource = lock.sharedResource('resource_name', writeTimeout=None, 
                               updateTimeout=None, readTimeout=None)

This will create the directory wiki/data/lock/resource_name, which will hold the locks for this resourse.

Only one instance can be created for a 'resource_name'. Calling again will return the same instance.

Questions

Options:

Creating a ReadLock

When you want to use the resource, you must acquire a ReadLock. This will prevent other processes or threads from acquiring a WriteLock and changing the data while you read it.

Example: using a read lock:

readLock = resource.readLock()
if readlock.acquire(timeout):
    try:
        # read the resource
    finally:
        readLock.release()

You can create many ReadLocks as you need for one resource, in the same process or in many processes or threads.

A read lock will create wiki/data/lock/resource_name/read_XXXXX. XXXX is a unique name created with tempfile.mkdtemp. Each lock will have a new unique directory in the resource directory.

Creating a WriteLock

When you want to replace the resource, you must acquire a WriteLock. This will prevent others from acquiring new ReadLocks or UpdateLocks. This lock must be used for the shortest time possible.

Only one WriteLock can be acquired.

Exmaple: using a WriteLock

writeLock = resource. writeLock()
if writeLock.acquire(timeout):
    try:
        # write the resource
    finally:
        writeLock.release()

A write lock will create wiki/data/lock/resource_name/write.

A WriteLock will be acquired only if there are no active ReadLocks or UpdateLock in the lock directory.

Creating an UpdateLock

When you want to replace the resource, but making the replacement will take long time and you don't want to make the resource unreadable, you create an UpdateLock. This will prevent others from acquiring a WriteLock and changing the resource while you read it. This will let other to create ReadLocks, and use the current state of the resource, while you update it to a new state in a temporary location.

Only one UpdateLock can be acquired.

Exmaple: using an UpdateLock

lock = resource.updateLock()
if lock.acquire(timeout):
    try:
        # copy the resource to temporary location
        # change the copy
        if lock.upgrade(WriteLock, timeout):
            # replace the old resource with the updated copy
    finally:
        lock.release()

An update lock will create wiki/data/lock/resource_name/update.

Expiring locks

Any expired locks in the directory will be removed when trying to acquire a new WriteLock or UpdateLock. The timeouts are the timeouts used in the constructor: writeTimeout, updateTimeout, readTimeout.

Any lock with age bigger then the timeout for that lock kind will be removed.

Converting a lock

To convert a read lock or an update lock to a write lock, call convert(LockClass, timeout). The lock will try to acquire a LockClass and remove the original lock directory.

Typical use:

lock = resource.updateLock()
if lock.acquire(timeout):
    try:
        # do the update
        if lock.convert(WriteLock, timeout):
            # write
    finally:
        lock.release()

Possible implementation:

    def convert(lockClass, timeout=None):
        if lockClass == self.__class__:
            raise LockConvertError('same class')
        delegate = lockClass(self.dir)
        if delegate.acquire(timeout):
            self._removeLockDir()
            self.delegate = delegate
            # from now on, everything should be delegated to self.delegate
            return True
        return False

Making a resource lockable

To make other developers life easier and prevent locking errors, each lockable class should supply a sharedResource method, with good default timeouts.

class Page:

    def sharedResource(writeTimeout=60, updateTimeout=3600, readTimeout=60):
        return lock.sharedResource(self.getPagePath('lock'), writeTimeout, updateTimeout, 
                                   readTimeout)

Now to lock a page, the client work directly with the page object:

lock = page.sharedResource().readLock()
if lock.acquire(timeout):
    try:
        # read page
    finally:
        lock.release()

Todo

MoinMoin: LockingSpecification (last edited 2007-10-29 19:15:39 by localhost)