From c67e1fb2c5592fcc29cf294315a0b579b9d5529d Mon Sep 17 00:00:00 2001 From: Johannes Terblanche Date: Wed, 9 Jul 2025 17:14:20 +0200 Subject: [PATCH 1/2] Immutable Folderstore id=>blob pair --- src/DataBlobs/services/BlobStores.jl | 43 +++++++++++++++++++--------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/src/DataBlobs/services/BlobStores.jl b/src/DataBlobs/services/BlobStores.jl index 42eee96f..9474ce63 100644 --- a/src/DataBlobs/services/BlobStores.jl +++ b/src/DataBlobs/services/BlobStores.jl @@ -176,7 +176,10 @@ end function getBlob(store::FolderStore{T}, blobId::UUID) where {T} blobfilename = joinpath(store.folder, string(store.label), string(blobId)) - if isfile(blobfilename) + tombstonefile = blobfilename * ".deleted" + if isfile(tombstonefile) + throw(IdNotFoundError("Blob (deleted)", blobId)) + elseif isfile(blobfilename) open(blobfilename) do f return read(f) end @@ -198,24 +201,28 @@ function addBlob!(store::FolderStore{T}, blobId::UUID, data::T) where {T} end function updateBlob!(store::FolderStore{T}, blobId::UUID, data::T) where {T} - blobfilename = joinpath(store.folder, string(store.label), string(blobId)) - if !isfile(blobfilename) - @warn "Key '$blobId' doesn't exist." - else - open(blobfilename, "w") do f - return write(f, data) - end - return data - end + error("updateBlob! is obsolete as blobsId=>Blob pairs are immutable.") end function deleteBlob!(store::FolderStore{T}, blobId::UUID) where {T} + # Tombstone pattern: instead of deleting the file, create a tombstone marker file blobfilename = joinpath(store.folder, string(store.label), string(blobId)) - if !isfile(blobfilename) + tombstonefile = blobfilename * ".deleted" + if isfile(blobfilename) + # Remove the actual blob file + rm(blobfilename) + # Create a tombstone marker + open(tombstonefile, "w") do f + write(f, "deleted") + end + return 1 + elseif isfile(tombstonefile) + # Already deleted + return 0 + else + # Not found throw(IdNotFoundError("Blob", blobId)) end - rm(blobfilename) - return 1 end function hasBlob(store::FolderStore, blobId::UUID) @@ -225,7 +232,15 @@ end hasBlob(store::FolderStore, entry::Blobentry) = hasBlob(store, entry.blobId) -listBlobs(store::FolderStore) = readdir(store.folder) +function listBlobs(store::FolderStore) + folder = joinpath(store.folder, string(store.label)) + # Parse folder to only include UUIDs automatically excluding tombstone files this way. + blobIds = map(readdir(folder)) do filename + tryparse(UUID, filename) + end + return filter(!isnothing, blobIds) +end + ##============================================================================== ## InMemoryBlobstore ##============================================================================== From ddd9e562ded7f281f93c29958ff0d5a3fd753807 Mon Sep 17 00:00:00 2001 From: Johannes Terblanche <6612981+Affie@users.noreply.github.com> Date: Tue, 15 Jul 2025 20:23:41 +0200 Subject: [PATCH 2/2] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/DataBlobs/services/BlobStores.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/DataBlobs/services/BlobStores.jl b/src/DataBlobs/services/BlobStores.jl index 9474ce63..e1abc971 100644 --- a/src/DataBlobs/services/BlobStores.jl +++ b/src/DataBlobs/services/BlobStores.jl @@ -201,7 +201,7 @@ function addBlob!(store::FolderStore{T}, blobId::UUID, data::T) where {T} end function updateBlob!(store::FolderStore{T}, blobId::UUID, data::T) where {T} - error("updateBlob! is obsolete as blobsId=>Blob pairs are immutable.") + return error("updateBlob! is obsolete as blobsId=>Blob pairs are immutable.") end function deleteBlob!(store::FolderStore{T}, blobId::UUID) where {T} @@ -213,7 +213,7 @@ function deleteBlob!(store::FolderStore{T}, blobId::UUID) where {T} rm(blobfilename) # Create a tombstone marker open(tombstonefile, "w") do f - write(f, "deleted") + return write(f, "deleted") end return 1 elseif isfile(tombstonefile) @@ -236,7 +236,7 @@ function listBlobs(store::FolderStore) folder = joinpath(store.folder, string(store.label)) # Parse folder to only include UUIDs automatically excluding tombstone files this way. blobIds = map(readdir(folder)) do filename - tryparse(UUID, filename) + return tryparse(UUID, filename) end return filter(!isnothing, blobIds) end