blob: 8e2273d570d05135e27a992855db406adc62687b [file] [log] [blame]
// Copyright 2013 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)
// lsong@google.com (Libo Song)
#ifndef PAGESPEED_SYSTEM_SYSTEM_CACHES_H_
#define PAGESPEED_SYSTEM_SYSTEM_CACHES_H_
#include <map>
#include <vector>
#include "pagespeed/kernel/base/basictypes.h"
#include "pagespeed/kernel/base/cache_interface.h"
#include "pagespeed/kernel/base/message_handler.h"
#include "pagespeed/kernel/base/md5_hasher.h"
#include "pagespeed/kernel/base/scoped_ptr.h"
#include "pagespeed/kernel/base/string.h"
#include "pagespeed/kernel/base/string_util.h"
#include "pagespeed/kernel/sharedmem/shared_mem_cache.h"
#include "pagespeed/system/redis_cache.h"
#include "pagespeed/system/system_rewrite_options.h"
namespace net_instaweb {
class AbstractSharedMem;
class AprMemCache;
class NamedLockManager;
class QueuedWorkerPool;
class RewriteDriverFactory;
class ServerContext;
class SlowWorker;
class Statistics;
class SystemCachePath;
// Helps manage setup of cache backends provided by the PSOL library
// (LRU, File, Memcached, and shared memory metadata), as well as named lock
// managers. The expectation is that the RewriteDriverFactory for the server
// will invoke this class's methods in appropriate spots.
//
// It is also expected that the RootInit() method will be called during server
// setup before the server launches any additional processes, and ChildInit()
// will be called on any child process handling requests. If the server
// is single-process, both methods should be called.
//
// Keep in mind, however, that when fork() is involved a process may
// effectively see both calls, in which case the 'ChildInit' call would
// come second and override the previous root status.
class SystemCaches {
public:
// CacheStats prefixes.
static const char kMemcachedAsync[];
static const char kMemcachedBlocking[];
static const char kRedisAsync[];
static const char kRedisBlocking[];
static const char kShmCache[];
static const char kDefaultSharedMemoryPath[];
enum StatFlags {
kDefaultStatFlags = 0,
kGlobalView = 1,
kIncludeMemcached = 2,
kIncludeRedis = 4
};
// Registers all statistics the cache backends may use.
static void InitStats(Statistics* statistics);
// thread_limit is an estimate of number of threads that may access the
// cache at the same time. Does not take ownership of shm_runtime.
SystemCaches(RewriteDriverFactory* factory,
AbstractSharedMem* shm_runtime,
int thread_limit);
// Note that you must call ShutDown() before this is deleted.
~SystemCaches();
bool is_root_process() const { return is_root_process_; }
// Note: RegisterConfig must be called for all relevant configurations
// before calling RootInit()
void RegisterConfig(SystemRewriteOptions* config);
void RootInit();
void ChildInit();
// Tries to block all asynchronous cache activity, causing lookups to
// fail, to help quicker shutdown. Not 100% guaranteed to work, as not
// all backends implement it.
void StopCacheActivity();
// Actually stops some of the work threads, and queues up deferred deletion of
// various objects on the RewriteDriverFactory.
void ShutDown(MessageHandler* message_handler);
// Configures server_context's caches based on its configuration.
void SetupCaches(ServerContext* server_context, bool enable_property_cache);
// Creates & registers a shared memory metadata cache segment with given
// name and size.
//
// Returns whether successful or not, and if not, *error_msg will contain
// an error message. Meant to be called from config parsing.
bool CreateShmMetadataCache(
StringPiece name, int64 size_kb, GoogleString* error_msg);
// Returns, perhaps creating it, an appropriate named manager for this config
// (potentially sharing with others as appropriate).
NamedLockManager* GetLockManager(SystemRewriteOptions* config);
// Print out stats appropriate for the given flags combination.
void PrintCacheStats(StatFlags flags, GoogleString* out);
// For cases where the thread limit isn't known at construction time, call
// set_thread_limit() before calling any other methods.
void set_thread_limit(int thread_limit) { thread_limit_ = thread_limit; }
// Finds a Cache for the file_cache_path in the config. If none exists,
// creates one, using all the other parameters in the SystemRewriteOptions.
// If multiple calls are made to get a file-cache with the same path, but
// with different cleaning parameters, the parameters are merged based
// on these rules:
// 1. An explicitly configured option is selected over a default without
// warning.
// 2. When there are two explicit settings, the higher size is picked,
// but the lower time-interval is picked. A warning is issued to
// the server log, as this situation should be resolved by the server
// administrator.
SystemCachePath* GetCache(SystemRewriteOptions* config);
private:
typedef SharedMemCache<64> MetadataShmCache;
struct MetadataShmCacheInfo {
MetadataShmCacheInfo()
: cache_to_use(NULL), cache_backend(NULL), initialized(false) {}
// Note that the fields may be NULL if e.g. initialization failed.
CacheInterface* cache_to_use; // may be CacheStats or such.
GoogleString segment;
MetadataShmCache* cache_backend;
bool initialized; // This is needed since in some scenarios we may
// not end up as far as calling ->Initialize() before
// we get shutdown.
};
struct ExternalCacheInterfaces {
ExternalCacheInterfaces() : async(NULL), blocking(NULL) {}
CacheInterface* async;
CacheInterface* blocking;
};
// Given a blocking cache, prepares a fully functional ExternalCacheInterfaces
// with both blocking and async versions. Async version is obtained by
// wrapping blocking cache in AsyncCache with given worker pool.
// If pool is NULL, this wrapping is omitted, effictively yielding a blocking
// cache instead of async (this is used for compatibility with
// MemcachedThreads 0 config option). Async version is then wrapped in
// CacheBatcher. The value of batcher_max_parallel_lookups will be used to
// override the batcher's max_parallel_lookups. If you don't want to override
// it, pass in -1.
//
// Each cache is also wrapped in CacheStatistics with given name. All newly
// created wrappers are owned by SystemCaches.
ExternalCacheInterfaces ConstructExternalCacheInterfacesFromBlocking(
CacheInterface* backend, QueuedWorkerPool* pool,
int batcher_max_parallel_lookups, const char* async_stats_name,
const char* blocking_stats_name);
// Constructs external cache interfaces for a configuration. Both blocking
// and (potentially) non-blocking interfaces are constructed, and given
// separate stats. The returned interfaces are owned by SystemCaches, and must
// not be freed by the caller.
//
// The corresponding external cache should be enabled in the config.
ExternalCacheInterfaces NewMemcached(SystemRewriteOptions* config);
ExternalCacheInterfaces NewRedis(SystemRewriteOptions* config);
// Either constructs a new external cache (memcached/redis) based on
// configuration or retrieves it from external_caches_map_ if it was already
// constructed. Note that not all objects are stored in the map, some may be
// created on each individual run (see impl for details).
ExternalCacheInterfaces NewExternalCache(SystemRewriteOptions* config);
// Returns any shared memory metadata cache configured for the given name, or
// NULL.
MetadataShmCacheInfo* LookupShmMetadataCache(const GoogleString& name);
// Returns the shared metadata cache explicitly configured for this config if
// it exists, otherwise return the default one, creating it if necessary.
// Returns NULL if shared memory isn't supported or if the default cache is
// disabled and this server context didn't explicitly configure its own.
MetadataShmCacheInfo* GetShmMetadataCacheOrDefault(
SystemRewriteOptions* config);
// Establishes common cohorts for the property cache.
void SetupPcacheCohorts(ServerContext* server_context,
bool enable_property_cache);
scoped_ptr<SlowWorker> slow_worker_;
RewriteDriverFactory* factory_;
AbstractSharedMem* shared_mem_runtime_;
int thread_limit_;
bool is_root_process_;
bool was_shut_down_;
// File-Caches are expensive. Just allocate one per distinct file-cache path.
// At the moment there is no consistency checking for other parameters. Note
// that the LRUCache is instantiated inside the SystemCachePath, so we get a
// new LRUCache for each distinct file-cache path. Also note that only the
// file-cache path is used as the key in this map. Other parameters changed,
// such as lru cache size or file cache clean interval, are taken from the
// first file-cache found configured to one address.
//
// TODO(jmarantz): Consider instantiating one LRUCache per process.
typedef std::map<GoogleString, SystemCachePath*> PathCacheMap;
PathCacheMap path_cache_map_;
// The QueuedWorkerPool for async cache-gets is shared among all memcached
// connections. We have similar pool for Redis.
// TODO(yeputons): consider reducing to a single pool. Potential problem: if
// both memcached and Redis are enabled and one of them goes down, it blocks
// requests to both servers. Actually, we have that problem already if there
// different vhosts use different external cache servers.
scoped_ptr<QueuedWorkerPool> memcached_pool_;
scoped_ptr<QueuedWorkerPool> redis_pool_;
// Explicit lists of AprMemCache/RedisCache instances are stored individually,
// as they require extra treatment during startup and shutdown.
// TODO(yeputons): consider reducing to a single vector when these classes
// have common base class. Potential problem: users may want to enable
// statistics for only memcached or only Redis (see kIncludeMemcached flag).
std::vector<AprMemCache*> memcache_servers_;
std::vector<RedisCache*> redis_servers_;
// As each external cache object typically holds a TCP connection, we do not
// want to allocate one per vhost (there can be tens of thousands of vhosts).
// So, we create only one object per each external cache configuration and
// store them in a map. See GetExternalCache() for details.
//
// All ExternalCacheInterfaces pairs stored already include (depending on
// options) instances of CacheBatcher, AsyncCache and CacheStats.
typedef std::map<GoogleString, ExternalCacheInterfaces> ExternalCachesMap;
ExternalCachesMap external_caches_map_;
// Map of any shared memory metadata caches we have + their CacheStats
// wrappers. These are named explicitly to make configuration comprehensible.
typedef std::map<GoogleString, MetadataShmCacheInfo*> MetadataShmCacheMap;
// Note that entries here may be NULL in cases of config errors.
MetadataShmCacheMap metadata_shm_caches_;
MD5Hasher cache_hasher_;
bool default_shm_metadata_cache_creation_failed_;
DISALLOW_COPY_AND_ASSIGN(SystemCaches);
};
} // namespace net_instaweb
#endif // PAGESPEED_SYSTEM_SYSTEM_CACHES_H_