Skip to content

Commit e666a88

Browse files
committed
More DFGv1 checked and Blobstore updates
1 parent 8ae0b9b commit e666a88

File tree

9 files changed

+183
-77
lines changed

9 files changed

+183
-77
lines changed

src/DataBlobs/entities/BlobStores.jl

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,31 @@
1+
"""
2+
AbstractBlobstore{T}
3+
4+
Abstract supertype for all blobstore implementations.
5+
6+
# Usage
7+
8+
Subtypes of `AbstractBlobstore{T}` must implement the required interface for blob storage and retrieval, such as:
9+
10+
- `add!(store, blobId, blob)`: Add a new blob to the store.
11+
- `get(store, blobId)`: Retrieve a blob by its ID.
12+
- `list(store)`: List all blob IDs in the store.
13+
14+
The parameter `T` represents the type of blobs stored (e.g., `Vector{UInt8}` or a custom `Blob` type).
15+
16+
See concrete implementations for details.
17+
18+
Design Notes
19+
- `blobId` is not considered unique across blobstores with different labels only within a single blobstore.
20+
- We cannot guarantee that `blobId` is unique across different blobstores with the same label and this is up to the end user.
21+
- Within a single blobstore `addBlob!` will fail if there is a UUID collision.
22+
- TODO: We should consider using uuid7 for `blobId`s (requires jl v1.12).
23+
- `Blobstrores`are identified by a `label::Symbol`, which allows for multiple blobstores to coexist in the same system.
24+
25+
TODO: If we want to make the `blobId`=>Blob pair immutable:
26+
- We can use the tombstone pattern to mark a blob as deleted. See FolderStore in PR#TODO.
27+
28+
Design goal: all `Blobstore`s with the same `label` can contain the same `blobId`=>`Blob` pair and the blobs should be identical since they are immutable.
29+
30+
"""
131
abstract type AbstractBlobstore{T} end

src/DataBlobs/services/BlobEntry.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ function addBlobentry!(var::VariableDFG, entry::Blobentry)
168168
return entry
169169
end
170170

171-
function addBlobentry!(dfg::AbstractDFG, vLbl::Symbol, entry::Blobentry;)
171+
function addBlobentry!(dfg::AbstractDFG, vLbl::Symbol, entry::Blobentry)
172172
return addBlobentry!(getVariable(dfg, vLbl), entry)
173173
end
174174

@@ -238,7 +238,7 @@ end
238238
"""
239239
$SIGNATURES
240240
241-
Does a blob entry (element) exist with `blobLabel`.
241+
Does a blob entry exist with `blobLabel`.
242242
"""
243243
hasBlobentry(var::AbstractDFGVariable, blobLabel::Symbol) = haskey(var.dataDict, blobLabel)
244244

src/DataBlobs/services/BlobStores.jl

Lines changed: 34 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ $(METHODLIST)
1313
function getBlob end
1414

1515
"""
16-
Adds a blob to the blob store or dfg with the given entry.
16+
Adds a blob to the blob store or dfg with the blobId.
1717
1818
Related
1919
[`addBlobentry!`](@ref)
@@ -81,34 +81,11 @@ end
8181
##==============================================================================
8282
## AbstractBlobstore derived CRUD for Blob
8383
##==============================================================================
84-
#TODO looking in all the blobstores does not make sense since since there is a chance that the blobId is not unique across blobstores.
85-
# using the cached blobstore is the right way to go here.
84+
#TODO maybe we should generalize and move the cached blobstore to DFG.
8685
function getBlob(dfg::AbstractDFG, entry::Blobentry)
87-
stores = getBlobstores(dfg)
88-
storekeys = collect(keys(stores))
89-
# first check the saved blobstore and then fall back to the rest
90-
fidx = findfirst(==(entry.blobstore), storekeys)
91-
if !isnothing(fidx)
92-
skey = storekeys[fidx]
93-
popat!(storekeys, fidx)
94-
pushfirst!(storekeys, skey)
95-
end
96-
for k in storekeys
97-
store = stores[k]
98-
try
99-
blob = getBlob(store, entry)
100-
return blob
101-
catch err
102-
if !(err isa KeyError)
103-
throw(err)
104-
end
105-
end
106-
end
107-
throw(
108-
KeyError(
109-
"could not find $(entry.label), uuid $(entry.blobId) in any of the listed blobstores:\n $([s->getLabel(s) for (s,v) in stores]))",
110-
),
111-
)
86+
storeLabel = entry.blobstore
87+
store = getBlobstore(dfg, storeLabel)
88+
return getBlob(store, entry.blobId)
11289
end
11390

11491
function getBlob(store::AbstractBlobstore, entry::Blobentry)
@@ -204,19 +181,18 @@ function getBlob(store::FolderStore{T}, blobId::UUID) where {T}
204181
return read(f)
205182
end
206183
else
207-
throw(KeyError("Could not find file '$(blobfilename)'."))
184+
throw(IdNotFoundError("Blob", blobId))
208185
end
209186
end
210187

211188
function addBlob!(store::FolderStore{T}, blobId::UUID, data::T) where {T}
212189
blobfilename = joinpath(store.folder, string(store.label), string(blobId))
213190
if isfile(blobfilename)
214-
throw(KeyError("Key '$blobId' blob already exists."))
191+
throw(IdExistsError("Blob", blobId))
215192
else
216193
open(blobfilename, "w") do f
217194
return write(f, data)
218195
end
219-
# return data
220196
return blobId
221197
end
222198
end
@@ -235,11 +211,13 @@ end
235211

236212
function deleteBlob!(store::FolderStore{T}, blobId::UUID) where {T}
237213
blobfilename = joinpath(store.folder, string(store.label), string(blobId))
214+
if !isfile(blobfilename)
215+
throw(IdNotFoundError("Blob", blobId))
216+
end
238217
rm(blobfilename)
239218
return 1
240219
end
241220

242-
#hasBlob or existsBlob?
243221
function hasBlob(store::FolderStore, blobId::UUID)
244222
blobfilename = joinpath(store.folder, string(store.label), string(blobId))
245223
return isfile(blobfilename)
@@ -265,12 +243,15 @@ function InMemoryBlobstore(storeKey::Symbol = :default_inmemory_store)
265243
end
266244

267245
function getBlob(store::InMemoryBlobstore, blobId::UUID)
246+
if !haskey(store.blobs, blobId)
247+
throw(IdNotFoundError("Blob", blobId))
248+
end
268249
return store.blobs[blobId]
269250
end
270251

271252
function addBlob!(store::InMemoryBlobstore{T}, blobId::UUID, data::T) where {T}
272253
if haskey(store.blobs, blobId)
273-
error("Key '$blobId' blob already exists.")
254+
throw(IdExistsError("Blob", blobId))
274255
end
275256
store.blobs[blobId] = data
276257
return blobId
@@ -284,6 +265,9 @@ function updateBlob!(store::InMemoryBlobstore{T}, blobId::UUID, data::T) where {
284265
end
285266

286267
function deleteBlob!(store::InMemoryBlobstore, blobId::UUID)
268+
if !haskey(store.blobs, blobId)
269+
throw(IdNotFoundError("Blob", blobId))
270+
end
287271
pop!(store.blobs, blobId)
288272
return 1
289273
end
@@ -319,25 +303,28 @@ end
319303

320304
function getBlob(store::LinkStore, blobId::UUID)
321305
fname = get(store.cache, blobId, nothing)
306+
if isnothing(fname)
307+
throw(IdNotFoundError("Blob", blobId))
308+
end
322309
return read(fname)
323310
end
324311

325312
function addBlob!(store::LinkStore, entry::Blobentry, linkfile::String)
326-
return addBlob!(store, entry.blobId, nothing, linkfile::String)
313+
return addBlob!(store, entry.blobId, linkfile)
327314
end
328315

329-
function addBlob!(store::LinkStore, blobId::UUID, blob::Any, linkfile::String)
316+
function addBlob!(store::LinkStore, blobId::UUID, linkfile::String)
330317
if haskey(store.cache, blobId)
331-
error("blobId $blobId already exists in the store")
318+
throw(IdExistsError("Blob", blobId))
332319
end
333320
push!(store.cache, blobId => linkfile)
334321
open(store.csvfile, "a") do f
335322
return println(f, blobId, ",", linkfile)
336323
end
337-
return getBlob(store, blobId)
324+
return blobId
338325
end
339326

340-
function deleteBlob!(store::LinkStore, args...)
327+
function deleteBlob!(store::LinkStore)
341328
return error("deleteDataBlob(::LinkStore) not supported")
342329
end
343330

@@ -404,12 +391,15 @@ Tables.rows(store::RowBlobstore) = values(store.blobs)
404391

405392
##
406393
function getBlob(store::RowBlobstore, blobId::UUID)
394+
if !haskey(store.blobs, blobId)
395+
throw(IdNotFoundError("Blob", blobId))
396+
end
407397
return getfield(store.blobs[blobId], :blob)
408398
end
409399

410400
function addBlob!(store::RowBlobstore{T}, blobId::UUID, blob::T) where {T}
411401
if haskey(store.blobs, blobId)
412-
error("Key '$blobId' blob already exists.")
402+
throw(IdExistsError("Blob", blobId))
413403
end
414404
store.blobs[blobId] = RowBlob(blobId, blob)
415405
return blobId
@@ -423,7 +413,10 @@ function updateBlob!(store::RowBlobstore{T}, blobId::UUID, blob::T) where {T}
423413
end
424414

425415
function deleteBlob!(store::RowBlobstore, blobId::UUID)
426-
getfield(pop!(store.blobs, blobId), :blob)
416+
if !haskey(store.blobs, blobId)
417+
throw(IdNotFoundError("Blob", blobId))
418+
end
419+
pop!(store.blobs, blobId)
427420
return 1
428421
end
429422

@@ -469,3 +462,4 @@ if false
469462
Tables.rowtable(sstore)
470463
end
471464
##
465+
##

src/DistributedFactorGraphs.jl

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,28 @@ using InteractiveUtils: subtypes
6060
# v1 name, signiture, return, and error checked
6161
export getFactor, getBlobentry, getGraphBlobentry
6262

63+
export addVariable!, addFactor!, addBlobentry!
64+
65+
export deleteVariable!
66+
6367
# v1 name, signiture, and return
6468

6569
# v1 name only
6670
export getVariable, getBlob, addBlob!
6771

72+
export hasVariable, hasFactor
73+
74+
# getBlob TODO do we want all of them easy portable vs convenience?
75+
# getBlob(::AbstractBlobstore, ::UUID)
76+
# getBlob(::AbstractBlobstore, ::Blobentry)
77+
# getBlob(::AbstractDFG, ::Blobentry)
78+
79+
# TODO get,add,delete|Blob still needs immutability discussion. but errors checked, tests needs updating though.
80+
81+
# TODO not yet implemented in DFG
82+
# addAgentBlobentry!
83+
# addGraphBlobentry!
84+
6885
##
6986
const DFG = DistributedFactorGraphs
7087
export DFG
@@ -121,9 +138,7 @@ export InMemoryDFGTypes, LocalDFG
121138

122139
# AbstractDFG Interface
123140
export exists,
124-
addVariable!,
125141
addVariables!,
126-
addFactor!,
127142
addFactors!,
128143
mergeVariable!,
129144
mergeFactor!,

src/GraphsDFG/GraphsDFG.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ import ...DistributedFactorGraphs:
2424
getAddHistory,
2525
addFactor!,
2626
getSolverParams,
27-
exists,
27+
hasVariable,
28+
hasFactor,
2829
isVariable,
2930
isFactor,
3031
mergeVariable!,

src/GraphsDFG/services/GraphsDFG.jl

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,14 @@ function getDFGMetadata(fg::GraphsDFG)
66
return metaprops
77
end
88

9-
function exists(
10-
dfg::GraphsDFG{P, V, F},
11-
node::V,
12-
) where {P <: AbstractParams, V <: AbstractDFGVariable, F <: AbstractDFGFactor}
13-
return haskey(dfg.g.variables, node.label)
9+
function hasVariable(dfg::GraphsDFG, label::Symbol)
10+
return haskey(dfg.g.variables, label)
1411
end
1512

16-
function exists(
17-
dfg::GraphsDFG{P, V, F},
18-
node::F,
19-
) where {P <: AbstractParams, V <: AbstractDFGVariable, F <: AbstractDFGFactor}
20-
return haskey(dfg.g.factors, node.label)
13+
function hasFactor(dfg::GraphsDFG, label::Symbol)
14+
return haskey(dfg.g.factors, label)
2115
end
2216

23-
exists(dfg::GraphsDFG, nId::Symbol) = haskey(dfg.g.labels, nId)
24-
25-
exists(dfg::GraphsDFG, node::DFGNode) = exists(dfg, node.label)
26-
2717
function isVariable(
2818
dfg::GraphsDFG{P, V, F},
2919
sym::Symbol,
@@ -109,7 +99,7 @@ function addFactor!(
10999
# @assert FactorGraphs.addFactor!(dfg.g, getVariableOrder(factor), factor)
110100
variableLabels = Symbol[factor._variableOrderSymbols...]
111101
for vlabel in variableLabels
112-
!exists(dfg, vlabel) && throw(LabelNotFoundError("Variable", vlabel))
102+
!hasVariable(dfg, vlabel) && throw(LabelNotFoundError("Variable", vlabel))
113103
end
114104
@assert FactorGraphs.addFactor!(dfg.g, variableLabels, factor)
115105
return factor
@@ -290,7 +280,7 @@ function listNeighbors(dfg::GraphsDFG, node::DFGNode; solvable::Int = 0)
290280
end
291281

292282
function listNeighbors(dfg::GraphsDFG, label::Symbol; solvable::Int = 0)
293-
if !exists(dfg, label)
283+
if !(hasVariable(dfg, label) || hasFactor(dfg, label))
294284
throw(LabelNotFoundError(label))
295285
end
296286

@@ -468,7 +458,8 @@ function findShortestPathDijkstra(
468458
dfg
469459
end
470460

471-
if !exists(dfg_, from) || !exists(dfg_, to)
461+
if !(asVariable(dfg_, from) || hasFactor(dfg_, from)) ||
462+
!(asVariable(dfg_, to) || hasFactor(dfg_, to))
472463
# assume filters excluded either `to` or `from` and hence no shortest path
473464
return Symbol[]
474465
end

src/errors.jl

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,44 @@ function Base.showerror(io::IO, ex::LabelExistsError)
4343
)
4444
end
4545

46+
"""
47+
IdNotFoundError(Id, available)
48+
49+
Error thrown when a requested Id is not found.
50+
"""
51+
struct IdNotFoundError <: Exception
52+
name::String
53+
Id::UUID
54+
available::Vector{UUID}
55+
end
56+
57+
IdNotFoundError(name::String, Id::UUID) = IdNotFoundError(name, Id, UUID[])
58+
IdNotFoundError(Id::UUID) = IdNotFoundError("Node", Id, UUID[])
59+
60+
function Base.showerror(io::IO, ex::IdNotFoundError)
61+
print(io, "IdNotFoundError: ", ex.name, " Id '", ex.Id, "' not found.")
62+
if !isempty(ex.available)
63+
println(io, " Available Ids:")
64+
show(io, ex.available)
65+
end
66+
end
67+
68+
"""
69+
IdExistsError(Id)
70+
71+
Error thrown when attempting to add an Id that already exists in the collection.
72+
"""
73+
struct IdExistsError <: Exception
74+
name::String
75+
Id::UUID
76+
end
77+
78+
IdExistsError(Id::UUID) = IdExistsError("Node", Id)
79+
80+
function Base.showerror(io::IO, ex::IdExistsError)
81+
return print(io, "IdExistsError: ", ex.name, " Id '", ex.Id, "' already exists.")
82+
end
83+
4684
"""
4785
SerializationError(msg)
4886

0 commit comments

Comments
 (0)