public void clear(final DateTime effectiveDate, final InternalCallContext context) throws OverdueException, OverdueApiException { GlobalLock lock = null; try { lock = locker.lockWithNumberOfTries(LockerType.ACCNT_INV_PAY.toString(), overdueable.getId().toString(), MAX_LOCK_RETRIES); clearWithLock(effectiveDate, context); } catch (final LockFailedException e) { log.warn("Failed to clear overdue for accountId='{}'", overdueable.getId(), e); } finally { if (lock != null) { lock.release(); } } }
@Test(groups = "slow") public void testSimpleLocking() throws IOException, LockFailedException { final String lockName = UUID.randomUUID().toString(); final GlobalLock lock = locker.lockWithNumberOfTries(LockerType.ACCNT_INV_PAY.toString(), lockName, 3); dbi.inTransaction(new TransactionCallback<Void>() { @Override public Void inTransaction(final Handle conn, final TransactionStatus status) throws Exception { conn.execute("insert into dummy2 (dummy_id) values ('" + UUID.randomUUID().toString() + "')"); return null; } }); Assert.assertEquals(locker.isFree(LockerType.ACCNT_INV_PAY.toString(), lockName), false); boolean gotException = false; try { locker.lockWithNumberOfTries(LockerType.ACCNT_INV_PAY.toString(), lockName, 1); } catch (LockFailedException e) { gotException = true; } Assert.assertTrue(gotException); lock.release(); Assert.assertEquals(locker.isFree(LockerType.ACCNT_INV_PAY.toString(), lockName), true); } }
@BeforeClass(groups = "slow") public void beforeClass() throws Exception { if (hasFailed()) { return; } final Injector g = Guice.createInjector(Stage.PRODUCTION, new TestUtilModuleWithEmbeddedDB(configSource, clock)); g.injectMembers(this); Assert.assertTrue(locker.isFree("a", "b")); }
/** * Atomically increment the refCount lock if we are already the owner of that lock. * * @param lockName * @return the ReentrantLockState: lock is FREE, or we already hold it (and incremented the refCount) or it held by somebody else */ public TryAcquireLockState tryAcquireLockForExistingOwner(final String lockName) { synchronized (lockTable) { final LockHolder lockHolder = lockTable.get(lockName); if (lockHolder == null) { return new TryAcquireLockState(ReentrantLockState.FREE); } final String maybeNullRequestId = getRequestId(); if (maybeNullRequestId == null || !lockHolder.getRequestId().equals(maybeNullRequestId)) { return new TryAcquireLockState(ReentrantLockState.HELD_NOT_OWNER); } else { // Increment value before we return while we hold the lockTable lock. lockHolder.increment(); return new TryAcquireLockState(ReentrantLockState.HELD_OWNER, lockHolder.getOriginalLock()); } } }
protected GlobalLock lock(final String lockName) { final TryAcquireLockState lockState = lockTable.tryAcquireLockForExistingOwner(lockName); if (lockState.getLockState() == ReentrantLock.ReentrantLockState.HELD_OWNER) { return lockState.getOriginalLock(); } if (lockState.getLockState() == ReentrantLock.ReentrantLockState.HELD_NOT_OWNER) { // In that case, we need to respect the provided timeout value try { Thread.sleep(TimeUnit.MILLISECONDS.convert(timeout, timeUnit)); } catch (final InterruptedException e) { Thread.currentThread().interrupt(); logger.warn("lock got interrupted", e); } return null; } return doLock(lockName); }
/** * Create a new LockHolder. This is done *after* the distributed lock was acquired. * * @param lockName */ public void createLock(final String lockName, final GlobalLock originalLock) { final String requestId = getRequestId(); if (requestId == null) { return; } synchronized (lockTable) { LockHolder lockHolder = lockTable.get(lockName); if (lockHolder != null) { throw new IllegalStateException(String.format("ReentrantLock createLock %s : lock already current request = %s, owner request = %s", lockName, requestId, lockHolder.getRequestId())); } if (lockHolder == null) { lockHolder = new LockHolder(requestId, originalLock); lockTable.put(lockName, lockHolder); } lockHolder.increment(); } }
@Override public GlobalLock execute() throws LockFailedException { final String lockName = getLockName(service, lockKey); int tries_left = retry; while (tries_left-- > 0) { final GlobalLock lock = lock(lockName); if (lock != null) { return lock; } if (tries_left > 0) { sleep(); } } logger.warn(String.format("Failed to acquire lock %s for service %s after %s retries", lockKey, service, retry)); throw new LockFailedException(); } });
@Override protected GlobalLock doLock(final String lockName) { Connection connection = null; boolean obtained = false; try { connection = dataSource.getConnection(); obtained = globalLockDao.lock(connection, lockName, timeout, timeUnit); if (obtained) { final GlobalLock lock = getGlobalLock(connection, lockName, new ResetReentrantLockCallback() { @Override public boolean reset(final String lockName) { return lockTable.releaseLock(lockName); } }); lockTable.createLock(lockName, lock); return lock; } } catch (final SQLException e) { logger.warn("Unable to obtain lock for {}", lockName, e); } finally { if (!obtained && connection != null) { try { connection.close(); } catch (final SQLException e) { logger.warn("Unable to close connection", e); } } } return null; }
/** * Release a lock. This is always called when the resources are feed * * @param lockName * @return true if nobody still holds the reentrant lock (and thefeore distributed lock can be freed) */ public boolean releaseLock(final String lockName) { // In case there no requestId set, this was not a 'reentrant' lock, so nothing to do but we need to return true // so distributed lock can be released // final String requestId = getRequestId(); if (requestId == null) { return true; } synchronized (lockTable) { final LockHolder lockHolder = lockTable.get(lockName); if (lockHolder == null) { throw new IllegalStateException(String.format("ReentrantLock releaseLock %s : cannot find lock in the table, current request = %s", lockName, requestId)); } if (!lockHolder.getRequestId().equals(requestId)) { throw new IllegalStateException(String.format("ReentrantLock releaseLock %s : current request = %s, owner request = %s", lockName, requestId, lockHolder.getRequestId())); } final boolean free = lockHolder.decrement(); if (free) { lockTable.remove(lockName); } return free; } }
@Override public Void execute() throws RuntimeException { if (resetCallback != null && !resetCallback.reset(lockName)) { // We are not the last one using that lock, bail early (AND don't close the connection)... return null; } try { lockDao.releaseLock(connection, lockName); } catch (final SQLException e) { logger.warn("Unable to release lock for " + lockName, e); } finally { try { connection.close(); } catch (final SQLException e) { logger.warn("Unable to close connection", e); } } return null; } });
@Override public boolean isFree(final String service, final String lockKey) { final String lockName = getLockName(service, lockKey); Connection connection = null; try { connection = dataSource.getConnection(); return globalLockDao.isLockFree(connection, lockName); } catch (final SQLException e) { logger.warn("Unable to check if lock is free", e); return false; } finally { if (connection != null) { try { connection.close(); } catch (final SQLException e) { logger.warn("Unable to close connection", e); } } } }
@Override public boolean reset(final String lockName) { return lockTable.releaseLock(lockName); } });
public GlobalLockerBase(final long timeout, final TimeUnit timeUnit) { this.timeout = timeout; this.timeUnit = timeUnit; this.lockTable = new ReentrantLock(); this.prof = new Profiling<GlobalLock, LockFailedException>(); }
public OverdueState refresh(final DateTime effectiveDate, final InternalCallContext context) throws OverdueException, OverdueApiException { if (overdueStateSet.size() < 1) { // No configuration available return overdueStateSet.getClearState(); } GlobalLock lock = null; try { lock = locker.lockWithNumberOfTries(LockerType.ACCNT_INV_PAY.toString(), overdueable.getId().toString(), MAX_LOCK_RETRIES); return refreshWithLock(effectiveDate, context); } catch (final LockFailedException e) { log.warn("Failed to process overdue for accountId='{}'", overdueable.getId(), e); } finally { if (lock != null) { lock.release(); } } return null; }
@Test(groups = "slow") public void testSimpleLocking() throws IOException, LockFailedException { final String lockName = UUID.randomUUID().toString(); final GlobalLock lock = locker.lockWithNumberOfTries(LockerType.ACCNT_INV_PAY.toString(), lockName, 3); dbi.inTransaction(new TransactionCallback<Void>() { @Override public Void inTransaction(final Handle conn, final TransactionStatus status) throws Exception { conn.execute("insert into dummy2 (dummy_id) values ('" + UUID.randomUUID().toString() + "')"); return null; } }); Assert.assertEquals(locker.isFree(LockerType.ACCNT_INV_PAY.toString(), lockName), false); boolean gotException = false; try { locker.lockWithNumberOfTries(LockerType.ACCNT_INV_PAY.toString(), lockName, 1); } catch (LockFailedException e) { gotException = true; } Assert.assertTrue(gotException); lock.release(); Assert.assertEquals(locker.isFree(LockerType.ACCNT_INV_PAY.toString(), lockName), true); } }
@Override public void release() { if (lockTable.releaseLock(lockName)) { locks.get(lockName).set(false); } } };
public void clear(final DateTime effectiveDate, final InternalCallContext context) throws OverdueException, OverdueApiException { GlobalLock lock = null; try { lock = locker.lockWithNumberOfTries(LockerType.ACCNT_INV_PAY.toString(), overdueable.getId().toString(), MAX_LOCK_RETRIES); clearWithLock(effectiveDate, context); } catch (final LockFailedException e) { log.warn("Failed to clear overdue for accountId='{}'", overdueable.getId(), e); } finally { if (lock != null) { lock.release(); } } }
public void processParentInvoiceForAdjustments(final Account childAccount, final UUID childInvoiceId, final InternalCallContext context) throws InvoiceApiException { GlobalLock lock = null; try { lock = locker.lockWithNumberOfTries(LockerType.ACCNT_INV_PAY.toString(), childAccount.getParentAccountId().toString(), invoiceConfig.getMaxGlobalLockRetries()); processParentInvoiceForAdjustmentsWithLock(childAccount, childInvoiceId, context); } catch (final LockFailedException e) { log.warn("Failed to process parent invoice for parentAccountId='{}'", childAccount.getParentAccountId().toString(), e); } finally { if (lock != null) { lock.release(); } } }
public void processParentInvoiceForInvoiceGeneration(final Account childAccount, final UUID childInvoiceId, final InternalCallContext context) throws InvoiceApiException { GlobalLock lock = null; try { lock = locker.lockWithNumberOfTries(LockerType.ACCNT_INV_PAY.toString(), childAccount.getParentAccountId().toString(), invoiceConfig.getMaxGlobalLockRetries()); processParentInvoiceForInvoiceGenerationWithLock(childAccount, childInvoiceId, context); } catch (final LockFailedException e) { log.warn("Failed to process parent invoice for parentAccountId='{}'", childAccount.getParentAccountId().toString(), e); } finally { if (lock != null) { lock.release(); } } }
public OverdueState refresh(final DateTime effectiveDate, final InternalCallContext context) throws OverdueException, OverdueApiException { if (overdueStateSet.size() < 1) { // No configuration available return overdueStateSet.getClearState(); } GlobalLock lock = null; try { lock = locker.lockWithNumberOfTries(LockerType.ACCNT_INV_PAY.toString(), overdueable.getId().toString(), MAX_LOCK_RETRIES); return refreshWithLock(effectiveDate, context); } catch (final LockFailedException e) { log.warn("Failed to process overdue for accountId='{}'", overdueable.getId(), e); } finally { if (lock != null) { lock.release(); } } return null; }