blob: f552962d7c7bb3ea5aabf5647451c52d4690e411 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jackrabbit.oak.plugins.index.elastic;
import java.io.IOException;
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.Type;
import org.apache.jackrabbit.oak.plugins.index.AsyncIndexInfo;
import org.apache.jackrabbit.oak.plugins.index.AsyncIndexInfoService;
import org.apache.jackrabbit.oak.plugins.index.IndexInfo;
import org.apache.jackrabbit.oak.plugins.index.IndexInfoProvider;
import org.apache.jackrabbit.oak.plugins.index.IndexUtils;
import org.apache.jackrabbit.oak.plugins.index.search.IndexDefinition;
import org.apache.jackrabbit.oak.spi.state.NodeState;
import org.apache.jackrabbit.oak.spi.state.NodeStateUtils;
import org.apache.jackrabbit.oak.spi.state.NodeStore;
import org.apache.jackrabbit.util.ISO8601;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import co.elastic.clients.elasticsearch._types.HealthStatus;
import co.elastic.clients.elasticsearch._types.Level;
import co.elastic.clients.elasticsearch.cluster.HealthRequest;
import co.elastic.clients.elasticsearch.cluster.HealthResponse;
class ElasticIndexInfoProvider implements IndexInfoProvider {
private final NodeStore nodeStore;
private final ElasticIndexTracker indexTracker;
private final AsyncIndexInfoService asyncIndexInfoService;
ElasticIndexInfoProvider(@NotNull NodeStore nodeStore,
@NotNull ElasticIndexTracker indexTracker,
@NotNull AsyncIndexInfoService asyncIndexInfoService) {
this.nodeStore = nodeStore;
this.indexTracker = indexTracker;
this.asyncIndexInfoService = asyncIndexInfoService;
}
@Override
public String getType() {
return ElasticIndexDefinition.TYPE_ELASTICSEARCH;
}
@Override
public @Nullable IndexInfo getInfo(String indexPath) {
ElasticIndexNode node = indexTracker.acquireIndexNode(indexPath);
try {
NodeState idxState = NodeStateUtils.getNode(nodeStore.getRoot(), indexPath);
String asyncName = IndexUtils.getAsyncLaneName(idxState, indexPath);
AsyncIndexInfo asyncInfo = asyncIndexInfoService.getInfo(asyncName);
return new ElasticIndexInfo(
indexPath,
asyncName,
asyncInfo != null ? asyncInfo.getLastIndexedTo() : -1L,
getStatusTimestamp(idxState, IndexDefinition.STATUS_LAST_UPDATED),
node.getIndexStatistics().numDocs(),
node.getIndexStatistics().primaryStoreSize(),
node.getIndexStatistics().creationDate(),
getStatusTimestamp(idxState, IndexDefinition.REINDEX_COMPLETION_TIMESTAMP)
);
} finally {
node.release();
}
}
@Override
public boolean isValid(String indexPath) throws IOException {
ElasticIndexNode node = indexTracker.acquireIndexNode(indexPath);
try {
HealthResponse response = node.getConnection().getClient().cluster()
.health(HealthRequest.of(hrb -> hrb
.index(node.getDefinition().getIndexAlias())
.level(Level.Indices)));
return response.indices().values().stream().map(i -> i.status() == HealthStatus.Green).findFirst().orElse(false);
} finally {
node.release();
}
}
private long getStatusTimestamp(NodeState idxState, String propName) {
NodeState status = idxState.getChildNode(IndexDefinition.STATUS_NODE);
if (status.exists()) {
PropertyState updatedTime = status.getProperty(propName);
if (updatedTime != null) {
return ISO8601.parse(updatedTime.getValue(Type.DATE)).getTimeInMillis();
}
}
return -1;
}
private static class ElasticIndexInfo implements IndexInfo {
private final String indexPath;
private final String asyncLane;
private final long indexedUpToTime;
private final long lastUpdatedTime;
private final long estimatedEntryCount;
private final long sizeInBytes;
private final long creationTimestamp;
private final long reindexCompletionTimestamp;
ElasticIndexInfo(String indexPath, String asyncLane, long indexedUpToTime, long lastUpdatedTime,
long estimatedEntryCount, long sizeInBytes, long creationTimestamp, long reindexCompletionTimestamp) {
this.indexPath = indexPath;
this.asyncLane = asyncLane;
this.indexedUpToTime = indexedUpToTime;
this.lastUpdatedTime = lastUpdatedTime;
this.estimatedEntryCount = estimatedEntryCount;
this.sizeInBytes = sizeInBytes;
this.creationTimestamp = creationTimestamp;
this.reindexCompletionTimestamp = reindexCompletionTimestamp;
}
@Override
public String getIndexPath() {
return indexPath;
}
@Override
public String getType() {
return ElasticIndexDefinition.TYPE_ELASTICSEARCH;
}
@Override
public @Nullable String getAsyncLaneName() {
return asyncLane;
}
@Override
public long getLastUpdatedTime() {
return lastUpdatedTime;
}
@Override
public long getIndexedUpToTime() {
return indexedUpToTime;
}
@Override
public long getEstimatedEntryCount() {
return estimatedEntryCount;
}
@Override
public long getSizeInBytes() {
return sizeInBytes;
}
@Override
public boolean hasIndexDefinitionChangedWithoutReindexing() {
// not available in elastic, stored index definitions not supported
return false;
}
@Override
public @Nullable String getIndexDefinitionDiff() {
// not available in elastic, stored index definitions not supported
return null;
}
@Override
public boolean hasHiddenOakLibsMount() {
// not available in elastic
return false;
}
@Override
public boolean hasPropertyIndexNode() {
// not available in elastic
return false;
}
@Override
public long getSuggestSizeInBytes() {
// not available in elastic
return -1;
}
@Override
public long getCreationTimestamp() {
return creationTimestamp;
}
@Override
public long getReindexCompletionTimestamp() {
return reindexCompletionTimestamp;
}
@Override
public void setActive(boolean value) {
// ignore
}
@Override
public boolean isActive() {
return true;
}
}
}