Skip to content

Commit

Permalink
feature disk cache use APFS cow optimize disk IO performance
Browse files Browse the repository at this point in the history
  • Loading branch information
kila2 committed Jan 17, 2025
1 parent 17ace28 commit a7b4fec
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import com.google.devtools.build.lib.exec.SpawnProgressEvent;
import com.google.devtools.build.lib.remote.common.CacheNotFoundException;
import com.google.devtools.build.lib.remote.common.LazyFileOutputStream;
import com.google.devtools.build.lib.remote.common.DirectCopyOutputStream;
import com.google.devtools.build.lib.remote.common.OutputDigestMismatchException;
import com.google.devtools.build.lib.remote.common.ProgressStatusListener;
import com.google.devtools.build.lib.remote.common.RemoteActionExecutionContext;
Expand Down Expand Up @@ -603,6 +604,7 @@ private ListenableFuture<Void> downloadFile(

reporter.started();
OutputStream out = new ReportingOutputStream(new LazyFileOutputStream(path), reporter);
OutputStream out = new DirectCopyOutputStream(new ReportingOutputStream(new LazyFileOutputStream(path), reporter));

ListenableFuture<Void> f = downloadBlob(context, digest, out);
f.addListener(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright 2021 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.remote.common;

import com.google.devtools.build.lib.vfs.Path;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;

/**
* use direct copy or to optimize io performance. this class only use to pass output file path.
*/
public class DirectCopyOutputStream extends OutputStream {

public final Path path;
private OutputStream out;
private boolean directCopyed;

public DirectCopyOutputStream(OutputStream out, Path path) {
this.path = path;
this.out = out;
}

public void setDirectCopyed(boolean directCopyed){
this.directCopyed = directCopyed;
}

public boolean getDirectCopyed(){
return this.directCopyed;
}

@Override
public void write(byte[] b) throws IOException {
if (directCopyed == false) {
out.write(b);
}
}

@Override
public void write(byte[] b, int off, int len) throws IOException {
if (directCopyed == false) {
out.write(b, off, len);
}
}

@Override
public void write(int b) throws IOException {
if (directCopyed == false) {
out.write(b);
}
}

@Override
public void flush() throws IOException {
if (directCopyed == false) {
out.flush();
}
}

@Override
public void close() throws IOException {
if (directCopyed == false) {
out.close();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,21 @@
import com.google.devtools.build.lib.remote.Store;
import com.google.devtools.build.lib.remote.common.CacheNotFoundException;
import com.google.devtools.build.lib.remote.common.RemoteCacheClient.ActionKey;
import com.google.devtools.build.lib.remote.common.DirectCopyOutputStream;
import com.google.devtools.build.lib.remote.util.DigestOutputStream;
import com.google.devtools.build.lib.remote.util.DigestUtil;
import com.google.devtools.build.lib.remote.util.Utils;
import com.google.devtools.build.lib.vfs.Path;
import com.google.protobuf.ByteString;
import com.google.protobuf.ExtensionRegistryLite;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import javax.annotation.Nullable;
Expand Down Expand Up @@ -140,32 +144,53 @@ public void captureFile(Path src, Digest digest, Store store) throws IOException
target.getParentDirectory().createDirectoryAndParents();
src.renameTo(target);
}

private ListenableFuture<Void> download(Digest digest, OutputStream out, Store store) {
return download(digest, out, store, null);
}
private ListenableFuture<Void> download(Digest digest, OutputStream out, Store store, DirectCopyOutputStream directCopyOut) {
return executorService.submit(
() -> {
Path path = toPath(digest, store);
if (!refresh(path)) {
throw new CacheNotFoundException(digest);
}
try (InputStream in = path.getInputStream()) {
ByteStreams.copy(in, out);
if (directCopyOut != null) {
try {
directCopyFile(path, directCopyOut.path);
directCopyOut.setDirectCopyed(true);
} catch (IOException e) {
try (InputStream in = path.getInputStream()) {
ByteStreams.copy(in, out);
}
}
} else {
try (InputStream in = path.getInputStream()) {
ByteStreams.copy(in, out);
}
}
return null;
});
}

public ListenableFuture<Void> downloadBlob(Digest digest, OutputStream out) {

@Nullable
DirectCopyOutputStream directCopyOut = (out instanceof DirectCopyOutputStream) ? (DirectCopyOutputStream)out : null;

@Nullable
DigestOutputStream digestOut = verifyDownloads ? digestUtil.newDigestOutputStream(out) : null;
return Futures.transformAsync(
download(digest, digestOut != null ? digestOut : out, Store.CAS),
download(digest, digestOut != null ? digestOut : out, Store.CAS, directCopyOut),
(v) -> {
try {
if (digestOut != null) {
Utils.verifyBlobContents(digest, digestOut.digest());
if (directCopyOut != null) {
Utils.verifyBlobContents(digest, digestUtil.compute(directCopyOut.path));
} else {
Utils.verifyBlobContents(digest, digestOut.digest());
out.flush();
}
}
out.flush();
return immediateFuture(null);
} catch (IOException e) {
return Futures.immediateFailedFuture(e);
Expand Down Expand Up @@ -267,8 +292,11 @@ public void close() {}
public ListenableFuture<Void> uploadFile(Digest digest, Path file) {
return executorService.submit(
() -> {
try (InputStream in = file.getInputStream()) {
saveFile(digest, Store.CAS, in);
try {

directSaveFile(digest, Store.CAS, file);
} catch (IOException e) {
throw e;
}
return null;
});
Expand Down Expand Up @@ -301,7 +329,32 @@ public Path toPath(Digest digest, Store store) {

public Path toPath(String hash, Store store) {
// Create the file in a subfolder to bypass possible folder file count limits.
return storeRootMap.get(store).getChild(hash.substring(0, 2)).getChild(hash);
return root.getChild(store.toString()).getChild(hash.substring(0, 2)).getChild(hash);
}

private void directCopyFile(Path src, Path dest) throws IOException {
java.nio.file.Path srcNIOPath = new File(src.asFragment().toString()).toPath();
java.nio.file.Path destNIOPath = new File(dest.asFragment().toString()).toPath();
try {
Files.copy(srcNIOPath, destNIOPath, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
} catch (IOException e) {
throw e;
}
}

private void directSaveFile(Digest digest, Store store, Path inputFile) throws IOException {
Path path = toPath(digest, store);

if (refresh(path)) {
return;
}

try {
path.getParentDirectory().createDirectoryAndParents();
directCopyFile(inputFile, path);
} catch (IOException e) {
throw e;
}
}

public void saveFile(Digest digest, Store store, InputStream in) throws IOException {
Expand Down

0 comments on commit a7b4fec

Please sign in to comment.