Named Locks
Named locks are essentially locks that are assigned to some given (opaque) ID. If you work with multiple resources that each can have unique ID assigned (i.e., file with an absolute path, some entities with unique ID), then you can use named locks to make sure they are being protected from concurrent read and write actions.
The building block for named locks is org.eclipse.aether.named.NamedLock
that is acquired from factory
org.eclipse.aether.named.NamedLockFactory
for given “name”. Two threads (or processes, or even distributed
processes, depending on backing implementation) that use same “name” can then coordinate each other by acquiring
“shared” or “exclusive” access to resource mapped to “name”. Named locks treat “name” just as an opaque identifier.
The design attempts for similarity with existing Java constructs like ReentrantReadWriteLock
,
and share similar properties as well.
Named locks properties:
- Reentrant: named locks allow you to reacquire same (shared or exclusive) lock you already have.
- Lock downgrading: named locks allow “lock downgrading”, but semantically this results in no-op (as thread already owns exclusive lock).
- Lock upgrading: is not supported and implementations will “fail fast” (throw runtime exception) for code that attempts to perform it. If required, proposed alternative is DCL.
- Interruption of acquisition: named locks support interruption during acquisition.
Named locks special properties:
- Coordination happens on Thread level (always) and goes all way up to processes or distributed processes (backing implementation dependant)
- Named locks can be JVM local only, host wide (file locking) or even fully distributed (Hazelcast, Redisson).
Named locks provide support classes for implementations, and provide out of the box several lock and name mapper implementations.
Following implementations are “local” (local to JVM) named lock implementations:
rwlock-local
implemented inorg.eclipse.aether.named.providers.LocalReadWriteLockNamedLockFactory
that uses JVMjava.util.concurrent.locks.ReentrantReadWriteLock
.semaphore-local
implemented inorg.eclipse.aether.named.providers.LocalSemaphoreNamedLockFactory
that uses JVMjava.util.concurrent.Semaphore
.noop
implemented inorg.eclipse.aether.named.providers.NoopNamedLockFactory
that uses no locking.
Note about “local” locks: they are in-JVM, in a way, they properly coordinate in case of multithreaded access from same JVM, but do not cover accesses across multiple processes and/or multiple hosts access. In other words, local named locks are only suited within one JVM with a multithreaded access.
Following named lock implementations use underlying file system advisory file locking:
file-lock
implemented inorg.eclipse.aether.named.providers.FileLockNamedLockFactory
that uses JVMjava.nio.channels.FileLock
.
The file-lock
implementation uses file system advisory file locking, hence, concurrently running Maven processes
set up to use file-lock
implementation can safely share one local repository. This is almost certain on
local file systems across all operating systems. In case of NFS mounts, file advisory locking MAY work if NFSv4+
used with complete setup (with all the necessary services like RPC
and portmapper
needed to implement NFS advisory
file locking, check your NFS and/or OS manuals for details). In short: if your (local or remote) FS correctly support
and implements advisory locking, it should work. Local FS usually does, while with NFS your mileage may vary.
Finally, “distributed” named lock implementations are the following (separate modules which require additional dependencies and remote services):
rwlock-redisson
implemented inorg.eclipse.aether.named.redisson.RedissonReadWriteLockNamedLockFactory
.semaphore-redisson
implemented inorg.eclipse.aether.named.redisson.RedissonSemaphoreNamedLockFactory
.semaphore-hazelcast-client
implemented inorg.eclipse.aether.named.hazelcast.HazelcastClientCPSemaphoreNamedLockFactory
.semaphore-hazelcast
implemented inorg.eclipse.aether.named.hazelcast.HazelcastCPSemaphoreNamedLockFactory
.
Sharing a local repository between multiple hosts (i.e., on a busy CI server) may be best done with one of distributed named lock, if NFS locking is not working for you.
The aforementioned (opaque) IDs need to be mapped from artifacts and metadata.
Out of the box, name mapper implementations are the following:
static
implemented inorg.eclipse.aether.internal.impl.synccontext.named.StaticNameMapper
.gav
implemented inorg.eclipse.aether.internal.impl.synccontext.named.GAVNameMapper
.discriminating
implemented inorg.eclipse.aether.internal.impl.synccontext.named.DiscriminatingNameMapper
.file-gav
implemented inorg.eclipse.aether.internal.impl.synccontext.named.FileGAVNameMapper
.
Note: the file-gav
name mapper MUST be used with file-lock
named locking, no other mapper will work with it.
Diagnostic collection in case of failures
If you experience locking failures, to get access to full dump (may be huge in case of big builds) on failure, enable lock diagnostic collection. When enabled, “diagnostic dump” will be emitted by factory to INFO log (console by default). Please note, that enabling diagnostic dump may increase heap requirement (as diagnostic is collected in memory).
To enable diagnostic collection, set Java System Property aether.named.diagnostic.enabled
to true
.
If diagnostic collection is not enabled, Resolver will “just” fail, telling about failed lock, but not reveal any information about other active locks and threads.