{"action":"created","comment":{"url":"https://api.github.com/repos/bazaarvoice/ostrich/pulls/comments/26532052","id":26532052,"diff_hunk":"@@ -0,0 +1,405 @@\n+package com.bazaarvoice.ostrich.pool;\n+\n+import com.bazaarvoice.ostrich.ServiceEndPoint;\n+import com.bazaarvoice.ostrich.ServiceFactory;\n+import com.bazaarvoice.ostrich.MultiThreadedServiceFactory;\n+import com.bazaarvoice.ostrich.metrics.Metrics;\n+import com.codahale.metrics.Counter;\n+import com.codahale.metrics.MetricRegistry;\n+import com.codahale.metrics.Timer;\n+import com.google.common.annotations.VisibleForTesting;\n+import com.google.common.collect.Maps;\n+import com.google.common.util.concurrent.ThreadFactoryBuilder;\n+import org.slf4j.Logger;\n+import org.slf4j.LoggerFactory;\n+\n+import java.util.LinkedList;\n+import java.util.List;\n+import java.util.Map;\n+import java.util.concurrent.Executors;\n+import java.util.concurrent.Future;\n+import java.util.concurrent.ScheduledExecutorService;\n+import java.util.concurrent.TimeUnit;\n+\n+import static com.google.common.base.Preconditions.checkArgument;\n+import static com.google.common.base.Preconditions.checkNotNull;\n+import static com.google.common.base.Preconditions.checkState;\n+\n+/**\n+ * A ServiceCache for \"heavy weight\" client instances, i.e. ones that are already thread safe.\n+ * Therefore unlike {@link com.bazaarvoice.ostrich.pool.SingleThreadedClientServiceCache}, we\n+ * can just map EndPoints to a single shared instance of a \"heavy weight\" client.\n+ *\n+ * This applies to third party client libraries for connecting to generic or specialized\n+ * services, i.e. {@code HttpClient}, {@code JestClient}, {@code ElasticSearchClient} etc.\n+ *\n+ * If your client library is multi-thread safe, this ServiceCache should provide better\n+ * performance than the {@link com.bazaarvoice.ostrich.pool.SingleThreadedClientServiceCache}.\n+ * @param <S> the Service type\n+ */\n+public class MultiThreadedClientServiceCache<S> implements ServiceCache<S> {\n+    private static final Logger LOG = LoggerFactory.getLogger(MultiThreadedClientServiceCache.class);\n+    /**\n+     * We want to be able to perform more than 300 checkOuts and checkIns per second.\n+     * Thus we would like those methods to use an un-synchronized / non-blocking Map implementation.\n+     *\n+     * In order to do that we use a Copy-On-Write approach where all modifications\n+     * (new Endpoint, Endpoint goes away, etc) will create a new Map instance.  This is acceptable as\n+     * Endpoint addition / removal events are rare compared to checkIns and checkOuts.\n+     *\n+     * Because we are changing the pointer that is \"_instancesPerEndpoint\", we have to use the\n+     * volatile keyword to force any thread that wants to access the map, to pull it from\n+     * main memory.   There is a performance penalty associated with volatile, but it is\n+     * better than synchronization.\n+     */\n+    private volatile Map<ServiceEndPoint, HeavyServiceHandle<S>> _instancesPerEndpoint = Maps.newHashMap();\n+    private volatile boolean _isClosed;\n+\n+    // timers to report endPoint register & evict, and serviceFactory create & destroy events\n+    private final Metrics.InstanceMetrics _metrics;\n+    private final Timer _registerTimer;\n+    private final Timer _evictionTimer;\n+    private final Counter _serviceCounter;\n+\n+    // factory to deal with service handles\n+    private final ServiceFactory<S> _serviceFactory;\n+\n+    // time to wait before registering the same endpoint already created by other thread\n+    private static final long DUP_REGISTRATION_WINDOW_MILLIS = 1000l; // 1 second\n+\n+    // scheduled cleanup job executor\n+    private final Future<?> _cleanupFuture;\n+    private final ScheduledExecutorService _cleanupExecutor;\n+\n+    // default time in seconds before the scheduled job cleans up the deleted items\n+    private static final int DEFAULT_CLEANUP_DELAY_SECONDS = 15;\n+    private static final int DEFAULT_EVICTION_DELAY_SECONDS = 3 * 60; // 3 minutes\n+    private final int _evictionDelayInSeconds;\n+\n+    /**\n+     * Subclass of {@link com.bazaarvoice.ostrich.pool.ServiceHandle} that add methods & data\n+     * to allow for Eviction and freshness checks.\n+     */\n+    private class HeavyServiceHandle<T extends S> extends BasicServiceHandle<S> {\n+\n+        private final long _sellByDate;\n+        // volatile as we want all threads to make sure they know what this value is","path":"core/src/main/java/com/bazaarvoice/ostrich/pool/MultiThreadedClientServiceCache.java","position":86,"original_position":86,"commit_id":"7718946c0d3c12ea0cae2c63b4342dfa2fe6da92","original_commit_id":"7718946c0d3c12ea0cae2c63b4342dfa2fe6da92","user":{"login":"dump247","id":448969,"avatar_url":"https://avatars.githubusercontent.com/u/448969?v=3","gravatar_id":"","url":"https://api.github.com/users/dump247","html_url":"https://github.com/dump247","followers_url":"https://api.github.com/users/dump247/followers","following_url":"https://api.github.com/users/dump247/following{/other_user}","gists_url":"https://api.github.com/users/dump247/gists{/gist_id}","starred_url":"https://api.github.com/users/dump247/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dump247/subscriptions","organizations_url":"https://api.github.com/users/dump247/orgs","repos_url":"https://api.github.com/users/dump247/repos","events_url":"https://api.github.com/users/dump247/events{/privacy}","received_events_url":"https://api.github.com/users/dump247/received_events","type":"User","site_admin":false},"body":"Agree with Mark. Comment is unnecessary since it doesn't do anything more than describe the volatile keyword.","created_at":"2015-03-16T22:00:49Z","updated_at":"2015-03-16T22:00:49Z","html_url":"https://github.com/bazaarvoice/ostrich/pull/170#discussion_r26532052","pull_request_url":"https://api.github.com/repos/bazaarvoice/ostrich/pulls/170","_links":{"self":{"href":"https://api.github.com/repos/bazaarvoice/ostrich/pulls/comments/26532052"},"html":{"href":"https://github.com/bazaarvoice/ostrich/pull/170#discussion_r26532052"},"pull_request":{"href":"https://api.github.com/repos/bazaarvoice/ostrich/pulls/170"}}},"pull_request":{"url":"https://api.github.com/repos/bazaarvoice/ostrich/pulls/170","id":30979754,"html_url":"https://github.com/bazaarvoice/ostrich/pull/170","diff_url":"https://github.com/bazaarvoice/ostrich/pull/170.diff","patch_url":"https://github.com/bazaarvoice/ostrich/pull/170.patch","issue_url":"https://api.github.com/repos/bazaarvoice/ostrich/issues/170","number":170,"state":"open","locked":false,"title":"Implement MultiThreaded and SingleThreaded service cache","user":{"login":"bivasdas","id":5587198,"avatar_url":"https://avatars.githubusercontent.com/u/5587198?v=3","gravatar_id":"","url":"https://api.github.com/users/bivasdas","html_url":"https://github.com/bivasdas","followers_url":"https://api.github.com/users/bivasdas/followers","following_url":"https://api.github.com/users/bivasdas/following{/other_user}","gists_url":"https://api.github.com/users/bivasdas/gists{/gist_id}","starred_url":"https://api.github.com/users/bivasdas/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bivasdas/subscriptions","organizations_url":"https://api.github.com/users/bivasdas/orgs","repos_url":"https://api.github.com/users/bivasdas/repos","events_url":"https://api.github.com/users/bivasdas/events{/privacy}","received_events_url":"https://api.github.com/users/bivasdas/received_events","type":"User","site_admin":false},"body":"\t  Created SingleThreaded and MultiThreaded service cache\r\n\t  Simplified, we just have a copy-on-write map and a single cleanup thread now\r\n\t  Added marker interface to force a specific type of service factory in multi thread cache\r\n\t  Updated perf-test-suite to use new multi threaded cache\r\n\t  Updated test suite with more metrics, added chaos worked to create chaos while test run\r\n\t  Updatd service pool test to test both cache\r\n\t  Fix all the docs","created_at":"2015-03-11T20:03:25Z","updated_at":"2015-03-16T22:00:49Z","closed_at":null,"merged_at":null,"merge_commit_sha":"53a61f8ffc421bcc13d153ebe70d0c7dab42df39","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/bazaarvoice/ostrich/pulls/170/commits","review_comments_url":"https://api.github.com/repos/bazaarvoice/ostrich/pulls/170/comments","review_comment_url":"https://api.github.com/repos/bazaarvoice/ostrich/pulls/comments{/number}","comments_url":"https://api.github.com/repos/bazaarvoice/ostrich/issues/170/comments","statuses_url":"https://api.github.com/repos/bazaarvoice/ostrich/statuses/7718946c0d3c12ea0cae2c63b4342dfa2fe6da92","head":{"label":"bivasdas:new-service-cache","ref":"new-service-cache","sha":"7718946c0d3c12ea0cae2c63b4342dfa2fe6da92","user":{"login":"bivasdas","id":5587198,"avatar_url":"https://avatars.githubusercontent.com/u/5587198?v=3","gravatar_id":"","url":"https://api.github.com/users/bivasdas","html_url":"https://github.com/bivasdas","followers_url":"https://api.github.com/users/bivasdas/followers","following_url":"https://api.github.com/users/bivasdas/following{/other_user}","gists_url":"https://api.github.com/users/bivasdas/gists{/gist_id}","starred_url":"https://api.github.com/users/bivasdas/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bivasdas/subscriptions","organizations_url":"https://api.github.com/users/bivasdas/orgs","repos_url":"https://api.github.com/users/bivasdas/repos","events_url":"https://api.github.com/users/bivasdas/events{/privacy}","received_events_url":"https://api.github.com/users/bivasdas/received_events","type":"User","site_admin":false},"repo":{"id":25937888,"name":"ostrich","full_name":"bivasdas/ostrich","owner":{"login":"bivasdas","id":5587198,"avatar_url":"https://avatars.githubusercontent.com/u/5587198?v=3","gravatar_id":"","url":"https://api.github.com/users/bivasdas","html_url":"https://github.com/bivasdas","followers_url":"https://api.github.com/users/bivasdas/followers","following_url":"https://api.github.com/users/bivasdas/following{/other_user}","gists_url":"https://api.github.com/users/bivasdas/gists{/gist_id}","starred_url":"https://api.github.com/users/bivasdas/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bivasdas/subscriptions","organizations_url":"https://api.github.com/users/bivasdas/orgs","repos_url":"https://api.github.com/users/bivasdas/repos","events_url":"https://api.github.com/users/bivasdas/events{/privacy}","received_events_url":"https://api.github.com/users/bivasdas/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/bivasdas/ostrich","description":"SOA Library","fork":true,"url":"https://api.github.com/repos/bivasdas/ostrich","forks_url":"https://api.github.com/repos/bivasdas/ostrich/forks","keys_url":"https://api.github.com/repos/bivasdas/ostrich/keys{/key_id}","collaborators_url":"https://api.github.com/repos/bivasdas/ostrich/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/bivasdas/ostrich/teams","hooks_url":"https://api.github.com/repos/bivasdas/ostrich/hooks","issue_events_url":"https://api.github.com/repos/bivasdas/ostrich/issues/events{/number}","events_url":"https://api.github.com/repos/bivasdas/ostrich/events","assignees_url":"https://api.github.com/repos/bivasdas/ostrich/assignees{/user}","branches_url":"https://api.github.com/repos/bivasdas/ostrich/branches{/branch}","tags_url":"https://api.github.com/repos/bivasdas/ostrich/tags","blobs_url":"https://api.github.com/repos/bivasdas/ostrich/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/bivasdas/ostrich/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/bivasdas/ostrich/git/refs{/sha}","trees_url":"https://api.github.com/repos/bivasdas/ostrich/git/trees{/sha}","statuses_url":"https://api.github.com/repos/bivasdas/ostrich/statuses/{sha}","languages_url":"https://api.github.com/repos/bivasdas/ostrich/languages","stargazers_url":"https://api.github.com/repos/bivasdas/ostrich/stargazers","contributors_url":"https://api.github.com/repos/bivasdas/ostrich/contributors","subscribers_url":"https://api.github.com/repos/bivasdas/ostrich/subscribers","subscription_url":"https://api.github.com/repos/bivasdas/ostrich/subscription","commits_url":"https://api.github.com/repos/bivasdas/ostrich/commits{/sha}","git_commits_url":"https://api.github.com/repos/bivasdas/ostrich/git/commits{/sha}","comments_url":"https://api.github.com/repos/bivasdas/ostrich/comments{/number}","issue_comment_url":"https://api.github.com/repos/bivasdas/ostrich/issues/comments{/number}","contents_url":"https://api.github.com/repos/bivasdas/ostrich/contents/{+path}","compare_url":"https://api.github.com/repos/bivasdas/ostrich/compare/{base}...{head}","merges_url":"https://api.github.com/repos/bivasdas/ostrich/merges","archive_url":"https://api.github.com/repos/bivasdas/ostrich/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/bivasdas/ostrich/downloads","issues_url":"https://api.github.com/repos/bivasdas/ostrich/issues{/number}","pulls_url":"https://api.github.com/repos/bivasdas/ostrich/pulls{/number}","milestones_url":"https://api.github.com/repos/bivasdas/ostrich/milestones{/number}","notifications_url":"https://api.github.com/repos/bivasdas/ostrich/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/bivasdas/ostrich/labels{/name}","releases_url":"https://api.github.com/repos/bivasdas/ostrich/releases{/id}","created_at":"2014-10-29T19:35:15Z","updated_at":"2015-02-24T20:14:46Z","pushed_at":"2015-03-16T18:14:08Z","git_url":"git://github.com/bivasdas/ostrich.git","ssh_url":"git@github.com:bivasdas/ostrich.git","clone_url":"https://github.com/bivasdas/ostrich.git","svn_url":"https://github.com/bivasdas/ostrich","homepage":"","size":1831,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"bazaarvoice:master","ref":"master","sha":"a4efa5a313cf9a753b6b9d64f41f1ac591e03783","user":{"login":"bazaarvoice","id":152469,"avatar_url":"https://avatars.githubusercontent.com/u/152469?v=3","gravatar_id":"","url":"https://api.github.com/users/bazaarvoice","html_url":"https://github.com/bazaarvoice","followers_url":"https://api.github.com/users/bazaarvoice/followers","following_url":"https://api.github.com/users/bazaarvoice/following{/other_user}","gists_url":"https://api.github.com/users/bazaarvoice/gists{/gist_id}","starred_url":"https://api.github.com/users/bazaarvoice/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bazaarvoice/subscriptions","organizations_url":"https://api.github.com/users/bazaarvoice/orgs","repos_url":"https://api.github.com/users/bazaarvoice/repos","events_url":"https://api.github.com/users/bazaarvoice/events{/privacy}","received_events_url":"https://api.github.com/users/bazaarvoice/received_events","type":"Organization","site_admin":false},"repo":{"id":4341250,"name":"ostrich","full_name":"bazaarvoice/ostrich","owner":{"login":"bazaarvoice","id":152469,"avatar_url":"https://avatars.githubusercontent.com/u/152469?v=3","gravatar_id":"","url":"https://api.github.com/users/bazaarvoice","html_url":"https://github.com/bazaarvoice","followers_url":"https://api.github.com/users/bazaarvoice/followers","following_url":"https://api.github.com/users/bazaarvoice/following{/other_user}","gists_url":"https://api.github.com/users/bazaarvoice/gists{/gist_id}","starred_url":"https://api.github.com/users/bazaarvoice/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bazaarvoice/subscriptions","organizations_url":"https://api.github.com/users/bazaarvoice/orgs","repos_url":"https://api.github.com/users/bazaarvoice/repos","events_url":"https://api.github.com/users/bazaarvoice/events{/privacy}","received_events_url":"https://api.github.com/users/bazaarvoice/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/bazaarvoice/ostrich","description":"SOA Library","fork":false,"url":"https://api.github.com/repos/bazaarvoice/ostrich","forks_url":"https://api.github.com/repos/bazaarvoice/ostrich/forks","keys_url":"https://api.github.com/repos/bazaarvoice/ostrich/keys{/key_id}","collaborators_url":"https://api.github.com/repos/bazaarvoice/ostrich/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/bazaarvoice/ostrich/teams","hooks_url":"https://api.github.com/repos/bazaarvoice/ostrich/hooks","issue_events_url":"https://api.github.com/repos/bazaarvoice/ostrich/issues/events{/number}","events_url":"https://api.github.com/repos/bazaarvoice/ostrich/events","assignees_url":"https://api.github.com/repos/bazaarvoice/ostrich/assignees{/user}","branches_url":"https://api.github.com/repos/bazaarvoice/ostrich/branches{/branch}","tags_url":"https://api.github.com/repos/bazaarvoice/ostrich/tags","blobs_url":"https://api.github.com/repos/bazaarvoice/ostrich/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/bazaarvoice/ostrich/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/bazaarvoice/ostrich/git/refs{/sha}","trees_url":"https://api.github.com/repos/bazaarvoice/ostrich/git/trees{/sha}","statuses_url":"https://api.github.com/repos/bazaarvoice/ostrich/statuses/{sha}","languages_url":"https://api.github.com/repos/bazaarvoice/ostrich/languages","stargazers_url":"https://api.github.com/repos/bazaarvoice/ostrich/stargazers","contributors_url":"https://api.github.com/repos/bazaarvoice/ostrich/contributors","subscribers_url":"https://api.github.com/repos/bazaarvoice/ostrich/subscribers","subscription_url":"https://api.github.com/repos/bazaarvoice/ostrich/subscription","commits_url":"https://api.github.com/repos/bazaarvoice/ostrich/commits{/sha}","git_commits_url":"https://api.github.com/repos/bazaarvoice/ostrich/git/commits{/sha}","comments_url":"https://api.github.com/repos/bazaarvoice/ostrich/comments{/number}","issue_comment_url":"https://api.github.com/repos/bazaarvoice/ostrich/issues/comments{/number}","contents_url":"https://api.github.com/repos/bazaarvoice/ostrich/contents/{+path}","compare_url":"https://api.github.com/repos/bazaarvoice/ostrich/compare/{base}...{head}","merges_url":"https://api.github.com/repos/bazaarvoice/ostrich/merges","archive_url":"https://api.github.com/repos/bazaarvoice/ostrich/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/bazaarvoice/ostrich/downloads","issues_url":"https://api.github.com/repos/bazaarvoice/ostrich/issues{/number}","pulls_url":"https://api.github.com/repos/bazaarvoice/ostrich/pulls{/number}","milestones_url":"https://api.github.com/repos/bazaarvoice/ostrich/milestones{/number}","notifications_url":"https://api.github.com/repos/bazaarvoice/ostrich/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/bazaarvoice/ostrich/labels{/name}","releases_url":"https://api.github.com/repos/bazaarvoice/ostrich/releases{/id}","created_at":"2012-05-15T22:13:54Z","updated_at":"2015-02-24T19:36:53Z","pushed_at":"2015-02-24T19:36:53Z","git_url":"git://github.com/bazaarvoice/ostrich.git","ssh_url":"git@github.com:bazaarvoice/ostrich.git","clone_url":"https://github.com/bazaarvoice/ostrich.git","svn_url":"https://github.com/bazaarvoice/ostrich","homepage":"","size":2463,"stargazers_count":21,"watchers_count":21,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":19,"mirror_url":null,"open_issues_count":12,"forks":19,"open_issues":12,"watchers":21,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/bazaarvoice/ostrich/pulls/170"},"html":{"href":"https://github.com/bazaarvoice/ostrich/pull/170"},"issue":{"href":"https://api.github.com/repos/bazaarvoice/ostrich/issues/170"},"comments":{"href":"https://api.github.com/repos/bazaarvoice/ostrich/issues/170/comments"},"review_comments":{"href":"https://api.github.com/repos/bazaarvoice/ostrich/pulls/170/comments"},"review_comment":{"href":"https://api.github.com/repos/bazaarvoice/ostrich/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/bazaarvoice/ostrich/pulls/170/commits"},"statuses":{"href":"https://api.github.com/repos/bazaarvoice/ostrich/statuses/7718946c0d3c12ea0cae2c63b4342dfa2fe6da92"}}}}