<p dir="auto">Using Elasticsearch 1.4.1 and running on Ubuntu Trusty with Java 1.7.0_72, the following call in the start method of a river plugin never returns:</p>
<div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="client.admin().indices().prepareExists(&quot;common&quot;).execute().actionGet();"><pre class="notranslate"><span class="pl-s1">client</span>.<span class="pl-en">admin</span>().<span class="pl-en">indices</span>().<span class="pl-en">prepareExists</span>(<span class="pl-s">"common"</span>).<span class="pl-en">execute</span>().<span class="pl-en">actionGet</span>();</pre></div>
<p dir="auto">It works using Elasticsearch 1.3.6.</p>
<p dir="auto">It also works without using <code class="notranslate">admin()</code>:</p>
<div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="client.prepareExists(&quot;common&quot;).execute().actionGet();"><pre class="notranslate"><span class="pl-s1">client</span>.<span class="pl-en">prepareExists</span>(<span class="pl-s">"common"</span>).<span class="pl-en">execute</span>().<span class="pl-en">actionGet</span>();</pre></div>
<p dir="auto">It also works on MacOS.</p>
<p dir="auto">Maybe this is related to <a href="https://github.com/elasticsearch/elasticsearch-river-couchdb/issues/78">https://github.com/elasticsearch/elasticsearch-river-couchdb/issues/78</a> ?</p>
<p dir="auto">The Elasticsearch API supports all CRUD operations on every single document, topped by things like optimistic versioning controls and real time get API. To achieve this we need several data structures that support looking up existing documents both in real time (before a refresh) and if not found there, we need to do a lookup by ID in Lucene to make sure we find and replace any existing document.  For certain use cases, if our users can guarantee that documents are inserted once and are never changed again (like, for example logging, metrics or a one off re-indexing of existing data) this turns out to be a significant overhead in terms of indexing speed, without adding any real value. We sometimes refer to these scenarios as the "append only usecase" as well.</p>
<p dir="auto">In the past, we have tried to <a href="https://github.com/elastic/elasticsearch/pull/5917" data-hovercard-type="pull_request" data-hovercard-url="/elastic/elasticsearch/pull/5917/hovercard">introduce optimizations</a> in this area, but had to go and <a href="https://github.com/elastic/elasticsearch/pull/9468" data-hovercard-type="pull_request" data-hovercard-url="/elastic/elasticsearch/pull/9468/hovercard">revert them</a> as their have proven to be <a href="https://github.com/elastic/elasticsearch/issues/8788" data-hovercard-type="issue" data-hovercard-url="/elastic/elasticsearch/issues/8788/hovercard">unsafe</a> under cetrain race/error condition in our distributed system.</p>
<p dir="auto">Since then things have evolved and we now have more tools at our disposal. This issue is to track the list of things it will take to optimize the append only use case, while keeping it safe. Most concretely, we should explore not maintaining the <a href="https://github.com/elastic/elasticsearch/blob/master/core/src/main/java/org/elasticsearch/index/engine/LiveVersionMap.java">LiveVersionMap</a> and always use <code class="notranslate">addDocument</code> (rather then <code class="notranslate">updateDocument</code>) when indexing to Lucene.  Some of the things listed here are already done. Some require more work.</p>
<p dir="auto">The list is mostly focused on dealing with the main issue in this cases: when an indexing operation is sent to the primary, it maybe be that the sending node is disconnect from the node with a primary and thus have no idea if the operation was successful or failed. In those cases, the node will resend it's request once the connection is restored. This may result in a duplicate delivery on the primary as thus requires the usage of <code class="notranslate">updateDocument</code>. It is easy to add a 'isRetry` flag on those retry requests but we still run the danger of the retry request being processed before the original, in which case we will have duplicates. This scenario needs to be avoided at all costs.</p>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> Disable optimization when shard is recovering and do things as we do today. During recovery document may be written twice to a shard.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> Rely in primary terms to solve collisions between request originating from an old primary and a retry.</li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox"> <del>Rely on the task manager to avoid have an original request and a retry request executing concurrently on a node. For this we need to make sure they use the same task id and reject/have one wait on another.</del></li>
</ul>
<p dir="auto"><del>Having dealt with concurrency, we need to deal with the case where the retry request is already executed and removed form the task manager. This can be done as follows:</del></p>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox"> <del>Introduce a new “connection generation” id (a long) that is incremented every time a connection is (re)established between the nodes. This generation id will be added to every request and will also be exchanged as part of establishing a connection, making sure the target node is aware of the new generation id before exposing the connection outside of the transport service.</del></li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox"> <del>Networking threads on the target node will check that the generation id of a request is still the current one <em>after</em> adding the request to the task manager. If this is not the case the request will be removed from the task manager and will be failed (though there is no way to respond now, because the channel is closed). This guarantees that an old request will never be executed <em>after</em> a new connection was established. This in turn means it can not be processed after a retry request was processed.</del></li>
</ul>
<p dir="auto">Instead of the above we went with a simpler approach of dealing with concurrency:</p>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox" checked=""> add a timestamp to each request with an autogen id and create a "safety" barrier guaranteeing that all problematic requests (i.e., an original request that is processed after a retry is request) have a lower timestamp. This allows us to identify them and do the the right things. See details <a href="https://github.com/elastic/elasticsearch/issues/19813#issuecomment-243079678" data-hovercard-type="issue" data-hovercard-url="/elastic/elasticsearch/issues/19813/hovercard">below</a></li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox"> <del>Use an index level settings to indicate to the engine it can disable the <code class="notranslate">LiveVersionMap</code> and use <code class="notranslate">addDocument</code>, when the shard is started. This can be updated on a closed index.</del></li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox"> <del>Disable real time get API on these indices.</del></li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox"> <del>Disable indexing with an id.</del></li>
<li class="task-list-item"><input type="checkbox" id="" disabled="" class="task-list-item-checkbox"> ~~Disable delete &amp; update API ~~</li>
</ul>
<p dir="auto">Instead of disabling single doc update/delete/index with id, we have managed to come up with a scheme to skip adding append only operations to the <code class="notranslate">LiveVersionMap</code> and marking it as unsafe. Whenever a non-append only indexing operation comes along we detect that and force a refresh. See <a class="issue-link js-issue-link" data-error-text="Failed to load title" data-id="281016456" data-permission-text="Title is private" data-url="https://github.com/elastic/elasticsearch/issues/27752" data-hovercard-type="pull_request" data-hovercard-url="/elastic/elasticsearch/pull/27752/hovercard" href="https://github.com/elastic/elasticsearch/pull/27752">#27752</a></p>