| -*- Text -*- |
| |
| |
| Introduction |
| ============ |
| One of the major performance problems with the first-generation working copy |
| library was the locking strategy. Each directory could have a write lock that |
| excluded other processes from performing certain actions on that directory. |
| |
| The locks were implemented as physical `lock' files which had to be placed and |
| removed in the administrative area of each directory. For many operations, |
| this necessitated a crawl of the working copy to lock or unlock various |
| directories, even if those directories would never be touched by the operation. |
| For working copies of even modest size, these crawls could easily dominate the |
| running time of the client, and were made even worse by high-latency |
| filesystems, such as NFS. |
| |
| Read-only locks didn't really exist; read-only access was implemented as a |
| `snapshot' of the working copy. This snapshot was potentially out-of-date |
| as soon as it was created; a snapshot of a working copy being modified under |
| write locks might become out-of-date while it was being created. There was no |
| mechanism to upgrade a read-only lock to a write lock and there was no way to |
| determine whether a read-only lock was up-to-date or out-of-date. |
| |
| The snapshot approach to read-only locks worked well for the command line |
| client as that only needs short lived handles on the database. It worked less |
| well for GUI clients which need long lived handles. |
| |
| By centralizing the working copy metadata as part of wc-ng, we can also |
| centralize our locking strategy and take advantage of the transaction and |
| locking primitives of the underlying sqlite database, while still maintaining |
| backward compatibility. This document describes the proposed implementation |
| guidelines for working copy locking in wc-ng. |
| |
| |
| Overview |
| ======== |
| Even with the addition of SQLite and its locking and transaction |
| capabilities, there are still instances where we will need to maintain our |
| own locks on the working copy. It is expected, though, that these |
| occasions are much fewer than in the old working copy library. |
| |
| This document deals specifically with write locks, which prevent multiple |
| processes from concurrently writing to the working copy metadata. The |
| sqlite transaction mechanism is used to ensure that the database is kept |
| consistent between calls to wc_db APIs. Thus, all readers at whatever point |
| they read the database will be shown a consistent view of the metadata, so |
| read locks are not needed for wc-ng. |
| |
| |
| Types of Locks |
| ============== |
| There are two type of locks in the working copy: logical and explicit. |
| Logical locks are what API consumers are referring to when they ask "is PATH |
| locked?" Explicit locks are the actual artifacts that are persisted which |
| the wc_db APIs can use to deduce logical locks. In wc-1, logical and explicit |
| locks were the same, but wc-ng adds the notion of lock inheritance, allowing |
| a single explicit lock to logically lock an entire subtree. |
| |
| |
| How Locks are Stored |
| ==================== |
| As of working copy format 15, locks are currently indicated as row in the |
| WC_LOCK table in the sqlite database. This table has the following schema: |
| |
| CREATE TABLE WC_LOCK ( |
| /* specifies the location of this node in the local filesystem */ |
| wc_id INTEGER NOT NULL REFERENCES WCROOT (id), |
| local_dir_relpath TEXT NOT NULL, |
| |
| PRIMARY KEY (wc_id, local_dir_relpath) |
| ); |
| |
| [ It is anticipated that future versions of the schema will add a LOCKED_LEVELS |
| column, so that column will be described below. ] |
| |
| An entry in the WC_LOCK table is equivalent to an explicit lock, and must |
| exist prior to several wc_db APIs which require persistent write access. In |
| order to accommodate backward compatibility, the LOCKED_LEVELS column can be |
| used to limit the depth of the logical lock specified by the explicit lock. |
| If the value is zero or positive, that number of directories below |
| LOCAL_DIR_RELPATH at to be locked. It is anticipated that this column will be |
| '-1' (lock to infinite depth) for all locks created through the wc_db APIs. |
| |
| |
| Using Locks |
| =========== |
| There are two kinds of operations which in wc_db APIs that need different kinds |
| of write checks: |
| |
| Atomic Operations |
| ----------------- |
| WC-NG operations that can operate without outside knowledge learned before |
| the operation. |
| |
| These functions that are just one sqlite transaction by itself, just need to |
| make sure nobody else has a write lock. Having a write lock is not required |
| for operations like just changing the actual properties on a node. Of course |
| nobody else can own a write lock, or it might change the properties after |
| sending the commit data, but before moving the data to the base_node table. |
| |
| In a centralized metadata scheme, it is easy to check that nobody else has |
| a write lock. (Checking if we ourselves have a write lock ourself is just a |
| memory lookup of course). |
| |
| Partial Operations |
| ------------------ |
| These operations rely on data read before the wc_db operation and only work |
| correctly if the data didn't change since reading. All entry based operations |
| are in this category and the WC-NG work tries to redesign several of these |
| operation to the first class of operations. |
| |
| Lock Overlapping |
| ---------------- |
| As in wc-1, locks may not overlap. For instance, a process which acquires |
| a depth-infinity lock for /A/B will encounter an error if it attempts to |
| later acquire a lock for /A/B/C, even though it already owns the logical |
| lock for that path. In this way wc-ng can ensure the explicit lock for a |
| given logical lock is stored in one location. This location will be the |
| first lock encountered on a recursive crawl up the working copy tree. |
| |
| |
| APIs |
| ---- |
| wc_db will provide several APIs to acquire, release and check locks. These |
| APIs are still under consideration. |
| |
| |
| Backward Compatibility |
| ====================== |
| This proposed write lock scheme will be fully backward compatible, thanks to |
| the LOCKED_LEVELS column. This allows old-style access batons to utilize the |
| new locking mechanisms internally and be compatible with processes using the |
| new APIs. |