blob: 362185aecd4e81c0063aff67597edc2ebd3ef88b [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)
#ifndef NET_INSTAWEB_UTIL_PUBLIC_PURGE_SET_H_
#define NET_INSTAWEB_UTIL_PUBLIC_PURGE_SET_H_
#include <algorithm>
#include <cstddef>
#include "pagespeed/kernel/base/basictypes.h"
#include "pagespeed/kernel/base/scoped_ptr.h"
#include "pagespeed/kernel/base/string.h"
#include "pagespeed/kernel/base/timer.h"
#include "pagespeed/kernel/cache/lru_cache_base.h"
namespace net_instaweb {
// Maintains a bounded collection of cache-purge records. These can
// be used to validate data read from a cache.
//
// The entire cache can be flushed as of a certain point in time by
// calling UpdateInvalidationTimestampMs.
//
// We bound the cache-purge data to a certain number of bytes. When
// we exceed that, we discard old invalidation records, and bump up
// the global invalidation timestamp to cover the evicted purges.
class PurgeSet {
class InvalidationTimestampHelper;
typedef LRUCacheBase<int64, InvalidationTimestampHelper> Lru;
public:
typedef Lru::Iterator Iterator;
// Used for sanity checking timestamps read from the cache.flush file,
// allowing for small skew and system clock adjustments. Setting this
// to 10 minutes means that we can prevent any cache entries from
// being valid for 10 minutes, disabling whatever functionality is
// dependent on that.
static const int64 kClockSkewAllowanceMs = 10 * Timer::kMinuteMs;
// Initial value used for the global timestamp. This means there
// is no valid timestamp.
static const int64 kInitialTimestampMs = -1;
// The default constructor makes a 1-byte invalidation set. Use
// set_max_size after construction. The default constructor is
// needed for CopyOnWrite.
PurgeSet();
explicit PurgeSet(size_t max_size);
PurgeSet(const PurgeSet& src);
~PurgeSet();
// Call this immediately after construction.
void set_max_size(size_t x) { lru_->set_max_bytes_in_cache(x); }
PurgeSet& operator=(const PurgeSet& src);
// Flushes any item in the cache older than timestamp_ms.
//
//
// Returns false if this request represents an excessive warp back in
// time.
bool UpdateGlobalInvalidationTimestampMs(int64 timestamp_ms);
// Adds a new cache purge record to the set. If we spill over our
// invalidation limit, we will reset the global cache purge-point based
// on the evicted node.
//
// Returns false if this request represents an excessive warp back in
// time.
bool Put(const GoogleString& key, int64 timestamp_ms);
// Merge two invalidation records.
void Merge(const PurgeSet& src);
// Validates a key against specific invalidation records for that
// key, and against the overall invalidation timestamp/
bool IsValid(const GoogleString& key, int64 timestamp_ms) const;
int64 global_invalidation_timestamp_ms() const {
return global_invalidation_timestamp_ms_;
}
bool has_global_invalidation_timestamp_ms() const {
return global_invalidation_timestamp_ms_ != kInitialTimestampMs;
}
Iterator Begin() const { return lru_->Begin(); }
Iterator End() const { return lru_->End(); }
int num_elements() const { return lru_->num_elements(); }
void Clear();
void Swap(PurgeSet* that);
bool Equals(const PurgeSet& that) const;
bool empty() const;
GoogleString ToString() const;
private:
class InvalidationTimestampHelper {
public:
explicit InvalidationTimestampHelper(PurgeSet* purge_set)
: purge_set_(purge_set) {
}
size_t size(int64 value) const {
return sizeof(value);
}
bool Equal(int64 a, int64 b) const {
return a == b;
}
// Update global invalidation timestamp whenever a purge record is
// evicted to guarantee that that resource remains purged.
void EvictNotify(int64 evicted_record_timestamp_ms) {
purge_set_->EvictNotify(evicted_record_timestamp_ms);
}
// Only replace purge records if the new one is newer.
bool ShouldReplace(int64 old_timestamp_ms, int64 new_timestamp_ms) const {
return new_timestamp_ms > old_timestamp_ms;
}
void Swap(InvalidationTimestampHelper* that) {
std::swap(purge_set_, that->purge_set_);
}
private:
PurgeSet* purge_set_;
};
friend class InvalidationTimestampHelper;
void EvictNotify(int64 evicted_record_timestamp_ms);
// Determines whether this timestamp is monotonically increasing from
// previous ones encountered. Small amounts of time-reversal are handled
// by setting them to a recently observed time. Large amounts of
// time-reversal cause false to be returned.
//
// Here several scenarios:
// 1. Time goes backward by a few minutes or less:
// a. On purge requests, force monotonically increasing time.
// b. IsValid: we may report false negatives, disabling PageSpeed
// for a few minutes.
// 2. Time moves backward by a large amount (>10 minutes):
// a. Purge requests: rejected until time is corrected. The only
// sure-fire remedy is to delete all caches, restart memcached
// and pagespeed servers.
// b. IsValid: returns false, disabling PageSpeed until the situation
// is corrected. We view it as unacceptable to bring purged cache
// entries back from the dead.
// TODO(jmarantz): add a statistic that gets bumped from IsValid when
// a far-future expires is detected.
bool SanitizeTimestamp(int64* timestamp_ms);
// Global invalidation timestamp value. Anything with a timestamp older than
// this is considered purged already.
int64 global_invalidation_timestamp_ms_;
// last_invalidation_timestamp_ms is used to keep the data structure invariant
// in the face of time jumping backwards. That can happen if someone resets
// the system-clock or there is a correction due to NTP sync, etc.
int64 last_invalidation_timestamp_ms_;
InvalidationTimestampHelper helper_;
scoped_ptr<Lru> lru_;
// Explicit copy-constructor and assign-operator are provided so
// this class can be used for CopyOnWrite.
};
} // namespace net_instaweb
#endif // NET_INSTAWEB_UTIL_PUBLIC_PURGE_SET_H_