Merge pull request #19 from mjohnson0580/master
Added Hangfire.Raven.Tests project. Nice work @mjohnson0580 and @xinix00
This commit is contained in:
+57
-50
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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": {
|
||||
|
||||
+10139
-10158
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user