**Build scan:**
https://gradle-enterprise.elastic.co/s/vugcgceftcmhk/tests/:server:internalClusterTest/org.elasticsearch.search.basic.SearchWithRandomIOExceptionsIT/testRandomDirectoryIOExceptions

**Reproduction line:**
```
./gradlew ':server:internalClusterTest' --tests "org.elasticsearch.search.basic.SearchWithRandomIOExceptionsIT.testRandomDirectoryIOExceptions" -Dtests.seed=6B3F5FB7EAF0379E -Dtests.locale=vi-VN -Dtests.timezone=Africa/Monrovia -Druntime.java=20
```

**Applicable branches:**
main

**Reproduces locally?:**
No

**Failure history:**
https://gradle-enterprise.elastic.co/scans/tests?tests.container=org.elasticsearch.search.basic.SearchWithRandomIOExceptionsIT&tests.test=testRandomDirectoryIOExceptions

**Failure excerpt:**
```
com.carrotsearch.randomizedtesting.UncaughtExceptionError: Captured an uncaught exception in thread: Thread[id=3707, name=elasticsearch[node_s1][generic][T#2], state=RUNNABLE, group=TGRP-SearchWithRandomIOExceptionsIT]


  Caused by: java.lang.AssertionError: java.lang.RuntimeException: MockDirectoryWrapper: cannot close: there are still 1 open files: {_2.cfs=1}

    at __randomizedtesting.SeedInfo.seed([6B3F5FB7EAF0379E]:0)
    at org.elasticsearch.core.AbstractRefCounted.decRef(AbstractRefCounted.java:71)
    at org.elasticsearch.index.store.Store.decRef(Store.java:406)
    at org.elasticsearch.index.shard.StoreRecovery.lambda$internalRecoverFromStore$9(StoreRecovery.java:488)
    at org.elasticsearch.action.ActionListenerImplementations$ResponseWrappingActionListener.onResponse(ActionListenerImplementations.java:236)
    at org.elasticsearch.action.ActionListenerImplementations$DelegatingResponseActionListener.onResponse(ActionListenerImplementations.java:182)
    at org.elasticsearch.index.CompositeIndexEventListener.callListeners(CompositeIndexEventListener.java:278)
    at org.elasticsearch.index.CompositeIndexEventListener.iterateBeforeIndexShardRecovery(CompositeIndexEventListener.java:287)
    at org.elasticsearch.index.CompositeIndexEventListener.beforeIndexShardRecovery(CompositeIndexEventListener.java:314)
    at org.elasticsearch.index.shard.IndexShard.preRecovery(IndexShard.java:1681)
    at org.elasticsearch.index.shard.StoreRecovery.internalRecoverFromStore(StoreRecovery.java:410)
    at org.elasticsearch.index.shard.StoreRecovery.recoverFromStore(StoreRecovery.java:92)
    at org.elasticsearch.index.shard.IndexShard.recoverFromStore(IndexShard.java:2338)
    at org.elasticsearch.action.ActionRunnable$3.doRun(ActionRunnable.java:73)
    at org.elasticsearch.common.util.concurrent.ThreadContext$ContextPreservingAbstractRunnable.doRun(ThreadContext.java:983)
    at org.elasticsearch.common.util.concurrent.AbstractRunnable.run(AbstractRunnable.java:26)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
    at java.lang.Thread.run(Thread.java:1623)

    Caused by: java.lang.RuntimeException: MockDirectoryWrapper: cannot close: there are still 1 open files: {_2.cfs=1}

      at org.apache.lucene.tests.store.MockDirectoryWrapper.close(MockDirectoryWrapper.java:876)
      at org.apache.lucene.store.FilterDirectory.close(FilterDirectory.java:111)
      at org.apache.lucene.store.FilterDirectory.close(FilterDirectory.java:111)
      at org.elasticsearch.index.store.Store$StoreDirectory.innerClose(Store.java:768)
      at org.elasticsearch.index.store.Store.closeInternal(Store.java:436)
      at org.elasticsearch.core.AbstractRefCounted$1.closeInternal(AbstractRefCounted.java:117)
      at org.elasticsearch.core.AbstractRefCounted.decRef(AbstractRefCounted.java:69)
      at org.elasticsearch.index.store.Store.decRef(Store.java:406)
      at org.elasticsearch.index.shard.StoreRecovery.lambda$internalRecoverFromStore$9(StoreRecovery.java:488)
      at org.elasticsearch.action.ActionListenerImplementations$ResponseWrappingActionListener.onResponse(ActionListenerImplementations.java:236)
      at org.elasticsearch.action.ActionListenerImplementations$DelegatingResponseActionListener.onResponse(ActionListenerImplementations.java:182)
      at org.elasticsearch.index.CompositeIndexEventListener.callListeners(CompositeIndexEventListener.java:278)
      at org.elasticsearch.index.CompositeIndexEventListener.iterateBeforeIndexShardRecovery(CompositeIndexEventListener.java:287)
      at org.elasticsearch.index.CompositeIndexEventListener.beforeIndexShardRecovery(CompositeIndexEventListener.java:314)
      at org.elasticsearch.index.shard.IndexShard.preRecovery(IndexShard.java:1681)
      at org.elasticsearch.index.shard.StoreRecovery.internalRecoverFromStore(StoreRecovery.java:410)
      at org.elasticsearch.index.shard.StoreRecovery.recoverFromStore(StoreRecovery.java:92)
      at org.elasticsearch.index.shard.IndexShard.recoverFromStore(IndexShard.java:2338)
      at org.elasticsearch.action.ActionRunnable$3.doRun(ActionRunnable.java:73)
      at org.elasticsearch.common.util.concurrent.ThreadContext$ContextPreservingAbstractRunnable.doRun(ThreadContext.java:983)
      at org.elasticsearch.common.util.concurrent.AbstractRunnable.run(AbstractRunnable.java:26)
      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
      at java.lang.Thread.run(Thread.java:1623)

      Caused by: java.lang.RuntimeException: unclosed IndexInput: _2.cfs

        at org.apache.lucene.tests.store.MockDirectoryWrapper.addFileHandle(MockDirectoryWrapper.java:783)
        at org.apache.lucene.tests.store.MockDirectoryWrapper.openInput(MockDirectoryWrapper.java:835)
        at org.apache.lucene.store.FilterDirectory.openInput(FilterDirectory.java:101)
        at org.apache.lucene.store.FilterDirectory.openInput(FilterDirectory.java:101)
        at org.apache.lucene.codecs.lucene90.Lucene90CompoundReader.<init>(Lucene90CompoundReader.java:78)
        at org.apache.lucene.codecs.lucene90.Lucene90CompoundFormat.getCompoundReader(Lucene90CompoundFormat.java:86)
        at org.apache.lucene.index.SegmentCoreReaders.<init>(SegmentCoreReaders.java:103)
        at org.apache.lucene.index.SegmentReader.<init>(SegmentReader.java:92)
        at org.apache.lucene.index.ReadersAndUpdates.getReader(ReadersAndUpdates.java:180)
        at org.apache.lucene.index.ReadersAndUpdates.getReadOnlyClone(ReadersAndUpdates.java:222)
        at org.apache.lucene.index.IndexWriter.lambda$getReader$0(IndexWriter.java:536)
        at org.apache.lucene.index.StandardDirectoryReader.open(StandardDirectoryReader.java:138)
        at org.apache.lucene.index.IndexWriter.getReader(IndexWriter.java:598)
        at org.apache.lucene.index.DirectoryReader.open(DirectoryReader.java:112)
        at org.apache.lucene.index.DirectoryReader.open(DirectoryReader.java:91)
        at org.elasticsearch.index.engine.InternalEngine.createReaderManager(InternalEngine.java:731)
        at org.elasticsearch.index.engine.InternalEngine.<init>(InternalEngine.java:283)
        at org.elasticsearch.index.engine.InternalEngine.<init>(InternalEngine.java:223)
        at org.elasticsearch.index.engine.InternalEngineFactory.newReadWriteEngine(InternalEngineFactory.java:14)
        at org.elasticsearch.index.shard.IndexShard.innerOpenEngineAndTranslog(IndexShard.java:2005)
        at org.elasticsearch.index.shard.IndexShard.openEngineAndRecoverFromTranslog(IndexShard.java:1969)
        at org.elasticsearch.index.shard.StoreRecovery.lambda$internalRecoverFromStore$9(StoreRecovery.java:479)
        at org.elasticsearch.action.ActionListenerImplementations$ResponseWrappingActionListener.onResponse(ActionListenerImplementations.java:236)
        at org.elasticsearch.action.ActionListenerImplementations$DelegatingResponseActionListener.onResponse(ActionListenerImplementations.java:182)
        at org.elasticsearch.index.CompositeIndexEventListener.callListeners(CompositeIndexEventListener.java:278)
        at org.elasticsearch.index.CompositeIndexEventListener.iterateBeforeIndexShardRecovery(CompositeIndexEventListener.java:287)
        at org.elasticsearch.index.CompositeIndexEventListener.beforeIndexShardRecovery(CompositeIndexEventListener.java:314)
        at org.elasticsearch.index.shard.IndexShard.preRecovery(IndexShard.java:1681)
        at org.elasticsearch.index.shard.StoreRecovery.internalRecoverFromStore(StoreRecovery.java:410)
        at org.elasticsearch.index.shard.StoreRecovery.recoverFromStore(StoreRecovery.java:92)
        at org.elasticsearch.index.shard.IndexShard.recoverFromStore(IndexShard.java:2338)
        at org.elasticsearch.action.ActionRunnable$3.doRun(ActionRunnable.java:73)
        at org.elasticsearch.common.util.concurrent.ThreadContext$ContextPreservingAbstractRunnable.doRun(ThreadContext.java:983)
        at org.elasticsearch.common.util.concurrent.AbstractRunnable.run(AbstractRunnable.java:26)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
        at java.lang.Thread.run(Thread.java:1623)

```
server/src/main/java/org/elasticsearch/index/engine/InternalEngine.java
&& relativeTimeInNanosSupplier.getAsLong() - lastFlushTimestamp < flushThresholdAgeInNanos) {
            return false;
        }
        /*
         * We flush to reduce the size of uncommitted translog but strictly speaking the uncommitted size won't always be
         * below the flush-threshold after a flush. To avoid getting into an endless loop of flushing, we only enable the
         * periodically flush condition if this condition is disabled after a flush. The condition will change if the new
         * commit points to the later generation the last commit's(eg. gen-of-last-commit < gen-of-new-commit)[1].
         *
         * When the local checkpoint equals to max_seqno, and translog-gen of the last commit equals to translog-gen of
         * the new commit, we know that the last generation must contain operations because its size is above the flush
         * threshold and the flush-threshold is guaranteed to be higher than an empty translog by the setting validation.
         * This guarantees that the new commit will point to the newly rolled generation. In fact, this scenario only
         * happens when the generation-threshold is close to or above the flush-threshold; otherwise we have rolled
         * generations as the generation-threshold was reached, then the first condition (eg. [1]) is already satisfied.
         *
         * This method is to maintain translog only, thus IndexWriter#hasUncommittedChanges condition is not considered.
         */
        final long translogGenerationOfNewCommit = translog.getMinGenerationForSeqNo(
            localCheckpointTracker.getProcessedCheckpoint() + 1
        ).translogFileGeneration;
        return translogGenerationOfLastCommit < translogGenerationOfNewCommit
            || localCheckpointTracker.getProcessedCheckpoint() == localCheckpointTracker.getMaxSeqNo();
    }
    @Override
    public void flush(boolean force, boolean waitIfOngoing, ActionListener<FlushResult> listener) throws EngineException {
        ensureOpen();
        if (force && waitIfOngoing == false) {
            assert false : "wait_if_ongoing must be true for a force flush: force=" + force + " wait_if_ongoing=" + waitIfOngoing;
            throw new IllegalArgumentException(