12 Commits

Author SHA1 Message Date
Austin Winstanley c65b8b1314 code cleanup, migrated to dotnet core, builds for dnx46, dnx451, net45, net451, net452, net46, net461, netcoreapp1.0, netstandard1.6 2016-09-12 20:03:18 -05:00
Austin Winstanley 343603fd7c Merge pull request #9 from xinix00/master
Improved implementation for RavenDB
2016-09-01 21:10:20 -05:00
Derek e5e8c76bee + Assemblies (more reliable in combination with RavenDB $type helper (used by Newtonsoft.Json))
+ JobWrapper instead of JSON text as Job parameter, is better in finding types (for example interfaces)
+ Method/Property JsonConverters for RavenDB, needed for the InvocationData replacement
~ Namespace fix (HangFire vs Hangfire (capital F)
- InvocationData, RavenDB is already a JSON database, so had no purpose to Serialize and Deserialize job data
.. Updated Hangfire.Core to latest 1.6.3 dependency
.. Downgraded Newtonsoft.Json to version matching RavenDB. Will be upgraded in RavenDB 3.5.
2016-08-21 23:27:10 +02:00
Derek ee6acb0e86 Changed some things back for own purpose 2016-08-19 11:29:09 +02:00
Derek 05df581bf3 Removed Dewey, updated other dependencies 2016-08-19 11:00:47 +02:00
Derek 44ef453b21 Merge error fixed. 2016-08-19 10:58:00 +02:00
Derek 01c9f9840e Merge branch 'master' of https://github.com/axial/hangfire-ravendb
# Conflicts:
#	src/Hangfire.Raven/Raven.Studio.Html5.zip
#	src/Hangfire.Raven/Repository.cs
#	src/Hangfire.Raven/packages.config
2016-08-19 10:32:29 +02:00
Derek ccf356279f Latest fixes 2016-08-19 10:27:51 +02:00
Austin Winstanley bb57d046d2 Merge pull request #8 from sigurbjorngudjonsson/master
Changed references from Dewey.Net 3.3 to Dewey 5.0
2016-08-18 14:57:40 -04:00
Sigurbjörn Guðjónsson fcfc61a993 Changed references from Dewey.Net 3.3 to Dewey 5.0 and updated other Nuget packages to latest 2016-08-18 08:28:16 +00:00
Derek 6f501e8303 Changed some bits and pieces 2016-08-17 07:17:36 +02:00
Derek 5d27d05ac0 + Added dashboard support
+ Better use of RavenDB
-- Naming of entities (removed baseEntity)
-- Added job history
-- Removing of Poll for Jobs (using RavenDB changes observer pattern)
+ Removed static Repository, now passed through via RavenStorage class
+ Updated to target latest Hangfire.Core and RavenDB.Client
+ Added Hangfire/RavenJobs index
2016-07-28 09:41:21 +02:00
57 changed files with 12869 additions and 1993 deletions
+19 -13
View File
@@ -1,33 +1,39 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.23107.0
VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hangfire.Raven", "src\Hangfire.Raven\Hangfire.Raven.csproj", "{A523C0E3-097D-4869-977F-15A717EA3E83}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleSample", "src\ConsoleSample\ConsoleSample.csproj", "{C02BB718-2AE4-434C-8668-C894FF663FCE}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_", "_", "{8CF2D2F6-BEFE-47FE-B141-0017FC6F97D8}"
ProjectSection(SolutionItems) = preProject
.gitignore = .gitignore
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("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ConsoleSample", "src\ConsoleSample\ConsoleSample.xproj", "{3955F2F1-99AF-4CC0-88F5-C9B69757A1B2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A523C0E3-097D-4869-977F-15A717EA3E83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A523C0E3-097D-4869-977F-15A717EA3E83}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A523C0E3-097D-4869-977F-15A717EA3E83}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A523C0E3-097D-4869-977F-15A717EA3E83}.Release|Any CPU.Build.0 = Release|Any CPU
{C02BB718-2AE4-434C-8668-C894FF663FCE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C02BB718-2AE4-434C-8668-C894FF663FCE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C02BB718-2AE4-434C-8668-C894FF663FCE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C02BB718-2AE4-434C-8668-C894FF663FCE}.Release|Any CPU.Build.0 = Release|Any CPU
{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
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} = {EF9BFE2C-0386-4EC4-83EC-D7E315F56735}
EndGlobalSection
EndGlobal
-86
View File
@@ -1,86 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{C02BB718-2AE4-434C-8668-C894FF663FCE}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ConsoleSample</RootNamespace>
<AssemblyName>ConsoleSample</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<TargetFrameworkProfile />
<NuGetPackageImportStamp>89909a41</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<CodeAnalysisRuleSet>ManagedMinimumRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<ItemGroup>
<Reference Include="Hangfire.Core, Version=1.5.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Hangfire.Core.1.5.1\lib\net45\Hangfire.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\packages\Newtonsoft.Json.5.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL">
<HintPath>..\..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="GenericServices.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Services.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Hangfire.Raven\Hangfire.Raven.csproj">
<Project>{a523c0e3-097d-4869-977f-15a717ea3e83}</Project>
<Name>Hangfire.Raven</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
+21
View File
@@ -0,0 +1,21 @@
<?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>3955f2f1-99af-4cc0-88f5-c9b69757a1b2</ProjectGuid>
<RootNamespace>ConsoleSample</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>
+21 -17
View File
@@ -1,6 +1,6 @@
using System;
using Hangfire;
using HangFire.Raven.Storage;
using Hangfire.Raven.Storage;
namespace ConsoleSample
{
@@ -10,32 +10,36 @@ namespace ConsoleSample
public static void Main()
{
// you can use Raven Storage and specify the connection string and database name
GlobalConfiguration.Configuration
.UseColouredConsoleLogProvider()
.UseRavenStorage("http://localhost:8080", "hangfire", "apikeytest");
try {
// you can use Raven Storage and specify the connection string and database name
GlobalConfiguration.Configuration
.UseColouredConsoleLogProvider()
.UseRavenStorage("http://localhost:9090", "Hangfire");
// you can use Raven Embedded Storage which runs in memory!
//GlobalConfiguration.Configuration
// .UseColouredConsoleLogProvider()
// .UseEmbeddedRavenStorage();
// you can use Raven Embedded Storage which runs in memory!
//GlobalConfiguration.Configuration
// .UseColouredConsoleLogProvider()
// .UseEmbeddedRavenStorage();
//you have to create an instance of background job server at least once for background jobs to run
var client = new BackgroundJobServer();
//you have to create an instance of background job server at least once for background jobs to run
var client = new BackgroundJobServer();
//BackgroundJob.Enqueue(() => Console.WriteLine("Background Job: Hello, world!"));
BackgroundJob.Enqueue(() => test());
//RecurringJob.AddOrUpdate(() => test(), Cron.Minutely);
//BackgroundJob.Enqueue(() => Console.WriteLine("Background Job: Hello, world!"));
//BackgroundJob.Enqueue(() => test());
RecurringJob.AddOrUpdate(() => test(), Cron.Minutely);
Console.WriteLine("Press Enter to exit...");
Console.ReadLine();
Console.WriteLine("Press Enter to exit...");
Console.ReadLine();
} catch (Exception ex) {
throw ex;
}
}
[AutomaticRetry(Attempts = 2, LogEvents = true, OnAttemptsExceeded = AttemptsExceededAction.Delete)]
public static void test()
{
Console.WriteLine($"{x++} Cron Job: Hello, world!");
throw new ArgumentException("fail");
//throw new ArgumentException("fail");
}
}
}
+6 -22
View File
@@ -1,35 +1,19 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// 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: AssemblyTitle("Hangfire.Hosts.Console")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Hangfire.Hosts.Console")]
[assembly: AssemblyCopyright("Copyright © 2013")]
[assembly: AssemblyProduct("ConsoleSample")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 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
// 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("3c3451fc-663d-4e33-ab72-d92beae38a97")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: Guid("3955f2f1-99af-4cc0-88f5-c9b69757a1b2")]
+1 -1
View File
@@ -7,7 +7,7 @@
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
-6
View File
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Hangfire.Core" version="1.5.1" targetFramework="net45" />
<package id="Newtonsoft.Json" version="5.0.1" targetFramework="net45" />
<package id="Owin" version="1.0" targetFramework="net45" />
</packages>
+55
View File
@@ -0,0 +1,55 @@
{
"title": "Hangfire.Raven ConsoleSample",
"version": "2.0.0",
"description": "RavenDB job storage for Hangfire",
"copyright": "Copyright @ 2016 Cady, LLC",
"authors": [
"Cady <contact@cady.io>"
],
"buildOptions": {
"allowUnsafe": true,
"debugType": "portable",
"emitEntryPoint": true,
"preserveCompilationContext": true
},
"packOptions": {
"tags": [
"Hangfire",
"AspNet",
"MVC",
"OWIN",
"RavenDB",
"Long-Running",
"Background",
"Fire-And-Forget",
"Delayed",
"Recurring",
"Tasks",
"Jobs",
"Scheduler",
"Threading",
"Queues"
],
"iconUrl": "https://avatars2.githubusercontent.com/u/7880472?v=3&s=200",
"projectUrl": "https://github.com/cady-io/hangfire-ravendb",
"licenseUrl": "http://www.gnu.org/licenses/lgpl-3.0.html",
"owners": [
"Cady <contact@cady.io>"
],
"repository": {
"type": "git",
"url": "https://github.com/cady-io/hangfire-ravendb"
},
"summary": "RavenDB job storage for Hangfire"
},
"runtimes": {
"win10-x64": {}
},
"frameworks": {
"net452": {}
},
"dependencies": {
"Hangfire.Raven": "2.0.0",
"System.ComponentModel.Primitives": "4.1.0"
}
}
+423
View File
@@ -0,0 +1,423 @@
{
"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": {}
}
},
"Microsoft.DotNet.InternalAbstractions/1.0.0": {
"type": "package",
"compile": {
"lib/net451/Microsoft.DotNet.InternalAbstractions.dll": {}
},
"runtime": {
"lib/net451/Microsoft.DotNet.InternalAbstractions.dll": {}
}
},
"Microsoft.Extensions.DependencyModel/1.0.0": {
"type": "package",
"dependencies": {
"Microsoft.DotNet.InternalAbstractions": "1.0.0",
"Newtonsoft.Json": "9.0.1"
},
"compile": {
"lib/net451/Microsoft.Extensions.DependencyModel.dll": {}
},
"runtime": {
"lib/net451/Microsoft.Extensions.DependencyModel.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.1.0": {
"type": "package",
"frameworkAssemblies": [
"System",
"mscorlib"
],
"compile": {
"ref/net45/System.ComponentModel.Primitives.dll": {}
},
"runtime": {
"lib/net45/System.ComponentModel.Primitives.dll": {}
}
},
"System.Data.Common/4.1.0": {
"type": "package",
"frameworkAssemblies": [
"System.Data",
"mscorlib"
],
"compile": {
"ref/net451/System.Data.Common.dll": {}
},
"runtime": {
"lib/net451/System.Data.Common.dll": {}
}
},
"Hangfire.Raven/2.0.0": {
"type": "project",
"framework": ".NETFramework,Version=v4.5.2",
"dependencies": {
"Hangfire.Core": "1.6.4",
"Microsoft.Extensions.DependencyModel": "1.0.0",
"Newtonsoft.Json": "9.0.1",
"RavenDB.Client": "3.5.0-rc-35169",
"System.Data.Common": "4.1.0"
},
"compile": {
"net452/Hangfire.Raven.dll": {}
},
"runtime": {
"net452/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": {}
}
},
"Microsoft.DotNet.InternalAbstractions/1.0.0": {
"type": "package",
"compile": {
"lib/net451/Microsoft.DotNet.InternalAbstractions.dll": {}
},
"runtime": {
"lib/net451/Microsoft.DotNet.InternalAbstractions.dll": {}
}
},
"Microsoft.Extensions.DependencyModel/1.0.0": {
"type": "package",
"dependencies": {
"Microsoft.DotNet.InternalAbstractions": "1.0.0",
"Newtonsoft.Json": "9.0.1"
},
"compile": {
"lib/net451/Microsoft.Extensions.DependencyModel.dll": {}
},
"runtime": {
"lib/net451/Microsoft.Extensions.DependencyModel.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.1.0": {
"type": "package",
"frameworkAssemblies": [
"System",
"mscorlib"
],
"compile": {
"ref/net45/System.ComponentModel.Primitives.dll": {}
},
"runtime": {
"lib/net45/System.ComponentModel.Primitives.dll": {}
}
},
"System.Data.Common/4.1.0": {
"type": "package",
"frameworkAssemblies": [
"System.Data",
"mscorlib"
],
"compile": {
"ref/net451/System.Data.Common.dll": {}
},
"runtime": {
"lib/net451/System.Data.Common.dll": {}
}
},
"Hangfire.Raven/2.0.0": {
"type": "project",
"framework": ".NETFramework,Version=v4.5.2",
"dependencies": {
"Hangfire.Core": "1.6.4",
"Microsoft.Extensions.DependencyModel": "1.0.0",
"Newtonsoft.Json": "9.0.1",
"RavenDB.Client": "3.5.0-rc-35169",
"System.Data.Common": "4.1.0"
},
"compile": {
"net452/Hangfire.Raven.dll": {}
},
"runtime": {
"net452/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"
]
},
"Microsoft.DotNet.InternalAbstractions/1.0.0": {
"sha512": "AAguUq7YyKk3yDWPoWA8DrLZvURxB/LrDdTn1h5lmPeznkFUpfC3p459w5mQYQE0qpquf/CkSQZ0etiV5vRHFA==",
"type": "package",
"path": "Microsoft.DotNet.InternalAbstractions/1.0.0",
"files": [
"Microsoft.DotNet.InternalAbstractions.1.0.0.nupkg.sha512",
"Microsoft.DotNet.InternalAbstractions.nuspec",
"lib/net451/Microsoft.DotNet.InternalAbstractions.dll",
"lib/netstandard1.3/Microsoft.DotNet.InternalAbstractions.dll"
]
},
"Microsoft.Extensions.DependencyModel/1.0.0": {
"sha512": "n55Y2T4qMgCNMrJaqAN+nlG2EH4XL+e9uxIg4vdFsQeF+L8UKxRdD3C35Bt+xk3vO3Zwp3g+6KFq2VPH2COSmg==",
"type": "package",
"path": "Microsoft.Extensions.DependencyModel/1.0.0",
"files": [
"Microsoft.Extensions.DependencyModel.1.0.0.nupkg.sha512",
"Microsoft.Extensions.DependencyModel.nuspec",
"lib/net451/Microsoft.Extensions.DependencyModel.dll",
"lib/netstandard1.6/Microsoft.Extensions.DependencyModel.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.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.1.0": {
"sha512": "sc/7eVCdxPrp3ljpgTKVaQGUXiW05phNWvtv/m2kocXqrUQvTVWKou1Edas2aDjTThLPZOxPYIGNb/HN0QjURg==",
"type": "package",
"path": "System.ComponentModel.Primitives/4.1.0",
"files": [
"System.ComponentModel.Primitives.4.1.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/_._"
]
},
"System.Data.Common/4.1.0": {
"sha512": "epU8jeTe7aE7RqGHq9rZ8b0Q4Ah7DgubzHQblgZMSqgW1saW868WmooSyC5ywf8upLBkcVLDu93W9GPWUYsU2Q==",
"type": "package",
"path": "System.Data.Common/4.1.0",
"files": [
"System.Data.Common.4.1.0.nupkg.sha512",
"System.Data.Common.nuspec",
"ThirdPartyNotices.txt",
"dotnet_library_license.txt",
"lib/MonoAndroid10/_._",
"lib/MonoTouch10/_._",
"lib/net451/System.Data.Common.dll",
"lib/netstandard1.2/System.Data.Common.dll",
"lib/portable-net451+win8+wp8+wpa81/System.Data.Common.dll",
"lib/xamarinios10/_._",
"lib/xamarinmac20/_._",
"lib/xamarintvos10/_._",
"lib/xamarinwatchos10/_._",
"ref/MonoAndroid10/_._",
"ref/MonoTouch10/_._",
"ref/net451/System.Data.Common.dll",
"ref/netstandard1.2/System.Data.Common.dll",
"ref/netstandard1.2/System.Data.Common.xml",
"ref/netstandard1.2/de/System.Data.Common.xml",
"ref/netstandard1.2/es/System.Data.Common.xml",
"ref/netstandard1.2/fr/System.Data.Common.xml",
"ref/netstandard1.2/it/System.Data.Common.xml",
"ref/netstandard1.2/ja/System.Data.Common.xml",
"ref/netstandard1.2/ko/System.Data.Common.xml",
"ref/netstandard1.2/ru/System.Data.Common.xml",
"ref/netstandard1.2/zh-hans/System.Data.Common.xml",
"ref/netstandard1.2/zh-hant/System.Data.Common.xml",
"ref/portable-net451+win8+wp8+wpa81/System.Data.Common.dll",
"ref/portable-net451+win8+wp8+wpa81/System.Data.Common.xml",
"ref/portable-net451+win8+wp8+wpa81/de/System.Data.Common.xml",
"ref/portable-net451+win8+wp8+wpa81/es/System.Data.Common.xml",
"ref/portable-net451+win8+wp8+wpa81/fr/System.Data.Common.xml",
"ref/portable-net451+win8+wp8+wpa81/it/System.Data.Common.xml",
"ref/portable-net451+win8+wp8+wpa81/ja/System.Data.Common.xml",
"ref/portable-net451+win8+wp8+wpa81/ko/System.Data.Common.xml",
"ref/portable-net451+win8+wp8+wpa81/ru/System.Data.Common.xml",
"ref/portable-net451+win8+wp8+wpa81/zh-hans/System.Data.Common.xml",
"ref/portable-net451+win8+wp8+wpa81/zh-hant/System.Data.Common.xml",
"ref/xamarinios10/_._",
"ref/xamarinmac20/_._",
"ref/xamarintvos10/_._",
"ref/xamarinwatchos10/_._"
]
},
"Hangfire.Raven/2.0.0": {
"type": "project",
"path": "../Hangfire.Raven/project.json",
"msbuildProject": "../Hangfire.Raven/Hangfire.Raven.xproj"
}
},
"projectFileDependencyGroups": {
"": [
"Hangfire.Raven >= 2.0.0",
"System.ComponentModel.Primitives >= 4.1.0"
],
".NETFramework,Version=v4.5.2": []
},
"tools": {},
"projectFileToolGroups": {}
}
+54
View File
@@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
#if !NET45
using Microsoft.Extensions.DependencyModel;
#endif
namespace Hangfire.Raven
{
/// <summary>
/// Helper class for getting assemblies
/// </summary>
public static class Assemblies
{
/// <summary>
/// Get all loaded assemblies
/// </summary>
/// <returns></returns>
public static IEnumerable<Assembly> Get()
{
#if NET45
return AppDomain.CurrentDomain.GetAssemblies().Where(a => a != null);
#else
return (from compilationLibrary in DependencyContext.Default.CompileLibraries
where compilationLibrary.Name.Contains(typeof(Assemblies).Namespace)
select Assembly.Load(new AssemblyName(compilationLibrary.Name)))
.ToList();
#endif
}
/// <summary>
/// Get class by name within an assembly
/// </summary>
/// <param name="assembly">Assembly</param>
/// <param name="type">Name of type</param>
/// <returns></returns>
public static Type GetClass(Assembly assembly, string type)
{
return (from t in assembly.GetTypes()
where t.FullName == type //&& !t.IsAbstract && t.IsClass
select t).FirstOrDefault();
}
public static string GetAssemblyName(MemberInfo value)
{
#if NET45
return value.DeclaringType.Assembly.GetName().Name;
#else
return value.DeclaringType.GetTypeInfo().Assembly.GetName().Name;
#endif
}
}
}
@@ -1,209 +1,116 @@
// This file is part of Hangfire.
// Copyright 2013-2014 Sergey Odinokov.
//
// Hangfire is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation, either version 3
// of the License, or any later version.
//
// Hangfire is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.
using System;
using System.Linq;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading;
using Hangfire.Annotations;
using HangFire.Raven;
using Hangfire.Raven.Entities;
using Hangfire.Raven.Storage;
namespace Hangfire.Raven.DistributedLock
namespace Hangfire.Raven.DistributedLocks
{
public class RavenDistributedLock : IDisposable
{
private const string LockMode = "Exclusive";
private const string LockOwner = "Session";
private const int CommandTimeoutAdditionSeconds = 1;
private static readonly IDictionary<int, string> LockErrorMessages = new Dictionary<int, string>
{
{ -1, "The lock request timed out" },
{ -2, "The lock request was canceled" },
{ -3, "The lock request was chosen as a deadlock victim" },
{ -999, "Indicates a parameter validation or other call error" }
};
private static readonly ThreadLocal<Dictionary<string, int>> AcquiredLocks = new ThreadLocal<Dictionary<string, int>>(() => new Dictionary<string, int>());
private readonly object _lock = new object();
private RavenStorage _storage;
private string _resource;
private RavenStorageOptions _options;
private DistributedLock _distributedLock;
private Timer _heartbeatTimer;
private TimeSpan _timeout;
private Timer _heartbeatTimer = null;
private readonly List<string> _skipLocks = new List<string>()
{
"HangFire/job:"
};
private bool _completed;
public RavenDistributedLock([NotNull] RavenStorage storage, [NotNull] string resource, TimeSpan timeout, RavenStorageOptions options)
public RavenDistributedLock([NotNull] RavenStorage storage, [NotNull] string resource, TimeSpan timeout)
{
storage.ThrowIfNull("storage");
resource.ThrowIfNull("resource");
if ((timeout.TotalSeconds + CommandTimeoutAdditionSeconds) > int.MaxValue) {
throw new ArgumentException(string.Format("The timeout specified is too large. Please supply a timeout equal to or less than {0} seconds",
int.MaxValue - CommandTimeoutAdditionSeconds),
"timeout");
}
_timeout = timeout;
_storage = storage;
_resource = resource;
_options = options;
if (!AcquiredLocks.Value.ContainsKey(_resource)) {
Acquire(_resource, timeout);
AcquiredLocks.Value[_resource] = 1;
} else {
AcquiredLocks.Value[_resource]++;
}
// -- Skip some locks
if (!_skipLocks.Any(a => _resource.StartsWith(a)))
Lock();
}
public void Dispose()
{
if (_completed)
return;
_completed = true;
if (!AcquiredLocks.Value.ContainsKey(_resource))
return;
AcquiredLocks.Value[_resource]--;
if (AcquiredLocks.Value[_resource] != 0)
return;
Release(_resource);
AcquiredLocks.Value.Remove(_resource);
Release();
}
internal void Acquire(string resource, TimeSpan timeout)
private void Lock()
{
try {
RemoveDeadLocks(resource);
using (var session = _storage.Repository.OpenSession()) {
_distributedLock = new DistributedLock() {
ClientId = _storage.Options.ClientId,
Resource = _resource
};
// Check lock
DateTime lockTimeoutTime = DateTime.Now.Add(timeout);
bool isLockedBySomeoneElse;
bool isFirstAttempt = true;
do {
using (var repository = new Repository()) {
isLockedBySomeoneElse = repository.Session.Query<DistributedLocks>()
.FirstOrDefault(t => t.Resource == resource && t.ClientId != _options.ClientId) != null;
}
session.Store(_distributedLock);
session.Advanced.AddExpire(_distributedLock, DateTime.UtcNow + _timeout);
if (isFirstAttempt == true) {
isFirstAttempt = false;
} else {
Thread.Sleep((int)timeout.TotalMilliseconds / 10);
}
try {
// Blocking session!
session.Advanced.UseOptimisticConcurrency = true;
session.SaveChanges();
} catch (Exception e) {
_distributedLock = null;
throw new RavenDistributedLockException("Lock already given.", e);
}
while ((isLockedBySomeoneElse == true) && (lockTimeoutTime >= DateTime.Now));
// Set lock
if (isLockedBySomeoneElse == false) {
using (var repository = new Repository()) {
var distributedLocks = repository.Session.Query<DistributedLocks>().Where(t => t.Resource == resource).ToList();
if (!distributedLocks.Any()) {
distributedLocks.Add(new DistributedLocks
{
Resource = resource,
});
}
foreach (var distributedLock in distributedLocks) {
distributedLock.ClientId = _options.ClientId;
distributedLock.LockCount = 1;
distributedLock.Heartbeat = DateTime.UtcNow;
repository.Save(distributedLock);
}
}
StartHeartBeat(_resource);
} else {
throw new RavenDistributedLockException(string.Format("Could not place a lock on the resource '{0}': {1}.", resource, "The lock request timed out"));
}
} catch (Exception ex) {
if (ex is RavenDistributedLockException) {
throw;
} else {
throw new RavenDistributedLockException(string.Format("Could not place a lock on the resource '{0}': {1}.", resource, "Check inner exception for details"), ex);
}
Heartbeat();
}
}
internal void Release(string resource)
private void Release()
{
try {
RemoveDeadLocks(resource);
// Remove resource lock
using (var repository = new Repository()) {
var distributedLocks = repository.Session.Query<DistributedLocks>().Where(t => t.Resource == _resource && t.ClientId == _options.ClientId).ToList();
foreach (var distributedLock in distributedLocks) {
repository.Delete(distributedLock);
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;
}
} catch (Exception ex) {
throw new RavenDistributedLockException(string.Format("Could not release a lock on the resource '{0}': {1}.", _resource, "Check inner exception for details"), ex);
}
}
private void StartHeartBeat(string resource)
private void Heartbeat()
{
Console.WriteLine("Starting heartbeat for resource: " + resource);
TimeSpan timerInterval = TimeSpan.FromMilliseconds(_options.DistributedLockLifetime.TotalMilliseconds / 5);
Console.WriteLine(".Starting heartbeat for resource: {0}", _resource);
TimeSpan timerInterval = TimeSpan.FromMilliseconds(_timeout.TotalMilliseconds / 3);
_heartbeatTimer = new Timer(state =>
{
_heartbeatTimer = new Timer(state => {
try {
using (var repository = new Repository()) {
var distributedLocks = repository.Session.Query<DistributedLocks>().Where(t => t.Resource == resource && t.ClientId == _options.ClientId).ToList();
Console.WriteLine("..Heartbeat for resource {0}", _resource);
using (var session = _storage.Repository.OpenSession()) {
var distributedLock = session.Load<DistributedLock>(_distributedLock.Id);
foreach (var distributedLock in distributedLocks) {
distributedLock.Heartbeat = DateTime.UtcNow;
repository.Save(distributedLock);
}
session.Advanced.AddExpire(distributedLock, DateTime.UtcNow + _timeout);
session.SaveChanges();
}
} catch (Exception ex) {
Console.WriteLine("Unable to update heartbeat on the resource '{0}'", ex, resource);
Console.WriteLine("...Unable to update heartbeat on the resource '{0}'", ex, _resource);
Release();
}
}, null, timerInterval, timerInterval);
}
private void RemoveDeadLocks(string resource)
{
using (var repository = new Repository()) {
var heartBeat = DateTime.UtcNow.Subtract(_options.DistributedLockLifetime);
var deadLocks = repository.Session.Query<DistributedLocks>().Where(t => t.Resource == resource && t.Heartbeat < heartBeat).ToList();
foreach (var deadlock in deadLocks) {
repository.Delete(deadlock);
}
}
}
}
}
@@ -1,6 +1,6 @@
using System;
namespace Hangfire.Raven.DistributedLock
namespace Hangfire.Raven.DistributedLocks
{
/// <summary>
/// Represents exceptions for distributed lock implementation for MongoDB
@@ -5,11 +5,8 @@ namespace Hangfire.Raven.Entities
public class AggregatedCounter
{
public string Id { get; set; }
public string Key { get; set; }
public long Value { get; set; }
public DateTime? ExpireAt { get; set; }
}
}
+3 -12
View File
@@ -1,18 +1,9 @@
using Hangfire.Raven.Entities.Identity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Hangfire.Raven.Entities
namespace Hangfire.Raven.Entities
{
public class Counter : BaseEntity
public class Counter
{
public string Key { get; set; }
public string Id { get; set; }
public int Value { get; set; }
public DateTime? ExpireAt { get; set; }
}
}
+11 -10
View File
@@ -1,16 +1,17 @@
using Hangfire.Raven.Entities.Identity;
using System;
namespace Hangfire.Raven.Entities
namespace Hangfire.Raven.Entities
{
public class DistributedLocks : BaseEntity
public class DistributedLock
{
public string Id {
get {
return string.Format("DistributedLocks/{0}", Resource);
}
set {
}
}
public string Resource { get; set; }
public string ClientId { get; set; }
public int LockCount { get; set; }
public DateTime Heartbeat { get; set; }
}
}
@@ -3,7 +3,6 @@
public class EnqueuedAndFetchedCount
{
public int? EnqueuedCount { get; set; }
public int? FetchedCount { get; set; }
}
}
@@ -1,7 +0,0 @@
namespace Hangfire.Raven.Entities.Identity
{
public class BaseEntity
{
public string Id { get; set; }
}
}
@@ -1,27 +0,0 @@
// This file is part of Hangfire.
// Copyright © 2013-2014 Sergey Odinokov.
//
// Hangfire is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation, either version 3
// of the License, or any later version.
//
// Hangfire is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.
using Hangfire.Raven.Entities.Identity;
namespace Hangfire.Raven.Entities
{
internal class JobParameter : BaseEntity
{
public string JobId { get; set; }
public string Name { get; set; }
public string Value { get; set; }
}
}
+3 -3
View File
@@ -1,10 +1,10 @@
using Hangfire.Raven.Entities.Identity;
using System;
using System;
namespace Hangfire.Raven.Entities
{
public class JobQueue : BaseEntity
public class JobQueue
{
public string Id { get; set; }
public string JobId { get; set; }
public string Queue { get; set; }
public DateTime? FetchedAt { get; set; }
+10 -24
View File
@@ -1,29 +1,13 @@
// This file is part of Hangfire.
// Copyright © 2013-2014 Sergey Odinokov.
//
// Hangfire is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation, either version 3
// of the License, or any later version.
//
// Hangfire is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.
using Hangfire.Annotations;
using Hangfire.Storage;
using HangFire.Raven;
using Hangfire.Annotations;
using Hangfire.Raven.Storage;
using Hangfire.Storage;
namespace Hangfire.Raven.Entities
{
public class RavenFetchedJob : IFetchedJob
{
private readonly RavenStorage _storage;
private bool _requeued { get; set; }
private bool _removedFromQueue { get; set; }
private bool _disposed { get; set; }
@@ -48,12 +32,13 @@ namespace Hangfire.Raven.Entities
public void RemoveFromQueue()
{
using (var repository = new Repository()) {
var job = repository.Session.Load<JobQueue>(Id);
using (var repository = _storage.Repository.OpenSession()) {
var job = repository.Load<JobQueue>(Id);
if (job != null) {
repository.Delete(job);
}
repository.SaveChanges();
}
_removedFromQueue = true;
@@ -61,12 +46,13 @@ namespace Hangfire.Raven.Entities
public void Requeue()
{
using (var repository = new Repository()) {
var job = repository.Session.Load<JobQueue>(Id);
using (var repository = _storage.Repository.OpenSession()) {
var job = repository.Load<JobQueue>(Id);
job.FetchedAt = null;
repository.Save(job);
repository.Store(job);
repository.SaveChanges();
}
_requeued = true;
+8 -24
View File
@@ -1,31 +1,15 @@
// This file is part of Hangfire.
// Copyright © 2013-2014 Sergey Odinokov.
//
// Hangfire is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation, either version 3
// of the License, or any later version.
//
// Hangfire is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.
using Hangfire.Raven.Entities.Identity;
using System;
using System.Collections.Generic;
namespace Hangfire.Raven.Entities
{
public class RavenHash : BaseEntity
public class RavenHash
{
public string Key { get; set; }
public string Field { get; set; }
public string Value { get; set; }
public DateTime? ExpireAt { get; set; }
public RavenHash()
{
this.Fields = new Dictionary<string, string>();
}
public string JobId { get; set; }
public string Id { get; set; }
public Dictionary<string, string> Fields { get; set; }
}
}
+41 -26
View File
@@ -1,34 +1,49 @@
// This file is part of Hangfire.
// Copyright © 2013-2014 Sergey Odinokov.
//
// Hangfire is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation, either version 3
// of the License, or any later version.
//
// Hangfire is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.
using Hangfire.Raven.Entities.Identity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Hangfire.Common;
using Hangfire.Storage;
using Hangfire.Storage.Monitoring;
namespace Hangfire.Raven.Entities
{
internal class RavenJob : BaseEntity
public class RavenJob
{
public string InvocationData { get; set; }
public string Arguments { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime? ExpireAt { get; set; }
public RavenJob()
{
this.Parameters = new Dictionary<string, string>();
this.History = new List<StateHistoryDto>();
}
public class JobWrapper
{
public IEnumerable<object> Arguments { get; set; }
public Type Type { get; set; }
public MethodInfo Method { get; set; }
public string StateId { get; set; }
public string StateName { get; set; }
public string StateReason { get; set; }
public string StateData { get; set; }
public Job GetJob()
{
return new Job(this.Type, this.Method, this.Arguments.ToArray());
}
public static JobWrapper Create(Job job)
{
var toReturn = new JobWrapper();
toReturn.Arguments = job.Args;
toReturn.Method = job.Method;
toReturn.Type = job.Type;
return toReturn;
}
}
public string Id { get; set; }
public JobWrapper Job { get; set; }
public IDictionary<string, string> Parameters { get; set; }
public DateTime CreatedAt { get; set; }
public StateData StateData { get; set; }
public List<StateHistoryDto> History { get; set; }
}
}
+4 -7
View File
@@ -1,14 +1,11 @@
using Hangfire.Raven.Entities.Identity;
using System;
namespace Hangfire.Raven.Entities
namespace Hangfire.Raven.Entities
{
public class RavenList : BaseEntity
public class RavenList
{
public string Id { get; set; }
public string Key { get; set; }
public string Value { get; set; }
public DateTime ExpireAt { get; set; }
}
}
+12 -20
View File
@@ -1,27 +1,19 @@
// This file is part of Hangfire.
// Copyright © 2013-2014 Sergey Odinokov.
//
// Hangfire is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation, either version 3
// of the License, or any later version.
//
// Hangfire is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.
using Hangfire.Raven.Entities.Identity;
using System;
using System;
using System.Collections.Generic;
namespace Hangfire.Raven.Entities
{
public class RavenServer : BaseEntity
public class RavenServer
{
public string Data { get; set; }
public class ServerData
{
public int WorkerCount { get; set; }
public IEnumerable<string> Queues { get; set; }
public DateTime? StartedAt { get; set; }
}
public string Id { get; set; }
public DateTime LastHeartbeat { get; set; }
public ServerData Data { get; set; }
}
}
+8 -9
View File
@@ -1,16 +1,15 @@
using Hangfire.Raven.Entities.Identity;
using System;
using System.Collections.Generic;
namespace Hangfire.Raven.Entities
{
public class RavenSet : BaseEntity
public class RavenSet
{
public string Key { get; set; }
public RavenSet()
{
this.Scores = new Dictionary<string, double>();
}
public double Score { get; set; }
public string Value { get; set; }
public DateTime? ExpireAt { get; set; }
public string Id { get; set; }
public Dictionary<string, double> Scores { get; set; }
}
}
-27
View File
@@ -1,27 +0,0 @@
// This file is part of Hangfire.
// Copyright © 2013-2014 Sergey Odinokov.
//
// Hangfire is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation, either version 3
// of the License, or any later version.
//
// Hangfire is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.
using System;
namespace Hangfire.Raven.Entities
{
public class ServerData
{
public int WorkerCount { get; set; }
public string[] Queues { get; set; }
public DateTime? StartedAt { get; set; }
}
}
-30
View File
@@ -1,30 +0,0 @@
// This file is part of Hangfire.
// Copyright © 2013-2014 Sergey Odinokov.
//
// Hangfire is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation, either version 3
// of the License, or any later version.
//
// Hangfire is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.
using Hangfire.Raven.Entities.Identity;
using System;
namespace Hangfire.Raven.Entities
{
public class State : BaseEntity
{
public string JobId { get; set; }
public string Name { get; set; }
public string Reason { get; set; }
public DateTime CreatedAt { get; set; }
public string Data { get; set; }
}
}
-74
View File
@@ -1,74 +0,0 @@
// This file is part of Hangfire.
// Copyright © 2013-2014 Sergey Odinokov.
//
// Hangfire is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation, either version 3
// of the License, or any later version.
//
// Hangfire is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.
using System;
using System.Threading;
using Hangfire.Logging;
using Hangfire.Server;
using System.Linq;
using HangFire.Raven;
using Hangfire.Raven.Entities;
namespace Hangfire.Raven
{
public class ExpirationManager : IServerComponent
{
private static readonly ILog Logger = LogProvider.GetCurrentClassLogger();
private readonly TimeSpan _checkInterval;
public ExpirationManager()
: this(TimeSpan.FromHours(1))
{
}
public ExpirationManager(TimeSpan checkInterval)
{
_checkInterval = checkInterval;
}
public void Execute(CancellationToken cancellationToken)
{
using (var repository = new Repository()) {
var session = repository.Session;
var now = DateTime.UtcNow;
var counters = session.Query<Counter>().Where(t => t.ExpireAt <= now).ToList();
counters.ForEach(t => session.Delete(t));
var jobs = session.Query<RavenJob>().Where(t => t.ExpireAt <= now).ToList();
jobs.ForEach(t => session.Delete(t));
var hashes = session.Query<RavenHash>().Where(t => t.ExpireAt <= now).ToList();
hashes.ForEach(t => session.Delete(t));
var lists = session.Query<RavenList>().Where(t => t.ExpireAt <= now).ToList();
lists.ForEach(t => session.Delete(t));
var sets = session.Query<RavenSet>().Where(t => t.ExpireAt <= now).ToList();
sets.ForEach(t => session.Delete(t));
}
cancellationToken.WaitHandle.WaitOne(_checkInterval);
}
public override string ToString()
{
return "Raven Expiration Manager";
}
}
}
@@ -1,2 +0,0 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=DataTypes/@EntryIndexedValue">False</s:Boolean></wpf:ResourceDictionary>
-154
View File
@@ -1,154 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{A523C0E3-097D-4869-977F-15A717EA3E83}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Hangfire.Raven</RootNamespace>
<AssemblyName>Hangfire.Raven</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<TargetFrameworkProfile />
<NuGetPackageImportStamp>5f064ca7</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Debug\Hangfire.Raven.xml</DocumentationFile>
<Prefer32Bit>false</Prefer32Bit>
<NoWarn>1591</NoWarn>
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>bin\Release\Hangfire.Raven.xml</DocumentationFile>
<Prefer32Bit>false</Prefer32Bit>
<NoWarn>1591</NoWarn>
</PropertyGroup>
<ItemGroup>
<Reference Include="Dewey.Net, Version=3.3.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Dewey.Net.3.3.0.0\lib\net45\Dewey.Net.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Hangfire.Core, Version=1.5.3.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Hangfire.Core.1.5.3\lib\net45\Hangfire.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Hangfire.SqlServer, Version=1.5.3.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Hangfire.SqlServer.1.5.3\lib\net45\Hangfire.SqlServer.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Owin, Version=3.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Owin.Host.SystemWeb, Version=3.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.Owin.Host.SystemWeb.3.0.1\lib\net45\Microsoft.Owin.Host.SystemWeb.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Owin">
<HintPath>..\..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
</Reference>
<Reference Include="Raven.Abstractions, Version=3.0.0.0, Culture=neutral, PublicKeyToken=37f41c7f99471593, processorArchitecture=MSIL">
<HintPath>..\..\packages\RavenDB.Database.3.0.30037\lib\net45\Raven.Abstractions.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Raven.Client.Lightweight, Version=3.0.0.0, Culture=neutral, PublicKeyToken=37f41c7f99471593, processorArchitecture=MSIL">
<HintPath>..\..\packages\RavenDB.Client.3.0.30037\lib\net45\Raven.Client.Lightweight.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Raven.Database, Version=3.0.0.0, Culture=neutral, PublicKeyToken=37f41c7f99471593, processorArchitecture=MSIL">
<HintPath>..\..\packages\RavenDB.Database.3.0.30037\lib\net45\Raven.Database.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="System.Transactions" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="DistributedLocks\RavenDistributedLock.cs" />
<Compile Include="DistributedLocks\RavenDistributedLockException.cs" />
<Compile Include="Entities\AggregatedCounter.cs" />
<Compile Include="Entities\Counter.cs" />
<Compile Include="Entities\DistributedLocks.cs" />
<Compile Include="Entities\EnqueuedAndFetchedCount.cs" />
<Compile Include="Entities\JobQueue.cs" />
<Compile Include="Entities\RavenHash.cs" />
<Compile Include="Entities\Identity\BaseEntity.cs" />
<Compile Include="Entities\RavenList.cs" />
<Compile Include="Entities\RavenSet.cs" />
<Compile Include="ExpirationManager.cs" />
<Compile Include="Entities\JobParameter.cs" />
<Compile Include="Entities\RavenJob.cs" />
<Compile Include="Entities\State.cs" />
<Compile Include="Entities\RavenServer.cs" />
<Compile Include="Entities\ServerData.cs" />
<Compile Include="Extensions\DatabaseExtensions.cs" />
<Compile Include="JobQueues\IPersistentJobQueue.cs" />
<Compile Include="JobQueues\IPersistentJobQueueMonitoringApi.cs" />
<Compile Include="JobQueues\IPersistentJobQueueProvider.cs" />
<Compile Include="Listeners\TakeNewestConflictResolutionListener.cs" />
<Compile Include="JobQueues\PersistentJobQueueProviderCollection.cs" />
<Compile Include="JobQueues\RavenJobQueue.cs" />
<Compile Include="Entities\RavenFetchedJob.cs" />
<Compile Include="ObjectExtensions.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="JobQueues\RavenJobQueueProvider.cs" />
<Compile Include="Repository.cs" />
<Compile Include="JobQueues\RavenJobQueueMonitoringApi.cs" />
<Compile Include="Storage\RavenStorage.cs" />
<Compile Include="Storage\RavenStorageExtensions.cs" />
<Compile Include="Storage\RavenStorageOptions.cs" />
<Compile Include="RavenConnection.cs" />
<Compile Include="RavenWriteOnlyTransaction.cs" />
<Compile Include="StateHandlers\DeletedStateHandler.cs" />
<Compile Include="StateHandlers\FailedStateHandler.cs" />
<Compile Include="StateHandlers\ProcessingStateHandler.cs" />
<Compile Include="StateHandlers\SucceededStateHandler.cs" />
</ItemGroup>
<ItemGroup>
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="Hangfire.Raven.nuspec" />
<None Include="packages.config" />
<None Include="Raven.Studio.Html5.zip">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
-29
View File
@@ -1,29 +0,0 @@
<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>$id$</id>
<version>$version$</version>
<title>$title$</title>
<description>$description$</description>
<authors>Axial Commerce</authors>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<copyright>Copyright © Axial Commerce</copyright>
<licenseUrl>http://www.apache.org/licenses/LICENSE-2.0</licenseUrl>
<projectUrl>https://github.com/axial/hangfire-ravendb</projectUrl>
<dependencies>
<group targetFramework="net45">
<dependency id="Dewey.Net" version="3.3.0.0" />
<dependency id="Hangfire" version="1.5.3" />
<dependency id="Hangfire.Core" version="1.5.3" />
<dependency id="Hangfire.SqlServer" version="1.5.3" />
<dependency id="Microsoft.Owin" version="3.0.1" />
<dependency id="Microsoft.Owin.Host.SystemWeb" version="3.0.1" />
<dependency id="Newtonsoft.Json" version="8.0.2" />
<dependency id="Owin" version="1.0" />
<dependency id="RavenDB.Client" version="3.0.30037" />
<dependency id="RavenDB.Database" version="3.0.30037" />
<dependency id="RavenDB.Embedded" version="3.0.30037" />
</group>
</dependencies>
</metadata>
</package>
+19
View File
@@ -0,0 +1,19 @@
<?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>ba18f6bf-5613-4b9d-a57a-c05c51148ec5</ProjectGuid>
<RootNamespace>Hangfire.Raven</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>
@@ -0,0 +1,28 @@
using System;
using System.Linq;
using Hangfire.Raven.Entities;
using Raven.Client.Indexes;
namespace Hangfire.Raven.Indexes
{
public class Hangfire_RavenJobs
: AbstractIndexCreationTask<RavenJob>
{
public class Mapping
{
public DateTime CreatedAt { get; set; }
public DateTime? ExpireAt { get; set; }
public string StateName { get; set; }
}
public Hangfire_RavenJobs()
{
Map = results => from result in results
select new Mapping {
StateName = result.StateData.Name,
CreatedAt = result.CreatedAt
};
this.Analyze("StateName", "WhitespaceAnalyzer");
}
}
}
@@ -0,0 +1,21 @@
using System;
using Raven.Abstractions.Indexing;
using Raven.Client.Indexes;
namespace Hangfire.Raven.Indexes
{
public class Raven_DocumentsByEntityName
: AbstractIndexCreationTask
{
public override IndexDefinition CreateIndexDefinition()
{
return null;
}
public class Mapping
{
public string Tag { get; set; }
public DateTime LastModified { get; set; }
}
}
}
@@ -1,19 +1,3 @@
// This file is part of Hangfire.
// Copyright © 2013-2014 Sergey Odinokov.
//
// Hangfire is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation, either version 3
// of the License, or any later version.
//
// Hangfire is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.
using System;
using System.Collections;
using System.Collections.Generic;
@@ -23,7 +7,7 @@ namespace Hangfire.Raven.JobQueues
public class PersistentJobQueueProviderCollection : IEnumerable<IPersistentJobQueueProvider>
{
private readonly List<IPersistentJobQueueProvider> _providers
= new List<IPersistentJobQueueProvider>();
= new List<IPersistentJobQueueProvider>();
private readonly Dictionary<string, IPersistentJobQueueProvider> _providersByQueue
= new Dictionary<string, IPersistentJobQueueProvider>(StringComparer.OrdinalIgnoreCase);
@@ -45,15 +29,14 @@ namespace Hangfire.Raven.JobQueues
_providers.Add(provider);
foreach (var queue in queues)
{
foreach (var queue in queues) {
_providersByQueue.Add(queue, provider);
}
}
public IPersistentJobQueueProvider GetProvider(string queue)
{
return _providersByQueue.ContainsKey(queue)
return _providersByQueue.ContainsKey(queue)
? _providersByQueue[queue]
: _defaultProvider;
}
+59 -55
View File
@@ -1,37 +1,21 @@
// This file is part of Hangfire.
// Copyright © 2013-2014 Sergey Odinokov.
//
// Hangfire is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation, either version 3
// of the License, or any later version.
//
// Hangfire is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading;
using Hangfire.Annotations;
using Hangfire.Storage;
using HangFire.Raven;
using Raven.Client.Linq;
using System.Linq;
using Hangfire.Raven.Entities;
using Hangfire.Raven.Storage;
using Hangfire.Storage;
using Raven.Abstractions.Data;
namespace Hangfire.Raven.JobQueues
{
public class RavenJobQueue : IPersistentJobQueue
public class RavenJobQueue
: IPersistentJobQueue
{
private readonly RavenStorage _storage;
private readonly RavenStorageOptions _options;
private static object _lockMethod = new object();
private BlockingCollection<string> _queue;
public RavenJobQueue([NotNull] RavenStorage storage, RavenStorageOptions options)
{
@@ -40,6 +24,31 @@ namespace Hangfire.Raven.JobQueues
_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]
@@ -51,51 +60,46 @@ namespace Hangfire.Raven.JobQueues
throw new ArgumentException("Queue array must be non-empty.", "queues");
}
lock (_lockMethod) {
JobQueue fetchedJob;
cancellationToken.ThrowIfCancellationRequested();
do {
using (var repository = new Repository()) {
fetchedJob = repository.Session.Query<JobQueue>().FirstOrDefault(t => t.FetchedAt == null
&& t.Queue.In(queues));
if (fetchedJob == null) {
fetchedJob = repository.Session.Query<JobQueue>().FirstOrDefault(t =>
t.FetchedAt < DateTime.UtcNow.AddSeconds(_options.InvisibilityTimeout.Negate().TotalSeconds)
&& t.Queue.In(queues));
if (fetchedJob == null) {
cancellationToken.WaitHandle.WaitOne(_options.QueuePollInterval);
cancellationToken.ThrowIfCancellationRequested();
continue;
}
}
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);
repository.Save(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);
}
}
while (fetchedJob == null);
return new RavenFetchedJob(_storage, fetchedJob);
}
public void Enqueue(string queue, string jobId)
{
using (var repository = new Repository()) {
var jobQueue = new JobQueue
{
using (var repository = _storage.Repository.OpenSession()) {
var jobQueue = new JobQueue {
Id = Repository.GetId(typeof(JobQueue), queue, jobId),
JobId = jobId,
Queue = queue
};
repository.Save(jobQueue);
repository.Store(jobQueue);
repository.SaveChanges();
}
}
}
@@ -1,33 +1,27 @@
// This file is part of Hangfire.
// Copyright © 2013-2014 Sergey Odinokov.
//
// Hangfire is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation, either version 3
// of the License, or any later version.
//
// Hangfire is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using Hangfire.Annotations;
using Hangfire.Raven.Entities;
using HangFire.Raven;
using Hangfire.Raven.JobQueues;
using Hangfire.Raven.Storage;
namespace Hangfire.Raven.JobQueues
{
internal class RavenJobQueueMonitoringApi : IPersistentJobQueueMonitoringApi
internal class RavenJobQueueMonitoringApi
: IPersistentJobQueueMonitoringApi
{
private RavenStorage _storage;
public RavenJobQueueMonitoringApi([NotNull] RavenStorage storage)
{
storage.ThrowIfNull("storage");
_storage = storage;
}
public IEnumerable<string> GetQueues()
{
using (var repository = new Repository()) {
return repository.Session.Query<JobQueue>()
using (var repository = _storage.Repository.OpenSession()) {
return repository.Query<JobQueue>()
.Select(x => x.Queue)
.Distinct()
.ToList();
@@ -39,25 +33,26 @@ namespace Hangfire.Raven.JobQueues
int start = @from + 1;
int end = from + perPage;
using (var repository = new Repository()) {
var jobs = repository.Session.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)
.ToList();
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)
.ToList();
var results = new List<RavenJob>();
foreach (var item in jobs) {
var job = repository.Session.Query<RavenJob>().FirstOrDefault(t => t.Id == item.JobId);
var job = repository.Load<RavenJob>(item.JobId);
if (job != null && repository.Session.Query<State>().FirstOrDefault(t => t.Id == job.StateId) != null) {
if (job.StateData != null)
results.Add(job);
}
}
return results.Select(t => t.Id).ToList();
return results.Select(t => t.Id.Split(new char[] { '/' }, 2)[1]).ToList();
}
}
public IEnumerable<string> GetFetchedJobIds(string queue, int @from, int perPage)
@@ -65,8 +60,8 @@ namespace Hangfire.Raven.JobQueues
int start = @from + 1;
int end = from + perPage;
using (var repository = new Repository()) {
var jobs = repository.Session.Query<JobQueue>().Where(t => t.Queue == queue && t.FetchedAt != null)
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);
@@ -74,10 +69,10 @@ namespace Hangfire.Raven.JobQueues
var results = new List<string>();
foreach (var item in jobs) {
var job = repository.Session.Query<RavenJob>().FirstOrDefault(t => t.Id == item.JobId);
var job = repository.Load<RavenJob>(item.JobId);
if (job != null) {
results.Add(job.Id);
results.Add(job.Id.Split(new char[] { '/' }, 2)[1]);
}
}
@@ -87,13 +82,11 @@ namespace Hangfire.Raven.JobQueues
public EnqueuedAndFetchedCount GetEnqueuedAndFetchedCount(string queue)
{
using (var repository = new Repository()) {
int enqueuedCount = repository.Session.Query<JobQueue>().Where(t => t.Queue == queue && t.FetchedAt == null).Count();
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();
int fetchedCount = repository.Session.Query<JobQueue>().Where(t => t.Queue == queue && t.FetchedAt != null).Count();
return new EnqueuedAndFetchedCount
{
return new EnqueuedAndFetchedCount {
EnqueuedCount = enqueuedCount,
FetchedCount = fetchedCount
};
@@ -1,26 +1,10 @@
// This file is part of Hangfire.
// Copyright © 2013-2014 Sergey Odinokov.
//
// Hangfire is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation, either version 3
// of the License, or any later version.
//
// Hangfire is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.
using System;
using Hangfire.Annotations;
using Hangfire.Annotations;
using Hangfire.Raven.Storage;
namespace Hangfire.Raven.JobQueues
{
public class RavenJobQueueProvider : IPersistentJobQueueProvider
public class RavenJobQueueProvider
: IPersistentJobQueueProvider
{
private readonly IPersistentJobQueue _jobQueue;
private readonly IPersistentJobQueueMonitoringApi _monitoringApi;
@@ -31,7 +15,7 @@ namespace Hangfire.Raven.JobQueues
options.ThrowIfNull("options");
_jobQueue = new RavenJobQueue(storage, options);
//_monitoringApi = new RavenJobQueueMonitoringApi(storage);
_monitoringApi = new RavenJobQueueMonitoringApi(storage);
}
public IPersistentJobQueue GetJobQueue()
@@ -1,10 +1,11 @@
using Raven.Abstractions.Data;
using System.Linq;
using Raven.Abstractions.Data;
using Raven.Client.Listeners;
using System.Linq;
namespace Hangfire.Raven.Listeners
{
public class TakeNewestConflictResolutionListener : IDocumentConflictListener
public class TakeNewestConflictResolutionListener
: IDocumentConflictListener
{
public bool TryResolveConflict(string key, JsonDocument[] conflictedDocs, out JsonDocument resolvedDocument)
{
+13 -11
View File
@@ -2,16 +2,18 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("Hangfire Raven")]
[assembly: AssemblyDescription("Raven job storage for Hangfire")]
[assembly: Guid("3d96bf2f-8854-4872-aee3-faf81d121a4d")]
// 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.Core")]
[assembly: AssemblyTrademark("")]
[assembly: InternalsVisibleTo("Hangfire.Raven.Tests")]
// Allow the generation of mocks for internal types
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
[assembly: AssemblyCompany("Axial Commerce")]
[assembly: AssemblyProduct("Hangfire Raven")]
[assembly: AssemblyCopyright("Copyright © Axial Commerce")]
[assembly: AssemblyVersion("1.8")]
[assembly: AssemblyFileVersion("1.8")]
// 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("ba18f6bf-5613-4b9d-a57a-c05c51148ec5")]
Binary file not shown.
+189 -242
View File
@@ -19,19 +19,17 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Hangfire.Common;
using Hangfire.Server;
using Hangfire.Storage;
using Raven.Client;
using Hangfire.Raven.DistributedLock;
using HangFire.Raven;
using Hangfire.Raven.DistributedLocks;
using Hangfire.Raven.Entities;
using Hangfire.Raven.Storage;
using Hangfire.Server;
using Hangfire.Storage;
using static Hangfire.Raven.Entities.RavenJob;
namespace Hangfire.Raven
{
public class RavenConnection : JobStorageConnection
{
private readonly RavenStorageOptions _options;
private readonly RavenStorage _storage;
public RavenConnection(RavenStorage ravenStorage)
@@ -39,33 +37,16 @@ namespace Hangfire.Raven
ravenStorage.ThrowIfNull("RavenStorage");
_storage = ravenStorage;
_options = new RavenStorageOptions();
}
public RavenConnection(RavenStorage ravenStorage, RavenStorageOptions options)
: this(ravenStorage)
{
options.ThrowIfNull("options");
public override IWriteOnlyTransaction CreateWriteTransaction() => new RavenWriteOnlyTransaction(_storage);
_storage = ravenStorage;
_options = options;
}
public override IWriteOnlyTransaction CreateWriteTransaction()
{
return new RavenWriteOnlyTransaction(_storage);
}
public override IDisposable AcquireDistributedLock(string resource, TimeSpan timeout)
{
return new RavenDistributedLock(_storage, string.Format("HangFire:{0}", resource), timeout, _options);
}
public static IDocumentStore _documentStore;
public override IDisposable AcquireDistributedLock(string resource, TimeSpan timeout) => new RavenDistributedLock(_storage, string.Format("HangFire/{0}", resource), timeout);
public override IFetchedJob FetchNextJob(string[] queues, CancellationToken cancellationToken)
{
queues.ThrowIfNull("queues");
if (queues.Length == 0) {
throw new ArgumentNullException("queues");
}
@@ -82,6 +63,7 @@ namespace Hangfire.Raven
}
var persistentQueue = providers[0].GetJobQueue();
return persistentQueue.Dequeue(queues, cancellationToken);
}
@@ -94,64 +76,43 @@ namespace Hangfire.Raven
job.ThrowIfNull("job");
parameters.ThrowIfNull("parameters");
var invocationData = InvocationData.Serialize(job);
using (var repository = _storage.Repository.OpenSession()) {
var guid = Guid.NewGuid().ToString();
var ravenJob = new RavenJob
{
Id = Guid.NewGuid().ToString(),
InvocationData = JobHelper.ToJson(invocationData),
Arguments = invocationData.Arguments,
CreatedAt = createdAt,
ExpireAt = createdAt.Add(expireIn)
};
var ravenJob = new RavenJob {
Id = Repository.GetId(typeof(RavenJob), guid),
Job = JobWrapper.Create(job),
CreatedAt = createdAt,
Parameters = parameters
};
using (var repository = new Repository()) {
repository.Save(ravenJob);
repository.Store(ravenJob);
repository.Advanced.AddExpire(ravenJob, createdAt + expireIn);
if (parameters.Count > 0) {
foreach (var parameter in parameters) {
repository.Save(new JobParameter
{
JobId = ravenJob.Id,
Name = parameter.Key,
Value = parameter.Value
});
}
}
repository.SaveChanges();
return ravenJob.Id.ToString();
return guid;
}
}
public override JobData GetJobData(string id)
public override JobData GetJobData(string key)
{
id.ThrowIfNull("id");
key.ThrowIfNull("key");
using (var repository = new Repository()) {
var jobData = repository.Session.Load<RavenJob>(id);
using (var repository = _storage.Repository.OpenSession()) {
var id = Repository.GetId(typeof(RavenJob), key);
var jobData = repository.Load<RavenJob>(id);
if (jobData == null) {
return null;
}
var invocationData = JobHelper.FromJson<InvocationData>(jobData.InvocationData);
invocationData.Arguments = jobData.Arguments;
var job = jobData.Job.GetJob();
Job job = null;
JobLoadException loadException = null;
try {
job = invocationData.Deserialize();
} catch (JobLoadException ex) {
loadException = ex;
}
return new JobData
{
return new JobData {
Job = job,
State = jobData.StateName,
CreatedAt = jobData.CreatedAt,
LoadException = loadException
State = jobData.StateData?.Name,
CreatedAt = jobData.CreatedAt
};
}
}
@@ -160,61 +121,56 @@ namespace Hangfire.Raven
{
jobId.ThrowIfNull("jobId");
using (var repository = new Repository()) {
RavenJob job = repository.Session.Load<RavenJob>(jobId);
using (var repository = _storage.Repository.OpenSession()) {
var id = Repository.GetId(typeof(RavenJob), jobId);
var job = repository.Load<RavenJob>(id);
if (job == null) {
return null;
}
State state = repository.Session.Query<State>().FirstOrDefault(t => t.Id == job.StateId);
if (state == null) {
return job.StateData;
}
}
public override void SetJobParameter(string jobId, string name, string value)
{
jobId.ThrowIfNull("jobId");
name.ThrowIfNull("name");
using (var repository = _storage.Repository.OpenSession()) {
var id = Repository.GetId(typeof(RavenJob), jobId);
var job = repository.Load<RavenJob>(id);
job.Parameters[name] = value;
repository.SaveChanges();
}
}
public override string GetJobParameter(string jobId, string name)
{
jobId.ThrowIfNull("jobId");
name.ThrowIfNull("name");
using (var repository = _storage.Repository.OpenSession()) {
var id = Repository.GetId(typeof(RavenJob), jobId);
var job = repository.Load<RavenJob>(id);
string value;
if (!job.Parameters.TryGetValue(name, out value)) {
if (name == "RetryCount") {
job.Parameters["RetryCount"] = "0";
repository.SaveChanges();
return "0";
}
return null;
}
return new StateData
{
Name = state.Name,
Reason = state.Reason,
Data = JobHelper.FromJson<Dictionary<string, string>>(state.Data)
};
}
}
public override void SetJobParameter(string id, string name, string value)
{
id.ThrowIfNull("id");
name.ThrowIfNull("name");
using (var repository = new Repository()) {
var results = repository.Session.Query<JobParameter>().Where(t => t.JobId == id && t.Name == name).ToList();
results.ForEach(t => {
t.Value = value;
repository.Save(t);
});
}
}
public override string GetJobParameter(string id, string name)
{
id.ThrowIfNull("id");
name.ThrowIfNull("name");
using (var repository = new Repository()) {
var jobParameter = repository.Session.Query<JobParameter>().FirstOrDefault(t => t.JobId == id && t.Name == name);
if (jobParameter == null && name == "RetryCount") {
jobParameter = new JobParameter
{
JobId = id,
Name = name,
Value = "0"
};
repository.Save(jobParameter);
}
return jobParameter != null ? jobParameter.Value : null;
return value;
}
}
@@ -222,10 +178,15 @@ namespace Hangfire.Raven
{
key.ThrowIfNull("key");
using (var repository = new Repository()) {
var results = repository.Session.Query<RavenSet>().Where(t => t.Key == key).Select(t => t.Value).ToList();
using (var repository = _storage.Repository.OpenSession()) {
var id = Repository.GetId(typeof(RavenSet), key);
var set = repository.Load<RavenSet>(id);
return new HashSet<string>(results);
if (set == null) {
return null;
}
return new HashSet<string>(set.Scores.Keys);
}
}
@@ -237,16 +198,15 @@ namespace Hangfire.Raven
throw new ArgumentException("The `toScore` value must be higher or equal to the `fromScore` value.");
}
using (var repository = new Repository()) {
var set = repository.Session.Query<RavenSet>()
.Where(t => t.Key == key)
.Where(t => t.Score >= fromScore)
.Where(t => t.Score <= toScore)
.OrderBy(t => t.Score)
.FirstOrDefault();
using (var repository = _storage.Repository.OpenSession()) {
var id = Repository.GetId(typeof(RavenSet), key);
var set = repository.Load<RavenSet>(id);
if (set == null) {
return null;
}
return set != null ? set.Value : null;
return set.Scores.OrderBy(a => a.Value).Select(a => a.Key).FirstOrDefault();
}
}
@@ -255,16 +215,23 @@ namespace Hangfire.Raven
key.ThrowIfNull("key");
keyValuePairs.ThrowIfNull("keyValuePairs");
using (var repository = new Repository()) {
foreach (var keyValuePair in keyValuePairs) {
var results = repository.Session.Query<RavenHash>().Where(t => t.Key == key && t.Field == keyValuePair.Key).ToList();
using (var repository = _storage.Repository.OpenSession()) {
var id = Repository.GetId(typeof(RavenHash), key);
var result = repository.Load<RavenHash>(id);
results.ForEach(t =>
{
t.Value = keyValuePair.Value;
repository.Save(t);
});
if (result == null) {
result = new RavenHash() {
Id = id
};
repository.Store(result);
}
foreach (var keyValuePair in keyValuePairs) {
result.Fields[keyValuePair.Key] = keyValuePair.Value;
}
repository.SaveChanges();
}
}
@@ -272,11 +239,10 @@ namespace Hangfire.Raven
{
key.ThrowIfNull("key");
using (var session = new Repository().Session) {
var result = session.Query<RavenHash>().Where(t => t.Key == key)
.ToDictionary(x => x.Field, x => x.Value);
using (var repository = _storage.Repository.OpenSession()) {
var result = repository.Load<RavenHash>(Repository.GetId(typeof(RavenHash), key));
return result.Count != 0 ? result : null;
return result?.Fields;
}
}
@@ -285,36 +251,26 @@ namespace Hangfire.Raven
serverId.ThrowIfNull("serverId");
context.ThrowIfNull("context");
var data = new ServerData
{
WorkerCount = context.WorkerCount,
Queues = context.Queues,
StartedAt = DateTime.UtcNow,
};
using (var repository = _storage.Repository.OpenSession()) {
var id = Repository.GetId(typeof(RavenServer), serverId);
var server = repository.Load<RavenServer>(id);
using (var repository = new Repository()) {
var servers = repository.Session.Query<RavenServer>()
.Where(t => t.Id == serverId)
.ToList();
if (server == null) {
server = new RavenServer() {
Id = id,
Data = new RavenServer.ServerData()
};
var queues = servers.SelectMany(t => JobHelper.FromJson<ServerData>(t.Data).Queues).ToList();
queues.AddRange(data.Queues.Select(t => t).ToList());
data.Queues = queues.ToArray();
data.WorkerCount += servers.Select(t => JobHelper.FromJson<ServerData>(t.Data).WorkerCount).Sum(t => t);
foreach (var item in servers) {
repository.Delete(item);
repository.Store(server);
}
var server = new RavenServer
{
Data = JobHelper.ToJson(data),
Id = serverId,
LastHeartbeat = DateTime.UtcNow
};
// Merge Queues and WorkerCount
server.Data.WorkerCount += context.WorkerCount;
server.Data.Queues = context.Queues.Concat(server.Data.Queues ?? new List<string>()).Distinct();
repository.Save(server);
server.LastHeartbeat = DateTime.UtcNow;
repository.SaveChanges();
}
}
@@ -322,14 +278,12 @@ namespace Hangfire.Raven
{
serverId.ThrowIfNull("serverId");
using (var repository = new Repository()) {
var servers = repository.Session.Query<RavenServer>()
.Where(t => t.Id == serverId)
.ToList();
using (var repository = _storage.Repository.OpenSession()) {
var id = Repository.GetId(typeof(RavenServer), serverId);
foreach (var item in servers) {
repository.Delete(item);
}
repository.Delete(id);
repository.SaveChanges();
}
}
@@ -337,19 +291,19 @@ namespace Hangfire.Raven
{
serverId.ThrowIfNull("serverId");
using (var repository = new Repository()) {
var server = repository.Session.Load<RavenServer>(serverId);
using (var repository = _storage.Repository.OpenSession()) {
var id = Repository.GetId(typeof(RavenServer), serverId);
var server = repository.Load<RavenServer>(id);
if (server == null) {
server = new RavenServer
{
Id = serverId
server = new RavenServer {
Id = id
};
}
server.LastHeartbeat = DateTime.UtcNow;
repository.Save(server);
repository.SaveChanges();
}
}
@@ -359,10 +313,10 @@ namespace Hangfire.Raven
throw new ArgumentException("The `timeOut` value must be positive.", "timeOut");
}
using (var repository = new Repository()) {
using (var repository = _storage.Repository.OpenSession()) {
var heartBeatCutOff = DateTime.UtcNow.Add(timeOut.Negate());
var results = repository.Session.Query<RavenServer>()
var results = repository.Query<RavenServer>()
.Where(t => t.LastHeartbeat < heartBeatCutOff)
.ToList();
@@ -370,6 +324,8 @@ namespace Hangfire.Raven
repository.Delete(item);
}
repository.SaveChanges();
return results.Count;
}
}
@@ -378,12 +334,15 @@ namespace Hangfire.Raven
{
key.ThrowIfNull("key");
using (var repository = new Repository()) {
var count = repository.Session.Query<RavenSet>()
.Where(t => t.Key == key)
.Count();
using (var repository = _storage.Repository.OpenSession()) {
var id = Repository.GetId(typeof(RavenSet), key);
var set = repository.Load<RavenSet>(id);
return count;
if (set == null) {
return 0;
}
return set.Scores.Count;
}
}
@@ -391,15 +350,18 @@ namespace Hangfire.Raven
{
key.ThrowIfNull("key");
using (var repository = new Repository()) {
var values = repository.Session.Query<RavenSet>()
.Where(t => t.Key == key)
.Skip(startingFrom - 1)
.Take(endingAt - startingFrom + 1)
.Select(t => t.Value)
.ToList();
using (var repository = _storage.Repository.OpenSession()) {
var id = Repository.GetId(typeof(RavenSet), key);
var set = repository.Load<RavenSet>(id);
return values;
if (set == null) {
return new List<string>();
}
return set.Scores.Skip(startingFrom - 1)
.Take(endingAt - startingFrom + 1)
.Select(t => t.Key)
.ToList();
}
}
@@ -407,17 +369,17 @@ namespace Hangfire.Raven
{
key.ThrowIfNull("key");
using (var repository = new Repository()) {
var expireAt = repository.Session.Query<RavenSet>()
.Where(t => t.Key == key)
.Select(t => t.ExpireAt)
.ToArray();
using (var repository = _storage.Repository.OpenSession()) {
var id = Repository.GetId(typeof(RavenSet), key);
var set = repository.Load<RavenSet>(id);
if (expireAt.Any() == false) {
var expireAt = repository.Advanced.GetExpire(set);
if (expireAt == null) {
return TimeSpan.FromSeconds(-1);
}
return expireAt.Where(t => t.HasValue).Min(t => t.Value) - DateTime.UtcNow;
return expireAt.Value - DateTime.UtcNow;
}
}
@@ -425,8 +387,8 @@ namespace Hangfire.Raven
{
key.ThrowIfNull("key");
using (var repository = new Repository()) {
var values = repository.Session.Query<AggregatedCounter>()
using (var repository = _storage.Repository.OpenSession()) {
var values = repository.Query<AggregatedCounter>()
.Where(t => t.Key == key)
.Select(t => t.Value)
.ToArray();
@@ -439,12 +401,10 @@ namespace Hangfire.Raven
{
key.ThrowIfNull("key");
using (var repository = new Repository()) {
var count = repository.Session.Query<RavenHash>()
.Where(t => t.Key == key)
.Count();
using (var repository = _storage.Repository.OpenSession()) {
var ravenHash = repository.Load<RavenHash>(Repository.GetId(typeof(RavenHash), key));
return count;
return ravenHash.Fields.Count;
}
}
@@ -452,18 +412,16 @@ namespace Hangfire.Raven
{
key.ThrowIfNull("key");
using (var repository = new Repository()) {
var hashes = repository.Session.Query<RavenHash>()
.Where(t => t.Key == key)
.ToList();
using (var repository = _storage.Repository.OpenSession()) {
var ravenHash = repository.Load<RavenHash>(Repository.GetId(typeof(RavenHash), key));
DateTime? result = hashes.Any() ? hashes.Min(x => x.ExpireAt) : null;
var expireAt = repository.Advanced.GetExpire(ravenHash);
if (!result.HasValue) {
if (!expireAt.HasValue) {
return TimeSpan.FromSeconds(-1);
}
return result.Value - DateTime.UtcNow;
return expireAt.Value - DateTime.UtcNow;
}
}
@@ -472,13 +430,16 @@ namespace Hangfire.Raven
key.ThrowIfNull("key");
name.ThrowIfNull("name");
using (var repository = new Repository()) {
var result = repository.Session.Query<RavenHash>()
.Where(t => t.Key == key)
.Where(t => t.Field == name)
.FirstOrDefault();
using (var repository = _storage.Repository.OpenSession()) {
var ravenHash = repository.Load<RavenHash>(Repository.GetId(typeof(RavenHash), key));
return result != null ? result.Value : null;
string result;
if (!ravenHash.Fields.TryGetValue(name, out result)) {
return null;
}
return result;
}
}
@@ -486,12 +447,10 @@ namespace Hangfire.Raven
{
key.ThrowIfNull("key");
using (var repository = new Repository()) {
var count = repository.Session.Query<RavenList>()
using (var repository = _storage.Repository.OpenSession()) {
return repository.Query<RavenList>()
.Where(t => t.Key == key)
.Count();
return count;
}
}
@@ -499,18 +458,10 @@ namespace Hangfire.Raven
{
key.ThrowIfNull("key");
using (var repository = new Repository()) {
var results = repository.Session.Query<RavenList>()
.Where(t => t.Key == key)
.ToList();
using (var repository = _storage.Repository.OpenSession()) {
// TODO: Implement!
DateTime? ttl = results.Any() ? results.Min(t => t.ExpireAt) : (DateTime?)null;
if (!ttl.HasValue) {
return TimeSpan.FromSeconds(-1);
}
return ttl.Value - DateTime.UtcNow;
return TimeSpan.FromSeconds(-1);
}
}
@@ -518,15 +469,13 @@ namespace Hangfire.Raven
{
key.ThrowIfNull("key");
using (var repository = new Repository()) {
var results = repository.Session.Query<RavenList>()
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();
return results;
}
}
@@ -534,13 +483,11 @@ namespace Hangfire.Raven
{
key.ThrowIfNull("key");
using (var repository = new Repository()) {
var results = repository.Session.Query<RavenList>()
using (var repository = _storage.Repository.OpenSession()) {
return repository.Query<RavenList>()
.Where(t => t.Key == key)
.Select(t => t.Value)
.ToList();
return results;
}
}
}
+115
View File
@@ -0,0 +1,115 @@
using System;
using System.Linq;
using System.Reflection;
using Raven.Imports.Newtonsoft.Json;
using Raven.Imports.Newtonsoft.Json.Linq;
namespace Hangfire.Raven
{
/// <summary>
/// Base class for Raven Json Converters
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class RavenJsonConverterBase<T>
: JsonConverter
{
public RavenJsonConverterBase()
{
}
/// <summary>
/// Read Json and convert to T
/// </summary>
/// <param name="type"></param>
/// <param name="value">Object to convert</param>
/// <param name="serializer">JsonSerializer</param>
/// <returns>Object</returns>
protected abstract T Read(Type type, object value, JsonSerializer serializer);
/// <summary>
/// Write Json and convert T to text
/// </summary>
/// <param name="value">Input</param>
/// <returns>Input as text</returns>
protected abstract object Write(T value, JsonSerializer serializer);
/// <summary>
/// Overriding the CanConvert of JsonConvert
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public override bool CanConvert(Type type) => typeof(T).IsAssignableFrom(type);
/// <summary>
/// Overriding the ReadJson of JsonConvert
/// </summary>
/// <param name="reader"></param>
/// <param name="type"></param>
/// <param name="existingValue"></param>
/// <param name="serializer"></param>
/// <returns></returns>
public override object ReadJson(JsonReader reader, Type type, object existingValue, JsonSerializer serializer)
{
T toReturn;
switch (reader.TokenType) {
case JsonToken.StartArray:
toReturn = Read(type, JArray.Load(reader), serializer);
break;
case JsonToken.StartObject:
toReturn = Read(type, JObject.Load(reader), serializer);
break;
default:
toReturn = Read(type, reader.Value, serializer);
break;
}
return toReturn;
}
/// <summary>
/// Overriding the WriteJson of JsonConvert
/// </summary>
/// <param name="writer"></param>
/// <param name="value"></param>
/// <param name="serializer"></param>
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, Write((T)value, serializer));
}
}
public class RavenJsonMethodConverter
: RavenJsonConverterBase<MethodInfo>
{
protected override MethodInfo Read(Type type, object value, JsonSerializer serializer)
{
var splitted = value.ToString().Split(';');
if (splitted.Count() == 3) {
return Assembly.GetEntryAssembly().GetType(splitted[1]).GetMethod(splitted[2]);
}
return null;
}
protected override object Write(MethodInfo value, JsonSerializer serializer) => $"{value.DeclaringType.GetTypeInfo().Assembly.GetName().Name};{value.DeclaringType.FullName};{value.Name}";
}
public class RavenJsonPropertyConverter
: RavenJsonConverterBase<PropertyInfo>
{
protected override PropertyInfo Read(Type type, object value, JsonSerializer serializer)
{
var splitted = value.ToString().Split(';');
if (splitted.Count() == 3) {
return Assembly.GetEntryAssembly().GetType(splitted[1]).GetProperty(splitted[2]);
}
return null;
}
protected override object Write(PropertyInfo value, JsonSerializer serializer) => $"{value.DeclaringType.GetTypeInfo().Assembly.GetName().Name};{value.DeclaringType.FullName};{value.Name}";
}
}
+201 -262
View File
@@ -1,120 +1,90 @@
// This file is part of Hangfire.
// Copyright © 2013-2014 Sergey Odinokov.
//
// Hangfire is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation, either version 3
// of the License, or any later version.
//
// Hangfire is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.
using System;
using System.Collections.Generic;
using System.Linq;
using Hangfire.Annotations;
using Hangfire.Common;
using Hangfire.States;
using Hangfire.Storage;
using HangFire.Raven;
using Hangfire.Raven.Entities;
using Hangfire.Raven.Storage;
using Hangfire.States;
using Hangfire.Storage;
using Hangfire.Storage.Monitoring;
using Raven.Abstractions.Data;
using Raven.Client;
namespace Hangfire.Raven
{
internal class RavenWriteOnlyTransaction : JobStorageTransaction
internal class RavenWriteOnlyTransaction
: JobStorageTransaction
{
private readonly Queue<Action<RavenConnection>> _commandQueue
= new Queue<Action<RavenConnection>>();
private readonly SortedSet<string> _lockedResources = new SortedSet<string>();
private readonly RavenStorage _storage;
private IDocumentSession _session;
private List<KeyValuePair<string, PatchRequest>> _patchRequests;
public RavenWriteOnlyTransaction([NotNull] RavenStorage storage)
{
storage.ThrowIfNull("storage");
_storage = storage;
_patchRequests = new List<KeyValuePair<string, PatchRequest>>();
_session = _storage.Repository.OpenSession();
_session.Advanced.UseOptimisticConcurrency = true;
}
public override void Commit()
{
foreach (var command in _commandQueue) {
command(new RavenConnection(_storage));
try {
_session.SaveChanges();
_session.Dispose();
var toPatch = _patchRequests.ToLookup(a => a.Key, a => a.Value);
foreach (var item in toPatch) {
_session.Advanced.DocumentStore.DatabaseCommands.Patch(item.Key, item.ToArray());
}
} catch {
Console.WriteLine("- Concurrency exception");
throw;
}
}
public override void ExpireJob(string jobId, TimeSpan expireIn)
{
using (var repository = new Repository()) {
var result = repository.Session.Load<RavenJob>(jobId);
var id = Repository.GetId(typeof(RavenJob), jobId);
var result = _session.Load<RavenJob>(id);
result.ExpireAt = DateTime.UtcNow.Add(expireIn);
repository.Save(result);
}
_session.Advanced.AddExpire(result, DateTime.UtcNow + expireIn);
}
public override void PersistJob(string jobId)
{
using (var repository = new Repository()) {
var result = repository.Session.Load<RavenJob>(jobId);
var id = Repository.GetId(typeof(RavenJob), jobId);
var result = _session.Load<RavenJob>(id);
result.ExpireAt = null;
repository.Save(result);
}
_session.Advanced.RemoveExpire(result);
}
public override void SetJobState(string jobId, IState state)
{
using (var repository = new Repository()) {
var jobState = repository.Session.Query<State>().FirstOrDefault(t => t.JobId == jobId);
var id = Repository.GetId(typeof(RavenJob), jobId);
var result = _session.Load<RavenJob>(id);
if (jobState == null) {
jobState = new State
{
CreatedAt = DateTime.UtcNow,
JobId = jobId
};
}
result.History.Insert(0, new StateHistoryDto() {
StateName = state.Name,
Data = state.SerializeData(),
Reason = state.Reason,
CreatedAt = DateTime.UtcNow
});
jobState.Reason = state.Reason;
jobState.Name = state.Name;
jobState.Data = JobHelper.ToJson(state.SerializeData());
repository.Save(jobState);
var result = repository.Session.Load<RavenJob>(jobId);
result.StateId = jobState.Id;
result.StateName = state.Name;
result.StateData = JobHelper.ToJson(state.SerializeData());
result.StateReason = state.Reason;
repository.Save(result);
}
result.StateData = new StateData() {
Name = state.Name,
Data = state.SerializeData(),
Reason = state.Reason
};
}
public override void AddJobState(string jobId, IState state)
{
using (var repository = new Repository()) {
var jobState = new State
{
Id = Guid.NewGuid().ToString(),
JobId = jobId,
Name = state.Name,
Reason = state.Reason,
CreatedAt = DateTime.UtcNow,
Data = JobHelper.ToJson(state.SerializeData())
};
repository.Save(jobState);
}
SetJobState(jobId, state);
}
public override void AddToQueue(string queue, string jobId)
@@ -127,83 +97,59 @@ namespace Hangfire.Raven
public override void IncrementCounter(string key)
{
using (var repository = new Repository()) {
var counter = repository.Session.Query<Counter>().FirstOrDefault(t => t.Key == key);
if (counter == null) {
counter = new Counter
{
Id = Guid.NewGuid().ToString(),
Key = key,
Value = 0
};
}
counter.Value += 1;
repository.Save(counter);
}
IncrementCounter(key, TimeSpan.MinValue);
}
public override void IncrementCounter(string key, TimeSpan expireIn)
{
using (var repository = new Repository()) {
var counter = repository.Session.Query<Counter>().FirstOrDefault(t => t.Key == key);
var id = Repository.GetId(typeof(Counter), key);
if (counter == null) {
counter = new Counter
{
Id = Guid.NewGuid().ToString(),
ExpireAt = DateTime.UtcNow.Add(expireIn),
Key = key,
Value = 0
};
if (_session.Load<Counter>(id) == null) {
var counter = new Counter() {
Id = id,
Value = 1
};
_session.Store(counter);
if (expireIn != TimeSpan.MinValue) {
_session.Advanced.AddExpire(counter, DateTime.UtcNow + expireIn);
}
counter.Value += 1;
repository.Save(counter);
} else {
_patchRequests.Add(new KeyValuePair<string, PatchRequest>(id, new PatchRequest() {
Type = PatchCommandType.Inc,
Name = "Value",
Value = 1
}));
}
}
public override void DecrementCounter(string key)
{
using (var repository = new Repository()) {
var counter = repository.Session.Query<Counter>().FirstOrDefault(t => t.Key == key);
if (counter == null) {
counter = new Counter
{
Id = Guid.NewGuid().ToString(),
Key = key,
Value = 0
};
}
counter.Value -= 1;
repository.Save(counter);
}
DecrementCounter(key, TimeSpan.MinValue);
}
public override void DecrementCounter(string key, TimeSpan expireIn)
{
using (var repository = new Repository()) {
var counter = repository.Session.Query<Counter>().FirstOrDefault(t => t.Key == key);
var id = Repository.GetId(typeof(Counter), key);
if (counter == null) {
counter = new Counter
{
Id = Guid.NewGuid().ToString(),
ExpireAt = DateTime.UtcNow.Add(expireIn),
Key = key,
Value = 0
};
if (_session.Load<Counter>(id) == null) {
var counter = new Counter() {
Id = id,
Value = -1
};
_session.Store(counter);
if (expireIn != TimeSpan.MinValue) {
_session.Advanced.AddExpire(counter, DateTime.UtcNow + expireIn);
}
counter.Value -= 1;
repository.Save(counter);
} else {
_patchRequests.Add(new KeyValuePair<string, PatchRequest>(id, new PatchRequest() {
Type = PatchCommandType.Inc,
Name = "Value",
Value = -1
}));
}
}
@@ -214,126 +160,27 @@ namespace Hangfire.Raven
public override void AddToSet(string key, string value, double score)
{
using (var repository = new Repository()) {
var set = repository.Session.Query<RavenSet>().FirstOrDefault(t => t.Key == key && t.Value == value);
var id = Repository.GetId(typeof(RavenSet), key);
if (set == null) {
set = new RavenSet
{
Id = Guid.NewGuid().ToString(),
Key = key,
Value = value
};
}
var set = _session.Load<RavenSet>(id) ?? new RavenSet() {
Id = id
};
set.Score = score;
_session.Store(set);
repository.Save(set);
}
set.Scores[value] = score;
}
public override void RemoveFromSet(string key, string value)
{
using (var repository = new Repository()) {
var set = repository.Session.Query<RavenSet>().FirstOrDefault(t => t.Key == key && t.Value == value);
var id = Repository.GetId(typeof(RavenSet), key);
if (set == null) {
return;
}
var set = _session.Load<RavenSet>(id);
repository.Delete(set);
}
}
set.Scores.Remove(value);
public override void InsertToList(string key, string value)
{
using (var repository = new Repository()) {
var list = repository.Session.Query<RavenList>().FirstOrDefault(t => t.Key == key && t.Value == value);
if (list == null) {
list = new RavenList
{
Id = Guid.NewGuid().ToString(),
Key = key
};
}
list.Value = value;
repository.Save(list);
}
}
public override void RemoveFromList(string key, string value)
{
using (var repository = new Repository()) {
var list = repository.Session.Query<RavenList>().FirstOrDefault(t => t.Key == key && t.Value == value);
if (list == null) {
return;
}
repository.Delete(list);
}
}
public override void TrimList(string key, int keepStartingFrom, int keepEndingAt)
{
using (var repository = new Repository()) {
var list = repository.Session.Query<RavenList>().Where(t => t.Key == key).ToList();
if (!list.Any()) {
return;
}
for (var i = 0; i < list.Count; ++i) {
if (i >= keepStartingFrom && i <= keepEndingAt) {
continue;
}
repository.Delete(list[i]);
}
}
}
public override void SetRangeInHash(string key, IEnumerable<KeyValuePair<string, string>> keyValuePairs)
{
key.ThrowIfNull("key");
keyValuePairs.ThrowIfNull("keyValuePairs");
using (var repository = new Repository()) {
foreach (var keyValuePair in keyValuePairs) {
var pair = keyValuePair;
var results = repository.Session.Query<RavenHash>().Where(t => t.Key == key && t.Field == pair.Key).ToList();
if (!results.Any()) {
results.Add(new RavenHash
{
Key = key,
Field = pair.Key,
});
}
foreach (var item in results) {
item.Value = pair.Value;
repository.Save(item);
}
}
}
}
public override void RemoveHash(string key)
{
key.ThrowIfNull("key");
using (var repository = new Repository()) {
var result = repository.Session.Query<RavenHash>().Where(t => t.Key == key).ToList();
foreach (var item in result) {
repository.Session.Delete(item);
}
if (set.Scores.Count == 0) {
_session.Delete(set);
}
}
@@ -341,33 +188,125 @@ namespace Hangfire.Raven
{
key.ThrowIfNull("key");
using (var repository = new Repository()) {
var result = repository.Session.Query<RavenSet>().Where(t => t.Key == key).ToList();
var id = Repository.GetId(typeof(RavenSet), key);
foreach (var item in result) {
repository.Session.Delete(item);
}
_session.Delete(id);
}
public override void ExpireSet([NotNull] string key, TimeSpan expireIn)
{
key.ThrowIfNull("key");
var id = Repository.GetId(typeof(RavenSet), key);
var set = _session.Load<RavenSet>(id);
_session.Advanced.AddExpire(set, DateTime.UtcNow + expireIn);
}
public override void PersistSet([NotNull] string key)
{
key.ThrowIfNull("key");
var id = Repository.GetId(typeof(RavenSet), key);
var set = _session.Load<RavenSet>(id);
_session.Advanced.RemoveExpire(set);
}
public override void InsertToList(string key, string value)
{
var list = _session.Query<RavenList>().FirstOrDefault(t => t.Key == key && t.Value == value);
if (list == null) {
list = new RavenList {
Id = Guid.NewGuid().ToString(),
Key = key
};
}
list.Value = value;
_session.Store(list);
}
public override void RemoveFromList(string key, string value)
{
var list = _session.Query<RavenList>().FirstOrDefault(t => t.Key == key && t.Value == value);
if (list == null) {
return;
}
_session.Delete(list);
}
public override void TrimList(string key, int keepStartingFrom, int keepEndingAt)
{
var list = _session.Query<RavenList>().Where(t => t.Key == key).ToList();
if (!list.Any()) {
return;
}
for (var i = 0; i < list.Count; ++i) {
if (i >= keepStartingFrom && i <= keepEndingAt) {
continue;
}
_session.Delete(list[i]);
}
}
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 result = _session.Load<RavenHash>(id);
// Not exists? Insert
if (result == null) {
result = new RavenHash() {
Id = id
};
_session.Store(result);
}
foreach (var keyValuePair in keyValuePairs) {
result.Fields[keyValuePair.Key] = keyValuePair.Value;
}
}
public override void RemoveHash(string key)
{
key.ThrowIfNull("key");
_session.Delete(Repository.GetId(typeof(RavenHash), key));
}
public override void ExpireHash(string key, TimeSpan expireIn)
{
key.ThrowIfNull("key");
using (var repository = new Repository()) {
var result = repository.Session.Query<RavenSet>().Where(t => t.Key == key).ToList();
var id = Repository.GetId(typeof(RavenHash), key);
var set = _session.Load<RavenHash>(id);
foreach (var item in result) {
item.ExpireAt = DateTime.UtcNow.Add(expireIn);
repository.Save(item);
}
}
_session.Advanced.AddExpire(set, DateTime.UtcNow + expireIn);
}
internal void QueueCommand(Action<RavenConnection> action)
public override void PersistHash([NotNull] string key)
{
_commandQueue.Enqueue(action);
key.ThrowIfNull("key");
var id = Repository.GetId(typeof(RavenHash), key);
var set = _session.Load<RavenHash>(id);
_session.Advanced.RemoveExpire(set);
}
}
}
+107 -349
View File
@@ -1,388 +1,146 @@
using Raven.Client;
using System;
using Dewey.Net.Types;
using Raven.Client.Document;
using Raven.Client.Embedded;
using System.Threading.Tasks;
using System;
using System.Collections.Generic;
using Dewey.Net.Generic;
using Raven.Abstractions.Data;
using System.Collections.Concurrent;
using Raven.Client.Linq;
using System.Linq;
using System.Linq.Expressions;
using Hangfire.Raven.Entities.Identity;
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 Raven.Imports.Newtonsoft.Json;
namespace HangFire.Raven
namespace Hangfire.Raven
{
public class Repository : IDisposable
public class RepositoryConfig
{
public static string ConnectionString { get; set; }
public static string ConnectionUrl { get; set; }
public static string DefaultDatabase { get; set; }
public static string APIKey { get; set; }
public string ConnectionString { get; set; }
public string ConnectionUrl { get; set; }
public string Database { get; set; }
public string APIKey { get; set; }
}
public class RepositoryObserver<T>
: IObserver<T>
{
private Action<T> action;
public RepositoryObserver(Action<T> input)
{
this.action = input;
}
public void OnCompleted()
{
}
public void OnError(Exception error)
{
}
public void OnNext(T value)
{
this.action.Invoke(value);
}
}
public class Repository
{
private RepositoryConfig _config;
private static IDocumentStore _documentStore;
private IDocumentSession _session;
private IAsyncDocumentSession _asyncSession;
private string _database;
private string _apiKey;
public static bool Embedded { get; set; }
public Repository(RepositoryConfig config)
{
_config = config;
public string Database => _database ?? DefaultDatabase;
if (!string.IsNullOrEmpty(_config.ConnectionString)) {
_documentStore = new DocumentStore {
ConnectionStringName = _config.ConnectionString,
ApiKey = _config.APIKey,
DefaultDatabase = _config.Database,
Conventions = new DocumentConvention() {
CustomizeJsonSerializer = delegate (JsonSerializer input) {
input.Converters.Add(new RavenJsonPropertyConverter());
input.Converters.Add(new RavenJsonMethodConverter());
}
}
};
} else {
_documentStore = new DocumentStore {
Url = _config.ConnectionUrl,
ApiKey = _config.APIKey,
DefaultDatabase = _config.Database,
Conventions = new DocumentConvention() {
CustomizeJsonSerializer = delegate (JsonSerializer input) {
input.Converters.Add(new RavenJsonPropertyConverter());
input.Converters.Add(new RavenJsonMethodConverter());
}
}
};
}
public string ApiKeyAccess => _apiKey ?? string.Empty;
_documentStore.Listeners.RegisterListener(new TakeNewestConflictResolutionListener());
_documentStore.Initialize();
new Hangfire_RavenJobs().Execute(_documentStore);
}
public FacetResults GetFacets(string index, IndexQuery query, List<Facet> facets)
{
return _documentStore.DatabaseCommands.GetFacets(index, query, facets);
}
public void ExecuteIndexes(List<AbstractIndexCreationTask> indexes)
{
_documentStore.ExecuteIndexes(indexes);
}
public static string GetId(Type type, params string[] id)
{
return _documentStore.Conventions.FindFullDocumentKeyFromNonStringIdentifier(string.Join("/", id), type, false);
}
public void Destroy()
{
if (!DocumentStore.DatabaseExists(Database)) {
if (!_documentStore.DatabaseExists(_config.Database)) {
return;
}
DocumentStore.DatabaseCommands.GlobalAdmin.DeleteDatabase(Database, hardDelete: true);
_documentStore.DatabaseCommands.GlobalAdmin.DeleteDatabase(_config.Database, hardDelete: true);
}
public void Create()
{
if (DocumentStore.DatabaseExists(Database)) {
if (_documentStore.DatabaseExists(_config.Database)) {
return;
}
DocumentStore.DatabaseCommands.GlobalAdmin.EnsureDatabaseExists(Database);
_documentStore.DatabaseCommands.GlobalAdmin.EnsureDatabaseExists(_config.Database);
}
public IDocumentStore DocumentStore
public IDisposable DocumentChange(Type documentType, Action<DocumentChangeNotification> action)
{
get
{
if (_documentStore == null) {
if (Embedded) {
_documentStore = new EmbeddableDocumentStore
{
RunInMemory = true
};
((EmbeddableDocumentStore)_documentStore).Configuration.Storage.Voron.AllowOn32Bits = true;
} else {
if (!string.IsNullOrEmpty(ConnectionString))
{
_documentStore = new DocumentStore
{
ConnectionStringName = ConnectionString,
ApiKey = APIKey
};
}
else
{
_documentStore = new DocumentStore
{
Url = ConnectionUrl,
ApiKey = APIKey
};
}
}
_documentStore.Conventions.DefaultQueryingConsistency = ConsistencyOptions.AlwaysWaitForNonStaleResultsAsOfLastWrite;
_documentStore.Listeners.RegisterListener(new TakeNewestConflictResolutionListener());
_documentStore.Conventions.DefaultQueryingConsistency = ConsistencyOptions.AlwaysWaitForNonStaleResultsAsOfLastWrite;
_documentStore.Initialize();
}
return _documentStore;
}
return _documentStore.Changes(_config.Database).ForDocumentsStartingWith(GetId(documentType, ""))
.Subscribe(new RepositoryObserver<DocumentChangeNotification>(action));
}
public IDocumentSession Session => _session ?? (_session = DocumentStore.OpenSession(Database));
public IAsyncDocumentSession AsyncSession => _asyncSession ?? (_asyncSession = DocumentStore.OpenAsyncSession(Database));
#region constructor
public Repository()
public IDisposable DocumentChange(Type documentType, string suffix, Action<DocumentChangeNotification> action)
{
_database = DefaultDatabase;
_apiKey = APIKey;
return _documentStore.Changes(_config.Database).ForDocumentsStartingWith(
GetId(documentType, string.Format("{0}/", suffix))
)
.Subscribe(new RepositoryObserver<DocumentChangeNotification>(action));
}
public Repository(string database, string APIKey)
public IDocumentSession OpenSession()
{
if (database.IsEmpty()) {
_database = DefaultDatabase;
} else {
_database = database;
}
if (APIKey.IsEmpty())
{
_apiKey = ApiKeyAccess;
}
else
{
_apiKey = APIKey;
}
return _documentStore.OpenSession(_config.Database);
}
#endregion
#region Bulk Insert
private BulkInsertOptions GetBulkInsertOptions(bool overwrite = true) => new BulkInsertOptions
public IAsyncDocumentSession OpenAsyncSession()
{
BatchSize = 100,
WriteTimeoutMilliseconds = 360000,
OverwriteExisting = overwrite
};
public void BulkInsert<T>(ConcurrentBag<T> entities, bool overwrite = true) where T : BaseEntity
{
if (_database.IsEmpty()) {
_database = DefaultDatabase;
}
using (var bulkInsert = DocumentStore.BulkInsert(Database, GetBulkInsertOptions(overwrite))) {
foreach (var item in entities) {
if (item == null) {
continue;
}
if (item.Id.IsEmpty()) {
item.Id = Guid.NewGuid().ToString();
}
bulkInsert.Store(item);
}
}
return _documentStore.OpenAsyncSession(_config.Database);
}
public void BulkInsert<T>(List<T> entities, bool overwrite = true) where T : BaseEntity
{
var concurrentBag = new ConcurrentBag<T>();
entities.ForEach(t => concurrentBag.Add(t));
BulkInsert(concurrentBag, overwrite);
}
#endregion
#region Find
public T FindById<T>(string id) where T : BaseEntity
{
if (id == null) {
throw new ArgumentException("Id must be provided.", nameof(id));
}
return Session.Load<T>(id);
}
public async Task<T> FindByIdAsync<T>(string id) where T : BaseEntity
{
if (id == null) {
throw new ArgumentException("Id must be provided.", nameof(id));
}
return await AsyncSession.LoadAsync<T>(id);
}
#endregion
#region Save
public void Save<T>(T entity) where T : BaseEntity
{
if (entity == null) {
throw new ArgumentException("Entity must be provided.", nameof(entity));
}
if (entity.Id.IsEmpty()) {
entity.Id = Guid.NewGuid().ToString();
}
Session.Store(entity);
Session.SaveChanges();
}
public async Task SaveAsync<T>(T entity) where T : BaseEntity
{
if (entity == null) {
throw new ArgumentException("Entity must be provided.", nameof(entity));
}
if (entity.Id.IsEmpty()) {
entity.Id = Guid.NewGuid().ToString();
}
await AsyncSession.StoreAsync(entity);
await AsyncSession.SaveChangesAsync();
}
public async Task SaveNoValidateAsync<T>(T entity) where T : BaseEntity
{
if (entity == null) {
throw new ArgumentException("Entity must be provided.", nameof(entity));
}
if (entity.Id.IsEmpty()) {
entity.Id = Guid.NewGuid().ToString();
}
await AsyncSession.StoreAsync(entity);
await AsyncSession.SaveChangesAsync();
}
public void SaveAll<T>(List<T> entities) where T : BaseEntity
{
if (entities.IsEmpty()) {
throw new ArgumentException("Data must be provided.", nameof(entities));
}
foreach (var item in entities) {
Save(item);
}
}
public async Task SaveAllAsync<T>(List<T> entities) where T : BaseEntity
{
if (entities.IsEmpty()) {
throw new ArgumentException("Data must be provided.", nameof(entities));
}
foreach (var item in entities) {
await SaveAsync(item);
}
}
#endregion
#region Delete
public void Delete(string id)
{
if (id == null) {
throw new ArgumentException("Id must be provided.", nameof(id));
}
Session.Delete(id);
Session.SaveChanges();
}
public async Task DeleteAsync(string id)
{
if (id == null) {
throw new ArgumentException("Id must be provided.", nameof(id));
}
AsyncSession.Delete(id);
await AsyncSession.SaveChangesAsync();
}
public void Delete<T>(T entity) where T : BaseEntity
{
if (entity == null) {
throw new ArgumentException("Entity must be provided.", nameof(entity));
}
Session.Delete(entity.Id);
Session.SaveChanges();
}
public async Task DeleteAsync<T>(T entity) where T : BaseEntity
{
if (entity == null) {
throw new ArgumentException("Entity must be provided.", nameof(entity));
}
AsyncSession.Delete(entity.Id);
await AsyncSession.SaveChangesAsync();
}
#endregion
#region Query
public IRavenQueryable<T> Query<T>() where T : BaseEntity
{
return Session.Query<T>();
}
public IRavenQueryable<T> QueryAsync<T>() where T : BaseEntity
{
return AsyncSession.Query<T>();
}
#endregion
#region Index
public IRavenQueryable<T> Index<T>(string indexName, bool isMapReduce = false) where T : BaseEntity
{
return Session.Query<T>(indexName, isMapReduce);
}
public IRavenQueryable<T> IndexAsync<T>(string indexName, bool isMapReduce = false) where T : BaseEntity
{
return AsyncSession.Query<T>(indexName, isMapReduce);
}
#endregion
#region Count
public int Count<T>() where T : BaseEntity
{
return Session.Query<T>().Count();
}
public async Task<int> CountAsync<T>() where T : BaseEntity
{
return await AsyncSession.Query<T>().CountAsync();
}
public int Count<T>(Expression<Func<T, bool>> predicate) where T : BaseEntity
{
return Session.Query<T>().Where(predicate).Count();
}
public async Task<int> CountAsync<T>(Expression<Func<T, bool>> predicate) where T : BaseEntity
{
return await AsyncSession.Query<T>().Where(predicate).CountAsync();
}
#endregion
#region IDisposable
private bool _disposed = false;
protected void Dispose(bool disposing)
{
if (!_disposed) {
if (disposing) {
_session = null;
_asyncSession = null;
}
_disposed = true;
}
}
public void Dispose()
{
Dispose(true);
}
#endregion
}
}
@@ -7,17 +7,16 @@ namespace Hangfire.Raven.StateHandlers
{
public void Apply(ApplyStateContext context, IWriteOnlyTransaction transaction)
{
transaction.InsertToList("deleted", context.JobId);
transaction.InsertToList("deleted", context.BackgroundJob.Id);
transaction.TrimList("deleted", 0, 99);
}
public void Unapply(ApplyStateContext context, IWriteOnlyTransaction transaction)
{
transaction.RemoveFromList("deleted", context.JobId);
transaction.RemoveFromList("deleted", context.BackgroundJob.Id);
}
public string StateName
{
public string StateName {
get { return DeletedState.StateName; }
}
}
@@ -1,7 +1,7 @@
using Hangfire.Common;
using System;
using Hangfire.Common;
using Hangfire.States;
using Hangfire.Storage;
using System;
namespace Hangfire.Raven.StateHandlers
{
@@ -9,16 +9,15 @@ namespace Hangfire.Raven.StateHandlers
{
public void Apply(ApplyStateContext context, IWriteOnlyTransaction transaction)
{
transaction.AddToSet("failed", context.JobId, JobHelper.ToTimestamp(DateTime.UtcNow));
transaction.AddToSet("failed", context.BackgroundJob.Id, JobHelper.ToTimestamp(DateTime.UtcNow));
}
public void Unapply(ApplyStateContext context, IWriteOnlyTransaction transaction)
{
transaction.RemoveFromSet("failed", context.JobId);
transaction.RemoveFromSet("failed", context.BackgroundJob.Id);
}
public string StateName
{
public string StateName {
get { return FailedState.StateName; }
}
}
@@ -1,7 +1,7 @@
using Hangfire.Common;
using System;
using Hangfire.Common;
using Hangfire.States;
using Hangfire.Storage;
using System;
namespace Hangfire.Raven.StateHandlers
{
@@ -9,16 +9,15 @@ namespace Hangfire.Raven.StateHandlers
{
public void Apply(ApplyStateContext context, IWriteOnlyTransaction transaction)
{
transaction.AddToSet("processing", context.JobId, JobHelper.ToTimestamp(DateTime.UtcNow));
transaction.AddToSet("processing", context.BackgroundJob.Id, JobHelper.ToTimestamp(DateTime.UtcNow));
}
public void Unapply(ApplyStateContext context, IWriteOnlyTransaction transaction)
{
transaction.RemoveFromSet("processing", context.JobId);
transaction.RemoveFromSet("processing", context.BackgroundJob.Id);
}
public string StateName
{
public string StateName {
get { return ProcessingState.StateName; }
}
}
@@ -7,17 +7,16 @@ namespace Hangfire.Raven.StateHandlers
{
public void Apply(ApplyStateContext context, IWriteOnlyTransaction transaction)
{
transaction.InsertToList("succeeded", context.JobId);
transaction.InsertToList("succeeded", context.BackgroundJob.Id);
transaction.TrimList("succeeded", 0, 99);
}
public void Unapply(ApplyStateContext context, IWriteOnlyTransaction transaction)
{
transaction.RemoveFromList("succeeded", context.JobId);
transaction.RemoveFromList("succeeded", context.BackgroundJob.Id);
}
public string StateName
{
public string StateName {
get { return SucceededState.StateName; }
}
}
+19 -40
View File
@@ -1,65 +1,50 @@
// This file is part of Hangfire.
// Copyright © 2013-2014 Sergey Odinokov.
//
// Hangfire is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation, either version 3
// of the License, or any later version.
//
// Hangfire is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.
using System;
using System.Collections.Generic;
using Hangfire.Logging;
using Hangfire.Server;
using Hangfire.Storage;
using Raven.Client.Document;
using Hangfire.Raven.Indexes;
using Hangfire.Raven.JobQueues;
using Hangfire.Storage;
using Raven.Client.Indexes;
namespace Hangfire.Raven.Storage
{
public class RavenStorage : JobStorage
{
private readonly RavenStorageOptions _options;
private readonly Repository _repository;
public RavenStorage()
: this(new RavenStorageOptions())
public RavenStorage(RepositoryConfig config)
: this(config, new RavenStorageOptions())
{
}
/// <summary>
/// Initializes RavenStorage from the provided SqlServerStorageOptions and either the provided connection
/// string or the connection string with provided name pulled from the application config file.
///
/// </summary>
/// <param name="connectionString"></param>
/// <param name="config"></param>
/// <param name="options"></param>
/// <exception cref="ArgumentNullException"><paramref name="connectionString"/> argument is null.</exception>
/// <exception cref="ArgumentNullException"><paramref name="options"/> argument is null.</exception>
/// <exception cref="ArgumentException"><paramref name="connectionString"/> argument is neither
/// a valid Raven connection string nor the name of a connection string in the application
/// config file.</exception>
public RavenStorage(RavenStorageOptions options)
public RavenStorage(RepositoryConfig config, RavenStorageOptions options)
{
options.ThrowIfNull("options");
_options = options;
_repository = new Repository(config);
_repository.ExecuteIndexes(new List<AbstractIndexCreationTask>()
{
new Hangfire_RavenJobs()
});
InitializeQueueProviders();
}
public RavenStorageOptions Options { get { return _options; } }
public Repository Repository { get { return _repository; } }
public virtual PersistentJobQueueProviderCollection QueueProviders { get; private set; }
public override IMonitoringApi GetMonitoringApi()
{
//return new SqlServerMonitoringApi(this, _options.DashboardJobListLimit);
return null;
return new RavenStorageMonitoringApi(this);
}
public override IStorageConnection GetConnection()
@@ -67,15 +52,9 @@ namespace Hangfire.Raven.Storage
return new RavenConnection(this);
}
public override IEnumerable<IServerComponent> GetComponents()
{
yield return new ExpirationManager(_options.JobExpirationCheckInterval);
}
public override void WriteOptionsToLog(ILog logger)
{
logger.Info("Using the following options for Raven job storage:");
logger.InfoFormat("Queue poll interval: {0}.", _options.QueuePollInterval);
}
private void InitializeQueueProviders()
@@ -1,37 +1,45 @@
// This file is part of Hangfire.
// Copyright © 2015 Sergey Odinokov.
//
// Hangfire is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation, either version 3
// of the License, or any later version.
//
// Hangfire is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.
using System;
using Raven.Client;
using Raven.Json.Linq;
using System;
using Hangfire;
using Hangfire.Raven.Storage;
using Hangfire.Raven;
// ReSharper disable once CheckNamespace
namespace HangFire.Raven.Storage
namespace Hangfire.Raven.Storage
{
public static class SqlServerStorageExtensions
public static class RavenServerStorageExtensions
{
public static void AddExpire<T>(this ISyncAdvancedSessionOperation advanced, T obj, DateTime dateTime)
{
advanced.GetMetadataFor(obj)["Raven-Expiration-Date"] = new RavenJValue(dateTime);
}
public static void RemoveExpire<T>(this ISyncAdvancedSessionOperation advanced, T obj)
{
advanced.GetMetadataFor(obj).Remove("Raven-Expiration-Date");
}
public static DateTime? GetExpire<T>(this ISyncAdvancedSessionOperation advanced, T obj)
{
RavenJToken token;
if (advanced.GetMetadataFor(obj).TryGetValue("Raven-Expiration-Date", out token)) {
var date = token.Value<DateTime>();
return date;
}
return null;
}
public static IGlobalConfiguration<RavenStorage> UseRavenStorage(this IGlobalConfiguration configuration, RavenStorage storage)
{
storage.ThrowIfNull("storage");
return configuration.UseStorage(storage);
}
public static IGlobalConfiguration<RavenStorage> UseRavenStorage(this IGlobalConfiguration configuration, string connectionString)
{
configuration.ThrowIfNull("configuration");
connectionString.ThrowIfNull("connectionString");
Repository.ConnectionString = connectionString;
var storage = new RavenStorage();
var config = new RepositoryConfig() {
ConnectionString = connectionString
};
var storage = new RavenStorage(config);
return configuration.UseStorage(storage);
}
@@ -46,10 +54,11 @@ namespace HangFire.Raven.Storage
throw new ArgumentException("Connection Url must begin with http or https!");
}
Repository.ConnectionUrl = connectionUrl;
Repository.DefaultDatabase = database;
var storage = new RavenStorage();
var config = new RepositoryConfig() {
ConnectionUrl = connectionUrl,
Database = database
};
var storage = new RavenStorage(config);
return configuration.UseStorage(storage);
}
@@ -64,11 +73,13 @@ namespace HangFire.Raven.Storage
throw new ArgumentException("Connection Url must begin with http or https!");
}
Repository.ConnectionUrl = connectionUrl;
Repository.DefaultDatabase = database;
Repository.APIKey = APIKey;
var config = new RepositoryConfig() {
ConnectionUrl = connectionUrl,
Database = database,
APIKey = APIKey
};
var storage = new RavenStorage();
var storage = new RavenStorage(config);
return configuration.UseStorage(storage);
}
@@ -84,33 +95,12 @@ namespace HangFire.Raven.Storage
throw new ArgumentException("Connection Url must begin with http or https!");
}
Repository.ConnectionUrl = connectionUrl;
Repository.DefaultDatabase = database;
var config = new RepositoryConfig() {
ConnectionUrl = connectionUrl,
Database = database
};
var storage = new RavenStorage(options);
return configuration.UseStorage(storage);
}
public static IGlobalConfiguration<RavenStorage> UseEmbeddedRavenStorage(this IGlobalConfiguration configuration)
{
configuration.ThrowIfNull("configuration");
Repository.Embedded = true;
var storage = new RavenStorage();
return configuration.UseStorage(storage);
}
public static IGlobalConfiguration<RavenStorage> UseEmbeddedRavenStorage(this IGlobalConfiguration configuration, RavenStorageOptions options)
{
configuration.ThrowIfNull("configuration");
options.ThrowIfNull("options");
Repository.Embedded = true;
var storage = new RavenStorage(options);
var storage = new RavenStorage(config, options);
return configuration.UseStorage(storage);
}
@@ -0,0 +1,408 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Hangfire.Annotations;
using Hangfire.Common;
using Hangfire.Raven.Entities;
using Hangfire.Raven.Indexes;
using Hangfire.States;
using Hangfire.Storage;
using Hangfire.Storage.Monitoring;
using Raven.Abstractions.Data;
using Raven.Client;
using Raven.Client.Linq;
namespace Hangfire.Raven.Storage
{
public class RavenStorageMonitoringApi
: IMonitoringApi
{
private RavenStorage _storage;
public RavenStorageMonitoringApi([NotNull]RavenStorage storage)
{
storage.ThrowIfNull("storage");
_storage = 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;
}
}
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();
return stats.TotalResults;
}
}
public long DeletedListCount()
{
return GetNumberOfJobsByStateName(DeletedState.StateName);
}
public long FailedCount()
{
return GetNumberOfJobsByStateName(FailedState.StateName);
}
public long ProcessingCount()
{
return GetNumberOfJobsByStateName(ProcessingState.StateName);
}
public long ScheduledCount()
{
return GetNumberOfJobsByStateName(ScheduledState.StateName);
}
public long SucceededListCount()
{
return GetNumberOfJobsByStateName(SucceededState.StateName);
}
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();
return stats.TotalResults;
}
}
public IDictionary<DateTime, long> FailedByDatesCount()
{
return GetTimelineStats("failed");
}
public IDictionary<DateTime, long> HourlyFailedJobs()
{
return GetHourlyTimelineStats("failed");
}
public IDictionary<DateTime, long> HourlySucceededJobs()
{
return GetHourlyTimelineStats("succeeded");
}
public IDictionary<DateTime, long> SucceededByDatesCount()
{
return GetTimelineStats("succeeded");
}
private Dictionary<DateTime, long> GetHourlyTimelineStats(string type)
{
var endDate = DateTime.UtcNow;
var dates = new List<DateTime>();
for (var i = 0; i < 24; i++) {
dates.Add(endDate);
endDate = endDate.AddHours(-1);
}
return GetTimelineStats(dates, x => string.Format("stats:{0}:{1}", type, x.ToString("yyyy-MM-dd-HH")));
}
private Dictionary<DateTime, long> GetTimelineStats(string type)
{
var endDate = DateTime.UtcNow.Date;
var dates = new List<DateTime>();
for (var i = 0; i < 7; i++) {
dates.Add(endDate);
endDate = endDate.AddDays(-1);
}
return GetTimelineStats(dates, x => string.Format("stats:{0}:{1}", type, x.ToString("yyyy-MM-dd")));
}
private Dictionary<DateTime, long> GetTimelineStats(List<DateTime> dates,
Func<DateTime, string> formatorAction)
{
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 counters = repository.Load<Counter>(id);
if (counters != null)
stats.Add(item, counters.Value);
else
stats.Add(item, 0);
}
}
return stats;
}
public StatisticsDto GetStatistics()
{
using (var transaction = _storage.Repository.OpenSession()) {
var stat = new RavenQueryStatistics();
transaction.Query<Raven_DocumentsByEntityName.Mapping, Raven_DocumentsByEntityName>()
.Where(a => a.Tag == "RavenServers")
.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 getFacetValues = facetResults.Results["StateName"].Values;
return new StatisticsDto() {
Servers = stat.TotalResults,
Queues = 2,
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,
Enqueued = getFacetValues.FirstOrDefault(a => a.Range == EnqueuedState.StateName)?.Hits ?? 0,
Failed = getFacetValues.FirstOrDefault(a => a.Range == FailedState.StateName)?.Hits ?? 0,
Processing = getFacetValues.FirstOrDefault(a => a.Range == ProcessingState.StateName)?.Hits ?? 0,
Deleted = getFacetValues.FirstOrDefault(a => a.Range == DeletedState.StateName)?.Hits ?? 0,
};
}
}
public JobList<DeletedJobDto> DeletedJobs(int from, int count)
{
return GetJobs(
from,
count,
DeletedState.StateName,
(jsonJob, job, stateData) => new DeletedJobDto {
Job = job,
DeletedAt = JobHelper.DeserializeNullableDateTime(stateData["DeletedAt"])
});
}
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);
return EnqueuedJobs(results);
}
}
public JobList<FailedJobDto> FailedJobs(int from, int count)
{
return GetJobs(from, count,
FailedState.StateName,
(jsonJob, job, stateData) => new FailedJobDto {
Job = job,
Reason = jsonJob.StateData.Reason,
ExceptionDetails = stateData["ExceptionDetails"],
ExceptionMessage = stateData["ExceptionMessage"],
ExceptionType = stateData["ExceptionType"],
FailedAt = JobHelper.DeserializeNullableDateTime(stateData["FailedAt"])
});
}
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);
return FetchedJobs(results);
}
}
public JobList<ScheduledJobDto> ScheduledJobs(int from, int count)
{
return GetJobs(from, count,
ScheduledState.StateName,
(jsonJob, job, stateData) => new ScheduledJobDto {
Job = job,
EnqueueAt = JobHelper.DeserializeDateTime(stateData["EnqueueAt"]),
ScheduledAt = JobHelper.DeserializeDateTime(stateData["ScheduledAt"])
});
}
public JobList<ProcessingJobDto> ProcessingJobs(int from, int count)
{
return GetJobs(from, count,
ProcessingState.StateName,
(jsonJob, job, stateData) => new ProcessingJobDto {
Job = job,
ServerId = stateData.ContainsKey("ServerId") ? stateData["ServerId"] : stateData["ServerName"],
StartedAt = JobHelper.DeserializeDateTime(stateData["StartedAt"])
});
}
public JobList<SucceededJobDto> SucceededJobs(int from, int count)
{
var toReturn = GetJobs(from, count,
SucceededState.StateName,
(jsonJob, job, stateData) => new SucceededJobDto {
Job = job,
InSucceededState = true,
Result = stateData.ContainsKey("Result") ? stateData["Result"] : null,
TotalDuration = stateData.ContainsKey("PerformanceDuration") && stateData.ContainsKey("Latency")
? (long?)long.Parse(stateData["PerformanceDuration"]) +
(long?)long.Parse(stateData["Latency"])
: null,
SucceededAt = JobHelper.DeserializeNullableDateTime(stateData["SucceededAt"])
});
return toReturn;
}
public JobDetailsDto JobDetails(string jobId)
{
jobId.ThrowIfNull("jobId");
using (var repository = _storage.Repository.OpenSession()) {
var id = Repository.GetId(typeof(RavenJob), jobId);
var job = repository.Load<RavenJob>(id);
return new JobDetailsDto {
CreatedAt = job.CreatedAt,
ExpireAt = repository.Advanced.GetExpire(job),
Job = job.Job.GetJob(),
History = job.History,
Properties = job.Parameters
};
}
}
public IList<QueueWithTopEnqueuedJobsDto> Queues()
{
using (var repository = _storage.Repository.OpenSession()) {
var query = repository.Query<JobQueue>().ToList();
var results = from item in query
group item by item.Queue into g
let total = g.Count()
let fetched = g.Count(a => a.FetchedAt.HasValue)
select new QueueWithTopEnqueuedJobsDto() {
Name = g.Key,
Length = total - fetched,
Fetched = fetched,
FirstJobs = EnqueuedJobs(g.Take(5).Select(a => a.JobId))
};
return results.ToList();
}
}
public IList<ServerDto> Servers()
{
using (var repository = _storage.Repository.OpenSession()) {
var servers = repository.Query<RavenServer>().ToList();
var query =
from server in servers
select new ServerDto {
Name = server.Id,
Heartbeat = server.LastHeartbeat,
Queues = server.Data.Queues.ToList(),
StartedAt = server.Data.StartedAt ?? DateTime.MinValue,
WorkersCount = server.Data.WorkerCount
};
return query.ToList();
}
}
private JobList<TDto> GetJobs<TDto>(
int from,
int count,
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)
.ToList();
return DeserializeJobs(jobs, selector);
}
}
private JobList<FetchedJobDto> FetchedJobs(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);
return DeserializeJobs(jobs, (jsonJob, job, stateData) => new FetchedJobDto {
Job = job,
State = jsonJob.StateData.Name,
FetchedAt = jsonJob.StateData.Name == ProcessingState.StateName
? JobHelper.DeserializeNullableDateTime(stateData["StartedAt"])
: null
});
}
}
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);
return DeserializeJobs(jobs, (jsonJob, job, stateData) => new EnqueuedJobDto {
Job = job,
State = jsonJob.StateData.Name,
EnqueuedAt = jsonJob.StateData.Name == EnqueuedState.StateName
? JobHelper.DeserializeNullableDateTime(stateData["EnqueuedAt"])
: null
});
}
}
private Job DeserializeJob(InvocationData invocationData)
{
try {
return invocationData.Deserialize();
} catch (JobLoadException) {
return null;
}
}
private JobList<TDto> DeserializeJobs<TDto>(
IEnumerable<RavenJob> jobs,
Func<RavenJob, Job, Dictionary<string, string>, TDto> selector)
{
var result = from job in jobs
let stateData = job.StateData.Data != null
? new Dictionary<string, string>(job.StateData.Data, StringComparer.OrdinalIgnoreCase)
: null
let dto = selector(job, job.Job.GetJob(), stateData)
select new KeyValuePair<string, TDto>(job.Id.Split(new char[] { '/' }, 2)[1], dto);
return new JobList<TDto>(result);
}
}
}
@@ -1,20 +1,5 @@
// This file is part of Hangfire.
// Copyright © 2013-2014 Sergey Odinokov.
//
// Hangfire is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation, either version 3
// of the License, or any later version.
//
// Hangfire is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with Hangfire. If not, see <http://www.gnu.org/licenses/>.
using System;
using System;
using System.Collections.Generic;
namespace Hangfire.Raven.Storage
{
@@ -22,59 +7,27 @@ namespace Hangfire.Raven.Storage
{
private readonly string _clientId = null;
private TimeSpan _queuePollInterval;
public RavenStorageOptions()
{
QueuePollInterval = TimeSpan.FromSeconds(15);
InvisibilityTimeout = TimeSpan.FromMinutes(30);
JobExpirationCheckInterval = TimeSpan.FromHours(1);
CountersAggregateInterval = TimeSpan.FromMinutes(5);
PrepareSchemaIfNecessary = true;
DashboardJobListLimit = 50000;
TransactionTimeout = TimeSpan.FromMinutes(1);
DistributedLockLifetime = TimeSpan.FromSeconds(30);
_clientId = Guid.NewGuid().ToString().Replace("-", String.Empty);
_clientId = Guid.NewGuid().ToString().Replace("-", string.Empty);
}
public TimeSpan QueuePollInterval
{
get { return _queuePollInterval; }
set
{
var message = string.Format(
"The QueuePollInterval property value should be positive. Given: {0}.",
value);
if (value == TimeSpan.Zero)
{
throw new ArgumentException(message, "value");
}
if (value != value.Duration())
{
throw new ArgumentException(message, "value");
}
_queuePollInterval = value;
}
}
[Obsolete("Does not make sense anymore. Background jobs re-queued instantly even after ungraceful shutdown now. Will be removed in 2.0.0.")]
public TimeSpan InvisibilityTimeout { get; set; }
public bool PrepareSchemaIfNecessary { get; set; }
public TimeSpan JobExpirationCheckInterval { get; set; }
public TimeSpan CountersAggregateInterval { get; set; }
public int? DashboardJobListLimit { get; set; }
public TimeSpan TransactionTimeout { get; set; }
public TimeSpan DistributedLockLifetime { get; set; }
public string ClientId
{
public IEnumerable<string> QueueNames { get; set; }
public string ClientId {
get { return _clientId; }
}
}
}
-15
View File
@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
-14
View File
@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Dewey.Net" version="3.3.0.0" targetFramework="net45" />
<package id="Hangfire" version="1.5.3" targetFramework="net45" />
<package id="Hangfire.Core" version="1.5.3" targetFramework="net45" />
<package id="Hangfire.SqlServer" version="1.5.3" targetFramework="net45" />
<package id="Microsoft.Owin" version="3.0.1" targetFramework="net45" />
<package id="Microsoft.Owin.Host.SystemWeb" version="3.0.1" targetFramework="net45" />
<package id="Newtonsoft.Json" version="8.0.2" targetFramework="net45" />
<package id="Owin" version="1.0" targetFramework="net45" />
<package id="RavenDB.Client" version="3.0.30037" targetFramework="net45" />
<package id="RavenDB.Database" version="3.0.30037" targetFramework="net45" />
<package id="RavenDB.Embedded" version="3.0.30037" targetFramework="net45" />
</packages>
+131
View File
@@ -0,0 +1,131 @@
{
"title": "Hangfire.Raven",
"version": "2.0.0",
"description": "RavenDB job storage for Hangfire",
"copyright": "Copyright @ 2016 Cady, LLC",
"authors": [
"Cady <contact@cady.io>"
],
"buildOptions": {
"allowUnsafe": true,
"debugType": "portable",
"preserveCompilationContext": true
},
"packOptions": {
"tags": [
"Hangfire",
"AspNet",
"MVC",
"OWIN",
"RavenDB",
"Long-Running",
"Background",
"Fire-And-Forget",
"Delayed",
"Recurring",
"Tasks",
"Jobs",
"Scheduler",
"Threading",
"Queues"
],
"iconUrl": "https://avatars2.githubusercontent.com/u/7880472?v=3&s=200",
"projectUrl": "https://github.com/cady-io/hangfire-ravendb",
"licenseUrl": "http://www.gnu.org/licenses/lgpl-3.0.html",
"owners": [
"Cady <contact@cady.io>"
],
"repository": {
"type": "git",
"url": "https://github.com/cady-io/hangfire-ravendb"
},
"summary": "RavenDB job storage for Hangfire"
},
"dependencies": {
"Hangfire.Core": "1.6.4",
"Newtonsoft.Json": "9.0.1"
},
"frameworks": {
"net45": {
"frameworkAssemblies": {
"System.Data": "4.0.0.0",
"System.Reflection": "4.0.0.0"
},
"dependencies": {
"RavenDB.Client": "3.5.0-rc-35169"
}
},
"net451": {
"dependencies": {
"Microsoft.Extensions.DependencyModel": "1.0.0",
"RavenDB.Client": "3.5.0-rc-35169",
"System.Data.Common": "4.1.0"
}
},
"net452": {
"dependencies": {
"Microsoft.Extensions.DependencyModel": "1.0.0",
"RavenDB.Client": "3.5.0-rc-35169",
"System.Data.Common": "4.1.0"
}
},
"net46": {
"dependencies": {
"Microsoft.Extensions.DependencyModel": "1.0.0",
"RavenDB.Client": "3.5.0-rc-35169",
"System.Data.Common": "4.1.0"
}
},
"net461": {
"dependencies": {
"Microsoft.Extensions.DependencyModel": "1.0.0",
"RavenDB.Client": "3.5.0-rc-35169",
"System.Data.Common": "4.1.0"
}
},
"dnx451": {
"dependencies": {
"Microsoft.Extensions.DependencyModel": "1.0.0",
"RavenDB.Client": "3.5.0-rc-35169",
"System.Data.Common": "4.1.0"
}
},
"dnx46": {
"dependencies": {
"Microsoft.Extensions.DependencyModel": "1.0.0",
"RavenDB.Client": "3.5.0-rc-35169",
"System.Data.Common": "4.1.0"
}
},
"netcoreapp1.0": {
"imports": [
"dotnet5.6",
"dnxcore50",
"portable-net45+win8"
],
"dependencies": {
"Microsoft.Extensions.DependencyModel": "1.0.0",
"Microsoft.NETCore.App": {
"version": "1.0.0",
"type": "platform"
},
"NETStandard.Library": "1.6.0",
"RavenDB.Client": "3.5.0-rc-35169",
"System.Data.Common": "4.1.0"
}
},
"netstandard1.6": {
"imports": [
"dotnet5.6",
"dnxcore50",
"portable-net45+win8"
],
"dependencies": {
"Microsoft.Extensions.DependencyModel": "1.0.0",
"NETStandard.Library": "1.6.0",
"RavenDB.Client": "3.5.0-rc-35169",
"System.Data.Common": "4.1.0"
}
}
}
}
File diff suppressed because it is too large Load Diff