| /* |
| * Copyright 2012 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_SYSTEM_APR_MEM_CACHE_H_ |
| #define PAGESPEED_SYSTEM_APR_MEM_CACHE_H_ |
| |
| #include <cstddef> |
| #include <vector> |
| |
| #include "pagespeed/kernel/base/atomic_bool.h" |
| #include "pagespeed/kernel/base/basictypes.h" |
| #include "pagespeed/kernel/base/string.h" |
| #include "pagespeed/kernel/base/string_util.h" |
| #include "pagespeed/kernel/base/timer.h" |
| #include "pagespeed/kernel/cache/cache_interface.h" |
| |
| struct apr_memcache2_t; |
| struct apr_memcache2_server_t; |
| struct apr_pool_t; |
| |
| namespace net_instaweb { |
| |
| class Hasher; |
| class MessageHandler; |
| class SharedString; |
| class Statistics; |
| class UpDownCounter; |
| class Variable; |
| |
| // Interface to memcached via the apr_memcache2*, as documented in |
| // http://apr.apache.org/docs/apr-util/1.4/group___a_p_r___util___m_c.html. |
| // |
| // While this class derives from CacheInterface, it is a blocking |
| // implementation, suitable for instantiating underneath an AsyncCache. |
| class AprMemCache : public CacheInterface { |
| public: |
| // Experimentally it seems large values larger than 1M bytes result in |
| // a failure, e.g. from load-tests: |
| // [Fri Jul 20 10:29:34 2012] [error] [mod_pagespeed 0.10.0.0-1699 @1522] |
| // AprMemCache::Put error: Internal error on key |
| // http://example.com/image.jpg, value-size 1393146 |
| // External to this class, we use a fallback cache (in Apache a FileCache) to |
| // handle too-large requests. This is managed by class FallbackCache in |
| // ../util. |
| static const size_t kValueSizeThreshold = 1 * 1000 * 1000; |
| |
| // Amount of time after a burst of errors to retry memcached operations. |
| static const int64 kHealthCheckpointIntervalMs = 30 * Timer::kSecondMs; |
| |
| // Maximum number of errors tolerated within kHealthCheckpointIntervalMs, |
| // after which AprMemCache will declare itself unhealthy for |
| // kHealthCheckpointIntervalMs. |
| static const int64 kMaxErrorBurst = 4; |
| |
| // servers is a comma-separated list of host[:port] where port defaults |
| // to 11211, the memcached default. |
| // |
| // thread_limit is used to provide apr_memcache2_server_create with |
| // a hard maximum number of client connections to open. |
| AprMemCache(const StringPiece& servers, int thread_limit, Hasher* hasher, |
| Statistics* statistics, Timer* timer, MessageHandler* handler); |
| ~AprMemCache(); |
| |
| static void InitStats(Statistics* statistics); |
| |
| const GoogleString& server_spec() const { return server_spec_; } |
| |
| // As mentioned above, Get and MultiGet are blocking in this implementation. |
| virtual void Get(const GoogleString& key, Callback* callback); |
| virtual void Put(const GoogleString& key, SharedString* value); |
| virtual void Delete(const GoogleString& key); |
| virtual void MultiGet(MultiGetRequest* request); |
| |
| // Connects to the server, returning whether the connection was |
| // successful or not. |
| bool Connect(); |
| |
| bool valid_server_spec() const { return valid_server_spec_; } |
| |
| // Get detailed status in a string, returning false if the server |
| // failed to return status. |
| bool GetStatus(GoogleString* status_string); |
| |
| static GoogleString FormatName() { return "AprMemCache"; } |
| virtual GoogleString Name() const { return FormatName(); } |
| |
| virtual bool IsBlocking() const { return true; } |
| |
| // Records in statistics that a system error occurred, helping it detect |
| // when it's unhealthy if they are too frequent. |
| void RecordError(); |
| |
| // Determines whether memcached is healthy enough to attempt another |
| // operation. Note that even though there may be multiple shards, |
| // some of which are healthy and some not, we don't currently track |
| // errors on a per-shard basis, so we effectively declare all the |
| // memcached instances unhealthy if any of them are. |
| virtual bool IsHealthy() const; |
| |
| // Close down the connection to the memcached servers. |
| virtual void ShutDown(); |
| |
| virtual bool MustEncodeKeyInValueOnPut() const { return true; } |
| virtual void PutWithKeyInValue(const GoogleString& key, |
| SharedString* key_and_value); |
| |
| // Sets the I/O timeout in microseconds. This should be called at |
| // setup time and not while there are operations in flight. |
| void set_timeout_us(int timeout_us); |
| |
| private: |
| void DecodeValueMatchingKeyAndCallCallback( |
| const GoogleString& key, const char* data, size_t data_len, |
| const char* calling_method, Callback* callback); |
| |
| // Puts a value that's already encoded with the key into the cache, without |
| // checking health first. This is meant to be called from Put and |
| // PutWithKeyInValue, which will do the health check. |
| void PutHelper(const GoogleString& key, SharedString* key_and_value); |
| |
| StringVector hosts_; |
| std::vector<int> ports_; |
| GoogleString server_spec_; |
| bool valid_server_spec_; |
| int thread_limit_; |
| int timeout_us_; |
| apr_pool_t* pool_; |
| apr_memcache2_t* memcached_; |
| std::vector<apr_memcache2_server_t*> servers_; |
| Hasher* hasher_; |
| Timer* timer_; |
| AtomicBool shutdown_; |
| |
| Variable* timeouts_; |
| UpDownCounter* last_error_checkpoint_ms_; |
| UpDownCounter* error_burst_size_; |
| |
| bool is_machine_local_; |
| MessageHandler* message_handler_; |
| |
| // When memcached is killed, we will generate errors for every cache |
| // operation. To bound the amount of logging we do, we keep track |
| // of the last time when we issued a log message for an APR failure. |
| // We use a Statistic here for this so that it's shared across |
| // Apache processes. |
| // |
| // Note that we have some messages indicating a potential functional issue on |
| // (e.g. key collision) and a variety of places where we print messages |
| // because the Apr routine failed. We are grouping together Apr failures |
| // for Get, Put, Delete, and MultiGet. We might at some point wish to |
| // track the last time we sent a message for each of those. |
| Variable* last_apr_error_; |
| |
| DISALLOW_COPY_AND_ASSIGN(AprMemCache); |
| }; |
| |
| } // namespace net_instaweb |
| |
| #endif // PAGESPEED_SYSTEM_APR_MEM_CACHE_H_ |