feat: catalog shared contracts (Tasks 1-4)
Task 1: Establish catalog identity and atomic file ownership - CatalogService, CatalogServiceInterface, ModelMetadata DTO - PdoCatalogRepository with atomic operations, savepoint support - FOR UPDATE file locks in ascending order, FOR NO KEY UPDATE for metadata - FK-compatible lock composition documented (spec §6) Task 2: M3 import & indexing engine delivery layer - ImportService, ImportWorker with durable collision handling - SpaceChecker, StreamingHasher, VerifiedCopier, StlValidator - FileEnumerator, LocalFilesystem, PathSafety - Console command wiring via ContainerWiringTest Task 3: Importer integration and repair - LegacyGuessedImportGroupingAdapter isolates first-directory guessing - ImportService uses adapter; ImportWorker uses CatalogServiceInterface - Null owners remain null; DomainException mapped to CATALOG_CONFLICT - SQL failure after registration/path update with caller sentinel rollback Task 4: PostgreSQL contention evidence and bounded acceptance - 6 new PostgresCatalogContractsTest cases with independent sessions - Generated schemas, 250ms lock timeouts, 5s statement timeouts - Ascending file locks, FOR SHARE contention, stale-owner rejection - Rollback/retry, nested transaction survival, root creation contention - Negative controls: replay FOR UPDATE removal (11 failures), metadata FOR UPDATE (4 FK KEY SHARE timeouts) - Fresh Docker PostgreSQL 17 / PHP 8.4.26: 473 tests, 2718 assertions, 10 skips, exit 0 All local: 474 tests, 2347 assertions, 54 skips, foundation/lint clean.
This commit is contained in:
1 parent
6b9fb54b4f
commit
a829d6e8c6
21 files changed
+1903
-61
No files matched your search
@@ -6,12 +6,54 @@ namespace MeshVault\Catalog;
|
||||
|
||||
use MeshVault\Repository\CatalogRepositoryInterface;
|
||||
|
||||
final class CatalogService
|
||||
final class CatalogService implements CatalogServiceInterface
|
||||
{
|
||||
public function __construct(private readonly CatalogRepositoryInterface $catalog)
|
||||
{
|
||||
}
|
||||
|
||||
/** @see CatalogServiceInterface::atomic() for supported lock-order compositions. */
|
||||
public function atomic(callable $operation): mixed
|
||||
{
|
||||
return $this->catalog->atomic($operation);
|
||||
}
|
||||
|
||||
public function modelMetadata(int $modelId): ?array
|
||||
{
|
||||
return $this->catalog->modelMetadata($modelId);
|
||||
}
|
||||
|
||||
public function updateModelMetadata(int $modelId, array $expected, array $replacement): array
|
||||
{
|
||||
return $this->catalog->updateModelMetadata($modelId, $expected, $replacement);
|
||||
}
|
||||
|
||||
public function changeFileStatus(int $fileId, string $expectedStatus, string $targetStatus): array
|
||||
{
|
||||
return $this->catalog->changeFileStatus($fileId, $expectedStatus, $targetStatus);
|
||||
}
|
||||
|
||||
public function file(int $fileId): ?array
|
||||
{
|
||||
return $this->catalog->file($fileId);
|
||||
}
|
||||
|
||||
public function fileByManagedPath(string $managedPath): ?array
|
||||
{
|
||||
return $this->catalog->fileByManagedPath($managedPath);
|
||||
}
|
||||
|
||||
public function registerFile(
|
||||
?int $modelId,
|
||||
string $managedPath,
|
||||
string $hash,
|
||||
int $size,
|
||||
?string $mime = null,
|
||||
string $status = 'Present'
|
||||
): int {
|
||||
return $this->catalog->recordFile($modelId, $managedPath, $hash, $size, $mime, $status);
|
||||
}
|
||||
|
||||
public function createModel(string $folderPath, string $modelName): int
|
||||
{
|
||||
return $this->catalog->atomic(fn (): int => $this->catalog->createModel($folderPath, $modelName));
|
||||
|
||||
Reference in new issue
Block a user