Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 42 additions & 16 deletions src/cache.toit
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ Typically, caches are stored in the user's home: \$(HOME)/.cache, but users can

To simplify testing, the environment variable '<app-name>_CACHE_DIR' can be used to
override the cache directory.

This library is thread-safe. The library uses atomic moves to
guarantee that cache entries are always complete. If two processes
try to write the same cache entry then the first one to finish wins
and the second one is ignored.

The cache has no way to update an existing entry. If you want to
update an entry, you must remove it first.
*/

/**
Expand Down Expand Up @@ -77,6 +85,15 @@ class Cache:
key-path := key-path_ key
return file.is-file key-path or file.is-directory key-path

/**
Returns a path to the cache entry with the given $key.

If the cache entry doesn't exist yet, then the returned string
points to a non-existing file.
*/
get-file-path key/string -> string:
return key-path_ key

/**
Variant of $(get key [block]).

Expand Down Expand Up @@ -113,6 +130,15 @@ class Cache:
key-path := get-file-path key block
return file.read-contents key-path

/**
Returns the path to the directory item with the given $key.

If the cache entry doesn't exist yet, then the returned string
points to a non-existing directory.
*/
get-directory-path key/string -> string:
return key-path_ key

/**
Returns the path to the cached directory item with the given $key.

Expand Down Expand Up @@ -221,39 +247,39 @@ interface FileStore:
Calls the given $block with the path as argument.
The temporary directory is deleted after the block returns.
*/
with-tmp-directory [block]
with-tmp-directory [block] -> none

/**
Saves the given $bytes as the content of $key.

If the key already exists, the generated content is dropped.
This can happen if two processes try to access the cache at the same time.
*/
save bytes/ByteArray
save bytes/io.Data -> none

/**
Calls the given $block with a $io.Writer.

The $block must write its chunks to the writer.
The writer is closed after the block returns.
*/
save-via-writer [block]
save-via-writer [block] -> none

/**
Copies the content of $path to the cache under $key.

If the key already exists, the generated content is dropped.
This can happen if two processes try to access the cache at the same time.
*/
copy path/string
copy path/string -> none

/**
Moves the file at $path to the cache under $key.

If the key already exists, the generated content is dropped.
This can happen if two processes try to access the cache at the same time.
*/
move path/string
move path/string -> none

// TODO(florian): add "download" method.
// download url/string --compressed/bool=false --path/string="":
Expand All @@ -275,23 +301,23 @@ interface DirectoryStore:
Calls the given $block with the path as argument.
The temporary directory is deleted after the block returns.
*/
with-tmp-directory [block]
with-tmp-directory [block] -> none

/**
Copies the content of the directory $path to the cache under $key.

If the key already exists, the generated content is dropped.
This can happen if two processes try to access the cache at the same time.
*/
copy path/string
copy path/string -> none

/**
Moves the directory at $path to the cache under $key.

If the key already exists, the generated content is dropped.
This can happen if two processes try to access the cache at the same time.
*/
move path/string
move path/string -> none

// TODO(florian): add "download" method.
// Must be a tar, tar.gz, tgz, or zip.
Expand All @@ -315,7 +341,7 @@ class FileStore_ implements FileStore:
Calls the given $block with the path as argument.
The temporary directory is deleted after the block returns.
*/
with-tmp-directory [block]:
with-tmp-directory [block] -> none:
cache_.with-tmp-directory_ block

/**
Expand All @@ -324,7 +350,7 @@ class FileStore_ implements FileStore:
If the key already exists, the generated content is dropped.
This can happen if two processes try to access the cache at the same time.
*/
save bytes/ByteArray:
save bytes/io.Data -> none:
store_: | file-path/string |
file.write-contents bytes --path=file-path

Expand All @@ -334,7 +360,7 @@ class FileStore_ implements FileStore:
The $block must write its chunks to the writer.
The writer is closed after the block returns.
*/
save-via-writer [block]:
save-via-writer [block] -> none:
store_: | file-path/string |
stream := file.Stream.for-write file-path
try:
Expand All @@ -348,7 +374,7 @@ class FileStore_ implements FileStore:
If the key already exists, the generated content is dropped.
This can happen if two processes try to access the cache at the same time.
*/
copy path/string:
copy path/string -> none:
store_: | file-path/string |
copy-file_ --source=path --target=file-path

Expand All @@ -358,7 +384,7 @@ class FileStore_ implements FileStore:
If the key already exists, the generated content is dropped.
This can happen if two processes try to access the cache at the same time.
*/
move path/string:
move path/string -> none:
if has-stored_: throw "Already saved content for key: $key"
if is-closed_: throw "FileStore is closed"

Expand Down Expand Up @@ -402,7 +428,7 @@ class DirectoryStore_ implements DirectoryStore:
Calls the given $block with the path as argument.
The temporary directory is deleted after the block returns.
*/
with-tmp-directory [block]:
with-tmp-directory [block] -> none:
cache_.with-tmp-directory_ block

/**
Expand All @@ -411,7 +437,7 @@ class DirectoryStore_ implements DirectoryStore:
If the key already exists, the generated content is dropped.
This can happen if two processes try to access the cache at the same time.
*/
copy path/string:
copy path/string -> none:
store_: | dir-path/string |
copy-directory --source=path --target=dir-path

Expand All @@ -421,7 +447,7 @@ class DirectoryStore_ implements DirectoryStore:
If the key already exists, the generated content is dropped.
This can happen if two processes try to access the cache at the same time.
*/
move path/string:
move path/string -> none:
store_: | dir-path/string |
// TODO(florian): we should be able to test whether the rename should succeed.
exception := catch: file.rename path dir-path
Expand Down
10 changes: 10 additions & 0 deletions tests/cache_test.toit
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ test-file-cache:

key2 := "dir/nested/many/levels/key2"
expect-not (c.contains key2)
key2-path := c.get-file-path key2
expect-equals "$cache-dir/$key2" key2-path
expect-not (file.is-file key2-path)

value2 := c.get key2: | store/cache.FileStore |
store.save #[4, 5, 6]
expect-equals value2 #[4, 5, 6]
Expand All @@ -43,6 +47,9 @@ test-file-cache:
throw "Should not be called"
expect-equals value2 #[4, 5, 6]

path = c.get-file-path key2
expect-equals "$cache-dir/$key2" path

path2 := c.get-file-path key2: | store |
throw "Should not be called"
expect-equals path2 "$cache-dir/$key2"
Expand All @@ -56,6 +63,9 @@ test-file-cache:
dir-entries.add entry

key3 := "dir/key3"
path = c.get-directory-path key3
expect-equals "$cache-dir/$key3" path

expect-not (c.contains key3)
value3 := c.get key3: | store/cache.FileStore |
store.with-tmp-directory: | tmp-dir |
Expand Down
Loading