All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.druid.client.cache.AbstractRedisCache Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

package org.apache.druid.client.cache;

import org.apache.druid.java.util.common.Pair;
import org.apache.druid.java.util.common.lifecycle.LifecycleStop;
import org.apache.druid.java.util.common.logger.Logger;
import org.apache.druid.java.util.emitter.service.ServiceEmitter;
import org.apache.druid.java.util.emitter.service.ServiceMetricEvent;
import redis.clients.jedis.exceptions.JedisException;

import java.util.Collections;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;

public abstract class AbstractRedisCache implements Cache
{
  private static final Logger log = new Logger(AbstractRedisCache.class);

  private final AtomicLong hitCount = new AtomicLong(0);
  private final AtomicLong missCount = new AtomicLong(0);
  private final AtomicLong timeoutCount = new AtomicLong(0);
  private final AtomicLong errorCount = new AtomicLong(0);

  private final AtomicLong priorRequestCount = new AtomicLong(0);
  // both get、put and getBulk will increase request count by 1
  private final AtomicLong totalRequestCount = new AtomicLong(0);

  private RedisCacheConfig.DurationConfig expiration;

  protected AbstractRedisCache(RedisCacheConfig config)
  {
    this.expiration = config.getExpiration();
  }

  @Override
  public byte[] get(NamedKey key)
  {
    totalRequestCount.incrementAndGet();
    try {
      byte[] bytes = getFromRedis(key.toByteArray());
      if (bytes == null) {
        missCount.incrementAndGet();
        return null;
      } else {
        hitCount.incrementAndGet();
        return bytes;
      }
    }
    catch (JedisException e) {
      if (e.getMessage().contains("Read timed out")) {
        timeoutCount.incrementAndGet();
      } else {
        errorCount.incrementAndGet();
      }
      log.warn(e, "Exception pulling item from cache");
      return null;
    }
  }

  @Override
  public void put(NamedKey key, byte[] value)
  {
    totalRequestCount.incrementAndGet();
    try {
      this.putToRedis(key.toByteArray(), value, this.expiration);
    }
    catch (JedisException e) {
      errorCount.incrementAndGet();
      log.warn(e, "Exception pushing item to cache");
    }
  }

  @Override
  public Map getBulk(Iterable keys)
  {
    totalRequestCount.incrementAndGet();
    try {
      Pair> results = this.mgetFromRedis(keys);

      hitCount.addAndGet(results.rhs.size());
      missCount.addAndGet(results.lhs - results.rhs.size());
      return results.rhs;
    }
    catch (JedisException e) {
      if (e.getMessage().contains("Read timed out")) {
        timeoutCount.incrementAndGet();
      } else {
        errorCount.incrementAndGet();
      }
      log.warn(e, "Exception pulling items from cache");
      return Collections.emptyMap();
    }
  }

  @Override
  public void close(String namespace)
  {
    // no resources to cleanup
  }

  @Override
  @LifecycleStop
  public void close()
  {
    cleanup();
  }

  @Override
  public CacheStats getStats()
  {
    return new CacheStats(
        hitCount.get(),
        missCount.get(),
        0,
        0,
        0,
        timeoutCount.get(),
        errorCount.get()
    );
  }

  @Override
  public boolean isLocal()
  {
    return false;
  }

  @Override
  public void doMonitor(ServiceEmitter emitter)
  {
    final long priorCount = priorRequestCount.get();
    final long totalCount = totalRequestCount.get();
    final ServiceMetricEvent.Builder builder = ServiceMetricEvent.builder();
    emitter.emit(builder.setMetric("query/cache/redis/total/requests", totalCount));
    emitter.emit(builder.setMetric("query/cache/redis/delta/requests", totalCount - priorCount));
    if (!priorRequestCount.compareAndSet(priorCount, totalCount)) {
      log.error("Prior value changed while I was reporting! updating anyways");
      priorRequestCount.set(totalCount);
    }
  }

  protected abstract byte[] getFromRedis(byte[] key);

  protected abstract void putToRedis(byte[] key, byte[] value, RedisCacheConfig.DurationConfig expiration);

  /**
   * The lhs of the returned pair is the count of input keys
   * The rhs of the returned pair is a map holding the values of their corresponding keys
   */
  protected abstract Pair> mgetFromRedis(Iterable keys);

  protected abstract void cleanup();
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy