{"action":"created","comment":{"url":"https://api.github.com/repos/OrchardCMS/Orchard/pulls/comments/37699321","id":37699321,"diff_hunk":"@@ -0,0 +1,204 @@\n+using System;\n+using System.Data;\n+using System.Linq;\n+using System.Threading.Tasks;\n+using Autofac;\n+using Orchard.Data;\n+using Orchard.Environment;\n+using Orchard.Exceptions;\n+using Orchard.Logging;\n+using Orchard.Services;\n+using Orchard.Tasks.Locking.Records;\n+\n+namespace Orchard.Tasks.Locking.Services {\n+\n+    public class DistributedLockService : Component, IDistributedLockService {\n+        private readonly IMachineNameProvider _machineNameProvider;\n+        private readonly ILifetimeScope _lifetimeScope;\n+        private readonly IClock _clock;\n+        private readonly object _transactionManagerLock = new object();\n+        private readonly IThreadProvider _threadProvider;\n+\n+        public DistributedLockService(IMachineNameProvider machineNameProvider, IThreadProvider threadProvider, ILifetimeScope lifetimeScope, IClock clock) {\n+            _machineNameProvider = machineNameProvider;\n+            _lifetimeScope = lifetimeScope;\n+            _clock = clock;\n+            _threadProvider = threadProvider;\n+        }\n+\n+        public bool TryAcquireLockForMachine(string name, TimeSpan maxValidFor, TimeSpan? timeout, out DistributedLock @lock) {\n+            return TryAcquireLock(name, maxValidFor, timeout, GetMachineName(), null, out @lock);\n+        }\n+\n+        public DistributedLock AcquireLockForMachine(string name, TimeSpan maxValidFor, TimeSpan? timeout) {\n+            return AcquireLock(name, maxValidFor, timeout, GetMachineName(), null);\n+        }\n+\n+        public bool TryAcquireLockForThread(string name, TimeSpan maxValidFor, TimeSpan? timeout, out DistributedLock @lock) {\n+            return TryAcquireLock(name, maxValidFor, timeout, GetMachineName(), GetThreadId(), out @lock);\n+        }\n+\n+        public DistributedLock AcquireLockForThread(string name, TimeSpan maxValidFor, TimeSpan? timeout) {\n+            return AcquireLock(name, maxValidFor, timeout, GetMachineName(), GetThreadId());\n+        }\n+\n+        public void ReleaseLock(DistributedLock @lock) {\n+            lock (_transactionManagerLock) {\n+                var childLifetimeScope = CreateChildLifetimeScope(@lock.Name);\n+\n+                try {\n+                    var repository = childLifetimeScope.Resolve<IRepository<DistributedLockRecord>>();\n+                    var transactionManager = childLifetimeScope.Resolve<ITransactionManager>();\n+                    transactionManager.RequireNew(IsolationLevel.ReadCommitted);\n+                    var lockId = Int32.Parse(@lock.Id);\n+                    var record = repository.Get(lockId);\n+\n+                    if (record == null)\n+                        throw new ObjectDisposedException(\"@lock\", \"No lock record could be found for the specified lock to be released.\");\n+\n+                    if (record.Count <= 0)\n+                        throw new ObjectDisposedException(\"@lock\", \"The specified lock has already been released.\");\n+\n+                    record.Count--;\n+                }\n+                catch (Exception ex) {\n+                    if (ex.IsFatal()) throw;\n+                    Logger.Error(ex, \"An non-fatal error occurred while trying to dispose a distributed lock with name '{0}' and ID {1}.\", @lock.Name, @lock.Id);\n+                }\n+                finally {\n+                    childLifetimeScope.Dispose();\n+                }\n+            }\n+        }\n+\n+        private bool TryAcquireLock(string name, TimeSpan maxValidFor, TimeSpan? timeout, string machineName, int? threadId, out DistributedLock @lock) {\n+            @lock = AcquireLockInternal(name, maxValidFor, machineName, threadId, timeout.GetValueOrDefault());\n+            if (@lock != null)\n+                return true;\n+\n+            Logger.Debug(\"Could not acquire a lock named '{0}'.\", name);\n+            return false;\n+        }\n+\n+        private DistributedLock AcquireLock(string name, TimeSpan maxValidFor, TimeSpan? timeout, string machineName, int? threadId) {\n+            var @lock = AcquireLockInternal(name, maxValidFor, machineName, threadId, timeout);\n+            if (@lock != null)\n+                return @lock;\n+\n+            throw new TimeoutException(\"Could not acquire a lock within the specified amount of time.\");\n+        }\n+\n+        private DistributedLock AcquireLockInternal(string name, TimeSpan maxValidFor, string machineName, int? threadId, TimeSpan? timeout = null) {\n+            try {\n+                DistributedLockRecord record = null;\n+                var acquired = Poll(() => (record = AcquireLockRecord(name, maxValidFor, machineName, threadId)) != null, timeout);\n+\n+                if (acquired) {\n+                    Logger.Debug(\"Successfully acquired a lock named '{0}'.\", name);\n+                    return threadId != null\n+                        ? DistributedLock.ForThread(this, name, machineName, threadId.Value, record.Id.ToString())\n+                        : DistributedLock.ForMachine(this, name, machineName, record.Id.ToString());\n+                }\n+            }\n+            catch (Exception ex) {\n+                Logger.Error(ex, \"Error while trying to acquire a lock named '{0}'.\", name);\n+                throw;\n+            }\n+\n+            return null;\n+        }\n+\n+        private DistributedLockRecord AcquireLockRecord(string name, TimeSpan maxValidFor, string machineName, int? threadId) {","path":"src/Orchard/Tasks/Locking/Services/DistributedLockService.cs","position":112,"original_position":111,"commit_id":"c5b0cac24a164e73bf552c72ca441d8eea29cbf1","original_commit_id":"81bf5d94fd253912204da4c645eaf1f8f1f5fe2f","user":{"login":"DanielStolt","id":3889090,"avatar_url":"https://avatars.githubusercontent.com/u/3889090?v=3","gravatar_id":"","url":"https://api.github.com/users/DanielStolt","html_url":"https://github.com/DanielStolt","followers_url":"https://api.github.com/users/DanielStolt/followers","following_url":"https://api.github.com/users/DanielStolt/following{/other_user}","gists_url":"https://api.github.com/users/DanielStolt/gists{/gist_id}","starred_url":"https://api.github.com/users/DanielStolt/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/DanielStolt/subscriptions","organizations_url":"https://api.github.com/users/DanielStolt/orgs","repos_url":"https://api.github.com/users/DanielStolt/repos","events_url":"https://api.github.com/users/DanielStolt/events{/privacy}","received_events_url":"https://api.github.com/users/DanielStolt/received_events","type":"User","site_admin":false},"body":"But that's how many overloads work. One does the main work, while others decorate and defer to that one. Would make perfect sense in this case wouldn't it? ","created_at":"2015-08-22T20:15:30Z","updated_at":"2015-08-22T20:15:30Z","html_url":"https://github.com/OrchardCMS/Orchard/pull/5628#discussion_r37699321","pull_request_url":"https://api.github.com/repos/OrchardCMS/Orchard/pulls/5628","_links":{"self":{"href":"https://api.github.com/repos/OrchardCMS/Orchard/pulls/comments/37699321"},"html":{"href":"https://github.com/OrchardCMS/Orchard/pull/5628#discussion_r37699321"},"pull_request":{"href":"https://api.github.com/repos/OrchardCMS/Orchard/pulls/5628"}}},"pull_request":{"url":"https://api.github.com/repos/OrchardCMS/Orchard/pulls/5628","id":42045871,"html_url":"https://github.com/OrchardCMS/Orchard/pull/5628","diff_url":"https://github.com/OrchardCMS/Orchard/pull/5628.diff","patch_url":"https://github.com/OrchardCMS/Orchard/pull/5628.patch","issue_url":"https://api.github.com/repos/OrchardCMS/Orchard/issues/5628","number":5628,"state":"open","locked":false,"title":"Feature/distributed locking","user":{"login":"sfmskywalker","id":938393,"avatar_url":"https://avatars.githubusercontent.com/u/938393?v=3","gravatar_id":"","url":"https://api.github.com/users/sfmskywalker","html_url":"https://github.com/sfmskywalker","followers_url":"https://api.github.com/users/sfmskywalker/followers","following_url":"https://api.github.com/users/sfmskywalker/following{/other_user}","gists_url":"https://api.github.com/users/sfmskywalker/gists{/gist_id}","starred_url":"https://api.github.com/users/sfmskywalker/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sfmskywalker/subscriptions","organizations_url":"https://api.github.com/users/sfmskywalker/orgs","repos_url":"https://api.github.com/users/sfmskywalker/repos","events_url":"https://api.github.com/users/sfmskywalker/events{/privacy}","received_events_url":"https://api.github.com/users/sfmskywalker/received_events","type":"User","site_admin":false},"body":"This PR adds a distributed locking service with a lock provider that uses a database table as the synchronization medium. This database implementation of `IDistributedLock` is provided as part of the `Orchard.TaskLease` module. A default (non-distributed) version of `IDistributedLock` is provided by the framework so that it's safe to use the service even if no real lock provider is enabled.\r\n\r\nThe `AutomaticDataMigrations` class has been updated to try and acquire a lock using the `IDistributedLockService` to ensure only one node in web farm scenarios execute the migrations after deployment or installing a new module.\r\n","created_at":"2015-08-10T17:41:32Z","updated_at":"2015-08-22T20:15:30Z","closed_at":null,"merged_at":null,"merge_commit_sha":"13322ff99fe6fb5b0403791d52c0fea4c0ef6abd","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/OrchardCMS/Orchard/pulls/5628/commits","review_comments_url":"https://api.github.com/repos/OrchardCMS/Orchard/pulls/5628/comments","review_comment_url":"https://api.github.com/repos/OrchardCMS/Orchard/pulls/comments{/number}","comments_url":"https://api.github.com/repos/OrchardCMS/Orchard/issues/5628/comments","statuses_url":"https://api.github.com/repos/OrchardCMS/Orchard/statuses/c5b0cac24a164e73bf552c72ca441d8eea29cbf1","head":{"label":"OrchardCMS:feature/distributed-locking","ref":"feature/distributed-locking","sha":"c5b0cac24a164e73bf552c72ca441d8eea29cbf1","user":{"login":"OrchardCMS","id":1159247,"avatar_url":"https://avatars.githubusercontent.com/u/1159247?v=3","gravatar_id":"","url":"https://api.github.com/users/OrchardCMS","html_url":"https://github.com/OrchardCMS","followers_url":"https://api.github.com/users/OrchardCMS/followers","following_url":"https://api.github.com/users/OrchardCMS/following{/other_user}","gists_url":"https://api.github.com/users/OrchardCMS/gists{/gist_id}","starred_url":"https://api.github.com/users/OrchardCMS/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/OrchardCMS/subscriptions","organizations_url":"https://api.github.com/users/OrchardCMS/orgs","repos_url":"https://api.github.com/users/OrchardCMS/repos","events_url":"https://api.github.com/users/OrchardCMS/events{/privacy}","received_events_url":"https://api.github.com/users/OrchardCMS/received_events","type":"Organization","site_admin":false},"repo":{"id":33889464,"name":"Orchard","full_name":"OrchardCMS/Orchard","owner":{"login":"OrchardCMS","id":1159247,"avatar_url":"https://avatars.githubusercontent.com/u/1159247?v=3","gravatar_id":"","url":"https://api.github.com/users/OrchardCMS","html_url":"https://github.com/OrchardCMS","followers_url":"https://api.github.com/users/OrchardCMS/followers","following_url":"https://api.github.com/users/OrchardCMS/following{/other_user}","gists_url":"https://api.github.com/users/OrchardCMS/gists{/gist_id}","starred_url":"https://api.github.com/users/OrchardCMS/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/OrchardCMS/subscriptions","organizations_url":"https://api.github.com/users/OrchardCMS/orgs","repos_url":"https://api.github.com/users/OrchardCMS/repos","events_url":"https://api.github.com/users/OrchardCMS/events{/privacy}","received_events_url":"https://api.github.com/users/OrchardCMS/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/OrchardCMS/Orchard","description":"Orchard is a free, open source, community-focused Content Management System built on the ASP.NET MVC platform.","fork":false,"url":"https://api.github.com/repos/OrchardCMS/Orchard","forks_url":"https://api.github.com/repos/OrchardCMS/Orchard/forks","keys_url":"https://api.github.com/repos/OrchardCMS/Orchard/keys{/key_id}","collaborators_url":"https://api.github.com/repos/OrchardCMS/Orchard/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/OrchardCMS/Orchard/teams","hooks_url":"https://api.github.com/repos/OrchardCMS/Orchard/hooks","issue_events_url":"https://api.github.com/repos/OrchardCMS/Orchard/issues/events{/number}","events_url":"https://api.github.com/repos/OrchardCMS/Orchard/events","assignees_url":"https://api.github.com/repos/OrchardCMS/Orchard/assignees{/user}","branches_url":"https://api.github.com/repos/OrchardCMS/Orchard/branches{/branch}","tags_url":"https://api.github.com/repos/OrchardCMS/Orchard/tags","blobs_url":"https://api.github.com/repos/OrchardCMS/Orchard/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/OrchardCMS/Orchard/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/OrchardCMS/Orchard/git/refs{/sha}","trees_url":"https://api.github.com/repos/OrchardCMS/Orchard/git/trees{/sha}","statuses_url":"https://api.github.com/repos/OrchardCMS/Orchard/statuses/{sha}","languages_url":"https://api.github.com/repos/OrchardCMS/Orchard/languages","stargazers_url":"https://api.github.com/repos/OrchardCMS/Orchard/stargazers","contributors_url":"https://api.github.com/repos/OrchardCMS/Orchard/contributors","subscribers_url":"https://api.github.com/repos/OrchardCMS/Orchard/subscribers","subscription_url":"https://api.github.com/repos/OrchardCMS/Orchard/subscription","commits_url":"https://api.github.com/repos/OrchardCMS/Orchard/commits{/sha}","git_commits_url":"https://api.github.com/repos/OrchardCMS/Orchard/git/commits{/sha}","comments_url":"https://api.github.com/repos/OrchardCMS/Orchard/comments{/number}","issue_comment_url":"https://api.github.com/repos/OrchardCMS/Orchard/issues/comments{/number}","contents_url":"https://api.github.com/repos/OrchardCMS/Orchard/contents/{+path}","compare_url":"https://api.github.com/repos/OrchardCMS/Orchard/compare/{base}...{head}","merges_url":"https://api.github.com/repos/OrchardCMS/Orchard/merges","archive_url":"https://api.github.com/repos/OrchardCMS/Orchard/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/OrchardCMS/Orchard/downloads","issues_url":"https://api.github.com/repos/OrchardCMS/Orchard/issues{/number}","pulls_url":"https://api.github.com/repos/OrchardCMS/Orchard/pulls{/number}","milestones_url":"https://api.github.com/repos/OrchardCMS/Orchard/milestones{/number}","notifications_url":"https://api.github.com/repos/OrchardCMS/Orchard/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/OrchardCMS/Orchard/labels{/name}","releases_url":"https://api.github.com/repos/OrchardCMS/Orchard/releases{/id}","created_at":"2015-04-13T19:35:52Z","updated_at":"2015-08-21T13:58:16Z","pushed_at":"2015-08-22T19:15:25Z","git_url":"git://github.com/OrchardCMS/Orchard.git","ssh_url":"git@github.com:OrchardCMS/Orchard.git","clone_url":"https://github.com/OrchardCMS/Orchard.git","svn_url":"https://github.com/OrchardCMS/Orchard","homepage":"http://orchardproject.net","size":194660,"stargazers_count":404,"watchers_count":404,"language":"C#","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":272,"mirror_url":null,"open_issues_count":1482,"forks":272,"open_issues":1482,"watchers":404,"default_branch":"dev"}},"base":{"label":"OrchardCMS:dev","ref":"dev","sha":"d2c622dac62d59910a9a0ee66e4f6d14b05e2363","user":{"login":"OrchardCMS","id":1159247,"avatar_url":"https://avatars.githubusercontent.com/u/1159247?v=3","gravatar_id":"","url":"https://api.github.com/users/OrchardCMS","html_url":"https://github.com/OrchardCMS","followers_url":"https://api.github.com/users/OrchardCMS/followers","following_url":"https://api.github.com/users/OrchardCMS/following{/other_user}","gists_url":"https://api.github.com/users/OrchardCMS/gists{/gist_id}","starred_url":"https://api.github.com/users/OrchardCMS/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/OrchardCMS/subscriptions","organizations_url":"https://api.github.com/users/OrchardCMS/orgs","repos_url":"https://api.github.com/users/OrchardCMS/repos","events_url":"https://api.github.com/users/OrchardCMS/events{/privacy}","received_events_url":"https://api.github.com/users/OrchardCMS/received_events","type":"Organization","site_admin":false},"repo":{"id":33889464,"name":"Orchard","full_name":"OrchardCMS/Orchard","owner":{"login":"OrchardCMS","id":1159247,"avatar_url":"https://avatars.githubusercontent.com/u/1159247?v=3","gravatar_id":"","url":"https://api.github.com/users/OrchardCMS","html_url":"https://github.com/OrchardCMS","followers_url":"https://api.github.com/users/OrchardCMS/followers","following_url":"https://api.github.com/users/OrchardCMS/following{/other_user}","gists_url":"https://api.github.com/users/OrchardCMS/gists{/gist_id}","starred_url":"https://api.github.com/users/OrchardCMS/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/OrchardCMS/subscriptions","organizations_url":"https://api.github.com/users/OrchardCMS/orgs","repos_url":"https://api.github.com/users/OrchardCMS/repos","events_url":"https://api.github.com/users/OrchardCMS/events{/privacy}","received_events_url":"https://api.github.com/users/OrchardCMS/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/OrchardCMS/Orchard","description":"Orchard is a free, open source, community-focused Content Management System built on the ASP.NET MVC platform.","fork":false,"url":"https://api.github.com/repos/OrchardCMS/Orchard","forks_url":"https://api.github.com/repos/OrchardCMS/Orchard/forks","keys_url":"https://api.github.com/repos/OrchardCMS/Orchard/keys{/key_id}","collaborators_url":"https://api.github.com/repos/OrchardCMS/Orchard/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/OrchardCMS/Orchard/teams","hooks_url":"https://api.github.com/repos/OrchardCMS/Orchard/hooks","issue_events_url":"https://api.github.com/repos/OrchardCMS/Orchard/issues/events{/number}","events_url":"https://api.github.com/repos/OrchardCMS/Orchard/events","assignees_url":"https://api.github.com/repos/OrchardCMS/Orchard/assignees{/user}","branches_url":"https://api.github.com/repos/OrchardCMS/Orchard/branches{/branch}","tags_url":"https://api.github.com/repos/OrchardCMS/Orchard/tags","blobs_url":"https://api.github.com/repos/OrchardCMS/Orchard/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/OrchardCMS/Orchard/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/OrchardCMS/Orchard/git/refs{/sha}","trees_url":"https://api.github.com/repos/OrchardCMS/Orchard/git/trees{/sha}","statuses_url":"https://api.github.com/repos/OrchardCMS/Orchard/statuses/{sha}","languages_url":"https://api.github.com/repos/OrchardCMS/Orchard/languages","stargazers_url":"https://api.github.com/repos/OrchardCMS/Orchard/stargazers","contributors_url":"https://api.github.com/repos/OrchardCMS/Orchard/contributors","subscribers_url":"https://api.github.com/repos/OrchardCMS/Orchard/subscribers","subscription_url":"https://api.github.com/repos/OrchardCMS/Orchard/subscription","commits_url":"https://api.github.com/repos/OrchardCMS/Orchard/commits{/sha}","git_commits_url":"https://api.github.com/repos/OrchardCMS/Orchard/git/commits{/sha}","comments_url":"https://api.github.com/repos/OrchardCMS/Orchard/comments{/number}","issue_comment_url":"https://api.github.com/repos/OrchardCMS/Orchard/issues/comments{/number}","contents_url":"https://api.github.com/repos/OrchardCMS/Orchard/contents/{+path}","compare_url":"https://api.github.com/repos/OrchardCMS/Orchard/compare/{base}...{head}","merges_url":"https://api.github.com/repos/OrchardCMS/Orchard/merges","archive_url":"https://api.github.com/repos/OrchardCMS/Orchard/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/OrchardCMS/Orchard/downloads","issues_url":"https://api.github.com/repos/OrchardCMS/Orchard/issues{/number}","pulls_url":"https://api.github.com/repos/OrchardCMS/Orchard/pulls{/number}","milestones_url":"https://api.github.com/repos/OrchardCMS/Orchard/milestones{/number}","notifications_url":"https://api.github.com/repos/OrchardCMS/Orchard/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/OrchardCMS/Orchard/labels{/name}","releases_url":"https://api.github.com/repos/OrchardCMS/Orchard/releases{/id}","created_at":"2015-04-13T19:35:52Z","updated_at":"2015-08-21T13:58:16Z","pushed_at":"2015-08-22T19:15:25Z","git_url":"git://github.com/OrchardCMS/Orchard.git","ssh_url":"git@github.com:OrchardCMS/Orchard.git","clone_url":"https://github.com/OrchardCMS/Orchard.git","svn_url":"https://github.com/OrchardCMS/Orchard","homepage":"http://orchardproject.net","size":194660,"stargazers_count":404,"watchers_count":404,"language":"C#","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":272,"mirror_url":null,"open_issues_count":1482,"forks":272,"open_issues":1482,"watchers":404,"default_branch":"dev"}},"_links":{"self":{"href":"https://api.github.com/repos/OrchardCMS/Orchard/pulls/5628"},"html":{"href":"https://github.com/OrchardCMS/Orchard/pull/5628"},"issue":{"href":"https://api.github.com/repos/OrchardCMS/Orchard/issues/5628"},"comments":{"href":"https://api.github.com/repos/OrchardCMS/Orchard/issues/5628/comments"},"review_comments":{"href":"https://api.github.com/repos/OrchardCMS/Orchard/pulls/5628/comments"},"review_comment":{"href":"https://api.github.com/repos/OrchardCMS/Orchard/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/OrchardCMS/Orchard/pulls/5628/commits"},"statuses":{"href":"https://api.github.com/repos/OrchardCMS/Orchard/statuses/c5b0cac24a164e73bf552c72ca441d8eea29cbf1"}}}}