{"action":"created","comment":{"url":"https://api.github.com/repos/druid-io/druid/pulls/comments/64825750","id":64825750,"diff_hunk":"@@ -0,0 +1,196 @@\n+/*\n+ * Licensed to Metamarkets Group Inc. (Metamarkets) under one\n+ * or more contributor license agreements. See the NOTICE file\n+ * distributed with this work for additional information\n+ * regarding copyright ownership. Metamarkets licenses this file\n+ * to you under the Apache License, Version 2.0 (the\n+ * \"License\"); you may not use this file except in compliance\n+ * with the License. You may obtain a copy of the License at\n+ *\n+ * http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ * Unless required by applicable law or agreed to in writing,\n+ * software distributed under the License is distributed on an\n+ * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n+ * KIND, either express or implied. See the License for the\n+ * specific language governing permissions and limitations\n+ * under the License.\n+ */\n+\n+package io.druid.client.cache;\n+\n+import com.github.benmanes.caffeine.cache.Cache;\n+import com.github.benmanes.caffeine.cache.Caffeine;\n+import com.github.benmanes.caffeine.cache.stats.CacheStats;\n+import com.google.common.collect.ImmutableMap;\n+import com.google.common.collect.Maps;\n+import com.google.common.primitives.Chars;\n+import com.google.common.primitives.Ints;\n+import com.metamx.common.logger.Logger;\n+import com.metamx.emitter.service.ServiceEmitter;\n+import com.metamx.emitter.service.ServiceMetricEvent;\n+import java.nio.ByteBuffer;\n+import java.util.Map;\n+import java.util.OptionalLong;\n+import java.util.concurrent.Executor;\n+import java.util.concurrent.TimeUnit;\n+import java.util.concurrent.atomic.AtomicReference;\n+import javax.annotation.Nullable;\n+import net.jpountz.lz4.LZ4Compressor;\n+import net.jpountz.lz4.LZ4Factory;\n+import net.jpountz.lz4.LZ4FastDecompressor;\n+\n+public class CaffeineCache implements io.druid.client.cache.Cache\n+{\n+  private static final Logger log = new Logger(CaffeineCache.class);\n+  private static final int FIXED_COST = 8; // Minimum cost in \"weight\" per entry;\n+  private final Cache<NamedKey, byte[]> cache;\n+  private final AtomicReference<CacheStats> priorStats = new AtomicReference<>(null);\n+  private final CaffeineCacheConfig config;\n+\n+\n+  public static CaffeineCache create(final CaffeineCacheConfig config)\n+  {\n+    return create(config, config.createExecutor());\n+  }\n+\n+  // Used in testing\n+  public static CaffeineCache create(final CaffeineCacheConfig config, @Nullable final Executor executor)\n+  {\n+    Caffeine<Object, Object> builder = Caffeine.newBuilder().recordStats();\n+    if (config.getExpireAfter() >= 0) {\n+      builder\n+          .expireAfterAccess(config.getExpireAfter(), TimeUnit.MILLISECONDS);\n+    }\n+    if (config.getSizeInBytes() >= 0) {\n+      builder\n+          .maximumWeight(config.getSizeInBytes())\n+          .weigher((NamedKey key, byte[] value) -> value.length\n+                                                   + key.key.length\n+                                                   + key.namespace.length() * Chars.BYTES\n+                                                   + FIXED_COST);\n+    }\n+    if (executor != null) {\n+      builder.executor(executor);\n+    }\n+    return new CaffeineCache(builder.build(), config);\n+  }\n+\n+  public CaffeineCache(final Cache<NamedKey, byte[]> cache, CaffeineCacheConfig config)\n+  {\n+    this.cache = cache;\n+    this.config = config;\n+  }\n+\n+  @Override\n+  public byte[] get(NamedKey key)\n+  {\n+    return deserialize(cache.getIfPresent(key));\n+  }\n+\n+  @Override\n+  public void put(NamedKey key, byte[] value)\n+  {\n+    cache.put(key, serialize(value));\n+  }\n+\n+  @Override\n+  public Map<NamedKey, byte[]> getBulk(Iterable<NamedKey> keys)\n+  {\n+    // The assumption here is that every value is accessed at least once. Materializing here ensures deserialize is only\n+    // called *once* per value.\n+    return ImmutableMap.copyOf(Maps.transformValues(cache.getAllPresent(keys), this::deserialize));\n+  }\n+\n+  // This is completely racy with put. Any values missed should be evicted later anyways. So no worries.\n+  @Override\n+  public void close(String namespace)\n+  {\n+    if (config.isEvictOnClose()) {\n+      cache.asMap().keySet().stream().filter(key -> key.namespace.equals(namespace)).forEach(cache::invalidate);\n+    }\n+  }\n+\n+  @Override\n+  public io.druid.client.cache.CacheStats getStats()\n+  {\n+    final com.github.benmanes.caffeine.cache.stats.CacheStats stats = cache.stats();\n+    final long size = cache\n+        .policy().eviction()\n+        .map(eviction -> eviction.isWeighted() ? eviction.weightedSize() : OptionalLong.empty())","path":"extensions-core/caffeine-cache/src/main/java/io/druid/client/cache/CaffeineCache.java","position":120,"original_position":120,"commit_id":"155729f03d3af8a4d5f7a7a0f90afbe2c9ea7619","original_commit_id":"155729f03d3af8a4d5f7a7a0f90afbe2c9ea7619","user":{"login":"gianm","id":1214075,"avatar_url":"https://avatars.githubusercontent.com/u/1214075?v=3","gravatar_id":"","url":"https://api.github.com/users/gianm","html_url":"https://github.com/gianm","followers_url":"https://api.github.com/users/gianm/followers","following_url":"https://api.github.com/users/gianm/following{/other_user}","gists_url":"https://api.github.com/users/gianm/gists{/gist_id}","starred_url":"https://api.github.com/users/gianm/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gianm/subscriptions","organizations_url":"https://api.github.com/users/gianm/orgs","repos_url":"https://api.github.com/users/gianm/repos","events_url":"https://api.github.com/users/gianm/events{/privacy}","received_events_url":"https://api.github.com/users/gianm/received_events","type":"User","site_admin":false},"body":"wtf java8 what's wrong with you\r\n\r\nok fine","created_at":"2016-05-26T21:39:55Z","updated_at":"2016-05-26T21:39:55Z","html_url":"https://github.com/druid-io/druid/pull/3028#discussion_r64825750","pull_request_url":"https://api.github.com/repos/druid-io/druid/pulls/3028","_links":{"self":{"href":"https://api.github.com/repos/druid-io/druid/pulls/comments/64825750"},"html":{"href":"https://github.com/druid-io/druid/pull/3028#discussion_r64825750"},"pull_request":{"href":"https://api.github.com/repos/druid-io/druid/pulls/3028"}}},"pull_request":{"url":"https://api.github.com/repos/druid-io/druid/pulls/3028","id":71618510,"html_url":"https://github.com/druid-io/druid/pull/3028","diff_url":"https://github.com/druid-io/druid/pull/3028.diff","patch_url":"https://github.com/druid-io/druid/pull/3028.patch","issue_url":"https://api.github.com/repos/druid-io/druid/issues/3028","number":3028,"state":"open","locked":false,"title":"Caffeine cache extension","user":{"login":"drcrallen","id":8213081,"avatar_url":"https://avatars.githubusercontent.com/u/8213081?v=3","gravatar_id":"","url":"https://api.github.com/users/drcrallen","html_url":"https://github.com/drcrallen","followers_url":"https://api.github.com/users/drcrallen/followers","following_url":"https://api.github.com/users/drcrallen/following{/other_user}","gists_url":"https://api.github.com/users/drcrallen/gists{/gist_id}","starred_url":"https://api.github.com/users/drcrallen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/drcrallen/subscriptions","organizations_url":"https://api.github.com/users/drcrallen/orgs","repos_url":"https://api.github.com/users/drcrallen/repos","events_url":"https://api.github.com/users/drcrallen/events{/privacy}","received_events_url":"https://api.github.com/users/drcrallen/received_events","type":"User","site_admin":false},"body":"A new local cache implementation using [Caffeine](https://github.com/ben-manes/caffeine) as the cache.\r\n\r\n![caffeinehot](https://cloud.githubusercontent.com/assets/8213081/15588813/8dce1828-2344-11e6-8c18-0c3ea84acc12.png)\r\n\r\nWe currently use this in production as l1 of a hybrid cache and memcached as the l2. The graph above shows the node mean TopN query time for production servers with and without using this cache in l1 (caffeine = false implies straight-to-memcached).\r\n\r\nThis extension *requires* java 8 to run, and therefore has a special profile that activates in the druid.io parent pom only when java8 is detected. This detection will eventually have to be improved to account for java9+\r\n\r\nI have not yet figured out a good way to get the distribution to switch for jdk7 vs 8.\r\n\r\nThis is a contribution of https://github.com/metamx/druid-cache-caffeine","created_at":"2016-05-26T20:23:51Z","updated_at":"2016-05-26T21:39:55Z","closed_at":null,"merged_at":null,"merge_commit_sha":"154fd35050f76293423f789ecef68e37f450c330","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/druid-io/druid/pulls/3028/commits","review_comments_url":"https://api.github.com/repos/druid-io/druid/pulls/3028/comments","review_comment_url":"https://api.github.com/repos/druid-io/druid/pulls/comments{/number}","comments_url":"https://api.github.com/repos/druid-io/druid/issues/3028/comments","statuses_url":"https://api.github.com/repos/druid-io/druid/statuses/155729f03d3af8a4d5f7a7a0f90afbe2c9ea7619","head":{"label":"metamx:addCaffeineCache","ref":"addCaffeineCache","sha":"155729f03d3af8a4d5f7a7a0f90afbe2c9ea7619","user":{"login":"metamx","id":299329,"avatar_url":"https://avatars.githubusercontent.com/u/299329?v=3","gravatar_id":"","url":"https://api.github.com/users/metamx","html_url":"https://github.com/metamx","followers_url":"https://api.github.com/users/metamx/followers","following_url":"https://api.github.com/users/metamx/following{/other_user}","gists_url":"https://api.github.com/users/metamx/gists{/gist_id}","starred_url":"https://api.github.com/users/metamx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/metamx/subscriptions","organizations_url":"https://api.github.com/users/metamx/orgs","repos_url":"https://api.github.com/users/metamx/repos","events_url":"https://api.github.com/users/metamx/events{/privacy}","received_events_url":"https://api.github.com/users/metamx/received_events","type":"Organization","site_admin":false},"repo":{"id":28248872,"name":"druid","full_name":"metamx/druid","owner":{"login":"metamx","id":299329,"avatar_url":"https://avatars.githubusercontent.com/u/299329?v=3","gravatar_id":"","url":"https://api.github.com/users/metamx","html_url":"https://github.com/metamx","followers_url":"https://api.github.com/users/metamx/followers","following_url":"https://api.github.com/users/metamx/following{/other_user}","gists_url":"https://api.github.com/users/metamx/gists{/gist_id}","starred_url":"https://api.github.com/users/metamx/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/metamx/subscriptions","organizations_url":"https://api.github.com/users/metamx/orgs","repos_url":"https://api.github.com/users/metamx/repos","events_url":"https://api.github.com/users/metamx/events{/privacy}","received_events_url":"https://api.github.com/users/metamx/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/metamx/druid","description":"Real²time Exploratory Analytics on Large Datasets","fork":true,"url":"https://api.github.com/repos/metamx/druid","forks_url":"https://api.github.com/repos/metamx/druid/forks","keys_url":"https://api.github.com/repos/metamx/druid/keys{/key_id}","collaborators_url":"https://api.github.com/repos/metamx/druid/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/metamx/druid/teams","hooks_url":"https://api.github.com/repos/metamx/druid/hooks","issue_events_url":"https://api.github.com/repos/metamx/druid/issues/events{/number}","events_url":"https://api.github.com/repos/metamx/druid/events","assignees_url":"https://api.github.com/repos/metamx/druid/assignees{/user}","branches_url":"https://api.github.com/repos/metamx/druid/branches{/branch}","tags_url":"https://api.github.com/repos/metamx/druid/tags","blobs_url":"https://api.github.com/repos/metamx/druid/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/metamx/druid/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/metamx/druid/git/refs{/sha}","trees_url":"https://api.github.com/repos/metamx/druid/git/trees{/sha}","statuses_url":"https://api.github.com/repos/metamx/druid/statuses/{sha}","languages_url":"https://api.github.com/repos/metamx/druid/languages","stargazers_url":"https://api.github.com/repos/metamx/druid/stargazers","contributors_url":"https://api.github.com/repos/metamx/druid/contributors","subscribers_url":"https://api.github.com/repos/metamx/druid/subscribers","subscription_url":"https://api.github.com/repos/metamx/druid/subscription","commits_url":"https://api.github.com/repos/metamx/druid/commits{/sha}","git_commits_url":"https://api.github.com/repos/metamx/druid/git/commits{/sha}","comments_url":"https://api.github.com/repos/metamx/druid/comments{/number}","issue_comment_url":"https://api.github.com/repos/metamx/druid/issues/comments{/number}","contents_url":"https://api.github.com/repos/metamx/druid/contents/{+path}","compare_url":"https://api.github.com/repos/metamx/druid/compare/{base}...{head}","merges_url":"https://api.github.com/repos/metamx/druid/merges","archive_url":"https://api.github.com/repos/metamx/druid/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/metamx/druid/downloads","issues_url":"https://api.github.com/repos/metamx/druid/issues{/number}","pulls_url":"https://api.github.com/repos/metamx/druid/pulls{/number}","milestones_url":"https://api.github.com/repos/metamx/druid/milestones{/number}","notifications_url":"https://api.github.com/repos/metamx/druid/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/metamx/druid/labels{/name}","releases_url":"https://api.github.com/repos/metamx/druid/releases{/id}","deployments_url":"https://api.github.com/repos/metamx/druid/deployments","created_at":"2014-12-19T22:30:25Z","updated_at":"2016-05-11T13:31:13Z","pushed_at":"2016-05-26T20:19:21Z","git_url":"git://github.com/metamx/druid.git","ssh_url":"git@github.com:metamx/druid.git","clone_url":"https://github.com/metamx/druid.git","svn_url":"https://github.com/metamx/druid","homepage":"http://druid.io","size":114105,"stargazers_count":64,"watchers_count":64,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":20,"mirror_url":null,"open_issues_count":1,"forks":20,"open_issues":1,"watchers":64,"default_branch":"master"}},"base":{"label":"druid-io:master","ref":"master","sha":"847501a93984cfd3fb7c99af5a7b94f1045a6e80","user":{"login":"druid-io","id":4203474,"avatar_url":"https://avatars.githubusercontent.com/u/4203474?v=3","gravatar_id":"","url":"https://api.github.com/users/druid-io","html_url":"https://github.com/druid-io","followers_url":"https://api.github.com/users/druid-io/followers","following_url":"https://api.github.com/users/druid-io/following{/other_user}","gists_url":"https://api.github.com/users/druid-io/gists{/gist_id}","starred_url":"https://api.github.com/users/druid-io/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/druid-io/subscriptions","organizations_url":"https://api.github.com/users/druid-io/orgs","repos_url":"https://api.github.com/users/druid-io/repos","events_url":"https://api.github.com/users/druid-io/events{/privacy}","received_events_url":"https://api.github.com/users/druid-io/received_events","type":"Organization","site_admin":false},"repo":{"id":6358188,"name":"druid","full_name":"druid-io/druid","owner":{"login":"druid-io","id":4203474,"avatar_url":"https://avatars.githubusercontent.com/u/4203474?v=3","gravatar_id":"","url":"https://api.github.com/users/druid-io","html_url":"https://github.com/druid-io","followers_url":"https://api.github.com/users/druid-io/followers","following_url":"https://api.github.com/users/druid-io/following{/other_user}","gists_url":"https://api.github.com/users/druid-io/gists{/gist_id}","starred_url":"https://api.github.com/users/druid-io/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/druid-io/subscriptions","organizations_url":"https://api.github.com/users/druid-io/orgs","repos_url":"https://api.github.com/users/druid-io/repos","events_url":"https://api.github.com/users/druid-io/events{/privacy}","received_events_url":"https://api.github.com/users/druid-io/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/druid-io/druid","description":"Column oriented distributed data store ideal for powering interactive applications","fork":false,"url":"https://api.github.com/repos/druid-io/druid","forks_url":"https://api.github.com/repos/druid-io/druid/forks","keys_url":"https://api.github.com/repos/druid-io/druid/keys{/key_id}","collaborators_url":"https://api.github.com/repos/druid-io/druid/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/druid-io/druid/teams","hooks_url":"https://api.github.com/repos/druid-io/druid/hooks","issue_events_url":"https://api.github.com/repos/druid-io/druid/issues/events{/number}","events_url":"https://api.github.com/repos/druid-io/druid/events","assignees_url":"https://api.github.com/repos/druid-io/druid/assignees{/user}","branches_url":"https://api.github.com/repos/druid-io/druid/branches{/branch}","tags_url":"https://api.github.com/repos/druid-io/druid/tags","blobs_url":"https://api.github.com/repos/druid-io/druid/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/druid-io/druid/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/druid-io/druid/git/refs{/sha}","trees_url":"https://api.github.com/repos/druid-io/druid/git/trees{/sha}","statuses_url":"https://api.github.com/repos/druid-io/druid/statuses/{sha}","languages_url":"https://api.github.com/repos/druid-io/druid/languages","stargazers_url":"https://api.github.com/repos/druid-io/druid/stargazers","contributors_url":"https://api.github.com/repos/druid-io/druid/contributors","subscribers_url":"https://api.github.com/repos/druid-io/druid/subscribers","subscription_url":"https://api.github.com/repos/druid-io/druid/subscription","commits_url":"https://api.github.com/repos/druid-io/druid/commits{/sha}","git_commits_url":"https://api.github.com/repos/druid-io/druid/git/commits{/sha}","comments_url":"https://api.github.com/repos/druid-io/druid/comments{/number}","issue_comment_url":"https://api.github.com/repos/druid-io/druid/issues/comments{/number}","contents_url":"https://api.github.com/repos/druid-io/druid/contents/{+path}","compare_url":"https://api.github.com/repos/druid-io/druid/compare/{base}...{head}","merges_url":"https://api.github.com/repos/druid-io/druid/merges","archive_url":"https://api.github.com/repos/druid-io/druid/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/druid-io/druid/downloads","issues_url":"https://api.github.com/repos/druid-io/druid/issues{/number}","pulls_url":"https://api.github.com/repos/druid-io/druid/pulls{/number}","milestones_url":"https://api.github.com/repos/druid-io/druid/milestones{/number}","notifications_url":"https://api.github.com/repos/druid-io/druid/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/druid-io/druid/labels{/name}","releases_url":"https://api.github.com/repos/druid-io/druid/releases{/id}","deployments_url":"https://api.github.com/repos/druid-io/druid/deployments","created_at":"2012-10-23T19:08:07Z","updated_at":"2016-05-26T11:29:42Z","pushed_at":"2016-05-26T21:38:40Z","git_url":"git://github.com/druid-io/druid.git","ssh_url":"git@github.com:druid-io/druid.git","clone_url":"https://github.com/druid-io/druid.git","svn_url":"https://github.com/druid-io/druid","homepage":"http://druid.io","size":130602,"stargazers_count":3272,"watchers_count":3272,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":728,"mirror_url":null,"open_issues_count":443,"forks":728,"open_issues":443,"watchers":3272,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/druid-io/druid/pulls/3028"},"html":{"href":"https://github.com/druid-io/druid/pull/3028"},"issue":{"href":"https://api.github.com/repos/druid-io/druid/issues/3028"},"comments":{"href":"https://api.github.com/repos/druid-io/druid/issues/3028/comments"},"review_comments":{"href":"https://api.github.com/repos/druid-io/druid/pulls/3028/comments"},"review_comment":{"href":"https://api.github.com/repos/druid-io/druid/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/druid-io/druid/pulls/3028/commits"},"statuses":{"href":"https://api.github.com/repos/druid-io/druid/statuses/155729f03d3af8a4d5f7a7a0f90afbe2c9ea7619"}}}}
{"actor":{"display_login":"gianm"}}