Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ public ManagedChannel newChannel(
private final RepositoryRemoteHelpersFactoryDelegate repositoryRemoteHelpersFactoryDelegate =
new RepositoryRemoteHelpersFactoryDelegate();

private Downloader remoteDownloader;
@Nullable private GrpcRemoteDownloader remoteDownloader;

private CredentialModule credentialModule;

Expand Down Expand Up @@ -1130,11 +1130,20 @@ public void afterCommand() {
// Some cleanup tasks must wait until every other BlazeModule's afterCommand() has run, as
// otherwise we might interfere with asynchronous remote downloads that are in progress.
RemoteActionContextProvider actionContextProviderRef = actionContextProvider;
GrpcRemoteDownloader remoteDownloaderRef = remoteDownloader;
TempPathGenerator tempPathGeneratorRef = tempPathGenerator;
AsynchronousMessageOutputStream<LogEntry> rpcLogFileRef = rpcLogFile;
if (actionContextProviderRef != null || tempPathGeneratorRef != null || rpcLogFileRef != null) {
if (actionContextProviderRef != null
|| remoteDownloaderRef != null
|| tempPathGeneratorRef != null
|| rpcLogFileRef != null) {
blockWaitingModule.submit(
() -> afterCommandTask(actionContextProviderRef, tempPathGeneratorRef, rpcLogFileRef));
() ->
afterCommandTask(
actionContextProviderRef,
remoteDownloaderRef,
tempPathGeneratorRef,
rpcLogFileRef));
}

lastRemoteOutputChecker = remoteOutputChecker;
Expand All @@ -1159,13 +1168,18 @@ public void afterCommand() {

private static void afterCommandTask(
@Nullable RemoteActionContextProvider actionContextProvider,
@Nullable GrpcRemoteDownloader remoteDownloader,
@Nullable TempPathGenerator tempPathGenerator,
@Nullable AsynchronousMessageOutputStream<LogEntry> rpcLogFile)
throws AbruptExitException {
if (actionContextProvider != null) {
actionContextProvider.afterCommand();
}

if (remoteDownloader != null) {
remoteDownloader.close();
}

if (tempPathGenerator != null) {
Path tempDir = tempPathGenerator.getTempDir();
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,16 @@ public GrpcRemoteDownloader(
this.remoteDownloaderLocalFallback = remoteDownloaderLocalFallback;
}

/**
* Releases the reference to the channel held by this downloader.
*
* <p>The {@link RemoteCacheClient} is owned by the caller and thus not closed here.
*/
@Override
public void close() {
if (closed.getAndSet(true)) {
return;
}
cacheClient.close();
channel.release();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
import com.google.devtools.common.options.OptionsParsingResult;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import io.grpc.BindableService;
import io.grpc.ManagedChannel;
import io.grpc.Server;
import io.grpc.ServerInterceptors;
import io.grpc.inprocess.InProcessChannelBuilder;
Expand All @@ -90,6 +91,7 @@
import java.net.URI;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
Expand All @@ -104,6 +106,7 @@
public final class RemoteModuleTest {
private static final String EXECUTION_SERVER_NAME = "execution-server";
private static final String CACHE_SERVER_NAME = "cache-server";
private static final String DOWNLOADER_SERVER_NAME = "downloader-server";
private static final String OUTPUT_SERVICE_SERVER_NAME = "output-service";
private static final ServerCapabilities CACHE_ONLY_CAPS =
ServerCapabilities.newBuilder()
Expand Down Expand Up @@ -261,15 +264,20 @@ private static RemoteOptions parseRemoteOptions(String... args) throws Exception
private RemoteModule remoteModule;
private RemoteOptions remoteOptions;
private Map<String, Map<String, ?>> serviceConfigsByTarget;
private List<ManagedChannel> createdChannels;

@Before
public void initialize() {
serviceConfigsByTarget = new HashMap<>();
createdChannels = new ArrayList<>();
remoteModule = new RemoteModule();
remoteModule.setChannelFactory(
(target, proxy, options, interceptors, serviceConfig) -> {
serviceConfigsByTarget.put(target, serviceConfig);
return InProcessChannelBuilder.forName(target).directExecutor().build();
ManagedChannel channel =
InProcessChannelBuilder.forName(target).directExecutor().build();
createdChannels.add(channel);
return channel;
});
remoteOptions = Options.getDefaults(RemoteOptions.class);
}
Expand Down Expand Up @@ -738,11 +746,90 @@ private CommandEnvironment beforeCommand(WorkspaceInitializer workspaceInitializ
throws IOException, AbruptExitException {
CommandEnvironment env =
createTestCommandEnvironment(remoteModule, remoteOptions, workspaceInitializer);
env.getRuntime().getBlazeModule(BlockWaitingModule.class).beforeCommand(env);
remoteModule.beforeCommand(env);
env.throwPendingException();
return env;
}

/** Runs the remote module's after-command cleanup and waits for it to complete. */
private void afterCommand(CommandEnvironment env) throws AbruptExitException {
remoteModule.afterCommand();
env.getRuntime().getBlazeModule(BlockWaitingModule.class).afterCommand();
}

/** Waits for the eagerly created channels of the current command to be connected. */
private void awaitChannelsConnected() throws Exception {
var combinedCache = remoteModule.getActionContextProvider().getCombinedCache();
if (combinedCache != null) {
var unused = combinedCache.getRemoteCacheCapabilities();
}
if (remoteModule.getRemoteDownloader() instanceof GrpcRemoteDownloader downloader) {
var unused = downloader.getChannel().withChannelBlocking(ch -> new Object());
}
}

@Test
public void remoteDownloader_separateEndpoint_channelsAreClosedAfterCommand() throws Exception {
Server cacheServer = createFakeServer(CACHE_SERVER_NAME, new CapabilitiesImpl(CACHE_ONLY_CAPS));
cacheServer.start();
Server downloaderServer = createFakeServer(DOWNLOADER_SERVER_NAME);
downloaderServer.start();

try {
remoteOptions.setRemoteCache(CACHE_SERVER_NAME);
remoteOptions.setRemoteDownloader(DOWNLOADER_SERVER_NAME);

var env = beforeCommand();
awaitChannelsConnected();
assertThat(createdChannels).hasSize(2);

afterCommand(env);

for (ManagedChannel channel : createdChannels) {
assertThat(channel.isTerminated()).isTrue();
}
} finally {
cacheServer.shutdownNow();
downloaderServer.shutdownNow();
cacheServer.awaitTermination();
downloaderServer.awaitTermination();
}
}

@Test
public void remoteDownloader_sharedEndpoint_channelIsClosedOnceCacheIsReleased()
throws Exception {
Server cacheServer = createFakeServer(CACHE_SERVER_NAME, new CapabilitiesImpl(CACHE_ONLY_CAPS));
cacheServer.start();

try {
remoteOptions.setRemoteCache(CACHE_SERVER_NAME);
remoteOptions.setRemoteDownloader(CACHE_SERVER_NAME);

var env = beforeCommand();
awaitChannelsConnected();
assertThat(createdChannels).hasSize(1);
ManagedChannel channel = createdChannels.get(0);

// Retain the cache beyond the end of the command, as e.g. the artifact uploader of an
// asynchronous BES upload does.
var combinedCache = remoteModule.getActionContextProvider().getCombinedCache();
combinedCache.retain();
afterCommand(env);

// Closing the downloader must not shut down the channel it shares with the cache.
assertThat(channel.isShutdown()).isFalse();

combinedCache.release();

assertThat(channel.isTerminated()).isTrue();
} finally {
cacheServer.shutdownNow();
cacheServer.awaitTermination();
}
}

@Test
public void diskCache_defaultLocation_resolvesToOutputUserRoot() throws Exception {
remoteOptions.setDiskCache(PathFragment.EMPTY_FRAGMENT);
Expand Down
Loading