com.yahoo.vespastat.BucketStatsRetriever Maven / Gradle / Ivy
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespastat;
import com.yahoo.document.BucketId;
import com.yahoo.document.BucketIdFactory;
import com.yahoo.document.DocumentId;
import com.yahoo.document.GlobalId;
import com.yahoo.document.select.BucketSelector;
import com.yahoo.document.select.BucketSet;
import com.yahoo.document.select.parser.ParseException;
import com.yahoo.documentapi.SyncParameters;
import com.yahoo.documentapi.messagebus.MessageBusDocumentAccess;
import com.yahoo.documentapi.messagebus.MessageBusSyncSession;
import com.yahoo.documentapi.messagebus.protocol.DocumentMessage;
import com.yahoo.documentapi.messagebus.protocol.GetBucketListMessage;
import com.yahoo.documentapi.messagebus.protocol.GetBucketListReply;
import com.yahoo.documentapi.messagebus.protocol.StatBucketMessage;
import com.yahoo.documentapi.messagebus.protocol.StatBucketReply;
import com.yahoo.messagebus.Reply;
import java.util.List;
/**
* This class fetches bucket information from Vespa
*
* @author bjorncs
*/
public class BucketStatsRetriever {
private final BucketIdFactory bucketIdFactory = new BucketIdFactory();
private final BucketSelector selector = new BucketSelector(bucketIdFactory);
private final MessageBusSyncSession session;
private final MessageBusDocumentAccess documentAccess;
public BucketStatsRetriever(
DocumentAccessFactory documentAccessFactory,
String route,
ShutdownHookRegistrar registrar) {
registerShutdownHook(registrar);
this.documentAccess = documentAccessFactory.createDocumentAccess();
this.session = documentAccess.createSyncSession(new SyncParameters.Builder().build());
this.session.setRoute(route);
}
private void registerShutdownHook(ShutdownHookRegistrar registrar) {
registrar.registerShutdownHook(() -> {
try {
session.destroy();
} catch (Exception e) {
// Ignore exception on shutdown
}
try {
documentAccess.shutdown();
} catch (Exception e) {
// Ignore exception on shutdown
}
});
}
public BucketId getBucketIdForType(ClientParameters.SelectionType type, String id) throws BucketStatsException {
switch (type) {
case DOCUMENT:
return bucketIdFactory.getBucketId(new DocumentId(id));
case BUCKET:
// The internal parser of BucketID is used since the Java Long.decode cannot handle unsigned longs.
return new BucketId(String.format("BucketId(%s)", id));
case GID:
return convertGidToBucketId(id);
case USER:
case GROUP:
try {
BucketSet bucketList = selector.getBucketList(createDocumentSelection(type, id));
if (bucketList.size() != 1) {
String message = String.format("Document selection must map to only one location. " +
"Specified selection matches %d locations.", bucketList.size());
throw new BucketStatsException(message);
}
return bucketList.iterator().next();
} catch (ParseException e) {
throw new BucketStatsException(String.format("Invalid id: %s (%s).", id, e.getMessage()), e);
}
default:
throw new RuntimeException("Unreachable code");
}
}
public String retrieveBucketStats(ClientParameters.SelectionType type, String id, BucketId bucketId, String bucketSpace) throws BucketStatsException {
String documentSelection = createDocumentSelection(type, id);
StatBucketMessage msg = new StatBucketMessage(bucketId, bucketSpace, documentSelection);
StatBucketReply statBucketReply = sendMessage(msg, StatBucketReply.class);
return statBucketReply.getResults();
}
public List retrieveBucketList(BucketId bucketId, String bucketSpace) throws BucketStatsException {
GetBucketListMessage msg = new GetBucketListMessage(bucketId, bucketSpace);
GetBucketListReply bucketListReply = sendMessage(msg, GetBucketListReply.class);
return bucketListReply.getBuckets();
}
private T sendMessage(DocumentMessage msg, Class expectedReply) throws BucketStatsException {
Reply reply = session.syncSend(msg);
return validateReply(reply, expectedReply);
}
private static T validateReply(Reply reply, Class type) throws BucketStatsException {
if (reply.hasErrors()) {
throw new BucketStatsException(makeErrorMessage(reply));
}
if (!type.isInstance(reply)) {
throw new BucketStatsException(String.format("Unexpected reply %s: '%s'", reply.getType(), reply.toString()));
}
return type.cast(reply);
}
private static String makeErrorMessage(Reply reply) {
StringBuilder b = new StringBuilder();
b.append("Request failed: \n");
for (int i = 0; i < reply.getNumErrors(); i++) {
b.append(String.format("\t %s\n", reply.getError(i)));
}
return b.toString();
}
private static String createDocumentSelection(ClientParameters.SelectionType type, String id) {
switch (type) {
case BUCKET:
return "true";
case DOCUMENT:
return String.format("id=\"%s\"", id);
case GID:
return String.format("id.gid=\"gid(%s)\"", id);
case USER:
return String.format("id.user=%s", id);
case GROUP:
return String.format("id.group=\"%s\"", id);
default:
throw new RuntimeException("Unreachable code");
}
}
private static BucketId convertGidToBucketId(String id) throws BucketStatsException {
if (!id.matches("0x\\p{XDigit}{24}")) {
throw new BucketStatsException("Invalid gid: " + id);
}
String hexWithoutPrefix = id.substring(2);
return new GlobalId(convertHexStringToByteArray(hexWithoutPrefix)).toBucketId();
}
private static byte[] convertHexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
int digit1 = Character.digit(s.charAt(i), 16);
int digit2 = Character.digit(s.charAt(i + 1), 16);
data[i / 2] = (byte) ((digit1 << 4) + digit2);
}
return data;
}
public interface ShutdownHookRegistrar {
void registerShutdownHook(Runnable runnable);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy