Beispiel #1
0
  /**
   * Revert a set of keys to the empty state. Will loop on this several times just in case the
   * memcache write fails - we don't want to leave the cache in a nasty state.
   */
  public void empty(Iterable<Key> keys) {
    Map<Key, Object> updates = new HashMap<>();

    for (Key key : keys) if (cacheControl.getExpirySeconds(key) != null) updates.put(key, null);

    this.memcacheWithRetry.putAll(updates);
  }
Beispiel #2
0
  /**
   * Gets the Buckets for the specified keys. A bucket is built around an IdentifiableValue so you
   * can putAll() them without the risk of overwriting other threads' changes. Buckets also hide the
   * underlying details of storage for negative, empty, and uncacheable results.
   *
   * <p>Note that worst case (a cold cache), obtaining each bucket might require three memcache
   * requests: a getIdentifiable() which returns null, a put(EMPTY), and another getIdentifiable().
   * Since there is no batch getIdentifiable(), this is *per key*.
   *
   * <p>When keys are uncacheable (per CacheControl) or the memcache is down, you will still get an
   * empty bucket back. The bucket will have null IdentifiableValue so we can identify it as
   * uncacheable.
   *
   * @return the buckets requested. Buckets will never be null. You will always get a bucket for
   *     every key.
   */
  public Map<Key, Bucket> getAll(Iterable<Key> keys) {
    Map<Key, Bucket> result = new HashMap<>();

    // Sort out the ones that are uncacheable
    Set<Key> potentials = new HashSet<>();

    for (Key key : keys) {
      if (cacheControl.getExpirySeconds(key) == null) result.put(key, new Bucket(key));
      else potentials.add(key);
    }

    Map<Key, IdentifiableValue> ivs;
    try {
      ivs = this.memcache.getIdentifiables(potentials);
    } catch (Exception ex) {
      // This should really only be a problem if the serialization format for an Entity changes,
      // or someone put a badly-serializing object in the cache underneath us.
      log.log(Level.WARNING, "Error obtaining cache for " + potentials, ex);
      ivs = new HashMap<>();
    }

    // Figure out cold cache values
    Map<Key, Object> cold = new HashMap<>();
    for (Key key : potentials) if (ivs.get(key) == null) cold.put(key, null);

    if (!cold.isEmpty()) {
      // The cache is cold for those values, so start them out with nulls that we can make an IV for
      this.memcache.putAll(cold);

      try {
        Map<Key, IdentifiableValue> ivs2 = this.memcache.getIdentifiables(cold.keySet());
        ivs.putAll(ivs2);
      } catch (Exception ex) {
        // At this point we should just not worry about it, the ivs will be null and uncacheable
      }
    }

    // Now create the remaining buckets
    for (Key key : keys) {
      // iv might still be null, which is ok - that means uncacheable
      IdentifiableValue iv = ivs.get(key);
      Bucket buck = (iv == null) ? new Bucket(key) : new Bucket(key, iv);
      result.put(key, buck);

      if (buck.isEmpty()) this.stats.recordMiss(buck.getKey());
      else this.stats.recordHit(buck.getKey());
    }

    return result;
  }
Beispiel #3
0
  /**
   * Put buckets in the cache, checking for cacheability and collisions.
   *
   * @return the set of keys that were *successfully* put without collision
   */
  private Set<Key> cachePutIfUntouched(Iterable<Bucket> buckets) {
    Map<Key, CasValues> payload = new HashMap<>();

    for (Bucket buck : buckets) {
      if (!buck.isCacheable()) continue;

      Integer expirySeconds = cacheControl.getExpirySeconds(buck.getKey());
      if (expirySeconds == null) continue;

      Expiration expiration = expirySeconds == 0 ? null : Expiration.byDeltaSeconds(expirySeconds);

      payload.put(buck.getKey(), new CasValues(buck.iv, buck.getNextToStore(), expiration));
    }

    return this.memcache.putIfUntouched(payload);
  }