Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .github/workflows/createrelease.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ jobs:
dotnet publish "TransactionProcessing.MerchantPos/TransactionProcessing.MerchantPos.csproj" --configuration Release --output TransactionProcessing.MerchantPos/publishOutput -r win-x64 --self-contained -p:Version=${{ steps.get_version.outputs.VERSION }} -p:AssemblyVersion=${{ steps.get_version.outputs.VERSION }} -p:FileVersion=${{ steps.get_version.outputs.VERSION }} -p:InformationalVersion=${{ steps.get_version.outputs.VERSION }}
dotnet publish "TransactionProcessing.MerchantFileProcessor/TransactionProcessing.MerchantFileProcessor.csproj" --configuration Release --output TransactionProcessing.MerchantFileProcessor/publishOutput -r win-x64 --self-contained -p:Version=${{ steps.get_version.outputs.VERSION }} -p:AssemblyVersion=${{ steps.get_version.outputs.VERSION }} -p:FileVersion=${{ steps.get_version.outputs.VERSION }} -p:InformationalVersion=${{ steps.get_version.outputs.VERSION }}
dotnet publish "HealthMonitoring/HealthMonitoring/HealthMonitoring.csproj" --configuration Release --output HealthMonitoring/publishOutput -r win-x64 --self-contained -p:Version=${{ steps.get_version.outputs.VERSION }} -p:AssemblyVersion=${{ steps.get_version.outputs.VERSION }} -p:FileVersion=${{ steps.get_version.outputs.VERSION }} -p:InformationalVersion=${{ steps.get_version.outputs.VERSION }}
dotnet publish "ScheduledTasks/DailySupportCheck/KurrentDbClient/KurrentDbClient.csproj" --configuration Release --output ScheduledTasks/DailySupportCheck/KurrentDbClient/publishOutput --no-self-contained

- name: Build Release Package (Merchant Pos)
run: |
Expand Down Expand Up @@ -162,6 +163,16 @@ jobs:
Where-Object { $_.FullName -notmatch '\\Reports\\|\\Tests\\' } |
Copy-Item -Destination $taskScriptDir -Force

$kurrentDbClientOutput = Get-ChildItem -Path $scheduledTasksRoot -Recurse -Directory -Filter publishOutput |
Where-Object { $_.FullName -match '\\DailySupportCheck\\KurrentDbClient\\publishOutput$' } |
Select-Object -First 1
if (-not $kurrentDbClientOutput) {
throw 'Packaged KurrentDB client output was not found in the scheduled-tasks artifact.'
}
$kurrentDbClientDestination = Join-Path $taskScriptDir 'KurrentDbClient'
New-Item -ItemType Directory -Path $kurrentDbClientDestination -Force | Out-Null
Copy-Item -Path (Join-Path $kurrentDbClientOutput.FullName '*') -Destination $kurrentDbClientDestination -Recurse -Force

Get-ChildItem -Path $scheduledTasksRoot -Recurse -Filter *.xml |
Where-Object { $_.FullName -notmatch '\\Tests\\' } |
ForEach-Object {
Expand Down Expand Up @@ -366,6 +377,16 @@ jobs:
Where-Object { $_.FullName -notmatch '\\Reports\\|\\Tests\\' } |
Copy-Item -Destination $taskScriptDir -Force

$kurrentDbClientOutput = Get-ChildItem -Path $scheduledTasksRoot -Recurse -Directory -Filter publishOutput |
Where-Object { $_.FullName -match '\\DailySupportCheck\\KurrentDbClient\\publishOutput$' } |
Select-Object -First 1
if (-not $kurrentDbClientOutput) {
throw 'Packaged KurrentDB client output was not found in the scheduled-tasks artifact.'
}
$kurrentDbClientDestination = Join-Path $taskScriptDir 'KurrentDbClient'
New-Item -ItemType Directory -Path $kurrentDbClientDestination -Force | Out-Null
Copy-Item -Path (Join-Path $kurrentDbClientOutput.FullName '*') -Destination $kurrentDbClientDestination -Recurse -Force

Get-ChildItem -Path $scheduledTasksRoot -Recurse -Filter *.xml |
Where-Object { $_.FullName -notmatch '\\Tests\\' } |
ForEach-Object {
Expand Down
11 changes: 11 additions & 0 deletions .github/workflows/pushtomain.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ jobs:
dotnet publish "TransactionProcessing.MerchantPos/TransactionProcessing.MerchantPos.csproj" --configuration Release --output TransactionProcessing.MerchantPos/publishOutput -r win-x64 --self-contained -p:Version=${{ steps.next_version.outputs.VERSION }} -p:AssemblyVersion=${{ steps.next_version.outputs.VERSION }} -p:FileVersion=${{ steps.next_version.outputs.VERSION }} -p:InformationalVersion=${{ steps.next_version.outputs.VERSION }}
dotnet publish "TransactionProcessing.MerchantFileProcessor/TransactionProcessing.MerchantFileProcessor.csproj" --configuration Release --output TransactionProcessing.MerchantFileProcessor/publishOutput -r win-x64 --self-contained -p:Version=${{ steps.next_version.outputs.VERSION }} -p:AssemblyVersion=${{ steps.next_version.outputs.VERSION }} -p:FileVersion=${{ steps.next_version.outputs.VERSION }} -p:InformationalVersion=${{ steps.next_version.outputs.VERSION }}
dotnet publish "HealthMonitoring/HealthMonitoring/HealthMonitoring.csproj" --configuration Release --output HealthMonitoring/publishOutput -r win-x64 --self-contained -p:Version=${{ steps.next_version.outputs.VERSION }} -p:AssemblyVersion=${{ steps.next_version.outputs.VERSION }} -p:FileVersion=${{ steps.next_version.outputs.VERSION }} -p:InformationalVersion=${{ steps.next_version.outputs.VERSION }}
dotnet publish "ScheduledTasks/DailySupportCheck/KurrentDbClient/KurrentDbClient.csproj" --configuration Release --output ScheduledTasks/DailySupportCheck/KurrentDbClient/publishOutput --no-self-contained

- name: Build Release Package (Merchant Pos)
run: |
Expand Down Expand Up @@ -176,6 +177,16 @@ jobs:
Where-Object { $_.FullName -notmatch '\\Reports\\|\\Tests\\' } |
Copy-Item -Destination $taskScriptDir -Force

$kurrentDbClientOutput = Get-ChildItem -Path $scheduledTasksRoot -Recurse -Directory -Filter publishOutput |
Where-Object { $_.FullName -match '\\DailySupportCheck\\KurrentDbClient\\publishOutput$' } |
Select-Object -First 1
if (-not $kurrentDbClientOutput) {
throw 'Packaged KurrentDB client output was not found in the scheduled-tasks artifact.'
}
$kurrentDbClientDestination = Join-Path $taskScriptDir 'KurrentDbClient'
New-Item -ItemType Directory -Path $kurrentDbClientDestination -Force | Out-Null
Copy-Item -Path (Join-Path $kurrentDbClientOutput.FullName '*') -Destination $kurrentDbClientDestination -Recurse -Force

Get-ChildItem -Path $scheduledTasksRoot -Recurse -Filter *.xml |
Where-Object { $_.FullName -notmatch '\\Tests\\' } |
ForEach-Object {
Expand Down
81 changes: 16 additions & 65 deletions ScheduledTasks/DailySupportCheck/Invoke-DailySupportCheck.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -707,35 +707,14 @@ function Import-KurrentDbClientAssembly {
[CmdletBinding()]
param([Parameter(Mandatory)] [string] $AssemblyPath)

if ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GetName().Name -eq 'KurrentDB.Client' }) {
if ([AppDomain]::CurrentDomain.GetAssemblies() | Where-Object { $_.GetName().Name -eq 'SupportTools.KurrentDbClient' }) {
return
}

if (-not (Test-Path -LiteralPath $AssemblyPath -PathType Leaf)) {
throw "KurrentDB client assembly was not found: $AssemblyPath"
}

$frameworkRoot = Join-Path ([Environment]::GetFolderPath('ProgramFiles')) 'dotnet\shared\Microsoft.AspNetCore.App'
$frameworkDirectory = if (Test-Path -LiteralPath $frameworkRoot) {
@(
Get-ChildItem -LiteralPath $frameworkRoot -Directory | Where-Object Name -like '9.*' | Sort-Object Name -Descending
Get-ChildItem -LiteralPath $frameworkRoot -Directory | Sort-Object Name -Descending
) | Select-Object -First 1
}

foreach ($dependencyName in @(
'Microsoft.Extensions.DependencyInjection.Abstractions.dll',
'Microsoft.Extensions.Logging.Abstractions.dll',
'Microsoft.Extensions.Options.dll'
)) {
if ($frameworkDirectory) {
$dependencyPath = Join-Path $frameworkDirectory.FullName $dependencyName
if (Test-Path -LiteralPath $dependencyPath -PathType Leaf) {
Add-Type -Path $dependencyPath
}
}
}

Add-Type -Path $AssemblyPath
}

Expand Down Expand Up @@ -767,11 +746,11 @@ function Test-KurrentDbWriteActivity {
}

$timeoutSeconds = if ($settings.PSObject.Properties['TimeoutSeconds']) { [int] $settings.TimeoutSeconds } else { 10 }
$assemblyPath = if ($settings.PSObject.Properties['ClientAssemblyPath'] -and -not [string]::IsNullOrWhiteSpace([string] $settings.ClientAssemblyPath)) {
[string] $settings.ClientAssemblyPath
$assemblyPath = if ($settings.PSObject.Properties['KurrentDbClientAssemblyPath'] -and -not [string]::IsNullOrWhiteSpace([string] $settings.KurrentDbClientAssemblyPath)) {
[string] $settings.KurrentDbClientAssemblyPath
}
else {
Join-Path $PSScriptRoot '..\..\StreamManagementTool\bin\Debug\net10.0\KurrentDB.Client.dll'
Join-Path $PSScriptRoot 'KurrentDbClient\SupportTools.KurrentDbClient.dll'
}
if (-not [IO.Path]::IsPathRooted($assemblyPath)) {
$assemblyPath = [IO.Path]::GetFullPath((Join-Path (Split-Path -Parent $Context.ConfigPath) $assemblyPath))
Expand All @@ -790,54 +769,26 @@ function Test-KurrentDbWriteActivity {
"esdb://$($baseUri.Host):${port}?tls=false&tlsVerifyCert=false"
}

$client = [KurrentDB.Client.KurrentDBClient]::new([KurrentDB.Client.KurrentDBClientSettings]::Create($connectionString))
$cancellationSource = [Threading.CancellationTokenSource]::new()
$cancellationSource.CancelAfter([timespan]::FromSeconds($timeoutSeconds))
$userCredentials = $null
$password = $null
if ($settings.PSObject.Properties['Username'] -and -not [string]::IsNullOrWhiteSpace([string] $settings.Username)) {
$passwordEnvironmentVariable = [string] $settings.PasswordEnvironmentVariable
$password = [Environment]::GetEnvironmentVariable($passwordEnvironmentVariable)
if ([string]::IsNullOrWhiteSpace($password)) {
throw "KurrentDB password environment variable '$passwordEnvironmentVariable' is not set."
}
$userCredentials = [KurrentDB.Client.UserCredentials]::new([string] $settings.Username, $password)
}

try {
$filter = [KurrentDB.Client.StreamFilter]::Prefix($indexName)
$read = $client.ReadAllAsync(
[KurrentDB.Client.Direction]::Backwards,
[KurrentDB.Client.Position]::End,
$filter,
$requestCount,
$false,
[timespan]::FromSeconds($timeoutSeconds),
$userCredentials,
$cancellationSource.Token
)
$enumerator = $read.GetAsyncEnumerator()
$events = [System.Collections.Generic.List[object]]::new()
try {
while ($enumerator.MoveNextAsync().AsTask().GetAwaiter().GetResult()) {
$currentEvent = $enumerator.Current.Event
$events.Add([pscustomobject]@{
EventId = [string] $currentEvent.EventId
EventType = [string] $currentEvent.EventType
Timestamp = $currentEvent.Created.ToUniversalTime().ToString('o')
Title = [string] $currentEvent.EventStreamId
})
}
}
finally {
$enumerator.DisposeAsync().AsTask().GetAwaiter().GetResult() | Out-Null
}

[pscustomobject]@{ events = @($events) }
}
finally {
$cancellationSource.Dispose()
$client.Dispose()
}
$reader = [SupportTools.KurrentDbClient.KurrentDbEventReader]::new()
$events = $reader.ReadRecentEventsAsync(
$connectionString,
$indexName,
$requestCount,
[timespan]::FromSeconds($timeoutSeconds),
[string] $settings.Username,
$password,
[Threading.CancellationToken]::None).GetAwaiter().GetResult()

[pscustomobject]@{ events = @($events) }
}
if ($Context.PSObject.Properties['KurrentDbWriteActivityProvider']) {
$provider = $Context.KurrentDbWriteActivityProvider
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<IsPackable>false</IsPackable>
<AssemblyName>SupportTools.KurrentDbClient</AssemblyName>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="KurrentDB.Client" Version="1.4.1" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using KurrentDB.Client;

namespace SupportTools.KurrentDbClient;

public sealed record KurrentDbEvent(
string EventId,
string EventType,
string Timestamp,
string Title);

public sealed class KurrentDbEventReader
{
public async Task<IReadOnlyList<KurrentDbEvent>> ReadRecentEventsAsync(
string connectionString,
string streamPrefix,
int eventCount,
TimeSpan timeout,
string? username = null,
string? password = null,
CancellationToken cancellationToken = default)
{
ArgumentException.ThrowIfNullOrWhiteSpace(connectionString);
ArgumentException.ThrowIfNullOrWhiteSpace(streamPrefix);
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(eventCount);

var settings = KurrentDBClientSettings.Create(connectionString);
using var client = new KurrentDBClient(settings);
using var timeoutSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
timeoutSource.CancelAfter(timeout);

UserCredentials? credentials = null;
if (!string.IsNullOrWhiteSpace(username))
credentials = new UserCredentials(username, password ?? string.Empty);

var events = new List<KurrentDbEvent>(eventCount);
var filter = StreamFilter.Prefix(streamPrefix);

await foreach (var resolvedEvent in client.ReadAllAsync(
Direction.Backwards,
Position.End,
filter,
eventCount,
resolveLinkTos: false,
deadline: timeout,
userCredentials: credentials,
cancellationToken: timeoutSource.Token))
{
var currentEvent = resolvedEvent.Event;
events.Add(new KurrentDbEvent(
currentEvent.EventId.ToString(),
currentEvent.EventType,
currentEvent.Created.ToUniversalTime().ToString("o"),
currentEvent.EventStreamId));
}

return events;
}
}
4 changes: 2 additions & 2 deletions ScheduledTasks/DailySupportCheck/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ The default configuration file is `daily-support-check.json` beside the script:
"KurrentDbWriteActivity": {
"Enabled": true,
"BaseUrl": "http://localhost:2113",
"ClientAssemblyPath": "..\\..\\StreamManagementTool\\bin\\Debug\\net10.0\\KurrentDB.Client.dll",
"KurrentDbClientAssemblyPath": ".\\KurrentDbClient\\SupportTools.KurrentDbClient.dll",
"Streams": [
{
"Name": "$idx-ce-TransactionAggregate",
Expand All @@ -102,7 +102,7 @@ The `KurrentDB Projections` check calls the configured `/projections/any` endpoi

The `Scheduled Tasks` check uses `Get-ScheduledTask` and `Get-ScheduledTaskInfo` to inspect only the configured tasks. It fails for missing, disabled, failed, overdue, or stale tasks, and reports the task state, last result, last run, next run, and failure reason. `ExpectedRunIntervalMinutes` determines how old the last run may be; the older `MaxLastRunAgeHours` property remains supported for compatibility. The current example checks `Daily Settlement` and `Replay Parked Queue` every 24 hours, and `Scavenge` every 7 days.

The `KurrentDB Write Activity` check reads the latest events backwards from `$all` using a secondary-index prefix filter for each configured index. It reports the event IDs, event types, and timestamps for each index. Each index passes when recent events are found, warns when fewer than its `EventCount` are available, and fails when no events are returned, the query is unavailable, or its latest event is older than its `MaxLatestEventAgeMinutes`. The overall check fails if any index fails and warns if one or more indexes warn. `ClientAssemblyPath` points to `KurrentDB.Client.dll`; it is resolved relative to the configuration file when relative. The check is disabled by default until the correct secondary indexes have been configured. For compatibility, the older single-stream properties (`StreamName`, `EventCount`, and `MaxLatestEventAgeMinutes`) are also accepted.
The `KurrentDB Write Activity` check reads the latest events backwards from `$all` using a secondary-index prefix filter for each configured index. It reports the event IDs, event types, and timestamps for each index. Each index passes when recent events are found, warns when fewer than its `EventCount` are available, and fails when no events are returned, the query is unavailable, or its latest event is older than its `MaxLatestEventAgeMinutes`. The overall check fails if any index fails and warns if one or more indexes warn. `KurrentDbClientAssemblyPath` points to the packaged `SupportTools.KurrentDbClient.dll` helper and is resolved relative to the configuration file when relative. The helper owns the KurrentDB client dependency so the check does not depend on `StreamManagementTool`. The check is disabled by default until the correct secondary indexes have been configured. For compatibility, the older single-stream properties (`StreamName`, `EventCount`, and `MaxLatestEventAgeMinutes`) are also accepted.

The default template checks are:

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
Describe 'Daily support check' {
It 'uses the packaged KurrentDB client rather than a source-tree build output' {
$configurationPath = Join-Path $PSScriptRoot '..\daily-support-check.json'
$configuration = Get-Content -LiteralPath $configurationPath -Raw | ConvertFrom-Json

$configuration.KurrentDbWriteActivity.KurrentDbClientAssemblyPath | Should -Be '.\KurrentDbClient\SupportTools.KurrentDbClient.dll'
}

BeforeAll {
. (Join-Path $PSScriptRoot '..\Invoke-DailySupportCheck.ps1')
}
Expand Down
2 changes: 1 addition & 1 deletion ScheduledTasks/DailySupportCheck/daily-support-check.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"KurrentDbWriteActivity": {
"Enabled": true,
"BaseUrl": "http://localhost:2113",
"ClientAssemblyPath": "..\\..\\StreamManagementTool\\bin\\Debug\\net10.0\\KurrentDB.Client.dll",
"KurrentDbClientAssemblyPath": ".\\KurrentDbClient\\SupportTools.KurrentDbClient.dll",
"Streams": [
{
"Name": "$idx-ce-TransactionAggregate",
Expand Down
Loading