EDMIService: Add Quartz Scheduler, Add Caching for Datatables

This commit is contained in:
Jonathan Jenne 2020-12-07 16:41:46 +01:00
parent 96c4ce1abc
commit 87f5c3887e
10 changed files with 334 additions and 6 deletions

View File

@ -59,6 +59,10 @@
<assemblyIdentity name="FirebirdSql.Data.FirebirdClient" publicKeyToken="3750abcc3150b00c" culture="neutral" /> <assemblyIdentity name="FirebirdSql.Data.FirebirdClient" publicKeyToken="3750abcc3150b00c" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-7.5.0.0" newVersion="7.5.0.0" /> <bindingRedirect oldVersion="0.0.0.0-7.5.0.0" newVersion="7.5.0.0" />
</dependentAssembly> </dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.1" newVersion="4.0.1.1" />
</dependentAssembly>
</assemblyBinding> </assemblyBinding>
</runtime> </runtime>
<startup> <startup>

View File

@ -19,6 +19,7 @@ Public Class EDMIService
Public Shared Filesystem As Filesystem.File Public Shared Filesystem As Filesystem.File
Public Shared EDMIArchive As EDMI.File.Archive Public Shared EDMIArchive As EDMI.File.Archive
Public Shared GlobalState As GlobalState Public Shared GlobalState As GlobalState
Public Shared Scheduler As Scheduler
Private ReadOnly _logger As Logger Private ReadOnly _logger As Logger
@ -64,6 +65,28 @@ Public Class EDMIService
End Function End Function
#End Region #End Region
#Region "Database"
Public Function ReturnDatatableFromCache(Name As String) As TableResult Implements IEDMIService.ReturnDatatableFromCache
Try
Dim oSql = ""
_logger.Info($"ReturnDatatableFromCache, SQL: {oSql}")
Dim oDataset As DataSet = Scheduler.DataSet
Dim oDataTable As DataTable = Nothing
If oDataset.Tables.Contains(Name) Then
oDataTable = oDataset.Tables.Item(Name)
Else
Throw New ApplicationException($"DataTable {Name} does not exist")
End If
Return New TableResult(oDataTable)
Catch ex As Exception
_logger.Error(ex)
Return New TableResult(ex.Message)
End Try
End Function
#End Region
#Region "=== Database (MSSQL IDB) ===" #Region "=== Database (MSSQL IDB) ==="
Public Function ReturnDatatable_MSSQL_IDB(SQL As String) As TableResult Implements IEDMIService.ReturnDatatable_MSSQL_IDB Public Function ReturnDatatable_MSSQL_IDB(SQL As String) As TableResult Implements IEDMIService.ReturnDatatable_MSSQL_IDB
Try Try

View File

@ -67,16 +67,39 @@
<HintPath>..\packages\FirebirdSql.Data.FirebirdClient.7.5.0\lib\net452\FirebirdSql.Data.FirebirdClient.dll</HintPath> <HintPath>..\packages\FirebirdSql.Data.FirebirdClient.7.5.0\lib\net452\FirebirdSql.Data.FirebirdClient.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Extensions.Logging.Abstractions, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.Logging.Abstractions.2.1.1\lib\netstandard2.0\Microsoft.Extensions.Logging.Abstractions.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL"> <Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.4.7.5\lib\net45\NLog.dll</HintPath> <HintPath>..\packages\NLog.4.7.5\lib\net45\NLog.dll</HintPath>
</Reference> </Reference>
<Reference Include="Quartz, Version=3.2.3.0, Culture=neutral, PublicKeyToken=f6b8c98a402cc8a4, processorArchitecture=MSIL">
<HintPath>..\packages\Quartz.3.2.3\lib\net461\Quartz.dll</HintPath>
</Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll</HintPath>
</Reference>
<Reference Include="System.ComponentModel.DataAnnotations" /> <Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Configuration" /> <Reference Include="System.Configuration" />
<Reference Include="System.Configuration.Install" /> <Reference Include="System.Configuration.Install" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
<Reference Include="System.Deployment" /> <Reference Include="System.Deployment" />
<Reference Include="System.Diagnostics.DiagnosticSource, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Diagnostics.DiagnosticSource.4.7.1\lib\net46\System.Diagnostics.DiagnosticSource.dll</HintPath>
</Reference>
<Reference Include="System.IO.Compression" /> <Reference Include="System.IO.Compression" />
<Reference Include="System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll</HintPath>
</Reference>
<Reference Include="System.Numerics" />
<Reference Include="System.Numerics.Vectors, Version=4.1.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.4.5.3\lib\net461\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.Remoting" />
<Reference Include="System.Runtime.Serialization" /> <Reference Include="System.Runtime.Serialization" />
<Reference Include="System.ServiceModel" /> <Reference Include="System.ServiceModel" />
<Reference Include="System.ServiceProcess" /> <Reference Include="System.ServiceProcess" />
@ -108,6 +131,9 @@
<Compile Include="Exceptions.vb" /> <Compile Include="Exceptions.vb" />
<Compile Include="Results\DatabaseResult.vb" /> <Compile Include="Results\DatabaseResult.vb" />
<Compile Include="EDMIService.vb" /> <Compile Include="EDMIService.vb" />
<Compile Include="Scheduler.vb" />
<Compile Include="Scheduler\DatatableJob.vb" />
<Compile Include="Scheduler\JobListener.vb" />
<Compile Include="WindowsService.vb"> <Compile Include="WindowsService.vb">
<SubType>Component</SubType> <SubType>Component</SubType>
</Compile> </Compile>

View File

@ -4,21 +4,23 @@ Imports DigitalData.Modules.Logging
Public Class GlobalState Public Class GlobalState
Private ReadOnly _LogConfig As LogConfig Private ReadOnly _LogConfig As LogConfig
Private ReadOnly _Logger As Logger Private ReadOnly _Logger As Logger
Private ReadOnly _MSSQL As MSSQLServer Private ReadOnly _MSSQL_IDB As MSSQLServer
Private ReadOnly _MSSQL_ECM As MSSQLServer
Public Property ObjectStores As New List(Of ObjectStore) Public Property ObjectStores As New List(Of ObjectStore)
Public Sub New(LogConfig As LogConfig, MSSQL_IDB As MSSQLServer) Public Sub New(LogConfig As LogConfig, MSSQL_IDB As MSSQLServer, MSSQL_ECM As MSSQLServer)
_LogConfig = LogConfig _LogConfig = LogConfig
_Logger = LogConfig.GetLogger() _Logger = LogConfig.GetLogger()
_MSSQL = MSSQL_IDB _MSSQL_IDB = MSSQL_IDB
_MSSQL_ECM = MSSQL_ECM
End Sub End Sub
Public Sub LoadObjectStores() Public Sub LoadObjectStores()
_Logger.Debug("Loading Object Stores") _Logger.Debug("Loading Object Stores")
Try Try
Dim oSQL As String = "SELECT * FROM VWIDB_OBJECTSTORE" Dim oSQL As String = "SELECT * FROM VWIDB_OBJECTSTORE"
Dim oDatatable As DataTable = _MSSQL.GetDatatable(oSQL) Dim oDatatable As DataTable = _MSSQL_IDB.GetDatatable(oSQL)
ObjectStores.Clear() ObjectStores.Clear()
@ -36,7 +38,6 @@ Public Class GlobalState
Next Next
Catch ex As Exception Catch ex As Exception
_Logger.Error(ex) _Logger.Error(ex)
Throw ex
End Try End Try
End Sub End Sub

View File

@ -11,6 +11,11 @@ Interface IEDMIService
Function Heartbeat() As Boolean Function Heartbeat() As Boolean
#End Region #End Region
#Region "Database"
<OperationContract>
Function ReturnDatatableFromCache(Name As String) As TableResult
#End Region
#Region "Database (Firebird)" #Region "Database (Firebird)"
<OperationContract> <OperationContract>
Function ReturnDatatable_Firebird(SQL As String) As TableResult Function ReturnDatatable_Firebird(SQL As String) As TableResult

View File

@ -0,0 +1,164 @@
Imports System.Collections.Specialized
Imports DigitalData.Modules.Database
Imports DigitalData.Modules.Logging
Imports Quartz
Imports Quartz.Impl
Imports Quartz.Impl.Matchers
Imports Quartz.Logging
Public Class Scheduler
Private _Factory As StdSchedulerFactory
Private _MSSQL As MSSQLServer
Private _Scheduler As IScheduler
Private _LogConfig As LogConfig
Private _Logger As DigitalData.Modules.Logging.Logger
Private _JobListener As JobListener
Private _Props = New NameValueCollection From {
{"quartz.serializer.type", "binary"}
}
Private Const JOB_GROUP As String = "DatatableJobs"
Public ReadOnly Property DataSet As DataSet
Get
Return _JobListener.Dataset
End Get
End Property
Public Sub New(LogConfig As LogConfig, MSSQL_ECM As MSSQLServer)
_LogConfig = LogConfig
_Logger = LogConfig.GetLogger()
_Factory = New StdSchedulerFactory(_Props)
_MSSQL = MSSQL_ECM
Dim oDataSet As New DataSet()
_JobListener = New JobListener(LogConfig, oDataSet)
Quartz.Logging.LogProvider.SetCurrentLogProvider(New LogProvider(_Logger))
End Sub
Public Async Sub Start()
' Get new scheduler
_Scheduler = Await _Factory.GetScheduler()
' configure it
_Scheduler.ListenerManager.AddJobListener(_JobListener,
GroupMatcher(Of JobKey).GroupEquals(JOB_GROUP))
' start it
Await _Scheduler.Start()
Dim oCronjobs As DataTable = Await GetCronJobs()
Try
If oCronjobs IsNot Nothing Then
_Logger.Debug("Loaded {0} cron jobs", oCronjobs.Rows.Count)
For Each oRow As DataRow In oCronjobs.Rows
Dim oDefinition As String = oRow.Item("CRON_DEFINITION")
Dim oTitle As String = oRow.Item("TITLE")
Dim oGuid As Integer = oRow.Item("GUID")
Dim oCronDetails As DataTable = Await GetCronJobDetails(oGuid)
If oCronDetails IsNot Nothing Then
_Logger.Debug("Loaded job [{0}]", oTitle)
_Logger.Debug("Job details: {0}", oCronDetails.Rows.Count)
_Logger.Debug("Job definition: {0}", oDefinition)
For Each oRowDetail In oCronDetails.Rows
Dim oTrigger As ITrigger
Dim oJob As IJobDetail
oTrigger = TriggerBuilder.Create().
WithIdentity(oTitle).
WithCronSchedule(oDefinition).
StartNow().
Build()
oJob = JobBuilder.Create(Of DatatableJob)().
WithIdentity(oGuid, JOB_GROUP).
UsingJobData(New JobDataMap() From {
{"LogConfig", _LogConfig},
{"MSSQL", _MSSQL},
{"CronJobId", oGuid},
{"CronJobTitle", oTitle},
{"CronJobDetails", oRowDetail}
}).
Build()
Await _Scheduler.ScheduleJob(oJob, oTrigger)
_Logger.Debug("Scheduled a new job for Cron Job Id [{0}]", oGuid)
Next
Else
_Logger.Warn("CronJob Details for CronJob [{0}] could not be fetched!", oGuid)
End If
Next
Else
_Logger.Warn("CronJobs could not be fetched!")
End If
Catch ex As Exception
_Logger.Error(ex)
_Logger.Warn("Unexpected Error while setting up scheduler: " & ex.Message)
End Try
End Sub
Public Async Function GetCronJobs() As Task(Of DataTable)
Try
Dim oSQL As String = "SELECT * FROM TBAPPSERV_CRON_JOB WHERE ACTIVE = 1"
Dim oDatatable As DataTable = Await _MSSQL.GetDatatableAsync(oSQL)
Return oDatatable
Catch ex As Exception
Return Nothing
End Try
End Function
Public Async Function GetCronJobDetails(CronJobId As Integer) As Task(Of DataTable)
Try
Dim oSQL As String = $"SELECT * FROM TBAPPSERV_CRON_DETAIL WHERE CRON_ID = {CronJobId}"
Dim oDatatable As DataTable = Await _MSSQL.GetDatatableAsync(oSQL)
Return oDatatable
Catch ex As Exception
Return Nothing
End Try
End Function
Public Async Sub [Stop]()
Await _Scheduler.Shutdown()
End Sub
Private Class LogProvider
Implements ILogProvider
Private _Logger As Modules.Logging.Logger
Public Sub New(Logger As DigitalData.Modules.Logging.Logger)
MyBase.New()
_Logger = Logger
End Sub
Public Function OpenNestedContext(message As String) As IDisposable Implements ILogProvider.OpenNestedContext
Throw New NotImplementedException()
End Function
Public Function OpenMappedContext(key As String, value As Object, Optional destructure As Boolean = False) As IDisposable Implements ILogProvider.OpenMappedContext
Throw New NotImplementedException()
End Function
Private Function GetLogger(name As String) As Logging.Logger Implements ILogProvider.GetLogger
Return Function(level, func, exception, parameters)
If exception IsNot Nothing Then
_Logger.Error(exception)
ElseIf level >= LogLevel.Debug AndAlso func IsNot Nothing Then
_Logger.Debug(func(), parameters)
ElseIf level >= LogLevel.Info AndAlso func IsNot Nothing Then
_Logger.Info(func(), parameters)
End If
Return True
End Function
End Function
End Class
End Class

View File

@ -0,0 +1,40 @@
Imports DigitalData.Modules.Database
Imports DigitalData.Modules.Logging
Imports Quartz
Public Class DatatableJob
Implements IJob
Public Function Execute(context As IJobExecutionContext) As Task Implements IJob.Execute
Dim oJobData = context.MergedJobDataMap
Dim oLogConfig As LogConfig = oJobData.Item("LogConfig")
Dim oCronJobTitle As String = oJobData.Item("CronJobTitle")
Dim oDetailRow As DataRow = oJobData.Item("CronJobDetails")
Dim oMSSQL As MSSQLServer = oJobData.Item("MSSQL")
Dim oConnectionId As Integer = oDetailRow.Item("CON_ID")
Dim oTitle As String = oDetailRow.Item("TITLE")
Dim oDatatableName As String = oDetailRow.Item("DT_NAME")
Dim oSQL As String = oDetailRow.Item("COMMAND")
Dim oLogger As Logger = oLogConfig.GetLogger()
oLogger.Debug("Running Command-Job [{0}]", oTitle)
oLogger.Debug("Datatable Name: {0}", oDatatableName)
oLogger.Debug("Connection Id: {0}", oConnectionId)
Dim oConnectionString = oMSSQL.Get_ConnectionStringforID(oConnectionId)
Try
Dim oResult = oMSSQL.GetDatatableWithConnection(oSQL, oConnectionString)
oResult.TableName = oDatatableName
oLogger.Debug("Result Datatable contains [{0}] rows", oResult.Rows.Count)
' Das Ergebnis speichern
context.Result = oResult
Catch ex As Exception
oLogger.Warn("Unhandled exception while executing SQL for Datatable {}")
End Try
Return Task.FromResult(True)
End Function
End Class

View File

@ -0,0 +1,49 @@
Imports System.Threading
Imports DigitalData.Modules.Logging
Imports Quartz
Imports Quartz.Listener
Public Class JobListener
Inherits JobListenerSupport
Public Overrides ReadOnly Property Name As String = "JobListener"
Public Property Dataset As DataSet
Private ReadOnly _Logger As Logger
Private ReadOnly _LogConfig As LogConfig
Public Sub New(LogConfig As LogConfig, ResultDataSet As DataSet)
MyBase.New()
_LogConfig = LogConfig
_Logger = LogConfig.GetLogger()
Dataset = ResultDataSet
End Sub
Public Overrides Function JobWasExecuted(context As IJobExecutionContext, jobException As JobExecutionException, Optional cancellationToken As CancellationToken = Nothing) As Task
Try
Dim oDataTable As DataTable = context.Result
Dim oDetailRow As DataRow = context.MergedJobDataMap.Item("CronJobDetails")
Dim oDatatableName As String = oDetailRow.Item("DT_NAME")
Dim oDatatableNameTemp As String = oDatatableName & "-TEMP"
If Dataset.Tables.Contains(oDatatableName) Then
_Logger.Debug("DataTable exists, renaming and replacing")
' Rename the new table, add TEMP suffix
oDataTable.TableName = oDatatableNameTemp
' Add the new table to the dataset
Dataset.Tables.Add(oDataTable)
' Remove the old table
Dataset.Tables.Remove(oDatatableName)
' Rename the new table
Dataset.Tables.Item(oDatatableNameTemp).TableName = oDatatableName
Else
_Logger.Debug("DataTable does not exist, adding")
Dataset.Tables.Add(oDataTable)
End If
Catch ex As Exception
_Logger.Error(ex)
End Try
Return MyBase.JobWasExecuted(context, jobException, cancellationToken)
End Function
End Class

View File

@ -23,6 +23,7 @@ Public Class WindowsService
Private _Archive As EDMI.File.Archive Private _Archive As EDMI.File.Archive
Private _Filesystem As Filesystem.File Private _Filesystem As Filesystem.File
Private _Global As GlobalState Private _Global As GlobalState
Private _Scheduler As Scheduler
Public Sub New() Public Sub New()
ServiceName = SERVICE_NAME ServiceName = SERVICE_NAME
@ -37,6 +38,7 @@ Public Class WindowsService
Dim oServicePath As String = AppDomain.CurrentDomain.BaseDirectory Dim oServicePath As String = AppDomain.CurrentDomain.BaseDirectory
_LogConfig = New LogConfig(LogConfig.PathType.CustomPath, oServicePath) _LogConfig = New LogConfig(LogConfig.PathType.CustomPath, oServicePath)
_LogConfig.Debug = True
_Logger = _LogConfig.GetLogger() _Logger = _LogConfig.GetLogger()
_Logger.Info("Service {0} is starting...", SERVICE_DISPLAY_NAME) _Logger.Info("Service {0} is starting...", SERVICE_DISPLAY_NAME)
_Logger.Info("ServiceDirectory: {0}", oServicePath) _Logger.Info("ServiceDirectory: {0}", oServicePath)
@ -56,11 +58,15 @@ Public Class WindowsService
_Archive = New EDMI.File.Archive(_LogConfig) _Archive = New EDMI.File.Archive(_LogConfig)
_Filesystem = New Filesystem.File(_LogConfig) _Filesystem = New Filesystem.File(_LogConfig)
_Global = New GlobalState(_LogConfig, _MSSQL_IDB) _Global = New GlobalState(_LogConfig, _MSSQL_IDB, _MSSQL_ECM)
_Scheduler = New Scheduler(_LogConfig, _MSSQL_ECM)
_Logger.Debug("Loading Objectstores") _Logger.Debug("Loading Objectstores")
_Global.LoadObjectStores() _Global.LoadObjectStores()
_Logger.Debug("Starting Scheduler")
_Scheduler.Start()
_Logger.Debug("Preparing WCF ServiceHost") _Logger.Debug("Preparing WCF ServiceHost")
EDMIService.MSSQL_ECM = _MSSQL_ECM EDMIService.MSSQL_ECM = _MSSQL_ECM
EDMIService.MSSQL_IDB = _MSSQL_IDB EDMIService.MSSQL_IDB = _MSSQL_IDB
@ -70,6 +76,7 @@ Public Class WindowsService
EDMIService.EDMIArchive = _Archive EDMIService.EDMIArchive = _Archive
EDMIService.Filesystem = _Filesystem EDMIService.Filesystem = _Filesystem
EDMIService.GlobalState = _Global EDMIService.GlobalState = _Global
EDMIService.Scheduler = _Scheduler
_Logger.Debug("Starting WCF ServiceHost") _Logger.Debug("Starting WCF ServiceHost")
_ServiceHost = New ServiceHost(GetType(EDMIService)) _ServiceHost = New ServiceHost(GetType(EDMIService))
@ -121,6 +128,8 @@ Public Class WindowsService
_ServiceHost.Close() _ServiceHost.Close()
_ServiceHost = Nothing _ServiceHost = Nothing
End If End If
_Scheduler.Stop()
End Sub End Sub
End Class End Class

View File

@ -1,5 +1,12 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="FirebirdSql.Data.FirebirdClient" version="7.5.0" targetFramework="net472" /> <package id="FirebirdSql.Data.FirebirdClient" version="7.5.0" targetFramework="net472" />
<package id="Microsoft.Extensions.Logging.Abstractions" version="2.1.1" targetFramework="net472" />
<package id="NLog" version="4.7.5" targetFramework="net472" /> <package id="NLog" version="4.7.5" targetFramework="net472" />
<package id="Quartz" version="3.2.3" targetFramework="net472" />
<package id="System.Buffers" version="4.5.1" targetFramework="net472" />
<package id="System.Diagnostics.DiagnosticSource" version="4.7.1" targetFramework="net472" />
<package id="System.Memory" version="4.5.4" targetFramework="net472" />
<package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net472" />
<package id="System.Runtime.CompilerServices.Unsafe" version="4.5.3" targetFramework="net472" />
</packages> </packages>