blob: 82b96e9b2501a94e18b6a5f83ba1b61abeba09c3 [file] [log] [blame]
// Copyright 2024 The Bazel Authors. All rights reserved.
//
// Licensed 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 com.google.devtools.build.lib.skyframe.serialization;
import static java.util.concurrent.Executors.newSingleThreadExecutor;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.hash.HashFunction;
import com.google.common.hash.Hashing;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.protobuf.ByteString;
import java.io.IOException;
import java.util.concurrent.Executor;
import javax.annotation.Nullable;
/**
* Bundles the components needed to store serialized values by fingerprint, the storage interface,
* the cache and the hash function for computing fingerprints.
*/
public final class FingerprintValueService {
private final Executor executor;
private final FingerprintValueStore store;
private final FingerprintValueCache cache;
/**
* The function used to generate fingerprints.
*
* <p>Used to derive {@link #fingerprintPlaceholder} and {@link #fingerprintLength}.
*/
private final HashFunction hashFunction;
private final ByteString fingerprintPlaceholder;
private final int fingerprintLength;
@VisibleForTesting
public static FingerprintValueService createForTesting() {
return createForTesting(
FingerprintValueStore.inMemoryStore(), /* exerciseDeserializationForTesting= */ true);
}
@VisibleForTesting
public static FingerprintValueService createForTesting(FingerprintValueStore store) {
return createForTesting(store, /* exerciseDeserializationForTesting= */ true);
}
@VisibleForTesting
public static FingerprintValueService createForTesting(
boolean exerciseDeserializationForTesting) {
return createForTesting(
FingerprintValueStore.inMemoryStore(), exerciseDeserializationForTesting);
}
private static FingerprintValueService createForTesting(
FingerprintValueStore store, boolean exerciseDeserializationForTesting) {
return new FingerprintValueService(
newSingleThreadExecutor(),
store,
new FingerprintValueCache(exerciseDeserializationForTesting),
Hashing.murmur3_128());
}
public FingerprintValueService(
Executor executor,
FingerprintValueStore store,
FingerprintValueCache cache,
HashFunction hashFunction) {
this.executor = executor;
this.store = store;
this.cache = cache;
this.hashFunction = hashFunction;
this.fingerprintPlaceholder = fingerprint(new byte[] {});
this.fingerprintLength = fingerprintPlaceholder.size();
}
/** Delegates to {@link FingerprintValueStore#put}. */
ListenableFuture<Void> put(ByteString fingerprint, byte[] serializedBytes) {
return store.put(fingerprint, serializedBytes);
}
/** Delegates to {@link FingerprintValueStore#get}. */
ListenableFuture<byte[]> get(ByteString fingerprint) throws IOException {
return store.get(fingerprint);
}
/** Delegates to {@link FingerprintValueCache#getOrClaimPutOperation}. */
@Nullable
Object getOrClaimPutOperation(
Object obj, @Nullable Object distinguisher, ListenableFuture<PutOperation> putOperation) {
return cache.getOrClaimPutOperation(obj, distinguisher, putOperation);
}
/** Delegates to {@link FingerprintValueCache#getOrClaimGetOperation}. */
@Nullable
Object getOrClaimGetOperation(
ByteString fingerprint,
@Nullable Object distinguisher,
ListenableFuture<Object> getOperation) {
return cache.getOrClaimGetOperation(fingerprint, distinguisher, getOperation);
}
/** Computes the fingerprint of {@code bytes}. */
ByteString fingerprint(byte[] bytes) {
return ByteString.copyFrom(hashFunction.hashBytes(bytes).asBytes());
}
/**
* A placeholder fingerprint to use when the actual fingerprint is not yet available.
*
* <p>The placeholder has the same length as the real fingerprint so the real fingerprint can
* overwrite the placeholder when it becomes available.
*/
ByteString fingerprintPlaceholder() {
return fingerprintPlaceholder;
}
/** The fixed length of fingerprints. */
int fingerprintLength() {
return fingerprintLength;
}
/**
* Executor for chaining work on top of futures returned by {@link #put} or {@link #get}.
*
* <p>Those callbacks may be executing on RPC threads that should not be blocked.
*/
Executor getExecutor() {
return executor;
}
}