Merge pull request #19 from mjohnson0580/master

Added Hangfire.Raven.Tests project. Nice work @mjohnson0580  and @xinix00
This commit is contained in:
Austin Winstanley
2017-03-07 15:19:12 -06:00
committed by GitHub
38 changed files with 28039 additions and 21916 deletions
+57 -50
View File
@@ -1,50 +1,57 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_", "_", "{8CF2D2F6-BEFE-47FE-B141-0017FC6F97D8}"
ProjectSection(SolutionItems) = preProject
.gitignore = .gitignore
global.json = global.json
README.md = README.md
EndProjectSection
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Hangfire.Raven", "src\Hangfire.Raven\Hangfire.Raven.xproj", "{BA18F6BF-5613-4B9D-A57A-C05C51148EC5}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{EF9BFE2C-0386-4EC4-83EC-D7E315F56735}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{54466714-23AC-4D87-90EB-EAD235F84F71}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Hangfire.Raven.Examples.Console", "examples\Hangfire.Raven.Examples.Console\Hangfire.Raven.Examples.Console.xproj", "{3955F2F1-99AF-4CC0-88F5-C9B69757A1B2}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Hangfire.Raven.Examples.AspNetCore", "examples\Hangfire.Raven.Examples.AspNetCore\Hangfire.Raven.Examples.AspNetCore.xproj", "{82A516D0-287C-4DD8-A153-2B3AFFB88241}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{BA18F6BF-5613-4B9D-A57A-C05C51148EC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BA18F6BF-5613-4B9D-A57A-C05C51148EC5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BA18F6BF-5613-4B9D-A57A-C05C51148EC5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BA18F6BF-5613-4B9D-A57A-C05C51148EC5}.Release|Any CPU.Build.0 = Release|Any CPU
{3955F2F1-99AF-4CC0-88F5-C9B69757A1B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3955F2F1-99AF-4CC0-88F5-C9B69757A1B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3955F2F1-99AF-4CC0-88F5-C9B69757A1B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3955F2F1-99AF-4CC0-88F5-C9B69757A1B2}.Release|Any CPU.Build.0 = Release|Any CPU
{82A516D0-287C-4DD8-A153-2B3AFFB88241}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{82A516D0-287C-4DD8-A153-2B3AFFB88241}.Debug|Any CPU.Build.0 = Debug|Any CPU
{82A516D0-287C-4DD8-A153-2B3AFFB88241}.Release|Any CPU.ActiveCfg = Release|Any CPU
{82A516D0-287C-4DD8-A153-2B3AFFB88241}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{BA18F6BF-5613-4B9D-A57A-C05C51148EC5} = {EF9BFE2C-0386-4EC4-83EC-D7E315F56735}
{3955F2F1-99AF-4CC0-88F5-C9B69757A1B2} = {54466714-23AC-4D87-90EB-EAD235F84F71}
{82A516D0-287C-4DD8-A153-2B3AFFB88241} = {54466714-23AC-4D87-90EB-EAD235F84F71}
EndGlobalSection
EndGlobal
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_", "_", "{8CF2D2F6-BEFE-47FE-B141-0017FC6F97D8}"
ProjectSection(SolutionItems) = preProject
.gitignore = .gitignore
global.json = global.json
README.md = README.md
EndProjectSection
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Hangfire.Raven", "src\Hangfire.Raven\Hangfire.Raven.xproj", "{BA18F6BF-5613-4B9D-A57A-C05C51148EC5}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{EF9BFE2C-0386-4EC4-83EC-D7E315F56735}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{54466714-23AC-4D87-90EB-EAD235F84F71}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Hangfire.Raven.Examples.Console", "examples\Hangfire.Raven.Examples.Console\Hangfire.Raven.Examples.Console.xproj", "{3955F2F1-99AF-4CC0-88F5-C9B69757A1B2}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Hangfire.Raven.Examples.AspNetCore", "examples\Hangfire.Raven.Examples.AspNetCore\Hangfire.Raven.Examples.AspNetCore.xproj", "{82A516D0-287C-4DD8-A153-2B3AFFB88241}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Hangfire.Raven.Tests", "src\Hangfire.Raven.Tests\Hangfire.Raven.Tests.xproj", "{2F2C50B7-C556-4A60-81AC-019DAF261556}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{BA18F6BF-5613-4B9D-A57A-C05C51148EC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BA18F6BF-5613-4B9D-A57A-C05C51148EC5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BA18F6BF-5613-4B9D-A57A-C05C51148EC5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BA18F6BF-5613-4B9D-A57A-C05C51148EC5}.Release|Any CPU.Build.0 = Release|Any CPU
{3955F2F1-99AF-4CC0-88F5-C9B69757A1B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3955F2F1-99AF-4CC0-88F5-C9B69757A1B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3955F2F1-99AF-4CC0-88F5-C9B69757A1B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3955F2F1-99AF-4CC0-88F5-C9B69757A1B2}.Release|Any CPU.Build.0 = Release|Any CPU
{82A516D0-287C-4DD8-A153-2B3AFFB88241}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{82A516D0-287C-4DD8-A153-2B3AFFB88241}.Debug|Any CPU.Build.0 = Debug|Any CPU
{82A516D0-287C-4DD8-A153-2B3AFFB88241}.Release|Any CPU.ActiveCfg = Release|Any CPU
{82A516D0-287C-4DD8-A153-2B3AFFB88241}.Release|Any CPU.Build.0 = Release|Any CPU
{2F2C50B7-C556-4A60-81AC-019DAF261556}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2F2C50B7-C556-4A60-81AC-019DAF261556}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2F2C50B7-C556-4A60-81AC-019DAF261556}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2F2C50B7-C556-4A60-81AC-019DAF261556}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{BA18F6BF-5613-4B9D-A57A-C05C51148EC5} = {EF9BFE2C-0386-4EC4-83EC-D7E315F56735}
{3955F2F1-99AF-4CC0-88F5-C9B69757A1B2} = {54466714-23AC-4D87-90EB-EAD235F84F71}
{82A516D0-287C-4DD8-A153-2B3AFFB88241} = {54466714-23AC-4D87-90EB-EAD235F84F71}
{2F2C50B7-C556-4A60-81AC-019DAF261556} = {EF9BFE2C-0386-4EC4-83EC-D7E315F56735}
EndGlobalSection
EndGlobal
@@ -43,8 +43,8 @@
"summary": "RavenDB job storage for Hangfire - AspNetCore Example"
},
"dependencies": {
"Hangfire.AspNetCore": "1.6.7",
"Hangfire.Raven": "2.1.1",
"Hangfire.AspNetCore": "1.6.8",
"Hangfire.Raven": { "target": "project" },
"Microsoft.NETCore.App": {
"version": "1.0.1",
"type": "platform"
File diff suppressed because it is too large Load Diff
@@ -52,7 +52,7 @@
"net452": { }
},
"dependencies": {
"Hangfire.Raven": "2.1.1",
"Hangfire.Raven": { "target": "project" },
"System.ComponentModel.Primitives": "4.3.0"
}
}
@@ -1,286 +1,306 @@
{
"locked": false,
"version": 2,
"targets": {
".NETFramework,Version=v4.5.2": {
"Hangfire.Core/1.6.4": {
"type": "package",
"dependencies": {
"Newtonsoft.Json": "5.0.0",
"Owin": "1.0.0"
},
"compile": {
"lib/net45/Hangfire.Core.dll": {}
},
"runtime": {
"lib/net45/Hangfire.Core.dll": {}
}
},
"Newtonsoft.Json/9.0.1": {
"type": "package",
"compile": {
"lib/net45/Newtonsoft.Json.dll": {}
},
"runtime": {
"lib/net45/Newtonsoft.Json.dll": {}
}
},
"Owin/1.0.0": {
"type": "package",
"compile": {
"lib/net40/Owin.dll": {}
},
"runtime": {
"lib/net40/Owin.dll": {}
}
},
"RavenDB.Client/3.5.0-rc-35169": {
"type": "package",
"frameworkAssemblies": [
"System.ComponentModel.Composition"
],
"compile": {
"lib/net45/Raven.Abstractions.dll": {},
"lib/net45/Raven.Client.Lightweight.dll": {}
},
"runtime": {
"lib/net45/Raven.Abstractions.dll": {},
"lib/net45/Raven.Client.Lightweight.dll": {}
}
},
"System.ComponentModel.Primitives/4.3.0": {
"type": "package",
"frameworkAssemblies": [
"System",
"mscorlib"
],
"compile": {
"ref/net45/System.ComponentModel.Primitives.dll": {}
},
"runtime": {
"lib/net45/System.ComponentModel.Primitives.dll": {}
}
},
"Hangfire.Raven/2.1.1": {
"type": "project",
"framework": ".NETFramework,Version=v4.5",
"dependencies": {
"Hangfire.Core": "1.6.4",
"Newtonsoft.Json": "9.0.1",
"RavenDB.Client": "3.5.0-rc-35169"
},
"frameworkAssemblies": [
"System.Data",
"System.Reflection"
],
"compile": {
"net45/Hangfire.Raven.dll": {}
},
"runtime": {
"net45/Hangfire.Raven.dll": {}
}
}
},
".NETFramework,Version=v4.5.2/win10-x64": {
"Hangfire.Core/1.6.4": {
"type": "package",
"dependencies": {
"Newtonsoft.Json": "5.0.0",
"Owin": "1.0.0"
},
"compile": {
"lib/net45/Hangfire.Core.dll": {}
},
"runtime": {
"lib/net45/Hangfire.Core.dll": {}
}
},
"Newtonsoft.Json/9.0.1": {
"type": "package",
"compile": {
"lib/net45/Newtonsoft.Json.dll": {}
},
"runtime": {
"lib/net45/Newtonsoft.Json.dll": {}
}
},
"Owin/1.0.0": {
"type": "package",
"compile": {
"lib/net40/Owin.dll": {}
},
"runtime": {
"lib/net40/Owin.dll": {}
}
},
"RavenDB.Client/3.5.0-rc-35169": {
"type": "package",
"frameworkAssemblies": [
"System.ComponentModel.Composition"
],
"compile": {
"lib/net45/Raven.Abstractions.dll": {},
"lib/net45/Raven.Client.Lightweight.dll": {}
},
"runtime": {
"lib/net45/Raven.Abstractions.dll": {},
"lib/net45/Raven.Client.Lightweight.dll": {}
}
},
"System.ComponentModel.Primitives/4.3.0": {
"type": "package",
"frameworkAssemblies": [
"System",
"mscorlib"
],
"compile": {
"ref/net45/System.ComponentModel.Primitives.dll": {}
},
"runtime": {
"lib/net45/System.ComponentModel.Primitives.dll": {}
}
},
"Hangfire.Raven/2.1.1": {
"type": "project",
"framework": ".NETFramework,Version=v4.5",
"dependencies": {
"Hangfire.Core": "1.6.4",
"Newtonsoft.Json": "9.0.1",
"RavenDB.Client": "3.5.0-rc-35169"
},
"frameworkAssemblies": [
"System.Data",
"System.Reflection"
],
"compile": {
"net45/Hangfire.Raven.dll": {}
},
"runtime": {
"net45/Hangfire.Raven.dll": {}
}
}
}
},
"libraries": {
"Hangfire.Core/1.6.4": {
"sha512": "QM+WNZ/B7t3BzOBumLb2NU9aoh76hb/Jdt9pV95+HSHZbPsvms7Jk9kUC9PLopdh9gpUL7L6dmnhEnpQJBBs+Q==",
"type": "package",
"path": "Hangfire.Core/1.6.4",
"files": [
"Hangfire.Core.1.6.4.nupkg.sha512",
"Hangfire.Core.nuspec",
"lib/net45/Hangfire.Core.dll",
"lib/net45/Hangfire.Core.xml",
"lib/netstandard1.3/Hangfire.Core.dll",
"lib/netstandard1.3/Hangfire.Core.xml"
]
},
"Newtonsoft.Json/9.0.1": {
"sha512": "U82mHQSKaIk+lpSVCbWYKNavmNH1i5xrExDEquU1i6I5pV6UMOqRnJRSlKO3cMPfcpp0RgDY+8jUXHdQ4IfXvw==",
"type": "package",
"path": "Newtonsoft.Json/9.0.1",
"files": [
"Newtonsoft.Json.9.0.1.nupkg.sha512",
"Newtonsoft.Json.nuspec",
"lib/net20/Newtonsoft.Json.dll",
"lib/net20/Newtonsoft.Json.xml",
"lib/net35/Newtonsoft.Json.dll",
"lib/net35/Newtonsoft.Json.xml",
"lib/net40/Newtonsoft.Json.dll",
"lib/net40/Newtonsoft.Json.xml",
"lib/net45/Newtonsoft.Json.dll",
"lib/net45/Newtonsoft.Json.xml",
"lib/netstandard1.0/Newtonsoft.Json.dll",
"lib/netstandard1.0/Newtonsoft.Json.xml",
"lib/portable-net40+sl5+wp80+win8+wpa81/Newtonsoft.Json.dll",
"lib/portable-net40+sl5+wp80+win8+wpa81/Newtonsoft.Json.xml",
"lib/portable-net45+wp80+win8+wpa81/Newtonsoft.Json.dll",
"lib/portable-net45+wp80+win8+wpa81/Newtonsoft.Json.xml",
"tools/install.ps1"
]
},
"Owin/1.0.0": {
"sha512": "OseTFniKmyp76mEzOBwIKGBRS5eMoYNkMKaMXOpxx9jv88+b6mh1rSaw43vjBOItNhaLFG3d0a20PfHyibH5sw==",
"type": "package",
"path": "Owin/1.0.0",
"files": [
"Owin.1.0.0.nupkg.sha512",
"Owin.nuspec",
"lib/net40/Owin.dll"
]
},
"RavenDB.Client/3.5.0-rc-35169": {
"sha512": "xP23wh0U4h8/MmuiFEYhCB+y9AA87DvsJOifmfynPrYYV9jOjFLLWa55iOmy/RNWakDso7td0m3AcfGygULXQw==",
"type": "package",
"path": "RavenDB.Client/3.5.0-rc-35169",
"files": [
"RavenDB.Client.3.5.0-rc-35169.nupkg.sha512",
"RavenDB.Client.nuspec",
"lib/net45/Raven.Abstractions.dll",
"lib/net45/Raven.Abstractions.pdb",
"lib/net45/Raven.Abstractions.xml",
"lib/net45/Raven.Client.Lightweight.XML",
"lib/net45/Raven.Client.Lightweight.dll",
"lib/net45/Raven.Client.Lightweight.pdb",
"lib/netstandard1.6/Raven.Abstractions.dll",
"lib/netstandard1.6/Raven.Abstractions.pdb",
"lib/netstandard1.6/Raven.Client.Lightweight.deps.json",
"lib/netstandard1.6/Raven.Client.Lightweight.dll",
"lib/netstandard1.6/Raven.Client.Lightweight.pdb",
"lib/netstandard1.6/Sparrow.dll",
"lib/netstandard1.6/Sparrow.pdb"
]
},
"System.ComponentModel.Primitives/4.3.0": {
"sha512": "j8GUkCpM8V4d4vhLIIoBLGey2Z5bCkMVNjEZseyAlm4n5arcsJOeI3zkUP+zvZgzsbLTYh4lYeP/ZD/gdIAPrw==",
"type": "package",
"path": "System.ComponentModel.Primitives/4.3.0",
"files": [
"System.ComponentModel.Primitives.4.3.0.nupkg.sha512",
"System.ComponentModel.Primitives.nuspec",
"ThirdPartyNotices.txt",
"dotnet_library_license.txt",
"lib/MonoAndroid10/_._",
"lib/MonoTouch10/_._",
"lib/net45/System.ComponentModel.Primitives.dll",
"lib/netstandard1.0/System.ComponentModel.Primitives.dll",
"lib/xamarinios10/_._",
"lib/xamarinmac20/_._",
"lib/xamarintvos10/_._",
"lib/xamarinwatchos10/_._",
"ref/MonoAndroid10/_._",
"ref/MonoTouch10/_._",
"ref/net45/System.ComponentModel.Primitives.dll",
"ref/netstandard1.0/System.ComponentModel.Primitives.dll",
"ref/netstandard1.0/System.ComponentModel.Primitives.xml",
"ref/netstandard1.0/de/System.ComponentModel.Primitives.xml",
"ref/netstandard1.0/es/System.ComponentModel.Primitives.xml",
"ref/netstandard1.0/fr/System.ComponentModel.Primitives.xml",
"ref/netstandard1.0/it/System.ComponentModel.Primitives.xml",
"ref/netstandard1.0/ja/System.ComponentModel.Primitives.xml",
"ref/netstandard1.0/ko/System.ComponentModel.Primitives.xml",
"ref/netstandard1.0/ru/System.ComponentModel.Primitives.xml",
"ref/netstandard1.0/zh-hans/System.ComponentModel.Primitives.xml",
"ref/netstandard1.0/zh-hant/System.ComponentModel.Primitives.xml",
"ref/xamarinios10/_._",
"ref/xamarinmac20/_._",
"ref/xamarintvos10/_._",
"ref/xamarinwatchos10/_._"
]
},
"Hangfire.Raven/2.1.1": {
"type": "project",
"path": "../../src/Hangfire.Raven/project.json",
"msbuildProject": "../../src/Hangfire.Raven/Hangfire.Raven.xproj"
}
},
"projectFileDependencyGroups": {
"": [
"Hangfire.Raven >= 2.1.1",
"System.ComponentModel.Primitives >= 4.3.0"
],
".NETFramework,Version=v4.5.2": []
},
"tools": {},
"projectFileToolGroups": {}
{
"locked": false,
"version": 2,
"targets": {
".NETFramework,Version=v4.5.2": {
"Hangfire.Core/1.6.8": {
"type": "package",
"dependencies": {
"Newtonsoft.Json": "5.0.0",
"Owin": "1.0.0"
},
"compile": {
"lib/net45/Hangfire.Core.dll": {}
},
"runtime": {
"lib/net45/Hangfire.Core.dll": {}
},
"resource": {
"lib/net45/es/Hangfire.Core.resources.dll": {
"locale": "es"
},
"lib/net45/zh/Hangfire.Core.resources.dll": {
"locale": "zh"
}
}
},
"Newtonsoft.Json/9.0.1": {
"type": "package",
"compile": {
"lib/net45/Newtonsoft.Json.dll": {}
},
"runtime": {
"lib/net45/Newtonsoft.Json.dll": {}
}
},
"Owin/1.0.0": {
"type": "package",
"compile": {
"lib/net40/Owin.dll": {}
},
"runtime": {
"lib/net40/Owin.dll": {}
}
},
"RavenDB.Client/3.5.3-patch-35190": {
"type": "package",
"frameworkAssemblies": [
"System.ComponentModel.Composition"
],
"compile": {
"lib/net45/Raven.Abstractions.dll": {},
"lib/net45/Raven.Client.Lightweight.dll": {}
},
"runtime": {
"lib/net45/Raven.Abstractions.dll": {},
"lib/net45/Raven.Client.Lightweight.dll": {}
}
},
"System.ComponentModel.Primitives/4.3.0": {
"type": "package",
"frameworkAssemblies": [
"System",
"mscorlib"
],
"compile": {
"ref/net45/System.ComponentModel.Primitives.dll": {}
},
"runtime": {
"lib/net45/System.ComponentModel.Primitives.dll": {}
}
},
"Hangfire.Raven/2.1.1": {
"type": "project",
"framework": ".NETFramework,Version=v4.5",
"dependencies": {
"Hangfire.Core": "1.6.8",
"Newtonsoft.Json": "9.0.1",
"RavenDB.Client": "3.5.3-patch-35190"
},
"frameworkAssemblies": [
"System.Data",
"System.Reflection"
],
"compile": {
"net45/Hangfire.Raven.dll": {}
},
"runtime": {
"net45/Hangfire.Raven.dll": {}
}
}
},
".NETFramework,Version=v4.5.2/win10-x64": {
"Hangfire.Core/1.6.8": {
"type": "package",
"dependencies": {
"Newtonsoft.Json": "5.0.0",
"Owin": "1.0.0"
},
"compile": {
"lib/net45/Hangfire.Core.dll": {}
},
"runtime": {
"lib/net45/Hangfire.Core.dll": {}
},
"resource": {
"lib/net45/es/Hangfire.Core.resources.dll": {
"locale": "es"
},
"lib/net45/zh/Hangfire.Core.resources.dll": {
"locale": "zh"
}
}
},
"Newtonsoft.Json/9.0.1": {
"type": "package",
"compile": {
"lib/net45/Newtonsoft.Json.dll": {}
},
"runtime": {
"lib/net45/Newtonsoft.Json.dll": {}
}
},
"Owin/1.0.0": {
"type": "package",
"compile": {
"lib/net40/Owin.dll": {}
},
"runtime": {
"lib/net40/Owin.dll": {}
}
},
"RavenDB.Client/3.5.3-patch-35190": {
"type": "package",
"frameworkAssemblies": [
"System.ComponentModel.Composition"
],
"compile": {
"lib/net45/Raven.Abstractions.dll": {},
"lib/net45/Raven.Client.Lightweight.dll": {}
},
"runtime": {
"lib/net45/Raven.Abstractions.dll": {},
"lib/net45/Raven.Client.Lightweight.dll": {}
}
},
"System.ComponentModel.Primitives/4.3.0": {
"type": "package",
"frameworkAssemblies": [
"System",
"mscorlib"
],
"compile": {
"ref/net45/System.ComponentModel.Primitives.dll": {}
},
"runtime": {
"lib/net45/System.ComponentModel.Primitives.dll": {}
}
},
"Hangfire.Raven/2.1.1": {
"type": "project",
"framework": ".NETFramework,Version=v4.5",
"dependencies": {
"Hangfire.Core": "1.6.8",
"Newtonsoft.Json": "9.0.1",
"RavenDB.Client": "3.5.3-patch-35190"
},
"frameworkAssemblies": [
"System.Data",
"System.Reflection"
],
"compile": {
"net45/Hangfire.Raven.dll": {}
},
"runtime": {
"net45/Hangfire.Raven.dll": {}
}
}
}
},
"libraries": {
"Hangfire.Core/1.6.8": {
"sha512": "c9au0lu14wOX7Zpm1dhjVIzXrlzotEe2mJHoM1n0E/erZrTUtIlruhDThkTO6QIRO1e6vklhXRhzYfQ4mnExHg==",
"type": "package",
"path": "Hangfire.Core/1.6.8",
"files": [
"Hangfire.Core.1.6.8.nupkg.sha512",
"Hangfire.Core.nuspec",
"lib/net45/Hangfire.Core.dll",
"lib/net45/Hangfire.Core.xml",
"lib/net45/es/Hangfire.Core.resources.dll",
"lib/net45/zh/Hangfire.Core.resources.dll",
"lib/netstandard1.3/Hangfire.Core.dll",
"lib/netstandard1.3/Hangfire.Core.xml",
"lib/netstandard1.3/es/Hangfire.Core.resources.dll",
"lib/netstandard1.3/zh/Hangfire.Core.resources.dll"
]
},
"Newtonsoft.Json/9.0.1": {
"sha512": "U82mHQSKaIk+lpSVCbWYKNavmNH1i5xrExDEquU1i6I5pV6UMOqRnJRSlKO3cMPfcpp0RgDY+8jUXHdQ4IfXvw==",
"type": "package",
"path": "Newtonsoft.Json/9.0.1",
"files": [
"Newtonsoft.Json.9.0.1.nupkg.sha512",
"Newtonsoft.Json.nuspec",
"lib/net20/Newtonsoft.Json.dll",
"lib/net20/Newtonsoft.Json.xml",
"lib/net35/Newtonsoft.Json.dll",
"lib/net35/Newtonsoft.Json.xml",
"lib/net40/Newtonsoft.Json.dll",
"lib/net40/Newtonsoft.Json.xml",
"lib/net45/Newtonsoft.Json.dll",
"lib/net45/Newtonsoft.Json.xml",
"lib/netstandard1.0/Newtonsoft.Json.dll",
"lib/netstandard1.0/Newtonsoft.Json.xml",
"lib/portable-net40+sl5+wp80+win8+wpa81/Newtonsoft.Json.dll",
"lib/portable-net40+sl5+wp80+win8+wpa81/Newtonsoft.Json.xml",
"lib/portable-net45+wp80+win8+wpa81/Newtonsoft.Json.dll",
"lib/portable-net45+wp80+win8+wpa81/Newtonsoft.Json.xml",
"tools/install.ps1"
]
},
"Owin/1.0.0": {
"sha512": "OseTFniKmyp76mEzOBwIKGBRS5eMoYNkMKaMXOpxx9jv88+b6mh1rSaw43vjBOItNhaLFG3d0a20PfHyibH5sw==",
"type": "package",
"path": "Owin/1.0.0",
"files": [
"Owin.1.0.0.nupkg.sha512",
"Owin.nuspec",
"lib/net40/Owin.dll"
]
},
"RavenDB.Client/3.5.3-patch-35190": {
"sha512": "AeX482QeGFtvQ6kKzEY2uVhW2F+tea/6J7aN8VlevL+UK4sLAOOvk45ciacXFzqhe5PcE2NazWjNgT6GrDd1VQ==",
"type": "package",
"path": "RavenDB.Client/3.5.3-patch-35190",
"files": [
"RavenDB.Client.3.5.3-patch-35190.nupkg.sha512",
"RavenDB.Client.nuspec",
"lib/net45/Raven.Abstractions.dll",
"lib/net45/Raven.Abstractions.pdb",
"lib/net45/Raven.Abstractions.xml",
"lib/net45/Raven.Client.Lightweight.XML",
"lib/net45/Raven.Client.Lightweight.dll",
"lib/net45/Raven.Client.Lightweight.pdb",
"lib/netstandard1.3/Raven.Abstractions.dll",
"lib/netstandard1.3/Raven.Abstractions.pdb",
"lib/netstandard1.3/Raven.Client.Lightweight.deps.json",
"lib/netstandard1.3/Raven.Client.Lightweight.dll",
"lib/netstandard1.3/Raven.Client.Lightweight.pdb",
"lib/netstandard1.3/Sparrow.dll",
"lib/netstandard1.3/Sparrow.pdb"
]
},
"System.ComponentModel.Primitives/4.3.0": {
"sha512": "j8GUkCpM8V4d4vhLIIoBLGey2Z5bCkMVNjEZseyAlm4n5arcsJOeI3zkUP+zvZgzsbLTYh4lYeP/ZD/gdIAPrw==",
"type": "package",
"path": "System.ComponentModel.Primitives/4.3.0",
"files": [
"System.ComponentModel.Primitives.4.3.0.nupkg.sha512",
"System.ComponentModel.Primitives.nuspec",
"ThirdPartyNotices.txt",
"dotnet_library_license.txt",
"lib/MonoAndroid10/_._",
"lib/MonoTouch10/_._",
"lib/net45/System.ComponentModel.Primitives.dll",
"lib/netstandard1.0/System.ComponentModel.Primitives.dll",
"lib/xamarinios10/_._",
"lib/xamarinmac20/_._",
"lib/xamarintvos10/_._",
"lib/xamarinwatchos10/_._",
"ref/MonoAndroid10/_._",
"ref/MonoTouch10/_._",
"ref/net45/System.ComponentModel.Primitives.dll",
"ref/netstandard1.0/System.ComponentModel.Primitives.dll",
"ref/netstandard1.0/System.ComponentModel.Primitives.xml",
"ref/netstandard1.0/de/System.ComponentModel.Primitives.xml",
"ref/netstandard1.0/es/System.ComponentModel.Primitives.xml",
"ref/netstandard1.0/fr/System.ComponentModel.Primitives.xml",
"ref/netstandard1.0/it/System.ComponentModel.Primitives.xml",
"ref/netstandard1.0/ja/System.ComponentModel.Primitives.xml",
"ref/netstandard1.0/ko/System.ComponentModel.Primitives.xml",
"ref/netstandard1.0/ru/System.ComponentModel.Primitives.xml",
"ref/netstandard1.0/zh-hans/System.ComponentModel.Primitives.xml",
"ref/netstandard1.0/zh-hant/System.ComponentModel.Primitives.xml",
"ref/xamarinios10/_._",
"ref/xamarinmac20/_._",
"ref/xamarintvos10/_._",
"ref/xamarinwatchos10/_._"
]
},
"Hangfire.Raven/2.1.1": {
"type": "project",
"path": "../../src/Hangfire.Raven/project.json",
"msbuildProject": "../../src/Hangfire.Raven/Hangfire.Raven.xproj"
}
},
"projectFileDependencyGroups": {
"": [
"Hangfire.Raven",
"System.ComponentModel.Primitives >= 4.3.0"
],
".NETFramework,Version=v4.5.2": []
},
"tools": {},
"projectFileToolGroups": {}
}
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>2f2c50b7-c556-4a60-81ac-019daf261556</ProjectGuid>
<RootNamespace>Hangfire.Raven.Tests</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>
@@ -0,0 +1,99 @@
using System;
using System.Linq;
using Moq;
using Xunit;
using Hangfire.Raven.JobQueues;
namespace Hangfire.Raven.Tests
{
public class PersistentJobQueueProviderCollectionFacts
{
private static readonly string[] Queues = { "default", "critical" };
private readonly Mock<IPersistentJobQueueProvider> _defaultProvider;
private readonly Mock<IPersistentJobQueueProvider> _provider;
public PersistentJobQueueProviderCollectionFacts()
{
_defaultProvider = new Mock<IPersistentJobQueueProvider>();
_provider = new Mock<IPersistentJobQueueProvider>();
}
[Fact]
public void Ctor_ThrowsAnException_WhenDefaultProviderIsNull()
{
Assert.Throws<ArgumentNullException>(
() => new PersistentJobQueueProviderCollection(null));
}
[Fact]
public void Enumeration_IncludesTheDefaultProvider()
{
var collection = CreateCollection();
var result = collection.ToArray();
Assert.Equal(1, result.Length);
Assert.Same(_defaultProvider.Object, result[0]);
}
[Fact]
public void GetProvider_ReturnsTheDefaultProvider_WhenProviderCanNotBeResolvedByQueue()
{
var collection = CreateCollection();
var provider = collection.GetProvider("queue");
Assert.Same(_defaultProvider.Object, provider);
}
[Fact]
public void Add_ThrowsAnException_WhenProviderIsNull()
{
var collection = CreateCollection();
var exception = Assert.Throws<ArgumentNullException>(
() => collection.Add(null, Queues));
Assert.Equal("provider", exception.ParamName);
}
[Fact]
public void Add_ThrowsAnException_WhenQueuesCollectionIsNull()
{
var collection = CreateCollection();
var exception = Assert.Throws<ArgumentNullException>(
() => collection.Add(_provider.Object, null));
Assert.Equal("queues", exception.ParamName);
}
[Fact]
public void Enumeration_ContainsAddedProvider()
{
var collection = CreateCollection();
collection.Add(_provider.Object, Queues);
Assert.Contains(_provider.Object, collection);
}
[Fact]
public void GetProvider_CanBeResolved_ByAnyQueue()
{
var collection = CreateCollection();
collection.Add(_provider.Object, Queues);
var provider1 = collection.GetProvider("default");
var provider2 = collection.GetProvider("critical");
Assert.NotSame(_defaultProvider.Object, provider1);
Assert.Same(provider1, provider2);
}
private PersistentJobQueueProviderCollection CreateCollection()
{
return new PersistentJobQueueProviderCollection(_defaultProvider.Object);
}
}
}
@@ -0,0 +1,22 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Xunit;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Hangfire.Raven.Tests")]
[assembly: AssemblyTrademark("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("2f2c50b7-c556-4a60-81ac-019daf261556")]
[assembly: CollectionBehavior(DisableTestParallelization = true)]
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,179 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Hangfire.Storage;
using Xunit;
using Hangfire.Raven.DistributedLocks;
using Hangfire.Raven.Storage;
using Hangfire.Raven.Entities;
using Raven.Client.Linq;
using System.Linq;
namespace Hangfire.Raven.Tests
{
public class RavenDistributedLockFacts
{
[Fact]
public void Ctor_ThrowsAnException_WhenResourceIsNull()
{
UseStorage(storage =>
{
var exception = Assert.Throws<ArgumentNullException>(
() => new RavenDistributedLock(storage, null, TimeSpan.Zero, new RavenStorageOptions()));
Assert.Equal("resource", exception.ParamName);
});
}
[Fact]
public void Ctor_ThrowsAnException_WhenStorageIsNull()
{
var exception = Assert.Throws<ArgumentNullException>(
() => new RavenDistributedLock(null, "resource1", TimeSpan.Zero, new RavenStorageOptions()));
Assert.Equal("storage", exception.ParamName);
}
[Fact]
public void Ctor_SetLock_WhenResourceIsNotLocked()
{
UseStorage(storage =>
{
using (new RavenDistributedLock(storage, "resource1", TimeSpan.Zero, new RavenStorageOptions()))
using (var session = storage.Repository.OpenSession())
{
var locksCount = session.Query<DistributedLock>().Where(_ => _.Resource == "resource1").Count();
Assert.Equal(1, locksCount);
}
});
}
[Fact]
public void Ctor_SetReleaseLock_WhenResourceIsNotLocked()
{
UseStorage(storage =>
{
using (new RavenDistributedLock(storage, "resource1", TimeSpan.Zero, new RavenStorageOptions()))
using (var session = storage.Repository.OpenSession())
{
var locksCount = session.Query<DistributedLock>().Where(_ => _.Resource == "resource1").Count();
Assert.Equal(1, locksCount);
}
using (var session = storage.Repository.OpenSession())
{
var locksCountAfter = session.Query<DistributedLock>().Where(_ => _.Resource == "resource1").Count();
Assert.Equal(0, locksCountAfter);
}
});
}
[Fact]
public void Ctor_AcquireLockWithinSameThread_WhenResourceIsLocked()
{
UseStorage(storage =>
{
using (new RavenDistributedLock(storage, "resource1", TimeSpan.Zero, new RavenStorageOptions()))
{
int locksCount;
using (var session = storage.Repository.OpenSession())
{
locksCount = session.Query<DistributedLock>().Where(_ => _.Resource == "resource1").Count();
Assert.Equal(1, locksCount);
}
using (new RavenDistributedLock(storage, "resource1", TimeSpan.Zero, new RavenStorageOptions()))
using (var session = storage.Repository.OpenSession())
{
locksCount = session.Query<DistributedLock>().Where(_ => _.Resource == "resource1").Count();
Assert.Equal(1, locksCount);
}
}
});
}
[Fact]
public void Ctor_ThrowsAnException_WhenResourceIsLocked()
{
UseStorage(storage =>
{
using (new RavenDistributedLock(storage, "resource1", TimeSpan.Zero, new RavenStorageOptions()))
using (var session = storage.Repository.OpenSession())
{
var locksCount = session.Query<DistributedLock>().Where(_ => _.Resource == "resource1").Count();
Assert.Equal(1, locksCount);
Task.Run(() =>
{
Assert.Throws<DistributedLockTimeoutException>(() =>
new RavenDistributedLock(storage, "resource1", TimeSpan.Zero, new RavenStorageOptions()));
}).Wait();
}
});
}
[Fact]
public void Ctor_WaitForLock_SignaledAtLockRelease()
{
UseStorage(storage =>
{
Task.Run(() =>
{
using (new RavenDistributedLock(storage, "resource1", TimeSpan.Zero, new RavenStorageOptions()))
{
Thread.Sleep(TimeSpan.FromSeconds(5));
}
});
// Wait just a bit to make sure the above lock is acuired
Thread.Sleep(TimeSpan.FromSeconds(1));
// Record when we try to aquire the lock
var startTime = DateTime.Now;
using (new RavenDistributedLock(storage, "resource1", TimeSpan.FromSeconds(30), new RavenStorageOptions()))
{
Assert.InRange(DateTime.Now - startTime, TimeSpan.Zero, TimeSpan.FromSeconds(5));
}
});
}
[Fact]
public void Ctor_ThrowsAnException_WhenOptionsIsNull()
{
UseStorage(storage =>
{
var exception = Assert.Throws<ArgumentNullException>(() =>
new RavenDistributedLock(storage, "resource1", TimeSpan.Zero, null));
Assert.Equal("options", exception.ParamName);
});
}
[Fact]
public void Ctor_SetLockExpireAtWorks_WhenResourceIsNotLocked()
{
UseStorage(storage =>
{
using (new RavenDistributedLock(storage, "resource1", TimeSpan.Zero, new RavenStorageOptions { DistributedLockLifetime = TimeSpan.FromSeconds(3) }))
using (var session = storage.Repository.OpenSession())
{
DateTime initialExpireAt = DateTime.UtcNow;
Thread.Sleep(TimeSpan.FromSeconds(5));
DistributedLock lockEntry = session.Query<DistributedLock>().Where(_ => _.Resource == "resource1").FirstOrDefault();
Assert.NotNull(lockEntry);
var expireAt = session.Advanced.GetExpire(lockEntry);
Assert.True(expireAt > initialExpireAt);
}
});
}
private static void UseStorage(Action<RavenStorage> action)
{
using (var repository = new TestRepository())
{
var storage = new RavenStorage(repository);
action(storage);
}
}
}
}
@@ -0,0 +1,166 @@
using Hangfire.Raven.Entities;
using Hangfire.Raven.Storage;
using System;
using System.Linq;
using Xunit;
namespace Hangfire.Raven.Tests
{
public class RavenFetchedJobFacts
{
private const string JobId = "id";
private const string Queue = "queue";
[Fact]
public void Ctor_ThrowsAnException_WhenStorageIsNull()
{
UseStorage(storage =>
{
var exception = Assert.Throws<ArgumentNullException>(
() => new RavenFetchedJob(null, new JobQueue()));
Assert.Equal("storage", exception.ParamName);
});
}
[Fact]
public void Ctor_ThrowsAnException_WhenJobQueueIsNull()
{
UseStorage(storage =>
{
var exception = Assert.Throws<ArgumentNullException>(() => new RavenFetchedJob(storage, null));
Assert.Equal("jobQueue", exception.ParamName);
});
}
[Fact]
public void Ctor_CorrectlySets_AllInstanceProperties()
{
UseStorage(storage =>
{
var fetchedJob = new RavenFetchedJob(storage, new JobQueue { Id = "1", JobId = JobId, Queue = Queue });
Assert.Equal("1", fetchedJob.Id);
Assert.Equal(JobId, fetchedJob.JobId);
Assert.Equal(Queue, fetchedJob.Queue);
});
}
[Fact]
public void RemoveFromQueue_ReallyDeletesTheJobFromTheQueue()
{
UseStorage(storage =>
{
// Arrange
var id = CreateJobQueueRecord(storage, "1", "default");
var processingJob = new RavenFetchedJob(storage, new JobQueue { Id = id, JobId = "1", Queue = "default" });
// Act
processingJob.RemoveFromQueue();
// Assert
using (var session = storage.Repository.OpenSession())
{
var count = session.Query<JobQueue>().Count();
Assert.Equal(0, count);
}
});
}
[Fact]
public void RemoveFromQueue_DoesNotDelete_UnrelatedJobs()
{
UseStorage(storage =>
{
// Arrange
CreateJobQueueRecord(storage, "1", "default");
CreateJobQueueRecord(storage, "1", "critical");
CreateJobQueueRecord(storage, "2", "default");
var fetchedJob = new RavenFetchedJob(storage, new JobQueue { Id = "999", JobId = "1", Queue = "default" });
// Act
fetchedJob.RemoveFromQueue();
// Assert
using (var session = storage.Repository.OpenSession())
{
var count = session.Query<JobQueue>().Count();
Assert.Equal(3, count);
}
});
}
[Fact]
public void Requeue_SetsFetchedAtValueToNull()
{
UseStorage(storage =>
{
// Arrange
var id = CreateJobQueueRecord(storage, "1", "default");
var processingJob = new RavenFetchedJob(storage, new JobQueue { Id = id, JobId = "1", Queue = "default" });
// Act
processingJob.Requeue();
// Assert
using (var session = storage.Repository.OpenSession())
{
var record = session.Query<JobQueue>().Single();
Assert.Null(record.FetchedAt);
}
});
}
[Fact]
public void Dispose_SetsFetchedAtValueToNull_IfThereWereNoCallsToComplete()
{
UseStorage(storage =>
{
// Arrange
var id = CreateJobQueueRecord(storage, "1", "default");
var processingJob = new RavenFetchedJob(storage, new JobQueue { Id = id, JobId = "1", Queue = "default" });
// Act
processingJob.Dispose();
// Assert
using (var session = storage.Repository.OpenSession())
{
var record = session.Query<JobQueue>().Single();
Assert.Null(record.FetchedAt);
}
});
}
private static string CreateJobQueueRecord(RavenStorage storage, string jobId, string queue)
{
var jobQueue = new JobQueue
{
Id = storage.Repository.GetId(typeof(JobQueue), queue, jobId),
JobId = jobId,
Queue = queue,
FetchedAt = DateTime.UtcNow
};
using (var session = storage.Repository.OpenSession())
{
session.Store(jobQueue);
session.SaveChanges();
}
return jobQueue.Id;
}
private static void UseStorage(Action<RavenStorage> action)
{
using (var repository = new TestRepository())
{
var storage = new RavenStorage(repository);
action(storage);
}
}
}
}
@@ -0,0 +1,394 @@
using Hangfire.Raven.Entities;
using Hangfire.Raven.JobQueues;
using Hangfire.Raven.Storage;
using System;
using System.Linq;
using System.Threading;
using Xunit;
namespace Hangfire.Raven.Tests
{
public class RavenJobQueueFacts
{
private static readonly string[] DefaultQueues = { "default" };
[Fact]
public void Ctor_ThrowsAnException_WhenStorageIsNull()
{
var exception = Assert.Throws<ArgumentNullException>(
() => new RavenJobQueue(null, new RavenStorageOptions()));
Assert.Equal("storage", exception.ParamName);
}
[Fact]
public void Ctor_ThrowsAnException_WhenOptionsValueIsNull()
{
UseStorage(storage =>
{
var exception = Assert.Throws<ArgumentNullException>(
() => new RavenJobQueue(storage, null));
Assert.Equal("options", exception.ParamName);
});
}
[Fact]
public void Dequeue_ShouldThrowAnException_WhenQueuesCollectionIsNull()
{
UseStorage(storage =>
{
var queue = CreateJobQueue(storage);
var exception = Assert.Throws<ArgumentNullException>(
() => queue.Dequeue(null, CreateTimingOutCancellationToken()));
Assert.Equal("queues", exception.ParamName);
});
}
[Fact]
public void Dequeue_ShouldThrowAnException_WhenQueuesCollectionIsEmpty()
{
UseStorage(storage =>
{
var queue = CreateJobQueue(storage);
var exception = Assert.Throws<ArgumentException>(
() => queue.Dequeue(new string[0], CreateTimingOutCancellationToken()));
Assert.Equal("queues", exception.ParamName);
});
}
[Fact]
public void Dequeue_ThrowsOperationCanceled_WhenCancellationTokenIsSetAtTheBeginning()
{
UseStorage(storage =>
{
var cts = new CancellationTokenSource();
cts.Cancel();
var queue = CreateJobQueue(storage);
Assert.Throws<OperationCanceledException>(() => queue.Dequeue(DefaultQueues, cts.Token));
});
}
[Fact]
public void Dequeue_ShouldWaitIndefinitely_WhenThereAreNoJobs()
{
UseStorage(storage =>
{
var cts = new CancellationTokenSource(200);
var queue = CreateJobQueue(storage);
Assert.Throws<OperationCanceledException>(() => queue.Dequeue(DefaultQueues, cts.Token));
});
}
[Fact]
public void Dequeue_ShouldFetchAJob_FromTheSpecifiedQueue()
{
// Arrange
UseStorage(storage =>
{
var queue = CreateJobQueue(storage);
var jobQueue = new JobQueue
{
Id = storage.Repository.GetId(typeof(JobQueue), "default", "1"),
JobId = "1",
Queue = "default"
};
using (var session = storage.Repository.OpenSession())
{
session.Store(jobQueue);
session.SaveChanges();
}
// Act
RavenFetchedJob payload = (RavenFetchedJob)queue.Dequeue(DefaultQueues, CreateTimingOutCancellationToken());
// Assert
var id = jobQueue.Id;
Assert.Equal(id, payload.Id);
Assert.Equal("1", payload.JobId);
Assert.Equal("default", payload.Queue);
});
}
[Fact]
public void Dequeue_ShouldLeaveJobInTheQueue_ButSetItsFetchedAtValue()
{
// Arrange
UseStorage(storage =>
{
var queue = CreateJobQueue(storage);
var jobId = Guid.NewGuid().ToString();
var job = new RavenJob
{
Id = storage.Repository.GetId(typeof(RavenJob), jobId),
InvocationData = null,
CreatedAt = DateTime.UtcNow
};
var jobQueue = new JobQueue
{
Id = storage.Repository.GetId(typeof(JobQueue), "default", jobId),
JobId = jobId,
Queue = "default"
};
using (var session = storage.Repository.OpenSession())
{
session.Store(job);
session.Store(jobQueue);
session.SaveChanges();
}
// Act
var payload = queue.Dequeue(DefaultQueues, CreateTimingOutCancellationToken());
// Assert
Assert.NotNull(payload);
using (var session = storage.Repository.OpenSession())
{
var fetchedAt = session.Query<JobQueue>().Where(_ => _.JobId == payload.JobId).FirstOrDefault().FetchedAt;
Assert.NotNull(fetchedAt);
Assert.True(fetchedAt > DateTime.UtcNow.AddMinutes(-1));
}
});
}
[Fact]
public void Dequeue_ShouldFetchATimedOutJobs_FromTheSpecifiedQueue()
{
// Arrange
UseStorage(storage =>
{
var queue = CreateJobQueue(storage);
var jobId = Guid.NewGuid().ToString();
var job = new RavenJob
{
Id = storage.Repository.GetId(typeof(RavenJob), jobId),
InvocationData = null,
CreatedAt = DateTime.UtcNow
};
var jobQueue = new JobQueue
{
Id = storage.Repository.GetId(typeof(JobQueue), "default", jobId),
JobId = jobId,
Queue = "default",
FetchedAt = DateTime.UtcNow.AddDays(-1)
};
using (var session = storage.Repository.OpenSession())
{
session.Store(job);
session.Store(jobQueue);
session.SaveChanges();
}
// Act
var payload = queue.Dequeue(DefaultQueues, CreateTimingOutCancellationToken());
// Assert
Assert.NotEmpty(payload.JobId);
});
}
[Fact]
public void Dequeue_ShouldSetFetchedAt_OnlyForTheFetchedJob()
{
UseStorage(storage =>
{
// Arrange
var queue = CreateJobQueue(storage);
using (var session = storage.Repository.OpenSession())
{
var job1Id = Guid.NewGuid().ToString();
var job1 = new RavenJob
{
Id = storage.Repository.GetId(typeof(RavenJob), job1Id),
InvocationData = null,
CreatedAt = DateTime.UtcNow
};
session.Store(job1);
var job2Id = Guid.NewGuid().ToString();
var job2 = new RavenJob
{
Id = storage.Repository.GetId(typeof(RavenJob), job2Id),
InvocationData = null,
CreatedAt = DateTime.UtcNow
};
session.Store(job2);
session.Store(new JobQueue
{
Id = storage.Repository.GetId(typeof(JobQueue), "default", job1Id),
JobId = job1Id,
Queue = "default"
});
session.Store(new JobQueue
{
Id = storage.Repository.GetId(typeof(JobQueue), "default", job2Id),
JobId = job2Id,
Queue = "default"
});
session.SaveChanges();
}
// Act
var payload = queue.Dequeue(DefaultQueues, CreateTimingOutCancellationToken());
// Assert
using (var session = storage.Repository.OpenSession())
{
var otherJobFetchedAt = session.Query<JobQueue>().Where(_ => _.JobId != payload.JobId).FirstOrDefault().FetchedAt;
Assert.Null(otherJobFetchedAt);
}
});
}
[Fact]
public void Dequeue_ShouldFetchJobs_OnlyFromSpecifiedQueues()
{
UseStorage(storage =>
{
var queue = CreateJobQueue(storage);
using (var session = storage.Repository.OpenSession())
{
var job1Id = Guid.NewGuid().ToString();
var job1 = new RavenJob
{
Id = storage.Repository.GetId(typeof(RavenJob), job1Id),
InvocationData = null,
CreatedAt = DateTime.UtcNow
};
session.Store(job1);
session.Store(new JobQueue
{
Id = storage.Repository.GetId(typeof(JobQueue), "critical", job1Id),
JobId = job1Id,
Queue = "critical"
});
session.SaveChanges();
}
Assert.Throws<OperationCanceledException>(() => queue.Dequeue(DefaultQueues, CreateTimingOutCancellationToken()));
});
}
[Fact]
public void Dequeue_ShouldFetchJobs_FromMultipleQueuesBasedOnQueuePriority()
{
UseStorage(storage =>
{
var queue = CreateJobQueue(storage);
using (var session = storage.Repository.OpenSession())
{
var criticalJobId = Guid.NewGuid().ToString();
var criticalJob = new RavenJob
{
Id = storage.Repository.GetId(typeof(RavenJob), criticalJobId),
InvocationData = null,
CreatedAt = DateTime.UtcNow
};
session.Store(criticalJob);
var defaultJobId = Guid.NewGuid().ToString();
var defaultJob = new RavenJob
{
Id = storage.Repository.GetId(typeof(RavenJob), defaultJobId),
InvocationData = null,
CreatedAt = DateTime.UtcNow
};
session.Store(defaultJob);
session.Store(new JobQueue
{
Id = storage.Repository.GetId(typeof(JobQueue), "default", defaultJobId),
JobId = defaultJobId,
Queue = "default"
});
session.Store(new JobQueue
{
Id = storage.Repository.GetId(typeof(JobQueue), "critical", criticalJobId),
JobId = criticalJobId,
Queue = "critical"
});
session.SaveChanges();
}
var critical = (RavenFetchedJob)queue.Dequeue(
new[] { "critical", "default" },
CreateTimingOutCancellationToken());
Assert.NotNull(critical.JobId);
Assert.Equal("critical", critical.Queue);
var @default = (RavenFetchedJob)queue.Dequeue(
new[] { "critical", "default" },
CreateTimingOutCancellationToken());
Assert.NotNull(@default.JobId);
Assert.Equal("default", @default.Queue);
});
}
[Fact]
public void Enqueue_AddsAJobToTheQueue()
{
UseStorage(storage =>
{
var queue = CreateJobQueue(storage);
queue.Enqueue("default", "1");
using (var session = storage.Repository.OpenSession())
{
var record = session.Query<JobQueue>().Single();
Assert.Equal("1", record.JobId.ToString());
Assert.Equal("default", record.Queue);
Assert.Null(record.FetchedAt);
}
});
}
private static CancellationToken CreateTimingOutCancellationToken()
{
var source = new CancellationTokenSource(TimeSpan.FromSeconds(10));
return source.Token;
}
private static RavenJobQueue CreateJobQueue(RavenStorage storage)
{
return new RavenJobQueue(storage, new RavenStorageOptions());
}
private static void UseStorage(Action<RavenStorage> action)
{
using (var repository = new TestRepository())
{
var storage = new RavenStorage(repository);
action(storage);
}
}
}
}
@@ -0,0 +1,323 @@
using Hangfire.Raven.Entities;
using Hangfire.Raven.JobQueues;
using Hangfire.Raven.Storage;
using Hangfire.Storage;
using System;
using System.Linq;
using Xunit;
namespace Hangfire.Raven.Tests
{
public class RavenJobQueueMonitoringApiFacts
{
private const string QueueName1 = "queueName1";
private const string QueueName2 = "queueName2";
[Fact]
public void Ctor_ThrowsAnException_WhenStorageIsNull()
{
var exception = Assert.Throws<ArgumentNullException>(() => new RavenJobQueueMonitoringApi(null));
Assert.Equal("storage", exception.ParamName);
}
[Fact]
public void GetQueues_ShouldReturnEmpty_WhenNoQueuesExist()
{
UseStorage(storage =>
{
var ravenJobQueueMonitoringApi = CreateRavenJobQueueMonitoringApi(storage);
var queues = ravenJobQueueMonitoringApi.GetQueues();
Assert.Empty(queues);
});
}
[Fact]
public void GetQueues_ShouldReturnOneQueue_WhenOneQueueExists()
{
UseStorage(storage =>
{
var ravenJobQueueMonitoringApi = CreateRavenJobQueueMonitoringApi(storage);
CreateJobQueue(storage, QueueName1, false);
var queues = ravenJobQueueMonitoringApi.GetQueues().ToList();
Assert.Equal(1, queues.Count);
Assert.Equal(QueueName1, queues.First());
});
}
[Fact]
public void GetQueues_ShouldReturnTwoUniqueQueues_WhenThreeNonUniqueQueuesExist()
{
UseStorage(storage =>
{
var ravenJobQueueMonitoringApi = CreateRavenJobQueueMonitoringApi(storage);
CreateJobQueue(storage, QueueName1, false);
CreateJobQueue(storage, QueueName1, false);
CreateJobQueue(storage, QueueName2, false);
var queues = ravenJobQueueMonitoringApi.GetQueues().ToList();
Assert.Equal(2, queues.Count);
Assert.True(queues.Contains(QueueName1));
Assert.True(queues.Contains(QueueName2));
});
}
[Fact]
public void GetEnqueuedJobIds_ShouldReturnEmpty_WheNoQueuesExist()
{
UseStorage(storage =>
{
var mongoJobQueueMonitoringApi = CreateRavenJobQueueMonitoringApi(storage);
var enqueuedJobIds = mongoJobQueueMonitoringApi.GetEnqueuedJobIds(QueueName1, 0, 10);
Assert.Empty(enqueuedJobIds);
});
}
[Fact]
public void GetEnqueuedJobIds_ShouldReturnEmpty_WhenOneJobWithAFetchedStateExists()
{
UseStorage(storage =>
{
var ravenJobQueueMonitoringApi = CreateRavenJobQueueMonitoringApi(storage);
CreateJobQueue(storage, QueueName1, true);
var enqueuedJobIds = ravenJobQueueMonitoringApi.GetEnqueuedJobIds(QueueName1, 0, 10).ToList();
Assert.Empty(enqueuedJobIds);
});
}
[Fact]
public void GetEnqueuedJobIds_ShouldReturnOneJobId_WhenOneJobExists()
{
UseStorage(storage =>
{
var ravenJobQueueMonitoringApi = CreateRavenJobQueueMonitoringApi(storage);
var jobQueue = CreateJobQueue(storage, QueueName1, false);
var enqueuedJobIds = ravenJobQueueMonitoringApi.GetEnqueuedJobIds(QueueName1, 0, 10).ToList();
Assert.Equal(1, enqueuedJobIds.Count);
Assert.Equal(jobQueue.JobId, enqueuedJobIds.First());
});
}
[Fact]
public void GetEnqueuedJobIds_ShouldReturnThreeJobIds_WhenThreeJobsExists()
{
UseStorage(storage =>
{
var ravenJobQueueMonitoringApi = CreateRavenJobQueueMonitoringApi(storage);
var jobQueue = CreateJobQueue(storage, QueueName1, false);
var jobQueue2 = CreateJobQueue(storage, QueueName1, false);
var jobQueue3 = CreateJobQueue(storage, QueueName1, false);
var enqueuedJobIds = ravenJobQueueMonitoringApi.GetEnqueuedJobIds(QueueName1, 0, 10).ToList();
Assert.Equal(3, enqueuedJobIds.Count);
Assert.True(enqueuedJobIds.Contains(jobQueue.JobId));
Assert.True(enqueuedJobIds.Contains(jobQueue2.JobId));
Assert.True(enqueuedJobIds.Contains(jobQueue3.JobId));
});
}
[Fact]
public void GetEnqueuedJobIds_ShouldReturnTwoJobIds_WhenThreeJobsExistsButOnlyTwoInRequestedQueue()
{
UseStorage(storage =>
{
var ravenJobQueueMonitoringApi = CreateRavenJobQueueMonitoringApi(storage);
var jobQueue = CreateJobQueue(storage, QueueName1, false);
var jobQueue2 = CreateJobQueue(storage, QueueName1, false);
CreateJobQueue(storage, QueueName2, false);
var enqueuedJobIds = ravenJobQueueMonitoringApi.GetEnqueuedJobIds(QueueName1, 0, 10).ToList();
Assert.Equal(2, enqueuedJobIds.Count);
Assert.True(enqueuedJobIds.Contains(jobQueue.JobId));
Assert.True(enqueuedJobIds.Contains(jobQueue2.JobId));
});
}
[Fact]
public void GetEnqueuedJobIds_ShouldReturnTwoJobIds_WhenThreeJobsExistsButLimitIsSet()
{
UseStorage(storage =>
{
var ravenJobQueueMonitoringApi = CreateRavenJobQueueMonitoringApi(storage);
var jobQueue = CreateJobQueue(storage, QueueName1, false);
var jobQueue2 = CreateJobQueue(storage, QueueName1, false);
CreateJobQueue(storage, QueueName1, false);
var enqueuedJobIds = ravenJobQueueMonitoringApi.GetEnqueuedJobIds(QueueName1, 0, 2).ToList();
Assert.Equal(2, enqueuedJobIds.Count);
Assert.True(enqueuedJobIds.Contains(jobQueue.JobId));
Assert.True(enqueuedJobIds.Contains(jobQueue2.JobId));
});
}
[Fact]
public void GetFetchedJobIds_ShouldReturnEmpty_WheNoQueuesExist()
{
UseStorage(storage =>
{
var ravenJobQueueMonitoringApi = CreateRavenJobQueueMonitoringApi(storage);
var enqueuedJobIds = ravenJobQueueMonitoringApi.GetFetchedJobIds(QueueName1, 0, 10);
Assert.Empty(enqueuedJobIds);
});
}
[Fact]
public void GetFetchedJobIds_ShouldReturnEmpty_WhenOneJobWithNonFetchedStateExists()
{
UseStorage(storage =>
{
var ravenJobQueueMonitoringApi = CreateRavenJobQueueMonitoringApi(storage);
CreateJobQueue(storage, QueueName1, false);
var enqueuedJobIds = ravenJobQueueMonitoringApi.GetFetchedJobIds(QueueName1, 0, 10).ToList();
Assert.Empty(enqueuedJobIds);
});
}
[Fact]
public void GetFetchedJobIds_ShouldReturnOneJobId_WhenOneJobExists()
{
UseStorage(storage =>
{
var ravenJobQueueMonitoringApi = CreateRavenJobQueueMonitoringApi(storage);
var jobQueue = CreateJobQueue(storage, QueueName1, true);
var enqueuedJobIds = ravenJobQueueMonitoringApi.GetFetchedJobIds(QueueName1, 0, 10).ToList();
Assert.Equal(1, enqueuedJobIds.Count);
Assert.Equal(jobQueue.JobId, enqueuedJobIds.First());
});
}
[Fact]
public void GetFetchedJobIds_ShouldReturnThreeJobIds_WhenThreeJobsExists()
{
UseStorage(storage =>
{
var ravenJobQueueMonitoringApi = CreateRavenJobQueueMonitoringApi(storage);
var jobQueue = CreateJobQueue(storage, QueueName1, true);
var jobQueue2 = CreateJobQueue(storage, QueueName1, true);
var jobQueue3 = CreateJobQueue(storage, QueueName1, true);
var enqueuedJobIds = ravenJobQueueMonitoringApi.GetFetchedJobIds(QueueName1, 0, 10).ToList();
Assert.Equal(3, enqueuedJobIds.Count);
Assert.True(enqueuedJobIds.Contains(jobQueue.JobId));
Assert.True(enqueuedJobIds.Contains(jobQueue2.JobId));
Assert.True(enqueuedJobIds.Contains(jobQueue3.JobId));
});
}
[Fact]
public void GetFetchedJobIds_ShouldReturnTwoJobIds_WhenThreeJobsExistsButOnlyTwoInRequestedQueue()
{
UseStorage(storage =>
{
var ravenJobQueueMonitoringApi = CreateRavenJobQueueMonitoringApi(storage);
var jobQueue = CreateJobQueue(storage, QueueName1, true);
var jobQueue2 = CreateJobQueue(storage, QueueName1, true);
CreateJobQueue(storage, QueueName2, true);
var enqueuedJobIds = ravenJobQueueMonitoringApi.GetFetchedJobIds(QueueName1, 0, 10).ToList();
Assert.Equal(2, enqueuedJobIds.Count);
Assert.True(enqueuedJobIds.Contains(jobQueue.JobId));
Assert.True(enqueuedJobIds.Contains(jobQueue2.JobId));
});
}
[Fact]
public void GetFetchedJobIds_ShouldReturnTwoJobIds_WhenThreeJobsExistsButLimitIsSet()
{
UseStorage(storage =>
{
var ravenJobQueueMonitoringApi = CreateRavenJobQueueMonitoringApi(storage);
var jobQueue = CreateJobQueue(storage, QueueName1, true);
var jobQueue2 = CreateJobQueue(storage, QueueName1, true);
CreateJobQueue(storage, QueueName1, true);
var enqueuedJobIds = ravenJobQueueMonitoringApi.GetFetchedJobIds(QueueName1, 0, 2).ToList();
Assert.Equal(2, enqueuedJobIds.Count);
Assert.True(enqueuedJobIds.Contains(jobQueue.JobId));
Assert.True(enqueuedJobIds.Contains(jobQueue2.JobId));
});
}
private static JobQueue CreateJobQueue(RavenStorage storage, string queue, bool isFetched)
{
var jobId = Guid.NewGuid().ToString();
var job = new RavenJob
{
Id = storage.Repository.GetId(typeof(RavenJob), jobId),
CreatedAt = DateTime.UtcNow,
StateData = new StateData()
};
var jobQueue = new JobQueue
{
Id = storage.Repository.GetId(typeof(JobQueue), queue, jobId),
Queue = queue,
JobId = jobId
};
if (isFetched)
{
jobQueue.FetchedAt = DateTime.UtcNow.AddDays(-1);
}
using (var session = storage.Repository.OpenSession())
{
session.Store(job);
session.Store(jobQueue);
session.SaveChanges();
}
return jobQueue;
}
private static RavenJobQueueMonitoringApi CreateRavenJobQueueMonitoringApi(RavenStorage storage)
{
return new RavenJobQueueMonitoringApi(storage);
}
private static void UseStorage(Action<RavenStorage> action)
{
using (var repository = new TestRepository())
{
var storage = new RavenStorage(repository);
action(storage);
}
}
}
}
@@ -0,0 +1,304 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Hangfire.Common;
using Hangfire.States;
using Hangfire.Storage;
using Moq;
using Xunit;
using Hangfire.Raven.JobQueues;
using Hangfire.Raven.Storage;
using Hangfire.Raven.Entities;
using System.Linq;
namespace Hangfire.Raven.Tests
{
public class RavenMonitoringApiFacts
{
private const string DefaultQueue = "default";
private const string FetchedStateName = "Fetched";
private const int From = 0;
private const int PerPage = 5;
private readonly Mock<IPersistentJobQueue> _queue;
private readonly Mock<IPersistentJobQueueProvider> _provider;
private readonly Mock<IPersistentJobQueueMonitoringApi> _persistentJobQueueMonitoringApi;
private readonly PersistentJobQueueProviderCollection _providers;
public RavenMonitoringApiFacts()
{
_queue = new Mock<IPersistentJobQueue>();
_persistentJobQueueMonitoringApi = new Mock<IPersistentJobQueueMonitoringApi>();
_provider = new Mock<IPersistentJobQueueProvider>();
_provider.Setup(x => x.GetJobQueue()).Returns(_queue.Object);
_provider.Setup(x => x.GetJobQueueMonitoringApi())
.Returns(_persistentJobQueueMonitoringApi.Object);
_providers = new PersistentJobQueueProviderCollection(_provider.Object);
}
[Fact]
public void GetStatistics_ReturnsZero_WhenNoJobsExist()
{
UseMonitoringApi((repository, monitoringApi) =>
{
var result = monitoringApi.GetStatistics();
Assert.Equal(0, result.Enqueued);
Assert.Equal(0, result.Failed);
Assert.Equal(0, result.Processing);
Assert.Equal(0, result.Scheduled);
});
}
[Fact]
public void GetStatistics_ReturnsExpectedCounts_WhenJobsExist()
{
UseMonitoringApi((repository, monitoringApi) =>
{
CreateJobInState(repository, "1", EnqueuedState.StateName);
CreateJobInState(repository, "2", EnqueuedState.StateName);
CreateJobInState(repository, "4", FailedState.StateName);
CreateJobInState(repository, "5", ProcessingState.StateName);
CreateJobInState(repository, "6", ScheduledState.StateName);
CreateJobInState(repository, "7", ScheduledState.StateName);
var result = monitoringApi.GetStatistics();
Assert.Equal(2, result.Enqueued);
Assert.Equal(1, result.Failed);
Assert.Equal(1, result.Processing);
Assert.Equal(2, result.Scheduled);
});
}
[Fact]
public void JobDetails_ReturnsNull_WhenThereIsNoSuchJob()
{
UseMonitoringApi((repository, monitoringApi) =>
{
var result = monitoringApi.JobDetails("547527");
Assert.Null(result);
});
}
[Fact]
public void JobDetails_ReturnsResult_WhenJobExists()
{
UseMonitoringApi((repository, monitoringApi) =>
{
var job1 = CreateJobInState(repository, "1", EnqueuedState.StateName);
var result = monitoringApi.JobDetails(job1.Id.Split(new[] { '/' }, 2)[1]);
Assert.NotNull(result);
Assert.NotNull(result.Job);
Assert.Equal("Arguments", result.Job.Args[0]);
Assert.True(DateTime.UtcNow.AddMinutes(-1) < result.CreatedAt);
Assert.True(result.CreatedAt < DateTime.UtcNow.AddMinutes(1));
});
}
[Fact]
public void EnqueuedJobs_ReturnsEmpty_WhenThereIsNoJobs()
{
UseMonitoringApi((repository, monitoringApi) =>
{
var jobIds = new List<string>();
_persistentJobQueueMonitoringApi.Setup(x => x
.GetEnqueuedJobIds(DefaultQueue, From, PerPage))
.Returns(jobIds);
var resultList = monitoringApi.EnqueuedJobs(DefaultQueue, From, PerPage);
Assert.Empty(resultList);
});
}
[Fact]
public void EnqueuedJobs_ReturnsSingleJob_WhenOneJobExistsThatIsNotFetched()
{
UseMonitoringApi((repository, monitoringApi) =>
{
var unfetchedJob = CreateJobInState(repository, "1", EnqueuedState.StateName);
var jobIds = new List<string> { unfetchedJob.Id };
_persistentJobQueueMonitoringApi.Setup(x => x
.GetEnqueuedJobIds(DefaultQueue, From, PerPage))
.Returns(jobIds.Select(x => x.Split(new[] { '/' }, 2)[1]));
var resultList = monitoringApi.EnqueuedJobs(DefaultQueue, From, PerPage);
Assert.Equal(1, resultList.Count);
});
}
[Fact]
public void EnqueuedJobs_ReturnsEmpty_WhenOneJobExistsThatIsFetched()
{
UseMonitoringApi((repository, monitoringApi) =>
{
var fetchedJob = CreateJobInState(repository, "1", FetchedStateName);
var jobIds = new List<string> { fetchedJob.Id };
_persistentJobQueueMonitoringApi.Setup(x => x
.GetEnqueuedJobIds(DefaultQueue, From, PerPage))
.Returns(jobIds.Select(x => x.Split(new[] { '/' }, 2)[1]));
var resultList = monitoringApi.EnqueuedJobs(DefaultQueue, From, PerPage);
Assert.Empty(resultList);
});
}
[Fact]
public void EnqueuedJobs_ReturnsUnfetchedJobsOnly_WhenMultipleJobsExistsInFetchedAndUnfetchedStates()
{
UseMonitoringApi((repository, monitoringApi) =>
{
var unfetchedJob = CreateJobInState(repository, "1", EnqueuedState.StateName);
var unfetchedJob2 = CreateJobInState(repository, "2", EnqueuedState.StateName);
var fetchedJob = CreateJobInState(repository, "3", FetchedStateName);
var jobIds = new List<string> { unfetchedJob.Id, unfetchedJob2.Id, fetchedJob.Id };
_persistentJobQueueMonitoringApi.Setup(x => x
.GetEnqueuedJobIds(DefaultQueue, From, PerPage))
.Returns(jobIds.Select(x => x.Split(new[] { '/' }, 2)[1]));
var resultList = monitoringApi.EnqueuedJobs(DefaultQueue, From, PerPage);
Assert.Equal(2, resultList.Count);
});
}
[Fact]
public void FetchedJobs_ReturnsEmpty_WhenThereIsNoJobs()
{
UseMonitoringApi((repository, monitoringApi) =>
{
var jobIds = new List<string>();
_persistentJobQueueMonitoringApi.Setup(x => x
.GetFetchedJobIds(DefaultQueue, From, PerPage))
.Returns(jobIds);
var resultList = monitoringApi.FetchedJobs(DefaultQueue, From, PerPage);
Assert.Empty(resultList);
});
}
[Fact]
public void FetchedJobs_ReturnsSingleJob_WhenOneJobExistsThatIsFetched()
{
UseMonitoringApi((repository, monitoringApi) =>
{
var fetchedJob = CreateJobInState(repository, "1", FetchedStateName);
var jobIds = new List<string> { fetchedJob.Id };
_persistentJobQueueMonitoringApi.Setup(x => x
.GetFetchedJobIds(DefaultQueue, From, PerPage))
.Returns(jobIds.Select(x => x.Split(new[] { '/' }, 2)[1]));
var resultList = monitoringApi.FetchedJobs(DefaultQueue, From, PerPage);
Assert.Equal(1, resultList.Count);
});
}
[Fact]
public void FetchedJobs_ReturnsEmpty_WhenOneJobExistsThatIsNotFetched()
{
UseMonitoringApi((repository, monitoringApi) =>
{
var unfetchedJob = CreateJobInState(repository, "1", EnqueuedState.StateName);
var jobIds = new List<string> { unfetchedJob.Id };
_persistentJobQueueMonitoringApi.Setup(x => x
.GetFetchedJobIds(DefaultQueue, From, PerPage))
.Returns(jobIds.Select(x => x.Split(new[] { '/' }, 2)[1]));
var resultList = monitoringApi.FetchedJobs(DefaultQueue, From, PerPage);
Assert.Empty(resultList);
});
}
[Fact]
public void FetchedJobs_ReturnsFetchedJobsOnly_WhenMultipleJobsExistsInFetchedAndUnfetchedStates()
{
UseMonitoringApi((repository, monitoringApi) =>
{
var fetchedJob = CreateJobInState(repository, "1", FetchedStateName);
var fetchedJob2 = CreateJobInState(repository, "2", FetchedStateName);
var unfetchedJob = CreateJobInState(repository, "3", EnqueuedState.StateName);
var jobIds = new List<string> { fetchedJob.Id, fetchedJob2.Id, unfetchedJob.Id };
_persistentJobQueueMonitoringApi.Setup(x => x
.GetFetchedJobIds(DefaultQueue, From, PerPage))
.Returns(jobIds.Select(x => x.Split(new[] { '/' }, 2)[1]));
var resultList = monitoringApi.FetchedJobs(DefaultQueue, From, PerPage);
Assert.Equal(2, resultList.Count);
});
}
public static void SampleMethod(string arg)
{
Debug.WriteLine(arg);
}
private void UseMonitoringApi(Action<IRepository, RavenStorageMonitoringApi> action)
{
using (var repository = new TestRepository())
{
var storage = new Mock<RavenStorage>(repository);
storage.Setup(x => x.QueueProviders).Returns(_providers);
action(repository, new RavenStorageMonitoringApi(storage.Object));
}
}
private RavenJob CreateJobInState(IRepository repository, string jobId, string stateName)
{
var job = Job.FromExpression(() => SampleMethod("Arguments"));
var ravenJob = new RavenJob
{
Id = repository.GetId(typeof(RavenJob), jobId),
InvocationData = InvocationData.Serialize(job),
CreatedAt = DateTime.UtcNow,
StateData = new StateData
{
Name = stateName,
Data =
stateName == EnqueuedState.StateName
? new Dictionary<string, string> { ["EnqueuedAt"] = $"{DateTime.UtcNow:o}" }
: new Dictionary<string, string>(),
}
};
var jobQueue = new JobQueue
{
FetchedAt = null,
Id = repository.GetId(typeof(JobQueue), DefaultQueue, jobId),
JobId = jobId,
Queue = DefaultQueue
};
if (stateName == FetchedStateName)
{
jobQueue.FetchedAt = DateTime.UtcNow;
}
using (var session = repository.OpenSession())
{
session.Store(ravenJob);
session.Store(jobQueue);
session.SaveChanges();
}
return ravenJob;
}
}
}
@@ -0,0 +1,59 @@
using System;
using Hangfire.Storage;
using Xunit;
using Hangfire.Raven.Storage;
namespace Hangfire.Raven.Tests
{
public class RavenStorageFacts
{
[Fact]
public void Ctor_ThrowsAnException_WhenRepositoryIsNull()
{
var exception = Assert.Throws<ArgumentNullException>(() => new RavenStorage((IRepository)null, new RavenStorageOptions()));
Assert.Equal("repository", exception.ParamName);
}
[Fact]
public void Ctor_ThrowsAnException_WhenOptionsValueIsNull()
{
using (var repository = new TestRepository())
{
var exception = Assert.Throws<ArgumentNullException>(() => new RavenStorage(repository, null));
Assert.Equal("options", exception.ParamName);
}
}
[Fact]
public void GetMonitoringApi_ReturnsNonNullInstance()
{
UseStorage(storage =>
{
IMonitoringApi api = storage.GetMonitoringApi();
Assert.NotNull(api);
});
}
[Fact]
public void GetConnection_ReturnsNonNullInstance()
{
UseStorage(storage =>
{
using (IStorageConnection connection = storage.GetConnection())
{
Assert.NotNull(connection);
}
});
}
private static void UseStorage(Action<RavenStorage> action)
{
using (var repository = new TestRepository())
{
action(new RavenStorage(repository));
}
}
}
}
@@ -0,0 +1,90 @@
using Hangfire.Raven.Storage;
using System;
using System.Collections.Generic;
using System.Linq;
using Xunit;
namespace Hangfire.Raven.Tests
{
public class RavenStorageOptionsFacts
{
[Fact]
public void Ctor_SetsTheDefaultOptions()
{
RavenStorageOptions options = new RavenStorageOptions();
Assert.True(options.InvisibilityTimeout > TimeSpan.Zero);
}
[Fact]
public void Ctor_SetsTheDefaultOptions_ShouldGenerateClientId()
{
var options = new RavenStorageOptions();
Assert.False(String.IsNullOrWhiteSpace(options.ClientId));
}
[Fact]
public void Ctor_SetsTheDefaultOptions_ShouldGenerateUniqueClientId()
{
var options1 = new RavenStorageOptions();
var options2 = new RavenStorageOptions();
var options3 = new RavenStorageOptions();
IEnumerable<string> result = new[] { options1.ClientId, options2.ClientId, options3.ClientId }.Distinct();
Assert.Equal(3, result.Count());
}
[Fact]
public void Set_QueuePollInterval_ShouldThrowAnException_WhenGivenIntervalIsEqualToZero()
{
var options = new RavenStorageOptions();
Assert.Throws<ArgumentException>(
() => options.QueuePollInterval = TimeSpan.Zero);
}
[Fact]
public void Set_QueuePollInterval_ShouldThrowAnException_WhenGivenIntervalIsNegative()
{
var options = new RavenStorageOptions();
Assert.Throws<ArgumentException>(
() => options.QueuePollInterval = TimeSpan.FromSeconds(-1));
}
[Fact]
public void Set_QueuePollInterval_SetsTheValue()
{
var options = new RavenStorageOptions
{
QueuePollInterval = TimeSpan.FromSeconds(1)
};
Assert.Equal(TimeSpan.FromSeconds(1), options.QueuePollInterval);
}
[Fact]
public void Set_DistributedLockLifetime_ShouldThrowAnException_WhenGivenValueIsEqualToZero()
{
var options = new RavenStorageOptions();
Assert.Throws<ArgumentException>(
() => options.DistributedLockLifetime = TimeSpan.Zero);
}
[Fact]
public void Set_DistributedLockLifetime_ShouldThrowAnException_WhenGivenValueIsNegative()
{
var options = new RavenStorageOptions();
Assert.Throws<ArgumentException>(
() => options.DistributedLockLifetime = TimeSpan.FromSeconds(-1));
}
[Fact]
public void Set_DistributedLockLifetime_SetsTheValue()
{
var options = new RavenStorageOptions
{
DistributedLockLifetime = TimeSpan.FromSeconds(1)
};
Assert.Equal(TimeSpan.FromSeconds(1), options.DistributedLockLifetime);
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using Raven.Abstractions.Data;
using Raven.Client;
using Raven.Client.Indexes;
using Raven.Client.Embedded;
using Hangfire.Raven.Listeners;
namespace Hangfire.Raven.Tests
{
public class TestRepository : IRepository
{
private readonly EmbeddableDocumentStore _documentStore;
public TestRepository()
{
_documentStore = new EmbeddableDocumentStore
{
RunInMemory = true,
DefaultDatabase = "Hangfire-Raven-Tests",
DataDirectory = @"~\Databases\Hangfire-Raven-Tests"
};
_documentStore.Listeners.RegisterListener(new NoStaleQueriesListener());
_documentStore.Listeners.RegisterListener(new TakeNewestConflictResolutionListener());
_documentStore.Initialize();
new RavenDocumentsByEntityName().Execute(_documentStore.DatabaseCommands, _documentStore.Conventions);
}
public void Create()
{
}
public void Destroy()
{
}
public void Dispose()
{
_documentStore.Dispose();
}
public IDisposable DocumentChange(Type documentType, Action<DocumentChangeNotification> action)
{
return _documentStore.Changes().ForDocumentsStartingWith(GetId(documentType, ""))
.Subscribe(new RepositoryObserver<DocumentChangeNotification>(action));
}
public IDisposable DocumentChange(Type documentType, string suffix, Action<DocumentChangeNotification> action)
{
return _documentStore.Changes().ForDocumentsStartingWith(GetId(documentType, string.Format("{0}/", suffix)))
.Subscribe(new RepositoryObserver<DocumentChangeNotification>(action));
}
public void ExecuteIndexes(List<AbstractIndexCreationTask> indexes)
{
_documentStore.ExecuteIndexes(indexes);
}
public FacetResults GetFacets(string index, IndexQuery query, List<Facet> facets)
{
return _documentStore.DatabaseCommands.GetFacets(index, query, facets);
}
public string GetId(Type type, params string[] id)
{
return _documentStore.Conventions.FindFullDocumentKeyFromNonStringIdentifier(string.Join("/", id), type, false);
}
public IAsyncDocumentSession OpenAsyncSession()
{
return _documentStore.OpenAsyncSession();
}
public IDocumentSession OpenSession()
{
return _documentStore.OpenSession();
}
}
}
+17
View File
@@ -0,0 +1,17 @@
{
"version": "1.0.0-*",
"testRunner": "xunit",
"dependencies": {
"Hangfire.Raven": { "target": "project" },
"Moq": "4.7.1",
"xunit": "2.2.0",
"dotnet-test-xunit": "2.2.0-preview2-build1029",
"RavenDB.Database": "3.5.3-patch-35190"
},
"frameworks": {
"net452": {
}
}
}
File diff suppressed because it is too large Load Diff
@@ -1,116 +1,219 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Hangfire.Annotations;
using Hangfire.Raven.Entities;
using Hangfire.Raven.Storage;
namespace Hangfire.Raven.DistributedLocks
{
public class RavenDistributedLock : IDisposable
{
private const int CommandTimeoutAdditionSeconds = 1;
private readonly object _lock = new object();
private RavenStorage _storage;
private string _resource;
private DistributedLock _distributedLock;
private Timer _heartbeatTimer;
private TimeSpan _timeout;
private readonly List<string> _skipLocks = new List<string>()
{
"HangFire/job:"
};
public RavenDistributedLock([NotNull] RavenStorage storage, [NotNull] string resource, TimeSpan timeout)
{
storage.ThrowIfNull("storage");
resource.ThrowIfNull("resource");
_timeout = timeout;
_storage = storage;
_resource = resource;
// -- Skip some locks
if (!_skipLocks.Any(a => _resource.StartsWith(a)))
Lock();
}
public void Dispose()
{
Release();
}
private void Lock()
{
using (var session = _storage.Repository.OpenSession()) {
_distributedLock = new DistributedLock() {
ClientId = _storage.Options.ClientId,
Resource = _resource
};
session.Store(_distributedLock);
session.Advanced.AddExpire(_distributedLock, DateTime.UtcNow + _timeout);
try {
// Blocking session!
session.Advanced.UseOptimisticConcurrency = true;
session.SaveChanges();
} catch (Exception e) {
_distributedLock = null;
throw new RavenDistributedLockException("Lock already given.", e);
}
}
Heartbeat();
}
private void Release()
{
lock (_lock) {
if (_distributedLock != null) {
// Non blocking session!
try {
using (var session = _storage.Repository.OpenSession()) {
session.Delete(_distributedLock.Id);
session.SaveChanges();
}
} catch {
Console.WriteLine("Unable to delete lock: {0}", _resource);
}
_distributedLock = null;
}
// Stop timer
if (_heartbeatTimer != null) {
_heartbeatTimer.Dispose();
_heartbeatTimer = null;
}
}
}
private void Heartbeat()
{
Console.WriteLine(".Starting heartbeat for resource: {0}", _resource);
TimeSpan timerInterval = TimeSpan.FromMilliseconds(_timeout.TotalMilliseconds / 3);
_heartbeatTimer = new Timer(state => {
try {
Console.WriteLine("..Heartbeat for resource {0}", _resource);
using (var session = _storage.Repository.OpenSession()) {
var distributedLock = session.Load<DistributedLock>(_distributedLock.Id);
session.Advanced.AddExpire(distributedLock, DateTime.UtcNow + _timeout);
session.SaveChanges();
}
} catch (Exception ex) {
Console.WriteLine("...Unable to update heartbeat on the resource '{0}'", ex, _resource);
Release();
}
}, null, timerInterval, timerInterval);
}
}
using System;
using System.Collections.Generic;
using System.Threading;
using Hangfire.Raven.Entities;
using Hangfire.Raven.Storage;
using Hangfire.Logging;
using Hangfire.Storage;
using Raven.Abstractions.Exceptions;
namespace Hangfire.Raven.DistributedLocks
{
public class RavenDistributedLock : IDisposable
{
private static readonly ILog Logger = LogProvider.For<RavenDistributedLock>();
private static readonly ThreadLocal<Dictionary<string, int>> AcquiredLocks
= new ThreadLocal<Dictionary<string, int>>(() => new Dictionary<string, int>());
private RavenStorage _storage;
private string _resource;
private readonly RavenStorageOptions _options;
private DistributedLock _distributedLock;
private Timer _heartbeatTimer;
private bool _completed;
private readonly object _lockObject = new object();
private string EventWaitHandleName => $@"{GetType().FullName}.{_resource}";
public RavenDistributedLock(RavenStorage storage, string resource, TimeSpan timeout, RavenStorageOptions options)
{
storage.ThrowIfNull("storage");
if (string.IsNullOrEmpty(resource))
{
throw new ArgumentNullException(nameof(resource));
}
if (timeout.TotalSeconds > int.MaxValue)
{
throw new ArgumentException($"The timeout specified is too large. Please supply a timeout equal to or less than {int.MaxValue} seconds", nameof(timeout));
}
options.ThrowIfNull("options");
_storage = storage;
_resource = resource;
_options = options;
if (!AcquiredLocks.Value.ContainsKey(_resource) || AcquiredLocks.Value[_resource] == 0)
{
Acquire(timeout);
AcquiredLocks.Value[_resource] = 1;
StartHeartBeat();
}
else
{
AcquiredLocks.Value[_resource]++;
}
}
public void Dispose()
{
if (_completed)
{
return;
}
_completed = true;
if (!AcquiredLocks.Value.ContainsKey(_resource))
{
return;
}
AcquiredLocks.Value[_resource]--;
if (AcquiredLocks.Value[_resource] > 0)
{
return;
}
lock (_lockObject)
{
AcquiredLocks.Value.Remove(_resource);
if (_heartbeatTimer != null)
{
_heartbeatTimer.Dispose();
_heartbeatTimer = null;
}
Release();
}
}
private void Acquire(TimeSpan timeout)
{
try
{
var isLockAcquired = false;
var now = DateTime.Now;
var lockTimeoutTime = now.Add(timeout);
while (!isLockAcquired && (lockTimeoutTime >= now))
{
using (var session = _storage.Repository.OpenSession())
{
_distributedLock = new DistributedLock()
{
ClientId = _storage.Options.ClientId,
Resource = _resource
};
session.Store(_distributedLock);
session.Advanced.AddExpire(_distributedLock, DateTime.UtcNow.Add(_options.DistributedLockLifetime));
try
{
// Blocking session!
session.Advanced.UseOptimisticConcurrency = true;
session.SaveChanges();
isLockAcquired = true;
}
catch (ConcurrencyException)
{
_distributedLock = null;
try
{
var eventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset, EventWaitHandleName);
eventWaitHandle.WaitOne((int)timeout.TotalMilliseconds / 10);
}
catch (PlatformNotSupportedException)
{
Thread.Sleep((int)timeout.TotalMilliseconds / 10);
}
now = DateTime.Now;
}
}
}
if (!isLockAcquired)
{
throw new DistributedLockTimeoutException(_resource);
}
}
catch (DistributedLockTimeoutException)
{
throw;
}
catch (Exception ex)
{
throw new RavenDistributedLockException($"Could not place a lock on the resource \'{_resource}\': Check inner exception for details.", ex);
}
}
private void Release()
{
try
{
if (_distributedLock != null)
{
// Non blocking session!
using (var session = _storage.Repository.OpenSession())
{
session.Delete(_distributedLock.Id);
session.SaveChanges();
_distributedLock = null;
}
}
EventWaitHandle eventWaitHandler;
if (EventWaitHandle.TryOpenExisting(EventWaitHandleName, out eventWaitHandler))
{
eventWaitHandler.Set();
}
}
catch (PlatformNotSupportedException)
{
}
catch (Exception ex)
{
_distributedLock = null;
throw new RavenDistributedLockException($"Could not release a lock on the resource \'{_resource}\': Check inner exception for details.", ex);
}
}
private void StartHeartBeat()
{
Logger.InfoFormat(".Starting heartbeat for resource: {0}", _resource);
TimeSpan timerInterval = TimeSpan.FromMilliseconds(_options.DistributedLockLifetime.TotalMilliseconds / 5);
_heartbeatTimer = new Timer(state =>
{
lock (_lockObject)
{
try
{
Logger.InfoFormat("..Heartbeat for resource {0}", _resource);
using (var session = _storage.Repository.OpenSession())
{
var distributedLock = session.Load<DistributedLock>(_distributedLock.Id);
session.Advanced.AddExpire(distributedLock, DateTime.UtcNow.Add(_options.DistributedLockLifetime));
session.SaveChanges();
}
}
catch (Exception ex)
{
Logger.ErrorFormat("...Unable to update heartbeat on the resource '{0}'. {1}", _resource, ex);
Release();
}
}
}, null, timerInterval, timerInterval);
}
}
}
@@ -2,14 +2,7 @@
{
public class DistributedLock
{
public string Id {
get {
return string.Format("DistributedLocks/{0}", Resource);
}
set {
}
}
public string Id => $"DistributedLocks/{Resource}";
public string Resource { get; set; }
public string ClientId { get; set; }
+9 -5
View File
@@ -1,11 +1,15 @@
namespace Hangfire.Raven.Entities
using System.Collections.Generic;
namespace Hangfire.Raven.Entities
{
public class RavenList
{
public RavenList()
{
Values = new List<string>();
}
public string Id { get; set; }
public string Key { get; set; }
public string Value { get; set; }
public List<string> Values { get; set; }
}
}
@@ -6,7 +6,7 @@ namespace Hangfire.Raven.Extensions
{
public static bool DatabaseExists(this IDocumentStore documentStore, string database)
{
var result = documentStore.DatabaseCommands.Head("Raven/Databases/" + database);
var result = documentStore.DatabaseCommands.ForSystemDatabase().Head("Raven/Databases/" + database);
return (result != null);
}
+21
View File
@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using Raven.Abstractions.Data;
using Raven.Client;
using Raven.Client.Indexes;
namespace Hangfire.Raven
{
public interface IRepository : IDisposable
{
void Create();
void Destroy();
IDisposable DocumentChange(Type documentType, Action<DocumentChangeNotification> action);
IDisposable DocumentChange(Type documentType, string suffix, Action<DocumentChangeNotification> action);
void ExecuteIndexes(List<AbstractIndexCreationTask> indexes);
FacetResults GetFacets(string index, IndexQuery query, List<Facet> facets);
string GetId(Type type, params string[] id);
IAsyncDocumentSession OpenAsyncSession();
IDocumentSession OpenSession();
}
}
@@ -0,0 +1,32 @@
using Hangfire.Raven.Entities;
using Raven.Abstractions.Indexing;
using Raven.Client.Indexes;
using System;
using System.Linq;
namespace Hangfire.Raven.Indexes
{
public class Hangfire_JobQueues
: AbstractIndexCreationTask<JobQueue>
{
public class Mapping
{
public DateTime? FetchedAt { get; set; }
public string Queue { get; set; }
public string JobId { get; set; }
}
public Hangfire_JobQueues()
{
Map = results => from result in results
select new Mapping
{
Queue = result.Queue,
FetchedAt = result.FetchedAt,
JobId = result.JobId
};
Analyze("Queue", "WhitespaceAnalyzer");
Sort("FetchedAt", SortOptions.String);
}
}
}
@@ -0,0 +1,26 @@
using System;
using System.Linq;
using Hangfire.Raven.Entities;
using Raven.Client.Indexes;
using Raven.Abstractions.Indexing;
namespace Hangfire.Raven.Indexes
{
public class Hangfire_RavenServers
: AbstractIndexCreationTask<RavenServer>
{
public class Mapping
{
public DateTime LastHeartbeat { get; set; }
}
public Hangfire_RavenServers()
{
Map = results => from result in results
select new Mapping {
LastHeartbeat = result.LastHeartbeat
};
Sort("LastHeartbeat", SortOptions.String);
}
}
}
+122 -106
View File
@@ -1,106 +1,122 @@
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading;
using Hangfire.Annotations;
using Hangfire.Raven.Entities;
using Hangfire.Raven.Storage;
using Hangfire.Storage;
using Raven.Abstractions.Data;
namespace Hangfire.Raven.JobQueues
{
public class RavenJobQueue
: IPersistentJobQueue
{
private readonly RavenStorage _storage;
private readonly RavenStorageOptions _options;
private BlockingCollection<string> _queue;
public RavenJobQueue([NotNull] RavenStorage storage, RavenStorageOptions options)
{
storage.ThrowIfNull("storage");
options.ThrowIfNull("options");
_storage = storage;
_options = options;
_queue = new BlockingCollection<string>();
using (var session = _storage.Repository.OpenSession()) {
var missed = session.Advanced.LoadStartingWith<JobQueue>(Repository.GetId(typeof(JobQueue), ""));
foreach (var miss in missed) {
_queue.Add(miss.Id);
}
}
// -- Queue listening
if (options.QueueNames == null) {
Console.WriteLine("Starting on ALL Queue's, this is not recommended, please specify using RavenStorageOptions. Use an empty IEnumerable (such as List<string> or string[0]) to not listen to any queue.");
_storage.Repository.DocumentChange(typeof(JobQueue), QueueFiller);
} else {
foreach (var queue in options.QueueNames) {
Console.WriteLine("Starting on queue: {0}", queue);
_storage.Repository.DocumentChange(typeof(JobQueue), queue, QueueFiller);
}
}
}
private void QueueFiller(DocumentChangeNotification notification)
{
if (notification.Type == DocumentChangeTypes.Put)
_queue.Add(notification.Id);
}
[NotNull]
public IFetchedJob Dequeue(string[] queues, CancellationToken cancellationToken)
{
queues.ThrowIfNull("queues");
if (queues.Length == 0) {
throw new ArgumentException("Queue array must be non-empty.", "queues");
}
JobQueue fetchedJob = null;
do {
var jobId = _queue.Take(cancellationToken);
using (var repository = _storage.Repository.OpenSession()) {
fetchedJob = repository.Load<JobQueue>(jobId);
if (fetchedJob != null &&
fetchedJob.FetchedAt == null &&
queues.Contains(fetchedJob.Queue)) {
fetchedJob.FetchedAt = DateTime.UtcNow;
repository.Store(fetchedJob);
try {
// Did someone else already picked it up?
repository.Advanced.UseOptimisticConcurrency = true;
repository.SaveChanges();
} catch {
fetchedJob = null;
}
} else {
fetchedJob = null;
}
}
}
while (fetchedJob == null);
return new RavenFetchedJob(_storage, fetchedJob);
}
public void Enqueue(string queue, string jobId)
{
using (var repository = _storage.Repository.OpenSession()) {
var jobQueue = new JobQueue {
Id = Repository.GetId(typeof(JobQueue), queue, jobId),
JobId = jobId,
Queue = queue
};
repository.Store(jobQueue);
repository.SaveChanges();
}
}
}
}
using System;
using System.Linq;
using System.Threading;
using Hangfire.Annotations;
using Hangfire.Raven.Entities;
using Hangfire.Raven.Storage;
using Hangfire.Storage;
using Raven.Abstractions.Exceptions;
using Raven.Client.Linq;
using Hangfire.Logging;
using Hangfire.Raven.Indexes;
using System.Linq.Expressions;
namespace Hangfire.Raven.JobQueues
{
public class RavenJobQueue : IPersistentJobQueue
{
private static readonly ILog Logger = LogProvider.For<RavenJobQueue>();
private readonly RavenStorage _storage;
private readonly RavenStorageOptions _options;
public RavenJobQueue([NotNull] RavenStorage storage, RavenStorageOptions options)
{
storage.ThrowIfNull("storage");
options.ThrowIfNull("options");
_storage = storage;
_options = options;
}
[NotNull]
public IFetchedJob Dequeue(string[] queues, CancellationToken cancellationToken)
{
queues.ThrowIfNull("queues");
if (queues.Length == 0)
{
throw new ArgumentException("Queue array must be non-empty.", "queues");
}
JobQueue fetchedJob = null;
var fetchConditions = new Expression<Func<Hangfire_JobQueues.Mapping, bool>>[]
{
job => job.FetchedAt == null,
job => job.FetchedAt < DateTime.UtcNow.AddSeconds(_options.InvisibilityTimeout.Negate().TotalSeconds)
};
var currentQueryIndex = 0;
do
{
cancellationToken.ThrowIfCancellationRequested();
var fetchCondition = fetchConditions[currentQueryIndex];
foreach (var queue in queues)
{
using (var repository = _storage.Repository.OpenSession())
{
foreach (var job in repository.Query<Hangfire_JobQueues.Mapping, Hangfire_JobQueues>()
.Where(fetchCondition)
.Where(job => job.Queue == queue)
.OfType<JobQueue>())
{
job.FetchedAt = DateTime.UtcNow;
try
{
// Did someone else already picked it up?
repository.Advanced.UseOptimisticConcurrency = true;
repository.SaveChanges();
fetchedJob = job;
break;
}
catch (ConcurrencyException)
{
repository.Advanced.Evict(job); // Avoid subsequent concurrency exceptions
}
}
}
if (fetchedJob != null)
{
break;
}
}
if (fetchedJob == null)
{
if (currentQueryIndex == fetchConditions.Length - 1)
{
cancellationToken.WaitHandle.WaitOne(_options.QueuePollInterval);
cancellationToken.ThrowIfCancellationRequested();
}
}
currentQueryIndex = (currentQueryIndex + 1) % fetchConditions.Length;
}
while (fetchedJob == null);
return new RavenFetchedJob(_storage, fetchedJob);
}
public void Enqueue(string queue, string jobId)
{
using (var repository = _storage.Repository.OpenSession())
{
var jobQueue = new JobQueue
{
Id = _storage.Repository.GetId(typeof(JobQueue), queue, jobId),
JobId = jobId,
Queue = queue
};
repository.Store(jobQueue);
repository.SaveChanges();
}
}
}
}
@@ -3,10 +3,11 @@ using System.Linq;
using Hangfire.Annotations;
using Hangfire.Raven.Entities;
using Hangfire.Raven.Storage;
using Hangfire.Raven.Indexes;
namespace Hangfire.Raven.JobQueues
{
internal class RavenJobQueueMonitoringApi
public class RavenJobQueueMonitoringApi
: IPersistentJobQueueMonitoringApi
{
private RavenStorage _storage;
@@ -28,68 +29,59 @@ namespace Hangfire.Raven.JobQueues
}
}
public IEnumerable<string> GetEnqueuedJobIds(string queue, int @from, int perPage)
public IEnumerable<string> GetEnqueuedJobIds(string queue, int pageFrom, int perPage)
{
int start = @from + 1;
int end = from + perPage;
using (var repository = _storage.Repository.OpenSession()) {
var jobs = repository.Query<JobQueue>()
.Where(t => t.Queue == queue && t.FetchedAt == null)
.Select((data, i) => new { Index = i + 1, Data = data })
.Where(_ => (_.Index >= start) && (_.Index <= end))
.Select(x => x.Data)
using (var repository = _storage.Repository.OpenSession())
{
return repository.Query<Hangfire_JobQueues.Mapping, Hangfire_JobQueues>()
.Where(a => a.Queue == queue && a.FetchedAt == null)
.Skip(pageFrom)
.Take(perPage)
.Select(a => a.JobId)
.ToList()
.Where(jobId =>
{
var job = repository.Load<RavenJob>(_storage.Repository.GetId(typeof(RavenJob), jobId));
return (job != null) && (job.StateData != null);
})
.ToList();
var results = new List<RavenJob>();
foreach (var item in jobs) {
var job = repository.Load<RavenJob>(item.JobId);
if (job.StateData != null)
results.Add(job);
}
return results.Select(t => t.Id.Split(new char[] { '/' }, 2)[1]).ToList();
}
}
public IEnumerable<string> GetFetchedJobIds(string queue, int @from, int perPage)
public IEnumerable<string> GetFetchedJobIds(string queue, int pageFrom, int perPage)
{
int start = @from + 1;
int end = from + perPage;
using (var repository = _storage.Repository.OpenSession()) {
var jobs = repository.Query<JobQueue>().Where(t => t.Queue == queue && t.FetchedAt != null)
.Select((data, i) => new { Index = i + 1, Data = data })
.Where(_ => (_.Index >= start) && (_.Index <= end))
.Select(x => x.Data);
var results = new List<string>();
foreach (var item in jobs) {
var job = repository.Load<RavenJob>(item.JobId);
if (job != null) {
results.Add(job.Id.Split(new char[] { '/' }, 2)[1]);
}
}
return results;
using (var repository = _storage.Repository.OpenSession())
{
return repository.Query<Hangfire_JobQueues.Mapping, Hangfire_JobQueues>()
.Where(a => a.Queue == queue && a.FetchedAt != null)
.Skip(pageFrom)
.Take(perPage)
.Select(a => a.JobId)
.ToList()
.Where(jobId =>
{
var job = repository.Load<RavenJob>(_storage.Repository.GetId(typeof(RavenJob), jobId));
return job != null;
})
.ToList();
}
}
public EnqueuedAndFetchedCount GetEnqueuedAndFetchedCount(string queue)
{
using (var repository = _storage.Repository.OpenSession()) {
int enqueuedCount = repository.Query<JobQueue>().Where(t => t.Queue == queue && t.FetchedAt == null).Count();
int fetchedCount = repository.Query<JobQueue>().Where(t => t.Queue == queue && t.FetchedAt != null).Count();
return new EnqueuedAndFetchedCount {
EnqueuedCount = enqueuedCount,
FetchedCount = fetchedCount
};
using (var repository = _storage.Repository.OpenSession())
{
var fetchedQuery = _storage.GetJobQueueFacets(repository, a => a.FetchedAt != null && a.Queue == queue);
var fetchedCount = fetchedQuery.Results["Queue"].Values.Sum(a => a.Hits);
var enqueuedQuery = _storage.GetJobQueueFacets(repository, a => a.FetchedAt == null && a.Queue == queue);
var enqueuedCount = enqueuedQuery.Results["Queue"].Values.Sum(a => a.Hits);
return new EnqueuedAndFetchedCount
{
EnqueuedCount = enqueuedCount,
FetchedCount = fetchedCount
};
}
}
}
@@ -0,0 +1,13 @@
using Raven.Client;
using Raven.Client.Listeners;
namespace Hangfire.Raven.Listeners
{
public class NoStaleQueriesListener : IDocumentQueryListener
{
public void BeforeQueryExecuted(IDocumentQueryCustomization queryCustomization)
{
queryCustomization.WaitForNonStaleResults();
}
}
}
+103 -51
View File
@@ -25,23 +25,24 @@ using Hangfire.Raven.Storage;
using Hangfire.Server;
using Hangfire.Storage;
using static Hangfire.Raven.Entities.RavenJob;
using Hangfire.Raven.Indexes;
namespace Hangfire.Raven
{
public class RavenConnection : JobStorageConnection
{
private readonly RavenStorage _storage;
public RavenConnection(RavenStorage ravenStorage)
public RavenConnection(RavenStorage storage)
{
ravenStorage.ThrowIfNull("RavenStorage");
storage.ThrowIfNull("storage");
_storage = ravenStorage;
_storage = storage;
}
public override IWriteOnlyTransaction CreateWriteTransaction() => new RavenWriteOnlyTransaction(_storage);
public override IDisposable AcquireDistributedLock(string resource, TimeSpan timeout) => new RavenDistributedLock(_storage, string.Format("HangFire/{0}", resource), timeout);
public override IDisposable AcquireDistributedLock(string resource, TimeSpan timeout) => new RavenDistributedLock(_storage, string.Format("HangFire/{0}", resource), timeout, _storage.Options);
public override IFetchedJob FetchNextJob(string[] queues, CancellationToken cancellationToken)
{
@@ -82,7 +83,7 @@ namespace Hangfire.Raven
var guid = Guid.NewGuid().ToString();
var ravenJob = new RavenJob {
Id = Repository.GetId(typeof(RavenJob), guid),
Id = _storage.Repository.GetId(typeof(RavenJob), guid),
InvocationData = invocationData,
CreatedAt = createdAt,
Parameters = parameters
@@ -102,7 +103,7 @@ namespace Hangfire.Raven
key.ThrowIfNull("key");
using (var repository = _storage.Repository.OpenSession()) {
var id = Repository.GetId(typeof(RavenJob), key);
var id = _storage.Repository.GetId(typeof(RavenJob), key);
var jobData = repository.Load<RavenJob>(id);
if (jobData == null) {
@@ -135,7 +136,7 @@ namespace Hangfire.Raven
jobId.ThrowIfNull("jobId");
using (var repository = _storage.Repository.OpenSession()) {
var id = Repository.GetId(typeof(RavenJob), jobId);
var id = _storage.Repository.GetId(typeof(RavenJob), jobId);
var job = repository.Load<RavenJob>(id);
if (job == null) {
@@ -152,7 +153,7 @@ namespace Hangfire.Raven
name.ThrowIfNull("name");
using (var repository = _storage.Repository.OpenSession()) {
var id = Repository.GetId(typeof(RavenJob), jobId);
var id = _storage.Repository.GetId(typeof(RavenJob), jobId);
var job = repository.Load<RavenJob>(id);
job.Parameters[name] = value;
@@ -167,9 +168,13 @@ namespace Hangfire.Raven
name.ThrowIfNull("name");
using (var repository = _storage.Repository.OpenSession()) {
var id = Repository.GetId(typeof(RavenJob), jobId);
var id = _storage.Repository.GetId(typeof(RavenJob), jobId);
var job = repository.Load<RavenJob>(id);
if (job == null) {
return null;
}
string value;
if (!job.Parameters.TryGetValue(name, out value)) {
@@ -192,11 +197,11 @@ namespace Hangfire.Raven
key.ThrowIfNull("key");
using (var repository = _storage.Repository.OpenSession()) {
var id = Repository.GetId(typeof(RavenSet), key);
var id = _storage.Repository.GetId(typeof(RavenSet), key);
var set = repository.Load<RavenSet>(id);
if (set == null) {
return null;
return new HashSet<string>();
}
return new HashSet<string>(set.Scores.Keys);
@@ -212,14 +217,14 @@ namespace Hangfire.Raven
}
using (var repository = _storage.Repository.OpenSession()) {
var id = Repository.GetId(typeof(RavenSet), key);
var id = _storage.Repository.GetId(typeof(RavenSet), key);
var set = repository.Load<RavenSet>(id);
if (set == null) {
return null;
}
return set.Scores.OrderBy(a => a.Value).Select(a => a.Key).FirstOrDefault();
return set.Scores.Where(a => a.Value >= fromScore && a.Value <= toScore).OrderBy(a => a.Value).Select(a => a.Key).FirstOrDefault();
}
}
@@ -229,7 +234,7 @@ namespace Hangfire.Raven
keyValuePairs.ThrowIfNull("keyValuePairs");
using (var repository = _storage.Repository.OpenSession()) {
var id = Repository.GetId(typeof(RavenHash), key);
var id = _storage.Repository.GetId(typeof(RavenHash), key);
var result = repository.Load<RavenHash>(id);
if (result == null) {
@@ -253,7 +258,7 @@ namespace Hangfire.Raven
key.ThrowIfNull("key");
using (var repository = _storage.Repository.OpenSession()) {
var result = repository.Load<RavenHash>(Repository.GetId(typeof(RavenHash), key));
var result = repository.Load<RavenHash>(_storage.Repository.GetId(typeof(RavenHash), key));
return result?.Fields;
}
@@ -265,21 +270,24 @@ namespace Hangfire.Raven
context.ThrowIfNull("context");
using (var repository = _storage.Repository.OpenSession()) {
var id = Repository.GetId(typeof(RavenServer), serverId);
var id = _storage.Repository.GetId(typeof(RavenServer), serverId);
var server = repository.Load<RavenServer>(id);
if (server == null) {
server = new RavenServer() {
Id = id,
Data = new RavenServer.ServerData()
Data = new RavenServer.ServerData()
{
StartedAt = DateTime.UtcNow
}
};
repository.Store(server);
}
// Merge Queues and WorkerCount
server.Data.WorkerCount += context.WorkerCount;
server.Data.Queues = context.Queues.Concat(server.Data.Queues ?? new List<string>()).Distinct();
server.Data.WorkerCount = context.WorkerCount;
server.Data.Queues = context.Queues;
server.Data.StartedAt = DateTime.UtcNow;
server.LastHeartbeat = DateTime.UtcNow;
@@ -292,7 +300,7 @@ namespace Hangfire.Raven
serverId.ThrowIfNull("serverId");
using (var repository = _storage.Repository.OpenSession()) {
var id = Repository.GetId(typeof(RavenServer), serverId);
var id = _storage.Repository.GetId(typeof(RavenServer), serverId);
repository.Delete(id);
@@ -305,13 +313,15 @@ namespace Hangfire.Raven
serverId.ThrowIfNull("serverId");
using (var repository = _storage.Repository.OpenSession()) {
var id = Repository.GetId(typeof(RavenServer), serverId);
var id = _storage.Repository.GetId(typeof(RavenServer), serverId);
var server = repository.Load<RavenServer>(id);
if (server == null) {
server = new RavenServer {
Id = id
};
repository.Store(server);
}
server.LastHeartbeat = DateTime.UtcNow;
@@ -329,8 +339,9 @@ namespace Hangfire.Raven
using (var repository = _storage.Repository.OpenSession()) {
var heartBeatCutOff = DateTime.UtcNow.Add(timeOut.Negate());
var results = repository.Query<RavenServer>()
var results = repository.Query<Hangfire_RavenServers.Mapping, Hangfire_RavenServers>()
.Where(t => t.LastHeartbeat < heartBeatCutOff)
.OfType<RavenServer>()
.ToList();
foreach (var item in results) {
@@ -348,7 +359,7 @@ namespace Hangfire.Raven
key.ThrowIfNull("key");
using (var repository = _storage.Repository.OpenSession()) {
var id = Repository.GetId(typeof(RavenSet), key);
var id = _storage.Repository.GetId(typeof(RavenSet), key);
var set = repository.Load<RavenSet>(id);
if (set == null) {
@@ -364,14 +375,14 @@ namespace Hangfire.Raven
key.ThrowIfNull("key");
using (var repository = _storage.Repository.OpenSession()) {
var id = Repository.GetId(typeof(RavenSet), key);
var id = _storage.Repository.GetId(typeof(RavenSet), key);
var set = repository.Load<RavenSet>(id);
if (set == null) {
return new List<string>();
}
return set.Scores.Skip(startingFrom - 1)
return set.Scores.Skip(startingFrom)
.Take(endingAt - startingFrom + 1)
.Select(t => t.Key)
.ToList();
@@ -383,8 +394,12 @@ namespace Hangfire.Raven
key.ThrowIfNull("key");
using (var repository = _storage.Repository.OpenSession()) {
var id = Repository.GetId(typeof(RavenSet), key);
var id = _storage.Repository.GetId(typeof(RavenSet), key);
var set = repository.Load<RavenSet>(id);
if (set == null) {
return TimeSpan.FromSeconds(-1);
}
var expireAt = repository.Advanced.GetExpire(set);
@@ -401,12 +416,14 @@ namespace Hangfire.Raven
key.ThrowIfNull("key");
using (var repository = _storage.Repository.OpenSession()) {
var values = repository.Query<AggregatedCounter>()
.Where(t => t.Key == key)
.Select(t => t.Value)
.ToArray();
var id = _storage.Repository.GetId(typeof(Counter), key);
var counter = repository.Load<Counter>(id);
return values.Any() ? values.Sum() : 0;
if (counter == null) {
return 0;
}
return counter.Value;
}
}
@@ -415,7 +432,11 @@ namespace Hangfire.Raven
key.ThrowIfNull("key");
using (var repository = _storage.Repository.OpenSession()) {
var ravenHash = repository.Load<RavenHash>(Repository.GetId(typeof(RavenHash), key));
var ravenHash = repository.Load<RavenHash>(_storage.Repository.GetId(typeof(RavenHash), key));
if (ravenHash == null) {
return 0;
}
return ravenHash.Fields.Count;
}
@@ -426,7 +447,11 @@ namespace Hangfire.Raven
key.ThrowIfNull("key");
using (var repository = _storage.Repository.OpenSession()) {
var ravenHash = repository.Load<RavenHash>(Repository.GetId(typeof(RavenHash), key));
var ravenHash = repository.Load<RavenHash>(_storage.Repository.GetId(typeof(RavenHash), key));
if (ravenHash == null) {
return TimeSpan.FromSeconds(-1);
}
var expireAt = repository.Advanced.GetExpire(ravenHash);
@@ -444,7 +469,11 @@ namespace Hangfire.Raven
name.ThrowIfNull("name");
using (var repository = _storage.Repository.OpenSession()) {
var ravenHash = repository.Load<RavenHash>(Repository.GetId(typeof(RavenHash), key));
var ravenHash = repository.Load<RavenHash>(_storage.Repository.GetId(typeof(RavenHash), key));
if (ravenHash == null) {
return null;
}
string result;
@@ -461,9 +490,14 @@ namespace Hangfire.Raven
key.ThrowIfNull("key");
using (var repository = _storage.Repository.OpenSession()) {
return repository.Query<RavenList>()
.Where(t => t.Key == key)
.Count();
var id = _storage.Repository.GetId(typeof(RavenList), key);
var list = repository.Load<RavenList>(id);
if (list == null) {
return 0;
}
return list.Values.Count;
}
}
@@ -472,9 +506,19 @@ namespace Hangfire.Raven
key.ThrowIfNull("key");
using (var repository = _storage.Repository.OpenSession()) {
// TODO: Implement!
var list = repository.Load<RavenList>(_storage.Repository.GetId(typeof(RavenList), key));
if (list == null) {
return TimeSpan.FromSeconds(-1);
}
return TimeSpan.FromSeconds(-1);
var expireAt = repository.Advanced.GetExpire(list);
if (!expireAt.HasValue) {
return TimeSpan.FromSeconds(-1);
}
return expireAt.Value - DateTime.UtcNow;
}
}
@@ -483,12 +527,16 @@ namespace Hangfire.Raven
key.ThrowIfNull("key");
using (var repository = _storage.Repository.OpenSession()) {
return repository.Query<RavenList>()
.Where(t => t.Key == key)
.Select(t => t.Value)
.Skip(startingFrom - 1)
.Take(endingAt - startingFrom + 1)
.ToList();
var id = _storage.Repository.GetId(typeof(RavenList), key);
var list = repository.Load<RavenList>(id);
if (list == null) {
return new List<string>();
}
return list.Values.Skip(startingFrom)
.Take(endingAt - startingFrom + 1)
.ToList();
}
}
@@ -497,10 +545,14 @@ namespace Hangfire.Raven
key.ThrowIfNull("key");
using (var repository = _storage.Repository.OpenSession()) {
return repository.Query<RavenList>()
.Where(t => t.Key == key)
.Select(t => t.Value)
.ToList();
var id = _storage.Repository.GetId(typeof(RavenList), key);
var list = repository.Load<RavenList>(id);
if (list == null) {
return new List<string>();
}
return list.Values;
}
}
}
+84 -38
View File
@@ -9,12 +9,15 @@ using Hangfire.Storage;
using Hangfire.Storage.Monitoring;
using Raven.Abstractions.Data;
using Raven.Client;
using Hangfire.Logging;
namespace Hangfire.Raven
{
internal class RavenWriteOnlyTransaction
public class RavenWriteOnlyTransaction
: JobStorageTransaction
{
private static readonly ILog Logger = LogProvider.For<RavenWriteOnlyTransaction>();
private readonly RavenStorage _storage;
private IDocumentSession _session;
private List<KeyValuePair<string, PatchRequest>> _patchRequests;
@@ -41,7 +44,7 @@ namespace Hangfire.Raven
_session.Advanced.DocumentStore.DatabaseCommands.Patch(item.Key, item.ToArray());
}
} catch {
Console.WriteLine("- Concurrency exception");
Logger.Error("- Concurrency exception");
throw;
}
@@ -49,7 +52,7 @@ namespace Hangfire.Raven
public override void ExpireJob(string jobId, TimeSpan expireIn)
{
var id = Repository.GetId(typeof(RavenJob), jobId);
var id = _storage.Repository.GetId(typeof(RavenJob), jobId);
var result = _session.Load<RavenJob>(id);
_session.Advanced.AddExpire(result, DateTime.UtcNow + expireIn);
@@ -57,7 +60,7 @@ namespace Hangfire.Raven
public override void PersistJob(string jobId)
{
var id = Repository.GetId(typeof(RavenJob), jobId);
var id = _storage.Repository.GetId(typeof(RavenJob), jobId);
var result = _session.Load<RavenJob>(id);
_session.Advanced.RemoveExpire(result);
@@ -65,7 +68,7 @@ namespace Hangfire.Raven
public override void SetJobState(string jobId, IState state)
{
var id = Repository.GetId(typeof(RavenJob), jobId);
var id = _storage.Repository.GetId(typeof(RavenJob), jobId);
var result = _session.Load<RavenJob>(id);
result.History.Insert(0, new StateHistoryDto() {
@@ -87,6 +90,24 @@ namespace Hangfire.Raven
SetJobState(jobId, state);
}
public override void AddRangeToSet(string key, IList<string> items)
{
key.ThrowIfNull("key");
items.ThrowIfNull("items");
var id = _storage.Repository.GetId(typeof(RavenSet), key);
var set = _session.Load<RavenSet>(id) ?? new RavenSet() {
Id = id
};
_session.Store(set);
foreach (var item in items) {
set.Scores[item] = 0.0;
}
}
public override void AddToQueue(string queue, string jobId)
{
var provider = _storage.QueueProviders.GetProvider(queue);
@@ -102,7 +123,7 @@ namespace Hangfire.Raven
public override void IncrementCounter(string key, TimeSpan expireIn)
{
var id = Repository.GetId(typeof(Counter), key);
var id = _storage.Repository.GetId(typeof(Counter), key);
if (_session.Load<Counter>(id) == null) {
var counter = new Counter() {
@@ -131,7 +152,7 @@ namespace Hangfire.Raven
public override void DecrementCounter(string key, TimeSpan expireIn)
{
var id = Repository.GetId(typeof(Counter), key);
var id = _storage.Repository.GetId(typeof(Counter), key);
if (_session.Load<Counter>(id) == null) {
var counter = new Counter() {
@@ -160,7 +181,7 @@ namespace Hangfire.Raven
public override void AddToSet(string key, string value, double score)
{
var id = Repository.GetId(typeof(RavenSet), key);
var id = _storage.Repository.GetId(typeof(RavenSet), key);
var set = _session.Load<RavenSet>(id) ?? new RavenSet() {
Id = id
@@ -173,10 +194,14 @@ namespace Hangfire.Raven
public override void RemoveFromSet(string key, string value)
{
var id = Repository.GetId(typeof(RavenSet), key);
var id = _storage.Repository.GetId(typeof(RavenSet), key);
var set = _session.Load<RavenSet>(id);
if (set == null) {
return;
}
set.Scores.Remove(value);
if (set.Scores.Count == 0) {
@@ -188,7 +213,7 @@ namespace Hangfire.Raven
{
key.ThrowIfNull("key");
var id = Repository.GetId(typeof(RavenSet), key);
var id = _storage.Repository.GetId(typeof(RavenSet), key);
_session.Delete(id);
}
@@ -197,7 +222,7 @@ namespace Hangfire.Raven
{
key.ThrowIfNull("key");
var id = Repository.GetId(typeof(RavenSet), key);
var id = _storage.Repository.GetId(typeof(RavenSet), key);
var set = _session.Load<RavenSet>(id);
_session.Advanced.AddExpire(set, DateTime.UtcNow + expireIn);
@@ -207,7 +232,7 @@ namespace Hangfire.Raven
{
key.ThrowIfNull("key");
var id = Repository.GetId(typeof(RavenSet), key);
var id = _storage.Repository.GetId(typeof(RavenSet), key);
var set = _session.Load<RavenSet>(id);
_session.Advanced.RemoveExpire(set);
@@ -216,56 +241,77 @@ namespace Hangfire.Raven
public override void InsertToList(string key, string value)
{
var list = _session.Query<RavenList>().FirstOrDefault(t => t.Key == key && t.Value == value);
var id = _storage.Repository.GetId(typeof(RavenList), key);
if (list == null) {
list = new RavenList {
Id = Guid.NewGuid().ToString(),
Key = key
};
}
list.Value = value;
var list = _session.Load<RavenList>(id) ?? new RavenList() {
Id = id
};
_session.Store(list);
list.Values.Add(value);
}
public override void RemoveFromList(string key, string value)
{
var list = _session.Query<RavenList>().FirstOrDefault(t => t.Key == key && t.Value == value);
var id = _storage.Repository.GetId(typeof(RavenList), key);
if (list == null) {
return;
var list = _session.Load<RavenList>(id);
if (list == null) {
return;
}
_session.Delete(list);
list.Values.RemoveAll(v => v == value);
if (list.Values.Count == 0) {
_session.Delete(list);
}
}
public override void TrimList(string key, int keepStartingFrom, int keepEndingAt)
{
var list = _session.Query<RavenList>().Where(t => t.Key == key).ToList();
var id = _storage.Repository.GetId(typeof(RavenList), key);
if (!list.Any()) {
return;
var list = _session.Load<RavenList>(id);
if (list == null) {
return;
}
for (var i = 0; i < list.Count; ++i) {
if (i >= keepStartingFrom && i <= keepEndingAt) {
continue;
}
list.Values = list.Values.Skip(keepStartingFrom).Take(keepEndingAt - keepStartingFrom + 1).ToList();
_session.Delete(list[i]);
if (list.Values.Count == 0) {
_session.Delete(list);
}
}
public override void ExpireList(string key, TimeSpan expireIn)
{
key.ThrowIfNull("key");
var id = _storage.Repository.GetId(typeof(RavenList), key);
var list = _session.Load<RavenList>(id);
_session.Advanced.AddExpire(list, DateTime.UtcNow + expireIn);
}
public override void PersistList(string key)
{
key.ThrowIfNull("key");
var id = _storage.Repository.GetId(typeof(RavenList), key);
var list = _session.Load<RavenList>(id);
_session.Advanced.RemoveExpire(list);
}
public override void SetRangeInHash(string key, IEnumerable<KeyValuePair<string, string>> keyValuePairs)
{
key.ThrowIfNull("key");
keyValuePairs.ThrowIfNull("keyValuePairs");
var id = Repository.GetId(typeof(RavenHash), key);
var id = _storage.Repository.GetId(typeof(RavenHash), key);
var result = _session.Load<RavenHash>(id);
// Not exists? Insert
@@ -286,14 +332,14 @@ namespace Hangfire.Raven
{
key.ThrowIfNull("key");
_session.Delete(Repository.GetId(typeof(RavenHash), key));
_session.Delete(_storage.Repository.GetId(typeof(RavenHash), key));
}
public override void ExpireHash(string key, TimeSpan expireIn)
{
key.ThrowIfNull("key");
var id = Repository.GetId(typeof(RavenHash), key);
var id = _storage.Repository.GetId(typeof(RavenHash), key);
var set = _session.Load<RavenHash>(id);
_session.Advanced.AddExpire(set, DateTime.UtcNow + expireIn);
@@ -303,7 +349,7 @@ namespace Hangfire.Raven
{
key.ThrowIfNull("key");
var id = Repository.GetId(typeof(RavenHash), key);
var id = _storage.Repository.GetId(typeof(RavenHash), key);
var set = _session.Load<RavenHash>(id);
_session.Advanced.RemoveExpire(set);
+38 -24
View File
@@ -1,13 +1,13 @@
using System;
using System.Collections.Generic;
using Hangfire.Raven.Extensions;
using Hangfire.Raven.Indexes;
using Hangfire.Raven.Listeners;
using Raven.Abstractions.Data;
using Raven.Client;
using Raven.Client.Document;
using Raven.Client.Indexes;
using System.IO;
namespace Hangfire.Raven
{
public class RepositoryConfig
@@ -44,31 +44,30 @@ namespace Hangfire.Raven
}
}
public class Repository
public class Repository : IRepository
{
private RepositoryConfig _config;
private static IDocumentStore _documentStore;
private DocumentStore _documentStore;
private string _database;
public Repository(RepositoryConfig config)
{
_config = config;
if (!string.IsNullOrEmpty(_config.ConnectionStringName)) {
if (!string.IsNullOrEmpty(config.ConnectionStringName)) {
_documentStore = new DocumentStore {
ConnectionStringName = _config.ConnectionStringName
ConnectionStringName = config.ConnectionStringName
};
} else {
_documentStore = new DocumentStore {
Url = _config.ConnectionUrl,
ApiKey = _config.ApiKey,
DefaultDatabase = _config.Database
Url = config.ConnectionUrl,
ApiKey = config.ApiKey,
DefaultDatabase = config.Database
};
}
_documentStore.Listeners.RegisterListener(new TakeNewestConflictResolutionListener());
_documentStore.Initialize();
_documentStore.Initialize(ensureDatabaseExists: false);
new Hangfire_RavenJobs().Execute(_documentStore);
_database = _documentStore.DefaultDatabase;
}
public FacetResults GetFacets(string index, IndexQuery query, List<Facet> facets)
@@ -81,38 +80,48 @@ namespace Hangfire.Raven
_documentStore.ExecuteIndexes(indexes);
}
public static string GetId(Type type, params string[] id)
public string GetId(Type type, params string[] id)
{
return _documentStore.Conventions.FindFullDocumentKeyFromNonStringIdentifier(string.Join("/", id), type, false);
}
public void Destroy()
{
if (!_documentStore.DatabaseExists(_config.Database)) {
if (_database == null || !_documentStore.DatabaseExists(_database)) {
return;
}
_documentStore.DatabaseCommands.GlobalAdmin.DeleteDatabase(_config.Database, hardDelete: true);
_documentStore.DatabaseCommands.GlobalAdmin.DeleteDatabase(_database, hardDelete: true);
}
public void Create()
{
if (_documentStore.DatabaseExists(_config.Database)) {
if (_database == null || _documentStore.DatabaseExists(_database)) {
return;
}
_documentStore.DatabaseCommands.GlobalAdmin.EnsureDatabaseExists(_config.Database);
_documentStore
.DatabaseCommands
.GlobalAdmin
.CreateDatabase(new DatabaseDocument {
Id = "Raven/Databases/" + _database,
Settings = {
{ "Raven/ActiveBundles", "DocumentExpiration" },
{ "Raven/StorageTypeName", "voron" },
{ "Raven/DataDir", Path.Combine("~", _database) },
}
});
}
public IDisposable DocumentChange(Type documentType, Action<DocumentChangeNotification> action)
{
return _documentStore.Changes(_config.Database).ForDocumentsStartingWith(GetId(documentType, ""))
return _documentStore.Changes(_database).ForDocumentsStartingWith(GetId(documentType, ""))
.Subscribe(new RepositoryObserver<DocumentChangeNotification>(action));
}
public IDisposable DocumentChange(Type documentType, string suffix, Action<DocumentChangeNotification> action)
{
return _documentStore.Changes(_config.Database).ForDocumentsStartingWith(
return _documentStore.Changes(_database).ForDocumentsStartingWith(
GetId(documentType, string.Format("{0}/", suffix))
)
.Subscribe(new RepositoryObserver<DocumentChangeNotification>(action));
@@ -120,12 +129,17 @@ namespace Hangfire.Raven
public IDocumentSession OpenSession()
{
return _documentStore.OpenSession(_config.Database);
return _documentStore.OpenSession(_database);
}
public IAsyncDocumentSession OpenAsyncSession()
{
return _documentStore.OpenAsyncSession(_config.Database);
}
return _documentStore.OpenAsyncSession(_database);
}
public void Dispose()
{
_documentStore.Dispose();
}
}
}
+62 -11
View File
@@ -1,16 +1,21 @@
using System.Collections.Generic;
using Hangfire.Logging;
using Hangfire.Raven.Indexes;
using Hangfire.Raven.JobQueues;
using Hangfire.Storage;
using Raven.Client.Indexes;
using System;
using System.Collections.Generic;
using Hangfire.Logging;
using Hangfire.Raven.Indexes;
using Hangfire.Raven.JobQueues;
using Hangfire.Storage;
using Raven.Client.Indexes;
using Raven.Abstractions.Data;
using System.Linq.Expressions;
using Raven.Client;
using Raven.Client.Linq;
namespace Hangfire.Raven.Storage
{
public class RavenStorage : JobStorage
{
private readonly RavenStorageOptions _options;
private readonly Repository _repository;
private readonly IRepository _repository;
public RavenStorage(RepositoryConfig config)
: this(config, new RavenStorageOptions())
@@ -23,22 +28,36 @@ namespace Hangfire.Raven.Storage
/// <param name="config"></param>
/// <param name="options"></param>
public RavenStorage(RepositoryConfig config, RavenStorageOptions options)
: this(new Repository(config), options)
{
}
public RavenStorage(IRepository repository)
: this(repository, new RavenStorageOptions())
{
}
public RavenStorage(IRepository repository, RavenStorageOptions options)
{
repository.ThrowIfNull("repository");
options.ThrowIfNull("options");
_options = options;
_repository = new Repository(config);
_repository = repository;
_repository.Create();
_repository.ExecuteIndexes(new List<AbstractIndexCreationTask>()
{
new Hangfire_RavenJobs()
new Hangfire_RavenJobs(),
new Hangfire_JobQueues(),
new Hangfire_RavenServers()
});
InitializeQueueProviders();
}
public RavenStorageOptions Options { get { return _options; } }
public Repository Repository { get { return _repository; } }
public IRepository Repository { get { return _repository; } }
public virtual PersistentJobQueueProviderCollection QueueProviders { get; private set; }
@@ -55,6 +74,38 @@ namespace Hangfire.Raven.Storage
public override void WriteOptionsToLog(ILog logger)
{
logger.Info("Using the following options for Raven job storage:");
}
public FacetResults GetRavenJobFacets(
IDocumentSession session,
Expression<Func<Hangfire_RavenJobs.Mapping, bool>> clause)
{
var query = session.Query<Hangfire_RavenJobs.Mapping, Hangfire_RavenJobs>();
if (clause != null)
query = query.Where(clause);
return query.ToFacets(new[] {
new Facet
{
Name = "StateName"
}
});
}
public FacetResults GetJobQueueFacets(
IDocumentSession session,
Expression<Func<Hangfire_JobQueues.Mapping, bool>> clause)
{
var query = session.Query<Hangfire_JobQueues.Mapping, Hangfire_JobQueues>();
if (clause != null)
query = query.Where(clause);
return query.ToFacets(new[] {
new Facet
{
Name = "Queue"
}
});
}
private void InitializeQueueProviders()
@@ -11,7 +11,8 @@ using Hangfire.Storage.Monitoring;
using Raven.Abstractions.Data;
using Raven.Client;
using Raven.Client.Linq;
using Hangfire.Raven.JobQueues;
namespace Hangfire.Raven.Storage
{
public class RavenStorageMonitoringApi
@@ -28,29 +29,18 @@ namespace Hangfire.Raven.Storage
public long EnqueuedCount(string queue)
{
using (var repository = _storage.Repository.OpenSession()) {
RavenQueryStatistics stats;
repository.Query<JobQueue>()
.Where(a => a.FetchedAt == null && a.Queue == queue)
.Take(0)
.Statistics(out stats)
.ToList();
return stats.TotalResults;
}
var queueApi = GetQueueApi(queue);
var counters = queueApi.GetEnqueuedAndFetchedCount(queue);
return counters.EnqueuedCount ?? 0;
}
public long FetchedCount(string queue)
{
using (var repository = _storage.Repository.OpenSession()) {
RavenQueryStatistics stats;
repository.Query<JobQueue>()
.Where(a => a.FetchedAt != null && a.Queue == queue)
.Take(0)
.Statistics(out stats)
.ToList();
{
var queueApi = GetQueueApi(queue);
var counters = queueApi.GetEnqueuedAndFetchedCount(queue);
return counters.FetchedCount ?? 0;
return stats.TotalResults;
}
}
public long DeletedListCount()
{
@@ -74,15 +64,12 @@ namespace Hangfire.Raven.Storage
}
private long GetNumberOfJobsByStateName(string stateName)
{
using (var repository = _storage.Repository.OpenSession()) {
RavenQueryStatistics stats;
repository.Query<RavenJob>()
.Where(a => a.StateData.Name == stateName)
.Take(0)
.Statistics(out stats)
.ToList();
using (var repository = _storage.Repository.OpenSession())
{
var facetResults = _storage.GetRavenJobFacets(repository, a => a.StateName == stateName);
var getFacetValues = facetResults.Results["StateName"].Values;
return stats.TotalResults;
return getFacetValues.Sum(a => a.Hits);
}
}
@@ -132,7 +119,7 @@ namespace Hangfire.Raven.Storage
var stats = new Dictionary<DateTime, long>();
using (var repository = _storage.Repository.OpenSession()) {
foreach (var item in dates) {
var id = Repository.GetId(typeof(Counter), formatorAction(item));
var id = _storage.Repository.GetId(typeof(Counter), formatorAction(item));
var counters = repository.Load<Counter>(id);
if (counters != null)
@@ -148,30 +135,22 @@ namespace Hangfire.Raven.Storage
{
using (var transaction = _storage.Repository.OpenSession()) {
var stat = new RavenQueryStatistics();
transaction.Query<Raven_DocumentsByEntityName.Mapping, Raven_DocumentsByEntityName>()
.Where(a => a.Tag == "RavenServers")
transaction.Query<RavenServer>()
.Take(0)
.Statistics(out stat)
.ToList();
var recurringJobs = transaction.Load<RavenSet>("RavenSets/recurring-jobs");
FacetResults facetResults = _storage.Repository
.GetFacets(
"Hangfire/RavenJobs",
new IndexQuery(),
new List<Facet>
{
new Facet
{
Name = "StateName"
}
});
var facetResults = _storage.GetRavenJobFacets(transaction, null);
var getFacetValues = facetResults.Results["StateName"].Values;
var facetJobResults = _storage.GetJobQueueFacets(transaction, null);
var getJobFacetValues = facetJobResults.Results["Queue"].Values;
return new StatisticsDto() {
Servers = stat.TotalResults,
Queues = 2,
Queues = getJobFacetValues.Count,
Recurring = recurringJobs?.Scores?.Count ?? 0,
Succeeded = getFacetValues.FirstOrDefault(a => a.Range == SucceededState.StateName)?.Hits ?? 0,
Scheduled = getFacetValues.FirstOrDefault(a => a.Range == ScheduledState.StateName)?.Hits ?? 0,
@@ -199,15 +178,10 @@ namespace Hangfire.Raven.Storage
}
public JobList<EnqueuedJobDto> EnqueuedJobs(string queue, int from, int perPage)
{
using (var repository = _storage.Repository.OpenSession()) {
var results = repository.Query<JobQueue>()
.Where(a => a.FetchedAt == null && a.Queue == queue)
.Skip(from)
.Take(perPage)
.Select(a => a.JobId);
var queueApi = GetQueueApi(queue);
var enqueuedJobIds = queueApi.GetEnqueuedJobIds(queue, from, perPage);
return EnqueuedJobs(results);
}
return EnqueuedJobs(enqueuedJobIds);
}
public JobList<FailedJobDto> FailedJobs(int from, int count)
{
@@ -224,15 +198,10 @@ namespace Hangfire.Raven.Storage
}
public JobList<FetchedJobDto> FetchedJobs(string queue, int from, int perPage)
{
using (var repository = _storage.Repository.OpenSession()) {
var results = repository.Query<JobQueue>()
.Where(a => a.FetchedAt != null && a.Queue == queue)
.Skip(from)
.Take(perPage)
.Select(a => a.JobId);
var queueApi = GetQueueApi(queue);
var fetchedJobIds = queueApi.GetFetchedJobIds(queue, from, perPage);
return FetchedJobs(results);
}
return FetchedJobs(fetchedJobIds);
}
public JobList<ScheduledJobDto> ScheduledJobs(int from, int count)
{
@@ -279,9 +248,13 @@ namespace Hangfire.Raven.Storage
jobId.ThrowIfNull("jobId");
using (var repository = _storage.Repository.OpenSession()) {
var id = Repository.GetId(typeof(RavenJob), jobId);
var id = _storage.Repository.GetId(typeof(RavenJob), jobId);
var job = repository.Load<RavenJob>(id);
if (job == null) {
return null;
}
return new JobDetailsDto {
CreatedAt = job.CreatedAt,
ExpireAt = repository.Advanced.GetExpire(job),
@@ -319,7 +292,7 @@ namespace Hangfire.Raven.Storage
var query =
from server in servers
select new ServerDto {
Name = server.Id,
Name = server.Id.Split(new[] { '/' }, 2)[1],
Heartbeat = server.LastHeartbeat,
Queues = server.Data.Queues.ToList(),
StartedAt = server.Data.StartedAt ?? DateTime.MinValue,
@@ -337,11 +310,14 @@ namespace Hangfire.Raven.Storage
string stateName,
Func<RavenJob, Job, Dictionary<string, string>, TDto> selector)
{
using (var repository = _storage.Repository.OpenSession()) {
var jobs = repository.Query<RavenJob>().Where(a => a.StateData.Name == stateName)
.OrderBy(a => a.CreatedAt)
.Skip(from)
.Take(count)
using (var repository = _storage.Repository.OpenSession())
{
var jobs = repository.Query<Hangfire_RavenJobs.Mapping, Hangfire_RavenJobs>()
.Where(a => a.StateName == stateName)
.OrderByDescending(a => a.CreatedAt)
.Skip(from)
.Take(count)
.OfType<RavenJob>()
.ToList();
return DeserializeJobs(jobs, selector);
@@ -351,14 +327,22 @@ namespace Hangfire.Raven.Storage
{
using (var repository = _storage.Repository.OpenSession()) {
var jobs = repository.Load<RavenJob>(
jobIds.Select(a => Repository.GetId(typeof(RavenJob), a))
jobIds.Select(a => _storage.Repository.GetId(typeof(RavenJob), a))
)
.Where(a => a != null && a.StateData != null);
.Where(a => a != null)
.ToList();
return DeserializeJobs(jobs, (jsonJob, job, stateData) => new FetchedJobDto {
Dictionary<string, JobQueue> jobIdToJobQueueMap = repository.Query<Hangfire_JobQueues.Mapping, Hangfire_JobQueues>()
.Where(a => a.JobId.In(jobs.Select(job => job.Id.Split(new char[] { '/' }, 2)[1])) && a.FetchedAt != null)
.OfType<JobQueue>()
.ToDictionary(jobQueue => jobQueue.JobId);
IEnumerable<RavenJob> filteredJobs = jobs.Where(job => jobIdToJobQueueMap.ContainsKey(job.Id.Split(new char[] { '/' }, 2)[1]));
return DeserializeJobs(filteredJobs, (jsonJob, job, stateData) => new FetchedJobDto {
Job = job,
State = jsonJob.StateData.Name,
FetchedAt = jsonJob.StateData.Name == ProcessingState.StateName
State = jsonJob.StateData?.Name,
FetchedAt = jsonJob.StateData?.Name == ProcessingState.StateName
? JobHelper.DeserializeNullableDateTime(stateData["StartedAt"])
: null
});
@@ -367,13 +351,23 @@ namespace Hangfire.Raven.Storage
private JobList<EnqueuedJobDto> EnqueuedJobs(IEnumerable<string> jobIds)
{
using (var repository = _storage.Repository.OpenSession()) {
var jobs = repository.Load<RavenJob>(jobIds.Select(a => Repository.GetId(typeof(RavenJob), a)))
.Where(a => a != null && a.StateData != null);
var jobs = repository.Load<RavenJob>(
jobIds.Select(a => _storage.Repository.GetId(typeof(RavenJob), a))
)
.Where(a => a != null)
.ToList();
return DeserializeJobs(jobs, (jsonJob, job, stateData) => new EnqueuedJobDto {
Dictionary<string, JobQueue> jobIdToJobQueueMap = repository.Query<Hangfire_JobQueues.Mapping, Hangfire_JobQueues>()
.Where(a => a.JobId.In(jobs.Select(job => job.Id.Split(new char[] { '/' }, 2)[1])) && a.FetchedAt == null)
.OfType<JobQueue>()
.ToDictionary(jobQueue => jobQueue.JobId);
IEnumerable<RavenJob> filteredJobs = jobs.Where(job => jobIdToJobQueueMap.ContainsKey(job.Id.Split(new char[] { '/' }, 2)[1]));
return DeserializeJobs(filteredJobs, (jsonJob, job, stateData) => new EnqueuedJobDto {
Job = job,
State = jsonJob.StateData.Name,
EnqueuedAt = jsonJob.StateData.Name == EnqueuedState.StateName
State = jsonJob.StateData?.Name,
EnqueuedAt = jsonJob.StateData?.Name == EnqueuedState.StateName
? JobHelper.DeserializeNullableDateTime(stateData["EnqueuedAt"])
: null
});
@@ -396,7 +390,7 @@ namespace Hangfire.Raven.Storage
Func<RavenJob, Job, Dictionary<string, string>, TDto> selector)
{
var result = from job in jobs
let stateData = job.StateData.Data != null
let stateData = (job.StateData != null) && (job.StateData.Data != null)
? new Dictionary<string, string>(job.StateData.Data, StringComparer.OrdinalIgnoreCase)
: null
let dto = selector(job, DeserializeJob(job.InvocationData), stateData)
@@ -404,5 +398,13 @@ namespace Hangfire.Raven.Storage
return new JobList<TDto>(result);
}
private IPersistentJobQueueMonitoringApi GetQueueApi(string queueName)
{
var provider = _storage.QueueProviders.GetProvider(queueName);
var monitoringApi = provider.GetJobQueueMonitoringApi();
return monitoringApi;
}
}
}
@@ -7,15 +7,42 @@ namespace Hangfire.Raven.Storage
{
private readonly string _clientId = null;
private TimeSpan _queuePollInterval;
private TimeSpan _distributedLockLifetime;
public RavenStorageOptions()
{
QueuePollInterval = TimeSpan.FromSeconds(15);
InvisibilityTimeout = TimeSpan.FromMinutes(30);
JobExpirationCheckInterval = TimeSpan.FromHours(1);
CountersAggregateInterval = TimeSpan.FromMinutes(5);
TransactionTimeout = TimeSpan.FromMinutes(1);
DistributedLockLifetime = TimeSpan.FromSeconds(30);
_clientId = Guid.NewGuid().ToString().Replace("-", string.Empty);
}
public TimeSpan QueuePollInterval
{
get { return _queuePollInterval; }
set
{
var message = $"The QueuePollInterval property value should be positive. Given: {value}.";
if (value == TimeSpan.Zero)
{
throw new ArgumentException(message, nameof(value));
}
if (value != value.Duration())
{
throw new ArgumentException(message, nameof(value));
}
_queuePollInterval = value;
}
}
public TimeSpan InvisibilityTimeout { get; set; }
public TimeSpan JobExpirationCheckInterval { get; set; }
@@ -23,6 +50,26 @@ namespace Hangfire.Raven.Storage
public TimeSpan TransactionTimeout { get; set; }
public TimeSpan DistributedLockLifetime
{
get { return _distributedLockLifetime; }
set
{
var message = $"The DistributedLockLifetime property value should be positive. Given: {value}.";
if (value == TimeSpan.Zero)
{
throw new ArgumentException(message, nameof(value));
}
if (value != value.Duration())
{
throw new ArgumentException(message, nameof(value));
}
_distributedLockLifetime = value;
}
}
public IEnumerable<string> QueueNames { get; set; }
public string ClientId {
+2 -2
View File
@@ -42,9 +42,9 @@
"summary": "RavenDB job storage for Hangfire"
},
"dependencies": {
"Hangfire.Core": "1.6.4",
"Hangfire.Core": "1.6.8",
"Newtonsoft.Json": "9.0.1",
"RavenDB.Client": "3.5.0-rc-35169"
"RavenDB.Client": "3.5.3-patch-35190"
},
"frameworks": {
"net45": {
File diff suppressed because it is too large Load Diff