implements PR-021
This commit is contained in:
parent
4278d045c2
commit
223f9a0f87
@ -0,0 +1,77 @@
|
||||
# PR-21 Point In-Memory Snapshot Updates After Write Commit
|
||||
|
||||
Domain Owner: `docs/packer`
|
||||
Cross-Domain Impact: `docs/studio`
|
||||
|
||||
## Briefing
|
||||
|
||||
The current write path still rebuilds the whole project runtime snapshot after a successful point write such as `REGISTER`.
|
||||
|
||||
That is too expensive for the runtime model we want.
|
||||
|
||||
Studio refresh on top of an already-updated in-memory snapshot is acceptable. Full filesystem rescan inside the packer after every point write is not.
|
||||
|
||||
This PR replaces the normal post-write `full refresh` path with point snapshot updates in memory, while keeping full reload available only as an explicit recovery fallback.
|
||||
|
||||
## Objective
|
||||
|
||||
Remove whole-project runtime rescan from the normal point write path and replace it with point in-memory snapshot updates after durable commit.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- [`./PR-14-project-runtime-core-snapshot-model-and-lifecycle.md`](./PR-14-project-runtime-core-snapshot-model-and-lifecycle.md)
|
||||
- [`./PR-15-snapshot-backed-asset-query-services.md`](./PR-15-snapshot-backed-asset-query-services.md)
|
||||
- [`./PR-16-write-lane-command-completion-and-used-write-services.md`](./PR-16-write-lane-command-completion-and-used-write-services.md)
|
||||
- [`./PR-20-asset-action-capabilities-and-register-first-delivery.md`](./PR-20-asset-action-capabilities-and-register-first-delivery.md)
|
||||
|
||||
## Scope
|
||||
|
||||
- add explicit point snapshot update support to the runtime registry
|
||||
- keep the write lane as the only owner of point runtime mutation after durable commit
|
||||
- replace post-write `runtimeRegistry.refresh(project)` in the active write path with point snapshot patching
|
||||
- deliver the first point patch for `REGISTER`
|
||||
- keep Studio refresh behavior unchanged
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- no background divergence detection
|
||||
- no reconcile loop
|
||||
- no change to Studio refresh semantics
|
||||
- no broad event model redesign
|
||||
- no speculative patch implementation for unused write actions
|
||||
|
||||
## Execution Method
|
||||
|
||||
1. Extend the runtime registry so it can update a loaded project snapshot by applying a point patch function inside the packer-owned write flow.
|
||||
2. Keep full snapshot reload available as recovery fallback, but do not use it in the normal successful point write path.
|
||||
3. Model a point patch for `REGISTER` that:
|
||||
- appends the new registry entry to the in-memory registry view
|
||||
- updates the matching runtime asset from unregistered to registered
|
||||
- preserves the rest of the snapshot untouched
|
||||
4. Apply the patch only after the durable disk commit succeeds.
|
||||
5. Keep read services unchanged from the Studio point of view; they should continue reading from the runtime snapshot.
|
||||
6. Keep Studio free to refresh list/details after write completion, because those reads now hit the already-updated in-memory snapshot.
|
||||
7. Add regression coverage proving that `REGISTER` no longer depends on whole-project rescan to become visible to subsequent reads.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- successful point writes no longer trigger whole-project runtime rescan in the normal path
|
||||
- the runtime registry supports point in-memory snapshot updates after durable commit
|
||||
- `REGISTER` updates the loaded project snapshot in place
|
||||
- subsequent `listAssets`, `getAssetDetails`, and `getAssetActions` read the updated state from memory
|
||||
- Studio refresh after `REGISTER` remains valid and does not require frontend-local state patching
|
||||
- full reload remains available only as fallback or explicit recovery path
|
||||
|
||||
## Validation
|
||||
|
||||
- packer tests for point snapshot patching on `REGISTER`
|
||||
- packer tests proving read-after-write coherence without full runtime rebuild
|
||||
- regression tests for fallback safety when point patching cannot be applied
|
||||
- Studio smoke validation confirming existing refresh behavior still works on top of the updated runtime snapshot
|
||||
|
||||
## Affected Artifacts
|
||||
|
||||
- `prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/services/**`
|
||||
- `prometeu-packer/prometeu-packer-v1/src/main/java/p/packer/models/**`
|
||||
- `prometeu-packer/prometeu-packer-v1/src/test/java/p/packer/services/**`
|
||||
- `prometeu-studio/src/main/java/p/studio/**`
|
||||
@ -80,6 +80,7 @@ The current production track for the standalone `prometeu-packer` project is:
|
||||
18. [`PR-18-legacy-service-retirement-and-regression-hardening.md`](./PR-18-legacy-service-retirement-and-regression-hardening.md)
|
||||
19. [`PR-19-api-surface-audit-model-separation-and-public-read-dtos.md`](./PR-19-api-surface-audit-model-separation-and-public-read-dtos.md)
|
||||
20. [`PR-20-asset-action-capabilities-and-register-first-delivery.md`](./PR-20-asset-action-capabilities-and-register-first-delivery.md)
|
||||
21. [`PR-21-point-in-memory-snapshot-updates-after-write-commit.md`](./PR-21-point-in-memory-snapshot-updates-after-write-commit.md)
|
||||
|
||||
Current wave discipline from `PR-11` onward:
|
||||
|
||||
@ -90,4 +91,4 @@ Current wave discipline from `PR-11` onward:
|
||||
|
||||
Recommended dependency chain:
|
||||
|
||||
`PR-01 -> PR-02 -> PR-03 -> PR-04 -> PR-05 -> PR-06 -> PR-07 -> PR-08 -> PR-09 -> PR-10 -> PR-11 -> PR-12 -> PR-13 -> PR-14 -> PR-15 -> PR-16 -> PR-17 -> PR-18 -> PR-19 -> PR-20`
|
||||
`PR-01 -> PR-02 -> PR-03 -> PR-04 -> PR-05 -> PR-06 -> PR-07 -> PR-08 -> PR-09 -> PR-10 -> PR-11 -> PR-12 -> PR-13 -> PR-14 -> PR-15 -> PR-16 -> PR-17 -> PR-18 -> PR-19 -> PR-20 -> PR-21`
|
||||
|
||||
@ -32,11 +32,13 @@ public final class Packer implements Closeable {
|
||||
runtimeRegistry,
|
||||
assetReferenceResolver,
|
||||
workspaceFoundation.lookup());
|
||||
final PackerRuntimePatchService runtimePatchService = new PackerRuntimePatchService(declarationParser);
|
||||
final PackerProjectWriteCoordinator writeCoordinator = new PackerProjectWriteCoordinator();
|
||||
return new Packer(new FileSystemPackerWorkspaceService(
|
||||
workspaceFoundation,
|
||||
assetDetailsService,
|
||||
assetActionReadService,
|
||||
runtimePatchService,
|
||||
runtimeRegistry,
|
||||
writeCoordinator,
|
||||
resolvedEventSink), runtimeRegistry, writeCoordinator);
|
||||
|
||||
@ -47,6 +47,7 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
private final PackerWorkspaceFoundation workspaceFoundation;
|
||||
private final PackerAssetDetailsService detailsService;
|
||||
private final PackerAssetActionReadService actionReadService;
|
||||
private final PackerRuntimePatchService runtimePatchService;
|
||||
private final PackerRuntimeRegistry runtimeRegistry;
|
||||
private final PackerProjectWriteCoordinator writeCoordinator;
|
||||
private final PackerEventSink eventSink;
|
||||
@ -55,12 +56,14 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
PackerWorkspaceFoundation workspaceFoundation,
|
||||
PackerAssetDetailsService detailsService,
|
||||
PackerAssetActionReadService actionReadService,
|
||||
PackerRuntimePatchService runtimePatchService,
|
||||
PackerRuntimeRegistry runtimeRegistry,
|
||||
PackerProjectWriteCoordinator writeCoordinator,
|
||||
PackerEventSink eventSink) {
|
||||
this.workspaceFoundation = Objects.requireNonNull(workspaceFoundation, "workspaceFoundation");
|
||||
this.detailsService = Objects.requireNonNull(detailsService, "detailsService");
|
||||
this.actionReadService = Objects.requireNonNull(actionReadService, "actionReadService");
|
||||
this.runtimePatchService = Objects.requireNonNull(runtimePatchService, "runtimePatchService");
|
||||
this.runtimeRegistry = Objects.requireNonNull(runtimeRegistry, "runtimeRegistry");
|
||||
this.writeCoordinator = Objects.requireNonNull(writeCoordinator, "writeCoordinator");
|
||||
this.eventSink = Objects.requireNonNull(eventSink, "eventSink");
|
||||
@ -218,7 +221,13 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
writeManifest(manifestPath, request, entry.assetUuid());
|
||||
final PackerRegistryState updated = workspaceFoundation.appendAllocatedEntry(registry, entry);
|
||||
workspaceFoundation.saveRegistry(project, updated);
|
||||
runtimeRegistry.refresh(project);
|
||||
runtimeRegistry.update(project, (snapshot, generation) -> runtimePatchService.afterCreateAsset(
|
||||
snapshot,
|
||||
generation,
|
||||
updated,
|
||||
entry,
|
||||
assetRoot,
|
||||
manifestPath));
|
||||
final CreateAssetResult result = new CreateAssetResult(
|
||||
PackerOperationStatus.SUCCESS,
|
||||
"Asset created: " + relativeAssetRoot,
|
||||
@ -262,7 +271,12 @@ public final class FileSystemPackerWorkspaceService implements PackerWorkspaceSe
|
||||
declaration.assetUuid());
|
||||
final PackerRegistryState updated = workspaceFoundation.appendAllocatedEntry(registry, entry);
|
||||
workspaceFoundation.saveRegistry(project, updated);
|
||||
runtimeRegistry.refresh(project);
|
||||
runtimeRegistry.update(project, (currentSnapshot, generation) -> runtimePatchService.afterRegisterAsset(
|
||||
currentSnapshot,
|
||||
generation,
|
||||
updated,
|
||||
entry,
|
||||
assetRoot));
|
||||
final RegisterAssetResult result = new RegisterAssetResult(
|
||||
PackerOperationStatus.SUCCESS,
|
||||
"Asset registered: " + relativeAssetRoot,
|
||||
|
||||
@ -6,6 +6,7 @@ import p.packer.models.PackerRuntimeSnapshot;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
public final class PackerProjectRuntime {
|
||||
private final PackerProjectContext project;
|
||||
@ -31,6 +32,13 @@ public final class PackerProjectRuntime {
|
||||
snapshot.set(Objects.requireNonNull(nextSnapshot, "nextSnapshot"));
|
||||
}
|
||||
|
||||
public PackerRuntimeSnapshot updateSnapshot(UnaryOperator<PackerRuntimeSnapshot> updater) {
|
||||
ensureActive();
|
||||
return snapshot.updateAndGet(current -> Objects.requireNonNull(
|
||||
Objects.requireNonNull(updater, "updater").apply(current),
|
||||
"updatedSnapshot"));
|
||||
}
|
||||
|
||||
public boolean disposed() {
|
||||
return disposed.get();
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public final class PackerRuntimeLoader {
|
||||
public final class PackerRuntimeLoader implements PackerRuntimeSnapshotLoader {
|
||||
private final PackerWorkspaceFoundation workspaceFoundation;
|
||||
private final PackerAssetDeclarationParser parser;
|
||||
|
||||
@ -30,6 +30,7 @@ public final class PackerRuntimeLoader {
|
||||
this.parser = Objects.requireNonNull(parser, "parser");
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackerRuntimeSnapshot load(PackerProjectContext project, long generation) {
|
||||
final PackerProjectContext safeProject = Objects.requireNonNull(project, "project");
|
||||
workspaceFoundation.initWorkspace(new InitWorkspaceRequest(safeProject));
|
||||
|
||||
@ -0,0 +1,68 @@
|
||||
package p.packer.services;
|
||||
|
||||
import p.packer.models.PackerAssetDeclarationParseResult;
|
||||
import p.packer.models.PackerRegistryEntry;
|
||||
import p.packer.models.PackerRegistryState;
|
||||
import p.packer.models.PackerRuntimeAsset;
|
||||
import p.packer.models.PackerRuntimeSnapshot;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
public final class PackerRuntimePatchService {
|
||||
private final PackerAssetDeclarationParser declarationParser;
|
||||
|
||||
public PackerRuntimePatchService(PackerAssetDeclarationParser declarationParser) {
|
||||
this.declarationParser = Objects.requireNonNull(declarationParser, "declarationParser");
|
||||
}
|
||||
|
||||
public PackerRuntimeSnapshot afterCreateAsset(
|
||||
PackerRuntimeSnapshot snapshot,
|
||||
long generation,
|
||||
PackerRegistryState updatedRegistry,
|
||||
PackerRegistryEntry entry,
|
||||
Path assetRoot,
|
||||
Path manifestPath) {
|
||||
final PackerAssetDeclarationParseResult parsed = declarationParser.parse(manifestPath);
|
||||
final List<PackerRuntimeAsset> updatedAssets = new ArrayList<>(snapshot.assets().stream()
|
||||
.filter(candidate -> !candidate.assetRoot().equals(assetRoot.toAbsolutePath().normalize()))
|
||||
.toList());
|
||||
updatedAssets.add(new PackerRuntimeAsset(
|
||||
assetRoot,
|
||||
manifestPath,
|
||||
Optional.of(entry),
|
||||
parsed));
|
||||
updatedAssets.sort(Comparator.comparing(asset -> asset.assetRoot().toString(), String.CASE_INSENSITIVE_ORDER));
|
||||
return new PackerRuntimeSnapshot(generation, updatedRegistry, updatedAssets);
|
||||
}
|
||||
|
||||
public PackerRuntimeSnapshot afterRegisterAsset(
|
||||
PackerRuntimeSnapshot snapshot,
|
||||
long generation,
|
||||
PackerRegistryState updatedRegistry,
|
||||
PackerRegistryEntry entry,
|
||||
Path assetRoot) {
|
||||
final List<PackerRuntimeAsset> updatedAssets = new ArrayList<>();
|
||||
boolean patched = false;
|
||||
for (PackerRuntimeAsset asset : snapshot.assets()) {
|
||||
if (asset.assetRoot().equals(assetRoot.toAbsolutePath().normalize())) {
|
||||
updatedAssets.add(new PackerRuntimeAsset(
|
||||
asset.assetRoot(),
|
||||
asset.manifestPath(),
|
||||
Optional.of(entry),
|
||||
asset.parsedDeclaration()));
|
||||
patched = true;
|
||||
} else {
|
||||
updatedAssets.add(asset);
|
||||
}
|
||||
}
|
||||
if (!patched) {
|
||||
throw new IllegalStateException("Unable to patch runtime snapshot for unregistered asset: " + assetRoot);
|
||||
}
|
||||
return new PackerRuntimeSnapshot(generation, updatedRegistry, updatedAssets);
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,10 @@
|
||||
package p.packer.services;
|
||||
|
||||
import p.packer.messages.PackerProjectContext;
|
||||
import p.packer.models.PackerRuntimeSnapshot;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@ -10,11 +12,11 @@ import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
public final class PackerRuntimeRegistry {
|
||||
private final PackerRuntimeLoader loader;
|
||||
private final PackerRuntimeSnapshotLoader loader;
|
||||
private final ConcurrentMap<ProjectKey, PackerProjectRuntime> runtimes = new ConcurrentHashMap<>();
|
||||
private final AtomicLong nextGeneration = new AtomicLong(1L);
|
||||
|
||||
public PackerRuntimeRegistry(PackerRuntimeLoader loader) {
|
||||
public PackerRuntimeRegistry(PackerRuntimeSnapshotLoader loader) {
|
||||
this.loader = Objects.requireNonNull(loader, "loader");
|
||||
}
|
||||
|
||||
@ -46,6 +48,27 @@ public final class PackerRuntimeRegistry {
|
||||
});
|
||||
}
|
||||
|
||||
public PackerProjectRuntime update(
|
||||
PackerProjectContext project,
|
||||
BiFunction<PackerRuntimeSnapshot, Long, PackerRuntimeSnapshot> updater) {
|
||||
final PackerProjectContext safeProject = Objects.requireNonNull(project, "project");
|
||||
final BiFunction<PackerRuntimeSnapshot, Long, PackerRuntimeSnapshot> safeUpdater =
|
||||
Objects.requireNonNull(updater, "updater");
|
||||
final ProjectKey key = ProjectKey.from(safeProject);
|
||||
return runtimes.compute(key, (ignored, current) -> {
|
||||
if (current == null || current.disposed()) {
|
||||
return new PackerProjectRuntime(safeProject, loader.load(safeProject, nextGeneration.getAndIncrement()));
|
||||
}
|
||||
try {
|
||||
final long generation = nextGeneration.getAndIncrement();
|
||||
current.updateSnapshot(snapshot -> safeUpdater.apply(snapshot, generation));
|
||||
} catch (RuntimeException exception) {
|
||||
current.replaceSnapshot(loader.load(safeProject, nextGeneration.getAndIncrement()));
|
||||
}
|
||||
return current;
|
||||
});
|
||||
}
|
||||
|
||||
public void dispose(PackerProjectContext project) {
|
||||
final PackerProjectRuntime removed = runtimes.remove(ProjectKey.from(Objects.requireNonNull(project, "project")));
|
||||
if (removed != null) {
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
package p.packer.services;
|
||||
|
||||
import p.packer.messages.PackerProjectContext;
|
||||
import p.packer.models.PackerRuntimeSnapshot;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface PackerRuntimeSnapshotLoader {
|
||||
PackerRuntimeSnapshot load(PackerProjectContext project, long generation);
|
||||
}
|
||||
@ -147,6 +147,32 @@ final class FileSystemPackerWorkspaceServiceTest {
|
||||
assertTrue(detailsResult.diagnostics().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void createAssetPatchesLoadedSnapshotWithoutWholeProjectReload() throws Exception {
|
||||
final Path projectRoot = tempDir.resolve("created-no-reload");
|
||||
final CountingLoader loader = countingLoader();
|
||||
final FileSystemPackerWorkspaceService service = service(ignored -> { }, loader);
|
||||
|
||||
service.listAssets(new ListAssetsRequest(project(projectRoot)));
|
||||
assertEquals(1, loader.loadCount());
|
||||
|
||||
final CreateAssetResult createResult = service.createAsset(new CreateAssetRequest(
|
||||
project(projectRoot),
|
||||
"ui/new-atlas",
|
||||
"new_atlas",
|
||||
AssetFamilyCatalog.IMAGE_BANK,
|
||||
OutputFormatCatalog.TILES_INDEXED_V1,
|
||||
OutputCodecCatalog.NONE,
|
||||
true));
|
||||
|
||||
assertEquals(PackerOperationStatus.SUCCESS, createResult.status());
|
||||
assertEquals(1, loader.loadCount());
|
||||
|
||||
final var detailsResult = service.getAssetDetails(new GetAssetDetailsRequest(project(projectRoot), createResult.assetReference()));
|
||||
assertEquals(PackerOperationStatus.SUCCESS, detailsResult.status());
|
||||
assertEquals(1, loader.loadCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void exposesRegisterActionForValidUnregisteredAsset() throws Exception {
|
||||
final Path projectRoot = copyFixture("workspaces/orphan-valid", tempDir.resolve("orphan-actions"));
|
||||
@ -201,6 +227,29 @@ final class FileSystemPackerWorkspaceServiceTest {
|
||||
assertTrue(events.stream().anyMatch(event -> event.kind() == PackerEventKind.ACTION_APPLIED));
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerAssetPatchesLoadedSnapshotWithoutWholeProjectReload() throws Exception {
|
||||
final Path projectRoot = copyFixture("workspaces/orphan-valid", tempDir.resolve("register-no-reload"));
|
||||
final CountingLoader loader = countingLoader();
|
||||
final FileSystemPackerWorkspaceService service = service(ignored -> { }, loader);
|
||||
|
||||
service.listAssets(new ListAssetsRequest(project(projectRoot)));
|
||||
assertEquals(1, loader.loadCount());
|
||||
|
||||
final RegisterAssetResult registerResult = service.registerAsset(new RegisterAssetRequest(
|
||||
project(projectRoot),
|
||||
AssetReference.forRelativeAssetRoot("orphans/ui_sounds")));
|
||||
|
||||
assertEquals(PackerOperationStatus.SUCCESS, registerResult.status());
|
||||
assertEquals(1, loader.loadCount());
|
||||
|
||||
final var actionsResult = service.getAssetActions(new GetAssetActionsRequest(
|
||||
project(projectRoot),
|
||||
registerResult.assetReference()));
|
||||
assertTrue(actionsResult.actions().isEmpty());
|
||||
assertEquals(1, loader.loadCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void blocksRegisterActionForInvalidDeclaration() throws Exception {
|
||||
final Path projectRoot = copyFixture("workspaces/invalid-missing-fields", tempDir.resolve("invalid-actions"));
|
||||
@ -341,27 +390,59 @@ final class FileSystemPackerWorkspaceServiceTest {
|
||||
}
|
||||
|
||||
private FileSystemPackerWorkspaceService service() {
|
||||
return service(ignored -> {
|
||||
});
|
||||
return service(ignored -> { });
|
||||
}
|
||||
|
||||
private FileSystemPackerWorkspaceService service(p.packer.events.PackerEventSink eventSink) {
|
||||
return service(eventSink, countingLoader());
|
||||
}
|
||||
|
||||
private FileSystemPackerWorkspaceService service(
|
||||
p.packer.events.PackerEventSink eventSink,
|
||||
PackerRuntimeSnapshotLoader loader) {
|
||||
final var foundation = new p.packer.services.PackerWorkspaceFoundation();
|
||||
final var parser = new p.packer.services.PackerAssetDeclarationParser();
|
||||
final var runtimeRegistry = new p.packer.services.PackerRuntimeRegistry(new p.packer.services.PackerRuntimeLoader(foundation, parser));
|
||||
final var runtimeRegistry = new p.packer.services.PackerRuntimeRegistry(loader);
|
||||
final var resolver = new p.packer.services.PackerAssetReferenceResolver(foundation.lookup());
|
||||
final var detailsService = new p.packer.services.PackerAssetDetailsService(runtimeRegistry, resolver);
|
||||
final var actionReadService = new p.packer.services.PackerAssetActionReadService(runtimeRegistry, resolver, foundation.lookup());
|
||||
final var runtimePatchService = new p.packer.services.PackerRuntimePatchService(parser);
|
||||
final var writeCoordinator = new p.packer.services.PackerProjectWriteCoordinator();
|
||||
return new FileSystemPackerWorkspaceService(
|
||||
foundation,
|
||||
detailsService,
|
||||
actionReadService,
|
||||
runtimePatchService,
|
||||
runtimeRegistry,
|
||||
writeCoordinator,
|
||||
eventSink);
|
||||
}
|
||||
|
||||
private CountingLoader countingLoader() {
|
||||
final var foundation = new p.packer.services.PackerWorkspaceFoundation();
|
||||
final var parser = new p.packer.services.PackerAssetDeclarationParser();
|
||||
return new CountingLoader(new p.packer.services.PackerRuntimeLoader(foundation, parser));
|
||||
}
|
||||
|
||||
private static final class CountingLoader implements PackerRuntimeSnapshotLoader {
|
||||
private final PackerRuntimeSnapshotLoader delegate;
|
||||
private int loadCount;
|
||||
|
||||
private CountingLoader(PackerRuntimeSnapshotLoader delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized p.packer.models.PackerRuntimeSnapshot load(PackerProjectContext project, long generation) {
|
||||
loadCount += 1;
|
||||
return delegate.load(project, generation);
|
||||
}
|
||||
|
||||
private synchronized int loadCount() {
|
||||
return loadCount;
|
||||
}
|
||||
}
|
||||
|
||||
private Path copyFixture(String relativePath, Path targetRoot) throws Exception {
|
||||
final Path sourceRoot = PackerFixtureLocator.fixtureRoot(relativePath);
|
||||
try (var stream = Files.walk(sourceRoot)) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user