Skip to content

Commit e5826db

Browse files
authored
Merge pull request #228 from JuliaRobotics/feature/4Q19/bigdatastores
In-memory and File-based Bigdata stores
2 parents 21054f3 + dddf617 commit e5826db

22 files changed

+516
-166
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ deps/deps.jl
55
docs/build
66
Manifest.toml
77
dev
8+
test/sandbox.jl

src/BigData.jl

Lines changed: 0 additions & 139 deletions
This file was deleted.

src/BigData/BigData.jl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
include("entities/AbstractDataStore.jl")
2+
include("entities/AbstractBigDataEntries.jl")
3+
include("entities/InMemoryDataStore.jl")
4+
include("entities/FileDataStore.jl")
5+
6+
include("services/AbstractDataStore.jl")
7+
include("services/AbstractBigDataEntries.jl")
8+
include("services/InMemoryDataStore.jl")
9+
include("services/FileDataStore.jl")
10+
11+
export AbstractDataStore
12+
13+
export GeneralBigDataEntry, MongodbBigDataEntry, FileBigDataEntry
14+
export InMemoryDataStore, FileDataStore
15+
16+
export getBigData, addBigData!, updateBigData!, deleteBigData!, listStoreEntries
17+
export copyStore
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
"""
2+
$(TYPEDEF)
3+
GeneralBigDataEntry is a generic multipurpose data entry that creates a unique
4+
reproducible key using userId_robotId_sessionId_variableId_key.
5+
"""
6+
mutable struct GeneralBigDataEntry <: AbstractBigDataEntry
7+
key::Symbol
8+
storeKey::Symbol # Could swap this to string, but using it as an index later, so better as a symbol I believe.
9+
createdTimestamp::DateTime
10+
lastUpdatedTimestamp::DateTime
11+
mimeType::String
12+
end
13+
14+
"""
15+
$(SIGNATURES)
16+
Internal function to generate a unique key for the entry - userId_robotId_sessionId_variable_key.
17+
Simple symbol.
18+
"""
19+
function _uniqueKey(dfg::G, v::V, key::Symbol)::Symbol where {G <: AbstractDFG, V <: AbstractDFGVariable}
20+
key = join(String.([dfg.userId, dfg.robotId, dfg.sessionId, label(v), String(key)]), "_")
21+
return Symbol(key)
22+
end
23+
24+
GeneralBigDataEntry(key::Symbol, storeKey::Symbol;
25+
mimeType::String="application/octet-stream") =
26+
GeneralBigDataEntry(key, storeKey, now(), now(), mimeType)
27+
28+
function GeneralBigDataEntry(dfg::G, var::V, key::Symbol;
29+
mimeType::String="application/octet-stream") where {G <: AbstractDFG, V <: AbstractDFGVariable}
30+
return GeneralBigDataEntry(key, _uniqueKey(dfg, var, key), mimeType=mimeType)
31+
end
32+
33+
"""
34+
$(TYPEDEF)
35+
BigDataEntry in MongoDB.
36+
"""
37+
struct MongodbBigDataEntry <: AbstractBigDataEntry
38+
key::Symbol
39+
oid::NTuple{12, UInt8} #mongodb object id
40+
#maybe other fields such as:
41+
#flags::Bool ready, valid, locked, permissions
42+
#MIMEType::Symbol
43+
end
44+
45+
"""
46+
$(TYPEDEF)
47+
BigDataEntry in a file.
48+
"""
49+
struct FileBigDataEntry <: AbstractBigDataEntry
50+
key::Symbol
51+
filename::String
52+
end
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
2+
"""
3+
$(TYPEDEF)
4+
Superclass of all key-value datastores.
5+
"""
6+
abstract type AbstractDataStore{T}
7+
end

src/BigData/entities/FileDataStore.jl

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
"""
2+
$(TYPEDEF)
3+
Simple file data store with a specified data type and a specified key type.
4+
"""
5+
struct FileDataStore <: AbstractDataStore{UInt8}
6+
folder::String
7+
FileDataStore(folder::String) = begin
8+
if !isdir(folder)
9+
@warn "Folder '$folder' doesn't exist - creating."
10+
mkpath(folder)
11+
end
12+
return new(folder)
13+
end
14+
end
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"""
2+
$(TYPEDEF)
3+
Simple in-memory data store with a specified data type and a specified key type.
4+
"""
5+
struct InMemoryDataStore{T, E <: AbstractBigDataEntry} <: AbstractDataStore{T}
6+
data::Dict{Symbol, T}
7+
entries::Dict{Symbol, E}
8+
end
9+
10+
"""
11+
$(SIGNATURES)
12+
Create an in-memory store using a specific data type.
13+
"""
14+
function InMemoryDataStore{T, E}() where {T, E <: AbstractBigDataEntry}
15+
return InMemoryDataStore{T, E}(Dict{Symbol, T}(), Dict{Symbol, E}())
16+
end
17+
18+
"""
19+
$(SIGNATURES)
20+
Create an in-memory store using binary data (UInt8) as a type.
21+
"""
22+
function InMemoryDataStore()
23+
return InMemoryDataStore{Vector{UInt8}, GeneralBigDataEntry}()
24+
end
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import Base: ==
2+
3+
function ==(a::GeneralBigDataEntry, b::GeneralBigDataEntry)
4+
return a.key == b.key &&
5+
a.storeKey == b.storeKey &&
6+
a.mimeType == b.mimeType &&
7+
Dates.value(a.createdTimestamp - b.createdTimestamp) < 1000 &&
8+
Dates.value(a.lastUpdatedTimestamp - b.lastUpdatedTimestamp) < 1000 #1 second
9+
end
10+
11+
function ==(a::MongodbBigDataEntry, b::MongodbBigDataEntry)
12+
return a.key == b.key && a.oid == b.oid
13+
end
14+
15+
function ==(a::FileBigDataEntry, b::FileBigDataEntry)
16+
return a.key == b.key && a.filename == b.filename
17+
end
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
"""
2+
$(SIGNATURES)
3+
Get the data for the specified entry, returns the data or Nothing.
4+
"""
5+
function getBigData(store::D, entry::E)::Union{Nothing, T} where {T, D <: AbstractDataStore{T}, E <: AbstractBigDataEntry}
6+
error("$(typeof(store)) doesn't override 'getData'.")
7+
end
8+
9+
"""
10+
$(SIGNATURES)
11+
Adds the data to the store with the given entry. The function will warn if the entry already
12+
exists and will overwrite it.
13+
"""
14+
function addBigData!(store::D, entry::E, data::T)::Union{Nothing, T} where {T, D <: AbstractDataStore{T}, E <: AbstractBigDataEntry}
15+
error("$(typeof(store)) doesn't override 'addData!'.")
16+
end
17+
18+
"""
19+
$(SIGNATURES)
20+
Update the data in the store. The function will error and return nothing if
21+
the entry does not exist.
22+
"""
23+
function updateBigData!(store::D, entry::E, data::T)::Union{Nothing, T} where {T, D <: AbstractDataStore{T}, E <: AbstractBigDataEntry}
24+
error("$(typeof(store)) doesn't override 'updateData!'.")
25+
end
26+
27+
"""
28+
$(SIGNATURES)
29+
Delete the data in the store for the given entry. The function will error and return nothing if
30+
the entry does not exist.
31+
"""
32+
function deleteBigData!(store::D, entry::E)::Union{Nothing, T} where {T, D <: AbstractDataStore{T}, E <: AbstractBigDataEntry}
33+
error("$(typeof(store)) doesn't override 'deleteData!'.")
34+
end
35+
36+
"""
37+
$(SIGNATURES)
38+
List all entries in the data store.
39+
"""
40+
function listStoreEntries(store::D)::Vector{E} where {D <: AbstractDataStore, E <: AbstractBigDataEntry}
41+
error("$(typeof(store)) doesn't override 'listEntries'.")
42+
end
43+
44+
"""
45+
$(SIGNATURES)
46+
Copies all the entries from the source into the destination.
47+
Can specify which entries to copy with the `sourceEntries` parameter.
48+
Returns the list of copied entries.
49+
"""
50+
function copyStore(sourceStore::D1, destStore::D2; sourceEntries=listEntries(sourceStore))::Vector{E} where {T, D1 <: AbstractDataStore{T}, D2 <: AbstractDataStore{T}, E <: AbstractBigDataEntry}
51+
# Quick check
52+
destEntries = listEntries(destStore)
53+
typeof(sourceEntries) != typeof(destEntries) && error("Can't copy stores, source has entries of type $(typeof(sourceEntries)), destination has entries of type $(typeof(destEntries)).")
54+
# Same source/destination check
55+
sourceStore == destStore && error("Can't specify same store for source and destination.")
56+
# Otherwise, continue
57+
for sourceEntry in sourceEntries
58+
addData!(destStore, deepcopy(sourceEntry), getData(sourceStore, sourceEntry))
59+
end
60+
return sourceEntries
61+
end

src/BigData/services/FileDataStore.jl

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
searchdir(path,key) = filter(x->occursin(key,x), readdir(path))
2+
filename(store::FileDataStore, entry::E) where E <: AbstractBigDataEntry = "$(store.folder)/$(entry.storeKey).dat"
3+
function readentry(store::FileDataStore, entry::E) where E <: AbstractBigDataEntry
4+
open(filename(store, entry)) do f
5+
return read(f)
6+
end
7+
end
8+
function writeentry(store::FileDataStore, entry::E, data::Vector{UInt8}) where E <: AbstractBigDataEntry
9+
open(filename(store, entry), "w") do f
10+
write(f, data)
11+
end
12+
end
13+
14+
function getBigData(store::FileDataStore, entry::E)::Union{Vector{UInt8}, Nothing} where {E <: AbstractBigDataEntry}
15+
length(searchdir(store.folder, String(entry.storeKey)*".dat")) !=1 && (@warn "Could not find unique file for key '$(entry.storeKey)'."; return nothing)
16+
return readentry(store, entry)
17+
end
18+
19+
function addBigData!(store::FileDataStore, entry::E, data::Vector{UInt8})::Vector{UInt8} where {E <: AbstractBigDataEntry}
20+
length(searchdir(store.folder, String(entry.storeKey)*".dat")) !=0 && @warn "Key '$(entry.storeKey)' already exists, overwriting!."
21+
writeentry(store, entry, data)
22+
# Update timestamp
23+
entry.lastUpdatedTimestamp = now()
24+
return getBigData(store, entry)
25+
end
26+
27+
function updateBigData!(store::FileDataStore, entry::E, data::Vector{UInt8})::Union{Vector{UInt8}, Nothing} where {E <: AbstractBigDataEntry}
28+
length(searchdir(store.folder, String(entry.storeKey)*".dat")) !=1 && (@warn "Could not find unique file for key '$(entry.storeKey)'."; return nothing)
29+
writeentry(store, entry, data)
30+
# Update timestamp
31+
entry.lastUpdatedTimestamp = now()
32+
return getBigData(store, entry)
33+
end
34+
35+
function deleteBigData!(store::FileDataStore, entry::E)::Vector{UInt8} where {E <: AbstractBigDataEntry}
36+
data = getBigData(store, entry)
37+
data == nothing && return nothing
38+
rm(filename(store, entry))
39+
return data
40+
end
41+
42+
# TODO: Manifest file
43+
#
44+
# function listStoreEntries(store::FileDataStore)::Vector{E} where {E <: AbstractBigDataEntry}
45+
# return collect(values(store.entries))
46+
# end

0 commit comments

Comments
 (0)