A file cache can be created with either the NewDefaultCache()
function to
get a cache with the defaults set, or NewCache()
to get a new cache with
0
values for everything; you will not be able to store items in this cache
until the values are changed; specifically, at a minimum, you should set
the MaxItems
field to be > 0.
Let's start with a basic example; we'll create a basic cache and give it a maximum item size of 128M:
cache := filecache.NewDefaultCache()
cache.MaxSize = 128 * filecache.Megabyte
cache.Start()
The Kilobyte
, Megabyte
, and Gigabyte
constants are provided as a
convience when setting cache sizes.
You can transparently read and cache a file using ReadFile
(and
ReadFileString
); if the file is not in the cache, it will be read
from the file system and returned; the cache will start a background
thread to cache the file. Similarly, the WriterFile
method will
write the file to the specified io.Writer
. For example, you could
create a FileServer
function along the lines of
func FileServer(w http.ResponseWriter, r *http.Request) {
path := r.URL.Path
if len(path) > 1 {
path = path[1:len(path)]
} else {
path = "."
}
err := cache.WriteFile(w, path)
if err == nil {
ServerError(w, r)
} else if err == filecache.ItemIsDirectory {
DirServer(w, r)
}
}
When cache.Start()
is called, a goroutine is launched in the background
that routinely checks the cache for expired items. The delay between
runs is specified as the number of seconds given by cache.Every
("every
cache.Every
seconds, check for expired items"). There are three criteria
used to determine whether an item in the cache should be expired; they are:
- Has the file been modified on disk? (The cache stores the last time of modification at the time of caching, and compares that to the file's current last modification time).
- Has the file been in the cache for longer than the maximum allowed time?
- Is the cache at capacity? When a file is being cached, a check is made to see if the cache is currently filled. If it is, the item that was last accessed the longest ago is expired and the new item takes its place. When loading items asynchronously, this check might miss the fact that the cache will be at capacity; the background scanner performs a check after its regular checks to ensure that the cache is not at capacity.
The background scanner can be disabled by setting cache.Every
to 0; if so,
cache expiration is only done when the cache is at capacity.
Once the cache is no longer needed, a call to cache.Stop()
will close down
the channels and signal the background scanner that it should stop.
The public fields of the FileCache
struct are:
MaxItems int // Maximum number of files to cache
MaxSize int64 // Maximum file size to store
ExpireItem int // Seconds a file should be cached for
Every int // Run an expiration check Every seconds
You can create a new file cache with one of two functions:
NewCache()
: creates a new bare repository that just has the underlying cache structure initialised. The public fields are all set to0
, which is very likely not useful (at a minimum, aMaxItems
of0
means no items can or will be stored in the cache).NewDefaultCache()
returns a new file cache initialised to some basic defaults. The defaults are:
DefaultExpireItem int = 300 // 5 minutes
DefaultMaxSize int64 = 4 * Megabyte
DefaultMaxItems int = 32
DefaultEvery int = 60 // 1 minute
These defaults are public variables, and you may change them to more useful values to your program.
Once the cache has been initialised, it needs to be started using the
Start()
method. This is important for initialising the goroutine responsible
for ensuring the cache remains updated, as well as setting up the asynchronous
caching goroutine. The Active
method returns true if the cache is currently
running. Start()
returns an error
if an error occurs; if one is returned,
the cache should not be used.
The FileCache
struct has several methods to return information about the
cache:
Size()
returns the number of files that are currently in the cache.FileSize()
returns the sum of the file sizes stored in the cache; each item on the cache takes up approximately 32 bytes on top of this as overhead.StoredFiles()
returns a list of strings containing the names of the files currently cached. These are not sorted in any way.InCache(name string)
returns true ifname
is in the cache.
While the cache has several methods available, there are four main functions you will likely use to interact with cache apart from initialisation and shutdown. All three of them provide transparent access to files; if the file is in the cache, it is read from the cache. Otherwise, the file is checked to make sure it is not a directory or uncacheable file, returning an error if this is the case. Finally, a goroutine is launched to cache the file in the background while the file is read and its contents provided directly from the filesystem.
ReadFile(name string) ([]byte, error)
is used to get the contents of file as a byte slice.ReadFileString(name string) (string, error)
is used to get the contents of a file as a string.WriteFile(w io.Writer, name) error
is used to write the contents of the file to theio.Writer
interface given.HttpWriteFile(w http.ResponseWriter, r *http.Request)
will write the contents of the file transparently over an HTTP connect. This should be used when the writer is an HTTP connection and will handle the appropriate HTTP headers.
If you are using the file cache in an HTTP server, you might find the following function useful:
HttpHandler(*FileCache) func(w http.ResponseWriter, r *http.Request)
returns a function that can then be used directly inhttp.HandleFunc
calls.
Most people can now skip to the Shutting Down section.
If you are certain a file has been cached, and you want to access it directly from the cache, you can use these functions:
GetItem(name string) ([]byte, bool)
will retrieve a byte slice containing the contents of the file and a boolean indiciating whether the file was in the cache. If it is not in the cache, the byte slice will be empty and no attempt is made to add the file to the cache.GetItemString(name string) (string, bool)
is the same asGetItem
except that it returns a string in place of the byte slice.WriteItem(w io.Writer, name string) (err error)
is the same asWriteFile
except that no attempt is made to add the file to cache if it is not present.
You can cache files without reading them using the two caching functions:
Cache(name string)
will cache the file in the background. It returns immediately and errors are not reported; you can determine if the item is in the cache with theInCache
method; note that as this is a background cache, the file may not immediately be cached.CacheNow(name string) error
will immediately cache the file and block until it has been cached, or until an error is returned.
The Remove(name string) (bool, error)
method will remove the file named
from the cache. If the file was not in the cache or could not be removed,
it returns false.
Once you are done with the cache, the Stop
method takes care of all the
necessary cleanup.
Take a look at cachesrv for an example of a caching fileserver.
filecache
is released under the ISC license:
Copyright (c) 2012 Kyle Isom <kyle@tyrfingr.is>
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.