This is an automated email from the ASF dual-hosted git repository.

rnewson pushed a commit to branch lucene-10-again
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit a0bdef22a8410dc4285814f3c8491187d912de4a
Author: Robert Newson <[email protected]>
AuthorDate: Mon Aug 18 17:04:11 2025 +0100

    Allow ?upgrade=true on search to trigger upgrade
---
 .../org/apache/couchdb/nouveau/core/Index.java     |   8 ++
 .../apache/couchdb/nouveau/core/IndexManager.java  | 107 +++++++++++++++------
 .../couchdb/nouveau/health/IndexHealthCheck.java   |   4 +-
 .../couchdb/nouveau/resources/IndexResource.java   |  27 ++++--
 .../couchdb/nouveau/lucene/LuceneIndexTest.java    |   2 +-
 src/nouveau/include/nouveau.hrl                    |   3 +-
 src/nouveau/src/nouveau_api.erl                    |  17 +++-
 src/nouveau/src/nouveau_fabric_info.erl            |  16 +--
 src/nouveau/src/nouveau_fabric_search.erl          |   4 +-
 src/nouveau/src/nouveau_httpd.erl                  |  15 ++-
 src/nouveau/src/nouveau_index_manager.erl          |   6 +-
 src/nouveau/src/nouveau_index_updater.erl          |   7 +-
 12 files changed, 151 insertions(+), 65 deletions(-)

diff --git a/nouveau/src/main/java/org/apache/couchdb/nouveau/core/Index.java 
b/nouveau/src/main/java/org/apache/couchdb/nouveau/core/Index.java
index b8a03e1d1..2e88241e6 100644
--- a/nouveau/src/main/java/org/apache/couchdb/nouveau/core/Index.java
+++ b/nouveau/src/main/java/org/apache/couchdb/nouveau/core/Index.java
@@ -52,6 +52,14 @@ public abstract class Index implements Closeable {
         return new IndexInfo(updateSeq, purgeSeq, numDocs, diskSize, 
upgradeRequired);
     }
 
+    public synchronized long getUpdateSeq() {
+        return updateSeq;
+    }
+
+    public synchronized long getPurgeSeq() {
+        return purgeSeq;
+    }
+
     protected abstract int doNumDocs() throws IOException;
 
     protected abstract long doDiskSize() throws IOException;
diff --git 
a/nouveau/src/main/java/org/apache/couchdb/nouveau/core/IndexManager.java 
b/nouveau/src/main/java/org/apache/couchdb/nouveau/core/IndexManager.java
index da185aa2a..8570bcd0b 100644
--- a/nouveau/src/main/java/org/apache/couchdb/nouveau/core/IndexManager.java
+++ b/nouveau/src/main/java/org/apache/couchdb/nouveau/core/IndexManager.java
@@ -38,6 +38,7 @@ import org.apache.couchdb.nouveau.api.IndexDefinition;
 import org.apache.couchdb.nouveau.lucene.LuceneAnalyzerFactory;
 import org.apache.couchdb.nouveau.lucene.LuceneIndex;
 import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.index.IndexFileNames;
 import org.apache.lucene.index.IndexWriter;
 import org.apache.lucene.index.IndexWriterConfig;
 import org.apache.lucene.index.IndexWriterConfig.OpenMode;
@@ -78,7 +79,8 @@ public final class IndexManager implements Managed {
         private final ReentrantReadWriteLock lock = new 
ReentrantReadWriteLock(true);
         private ScheduledFuture<?> commitFuture;
         private HolderState state = HolderState.NOT_LOADED;
-        private Index index;
+        private Index latestIndex;
+        private Index legacyIndex;
     }
 
     private static final Logger LOGGER = 
LoggerFactory.getLogger(IndexManager.class);
@@ -104,7 +106,7 @@ public final class IndexManager implements Managed {
 
     private StripedLock<String> createLock;
 
-    public <R> R with(final String name, final IndexFunction<Index, R> 
indexFun)
+    public <R> R with(final String name, final boolean upgrade, final 
IndexFunction<Index, R> indexFun)
             throws IOException, InterruptedException {
         evictIfOverCapacity();
 
@@ -123,21 +125,29 @@ public final class IndexManager implements Managed {
 
             // Load if not already loaded or remove if Lucene closed the index 
elsewhere.
             if (holder.state == HolderState.NOT_LOADED
-                    || (holder.state == HolderState.LOADED && 
!holder.index.isOpen())) {
+                    || (holder.state == HolderState.LOADED
+                            && (!holder.latestIndex.isOpen()
+                                    || (holder.legacyIndex != null && 
!holder.legacyIndex.isOpen())
+                                    || upgradeComplete(holder)))) {
                 holder.lock.readLock().unlock();
                 holder.lock.writeLock().lock();
                 try {
-                    if (holder.state == HolderState.LOADED && 
!holder.index.isOpen()) {
+                    if (holder.state == HolderState.LOADED && 
!holder.latestIndex.isOpen()
+                            || (holder.legacyIndex != null && 
!holder.legacyIndex.isOpen())) {
                         LOGGER.info("removing closed index {}", name);
                         holder.state = HolderState.UNLOADED;
-                        holder.index = null;
+                        if (holder.latestIndex.isOpen() || 
holder.legacyIndex.isOpen()) {
+                            close(name, holder);
+                        }
+                        holder.latestIndex = null;
+                        holder.legacyIndex = null;
                         synchronized (cache) {
                             cache.remove(name, holder);
                         }
                         continue retry;
                     }
                     if (holder.state == HolderState.NOT_LOADED) {
-                        holder.index = load(name);
+                        load(name, holder);
                         holder.commitFuture = 
this.schedulerExecutorService.scheduleWithFixedDelay(
                                 commitFun(name, holder),
                                 commitIntervalSeconds,
@@ -145,6 +155,12 @@ public final class IndexManager implements Managed {
                                 TimeUnit.SECONDS);
                         holder.state = HolderState.LOADED;
                     }
+                    if (holder.state == HolderState.LOADED && 
upgradeComplete(holder)) {
+                        LOGGER.info("Upgrade of {} complete, removing legacy 
index", name);
+                        holder.legacyIndex.setDeleteOnClose(true);
+                        holder.legacyIndex.close();
+                        holder.legacyIndex = null;
+                    }
                     holder.lock.readLock().lock();
                 } finally {
                     holder.lock.writeLock().unlock();
@@ -159,7 +175,11 @@ public final class IndexManager implements Managed {
                         Thread.sleep(1000);
                         continue retry;
                     case LOADED:
-                        return indexFun.apply(holder.index);
+                        // Use latest index if upgrading or legacy index if it 
exists if not.
+                        var index = upgrade
+                                ? holder.latestIndex
+                                : holder.legacyIndex != null ? 
holder.legacyIndex : holder.latestIndex;
+                        return indexFun.apply(index);
                 }
             } finally {
                 holder.lock.readLock().unlock();
@@ -167,6 +187,14 @@ public final class IndexManager implements Managed {
         }
     }
 
+    private boolean upgradeComplete(final IndexHolder holder) throws 
IOException {
+        if (holder.legacyIndex == null) {
+            return false;
+        }
+        return holder.latestIndex.getUpdateSeq() >= 
holder.legacyIndex.getUpdateSeq()
+                && holder.latestIndex.getPurgeSeq() >= 
holder.legacyIndex.getPurgeSeq();
+    }
+
     private void evictIfOverCapacity() throws IOException, 
InterruptedException {
         while (true) {
             final String candidate;
@@ -190,7 +218,10 @@ public final class IndexManager implements Managed {
             switch (holder.state) {
                 case LOADED:
                     if (forceDelete) {
-                        holder.index.setDeleteOnClose(true);
+                        holder.latestIndex.setDeleteOnClose(true);
+                        if (holder.legacyIndex != null) {
+                            holder.legacyIndex.setDeleteOnClose(true);
+                        }
                     }
                     LOGGER.info("closing {}", name);
                     try {
@@ -199,7 +230,8 @@ public final class IndexManager implements Managed {
                         LOGGER.error("I/O exception when evicting {}", name, 
e);
                     }
                     holder.state = HolderState.UNLOADED;
-                    holder.index = null;
+                    holder.latestIndex = null;
+                    holder.legacyIndex = null;
                     break;
                 case NOT_LOADED:
                 case UNLOADED:
@@ -331,8 +363,11 @@ public final class IndexManager implements Managed {
         return () -> {
             holder.lock.readLock().lock();
             try {
-                if (holder.index.commit()) {
-                    LOGGER.info("committed {}", name);
+                if (holder.latestIndex.commit()) {
+                    LOGGER.info("committed {} (latest)", name);
+                }
+                if (holder.legacyIndex != null && holder.legacyIndex.commit()) 
{
+                    LOGGER.info("committed {} (legacy)", name);
                 }
             } catch (final IOException e) {
                 LOGGER.warn("I/O exception while committing " + name, e);
@@ -377,14 +412,31 @@ public final class IndexManager implements Managed {
         throw new WebApplicationException(name + " attempts to escape from 
index root directory", Status.BAD_REQUEST);
     }
 
-    private Index load(final String name) throws IOException {
+    private void load(final String name, final IndexHolder holder) throws 
IOException {
         LOGGER.info("opening {}", name);
         final Path path = indexPath(name);
         final IndexDefinition indexDefinition = loadIndexDefinition(name);
         final Analyzer analyzer = 
LuceneAnalyzerFactory.fromDefinition(indexDefinition);
-        final int version = getIndexVersion(path);
+        holder.latestIndex = load(name, Version.LATEST.major, analyzer);
+        if (indexExists(path.resolve(Integer.toString(Version.LATEST.major - 
1)))) {
+            LOGGER.warn("{} has a legacy index", name);
+            holder.legacyIndex = load(name, Version.LATEST.major - 1, 
analyzer);
+        }
+    }
+
+    // Same logic as DirectoryReader.indexExists() but without needing a 
Directory.
+    private boolean indexExists(final Path path) throws IOException {
+        if (!Files.exists(path)) {
+            return false;
+        }
+        try (var stream = Files.walk(path, 1)) {
+            return stream.anyMatch(f -> 
f.getFileName().toString().startsWith(IndexFileNames.SEGMENTS + "_"));
+        }
+    }
+
+    private LuceneIndex load(final String name, final int version, final 
Analyzer analyzer) throws IOException {
+        final Path indexPath = 
indexPath(name).resolve(Integer.toString(version));
         final boolean upgradeRequired = version < Version.LATEST.major;
-        final Path indexPath = path.resolve(Integer.toString(version));
         final Directory dir = new 
DirectIODirectory(FSDirectory.open(indexPath));
         final IndexWriterConfig config = new IndexWriterConfig(analyzer);
         config.setOpenMode(upgradeRequired ? OpenMode.APPEND : 
OpenMode.CREATE_OR_APPEND);
@@ -396,19 +448,6 @@ public final class IndexManager implements Managed {
         return new LuceneIndex(analyzer, writer, updateSeq, purgeSeq, 
upgradeRequired, searcherManager);
     }
 
-    /**
-     * Find highest version index on disk, or latest version if none.
-     */
-    private int getIndexVersion(final Path path) throws IOException {
-        if 
(Files.exists(path.resolve(Integer.toString(Version.LATEST.major)))) {
-            return Version.LATEST.major;
-        }
-        if (Files.exists(path.resolve(Integer.toString(Version.LATEST.major - 
1)))) {
-            return Version.LATEST.major - 1;
-        }
-        return Version.LATEST.major;
-    }
-
     private long getSeq(final IndexWriter writer, final String key) throws 
IOException {
         final Iterable<Map.Entry<String, String>> commitData = 
writer.getLiveCommitData();
         if (commitData == null) {
@@ -429,15 +468,21 @@ public final class IndexManager implements Managed {
         holder.commitFuture.cancel(true);
         IOUtils.runAll(
                 () -> {
-                    if (holder.index.commit()) {
-                        LOGGER.debug("committed {} before close", name);
+                    if (holder.latestIndex.commit()) {
+                        LOGGER.debug("committed {} (latest) before close", 
name);
+                    }
+                    if (holder.legacyIndex != null && 
holder.legacyIndex.commit()) {
+                        LOGGER.debug("committed {} (legacy) before close", 
name);
                     }
                 },
                 () -> {
-                    holder.index.close();
+                    holder.latestIndex.close();
+                    if (holder.legacyIndex != null) {
+                        holder.legacyIndex.close();
+                    }
                 },
                 () -> {
-                    if (holder.index.isDeleteOnClose()) {
+                    if (holder.latestIndex.isDeleteOnClose()) {
                         IOUtils.rm(indexRootPath(name));
                     }
                 });
diff --git 
a/nouveau/src/main/java/org/apache/couchdb/nouveau/health/IndexHealthCheck.java 
b/nouveau/src/main/java/org/apache/couchdb/nouveau/health/IndexHealthCheck.java
index f20efe0c9..64733eb20 100644
--- 
a/nouveau/src/main/java/org/apache/couchdb/nouveau/health/IndexHealthCheck.java
+++ 
b/nouveau/src/main/java/org/apache/couchdb/nouveau/health/IndexHealthCheck.java
@@ -43,13 +43,13 @@ public final class IndexHealthCheck extends HealthCheck {
         try {
             final DocumentUpdateRequest documentUpdateRequest =
                     new DocumentUpdateRequest(0, 1, null, 
Collections.emptyList());
-            indexResource.updateDoc(name, "foo", documentUpdateRequest);
+            indexResource.updateDoc(name, true, "foo", documentUpdateRequest);
 
             final SearchRequest searchRequest = new SearchRequest();
             searchRequest.setQuery("_id:foo");
             searchRequest.setMinUpdateSeq(1);
 
-            final SearchResults searchResults = 
indexResource.searchIndex(name, searchRequest);
+            final SearchResults searchResults = 
indexResource.searchIndex(name, true, searchRequest);
             if (searchResults.getTotalHits() == 1) {
                 return Result.healthy();
             } else {
diff --git 
a/nouveau/src/main/java/org/apache/couchdb/nouveau/resources/IndexResource.java 
b/nouveau/src/main/java/org/apache/couchdb/nouveau/resources/IndexResource.java
index a52e00da9..d66220496 100644
--- 
a/nouveau/src/main/java/org/apache/couchdb/nouveau/resources/IndexResource.java
+++ 
b/nouveau/src/main/java/org/apache/couchdb/nouveau/resources/IndexResource.java
@@ -26,6 +26,7 @@ import jakarta.ws.rs.PUT;
 import jakarta.ws.rs.Path;
 import jakarta.ws.rs.PathParam;
 import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.QueryParam;
 import jakarta.ws.rs.core.MediaType;
 import java.io.IOException;
 import java.util.List;
@@ -65,10 +66,11 @@ public final class IndexResource {
     @Path("/doc/{docId}")
     public Ok deleteDoc(
             @PathParam("name") String name,
+            @QueryParam("upgrade") boolean upgrade,
             @PathParam("docId") String docId,
             @NotNull @Valid DocumentDeleteRequest request)
             throws Exception {
-        return indexManager.with(name, (index) -> {
+        return indexManager.with(name, upgrade, (index) -> {
             index.delete(docId, request);
             return Ok.INSTANCE;
         });
@@ -81,15 +83,20 @@ public final class IndexResource {
     }
 
     @GET
-    public IndexInfo getIndexInfo(@PathParam("name") String name) throws 
Exception {
-        return indexManager.with(name, (index) -> {
+    public IndexInfo getIndexInfo(@PathParam("name") String name, 
@QueryParam("upgrade") boolean upgrade)
+            throws Exception {
+        return indexManager.with(name, upgrade, (index) -> {
             return index.info();
         });
     }
 
     @POST
-    public Ok setIndexInfo(@PathParam("name") String name, @NotNull @Valid 
IndexInfoRequest request) throws Exception {
-        return indexManager.with(name, (index) -> {
+    public Ok setIndexInfo(
+            @PathParam("name") String name,
+            @QueryParam("upgrade") boolean upgrade,
+            @NotNull @Valid IndexInfoRequest request)
+            throws Exception {
+        return indexManager.with(name, upgrade, (index) -> {
             if (request.getMatchUpdateSeq().isPresent()
                     && request.getUpdateSeq().isPresent()) {
                 index.setUpdateSeq(
@@ -107,9 +114,12 @@ public final class IndexResource {
 
     @POST
     @Path("/search")
-    public SearchResults searchIndex(@PathParam("name") String name, @NotNull 
@Valid SearchRequest request)
+    public SearchResults searchIndex(
+            @PathParam("name") String name,
+            @QueryParam("upgrade") boolean upgrade,
+            @NotNull @Valid SearchRequest request)
             throws Exception {
-        return indexManager.with(name, (index) -> {
+        return indexManager.with(name, upgrade, (index) -> {
             return index.search(request);
         });
     }
@@ -118,10 +128,11 @@ public final class IndexResource {
     @Path("/doc/{docId}")
     public Ok updateDoc(
             @PathParam("name") String name,
+            @QueryParam("upgrade") boolean upgrade,
             @PathParam("docId") String docId,
             @NotNull @Valid DocumentUpdateRequest request)
             throws Exception {
-        return indexManager.with(name, (index) -> {
+        return indexManager.with(name, upgrade, (index) -> {
             index.update(docId, request);
             return Ok.INSTANCE;
         });
diff --git 
a/nouveau/src/test/java/org/apache/couchdb/nouveau/lucene/LuceneIndexTest.java 
b/nouveau/src/test/java/org/apache/couchdb/nouveau/lucene/LuceneIndexTest.java
index 9206b83d9..10dd599e8 100644
--- 
a/nouveau/src/test/java/org/apache/couchdb/nouveau/lucene/LuceneIndexTest.java
+++ 
b/nouveau/src/test/java/org/apache/couchdb/nouveau/lucene/LuceneIndexTest.java
@@ -56,7 +56,7 @@ public class LuceneIndexTest {
         config.setUseCompoundFile(false);
         final IndexWriter writer = new IndexWriter(dir, config);
         final SearcherManager searcherManager = new SearcherManager(writer, 
null);
-        return new LuceneIndex(analyzer, writer, 0L, 0L, searcherManager);
+        return new LuceneIndex(analyzer, writer, 0L, 0L, false, 
searcherManager);
     }
 
     protected final void cleanup(final Index index) throws IOException {
diff --git a/src/nouveau/include/nouveau.hrl b/src/nouveau/include/nouveau.hrl
index e50cd45d3..b8d986161 100644
--- a/src/nouveau/include/nouveau.hrl
+++ b/src/nouveau/include/nouveau.hrl
@@ -19,5 +19,6 @@
     def,
     def_lang,
     name,
-    sig=nil
+    sig=nil,
+    upgrade=false
 }).
diff --git a/src/nouveau/src/nouveau_api.erl b/src/nouveau/src/nouveau_api.erl
index 319170a4c..f59efe599 100644
--- a/src/nouveau/src/nouveau_api.erl
+++ b/src/nouveau/src/nouveau_api.erl
@@ -171,7 +171,7 @@ update_doc(#index{} = Index, DocId, MatchSeq, UpdateSeq, 
Partition, Fields) when
             send_error(Reason)
     end.
 
-search(#index{} = Index, QueryArgs) ->
+search(#index{} = Index, #{} = QueryArgs) ->
     Resp = send_if_enabled(
         search_path(Index), [?JSON_CONTENT_TYPE], <<"POST">>, 
jiffy:encode(QueryArgs)
     ),
@@ -219,18 +219,25 @@ set_seq(#index{} = Index, ReqBody) ->
 index_path(Path) when is_binary(Path) ->
     [<<"/index/">>, couch_util:url_encode(Path)];
 index_path(#index{} = Index) ->
-    [<<"/index/">>, couch_util:url_encode(nouveau_util:index_name(Index))].
+    upgrade(Index, [<<"/index/">>, 
couch_util:url_encode(nouveau_util:index_name(Index))]).
 
 doc_path(#index{} = Index, DocId) ->
-    [
+    upgrade(Index, [
         <<"/index/">>,
         couch_util:url_encode(nouveau_util:index_name(Index)),
         <<"/doc/">>,
         couch_util:url_encode(DocId)
-    ].
+    ]).
 
 search_path(#index{} = Index) ->
-    [<<"/index/">>, couch_util:url_encode(nouveau_util:index_name(Index)), 
<<"/search">>].
+    upgrade(Index, [
+        <<"/index/">>, couch_util:url_encode(nouveau_util:index_name(Index)), 
<<"/search">>
+    ]).
+
+upgrade(#index{upgrade = false}, Path) ->
+    Path;
+upgrade(#index{upgrade = true}, Path) ->
+    [Path, <<"?upgrade=true">>].
 
 jaxrs_error(400, Body) ->
     {bad_request, message(Body)};
diff --git a/src/nouveau/src/nouveau_fabric_info.erl 
b/src/nouveau/src/nouveau_fabric_info.erl
index 45bdf177e..0f62d5dca 100644
--- a/src/nouveau/src/nouveau_fabric_info.erl
+++ b/src/nouveau/src/nouveau_fabric_info.erl
@@ -15,22 +15,22 @@
 
 -module(nouveau_fabric_info).
 
--export([go/3]).
+-export([go/4]).
 
 -include_lib("mem3/include/mem3.hrl").
+-include_lib("nouveau/include/nouveau.hrl").
 
-go(DbName, DDocId, IndexName) when is_binary(DDocId) ->
+go(DbName, DDocId, IndexName, Upgrade) when is_binary(DDocId), 
is_boolean(Upgrade) ->
     {ok, DDoc} = fabric:open_doc(DbName, <<"_design/", DDocId/binary>>, 
[ejson_body]),
-    go(DbName, DDoc, IndexName);
-go(DbName, DDoc, IndexName) ->
+    go(DbName, DDoc, IndexName, Upgrade);
+go(DbName, DDoc, IndexName, Upgrade) when is_boolean(Upgrade) ->
     case nouveau_util:design_doc_to_index(DbName, DDoc, IndexName) of
         {ok, Index} ->
-            go(DbName, DDoc, IndexName, Index);
+            go(DbName, DDoc, IndexName, Index#index{upgrade = Upgrade});
         {error, Reason} ->
             {error, Reason}
-    end.
-
-go(DbName, _DDoc, _IndexName, Index) ->
+    end;
+go(DbName, _DDoc, _IndexName, #index{} = Index) ->
     Shards = mem3:shards(DbName),
     Counters0 = lists:map(
         fun(#shard{} = Shard) ->
diff --git a/src/nouveau/src/nouveau_fabric_search.erl 
b/src/nouveau/src/nouveau_fabric_search.erl
index 3ec9d96fd..fa6e0db2e 100644
--- a/src/nouveau/src/nouveau_fabric_search.erl
+++ b/src/nouveau/src/nouveau_fabric_search.erl
@@ -20,6 +20,7 @@
 -include_lib("mem3/include/mem3.hrl").
 -include_lib("couch/include/couch_db.hrl").
 -include("nouveau_int.hrl").
+-include_lib("nouveau/include/nouveau.hrl").
 
 -record(state, {
     limit,
@@ -36,9 +37,10 @@ go(DbName, GroupId, IndexName, QueryArgs0) when 
is_binary(GroupId) ->
     ),
     go(DbName, DDoc, IndexName, QueryArgs0);
 go(DbName, #doc{} = DDoc, IndexName, QueryArgs0) ->
+    #{upgrade := Upgrade} = QueryArgs0,
     case nouveau_util:design_doc_to_index(DbName, DDoc, IndexName) of
         {ok, Index} ->
-            go(DbName, DDoc, IndexName, QueryArgs0, Index);
+            go(DbName, DDoc, IndexName, QueryArgs0, Index#index{upgrade = 
Upgrade});
         {error, Reason} ->
             {error, Reason}
     end.
diff --git a/src/nouveau/src/nouveau_httpd.erl 
b/src/nouveau/src/nouveau_httpd.erl
index 878e001b7..6ae470ade 100644
--- a/src/nouveau/src/nouveau_httpd.erl
+++ b/src/nouveau/src/nouveau_httpd.erl
@@ -75,7 +75,8 @@ handle_search_req_int(#httpd{method = 'GET', path_parts = [_, 
_, _, _, IndexName
         counts => chttpd:qs_value(Req, "counts"),
         update => chttpd:qs_value(Req, "update"),
         bookmark => chttpd:qs_value(Req, "bookmark"),
-        include_docs => chttpd:qs_value(Req, "include_docs")
+        include_docs => chttpd:qs_value(Req, "include_docs"),
+        upgrade => chttpd:qs_value(Req, "upgrade")
     }),
     handle_search_req(Req, DbName, DDoc, IndexName, QueryArgs, ?RETRY_LIMIT);
 handle_search_req_int(
@@ -95,7 +96,8 @@ handle_search_req_int(
         counts => json_or_undefined(<<"counts">>, ReqBody),
         update => maps:get(<<"update">>, ReqBody, undefined),
         bookmark => maps:get(<<"bookmark">>, ReqBody, undefined),
-        include_docs => maps:get(<<"include_docs">>, ReqBody, undefined)
+        include_docs => maps:get(<<"include_docs">>, ReqBody, undefined),
+        upgrade => maps:get(<<"upgrade">>, ReqBody, undefined)
     }),
     handle_search_req(Req, DbName, DDoc, IndexName, QueryArgs, ?RETRY_LIMIT);
 handle_search_req_int(Req, _Db, _DDoc) ->
@@ -136,7 +138,8 @@ handle_info_req(
 ) ->
     check_if_enabled(),
     DbName = couch_db:name(Db),
-    case nouveau_fabric_info:go(DbName, DDoc, IndexName) of
+    Upgrade = chttpd:qs_value(Req, "upgrade") == "true",
+    case nouveau_fabric_info:go(DbName, DDoc, IndexName, Upgrade) of
         {ok, IndexInfo} ->
             send_json(
                 Req,
@@ -260,6 +263,12 @@ validate_query_arg(include_docs, "false") ->
     false;
 validate_query_arg(include_docs, "true") ->
     true;
+validate_query_arg(upgrade, undefined) ->
+    false;
+validate_query_arg(upgrade, "false") ->
+    false;
+validate_query_arg(upgrade, "true") ->
+    true;
 validate_query_arg(Key, Val) ->
     Msg = io_lib:format("Invalid value for ~p: ~p", [Key, Val]),
     throw({query_parse_error, ?l2b(Msg)}).
diff --git a/src/nouveau/src/nouveau_index_manager.erl 
b/src/nouveau/src/nouveau_index_manager.erl
index 45f7a1e8d..04f14ccf8 100644
--- a/src/nouveau/src/nouveau_index_manager.erl
+++ b/src/nouveau/src/nouveau_index_manager.erl
@@ -60,10 +60,12 @@ init(_) ->
     {ok, nil}.
 
 handle_call({update, #index{} = Index0}, From, State) ->
-    DbSig = {Index0#index.dbname, Index0#index.sig},
+    DbSig = {Index0#index.dbname, Index0#index.sig, Index0#index.upgrade},
     case ets:lookup(?BY_DBSIG, DbSig) of
         [] ->
-            {_IndexerPid, IndexerRef} = spawn_monitor(nouveau_index_updater, 
update, [Index0]),
+            {_IndexerPid, IndexerRef} = spawn_monitor(nouveau_index_updater, 
update, [
+                Index0
+            ]),
             Queue = queue:in(From, queue:new()),
             true = ets:insert(?BY_DBSIG, {DbSig, Index0, Queue}),
             true = ets:insert(?BY_REF, {IndexerRef, DbSig});
diff --git a/src/nouveau/src/nouveau_index_updater.erl 
b/src/nouveau/src/nouveau_index_updater.erl
index 3952a893f..ff2cf07cd 100644
--- a/src/nouveau/src/nouveau_index_updater.erl
+++ b/src/nouveau/src/nouveau_index_updater.erl
@@ -18,10 +18,10 @@
 -include("nouveau.hrl").
 
 %% public api
--export([outdated/1, get_db_info/1]).
+-export([get_db_info/1]).
 
 %% callbacks
--export([update/1]).
+-export([outdated/1, update/1]).
 
 -import(couch_query_servers, [get_os_process/1, ret_os_process/1, 
proc_prompt/2]).
 -import(nouveau_util, [index_path/1]).
@@ -70,7 +70,8 @@ update(#index{} = Index) ->
                     {index, Index#index.name},
                     {progress, 0},
                     {changes_done, 0},
-                    {total_changes, TotalChanges}
+                    {total_changes, TotalChanges},
+                    {upgrading, Index#index.upgrade}
                 ]),
 
                 %% update status every half second

Reply via email to