API Reference
Everything exported by DynamicObjects. For usage and worked examples see the manual.
The struct macro
Missing docstring.
Missing docstring for @dynamicstruct. Check Documenter's build log for details.
In-struct property markers
These are not real macros — they are pattern-matched by @dynamicstruct inside a struct body. Outside a struct body they're either no-ops, real macros (e.g. @memo), or undefined. Don't rely on them in arbitrary positions.
| Marker | Effect |
|---|---|
@cached prop = expr | Persist to disk under cache_path. Per-key for indexed properties. |
@cached v"N" prop = expr | Versioned disk cache; bumping N invalidates files without changing inputs. |
@persist prop = expr | Write the in-memory value back to disk on demand (see @persist). |
@lru N prop(idx) = expr | Bound the per-property in-memory dict to N entries (LRU eviction). |
@memo prop = expr | Inside a struct: rewrite call → bracket access. Outside: process-wide function memoize. |
Cache inspection
Real macros — usable inside and outside @dynamicstruct bodies. Inside a body, drop the object prefix and use the bare property name.
DynamicObjects.@cache_status Macro
@cache_status o.prop
@cache_status o.prop[indices...]Return the disk-cache status of a @cached property as a Symbol:
:unstarted— no cache file exists yet.:started— an empty placeholder file exists (previous run may have crashed).:ready— a complete cache file exists and can be deserialized.
Can be used both outside and inside a @dynamicstruct body. Inside a struct definition, omit the object prefix — just use the property name:
# Outside the struct:
@cache_status e.result # :unstarted (before first access)
e.result
@cache_status e.result # :ready
@cache_status e.ci[2] # for indexable properties
# Inside the struct body:
@dynamicstruct struct App
@cached result(key) = expensive(key)
status(key) = @cache_status result[key] # :unstarted, :started, or :ready
endDynamicObjects.@is_cached Macro
@is_cached o.prop
@is_cached o.prop[indices...]Return true if the disk cache for o.prop (or o.prop[indices...]) is :ready, i.e. the cached value can be loaded from disk without recomputation.
Can be used both outside and inside a @dynamicstruct body. Inside a struct definition, omit the object prefix — just use the property name:
# Outside the struct:
@is_cached e.result # false before first access, true afterwards
# Inside the struct body:
@dynamicstruct struct App
@cached result(key) = expensive(key)
summary(key) = if @is_cached result[key]
"cached: $(result[key])"
else
"not yet computed"
end
endDynamicObjects.@cache_path Macro
@cache_path o.prop
@cache_path o.prop[indices...]Return the file path where the disk-cached value of o.prop (or o.prop[indices...]) is (or would be) stored.
@cache_path e.result # e.g. "cache/<hash>/result.sjl"
@cache_path e.ci[2] # "cache/<hash>/2.sjl"DynamicObjects.@clear_cache! Macro
@clear_cache! o.prop
@clear_cache! o.prop[indices...]Clear the disk cache (and in-memory cache) for a @cached property.
Without indices, clears all cached entries for the property (both the in-memory value and all .sjl files for that property on disk). With indices, clears only the specific entry.
@clear_cache! e.result # clear all cached entries for `result`
@clear_cache! e.ci[3] # clear only the ci[3] entryDynamicObjects.@persist Macro
@persist o.prop
@persist o.prop[indices...]Write the in-memory value of o.prop (or the indexed entry o.prop[indices...]) back to its disk cache. Use after mutating a value in place when the property was declared with @cached and the on-disk copy is now stale relative to the in-memory copy.
Functions
DynamicObjects.remake Function
remake(obj; kwargs...)Create a new instance of the same @dynamicstruct type as obj, copying all fixed fields from obj and overriding any specified via keyword arguments.
Keyword arguments that correspond to fixed fields replace those field values in the new instance. Any remaining keyword arguments are forwarded to the constructor as cache pre-population overrides.
Example
@dynamicstruct struct Config
n::Int
scale::Float64
result = scale * sum(rand(n))
end
c = Config(100, 2.0)
c2 = remake(c; n=200) # n=200, scale=2.0, result recomputed fresh
c3 = remake(c; scale=3.0) # n=100, scale=3.0, result recomputed fresh
c4 = remake(c; result=0.0) # n=100, scale=2.0, result pre-set to 0.0DynamicObjects.fetchindex Function
fetchindex(fetch, ip, indices...; kwargs...)Call getindex(ip, indices...; kwargs...) with a custom fetch function.
For IndexableProperty backed by a ThreadsafeDict, getindex spawns a Task for the computation. The fetch callback receives (rv, status) where rv is the Task (still running) or the computed result (done), and status is the substatus object (from __substatus__) or nothing.
Pass force=true to unconditionally recompute: clears both the in-memory cache entry and the on-disk cache file so getindex always spawns a fresh Task.
Example
fetchindex(app.results, key) do rv, status
if rv isa Task
# still computing — status is the progress node
render_progress(status)
else
# done — render result
render(rv)
end
endDynamicObjects.fetchindex! Function
fetchindex!(callback, ip, indices...; fetch=Base.fetch, kwargs...)In-place variant of fetchindex. When callback is nothing, falls through to a plain getindex(ip, indices...; fetch, kwargs...) — useful for sites that opt out of the two-phase fetch dance without changing call shape.
DynamicObjects.getstatus Function
getstatus(ip::IndexableProperty, indices...; kwargs...)Return the status object associated with an in-flight computation for the given key, or nothing if no status exists (computation not started, already finished, or no __substatus__ defined).
Only meaningful for IndexableProperty backed by a ThreadsafeDict.
Cache maintenance
DynamicObjects.entries Function
entries(ip::IndexableProperty)Return a vector of (; key, state, status, value) for all entries in a ThreadsafeDict-backed IndexableProperty. state is one of :running, :failed, :finishing, or :done. value is the cached result (for :done) or the Task (for running/failed/finishing). status is the substatus object or nothing.
DynamicObjects.cached_entries Function
cached_entries(ip::IndexableProperty)Return a vector of (key, value) pairs for completed (non-Task) entries only.
DynamicObjects.clear_all_caches! Function
clear_all_caches!(obj)Clear all @cached properties on a @dynamicstruct instance — both in-memory and on disk. Equivalent to clear_mem_caches! + clear_disk_caches!.
DynamicObjects.clear_mem_caches! Function
clear_mem_caches!(obj)Clear all in-memory memoized property values on a @dynamicstruct instance, leaving disk caches (@cached files) untouched. Every derived property — including child DOs stored as values — will be recomputed on next access.
This is useful after hot-reloading code via Revise: property values computed by old method definitions stay memoized until the process restarts or this function is called.
sourceDynamicObjects.clear_disk_caches! Function
clear_disk_caches!(obj)Delete all on-disk cache files for @cached properties on a @dynamicstruct instance. In-memory values are left intact (they'll be stale until clear_mem_caches! is also called, or until the process restarts).
Cancellation
DynamicObjects.cancel! Function
cancel!(ip::IndexableProperty, indices...; kwargs...)Cancel a running task for the given key on a ThreadsafeDict-backed IndexableProperty. Returns true if a running task was found and interrupted, false otherwise.
DynamicObjects.cancel_all! Function
cancel_all!(ip::IndexableProperty)Cancel all running tasks on a ThreadsafeDict-backed IndexableProperty.
Error handling
DynamicObjects.PropertyComputationError Type
PropertyComputationError <: ExceptionWraps an error that occurred during lazy property computation, adding context about which property failed (property name, type, indices/kwargs). The original exception and backtrace are stored in the cause field.
DynamicObjects.unwrap_error Function
unwrap_error(e)Recursively unwrap TaskFailedException, CompositeException, and PropertyComputationError wrappers to find the root cause exception.
Persistent / bounded collections
DynamicObjects.PersistentSet Type
PersistentSet(path)A thread-safe Set that persists to disk via Serialization. Loads existing data from path on construction, or starts empty if the file doesn't exist.
DynamicObjects.LazyPersistentDict Type
LazyPersistentDict{D<:AbstractDict}(path[, empty_data]; seed!)Threadsafe dict backed by Serialization.serialize/deserialize. The backing file path is resolved lazily via a callable path so the constructor itself is precompile-safe (no mkpath, no file I/O). The on-disk file is loaded on the first operation (double-checked under the lock), and the optional seed!(data) callback runs once after load if the dict is empty. Mutations persist to disk synchronously under the lock.
path may be an AbstractString (fixed path) or a 0-arg function returning a String. Pass an ordered backing dict (e.g. OrderedDict{K,V}()) to preserve insertion order.
DynamicObjects.LRUDict Type
LRUDict{K,V}(maxsize)Plain (non-thread-safe) Dict bounded to maxsize entries via least-recently-used eviction. Used as the per-property in-memory cache for @lru-marked properties on :serial @dynamicstruct instances.
DynamicObjects.ThreadsafeLRUDict Type
ThreadsafeLRUDict{K,V}(maxsize)A ThreadsafeDict variant that bounds its cache to maxsize entries, evicting the least-recently-used keys on insert. Eviction skips keys that have an in-flight Task (i.e. a running computation), so callers awaiting a result never observe its cache slot vanish underneath them. If every slot is pinned by a running task, the dict is allowed to temporarily exceed maxsize.
The (lock, cache, tasks, status) shape matches ThreadsafeDict, so all the generic dispatch on <:AbstractThreadsafeDict (getstatus, cancel!, fetchindex, entries, …) works unchanged.
Pluggable key tracking
For bounding on-disk caches when the full key set isn't known up front.
DynamicObjects.KeyTracker Type
KeyTrackerAbstract type for pluggable accessed-keys persistence strategies. Implement record!(tracker, key) and load_keys(tracker) for custom strategies.
Override key_tracker(o, ::Val{name}) on your object type to select a strategy.
DynamicObjects.SharedFileTracker Type
SharedFileTracker(path)Default strategy: all pods/processes share a single _keys.sjl file. Simple, but not safe for concurrent multi-process writes to NFS.
DynamicObjects.PerPodFileTracker Type
PerPodFileTracker(base_path, pod_id)Per-pod strategy: each pod writes only to its own _keys_{pod_id}.sjl file. load_keys unions all matching files. Safe for NFS multi-pod setups — writes are never concurrent since each pod touches only its own file.
DynamicObjects.NoKeyTracker Type
NoKeyTracker()No-op strategy: never records or loads keys. Use when tracking is unwanted.
sourceDynamicObjects.key_tracker Function
key_tracker(o, ::Val{name}) -> KeyTrackerReturn the KeyTracker to use for property name on object o. Override this method on your type to change the tracking strategy.
# Example: use per-pod files for all indexed properties on MyType
DynamicObjects.key_tracker(o::MyType, ::Val{name}) where {name} =
DynamicObjects.PerPodFileTracker(joinpath(o.cache_path, string(name) * "_keys"), pod_id)DynamicObjects.record! Function
record!(tracker::KeyTracker, key)Record that key was accessed, persisting according to the tracker's strategy.
DynamicObjects.load_keys Function
load_keys(tracker::KeyTracker) -> SetLoad the full set of recorded keys according to the tracker's strategy.
source