Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stop search indexer when it is deleted. #16

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

brkolla
Copy link
Contributor

@brkolla brkolla commented May 9, 2017

Allow dreyfus to listen to ddoc_updated events and check if the index
is deleted as a result of update to it's design document. If the index
is deleted then it will stop the index if waitlist is empty, if the
waitlist is not empty then it will wait for a minute before shutting
down.

BugzId:85718

@brkolla
Copy link
Contributor Author

brkolla commented May 10, 2017

@rnewson Can you please review the dreyfus code change
@iilyak Can you please review the unit tests

DbShards = [mem3:name(Sh) || Sh <- mem3:local_shards(mem3:dbname(DbName))],
lists:foreach(fun(DbShard) ->
lists:foreach(fun({_DbShard, {_DDocId, Sig}}) ->
case ets:lookup(?BY_SIG, {DbShard, Sig}) of
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indent case

[] ->
ok
end
end, ets:match_object(?BY_DB, {DbShard, {DDocId, '$1'}})) end, DbShards),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you move second end to another line?

@@ -113,25 +115,48 @@ handle_db_event(DbName, created, _St) ->
handle_db_event(DbName, deleted, _St) ->
gen_server:cast(?MODULE, {cleanup, DbName}),
{ok, nil};
handle_db_event(DbName, {ddoc_updated, DDocId}, _St) ->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are subscribing for all_dbs here. This means that you would get non-clustered databases as well. You should distinguish these two cases. Which can be done by matching on <<"shards/", _/binary>> = DbName.

ok = config:set("admins", ?USER, ?PASS, _Persist=false),
Ctx.

setup(PortType) ->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dreyfus is not accessible on backdoor interface. Therefore there is no need to pass PortType and use foreachx in the test.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to update this unit test without the PortType and couldn't make it work.. Can you please show me an example unit test code that I can follow? I want to use this in the mean time as it's doing the intended job.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dreyfus_by_db, {'$1', {DDocId, Sig}}),
lists:foldl(fun({DbName, {_DDocId, _Sig}}, Acc) ->
case ets:lookup(dreyfus_by_sig, {DbName, Sig}) of
[{_, Pid}] -> [Pid| Acc];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no guarantee that dreyfus_by_sig ets table contains Pid of an indexer process. There is a case when it would contain a Pid of an opener process see here.
In order to resolve this ambiguity you could use the approach similar to the this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, I am issuing a search request, before I call this method.. so it would have opened the index pid before it executes this code..

]},
UpdatedDDocJsonObj.

ensureIndexIsOpened(DbName, DDocId, IndexName) ->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't use camel case naming convention in erlang

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

test_request:get(ReqUrl, [?AUTH]),
?assertEqual(200, Status),
DDocId = "_design/searchddoc",
Sig = ensureIndexIsOpened(DbName, DDocId, "other"),
Copy link
Contributor

@iilyak iilyak May 31, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You want to make sure that the following conditions are satisfied:

  1. the deleted index is not in ets table
  2. the old index Pid is not running after deletion of search index.

Also keep in mind that you would have multiple indexers running (one per database shard).
I would suggest to use Mayya's approach from here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the code in here is taking care of handling the event in all db shards.

Copy link
Contributor Author

@brkolla brkolla Jun 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, if the index pid is not there in ets means it is not running right? Isn't that enough to verify that the index pid for the old index is not running? What's the scenario where the index pid would not be in ets but would still be running.. I don't think that's possible..

}.

should_stop_indexing_after_deleting_index(PortType, {Host, DbName}) ->
{timeout, 60,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why you had to increase timeout?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the test was failing if I didn't increase timeout.. May be because I am waiting for the index to be closed before I verify the index is indeed closed.. And that wait seems like is causing the problem.

Copy link
Contributor

@iilyak iilyak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added some comments

@brkolla
Copy link
Contributor Author

brkolla commented May 31, 2017

Thanks @iilyak. Will check them out and make the suggested changes as needed.

@brkolla brkolla force-pushed the 85718-stop-search-index-on-delete branch from 0739e23 to 7214672 Compare June 2, 2017 02:00
@@ -40,6 +40,8 @@
waiting_list=[]
}).

-define(INDEX_SHUTDOWN_DELAY, 60000). % 1 minute
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needs to be configurable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like we don't have any config for dreyfus, if I make this configurable then do we need to add a new config file?
Not sure if it's worth the effort to make this configurable.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean config:get, that's available to all applications.

false
end
end,
case IndexDeleted of
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation is misleadingly wrong here.

@@ -157,9 +159,43 @@ handle_call(info, _From, State) -> % obsolete
Reply = info_int(State#state.index_pid),
{reply, Reply, State}.

handle_cast({ddoc_updated, DDocResult}, State) ->
#state{dbname = DbName, index=#index{sig=CurrentSig}, waiting_list = WaitList} = State,
IndexDeleted = case DDocResult of
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extract this into a function for clarity.

case Index of
[] ->
true;
_ ->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

match something explicitly here, a non-empty list, for example. we don't expect any other result from get_index_with_sig, so don't have clauses for them.

end;
false ->
{noreply, State}
end;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since we always return {noreply, State}, move it to after the case statement so we say it once.

lists:flatmap(
fun(IndexName) ->
case (catch design_doc_to_index(Doc, IndexName)) of
{ok, #index{sig=Sig_New}=Index} when Sig_New =:= Sig -> [Index];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you could just do {ok, #index{sig=Sig}=Index} here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am trying to compare the if the index list has the index matching the Sig that was passed in as a parameter to this function.

lists:foreach(fun({_DbShard, {_DDocId, Sig}}) ->
case ets:lookup(?BY_SIG, {DbShard, Sig}) of
[{_, IndexPid}] ->
(catch gen_server:cast(IndexPid, {ddoc_updated, DDocResult}));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why catch?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be honest, I just copied the code from couch_index_server and hence the catch.. I thought the motivation was to not kill the index manager if there was an error sending message to search index.. But only time this could raise an error is when something really bad happens to the node.. I will take out the exception..

{IndexNames, _} = lists:unzip(IndexList),
lists:flatmap(
fun(IndexName) ->
case (catch design_doc_to_index(Doc, IndexName)) of
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why catch?

[] ->
ok
end
end, ets:match_object(?BY_DB, {DbShard, {DDocId, '$1'}}))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the $1 for if we don't apply a match condition?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I followed the same concept as in couch_index_server. I thought this would basically find all the search indices opened for the given ddoc matching DDocId. Am I missing something here?


delete_from_ets(Pid, DbName, Sig) ->
case ets:match_object(?BY_DB, {DbName, {'$1', Sig}}) of
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the $1 for if we don't apply a match condition?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is to find the design doc id for a given DB and Search index, so that I can remove it from the dreyfus_by_db ets.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As Robert mentioned this shouldn't be a '$1', but '_'. We need to bind if we want to compare that the values at different positions are the same. For example {'$1', a, '$1'} would match {22, a, 22}, but it wouldn't match {44, a, 32}.

@brkolla brkolla force-pushed the 85718-stop-search-index-on-delete branch from 7214672 to ddd87b2 Compare June 5, 2017 13:55
@brkolla
Copy link
Contributor Author

brkolla commented Jun 5, 2017

@rnewson @iilyak Have updated the code to reflect the changes as per your suggestions and also replied to your comments. Can you please take another attempt to review?

@brkolla brkolla force-pushed the 85718-stop-search-index-on-delete branch from ddd87b2 to 9194218 Compare June 6, 2017 14:03
@brkolla brkolla force-pushed the 85718-stop-search-index-on-delete branch from 9194218 to 7e0d982 Compare June 15, 2017 14:09
@@ -40,6 +40,9 @@
waiting_list=[]
}).

-define(INDEX_SHUTDOWN_DELAY,
config:get("dreyfus", "index_shutdown_delay", 60000)). % 1 minute
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

config:get returns a string, so the default needs to be a string, or use get_integer. And move this to a function please.

@brkolla brkolla force-pushed the 85718-stop-search-index-on-delete branch from 7e0d982 to 4abddf9 Compare June 19, 2017 14:42
@brkolla
Copy link
Contributor Author

brkolla commented Jun 19, 2017

@rnewson, Done. Moved the config:get to a function and also changed it to use get_integer.

?assertEqual(0, length(IndexPidsFromEts)),
ok.

getIndex(DbName, DDocId, IndexName) ->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't use CamelCase naming in function names. this function would be called get_index.

@brkolla brkolla force-pushed the 85718-stop-search-index-on-delete branch from 4abddf9 to c7e1278 Compare June 19, 2017 17:44
?assertEqual(0, length(IndexPidsFromEts)),
ok.

getIndex(DbName, DDocId, IndexName) ->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we return not_found instead of ok?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really.. The intent of that function is that it ensures that the index is closed and returning 'ok only if it succeeds that.. otherwise the assert would fail and cause the test to stop..

?assert(length(IndexPidsFromEts) > 0),
Sig.

ensure_index_is_closed(DbName, DDocId, IndexName, Sig) ->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do you know that index Pids are actually killed? It seems like you rely on the info from ets. In this case if we would introduce a bug in a Pid accounting in ets tables we wouldn't be able to catch the problem.
You could pass the Pids of indexer instances as an argument to this function or make this check in the test case itself.


updated_ddoc_json_obj(DbName, DDocId) ->
{ok, DDoc} = fabric:open_doc(DbName, <<DDocId/binary>>, [ejson_body, ?ADMIN_CTX]),
% {DDocJsonObj} = ?DDOC_UPDATE,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't forget to remove commented out code in the final version.

@brkolla brkolla force-pushed the 85718-stop-search-index-on-delete branch from c7e1278 to 2db1f44 Compare June 20, 2017 15:53
@brkolla
Copy link
Contributor Author

brkolla commented Jun 20, 2017

@iilyak Changed the test code to pass the indexpids and ensure that they are closed.

@brkolla brkolla force-pushed the 85718-stop-search-index-on-delete branch from 2db1f44 to ca5ab30 Compare June 28, 2017 21:56
Copy link
Contributor

@rnewson rnewson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some substantial issues to address here.

@@ -40,6 +40,8 @@
waiting_list=[]
}).

-define(INDEX_SHUTDOWN_DELAY, index_shutdown_delay()).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove this define, just call the function where needed.

handle_cast({ddoc_updated, DDocResult}, State) ->
#state{index=#index{sig=CurrentSig}, waiting_list = WaitList} = State,
case check_if_index_is_deleted(DDocResult, CurrentSig) of
true ->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this case statement will crash when the index isn't deleted as you haven't written the false case. I would also suggest collapsing this to one case statement using {check_if_index_is_deleted(DDocResult, CurrentSig), WaitList}

case WaitList of
[] ->
self() ! index_deleted;
[_Index] ->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this case statement assumes the wait list will have zero or one items, and crashes if there is more than one waiter.

[] ->
self() ! index_deleted;
[_Index] ->
erlang:send_after(?INDEX_SHUTDOWN_DELAY, self(), index_deleted)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be better to wait until the list is empty. New requests should not be possible if the index has been deleted.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought the whole idea of this work is to stop indexing for the deleted indices which could have already triggered and in progress. Agree that there won't be any _search end point requests after the index is deleted. Basically giving 1 minute to complete the pending requests, if they are not done then it will end the request.

get_index_with_sig(#doc{body={Fields}}=Doc, Sig) ->
RawIndexes = couch_util:get_value(<<"indexes">>, Fields, {[]}),
case RawIndexes of
{IndexList} when is_list(IndexList) ->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this case statement has one clause, and crashes on anything else. If that's the intent, you don't need a case statement.

@@ -222,6 +243,35 @@ code_change(_OldVsn, State, _Extra) ->

% private functions.

get_index_with_sig(#doc{body={Fields}}=Doc, Sig) ->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function only looks for an index of given signature in a single design document. A search index might be referred to by multiple design documents.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't know about this case of index could be referred by multiple design documents. If that's the case I have to scan all the design docs in the db and ensure that it's not referred in other docs.

[] ->
ok
end
end, ets:match_object(?BY_DB, {DbShard, {DDocId, '$1'}}))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replace '$1' with '_'


get_index_by_sig_from_ets(DDocId, Sig) ->
Indexes = ets:match_object(
dreyfus_by_db, {'$1', {DDocId, Sig}}),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replace '$1' with '_'

@brkolla brkolla force-pushed the 85718-stop-search-index-on-delete branch from ca5ab30 to fd8e486 Compare July 7, 2017 14:21
ok;
_ ->
ok
end.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part looks good to me

@brkolla brkolla force-pushed the 85718-stop-search-index-on-delete branch from fd8e486 to b5ce340 Compare July 10, 2017 17:40
}=State) ->
case check_if_index_is_deleted(DbName, DDocResult, Sig) of
true ->
erlang:send_after(index_shutdown_delay(), self(), index_deleted);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will crash the gen server as it returns a value that handle_cast does not expect (a TimerRef).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the delay for?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the gen server should shut down after serving last request after deletion.

case Reason of
{shutdown, ddoc_updated} ->
send_all(State#state.waiting_list, ddoc_updated),
ok;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the return value of terminate is not relevant, I would leave this ok; out.

{not_found, deleted} ->
IndicesWithSig = indices_with_sig_from_other_ddocs(DbName, CurrentSig);
{ok, DDoc} ->
IndicesWithSig = indices_with_sig(DbName, DDoc, CurrentSig)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is wrong. we always need to check if any ddoc refers to the index before we can consider that it's deleted.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are indeed doing that.. Inside the indices_with_sig, it will first check if it's deleted then it will check if it referred from other ddocs

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per the discussion in slack, will rename/reorg this code to make it easy to read..

@brkolla brkolla force-pushed the 85718-stop-search-index-on-delete branch from b5ce340 to aec2784 Compare July 12, 2017 16:13
check_if_index_is_deleted(DbName, DDocResult, CurrentSig) ->
case DDocResult of
{not_found, deleted} ->
IndicesWithSig = indices_with_sig_from_other_ddocs(DbName, CurrentSig);
Copy link
Contributor

@mayya-sharipova mayya-sharipova Jul 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this indices_with_sig_from_other_ddocs seems to be very expensive, especially doing exactly same operation for all N shards.
I would suggest avoid this check. First, it is expensive. Second, it seems to me that it is not very likely that the customers have exact same indexes for the same db from diff Design docs and using these Design Docs at the same time. Third, even if we close the index used by another ddoc the danger is minimal, if will be reopened with the next query/ken job.

Even if we still want to keep this check it should be initiated from a single process spawn from dreyfus_index_manager and then results passed to all concerning dreyfus_index processes.

@rnewson @brkolla What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@brkolla Paul D. has commented on my similar code, and it seems that I was wrong. It is common practice to have the same DDocs when updating them. So, please keep the code as before checking on indices_with_sig_from_other_ddocs

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

K, will leave it as it is then. Thx.

@@ -157,9 +157,27 @@ handle_call(info, _From, State) -> % obsolete
Reply = info_int(State#state.index_pid),
{reply, Reply, State}.

handle_cast({ddoc_updated, DDocResult},
#state{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indent by 4 spaces.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

on principle, we only extract terms in the function head if we are matching on them. so pull Sig and DbName out on a separate line. This will also make the function head much smaller and it will fit on one line.

handle_cast({ddoc_updated, DDocResult}, #state{} = State) ->
    #index{sig = Sig, dbname = DbName} = State#state.index,

}=State) ->
case check_if_index_is_deleted(DbName, DDocResult, Sig) of
true ->
erlang:send_after(index_shutdown_delay(), self(), index_deleted),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

think I said it before, but an arbitrary delay doesn't seem write. If a design document is updated such that there is no reference to this index, we should either let any requests to it finish and then delete the index or just delete it. The arbitrary delay is, I'm guessing, intended to avoid the errors caused by deleting an index while it's being queried? If so, we should at least document that it's a bit hackish.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rnewson what is the way to "let any requests to it finish"? How do we know that requests are finished? check if Waiters == [] will not work for stale requests that don't wait for indexing to catch up.

@brkolla brkolla force-pushed the 85718-stop-search-index-on-delete branch from aec2784 to 10c62ee Compare July 19, 2017 21:33
@brkolla
Copy link
Contributor Author

brkolla commented Jul 19, 2017

@rnewson. I have updated the changes to make changes as per your suggestions..
And in regard to the comment quoted by you (see below), I will do whatever you and @mayya-sharipova decide. I can add a comment in the code summarizing the reasoning for our approach.
As I and @mayya-sharipova mentioned there is no easy way to identify the pids representing the end users (and waiting for indexing to catch up) vs the pids initiated from ken. We can add the comment and move on.

think I said it before, but an arbitrary delay doesn't seem write. If a design document is updated such that there is no reference to this index, we should either let any requests to it finish and then delete the index or just delete it. The arbitrary delay is, I'm guessing, intended to avoid the errors caused by deleting an index while it's being queried? If so, we should at least document that it's a bit hackish.

Allow dreyfus to listen to ddoc_updated events and check if the index
is deleted as a result of update to it's design document. If the index
is deleted then it will stop the index. If there are any pids waiting
after 30 seconds, it will send a message 'ddoc_updates' before it stops
the index.

BugzId:85718
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants