{"action":"created","comment":{"url":"https://api.github.com/repos/elastic/elasticsearch/pulls/comments/30436871","id":30436871,"diff_hunk":"@@ -0,0 +1,755 @@\n+/*\n+ * Licensed to Elasticsearch under one or more contributor\n+ * license agreements. See the NOTICE file distributed with\n+ * this work for additional information regarding copyright\n+ * ownership. Elasticsearch licenses this file to you under\n+ * the Apache License, Version 2.0 (the \"License\"); you may\n+ * not use this file except in compliance with the License.\n+ * You may obtain a copy of the License at\n+ *\n+ *    http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ * Unless required by applicable law or agreed to in writing,\n+ * software distributed under the License is distributed on an\n+ * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n+ * KIND, either express or implied.  See the License for the\n+ * specific language governing permissions and limitations\n+ * under the License.\n+ */\n+package org.elasticsearch.indices;\n+\n+import com.google.common.collect.ImmutableMap;\n+import org.elasticsearch.ElasticsearchException;\n+import org.elasticsearch.action.ActionListener;\n+import org.elasticsearch.action.admin.indices.flush.FlushRequest;\n+import org.elasticsearch.cluster.ClusterService;\n+import org.elasticsearch.cluster.ClusterState;\n+import org.elasticsearch.cluster.metadata.IndexMetaData;\n+import org.elasticsearch.cluster.node.DiscoveryNode;\n+import org.elasticsearch.cluster.routing.ImmutableShardRouting;\n+import org.elasticsearch.cluster.routing.IndexRoutingTable;\n+import org.elasticsearch.cluster.routing.IndexShardRoutingTable;\n+import org.elasticsearch.cluster.routing.ShardRouting;\n+import org.elasticsearch.common.Strings;\n+import org.elasticsearch.common.component.AbstractComponent;\n+import org.elasticsearch.common.inject.Inject;\n+import org.elasticsearch.common.io.stream.StreamInput;\n+import org.elasticsearch.common.io.stream.StreamOutput;\n+import org.elasticsearch.common.settings.Settings;\n+import org.elasticsearch.common.unit.TimeValue;\n+import org.elasticsearch.common.util.concurrent.ConcurrentCollections;\n+import org.elasticsearch.common.util.concurrent.CountDown;\n+import org.elasticsearch.index.IndexService;\n+import org.elasticsearch.index.IndexShardMissingException;\n+import org.elasticsearch.index.engine.Engine;\n+import org.elasticsearch.index.shard.IndexShard;\n+import org.elasticsearch.index.shard.IndexShardException;\n+import org.elasticsearch.index.shard.ShardId;\n+import org.elasticsearch.threadpool.ThreadPool;\n+import org.elasticsearch.transport.*;\n+\n+import java.io.IOException;\n+import java.util.HashMap;\n+import java.util.List;\n+import java.util.Map;\n+import java.util.concurrent.CountDownLatch;\n+import java.util.concurrent.TimeUnit;\n+import java.util.concurrent.atomic.AtomicInteger;\n+\n+public class SyncedFlushService extends AbstractComponent {\n+\n+    public static final String PRE_SYNCED_FLUSH_ACTION_NAME = \"internal:indices/flush/synced/pre\";\n+    public static final String SYNCED_FLUSH_ACTION_NAME = \"internal:indices/flush/synced/sync\";\n+    public static final String IN_FLIGHT_OPS_ACTION_NAME = \"internal:indices/flush/synced/in_flight\";\n+\n+    public static final String SETTING_PRE_SYNC_TIMEOUT = \"indices.flush.synced.presync_timeout\";\n+    public static final String SETTING_SYNC_TIMEOUT = \"indices.flush.synced.sync_timeout\";\n+    public static final String SETTING_IN_FLIGHT_OPS_TIMEOUT = \"indices.flush.synced.in_flight_ops_timeout\";\n+\n+    private final IndicesService indicesService;\n+    private final ClusterService clusterService;\n+    private final TransportService transportService;\n+    private final TimeValue preSyncTimeout;\n+    private final TimeValue syncTimeout;\n+    private final TimeValue inflightOpsTimeout;\n+\n+    @Inject\n+    public SyncedFlushService(Settings settings, IndicesService indicesService, ClusterService clusterService, TransportService transportService) {\n+        super(settings);\n+        this.indicesService = indicesService;\n+        this.clusterService = clusterService;\n+        this.transportService = transportService;\n+\n+        transportService.registerRequestHandler(PRE_SYNCED_FLUSH_ACTION_NAME, PreSyncedFlushRequest.class, ThreadPool.Names.FLUSH, new PreSyncedFlushTransportHandler());\n+        transportService.registerRequestHandler(SYNCED_FLUSH_ACTION_NAME, SyncedFlushRequest.class, ThreadPool.Names.FLUSH, new SyncedFlushTransportHandler());\n+        transportService.registerRequestHandler(IN_FLIGHT_OPS_ACTION_NAME, InFlightOpsRequest.class, ThreadPool.Names.SAME, new InFlightOpCountTransportHandler());\n+        preSyncTimeout = settings.getAsTime(SETTING_PRE_SYNC_TIMEOUT, TimeValue.timeValueMinutes(5));\n+        syncTimeout = settings.getAsTime(SETTING_SYNC_TIMEOUT, TimeValue.timeValueMinutes(5));\n+        inflightOpsTimeout = settings.getAsTime(SETTING_IN_FLIGHT_OPS_TIMEOUT, TimeValue.timeValueMinutes(5));\n+        indicesService.indicesLifecycle().addListener(new IndicesLifecycle.Listener() {\n+            @Override\n+            public void onShardInactive(final IndexShard indexShard) {\n+                // we only want to call sync flush once, so only trigger it when we are on a primary\n+                if (indexShard.routingEntry().primary()) {\n+                    attemptSyncedFlush(indexShard.shardId(), new ActionListener<SyncedFlushResult>() {\n+                        @Override\n+                        public void onResponse(SyncedFlushResult syncedFlushResult) {\n+                            logger.debug(\"{} sync flush on inactive shard returned successfully for sync_id: {}\", syncedFlushResult.getShardId(), syncedFlushResult.syncId());\n+                        }\n+\n+                        @Override\n+                        public void onFailure(Throwable e) {\n+                            logger.debug(\"{} sync flush on inactive shard failed\", e, indexShard.shardId());\n+                        }\n+                    });\n+                }\n+            }\n+        });\n+    }\n+\n+    /*\n+    * Tries to flush all copies of a shard and write a sync id to it.\n+    * After a synced flush two shard copies may only contain the same sync id if they contain the same documents.\n+    * To ensure this, synced flush works in three steps:\n+    * 1. Flush all shard copies and gather the commit points for each copy after the flush\n+    * 2. Ensure that there are no ongoing indexing operations on the primary\n+    * 3. Perform an additional flush on each shard copy that writes the sync id\n+    *\n+    * Step 3 is only executed on a shard if\n+    * a) the shard has no uncommitted changes since the last flush\n+    * b) the last flush was the one executed in 1 (use the collected commit id to verify this)\n+    *\n+    * This alone is not enough to ensure that all copies contain the same documents. Without step 2 a sync id would be written for inconsistent copies in the following scenario:\n+    *\n+    * Write operation has completed on a primary and is being sent to replicas. The write request does not reach the replicas until sync flush is finished.\n+    * Step 1 is executed. After the flush the commit points on primary contains a write operation that the replica does not have.\n+    * Step 3 will be executed on primary and replica as well because there are no uncommitted changes on primary (the first flush committed them) and there are no uncommitted\n+    * changes on the replica (the write operation has not reached the replica yet).\n+    *\n+    * Step 2 detects this scenario and fails the whole synced flush if a write operation is ongoing on the primary.\n+    *\n+    * Synced flush is a best effort operation. The sync id may be written on all, some or none of the copies. \n+    *\n+    * **/\n+\n+    public void attemptSyncedFlush(ShardId shardId, ActionListener<SyncedFlushResult> actionListener) {\n+        try {\n+            final ClusterState state = clusterService.state();\n+            final IndexShardRoutingTable shardRoutingTable = getActiveShardRoutings(shardId, state);\n+            final List<ShardRouting> activeShards = shardRoutingTable.activeShards();\n+            Map<String, byte[]> commitIds = sendPreSyncRequests(activeShards, state, shardId);\n+\n+            if (commitIds.isEmpty()) {\n+                actionListener.onResponse(new SyncedFlushResult(shardId, \"all shards failed to commit on pre-sync\"));\n+            }\n+\n+            int inflight = getInflightOpsCount(shardId, state, shardRoutingTable);\n+            if (inflight != 1) {\n+                actionListener.onResponse(new SyncedFlushResult(shardId, \"operation counter on primary is non zero [\" + inflight + \"]\"));\n+            }\n+            String syncId = Strings.base64UUID();\n+            sendSyncRequests(syncId, activeShards, state, commitIds, shardId, actionListener);\n+        } catch (Throwable t) {\n+            actionListener.onFailure(t);\n+        }\n+    }\n+\n+    final IndexShardRoutingTable getActiveShardRoutings(ShardId shardId, ClusterState state) {\n+        final IndexRoutingTable indexRoutingTable = state.routingTable().index(shardId.index().name());\n+        if (indexRoutingTable == null) {\n+            IndexMetaData index = state.getMetaData().index(shardId.index().getName());\n+            if (index != null && index.state() == IndexMetaData.State.CLOSE) {\n+                throw new IndexClosedException(shardId.index());\n+            }\n+            throw new IndexMissingException(shardId.index());\n+        }\n+        final IndexShardRoutingTable shardRoutingTable = indexRoutingTable.shard(shardId.id());\n+        if (shardRoutingTable == null) {\n+            throw new IndexShardMissingException(shardId);\n+        }\n+        return shardRoutingTable;\n+    }\n+\n+    /**\n+     * returns the number of inflight operations on primary. -1 upon error.\n+     */\n+    protected int getInflightOpsCount(final ShardId shardId, ClusterState state, IndexShardRoutingTable shardRoutingTable) {\n+        final ShardRouting primaryShard = shardRoutingTable.primaryShard();\n+        final DiscoveryNode primaryNode = state.nodes().get(primaryShard.currentNodeId());\n+        if (primaryNode == null) {\n+            logger.trace(\"{} failed to resolve node for primary shard {}, skipping sync\", shardId, primaryShard);\n+            return -1;\n+        }\n+        final AtomicInteger result = new AtomicInteger(-1);\n+        final CountDownLatch latch = new CountDownLatch(1);\n+        logger.trace(\"{} retrieving in flight operation count\", shardId);\n+        transportService.sendRequest(primaryNode, IN_FLIGHT_OPS_ACTION_NAME, new InFlightOpsRequest(shardId),\n+                new BaseTransportResponseHandler<InFlightOpsResponse>() {\n+                    @Override\n+                    public InFlightOpsResponse newInstance() {\n+                        return new InFlightOpsResponse();\n+                    }\n+\n+                    @Override\n+                    public void handleResponse(InFlightOpsResponse response) {\n+                        result.set(response.opCount());\n+                        latch.countDown();\n+                    }\n+\n+                    @Override\n+                    public void handleException(TransportException exp) {\n+                        logger.debug(\"{} unexpected error while retrieving inflight op count\", shardId);\n+                        result.set(-1);\n+                        latch.countDown();\n+                    }\n+\n+                    @Override\n+                    public String executor() {\n+                        return ThreadPool.Names.SAME;\n+                    }\n+                });\n+        try {\n+            if (latch.await(inflightOpsTimeout.millis(), TimeUnit.MILLISECONDS) == false) {\n+                logger.debug(\"{} in flight operation check timed out after [{}]\", shardId, syncTimeout);\n+            }\n+        } catch (InterruptedException e) {\n+            logger.debug(\"{} interrupted while waiting for in flight operation check\", shardId);\n+        }\n+        final int count = result.get();\n+        logger.trace(\"{} in flight operation count [{}]\", shardId, count);\n+        return count;\n+    }\n+\n+\n+    void sendSyncRequests(final String syncId, List<ShardRouting> shards, ClusterState state, Map<String, byte[]> expectedCommitIds, final ShardId shardId, final ActionListener<SyncedFlushResult> listener) {\n+        final CountDown countDownLatch = new CountDown(shards.size());\n+        final Map<ShardRouting, SyncedFlushResponse> results = ConcurrentCollections.newConcurrentMap();\n+        for (final ShardRouting shard : shards) {\n+            final DiscoveryNode node = state.nodes().get(shard.currentNodeId());\n+            if (node == null) {\n+                logger.trace(\"{} is assigned to an unknown node. skipping for sync id [{}]. shard routing {}\", shardId, syncId, shard);\n+                results.put(shard, new SyncedFlushResponse(\"unknown node\"));\n+                if (countDownLatch.countDown()) {\n+                    listener.onResponse(new SyncedFlushResult(shardId, syncId, results));\n+                }\n+                continue;\n+            }\n+            final byte[] expectedCommitId = expectedCommitIds.get(shard.currentNodeId());\n+            if (expectedCommitId == null) {\n+                logger.trace(\"{} can't resolve expected commit id for {}, skipping for sync id [{}]. shard routing {}\", shardId, syncId, shard);\n+                results.put(shard, new SyncedFlushResponse(\"no commit id from pre-sync flush\"));\n+                if (countDownLatch.countDown()) {\n+                    listener.onResponse(new SyncedFlushResult(shardId, syncId, results));\n+                }\n+                continue;\n+            }\n+            logger.trace(\"{} sending synced flush request to {}. sync id [{}].\", shardId, shard, syncId);\n+            transportService.sendRequest(node, SYNCED_FLUSH_ACTION_NAME, new SyncedFlushRequest(shard.shardId(), syncId, expectedCommitId),\n+                    new BaseTransportResponseHandler<SyncedFlushResponse>() {\n+                        @Override\n+                        public SyncedFlushResponse newInstance() {\n+                            return new SyncedFlushResponse();\n+                        }\n+\n+                        @Override\n+                        public void handleResponse(SyncedFlushResponse response) {\n+                            SyncedFlushResponse existing = results.put(shard, response);\n+                            assert existing == null : \"got two answers for node [\" + node + \"]\";\n+                            // count after the assert so we won't decrement twice in handleException\n+                            if (countDownLatch.countDown()) {\n+                                listener.onResponse(new SyncedFlushResult(shardId, syncId, results));\n+                            }\n+                        }\n+\n+                        @Override\n+                        public void handleException(TransportException exp) {\n+                            logger.trace(\"{} error while performing synced flush on [{}], skipping\", exp, shardId, shard);\n+                            results.put(shard, new SyncedFlushResponse(exp.getMessage()));\n+                            if (countDownLatch.countDown()) {\n+                                listener.onResponse(new SyncedFlushResult(shardId, syncId, results));\n+                            }\n+                        }\n+\n+                        @Override\n+                        public String executor() {\n+                            return ThreadPool.Names.SAME;\n+                        }\n+                    });\n+        }\n+\n+    }\n+\n+    /**\n+     * send presync requests to all started copies of the given shard\n+     */\n+    Map<String, byte[]> sendPreSyncRequests(final List<ShardRouting> shards, final ClusterState state, final ShardId shardId) {\n+        final CountDownLatch countDownLatch = new CountDownLatch(shards.size());\n+        final Map<String, byte[]> commitIds = ConcurrentCollections.newConcurrentMap();\n+        for (final ShardRouting shard : shards) {\n+            logger.trace(\"{} sending pre-synced flush request to {}\", shardId, shard);\n+            final DiscoveryNode node = state.nodes().get(shard.currentNodeId());\n+            if (node == null) {\n+                logger.trace(\"{} shard routing {} refers to an unknown node. skipping.\", shardId, shard);\n+                countDownLatch.countDown();\n+                continue;\n+            }\n+            transportService.sendRequest(node, PRE_SYNCED_FLUSH_ACTION_NAME, new PreSyncedFlushRequest(shard.shardId()), new BaseTransportResponseHandler<PreSyncedFlushResponse>() {\n+                @Override\n+                public PreSyncedFlushResponse newInstance() {\n+                    return new PreSyncedFlushResponse();\n+                }\n+\n+                @Override\n+                public void handleResponse(PreSyncedFlushResponse response) {\n+                    byte[] existing = commitIds.put(node.id(), response.commitId());\n+                    assert existing == null : \"got two answers for node [\" + node + \"]\";\n+                    // count after the assert so we won't decrement twice in handleException\n+                    countDownLatch.countDown();\n+                }\n+\n+                @Override\n+                public void handleException(TransportException exp) {\n+                    logger.trace(\"{} error while performing pre synced flush on [{}], skipping\", shardId, exp, shard);\n+                    countDownLatch.countDown();\n+                }\n+\n+                @Override\n+                public String executor() {\n+                    return ThreadPool.Names.SAME;\n+                }\n+            });\n+        }\n+        try {\n+            if (countDownLatch.await(preSyncTimeout.millis(), TimeUnit.MILLISECONDS) == false) {\n+                logger.debug(\"{} waiting for pre sync flush requests timed out after [{}]. pending ops [{}]\", shardId, preSyncTimeout, countDownLatch.getCount());\n+            }\n+        } catch (InterruptedException e) {\n+            logger.debug(\"{} interrupted while waiting for presync requests\", shardId);\n+        }\n+\n+        return commitIds;\n+    }\n+\n+    private PreSyncedFlushResponse performPreSyncedFlush(PreSyncedFlushRequest request) {\n+        IndexShard indexShard = indicesService.indexServiceSafe(request.shardId().getIndex()).shardSafe(request.shardId().id());\n+        FlushRequest flushRequest = new FlushRequest().force(false).waitIfOngoing(true);\n+        logger.trace(\"{} performing pre sync flush\", request.shardId());\n+        byte[] id = indexShard.flush(flushRequest);\n+        logger.trace(\"{} pre sync flush done. commit id {}\", request.shardId(), id);\n+        return new PreSyncedFlushResponse(id);\n+    }\n+\n+    private SyncedFlushResponse performSyncedFlush(SyncedFlushRequest request) {\n+        IndexService indexService = indicesService.indexServiceSafe(request.shardId().getIndex());\n+        IndexShard indexShard = indexService.shardSafe(request.shardId().id());\n+        logger.trace(\"{} performing sync flush. sync id [{}], expected commit id {}\", request.shardId(), request.syncId(), request.expectedCommitId());\n+        Engine.SyncedFlushResult result = indexShard.syncFlushIfNoPendingChanges(request.syncId(), request.expectedCommitId());\n+        logger.trace(\"{} sync flush done. sync id [{}], result  [{}]\", request.shardId(), request.syncId(), result);\n+        switch (result) {\n+            case SUCCESS:\n+                return new SyncedFlushResponse();\n+            case FAILED_COMMIT_MISMATCH:\n+                return new SyncedFlushResponse(\"commit has changed\");\n+            case FAILED_PENDING_OPERATIONS:\n+                return new SyncedFlushResponse(\"pending operations\");\n+            default:\n+                throw new ElasticsearchException(\"unknown synced flush result [\" + result + \"]\");\n+        }\n+    }\n+\n+    private InFlightOpsResponse performInFlightOps(InFlightOpsRequest request) {\n+        IndexService indexService = indicesService.indexServiceSafe(request.shardId().getIndex());\n+        IndexShard indexShard = indexService.shardSafe(request.shardId().id());\n+        if (indexShard.routingEntry().primary() == false) {\n+            throw new IndexShardException(request.shardId(), \"expected a primary shard\");\n+        }\n+        int opCount = indexShard.getOperationsCount();\n+        logger.trace(\"{} in flight operations sampled at [{}]\", request.shardId(), opCount);\n+        return new InFlightOpsResponse(opCount);\n+    }\n+\n+    /**\n+     * Result for all copies of a shard\n+     */\n+    public static class SyncedFlushResult extends TransportResponse {\n+        private String failureReason;\n+        private Map<ShardRouting, SyncedFlushResponse> shardResponses;\n+        private String syncId;\n+        private ShardId shardId;\n+\n+        public SyncedFlushResult() {\n+        }\n+\n+        public ShardId getShardId() {\n+            return shardId;\n+        }\n+\n+        /**\n+         * failure constructor\n+         */\n+        public SyncedFlushResult(ShardId shardId, String failureReason) {\n+            this.syncId = null;\n+            this.failureReason = failureReason;\n+            this.shardResponses = ImmutableMap.of();\n+            this.shardId = shardId;\n+        }\n+\n+        /**\n+         * success constructor\n+         */\n+        public SyncedFlushResult(ShardId shardId, String syncId, Map<ShardRouting, SyncedFlushResponse> shardResponses) {\n+            this.failureReason = null;\n+            ImmutableMap.Builder<ShardRouting, SyncedFlushResponse> builder = ImmutableMap.builder();\n+            this.shardResponses = builder.putAll(shardResponses).build();\n+            this.syncId = syncId;\n+            this.shardId = shardId;\n+        }\n+\n+        /**\n+         * @return true if one or more shard copies was successful, false if all failed before step three of synced flush\n+         */\n+        public boolean success() {\n+            return syncId != null;\n+        }\n+\n+        /**\n+         * @return the reason for the failure if synced flush failed before step three of synced flush\n+         */\n+        public String failureReason() {\n+            return failureReason;\n+        }\n+\n+        public String syncId() {\n+            return syncId;\n+        }\n+\n+        /**\n+         * @return total number of shards for which a sync attempt was made\n+         */\n+        public int totalShards() {\n+            return shardResponses.size();\n+        }\n+\n+        /**\n+         * @return total number of successful shards\n+         */\n+        public int successfulShards() {\n+            int i = 0;\n+            for (SyncedFlushResponse result : shardResponses.values()) {\n+                if (result.success()) {\n+                    i++;\n+                }\n+            }\n+            return i;\n+        }\n+\n+        /**\n+         * @return Individual responses for each shard copy with a detailed failure message if the copy failed to perform the synced flush.\n+         * Empty if synced flush failed before step three.\n+         */\n+        public Map<ShardRouting, SyncedFlushResponse> shardResponses() {\n+            return shardResponses;\n+        }\n+\n+        @Override\n+        public void writeTo(StreamOutput out) throws IOException {\n+            super.writeTo(out);\n+            out.writeOptionalString(failureReason);\n+            out.writeOptionalString(syncId);\n+            out.writeVInt(shardResponses.size());\n+            for (Map.Entry<ShardRouting, SyncedFlushResponse> result : shardResponses.entrySet()) {\n+                result.getKey().writeTo(out);\n+                result.getValue().writeTo(out);\n+            }\n+            shardId.writeTo(out);\n+        }\n+\n+        @Override\n+        public void readFrom(StreamInput in) throws IOException {\n+            super.readFrom(in);\n+            failureReason = in.readOptionalString();\n+            syncId = in.readOptionalString();\n+            int size = in.readVInt();\n+            ImmutableMap.Builder<ShardRouting, SyncedFlushResponse> builder = ImmutableMap.builder();\n+            for (int i = 0; i < size; i++) {\n+                ImmutableShardRouting shardRouting = ImmutableShardRouting.readShardRoutingEntry(in);\n+                SyncedFlushResponse syncedFlushRsponse = new SyncedFlushResponse();\n+                syncedFlushRsponse.readFrom(in);\n+                builder.put(shardRouting, syncedFlushRsponse);\n+            }\n+            shardResponses = builder.build();\n+            shardId = ShardId.readShardId(in);\n+        }\n+\n+        public ShardId shardId() {\n+            return shardId;\n+        }\n+    }\n+\n+    final static class PreSyncedFlushRequest extends TransportRequest {\n+        private ShardId shardId;\n+","path":"src/main/java/org/elasticsearch/indices/SyncedFlushService.java","position":491,"original_position":491,"commit_id":"7a8d08a4a38f53e9f8f5905427303feb6d04bbd7","original_commit_id":"7a8d08a4a38f53e9f8f5905427303feb6d04bbd7","user":{"login":"s1monw","id":973334,"avatar_url":"https://avatars.githubusercontent.com/u/973334?v=3","gravatar_id":"","url":"https://api.github.com/users/s1monw","html_url":"https://github.com/s1monw","followers_url":"https://api.github.com/users/s1monw/followers","following_url":"https://api.github.com/users/s1monw/following{/other_user}","gists_url":"https://api.github.com/users/s1monw/gists{/gist_id}","starred_url":"https://api.github.com/users/s1monw/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/s1monw/subscriptions","organizations_url":"https://api.github.com/users/s1monw/orgs","repos_url":"https://api.github.com/users/s1monw/repos","events_url":"https://api.github.com/users/s1monw/events{/privacy}","received_events_url":"https://api.github.com/users/s1monw/received_events","type":"User","site_admin":false},"body":"extra newline","created_at":"2015-05-15T19:08:55Z","updated_at":"2015-05-15T19:08:55Z","html_url":"https://github.com/elastic/elasticsearch/pull/11179#discussion_r30436871","pull_request_url":"https://api.github.com/repos/elastic/elasticsearch/pulls/11179","_links":{"self":{"href":"https://api.github.com/repos/elastic/elasticsearch/pulls/comments/30436871"},"html":{"href":"https://github.com/elastic/elasticsearch/pull/11179#discussion_r30436871"},"pull_request":{"href":"https://api.github.com/repos/elastic/elasticsearch/pulls/11179"}}},"pull_request":{"url":"https://api.github.com/repos/elastic/elasticsearch/pulls/11179","id":35496649,"html_url":"https://github.com/elastic/elasticsearch/pull/11179","diff_url":"https://github.com/elastic/elasticsearch/pull/11179.diff","patch_url":"https://github.com/elastic/elasticsearch/pull/11179.patch","issue_url":"https://api.github.com/repos/elastic/elasticsearch/issues/11179","number":11179,"state":"open","locked":false,"title":"Seal indices for faster recovery ","user":{"login":"brwe","id":4320215,"avatar_url":"https://avatars.githubusercontent.com/u/4320215?v=3","gravatar_id":"","url":"https://api.github.com/users/brwe","html_url":"https://github.com/brwe","followers_url":"https://api.github.com/users/brwe/followers","following_url":"https://api.github.com/users/brwe/following{/other_user}","gists_url":"https://api.github.com/users/brwe/gists{/gist_id}","starred_url":"https://api.github.com/users/brwe/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/brwe/subscriptions","organizations_url":"https://api.github.com/users/brwe/orgs","repos_url":"https://api.github.com/users/brwe/repos","events_url":"https://api.github.com/users/brwe/events{/privacy}","received_events_url":"https://api.github.com/users/brwe/received_events","type":"User","site_admin":false},"body":"Add the ability to seal an index. A seal is an id that is written to a primary and its replicas if there are no on going write operations on that shard while the sealing is in progress. Recovery skips file copying for a replica shard if the local copy on a node has the same seal id that the primary has.\r\nSealing can be triggered in two ways: Automatically, when the IndexingMemoryController detects that shard has become inactive and manually with an api.\r\n\r\nrelates to #10032","created_at":"2015-05-15T09:15:41Z","updated_at":"2015-05-15T19:08:55Z","closed_at":null,"merged_at":null,"merge_commit_sha":"a86697b17281472f601ca4d18814b942b8bb355e","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/elastic/elasticsearch/pulls/11179/commits","review_comments_url":"https://api.github.com/repos/elastic/elasticsearch/pulls/11179/comments","review_comment_url":"https://api.github.com/repos/elastic/elasticsearch/pulls/comments{/number}","comments_url":"https://api.github.com/repos/elastic/elasticsearch/issues/11179/comments","statuses_url":"https://api.github.com/repos/elastic/elasticsearch/statuses/7a8d08a4a38f53e9f8f5905427303feb6d04bbd7","head":{"label":"elastic:feature/synced_flush","ref":"feature/synced_flush","sha":"7a8d08a4a38f53e9f8f5905427303feb6d04bbd7","user":{"login":"elastic","id":6764390,"avatar_url":"https://avatars.githubusercontent.com/u/6764390?v=3","gravatar_id":"","url":"https://api.github.com/users/elastic","html_url":"https://github.com/elastic","followers_url":"https://api.github.com/users/elastic/followers","following_url":"https://api.github.com/users/elastic/following{/other_user}","gists_url":"https://api.github.com/users/elastic/gists{/gist_id}","starred_url":"https://api.github.com/users/elastic/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/elastic/subscriptions","organizations_url":"https://api.github.com/users/elastic/orgs","repos_url":"https://api.github.com/users/elastic/repos","events_url":"https://api.github.com/users/elastic/events{/privacy}","received_events_url":"https://api.github.com/users/elastic/received_events","type":"Organization","site_admin":false},"repo":{"id":507775,"name":"elasticsearch","full_name":"elastic/elasticsearch","owner":{"login":"elastic","id":6764390,"avatar_url":"https://avatars.githubusercontent.com/u/6764390?v=3","gravatar_id":"","url":"https://api.github.com/users/elastic","html_url":"https://github.com/elastic","followers_url":"https://api.github.com/users/elastic/followers","following_url":"https://api.github.com/users/elastic/following{/other_user}","gists_url":"https://api.github.com/users/elastic/gists{/gist_id}","starred_url":"https://api.github.com/users/elastic/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/elastic/subscriptions","organizations_url":"https://api.github.com/users/elastic/orgs","repos_url":"https://api.github.com/users/elastic/repos","events_url":"https://api.github.com/users/elastic/events{/privacy}","received_events_url":"https://api.github.com/users/elastic/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/elastic/elasticsearch","description":"Open Source, Distributed, RESTful Search Engine","fork":false,"url":"https://api.github.com/repos/elastic/elasticsearch","forks_url":"https://api.github.com/repos/elastic/elasticsearch/forks","keys_url":"https://api.github.com/repos/elastic/elasticsearch/keys{/key_id}","collaborators_url":"https://api.github.com/repos/elastic/elasticsearch/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/elastic/elasticsearch/teams","hooks_url":"https://api.github.com/repos/elastic/elasticsearch/hooks","issue_events_url":"https://api.github.com/repos/elastic/elasticsearch/issues/events{/number}","events_url":"https://api.github.com/repos/elastic/elasticsearch/events","assignees_url":"https://api.github.com/repos/elastic/elasticsearch/assignees{/user}","branches_url":"https://api.github.com/repos/elastic/elasticsearch/branches{/branch}","tags_url":"https://api.github.com/repos/elastic/elasticsearch/tags","blobs_url":"https://api.github.com/repos/elastic/elasticsearch/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/elastic/elasticsearch/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/elastic/elasticsearch/git/refs{/sha}","trees_url":"https://api.github.com/repos/elastic/elasticsearch/git/trees{/sha}","statuses_url":"https://api.github.com/repos/elastic/elasticsearch/statuses/{sha}","languages_url":"https://api.github.com/repos/elastic/elasticsearch/languages","stargazers_url":"https://api.github.com/repos/elastic/elasticsearch/stargazers","contributors_url":"https://api.github.com/repos/elastic/elasticsearch/contributors","subscribers_url":"https://api.github.com/repos/elastic/elasticsearch/subscribers","subscription_url":"https://api.github.com/repos/elastic/elasticsearch/subscription","commits_url":"https://api.github.com/repos/elastic/elasticsearch/commits{/sha}","git_commits_url":"https://api.github.com/repos/elastic/elasticsearch/git/commits{/sha}","comments_url":"https://api.github.com/repos/elastic/elasticsearch/comments{/number}","issue_comment_url":"https://api.github.com/repos/elastic/elasticsearch/issues/comments{/number}","contents_url":"https://api.github.com/repos/elastic/elasticsearch/contents/{+path}","compare_url":"https://api.github.com/repos/elastic/elasticsearch/compare/{base}...{head}","merges_url":"https://api.github.com/repos/elastic/elasticsearch/merges","archive_url":"https://api.github.com/repos/elastic/elasticsearch/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/elastic/elasticsearch/downloads","issues_url":"https://api.github.com/repos/elastic/elasticsearch/issues{/number}","pulls_url":"https://api.github.com/repos/elastic/elasticsearch/pulls{/number}","milestones_url":"https://api.github.com/repos/elastic/elasticsearch/milestones{/number}","notifications_url":"https://api.github.com/repos/elastic/elasticsearch/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/elastic/elasticsearch/labels{/name}","releases_url":"https://api.github.com/repos/elastic/elasticsearch/releases{/id}","created_at":"2010-02-08T13:20:56Z","updated_at":"2015-05-15T15:08:34Z","pushed_at":"2015-05-15T19:06:46Z","git_url":"git://github.com/elastic/elasticsearch.git","ssh_url":"git@github.com:elastic/elasticsearch.git","clone_url":"https://github.com/elastic/elasticsearch.git","svn_url":"https://github.com/elastic/elasticsearch","homepage":"http://elasticsearch.org","size":360548,"stargazers_count":11176,"watchers_count":11176,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":3525,"mirror_url":null,"open_issues_count":1126,"forks":3525,"open_issues":1126,"watchers":11176,"default_branch":"master"}},"base":{"label":"elastic:master","ref":"master","sha":"f05808d59e344ccee07ace5f26dece190c6710ec","user":{"login":"elastic","id":6764390,"avatar_url":"https://avatars.githubusercontent.com/u/6764390?v=3","gravatar_id":"","url":"https://api.github.com/users/elastic","html_url":"https://github.com/elastic","followers_url":"https://api.github.com/users/elastic/followers","following_url":"https://api.github.com/users/elastic/following{/other_user}","gists_url":"https://api.github.com/users/elastic/gists{/gist_id}","starred_url":"https://api.github.com/users/elastic/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/elastic/subscriptions","organizations_url":"https://api.github.com/users/elastic/orgs","repos_url":"https://api.github.com/users/elastic/repos","events_url":"https://api.github.com/users/elastic/events{/privacy}","received_events_url":"https://api.github.com/users/elastic/received_events","type":"Organization","site_admin":false},"repo":{"id":507775,"name":"elasticsearch","full_name":"elastic/elasticsearch","owner":{"login":"elastic","id":6764390,"avatar_url":"https://avatars.githubusercontent.com/u/6764390?v=3","gravatar_id":"","url":"https://api.github.com/users/elastic","html_url":"https://github.com/elastic","followers_url":"https://api.github.com/users/elastic/followers","following_url":"https://api.github.com/users/elastic/following{/other_user}","gists_url":"https://api.github.com/users/elastic/gists{/gist_id}","starred_url":"https://api.github.com/users/elastic/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/elastic/subscriptions","organizations_url":"https://api.github.com/users/elastic/orgs","repos_url":"https://api.github.com/users/elastic/repos","events_url":"https://api.github.com/users/elastic/events{/privacy}","received_events_url":"https://api.github.com/users/elastic/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/elastic/elasticsearch","description":"Open Source, Distributed, RESTful Search Engine","fork":false,"url":"https://api.github.com/repos/elastic/elasticsearch","forks_url":"https://api.github.com/repos/elastic/elasticsearch/forks","keys_url":"https://api.github.com/repos/elastic/elasticsearch/keys{/key_id}","collaborators_url":"https://api.github.com/repos/elastic/elasticsearch/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/elastic/elasticsearch/teams","hooks_url":"https://api.github.com/repos/elastic/elasticsearch/hooks","issue_events_url":"https://api.github.com/repos/elastic/elasticsearch/issues/events{/number}","events_url":"https://api.github.com/repos/elastic/elasticsearch/events","assignees_url":"https://api.github.com/repos/elastic/elasticsearch/assignees{/user}","branches_url":"https://api.github.com/repos/elastic/elasticsearch/branches{/branch}","tags_url":"https://api.github.com/repos/elastic/elasticsearch/tags","blobs_url":"https://api.github.com/repos/elastic/elasticsearch/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/elastic/elasticsearch/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/elastic/elasticsearch/git/refs{/sha}","trees_url":"https://api.github.com/repos/elastic/elasticsearch/git/trees{/sha}","statuses_url":"https://api.github.com/repos/elastic/elasticsearch/statuses/{sha}","languages_url":"https://api.github.com/repos/elastic/elasticsearch/languages","stargazers_url":"https://api.github.com/repos/elastic/elasticsearch/stargazers","contributors_url":"https://api.github.com/repos/elastic/elasticsearch/contributors","subscribers_url":"https://api.github.com/repos/elastic/elasticsearch/subscribers","subscription_url":"https://api.github.com/repos/elastic/elasticsearch/subscription","commits_url":"https://api.github.com/repos/elastic/elasticsearch/commits{/sha}","git_commits_url":"https://api.github.com/repos/elastic/elasticsearch/git/commits{/sha}","comments_url":"https://api.github.com/repos/elastic/elasticsearch/comments{/number}","issue_comment_url":"https://api.github.com/repos/elastic/elasticsearch/issues/comments{/number}","contents_url":"https://api.github.com/repos/elastic/elasticsearch/contents/{+path}","compare_url":"https://api.github.com/repos/elastic/elasticsearch/compare/{base}...{head}","merges_url":"https://api.github.com/repos/elastic/elasticsearch/merges","archive_url":"https://api.github.com/repos/elastic/elasticsearch/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/elastic/elasticsearch/downloads","issues_url":"https://api.github.com/repos/elastic/elasticsearch/issues{/number}","pulls_url":"https://api.github.com/repos/elastic/elasticsearch/pulls{/number}","milestones_url":"https://api.github.com/repos/elastic/elasticsearch/milestones{/number}","notifications_url":"https://api.github.com/repos/elastic/elasticsearch/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/elastic/elasticsearch/labels{/name}","releases_url":"https://api.github.com/repos/elastic/elasticsearch/releases{/id}","created_at":"2010-02-08T13:20:56Z","updated_at":"2015-05-15T15:08:34Z","pushed_at":"2015-05-15T19:06:46Z","git_url":"git://github.com/elastic/elasticsearch.git","ssh_url":"git@github.com:elastic/elasticsearch.git","clone_url":"https://github.com/elastic/elasticsearch.git","svn_url":"https://github.com/elastic/elasticsearch","homepage":"http://elasticsearch.org","size":360548,"stargazers_count":11176,"watchers_count":11176,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":3525,"mirror_url":null,"open_issues_count":1126,"forks":3525,"open_issues":1126,"watchers":11176,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/elastic/elasticsearch/pulls/11179"},"html":{"href":"https://github.com/elastic/elasticsearch/pull/11179"},"issue":{"href":"https://api.github.com/repos/elastic/elasticsearch/issues/11179"},"comments":{"href":"https://api.github.com/repos/elastic/elasticsearch/issues/11179/comments"},"review_comments":{"href":"https://api.github.com/repos/elastic/elasticsearch/pulls/11179/comments"},"review_comment":{"href":"https://api.github.com/repos/elastic/elasticsearch/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/elastic/elasticsearch/pulls/11179/commits"},"statuses":{"href":"https://api.github.com/repos/elastic/elasticsearch/statuses/7a8d08a4a38f53e9f8f5905427303feb6d04bbd7"}}}}