private void pruneDeletedTombstones() {
    long timeMSec = engineConfig.getThreadPool().estimatedTimeInMillis();

    // TODO: not good that we reach into LiveVersionMap here; can we move this inside VersionMap
    // instead?  problem is the dirtyLock...

    // we only need to prune the deletes map; the current/old version maps are cleared on refresh:
    for (Map.Entry<BytesRef, VersionValue> entry : versionMap.getAllTombstones()) {
      BytesRef uid = entry.getKey();
      synchronized (
          dirtyLock(
              uid)) { // can we do it without this lock on each value? maybe batch to a set and get
                      // the lock once per set?

        // Must re-get it here, vs using entry.getValue(), in case the uid was indexed/deleted since
        // we pulled the iterator:
        VersionValue versionValue = versionMap.getTombstoneUnderLock(uid);
        if (versionValue != null) {
          if (timeMSec - versionValue.time() > getGcDeletesInMillis()) {
            versionMap.removeTombstoneUnderLock(uid);
          }
        }
      }
    }

    lastDeleteVersionPruneTimeMSec = timeMSec;
  }
  private boolean innerIndex(Index index) throws IOException {
    synchronized (dirtyLock(index.uid())) {
      lastWriteNanos = index.startTime();
      final long currentVersion;
      final boolean deleted;
      VersionValue versionValue = versionMap.getUnderLock(index.uid().bytes());
      if (versionValue == null) {
        currentVersion = loadCurrentVersionFromIndex(index.uid());
        deleted = currentVersion == Versions.NOT_FOUND;
      } else {
        deleted = versionValue.delete();
        if (engineConfig.isEnableGcDeletes()
            && versionValue.delete()
            && (engineConfig.getThreadPool().estimatedTimeInMillis() - versionValue.time())
                > getGcDeletesInMillis()) {
          currentVersion = Versions.NOT_FOUND; // deleted, and GC
        } else {
          currentVersion = versionValue.version();
        }
      }

      long expectedVersion = index.version();
      if (isVersionConflictForWrites(index, currentVersion, deleted, expectedVersion)) {
        if (index.origin() != Operation.Origin.RECOVERY) {
          throw new VersionConflictEngineException(
              shardId,
              index.type(),
              index.id(),
              index
                  .versionType()
                  .explainConflictForWrites(currentVersion, expectedVersion, deleted));
        }
        return false;
      }
      long updatedVersion = index.versionType().updateVersion(currentVersion, expectedVersion);

      final boolean created;
      index.updateVersion(updatedVersion);

      if (currentVersion == Versions.NOT_FOUND) {
        // document does not exists, we can optimize for create
        created = true;
        index(index, indexWriter);
      } else {
        created = update(index, versionValue, indexWriter);
      }
      Translog.Location translogLocation = translog.add(new Translog.Index(index));

      versionMap.putUnderLock(
          index.uid().bytes(), new VersionValue(updatedVersion, translogLocation));
      index.setTranslogLocation(translogLocation);
      return created;
    }
  }
  @Override
  public GetResult get(Get get, Function<String, Searcher> searcherFactory) throws EngineException {
    try (ReleasableLock lock = readLock.acquire()) {
      ensureOpen();
      if (get.realtime()) {
        VersionValue versionValue = versionMap.getUnderLock(get.uid().bytes());
        if (versionValue != null) {
          if (versionValue.delete()) {
            return GetResult.NOT_EXISTS;
          }
          if (get.versionType().isVersionConflictForReads(versionValue.version(), get.version())) {
            Uid uid = Uid.createUid(get.uid().text());
            throw new VersionConflictEngineException(
                shardId,
                uid.type(),
                uid.id(),
                get.versionType().explainConflictForReads(versionValue.version(), get.version()));
          }
          Translog.Operation op = translog.read(versionValue.translogLocation());
          if (op != null) {
            return new GetResult(true, versionValue.version(), op.getSource());
          }
        }
      }

      // no version, get the version from the index, we know that we refresh on flush
      return getFromSearcher(get, searcherFactory);
    }
  }
 private static boolean update(Index index, VersionValue versionValue, IndexWriter indexWriter)
     throws IOException {
   boolean created;
   if (versionValue != null) {
     created = versionValue.delete(); // we have a delete which is not GC'ed...
   } else {
     created = false;
   }
   if (index.docs().size() > 1) {
     indexWriter.updateDocuments(index.uid(), index.docs());
   } else {
     indexWriter.updateDocument(index.uid(), index.docs().get(0));
   }
   return created;
 }
  private void innerDelete(Delete delete) throws IOException {
    synchronized (dirtyLock(delete.uid())) {
      lastWriteNanos = delete.startTime();
      final long currentVersion;
      final boolean deleted;
      VersionValue versionValue = versionMap.getUnderLock(delete.uid().bytes());
      if (versionValue == null) {
        currentVersion = loadCurrentVersionFromIndex(delete.uid());
        deleted = currentVersion == Versions.NOT_FOUND;
      } else {
        deleted = versionValue.delete();
        if (engineConfig.isEnableGcDeletes()
            && versionValue.delete()
            && (engineConfig.getThreadPool().estimatedTimeInMillis() - versionValue.time())
                > getGcDeletesInMillis()) {
          currentVersion = Versions.NOT_FOUND; // deleted, and GC
        } else {
          currentVersion = versionValue.version();
        }
      }

      long updatedVersion;
      long expectedVersion = delete.version();
      if (delete
          .versionType()
          .isVersionConflictForWrites(currentVersion, expectedVersion, deleted)) {
        if (delete.origin() == Operation.Origin.RECOVERY) {
          return;
        } else {
          throw new VersionConflictEngineException(
              shardId,
              delete.type(),
              delete.id(),
              delete
                  .versionType()
                  .explainConflictForWrites(currentVersion, expectedVersion, deleted));
        }
      }
      updatedVersion = delete.versionType().updateVersion(currentVersion, expectedVersion);
      final boolean found;
      if (currentVersion == Versions.NOT_FOUND) {
        // doc does not exist and no prior deletes
        found = false;
      } else if (versionValue != null && versionValue.delete()) {
        // a "delete on delete", in this case, we still increment the version, log it, and return
        // that version
        found = false;
      } else {
        // we deleted a currently existing document
        indexWriter.deleteDocuments(delete.uid());
        found = true;
      }

      delete.updateVersion(updatedVersion, found);
      Translog.Location translogLocation = translog.add(new Translog.Delete(delete));
      versionMap.putUnderLock(
          delete.uid().bytes(),
          new DeleteVersionValue(
              updatedVersion,
              engineConfig.getThreadPool().estimatedTimeInMillis(),
              translogLocation));
      delete.setTranslogLocation(translogLocation);
    }
  }