Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
ff37431
Cloned the RavenDB 3.5 persister project to RavenDB5, updated the lic…
ramonsmits Sep 6, 2023
1639528
Artifact path to new RavenDB5 folder + RavenDB package
ramonsmits Sep 6, 2023
e96b3b3
Added missing namespace, tried updating most indexes
ramonsmits Sep 6, 2023
6f18f1a
Raven.Imports.Newtonsoft.Json replaced with Newtonsoft.Json.Linq
ramonsmits Sep 6, 2023
5aa4723
More namespaces
ramonsmits Sep 6, 2023
ed0c113
more namespaces
ramonsmits Sep 6, 2023
6b17008
RavenQueryStatistics
ramonsmits Sep 6, 2023
91f73b4
.As<T>
ramonsmits Sep 6, 2023
b80271d
WhereBetween is now INCLUSIVE where previously it was EXCLUSIVE
ramonsmits Sep 6, 2023
d2687dd
namespaces
ramonsmits Sep 6, 2023
8644e4d
Breaking change header meta data key changes
ramonsmits Sep 6, 2023
0becd8a
.Value<DateTime>
ramonsmits Sep 6, 2023
fd77adc
Raven5 will not store Audit or saga history, no need to clean them
DavidBoike Sep 7, 2023
fc6985d
No saga audit details in Raven5
DavidBoike Sep 7, 2023
193f970
No SagaHistory/SagaSnapshot or Audit Retention in Raven5
DavidBoike Sep 7, 2023
b37efcc
No audits in Raven5 means no failed audits either
DavidBoike Sep 7, 2023
d59bb8f
Change CheckRavenDBIndexLag custom check given database reports diffe…
DavidBoike Sep 7, 2023
4aba28c
More SagaAudit stuff
DavidBoike Sep 7, 2023
ca58b0c
No such thing as bundles. Have to use https://ravendb.net/docs/artic…
DavidBoike Sep 7, 2023
b5dca28
Try to fix migration (which may not even be needed)
DavidBoike Sep 7, 2023
8815393
Adding ExpirationProcessTimerInSeconds config back in, we'll need it …
DavidBoike Sep 7, 2023
f36d892
Remove all the manual expiration stuff
DavidBoike Sep 7, 2023
e347530
integration events dispatcher
tmasternak Sep 8, 2023
9b8a82a
small comment
tmasternak Sep 8, 2023
3e2e495
etag in querystatsinfo
tmasternak Sep 8, 2023
1671294
subscription storage
tmasternak Sep 8, 2023
93f1931
custom check data store
tmasternak Sep 8, 2023
0bc9d54
ravendb5 configuration and setup
tmasternak Sep 8, 2023
68f0973
Kill unsupported transformer, talk about complicated
DavidBoike Sep 8, 2023
4a4d750
Couple unused usings, rest of file fine
DavidBoike Sep 8, 2023
05cd8f3
Won't be any legacy subscriptions stored in a brand new Raven5 DB
DavidBoike Sep 8, 2023
794003e
Believe this was to support the expiration bundle, could be indexes b…
DavidBoike Sep 8, 2023
23780e8
cleanup RavenBootstrapper
tmasternak Sep 11, 2023
18757ca
ravendb5 lifecycle creation and resolution
tmasternak Sep 11, 2023
d7f18ed
🩹 Removed `MessagesBodyTransformer`, not needed as message bodies are…
ramonsmits Sep 11, 2023
eb9b804
Simplify MessagesViewIndex to not be multi-map since there will be no…
DavidBoike Sep 11, 2023
a46aaf9
These methods return Audit data and probably shouldn't be in the Erro…
DavidBoike Sep 11, 2023
dc7e5fc
MessagesViewTransformer not actually used
DavidBoike Sep 11, 2023
3797cd1
ErrorMessagesDataStore minor API fixes
DavidBoike Sep 11, 2023
9b58da4
QueryResultAysnc() => GetQueryResultAsync()
DavidBoike Sep 11, 2023
6e52250
index error custom check
tmasternak Sep 12, 2023
83c9da5
removing lastmodified extension
tmasternak Sep 12, 2023
3a9a5fb
commenting out transformers
tmasternak Sep 12, 2023
69df870
RavenDbRecoverabilityIngestionUnitOfWork
tmasternak Sep 12, 2023
fbf9efe
missing bit
tmasternak Sep 12, 2023
b2a4603
UnarchiveDocumentManager
tmasternak Sep 12, 2023
b45c8fb
ArchiveDocumentManager
tmasternak Sep 12, 2023
b4ad697
RavenDbMonitoringIngestionUnitOfWork and async fixes
tmasternak Sep 12, 2023
22b572d
⚜️Compiler hints
ramonsmits Sep 12, 2023
d08fe26
🩹 ErrorMessagesDataStore
ramonsmits Sep 12, 2023
0abd1d1
🩹 Just churning through the errors and small non-complex fixes
ramonsmits Sep 12, 2023
6880e9e
🩹 BeforeQueryExecution(index => index.Cutoff = cutoff) can no longer …
ramonsmits Sep 12, 2023
96e0cc2
Simple fixes
DavidBoike Sep 12, 2023
de1e8d9
Fix DocumentQuery FilterByStatus, hopefully will still be useful
DavidBoike Sep 12, 2023
a54f754
RetryDocumentDataStore patch operations
DavidBoike Sep 12, 2023
97a545d
Fix weirdly complex include API usage
DavidBoike Sep 12, 2023
6b01993
Patches in RetryBatchesDataStore
DavidBoike Sep 12, 2023
67868ad
A patch and a delete
DavidBoike Sep 12, 2023
bbe8dce
Comment out the rest of the Transformer stuff
DavidBoike Sep 12, 2023
2fcc3d7
Compile is green
DavidBoike Sep 12, 2023
ad06ef1
🩹Build order dependencies
ramonsmits Sep 13, 2023
c9a9295
*This* is how Raven5 persister gets built before ServiceControl core
DavidBoike Sep 13, 2023
c1242d0
Fix the double-executed lifecycle creation
DavidBoike Sep 13, 2023
9ec6376
App.config change to run primary using Raven5
DavidBoike Sep 13, 2023
92baa38
🩹 Resolved `PersistenceLifecycleHostedService` via DI to ensure lifec…
ramonsmits Sep 14, 2023
e88f9ae
🩹 Fix RavenDBPersisterSettings.LogsMode init
ramonsmits Sep 14, 2023
1af53ad
🩹 Fix LogPath init
ramonsmits Sep 14, 2023
b08ff5f
🩹 Fix inverted UseEmbeddedServer init issue
ramonsmits Sep 14, 2023
01356ad
🩹 Fix dubbble RavenDbEmbeddedPersistenceLifecycle DI instance creation
ramonsmits Sep 14, 2023
7c4a37e
🩹 Resolves: Cannot set identity value 'CustomChecks/193-A' on propert…
ramonsmits Sep 14, 2023
1f3dd8a
🩹 IndexLag in RavenDB5 is TimeSpan which resulted in a string format …
ramonsmits Sep 14, 2023
6681b09
🚧 TODO: Ensure audit ravendb5 persistence uses the same index lag b…
ramonsmits Sep 14, 2023
dabb15f
Restore message queries that must return failed message info to augme…
DavidBoike Sep 14, 2023
5eb9941
Revert "MessagesViewTransformer not actually used"
DavidBoike Sep 14, 2023
7320791
Add enough fake code to compile transformer-related code for now
DavidBoike Sep 14, 2023
1199961
Fix custom check datastore
DavidBoike Sep 15, 2023
d7d4754
Same custom check fix for Raven5
DavidBoike Sep 15, 2023
fdceb55
oops
DavidBoike Sep 15, 2023
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
1 change: 1 addition & 0 deletions src/Persisters.Primary.Includes.props
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<Project>
<ItemGroup Label="Persisters">
<ProjectReference Include="..\ServiceControl.Persistence.RavenDb\ServiceControl.Persistence.RavenDb.csproj" ReferenceOutputAssembly="false" Private="false" />
<ProjectReference Include="..\ServiceControl.Persistence.RavenDb5\ServiceControl.Persistence.RavenDb5.csproj" ReferenceOutputAssembly="false" Private="false" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ public static IHostBuilder SetupPersistence(this IHostBuilder hostBuilder,
{
var lifecycle = persistence.Configure(serviceCollection);

serviceCollection.AddHostedService(_ => new PersistenceLifecycleHostedService(lifecycle));
serviceCollection.AddSingleton(new PersistenceLifecycleHostedService(lifecycle));
serviceCollection.AddHostedService(sp => sp.GetRequiredService<PersistenceLifecycleHostedService>());
});

return hostBuilder;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ public override Task<CheckResult> PerformCheck()
return CheckResult.Failed(message);
}

static ILog Logger = LogManager.GetLogger<CheckRavenDBIndexLag>();
static readonly ILog Logger = LogManager.GetLogger<CheckRavenDBIndexLag>();

IDocumentStore store;
readonly IDocumentStore store;
}
}
22 changes: 0 additions & 22 deletions src/ServiceControl.Persistence.RavenDb/ErrorMessagesDataStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -136,28 +136,6 @@ SortInfo sortInfo
}
}

public async Task<QueryResult<IList<MessagesView>>> GetAllMessagesForEndpoint(
string searchTerms,
string receivingEndpointName,
PagingInfo pagingInfo,
SortInfo sortInfo
)
{
using (var session = documentStore.OpenAsyncSession())
{
var results = await session.Query<MessagesViewIndex.SortAndFilterOptions, MessagesViewIndex>()
.Statistics(out var stats)
.Search(x => x.Query, searchTerms)
.Where(m => m.ReceivingEndpointName == receivingEndpointName)
.Sort(sortInfo)
.Paging(pagingInfo)
.TransformWith<MessagesViewTransformer, MessagesView>()
.ToListAsync();

return new QueryResult<IList<MessagesView>>(results, stats.ToQueryStatsInfo());
}
}

public async Task<FailedMessage> FailedMessageFetch(string failedMessageId)
{
using (var session = documentStore.OpenAsyncSession())
Expand Down
4 changes: 3 additions & 1 deletion src/ServiceControl.Persistence.RavenDb/EventLogDataStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ public async Task Add(EventLogItem logItem)
{
using (var session = documentStore.OpenAsyncSession())
{
var results = await session.Query<EventLogItem>().Statistics(out var stats)
var results = await session
.Query<EventLogItem>()
.Statistics(out var stats)
.OrderByDescending(p => p.RaisedAt)
.Paging(pagingInfo)
.ToListAsync();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public async Task<CheckStateChange> UpdateCustomCheckStatus(CustomCheckDetail de
{
customCheck = new CustomCheck
{
Id = id
Id = MakeId(id)
};
}

Expand All @@ -54,6 +54,11 @@ public async Task<CheckStateChange> UpdateCustomCheckStatus(CustomCheckDetail de
return status;
}

static string MakeId(Guid id)
{
return $"CustomChecks/{id}";
}

public async Task<QueryResult<IList<CustomCheck>>> GetStats(PagingInfo paging, string status = null)
{
using (var session = store.OpenAsyncSession())
Expand All @@ -73,7 +78,7 @@ public async Task<QueryResult<IList<CustomCheck>>> GetStats(PagingInfo paging, s

public async Task DeleteCustomCheck(Guid id)
{
await store.AsyncDatabaseCommands.DeleteAsync(store.Conventions.DefaultFindFullDocumentKeyFromNonStringIdentifier(id, typeof(CustomCheck), false), null);
await store.AsyncDatabaseCommands.DeleteAsync(MakeId(id), null);
}

public async Task<int> GetNumberOfFailedChecks()
Expand Down
5 changes: 1 addition & 4 deletions src/ServiceControl.Persistence.RavenDb/RavenDbPersistence.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,7 @@ public void Configure(IServiceCollection serviceCollection)
serviceCollection.AddSingleton<IServiceControlSubscriptionStorage, RavenDbSubscriptionStorage>();
}

public IPersistenceLifecycle CreateLifecycle()
{
return new RavenDbPersistenceLifecycle(ravenStartup, documentStore);
}
public void ConfigureLifecycle(IServiceCollection serviceCollection) => serviceCollection.AddSingleton<IPersistenceLifecycle>(new RavenDbPersistenceLifecycle(ravenStartup, documentStore));

public IPersistenceInstaller CreateInstaller()
{
Expand Down
4 changes: 4 additions & 0 deletions src/ServiceControl.Persistence.RavenDb5/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[*.cs]

# Justification: ServiceControl app has no synchronization context
dotnet_diagnostic.CA2007.severity = none
40 changes: 40 additions & 0 deletions src/ServiceControl.Persistence.RavenDb5/Chunker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
namespace ServiceControl.Infrastructure.RavenDB
{
using System;
using System.Threading;

static class Chunker
{
public static int ExecuteInChunks<T1, T2>(int total, Func<T1, T2, int, int, int> action, T1 t1, T2 t2, CancellationToken cancellationToken = default)
{
if (total == 0)
{
return 0;
}

if (total < CHUNK_SIZE)
{
return action(t1, t2, 0, total - 1);
}

int start = 0, end = CHUNK_SIZE - 1;
var chunkCount = 0;
do
{
chunkCount += action(t1, t2, start, end);

start = end + 1;
end += CHUNK_SIZE;
if (end >= total)
{
end = total - 1;
}
}
while (start < total && !cancellationToken.IsCancellationRequested);

return chunkCount;
}

const int CHUNK_SIZE = 500;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
namespace ServiceControl.Operations
{
using System;
using System.IO;
using System.Threading.Tasks;
using NServiceBus.CustomChecks;
using NServiceBus.Logging;
using Persistence.RavenDb;

class CheckFreeDiskSpace : CustomCheck
{
public CheckFreeDiskSpace(RavenDBPersisterSettings settings) : base("ServiceControl database", "Storage space", TimeSpan.FromMinutes(5))
{
dataPath = settings.DatabasePath;
percentageThreshold = settings.DataSpaceRemainingThreshold;

Logger.Debug($"Check ServiceControl data drive space remaining custom check starting. Threshold {percentageThreshold:P0}");
}

public override Task<CheckResult> PerformCheck()
{
var dataPathRoot = Path.GetPathRoot(dataPath);

if (dataPathRoot == null)
{
throw new Exception($"Unable to find the root of the data path {dataPath}");
}

var dataDriveInfo = new DriveInfo(dataPathRoot);
var availableFreeSpace = (decimal)dataDriveInfo.AvailableFreeSpace;
var totalSpace = (decimal)dataDriveInfo.TotalSize;

var percentRemaining = (decimal)dataDriveInfo.AvailableFreeSpace / dataDriveInfo.TotalSize;

if (Logger.IsDebugEnabled)
{
Logger.Debug($"Free space: {availableFreeSpace:N0}B | Total: {totalSpace:N0}B | Percent remaining {percentRemaining:P1}");
}

return percentRemaining > percentageThreshold
? CheckResult.Pass
: CheckResult.Failed($"{percentRemaining:P0} disk space remaining on data drive '{dataDriveInfo.VolumeLabel} ({dataDriveInfo.RootDirectory})' on '{Environment.MachineName}'.");
}

public static void Validate(RavenDBPersisterSettings settings)
{
var threshold = settings.DataSpaceRemainingThreshold;

string message;

if (threshold < 0)
{
message = $"{RavenDbPersistenceConfiguration.DataSpaceRemainingThresholdKey} is invalid, minimum value is 0.";
Logger.Fatal(message);
throw new Exception(message);
}

if (threshold > 100)
{
message = $"{RavenDbPersistenceConfiguration.DataSpaceRemainingThresholdKey} is invalid, maximum value is 100.";
Logger.Fatal(message);
throw new Exception(message);
}
}

readonly string dataPath;
readonly decimal percentageThreshold;

public const int DataSpaceRemainingThresholdDefault = 20;
static readonly ILog Logger = LogManager.GetLogger(typeof(CheckFreeDiskSpace));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
namespace ServiceControl.Operations
{
using System;
using System.IO;
using System.Threading.Tasks;
using NServiceBus.CustomChecks;
using NServiceBus.Logging;
using Persistence.RavenDb;
using ServiceControl.Persistence;

class CheckMinimumStorageRequiredForIngestion : CustomCheck
{
public CheckMinimumStorageRequiredForIngestion(
MinimumRequiredStorageState stateHolder,
RavenDBPersisterSettings settings)
: base("Message Ingestion Process", "ServiceControl Health", TimeSpan.FromSeconds(5))
{
this.stateHolder = stateHolder;
this.settings = settings;

dataPathRoot = Path.GetPathRoot(settings.DatabasePath);
}

public override Task<CheckResult> PerformCheck()
{
percentageThreshold = settings.MinimumStorageLeftRequiredForIngestion / 100m;

if (dataPathRoot == null)
{
stateHolder.CanIngestMore = true;
return SuccessResult;
}

Logger.Debug($"Check ServiceControl data drive space starting. Threshold {percentageThreshold:P0}");

var dataDriveInfo = new DriveInfo(dataPathRoot);
var availableFreeSpace = (decimal)dataDriveInfo.AvailableFreeSpace;
var totalSpace = (decimal)dataDriveInfo.TotalSize;

var percentRemaining = (decimal)dataDriveInfo.AvailableFreeSpace / dataDriveInfo.TotalSize;

if (Logger.IsDebugEnabled)
{
Logger.Debug($"Free space: {availableFreeSpace:N0}B | Total: {totalSpace:N0}B | Percent remaining {percentRemaining:P1}");
}

if (percentRemaining > percentageThreshold)
{
stateHolder.CanIngestMore = true;
return SuccessResult;
}

var message = $"Error message ingestion stopped! {percentRemaining:P0} disk space remaining on data drive '{dataDriveInfo.VolumeLabel} ({dataDriveInfo.RootDirectory})' on '{Environment.MachineName}'. This is less than {percentageThreshold}% - the minimal required space configured. The threshold can be set using the {RavenBootstrapper.MinimumStorageLeftRequiredForIngestionKey} configuration setting.";
Logger.Warn(message);
stateHolder.CanIngestMore = false;
return CheckResult.Failed(message);
}

public static void Validate(RavenDBPersisterSettings settings)
{
int threshold = settings.MinimumStorageLeftRequiredForIngestion;

string message;
if (threshold < 0)
{
message = $"{RavenBootstrapper.MinimumStorageLeftRequiredForIngestionKey} is invalid, minimum value is 0.";
Logger.Fatal(message);
throw new Exception(message);
}

if (threshold > 100)
{
message = $"{RavenBootstrapper.MinimumStorageLeftRequiredForIngestionKey} is invalid, maximum value is 100.";
Logger.Fatal(message);
throw new Exception(message);
}
}

public const int MinimumStorageLeftRequiredForIngestionDefault = 5;

readonly MinimumRequiredStorageState stateHolder;
readonly RavenDBPersisterSettings settings;
readonly string dataPathRoot;

decimal percentageThreshold;

static readonly Task<CheckResult> SuccessResult = Task.FromResult(CheckResult.Pass);
static readonly ILog Logger = LogManager.GetLogger(typeof(CheckMinimumStorageRequiredForIngestion));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
namespace ServiceControl
{
using System;
using System.Text;
using System.Threading.Tasks;
using NServiceBus.CustomChecks;
using NServiceBus.Logging;
using Raven.Client.Documents;
using Raven.Client.Documents.Operations.Indexes;

class CheckRavenDBIndexErrors : CustomCheck
{
public CheckRavenDBIndexErrors(IDocumentStore store)
: base("Error Database Index Errors", "ServiceControl Health", TimeSpan.FromMinutes(5))
{
this.store = store;
}

public override Task<CheckResult> PerformCheck()
{
var indexErrors = store.Maintenance.Send(new GetIndexErrorsOperation());

if (indexErrors.Length == 0)
{
return CheckResult.Pass;
}

var text = new StringBuilder();
text.AppendLine("Detected RavenDB index errors, please start maintenance mode and resolve the following issues:");

foreach (var indexError in indexErrors)
{
foreach (var indexingError in indexError.Errors)
{
text.AppendLine($"- Index [{indexError.Name}] error: {indexError.Name} (Action: {indexingError.Action}, Doc: {indexingError.Document}, At: {indexingError.Timestamp})");
}
}

text.AppendLine().AppendLine("See: https://docs.particular.net/search?q=servicecontrol+troubleshooting");

var message = text.ToString();
Logger.Error(message);
return CheckResult.Failed(message);
}

static readonly ILog Logger = LogManager.GetLogger<CheckRavenDBIndexLag>();

readonly IDocumentStore store;
}
}
Loading