org.elasticsearch.common.lease.Releasable Java Examples

The following examples show how to use org.elasticsearch.common.lease.Releasable. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: test.java    From vscode-extension with MIT License 6 votes vote down vote up
@Override
public Closeable acquireRetentionLock() {
    if (softDeleteEnabled) {
        final Releasable softDeletesRetentionLock = softDeletesPolicy.acquireRetentionLock();
        final Closeable translogRetentionLock;
        try {
            translogRetentionLock = translog.acquireRetentionLock();
        } catch (Exception e) {
            softDeletesRetentionLock.close();
            throw e;
        }
        return () -> IOUtils.close(translogRetentionLock, softDeletesRetentionLock);
    } else {
        return translog.acquireRetentionLock();
    }
}
 
Example #2
Source File: RecoverySourceHandlerTests.java    From crate with Apache License 2.0 6 votes vote down vote up
@Test
public void testCancellationsDoesNotLeakPrimaryPermits() throws Exception {
    final CancellableThreads cancellableThreads = new CancellableThreads();
    final IndexShard shard = mock(IndexShard.class);
    final AtomicBoolean freed = new AtomicBoolean(true);
    when(shard.isRelocatedPrimary()).thenReturn(false);
    doAnswer(invocation -> {
        freed.set(false);
        ((ActionListener<Releasable>)invocation.getArguments()[0]).onResponse(() -> freed.set(true));
        return null;
    }).when(shard).acquirePrimaryOperationPermit(any(), anyString(), anyObject());

    Thread cancelingThread = new Thread(() -> cancellableThreads.cancel("test"));
    cancelingThread.start();
    try {
        RecoverySourceHandler.runUnderPrimaryPermit(() -> {}, "test", shard, cancellableThreads, logger);
    } catch (CancellableThreads.ExecutionCancelledException e) {
        // expected.
    }
    cancelingThread.join();
    // we have to use assert busy as we may be interrupted while acquiring the permit, if so we want to check
    // that the permit is released.
    assertBusy(() -> assertTrue(freed.get()));
}
 
Example #3
Source File: KeyedLock.java    From crate with Apache License 2.0 6 votes vote down vote up
/**
 * Tries to acquire the lock for the given key and returns it. If the lock can't be acquired null is returned.
 */
public Releasable tryAcquire(T key) {
    final KeyLock perNodeLock = map.get(key);
    if (perNodeLock == null) {
        return tryCreateNewLock(key);
    }
    if (perNodeLock.tryLock()) { // ok we got it - make sure we increment it accordingly otherwise release it again
        int i;
        while ((i = perNodeLock.count.get()) > 0) {
            // we have to do this in a loop here since even if the count is > 0
            // there could be a concurrent blocking acquire that changes the count and then this CAS fails. Since we already got
            // the lock we should retry and see if we can still get it or if the count is 0. If that is the case and we give up.
            if (perNodeLock.count.compareAndSet(i, i + 1)) {
                return new ReleasableLock(key, perNodeLock);
            }
        }
        perNodeLock.unlock(); // make sure we unlock and don't leave the lock in a locked state
    }
    return null;
}
 
Example #4
Source File: KeyedLock.java    From crate with Apache License 2.0 6 votes vote down vote up
/**
 * Acquires a lock for the given key. The key is compared by it's equals method not by object identity. The lock can be acquired
 * by the same thread multiple times. The lock is released by closing the returned {@link Releasable}.
 */
public Releasable acquire(T key) {
    while (true) {
        KeyLock perNodeLock = map.get(key);
        if (perNodeLock == null) {
            ReleasableLock newLock = tryCreateNewLock(key);
            if (newLock != null) {
                return newLock;
            }
        } else {
            int i = perNodeLock.count.get();
            if (i > 0 && perNodeLock.count.compareAndSet(i, i + 1)) {
                perNodeLock.lock();
                return new ReleasableLock(key, perNodeLock);
            }
        }
    }
}
 
Example #5
Source File: IndexShardOperationPermits.java    From crate with Apache License 2.0 6 votes vote down vote up
private Releasable acquire(Object debugInfo, StackTraceElement[] stackTrace) throws InterruptedException {
    assert Thread.holdsLock(this);
    if (semaphore.tryAcquire(1, 0, TimeUnit.SECONDS)) { // the un-timed tryAcquire methods do not honor the fairness setting
        final AtomicBoolean closed = new AtomicBoolean();
        final Releasable releasable = () -> {
            if (closed.compareAndSet(false, true)) {
                if (Assertions.ENABLED) {
                    Tuple<String, StackTraceElement[]> existing = issuedPermits.remove(closed);
                    assert existing != null;
                }
                semaphore.release(1);
            }
        };
        if (Assertions.ENABLED) {
            issuedPermits.put(closed, new Tuple<>(debugInfo.toString(), stackTrace));
        }
        return releasable;
    } else {
        // this should never happen, if it does something is deeply wrong
        throw new IllegalStateException("failed to obtain permit but operations are not delayed");
    }
}
 
Example #6
Source File: LiveVersionMap.java    From crate with Apache License 2.0 6 votes vote down vote up
/**
 * Try to prune tombstones whose timestamp is less than maxTimestampToPrune and seqno at most the maxSeqNoToPrune.
 */
void pruneTombstones(long maxTimestampToPrune, long maxSeqNoToPrune) {
    for (Map.Entry<BytesRef, DeleteVersionValue> entry : tombstones.entrySet()) {
        // we do check before we actually lock the key - this way we don't need to acquire the lock for tombstones that are not
        // prune-able. If the tombstone changes concurrently we will re-read and step out below since if we can't collect it now w
        // we won't collect the tombstone below since it must be newer than this one.
        if (canRemoveTombstone(maxTimestampToPrune, maxSeqNoToPrune, entry.getValue())) {
            final BytesRef uid = entry.getKey();
            try (Releasable lock = keyedLock.tryAcquire(uid)) {
                // we use tryAcquire here since this is a best effort and we try to be least disruptive
                // this method is also called under lock in the engine under certain situations such that this can lead to deadlocks
                // if we do use a blocking acquire. see #28714
                if (lock != null) { // did we get the lock?
                    // Must re-get it here, vs using entry.getValue(), in case the uid was indexed/deleted since we pulled the iterator:
                    final DeleteVersionValue versionValue = tombstones.get(uid);
                    if (versionValue != null) {
                        if (canRemoveTombstone(maxTimestampToPrune, maxSeqNoToPrune, versionValue)) {
                            removeTombstoneUnderLock(uid);
                        }
                    }
                }
            }
        }
    }
}
 
Example #7
Source File: SignificantTermsAggregatorFactory.java    From Elasticsearch with Apache License 2.0 5 votes vote down vote up
@Override
public void close() {
    try {
        if (termsEnum instanceof Releasable) {
            ((Releasable) termsEnum).close();
        }
    } finally {
        termsEnum = null;
    }
}
 
Example #8
Source File: TransportReplicationAction.java    From crate with Apache License 2.0 5 votes vote down vote up
/**
 * Tries to acquire reference to {@link IndexShard} to perform a primary operation. Released after performing primary operation locally
 * and replication of the operation to all replica shards is completed / failed (see {@link ReplicationOperation}).
 */
private void acquirePrimaryShardReference(ShardId shardId, String allocationId, long primaryTerm,
                                          ActionListener<PrimaryShardReference> onReferenceAcquired, Object debugInfo) {
    IndexShard indexShard = getIndexShard(shardId);
    // we may end up here if the cluster state used to route the primary is so stale that the underlying
    // index shard was replaced with a replica. For example - in a two node cluster, if the primary fails
    // the replica will take over and a replica will be assigned to the first node.
    if (indexShard.routingEntry().primary() == false) {
        throw new ReplicationOperation.RetryOnPrimaryException(indexShard.shardId(),
            "actual shard is not a primary " + indexShard.routingEntry());
    }
    final String actualAllocationId = indexShard.routingEntry().allocationId().getId();
    if (actualAllocationId.equals(allocationId) == false) {
        throw new ShardNotFoundException(shardId, "expected aID [{}] but found [{}]", allocationId, actualAllocationId);
    }
    final long actualTerm = indexShard.getPendingPrimaryTerm();
    if (actualTerm != primaryTerm) {
        throw new ShardNotFoundException(shardId, "expected aID [{}] with term [{}] but found [{}]", allocationId,
            primaryTerm, actualTerm);
    }

    ActionListener<Releasable> onAcquired = new ActionListener<Releasable>() {
        @Override
        public void onResponse(Releasable releasable) {
            onReferenceAcquired.onResponse(new PrimaryShardReference(indexShard, releasable));
        }

        @Override
        public void onFailure(Exception e) {
            onReferenceAcquired.onFailure(e);
        }
    };

    indexShard.acquirePrimaryOperationPermit(onAcquired, executor, debugInfo);
}
 
Example #9
Source File: TransportReplicationAction.java    From crate with Apache License 2.0 5 votes vote down vote up
@Override
public void onResponse(Releasable releasable) {
    try {
        final ReplicaResult replicaResult = shardOperationOnReplica(request, replica);
        releasable.close(); // release shard operation lock before responding to caller
        final TransportReplicationAction.ReplicaResponse response =
                new ReplicaResponse(replica.getLocalCheckpoint(), replica.getLastSyncedGlobalCheckpoint());
        replicaResult.respond(new ResponseListener(response));
    } catch (final Exception e) {
        Releasables.closeWhileHandlingException(releasable); // release shard operation lock before responding to caller
        AsyncReplicaAction.this.onFailure(e);
    }
}
 
Example #10
Source File: Translog.java    From crate with Apache License 2.0 5 votes vote down vote up
private Closeable acquireTranslogGenFromDeletionPolicy(long viewGen) {
    Releasable toClose = deletionPolicy.acquireTranslogGen(viewGen);
    return () -> {
        try {
            toClose.close();
        } finally {
            trimUnreferencedReaders();
            closeFilesIfNoPendingRetentionLocks();
        }
    };
}
 
Example #11
Source File: TranslogDeletionPolicy.java    From crate with Apache License 2.0 5 votes vote down vote up
/**
 * acquires the basis generation for a new snapshot. Any translog generation above, and including, the returned generation
 * will not be deleted until the returned {@link Releasable} is closed.
 */
synchronized Releasable acquireTranslogGen(final long translogGen) {
    translogRefCounts.computeIfAbsent(translogGen, l -> Counter.newCounter(false)).addAndGet(1);
    final AtomicBoolean closed = new AtomicBoolean();
    assert assertAddTranslogRef(closed);
    return () -> {
        if (closed.compareAndSet(false, true)) {
            releaseTranslogGen(translogGen);
            assert assertRemoveTranslogRef(closed);
        }
    };
}
 
Example #12
Source File: IndexShardOperationPermits.java    From crate with Apache License 2.0 5 votes vote down vote up
private PermitAwareThreadedActionListener(ThreadPool threadPool, String executor, ActionListener<Releasable> listener,
                                          boolean forceExecution) {
    this.threadPool = threadPool;
    this.executor = executor;
    this.listener = listener;
    this.forceExecution = forceExecution;
}
 
Example #13
Source File: IndexShardOperationPermits.java    From crate with Apache License 2.0 5 votes vote down vote up
private DelayedOperation(ActionListener<Releasable> listener, Object debugInfo, StackTraceElement[] stackTrace) {
    this.listener = listener;
    if (Assertions.ENABLED) {
        this.debugInfo = "[delayed] " + debugInfo;
        this.stackTrace = stackTrace;
    } else {
        this.debugInfo = null;
        this.stackTrace = null;
    }
}
 
Example #14
Source File: IndexShardOperationPermits.java    From crate with Apache License 2.0 5 votes vote down vote up
private void acquire(final ActionListener<Releasable> onAcquired, final String executorOnDelay, final boolean forceExecution,
                    final Object debugInfo, final StackTraceElement[] stackTrace) {
    if (closed) {
        onAcquired.onFailure(new IndexShardClosedException(shardId));
        return;
    }
    final Releasable releasable;
    try {
        synchronized (this) {
            if (queuedBlockOperations > 0) {
                final ActionListener<Releasable> wrappedListener;
                if (executorOnDelay != null) {
                    wrappedListener = new PermitAwareThreadedActionListener(
                        threadPool,
                        executorOnDelay,
                        onAcquired,
                        forceExecution
                    );
                } else {
                    wrappedListener = onAcquired;
                }
                delayedOperations.add(new DelayedOperation(wrappedListener, debugInfo, stackTrace));
                return;
            } else {
                releasable = acquire(debugInfo, stackTrace);
            }
        }
    } catch (final InterruptedException e) {
        onAcquired.onFailure(e);
        return;
    }
    // execute this outside the synchronized block!
    onAcquired.onResponse(releasable);
}
 
Example #15
Source File: SoftDeletesPolicy.java    From crate with Apache License 2.0 5 votes vote down vote up
/**
 * Acquires a lock on soft-deleted documents to prevent them from cleaning up in merge processes. This is necessary to
 * make sure that all operations that are being retained will be retained until the lock is released.
 * This is a analogy to the translog's retention lock; see {@link Translog#acquireRetentionLock()}
 */
synchronized Releasable acquireRetentionLock() {
    assert retentionLockCount >= 0 : "Invalid number of retention locks [" + retentionLockCount + "]";
    retentionLockCount++;
    final AtomicBoolean released = new AtomicBoolean();
    return () -> {
        if (released.compareAndSet(false, true)) {
            releaseRetentionLock();
        }
    };
}
 
Example #16
Source File: RecoverySourceHandler.java    From crate with Apache License 2.0 5 votes vote down vote up
static void runUnderPrimaryPermit(CancellableThreads.Interruptable runnable, String reason,
                                  IndexShard primary, CancellableThreads cancellableThreads, Logger logger) {
    cancellableThreads.execute(() -> {
        CompletableFuture<Releasable> permit = new CompletableFuture<>();
        final ActionListener<Releasable> onAcquired = new ActionListener<>() {
            @Override
            public void onResponse(Releasable releasable) {
                if (permit.complete(releasable) == false) {
                    releasable.close();
                }
            }

            @Override
            public void onFailure(Exception e) {
                permit.completeExceptionally(e);
            }
        };
        primary.acquirePrimaryOperationPermit(onAcquired, ThreadPool.Names.SAME, reason);
        try (Releasable ignored = FutureUtils.get(permit)) {
            // check that the IndexShard still has the primary authority. This needs to be checked under operation permit to prevent
            // races, as IndexShard will switch its authority only when it holds all operation permits, see IndexShard.relocated()
            if (primary.isRelocatedPrimary()) {
                throw new IndexShardRelocatedException(primary.shardId());
            }
            runnable.run();
        } finally {
            // just in case we got an exception (likely interrupted) while waiting for the get
            permit.whenComplete((r, e) -> {
                if (r != null) {
                    r.close();
                }
                if (e != null) {
                    logger.trace("suppressing exception on completion (it was already bubbled up or the operation was aborted)", e);
                }
            });
        }
    });
}
 
Example #17
Source File: SearchContext.java    From Elasticsearch with Apache License 2.0 5 votes vote down vote up
/**
 * Schedule the release of a resource. The time when {@link Releasable#close()} will be called on this object
 * is function of the provided {@link Lifetime}.
 */
public void addReleasable(Releasable releasable, Lifetime lifetime) {
    if (clearables == null) {
        clearables = MultimapBuilder.enumKeys(Lifetime.class).arrayListValues().build();
    }
    clearables.put(lifetime, releasable);
}
 
Example #18
Source File: SearchContext.java    From Elasticsearch with Apache License 2.0 5 votes vote down vote up
public void clearReleasables(Lifetime lifetime) {
    if (clearables != null) {
        List<Collection<Releasable>> releasables = new ArrayList<>();
        for (Lifetime lc : Lifetime.values()) {
            if (lc.compareTo(lifetime) > 0) {
                break;
            }
            releasables.add(clearables.removeAll(lc));
        }
        Releasables.close(Iterables.concat(releasables));
    }
}
 
Example #19
Source File: ParentChildIndexFieldData.java    From Elasticsearch with Apache License 2.0 5 votes vote down vote up
@Override
public void close() {
    List<Releasable> closeables = new ArrayList<>();
    for (OrdinalMapAndAtomicFieldData fds : atomicFD.values()) {
        closeables.addAll(Arrays.asList(fds.fieldData));
    }
    Releasables.close(closeables);
}
 
Example #20
Source File: Engine.java    From Elasticsearch with Apache License 2.0 4 votes vote down vote up
public Releasable acquireThrottle() {
    return lock.acquire();
}
 
Example #21
Source File: BucketsAggregator.java    From Elasticsearch with Apache License 2.0 4 votes vote down vote up
@Override
public final void close() {
    try (Releasable releasable = docCounts) {
        super.close();
    }
}
 
Example #22
Source File: TransportReplicationAction.java    From crate with Apache License 2.0 4 votes vote down vote up
PrimaryShardReference(IndexShard indexShard, Releasable operationLock) {
    super(indexShard, operationLock);
}
 
Example #23
Source File: TransportReplicationAction.java    From crate with Apache License 2.0 4 votes vote down vote up
ShardReference(IndexShard indexShard, Releasable operationLock) {
    this.indexShard = indexShard;
    this.operationLock = operationLock;
}
 
Example #24
Source File: NettyHttpChannel.java    From Elasticsearch with Apache License 2.0 4 votes vote down vote up
@Override
public void doSendResponse(RestResponse response) {
    // if the response object was created upstream, then use it;
    // otherwise, create a new one
    HttpResponse resp = newResponse();
    resp.setStatus(getStatus(response.status()));

    CorsHandler.setCorsResponseHeaders(nettyRequest, resp, transport.getCorsConfig());

    String opaque = nettyRequest.headers().get("X-Opaque-Id");
    if (opaque != null) {
        resp.headers().add("X-Opaque-Id", opaque);
    }

    // Add all custom headers
    Map<String, List<String>> customHeaders = response.getHeaders();
    if (customHeaders != null) {
        for (Map.Entry<String, List<String>> headerEntry : customHeaders.entrySet()) {
            for (String headerValue : headerEntry.getValue()) {
                resp.headers().add(headerEntry.getKey(), headerValue);
            }
        }
    }

    BytesReference content = response.content();
    ChannelBuffer buffer;
    boolean addedReleaseListener = false;
    try {
        buffer = content.toChannelBuffer();
        resp.setContent(buffer);

        // If our response doesn't specify a content-type header, set one
        if (!resp.headers().contains(HttpHeaders.Names.CONTENT_TYPE)) {
            resp.headers().add(HttpHeaders.Names.CONTENT_TYPE, response.contentType());
        }

        // If our response has no content-length, calculate and set one
        if (!resp.headers().contains(HttpHeaders.Names.CONTENT_LENGTH)) {
            resp.headers().add(HttpHeaders.Names.CONTENT_LENGTH, String.valueOf(buffer.readableBytes()));
        }

        if (transport.resetCookies) {
            String cookieString = nettyRequest.headers().get(HttpHeaders.Names.COOKIE);
            if (cookieString != null) {
                CookieDecoder cookieDecoder = new CookieDecoder();
                Set<Cookie> cookies = cookieDecoder.decode(cookieString);
                if (!cookies.isEmpty()) {
                    // Reset the cookies if necessary.
                    CookieEncoder cookieEncoder = new CookieEncoder(true);
                    for (Cookie cookie : cookies) {
                        cookieEncoder.addCookie(cookie);
                    }
                    resp.headers().add(HttpHeaders.Names.SET_COOKIE, cookieEncoder.encode());
                }
            }
        }

        ChannelFuture future;

        if (orderedUpstreamMessageEvent != null) {
            OrderedDownstreamChannelEvent downstreamChannelEvent = new OrderedDownstreamChannelEvent(orderedUpstreamMessageEvent, 0, true, resp);
            future = downstreamChannelEvent.getFuture();
            channel.getPipeline().sendDownstream(downstreamChannelEvent);
        } else {
            future = channel.write(resp);
        }

        if (content instanceof Releasable) {
            future.addListener(new ReleaseChannelFutureListener((Releasable) content));
            addedReleaseListener = true;
        }

        if (isCloseConnection()) {
            future.addListener(ChannelFutureListener.CLOSE);
        }

    } finally {
        if (!addedReleaseListener && content instanceof Releasable) {
            ((Releasable) content).close();
        }
    }
}
 
Example #25
Source File: BigArrays.java    From crate with Apache License 2.0 4 votes vote down vote up
AbstractArrayWrapper(BigArrays bigArrays, long size, Releasable releasable, boolean clearOnResize) {
    super(bigArrays, clearOnResize);
    this.releasable = releasable;
    this.size = size;
}
 
Example #26
Source File: ReleasablePagedBytesReference.java    From crate with Apache License 2.0 4 votes vote down vote up
public ReleasablePagedBytesReference(ByteArray byteArray, int length, Releasable releasable) {
    super(byteArray, length);
    this.releasable = releasable;
}
 
Example #27
Source File: PreVoteCollectorTests.java    From crate with Apache License 2.0 4 votes vote down vote up
private void startAndRunCollector(DiscoveryNode... votingNodes) {
    try (Releasable ignored = startCollector(votingNodes)) {
        runCollector();
    }
}
 
Example #28
Source File: Engine.java    From crate with Apache License 2.0 4 votes vote down vote up
public Releasable acquireThrottle() {
    return lock.acquire();
}
 
Example #29
Source File: ReleaseChannelFutureListener.java    From Elasticsearch with Apache License 2.0 4 votes vote down vote up
public ReleaseChannelFutureListener(Releasable releasable) {
    this.releasable = releasable;
}
 
Example #30
Source File: IndexShard.java    From crate with Apache License 2.0 4 votes vote down vote up
/**
 * Acquire a replica operation permit whenever the shard is ready for indexing (see
 * {@link #acquirePrimaryOperationPermit(ActionListener, String, Object)}). If the given primary term is lower than then one in
 * {@link #shardRouting}, the {@link ActionListener#onFailure(Exception)} method of the provided listener is invoked with an
 * {@link IllegalStateException}. If permit acquisition is delayed, the listener will be invoked on the executor with the specified
 * name.
 *
 * @param opPrimaryTerm              the operation primary term
 * @param globalCheckpoint           the global checkpoint associated with the request
 * @param maxSeqNoOfUpdatesOrDeletes the max seq_no of updates (index operations overwrite Lucene) or deletes captured on the primary
 *                                   after this replication request was executed on it (see {@link #getMaxSeqNoOfUpdatesOrDeletes()}
 * @param onPermitAcquired           the listener for permit acquisition
 * @param executorOnDelay            the name of the executor to invoke the listener on if permit acquisition is delayed
 * @param debugInfo                  an extra information that can be useful when tracing an unreleased permit. When assertions are
 *                                   enabled the tracing will capture the supplied object's {@link Object#toString()} value.
 *                                   Otherwise the object isn't used
 */
public void acquireReplicaOperationPermit(final long opPrimaryTerm,
                                          final long globalCheckpoint,
                                          final long maxSeqNoOfUpdatesOrDeletes,
                                          final ActionListener<Releasable> onPermitAcquired,
                                          final String executorOnDelay,
                                          final Object debugInfo) {
    verifyNotClosed();
    if (opPrimaryTerm > pendingPrimaryTerm) {
        synchronized (mutex) {
            if (opPrimaryTerm > pendingPrimaryTerm) {
                IndexShardState shardState = state();
                // only roll translog and update primary term if shard has made it past recovery
                // Having a new primary term here means that the old primary failed and that there is a new primary, which again
                // means that the master will fail this shard as all initializing shards are failed when a primary is selected
                // We abort early here to prevent an ongoing recovery from the failed primary to mess with the global / local checkpoint
                if (shardState != IndexShardState.POST_RECOVERY &&
                    shardState != IndexShardState.STARTED) {
                    throw new IndexShardNotStartedException(shardId, shardState);
                }

                if (opPrimaryTerm > pendingPrimaryTerm) {
                    bumpPrimaryTerm(opPrimaryTerm, () -> {
                        updateGlobalCheckpointOnReplica(globalCheckpoint, "primary term transition");
                        final long currentGlobalCheckpoint = getLastKnownGlobalCheckpoint();
                        final long maxSeqNo = seqNoStats().getMaxSeqNo();
                        logger.info("detected new primary with primary term [{}], global checkpoint [{}], max_seq_no [{}]",
                            opPrimaryTerm, currentGlobalCheckpoint, maxSeqNo);
                        if (currentGlobalCheckpoint < maxSeqNo) {
                            resetEngineToGlobalCheckpoint();
                        } else {
                            getEngine().rollTranslogGeneration();
                        }
                    });
                }
            }
        }
    }

    assert opPrimaryTerm <= pendingPrimaryTerm
            : "operation primary term [" + opPrimaryTerm + "] should be at most [" + pendingPrimaryTerm + "]";
    indexShardOperationPermits.acquire(
            new ActionListener<Releasable>() {
                @Override
                public void onResponse(final Releasable releasable) {
                    if (opPrimaryTerm < operationPrimaryTerm) {
                        releasable.close();
                        final String message = String.format(
                                Locale.ROOT,
                                "%s operation primary term [%d] is too old (current [%d])",
                                shardId,
                                opPrimaryTerm,
                                operationPrimaryTerm);
                        onPermitAcquired.onFailure(new IllegalStateException(message));
                    } else {
                        assert assertReplicationTarget();
                        try {
                            updateGlobalCheckpointOnReplica(globalCheckpoint, "operation");
                            advanceMaxSeqNoOfUpdatesOrDeletes(maxSeqNoOfUpdatesOrDeletes);
                        } catch (Exception e) {
                            releasable.close();
                            onPermitAcquired.onFailure(e);
                            return;
                        }
                        onPermitAcquired.onResponse(releasable);
                    }
                }

                @Override
                public void onFailure(final Exception e) {
                    onPermitAcquired.onFailure(e);
                }
            },
            executorOnDelay,
            true, debugInfo);
}