wip
diff --git a/subprojects/build-cache-http/src/main/java/org/gradle/caching/http/internal/AsyncHttpBuildCacheService.java b/subprojects/build-cache-http/src/main/java/org/gradle/caching/http/internal/AsyncHttpBuildCacheService.java
new file mode 100644
index 0000000..ab3e247
--- /dev/null
+++ b/subprojects/build-cache-http/src/main/java/org/gradle/caching/http/internal/AsyncHttpBuildCacheService.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2023 the original author or authors.
+ *
+ * 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 org.gradle.caching.http.internal;
+
+import org.gradle.caching.AsyncBuildCacheService;
+import org.gradle.caching.BuildCacheEntryReader;
+import org.gradle.caching.BuildCacheEntryWriter;
+import org.gradle.caching.BuildCacheException;
+import org.gradle.caching.BuildCacheKey;
+
+import java.io.IOException;
+import java.util.concurrent.CompletableFuture;
+
+public class AsyncHttpBuildCacheService implements AsyncBuildCacheService {
+
+    @Override
+    public CompletableFuture<Boolean> contains(BuildCacheKey key) {
+        return AsyncBuildCacheService.super.contains(key);
+    }
+
+    @Override
+    public CompletableFuture<Boolean> loadAsync(BuildCacheKey key, BuildCacheEntryReader reader) throws BuildCacheException {
+        return null;
+    }
+
+    @Override
+    public CompletableFuture<Void> store(BuildCacheKey key, BuildCacheEntryWriter writer) throws BuildCacheException {
+        return null;
+    }
+
+    @Override
+    public void close() throws IOException {
+
+    }
+}
diff --git a/subprojects/build-cache/src/main/java/org/gradle/caching/internal/controller/AsyncNextGenBuildCacheHandler.java b/subprojects/build-cache/src/main/java/org/gradle/caching/internal/controller/AsyncNextGenBuildCacheHandler.java
new file mode 100644
index 0000000..c030e89
--- /dev/null
+++ b/subprojects/build-cache/src/main/java/org/gradle/caching/internal/controller/AsyncNextGenBuildCacheHandler.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2023 the original author or authors.
+ *
+ * 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 org.gradle.caching.internal.controller;
+
+import org.gradle.caching.BuildCacheEntryReader;
+import org.gradle.caching.BuildCacheEntryWriter;
+import org.gradle.caching.BuildCacheException;
+import org.gradle.caching.BuildCacheKey;
+
+import java.io.Closeable;
+import java.util.concurrent.CompletableFuture;
+
+public interface AsyncNextGenBuildCacheHandler extends Closeable {
+
+    boolean canLoad();
+
+    boolean canStore();
+
+    CompletableFuture<Boolean> contains(BuildCacheKey key);
+
+    CompletableFuture<Boolean> load(BuildCacheKey key, BuildCacheEntryReader reader) throws BuildCacheException;
+
+    CompletableFuture<Void> store(BuildCacheKey key, BuildCacheEntryWriter writer) throws BuildCacheException;
+
+}
diff --git a/subprojects/build-cache/src/main/java/org/gradle/caching/internal/controller/DefaultNextGenBuildCacheAccess.java b/subprojects/build-cache/src/main/java/org/gradle/caching/internal/controller/DefaultNextGenBuildCacheAccess.java
index 71aabbd..188d0ba 100644
--- a/subprojects/build-cache/src/main/java/org/gradle/caching/internal/controller/DefaultNextGenBuildCacheAccess.java
+++ b/subprojects/build-cache/src/main/java/org/gradle/caching/internal/controller/DefaultNextGenBuildCacheAccess.java
@@ -44,14 +44,14 @@ public class DefaultNextGenBuildCacheAccess implements NextGenBuildCacheAccess {
     private static final CompletableFuture<?>[] EMPTY_COMPLETABLE_FUTURE_ARRAY = new CompletableFuture<?>[0];
 
     private final NextGenBuildCacheHandler local;
-    private final NextGenBuildCacheHandler remote;
+    private final AsyncNextGenBuildCacheHandler remote;
     private final BufferProvider bufferProvider;
     private final ManagedThreadPoolExecutor remoteProcessor;
     private final ConcurrencyCounter counter;
 
     public DefaultNextGenBuildCacheAccess(
         NextGenBuildCacheHandler local,
-        NextGenBuildCacheHandler remote,
+        AsyncNextGenBuildCacheHandler remote,
         BufferProvider bufferProvider,
         ExecutorFactory executorFactory
     ) {
@@ -66,6 +66,8 @@ public DefaultNextGenBuildCacheAccess(
     @Override
     public <T> void load(Map<BuildCacheKey, T> entries, LoadHandler<T> handler) {
         List<CompletableFuture<Void>> remoteDownloads = new ArrayList<>();
+
+
         entries.forEach((key, payload) -> {
             boolean foundLocally = local.canLoad() && local.load(key, input -> handler.handle(input, payload));
             if (!foundLocally && remote.canLoad()) {
@@ -85,6 +87,7 @@ public <T> void store(Map<BuildCacheKey, T> entries, StoreHandler<T> handler) {
                 return;
             }
             if (!local.contains(key)) {
+                //FIXME could be async?
                 local.store(key, handler.handle(payload));
             }
             // TODO Add error handling
diff --git a/subprojects/build-cache/src/main/java/org/gradle/caching/internal/controller/service/BaseRemoteBuildCacheServiceHandle.java b/subprojects/build-cache/src/main/java/org/gradle/caching/internal/controller/service/BaseRemoteBuildCacheServiceHandle.java
index 2371e37..9abda9d 100644
--- a/subprojects/build-cache/src/main/java/org/gradle/caching/internal/controller/service/BaseRemoteBuildCacheServiceHandle.java
+++ b/subprojects/build-cache/src/main/java/org/gradle/caching/internal/controller/service/BaseRemoteBuildCacheServiceHandle.java
@@ -82,11 +82,11 @@ public final Optional<BuildCacheLoadResult> maybeLoad(BuildCacheKey key, File lo
     }
 
     protected void loadInner(String description, BuildCacheKey key, LoadTarget loadTarget) {
-        service.load(key, loadTarget);
+        service.loadAsync(key, loadTarget);
     }
 
     protected void loadInner(BuildCacheKey key, BuildCacheEntryReader entryReader) {
-        service.load(key, entryReader);
+        service.loadAsync(key, entryReader);
     }
 
     private Optional<BuildCacheLoadResult> maybeUnpack(LoadTarget loadTarget, Function<File, BuildCacheLoadResult> unpackFunction) {
diff --git a/subprojects/core-api/src/main/java/org/gradle/caching/AsyncBuildCacheService.java b/subprojects/core-api/src/main/java/org/gradle/caching/AsyncBuildCacheService.java
new file mode 100644
index 0000000..ff452fc
--- /dev/null
+++ b/subprojects/core-api/src/main/java/org/gradle/caching/AsyncBuildCacheService.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2023 the original author or authors.
+ *
+ * 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 org.gradle.caching;
+
+import org.gradle.api.Incubating;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+
+public interface AsyncBuildCacheService extends BuildCacheService { // TODO temp fix to reduce the scope of the changes and just downcast where required -> DefaultNextGenBuildCacheAccess
+    @Incubating
+    default CompletableFuture<Boolean> containsAsync(BuildCacheKey key) {
+        return loadAsync(key, __ -> {});
+    }
+
+    CompletableFuture<Boolean> loadAsync(BuildCacheKey key, BuildCacheEntryReader reader) throws BuildCacheException;
+
+    CompletableFuture<Void> storeAsync(BuildCacheKey key, BuildCacheEntryWriter writer) throws BuildCacheException;
+
+
+    @Override
+    default boolean contains(BuildCacheKey key) {
+        try {
+            return containsAsync(key).get();
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        } catch (ExecutionException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    default boolean load(BuildCacheKey key, BuildCacheEntryReader reader) throws BuildCacheException {
+        try {
+            return loadAsync(key, reader).get();
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        } catch (ExecutionException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    default void store(BuildCacheKey key, BuildCacheEntryWriter writer) throws BuildCacheException {
+        try {
+            storeAsync(key, writer).get();
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        } catch (ExecutionException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/subprojects/core/src/main/java/org/gradle/caching/internal/services/AbstractBuildCacheControllerFactory.java b/subprojects/core/src/main/java/org/gradle/caching/internal/services/AbstractBuildCacheControllerFactory.java
index 336fe52..a197b7b 100644
--- a/subprojects/core/src/main/java/org/gradle/caching/internal/services/AbstractBuildCacheControllerFactory.java
+++ b/subprojects/core/src/main/java/org/gradle/caching/internal/services/AbstractBuildCacheControllerFactory.java
@@ -21,6 +21,7 @@
 import org.gradle.StartParameter;
 import org.gradle.api.internal.GeneratedSubclasses;
 import org.gradle.api.internal.cache.StringInterner;
+import org.gradle.caching.AsyncBuildCacheService;
 import org.gradle.caching.BuildCacheService;
 import org.gradle.caching.BuildCacheServiceFactory;
 import org.gradle.caching.configuration.BuildCache;
@@ -81,7 +82,7 @@ public AbstractBuildCacheControllerFactory(
 
     abstract protected BuildCacheController doCreateController(
         @Nullable DescribedBuildCacheService<DirectoryBuildCache, L> localDescribedService,
-        @Nullable DescribedBuildCacheService<BuildCache, BuildCacheService> remoteDescribedService
+        @Nullable DescribedBuildCacheService<BuildCache, AsyncBuildCacheService> remoteDescribedService
     );
 
     @Override
diff --git a/subprojects/core/src/main/java/org/gradle/caching/internal/services/LegacyBuildCacheControllerFactory.java b/subprojects/core/src/main/java/org/gradle/caching/internal/services/LegacyBuildCacheControllerFactory.java
index 587bd1d..3938af8 100644
--- a/subprojects/core/src/main/java/org/gradle/caching/internal/services/LegacyBuildCacheControllerFactory.java
+++ b/subprojects/core/src/main/java/org/gradle/caching/internal/services/LegacyBuildCacheControllerFactory.java
@@ -20,6 +20,7 @@
 import org.gradle.api.internal.cache.StringInterner;
 import org.gradle.api.internal.file.temp.TemporaryFileProvider;
 import org.gradle.api.logging.configuration.ShowStacktrace;
+import org.gradle.caching.AsyncBuildCacheService;
 import org.gradle.caching.BuildCacheService;
 import org.gradle.caching.configuration.BuildCache;
 import org.gradle.caching.internal.controller.BuildCacheController;
@@ -62,7 +63,7 @@ public LegacyBuildCacheControllerFactory(
     @Override
     protected BuildCacheController doCreateController(
         @Nullable DescribedBuildCacheService<DirectoryBuildCache, DirectoryBuildCacheService> localDescribedService,
-        @Nullable DescribedBuildCacheService<BuildCache, BuildCacheService> remoteDescribedService
+        @Nullable DescribedBuildCacheService<BuildCache, AsyncBuildCacheService> remoteDescribedService
     ) {
         BuildCacheServicesConfiguration config = toConfiguration(
             localDescribedService,
@@ -88,7 +89,7 @@ protected BuildCacheController doCreateController(
 
     private static BuildCacheServicesConfiguration toConfiguration(
         @Nullable DescribedBuildCacheService<DirectoryBuildCache, DirectoryBuildCacheService> local,
-        @Nullable DescribedBuildCacheService<BuildCache, BuildCacheService> remote
+        @Nullable DescribedBuildCacheService<BuildCache, AsyncBuildCacheService> remote
     ) {
         boolean localPush = local != null && local.config.isPush();
         boolean remotePush = remote != null && remote.config.isPush();
diff --git a/subprojects/core/src/main/java/org/gradle/caching/internal/services/NextGenBuildCacheControllerFactory.java b/subprojects/core/src/main/java/org/gradle/caching/internal/services/NextGenBuildCacheControllerFactory.java
index d62111d..b48bd72 100644
--- a/subprojects/core/src/main/java/org/gradle/caching/internal/services/NextGenBuildCacheControllerFactory.java
+++ b/subprojects/core/src/main/java/org/gradle/caching/internal/services/NextGenBuildCacheControllerFactory.java
@@ -18,12 +18,14 @@
 
 import org.gradle.StartParameter;
 import org.gradle.api.internal.cache.StringInterner;
+import org.gradle.caching.AsyncBuildCacheService;
 import org.gradle.caching.BuildCacheEntryReader;
 import org.gradle.caching.BuildCacheEntryWriter;
 import org.gradle.caching.BuildCacheException;
 import org.gradle.caching.BuildCacheKey;
 import org.gradle.caching.BuildCacheService;
 import org.gradle.caching.configuration.BuildCache;
+import org.gradle.caching.internal.controller.AsyncNextGenBuildCacheHandler;
 import org.gradle.caching.internal.controller.BuildCacheController;
 import org.gradle.caching.internal.controller.DefaultNextGenBuildCacheAccess;
 import org.gradle.caching.internal.controller.GZipNextGenBuildCacheAccess;
@@ -42,6 +44,7 @@
 
 import javax.annotation.Nullable;
 import java.io.IOException;
+import java.util.concurrent.CompletableFuture;
 
 public final class NextGenBuildCacheControllerFactory extends AbstractBuildCacheControllerFactory<H2BuildCacheService> {
 
@@ -76,12 +79,12 @@ public NextGenBuildCacheControllerFactory(
     @Override
     protected BuildCacheController doCreateController(
         @Nullable DescribedBuildCacheService<DirectoryBuildCache, H2BuildCacheService> localDescribedService,
-        @Nullable DescribedBuildCacheService<BuildCache, BuildCacheService> remoteDescribedService
+        @Nullable DescribedBuildCacheService<BuildCache, AsyncBuildCacheService> remoteDescribedService
     ) {
         IncubationLogger.incubatingFeatureUsed("Next generation build cache");
 
         NextGenBuildCacheHandler local = resolveService(localDescribedService);
-        NextGenBuildCacheHandler remote = resolveService(remoteDescribedService);
+        AsyncNextGenBuildCacheHandler remote = resolveRemoteService(remoteDescribedService);
 
         return new NextGenBuildCacheController(
             buildInvocationScopeId.getId().asString(),
@@ -107,6 +110,12 @@ private static NextGenBuildCacheHandler resolveService(@Nullable DescribedBuildC
             : DISABLED_BUILD_CACHE_HANDLER;
     }
 
+    private static AsyncNextGenBuildCacheHandler resolveRemoteService(@Nullable DescribedBuildCacheService<? extends BuildCache, ? extends AsyncBuildCacheService> describedService) {
+        return describedService != null && describedService.config.isEnabled()
+            ? new AsyncDefaultNextGenBuildCacheHandler(describedService.service, describedService.config.isPush())
+            : DISABLED_ASYNC_BUILD_CACHE_HANDLER;
+    }
+
     private static final NextGenBuildCacheHandler DISABLED_BUILD_CACHE_HANDLER = new NextGenBuildCacheHandler() {
         @Override
         public boolean canLoad() {
@@ -137,6 +146,81 @@ public void close() {
         }
     };
 
+    private static final AsyncNextGenBuildCacheHandler DISABLED_ASYNC_BUILD_CACHE_HANDLER = new AsyncNextGenBuildCacheHandler() {
+        @Override
+        public boolean canLoad() {
+            return false;
+        }
+
+        @Override
+        public boolean canStore() {
+            return false;
+        }
+
+        @Override
+        public CompletableFuture<Boolean> contains(BuildCacheKey key) {
+            return null;
+        }
+
+        @Override
+        public CompletableFuture<Boolean> load(BuildCacheKey key, BuildCacheEntryReader reader) throws BuildCacheException {
+            return null;
+        }
+
+        @Override
+        public CompletableFuture<Void> store(BuildCacheKey key, BuildCacheEntryWriter writer) throws BuildCacheException {
+            return null;
+        }
+
+        @Override
+        public void close() throws IOException {
+
+        }
+    }
+
+
+
+    private static class AsyncDefaultNextGenBuildCacheHandler implements AsyncNextGenBuildCacheHandler {
+        private final AsyncBuildCacheService service;
+        private final boolean pushEnabled;
+
+        public AsyncDefaultNextGenBuildCacheHandler(AsyncBuildCacheService service, boolean pushEnabled) {
+            this.service = service;
+            this.pushEnabled = pushEnabled;
+        }
+
+        @Override
+        public boolean canLoad() {
+            return false;
+        }
+
+        @Override
+        public boolean canStore() {
+            return false;
+        }
+
+        @Override
+        public CompletableFuture<Boolean> contains(BuildCacheKey key) {
+            return null;
+        }
+
+        @Override
+        public CompletableFuture<Boolean> load(BuildCacheKey key, BuildCacheEntryReader reader) throws BuildCacheException {
+            return null;
+        }
+
+        @Override
+        public CompletableFuture<Void> store(BuildCacheKey key, BuildCacheEntryWriter writer) throws BuildCacheException {
+            return null;
+        }
+
+        @Override
+        public void close() throws IOException {
+
+        }
+    }
+
+
     private static class DefaultNextGenBuildCacheHandler implements NextGenBuildCacheHandler {
         private final BuildCacheService service;
         private final boolean pushEnabled;
@@ -163,7 +247,7 @@ public boolean contains(BuildCacheKey key) {
 
         @Override
         public boolean load(BuildCacheKey key, BuildCacheEntryReader reader) throws BuildCacheException {
-            return service.load(key, reader);
+            return service.loadAsync(key, reader);
         }
 
         @Override