blob: 2993a1952543b617f9da782eb751b5c60c1232e2 [file] [log] [blame]
/*
* Copyright 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Author: jmarantz@google.com (Joshua Marantz)
#ifndef PAGESPEED_KERNEL_BASE_CACHE_INTERFACE_H_
#define PAGESPEED_KERNEL_BASE_CACHE_INTERFACE_H_
#include <vector>
#include "base/logging.h"
#include "pagespeed/kernel/base/basictypes.h"
#include "pagespeed/kernel/base/string.h"
#include "pagespeed/kernel/base/shared_string.h"
namespace net_instaweb {
// Abstract interface for a cache.
class CacheInterface {
public:
enum KeyState {
kAvailable = 0, // Requested key is available for serving
kNotFound = 1, // Requested key needs to be written
kOverload = 2, // Lookup is discarded due to cache server is overloaded
kNetworkError = 3, // Cache lookup ended up in network error
kTimeout = 4, // Request timeout
};
class Callback {
public:
virtual ~Callback();
SharedString* value() { return &value_; }
// These methods are meant for use of callback subclasses that wrap
// around other callbacks. Normal cache implementations should
// just use CacheInterface::ValidateAndReportResult.
bool DelegatedValidateCandidate(const GoogleString& key, KeyState state) {
return ValidateCandidate(key, state);
}
void DelegatedDone(KeyState state) {
Done(state);
}
protected:
friend class CacheInterface;
// This method exists to let cache clients do application-specific
// validation of cache results. This is important for 2-level caches,
// as with distributed setups it's possible that an entry in the L1 is
// invalid (e.g. an HTTP resource past expiration), while the L2 cache
// has a valid result.
//
// This method will be invoked for all potential cache results,
// (with the value filled in into value()). Returning
// 'false' lets the implementation effectively veto a value as
// expired or invalid for semantic reasons.
//
// Note that implementations may not invoke any cache operations,
// as it may be invoked with locks held.
virtual bool ValidateCandidate(const GoogleString& key,
KeyState state) { return true; }
// This method is called once the cache implementation has found
// a match that was accepted by ValidateCandidate (in which
// case state == kAvailable) or it has failed to do so (state == kNotFound).
//
// Implementations are free to invoke cache operations, as all cache
// locks are guaranteed to be released.
virtual void Done(KeyState state) = 0;
private:
SharedString value_;
};
// Helper class for use with implementations for which IsBlocking is true.
// It simply saves the state, value, and whether Done() has been called.
class SynchronousCallback : public Callback {
public:
SynchronousCallback() { Reset(); }
bool called() const { return called_; }
KeyState state() const { return state_; }
// super.value() is used to get/set the value.
void Reset() {
called_ = false;
state_ = CacheInterface::kNotFound;
SharedString empty;
*value() = empty;
}
virtual void Done(CacheInterface::KeyState state) {
called_ = true;
state_ = state;
}
private:
bool called_;
CacheInterface::KeyState state_;
DISALLOW_COPY_AND_ASSIGN(SynchronousCallback);
};
// Vector of structures used to initiate a MultiGet.
struct KeyCallback {
KeyCallback(const GoogleString& k, Callback* c) : key(k), callback(c) {}
GoogleString key;
Callback* callback;
};
typedef std::vector<KeyCallback> MultiGetRequest;
static const char* KeyStateName(KeyState state);
CacheInterface();
virtual ~CacheInterface();
// Initiates a cache fetch, calling callback->ValidateCandidate()
// and then callback->Done(state) when done.
//
// Note: implementations should normally invoke the callback via
// ValidateAndReportResult, which will combine ValidateCandidate() and
// Done() together properly.
virtual void Get(const GoogleString& key, Callback* callback) = 0;
// Gets multiple keys, calling multiple callbacks. Default implementation
// simply loops over all the keys and calls Get.
//
// MultiGetRequest, declared above, is a vector of structs of keys
// and callbacks.
//
// Ownership of the request is transferred to this function.
virtual void MultiGet(MultiGetRequest* request);
// Puts a value into the cache. The value that is passed in is not modified,
// but the SharedString is passed by non-const pointer because its reference
// count is bumped.
virtual void Put(const GoogleString& key, SharedString* value) = 0;
virtual void Delete(const GoogleString& key) = 0;
// Convenience method to do a Put from a GoogleString* value. The
// bytes will be swapped out of the value and into a temp
// SharedString.
void PutSwappingString(const GoogleString& key, GoogleString* value) {
SharedString shared_string;
shared_string.SwapWithString(value);
Put(key, &shared_string);
}
// The name of this CacheInterface -- used for logging and debugging.
//
// It is strongly recommended that you provide a static GoogleString
// FormatName(...) method for use in formatting the Name() return,
// and in testing, e.g. in third_party/pagespeed/system/system_caches_test.cc.
virtual GoogleString Name() const = 0;
// If this cache is merely a wrapper around a backend that actually
// does all the work, returns that backend cache object. Otherwise
// just returns 'this'. Used for testing.
virtual CacheInterface* Backend();
// Returns true if this cache is guaranteed to call its callbacks before
// returning from Get and MultiGet.
virtual bool IsBlocking() const = 0;
// Returns true if the cache is in a healthy state. Memory and
// file-based caches can simply return 'true'. But for server-based
// caches, it is handy to be able to query to see whether it is in a
// good state. It should be safe to call this frequently -- the
// implementation shouldn't do much more than check a bool flag
// under mutex.
virtual bool IsHealthy() const = 0;
// Stops all cache activity. Further Put/Delete calls will be dropped, and
// MultiGet/Get will call the callback with kNotFound immediately. Note there
// is no Enable(); once the cache is stopped it is stopped forever. This
// function is intended for use during process shutdown.
virtual void ShutDown() = 0;
// To deal with underlying cache systems (e.g. memcached) that
// cannot tolerate arbitrary-sized keys, we use a hash of the
// key and put the key in the value, using the functions in
// key_value_codec.h.
//
// To do this without pointlessly copying the value bytes, we
// use SharedString::Append(). However, that's not thread-safe.
// So when making a cache Asynchronous with AsyncCache, we must
// do the SharedString::Append call in the thread that initiates
// the Put, before queuing a threaded Put.
//
// This method indicates whether a cache implementation requires
// encoding the keys in the value using key_value_codec.
virtual bool MustEncodeKeyInValueOnPut() const { return false; }
// Performs a cache Put, but assumes the key has already been
// encoded into the value with key_value_codec. It is only valid
// to call this when MustEncodeKeyInValueOnPut() returns true.
virtual void PutWithKeyInValue(const GoogleString& key,
SharedString* key_and_value) {
CHECK(false);
}
protected:
// Invokes callback->ValidateCandidate() and callback->Done() as appropriate.
void ValidateAndReportResult(const GoogleString& key, KeyState state,
Callback* callback);
// Helper method to report a NotFound on each MultiGet key. Deletes
// the request.
void ReportMultiGetNotFound(MultiGetRequest* request);
};
} // namespace net_instaweb
#endif // PAGESPEED_KERNEL_BASE_CACHE_INTERFACE_H_