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
55 changes: 47 additions & 8 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:sil
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
# Modifier preferences
dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
dotnet_style_require_accessibility_modifiers = for_non_interface_members:warning
dotnet_style_readonly_field = true:suggestion
# Expression-level preferences
dotnet_style_object_initializer = true:suggestion
Expand Down Expand Up @@ -113,7 +113,7 @@ csharp_style_conditional_delegate_call = true:suggestion
# Modifier preferences
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion
# Expression-level preferences
csharp_prefer_braces = true:silent
csharp_prefer_braces = true:warning
csharp_style_deconstructed_variable_declaration = true:suggestion
csharp_prefer_simple_default_expression = true:suggestion
csharp_style_prefer_local_over_anonymous_function = true:suggestion
Expand Down Expand Up @@ -245,11 +245,12 @@ dotnet_diagnostic.SA1309.severity = none
# Tuple element names should use correct casing
dotnet_diagnostic.SA1316.severity = none

# File may only contain a single class
dotnet_diagnostic.SA1402.severity = suggestion
# File may only contain a single class. Off by default and warning for src alone, at the bottom
# of this file: tests, samples and benchmarks keep their small helper types next to what uses them.
dotnet_diagnostic.SA1402.severity = none

# Braces must not be omitted
dotnet_diagnostic.SA1503.severity = suggestion
dotnet_diagnostic.SA1503.severity = warning

# ElementsMustBeDocumented
dotnet_diagnostic.SA1600.severity = none
Expand Down Expand Up @@ -421,13 +422,13 @@ dotnet_diagnostic.SA1208.severity = none
dotnet_diagnostic.SA1135.severity = none

# SA1413: Use trailing comma in multi-line initializers
dotnet_diagnostic.SA1413.severity = suggestion
dotnet_diagnostic.SA1413.severity = warning

# SA1300: Element should begin with upper-case letter
dotnet_diagnostic.SA1300.severity = suggestion
dotnet_diagnostic.SA1300.severity = warning

# SA1117: Parameters should be on same line or separate lines
dotnet_diagnostic.SA1117.severity = suggestion
dotnet_diagnostic.SA1117.severity = warning

# CS8600: Converting null literal or possible null value to non-nullable type.
dotnet_diagnostic.CS8600.severity = error
Expand Down Expand Up @@ -471,3 +472,41 @@ dotnet_diagnostic.RS0041.severity = none
dotnet_diagnostic.RS0026.severity = none
dotnet_diagnostic.RS0027.severity = none

# StyleCop.Analyzers ships ~5000 warnings' worth of rules. Every category below is off except
# OrderingRules. A rule id beats its category here, so anything an exact id further up had already
# switched off has to be named again to apply: SA1204 and SA1208 are, SA1200 and SA1649 are not.
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.SpecialRules.severity = none
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.SpacingRules.severity = none
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.ReadabilityRules.severity = none
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.NamingRules.severity = none
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.MaintainabilityRules.severity = none
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.LayoutRules.severity = none
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.DocumentationRules.severity = none

# SA1649 (file name should match the first type) is already none further up, and stays that way:
# it does not recognise the OfT suffix this repo uses for generic types, so it would ask to
# rename CacheOfT.cs, ICacheOfT.cs and five more to Cache{T}.cs.
#
# SX1309, which asks every field to begin with an underscore, is already warning further up and
# stays that way: it is this repo's field convention, and SA1309 is off above for the same reason.
# Referencing the package is what makes it report, so the five fields that broke it are fixed.
Comment thread
cosmin-staicu marked this conversation as resolved.

# SA1204 (static before instance) and SA1208 (System usings first) are none further up. A specific
# id beats the category below, so each has to be named here to actually apply; the pass satisfies
# both. SA1200 stays none: this repo puts using directives above the file-scoped namespace.
dotnet_diagnostic.SA1204.severity = warning
dotnet_diagnostic.SA1208.severity = warning
Comment thread
cosmin-staicu marked this conversation as resolved.

# Ordering: the whole category, so SA1201 order by kind, SA1202 order by access, SA1204 static
# first, SA1203 constants first, SA1214 readonly first and SA1210 (usings sorted) all apply
# without a list of ids to maintain. Nothing off the shelf applies these: neither dotnet format nor
# the Roslynator CLI can drive StyleCop's fixer — both answer that no code fix was found — and
# Rider's layout engine ranks constants and statics above accessibility, which moves SA1202 the
# wrong way. The tree was brought into line by a one-off Roslyn pass instead, and is clean now.
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.OrderingRules.severity = warning
Comment thread
cosmin-staicu marked this conversation as resolved.
Comment thread
cosmin-staicu marked this conversation as resolved.

[src/**.cs]
# One top-level type per file, for the shipped surface only. topLevelTypes in stylecop.json widens
# SA1402 past its default of class alone. Tests, samples and benchmarks keep their small helper
# types next to what uses them: 17 files would otherwise have to be broken up.
dotnet_diagnostic.SA1402.severity = warning
7 changes: 6 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,13 @@ jobs:
- name: Restore
run: dotnet restore UiPath.Caching.slnx

# -warnaserror is what keeps the analyzer rules in .editorconfig from drifting back:
# TreatWarningsAsErrors stays false so a local build still compiles while you work. It rides
# on this job alone. build-linux builds under the SonarScanner, whose analyzer package is not
# pinned here, so a new Sonar rule would fail the build instead of reporting; the analyzers
# this repo does pin run identically on both jobs, so gating one catches the same drift.
- name: Build
run: dotnet build UiPath.Caching.slnx -c Release --no-restore
run: dotnet build UiPath.Caching.slnx -c Release --no-restore -warnaserror

# Redis-backed integration tests run on the Linux job (service containers are Linux-only).
# Windows is build-only to validate cross-platform compilation.
Expand Down
9 changes: 9 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,13 @@
<ItemGroup Condition="'$(IsPackable)' != 'false'">
<None Include="$(MSBuildThisFileDirectory)README.md" Pack="true" PackagePath="\" />
</ItemGroup>
<!-- StyleCop is here for the ordering rules and SA1402 (one top-level type per file, src only).
Everything else it ships is switched off by category in .editorconfig. -->
<ItemGroup>
<PackageReference Include="StyleCop.Analyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<AdditionalFiles Include="$(MSBuildThisFileDirectory)stylecop.json" Link="stylecop.json" />
</ItemGroup>
</Project>
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
<PackageVersion Include="Aspire.Hosting.Redis" Version="13.5.3" />
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
<PackageVersion Include="StackExchange.Redis" Version="3.1.31" />
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
<PackageVersion Include="Azure.Identity" Version="1.21.0" />
<PackageVersion Include="Microsoft.Azure.StackExchangeRedis" Version="3.3.1" />
<PackageVersion Include="AsyncKeyedLock" Version="8.0.2" />
Expand Down
8 changes: 4 additions & 4 deletions benchmarks/UiPath.Caching.Benchmarks/CacheBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ namespace UiPath.Caching.Benchmarks;
[HtmlExporter]
public class CacheBenchmark
{

private const int _batchSize = 50;
private Entry<CustomObject>[] _entries = default!;

[Params(500, 2_500)]
Expand All @@ -25,7 +27,7 @@ public class CacheBenchmark

protected Func<CustomObject> CreateRandomObject { get; set; } = default!;

private const int _batchSize = 50;
private ICache<CustomObject> RandomCache => _entries[Random.Shared.Next(0, _entries.Length)].Cache;

[GlobalSetup]
public void Setup()
Expand All @@ -35,13 +37,11 @@ public void Setup()
"Small" => CustomObject.RandomSmall,
"Medium" => CustomObject.RandomMedium,
"Large" => CustomObject.RandomLarge,
_ => throw new NotSupportedException(ObjectSize)
_ => throw new NotSupportedException(ObjectSize),
};
_entries = SetupHelper.Setup(2, Cache, $"Redis{Topic}", NumKeys, CreateRandomObject);
}

private ICache<CustomObject> RandomCache => _entries[Random.Shared.Next(0, _entries.Length)].Cache;


[GlobalCleanup]
public void Cleanup() => SetupHelper.Cleanup(_entries);
Expand Down
4 changes: 2 additions & 2 deletions benchmarks/UiPath.Caching.Benchmarks/CustomObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public static CustomObject RandomLarge()
Property2 = GenerateRandomString(1_000),
Property3 = GenerateRandomDoubleArray(10_000),
UtcDateTime = DateTime.UtcNow,
GuidList = GenerateRandomGuidList(5_000)
GuidList = GenerateRandomGuidList(5_000),
};

return customObject;
Expand All @@ -32,7 +32,7 @@ public static CustomObject RandomMedium()
Property2 = GenerateRandomString(100),
Property3 = GenerateRandomDoubleArray(10),
UtcDateTime = DateTime.UtcNow,
GuidList = GenerateRandomGuidList(10)
GuidList = GenerateRandomGuidList(10),
};

return customObject;
Expand Down
4 changes: 2 additions & 2 deletions benchmarks/UiPath.Caching.Benchmarks/SerializerBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ namespace UiPath.Caching.Benchmarks;
[CategoriesColumn]
public class SerializerBenchmark
{
[Params("Small", "Medium", "Large")]
public string Size { get; set; } = "Medium";

private CustomObject _obj = default!;
private RedisValue _payload;
[Params("Small", "Medium", "Large")]
public string Size { get; set; } = "Medium";

[GlobalSetup]
public void Setup()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using System.Collections.Concurrent;
using System.Globalization;
using UiPath.Caching.Benchmarks;
using Microsoft.Extensions.DependencyInjection;
using StackExchange.Redis;
using UiPath.Caching.Benchmarks;
using UiPath.Caching.Redis;

namespace UiPath.Caching.Benchmarks;
Expand Down Expand Up @@ -31,6 +31,8 @@ namespace UiPath.Caching.Benchmarks;
// Run with: dotnet run -c Release --framework net8.0 -- doorbell [durationSec] [writeHz]
internal static class StreamNotifyDoorbellHarness
{

private const string TimestampPrefix = "ts:";
public static async Task RunAsync(int durationSec = 20, int writeHz = 5)
{
var cells = new (bool NotifyEnabled, string PollInterval)[]
Expand Down Expand Up @@ -170,6 +172,4 @@ private static async Task<List<TimeSpan>> MeasureCellAsync(bool notifyEnabled, s
// Hosts dispose via 'using'.
}
}

private const string TimestampPrefix = "ts:";
}
40 changes: 20 additions & 20 deletions samples/UiPath.Caching.Sample.ServiceDefaults/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,25 +83,6 @@ public static TBuilder ConfigureOpenTelemetry<TBuilder>(this TBuilder builder) w
return builder;
}

private static TBuilder AddOpenTelemetryExporters<TBuilder>(this TBuilder builder) where TBuilder : IHostApplicationBuilder
{
var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]);

if (useOtlpExporter)
{
builder.Services.AddOpenTelemetry().UseOtlpExporter();
}

// Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package)
//if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"]))
//{
// builder.Services.AddOpenTelemetry()
// .UseAzureMonitor();
//}

return builder;
}

public static TBuilder AddDefaultHealthChecks<TBuilder>(this TBuilder builder) where TBuilder : IHostApplicationBuilder
{
builder.Services.AddHealthChecks()
Expand All @@ -123,10 +104,29 @@ public static WebApplication MapDefaultEndpoints(this WebApplication app)
// Only health checks tagged with the "live" tag must pass for app to be considered alive
app.MapHealthChecks(AlivenessEndpointPath, new HealthCheckOptions
{
Predicate = r => r.Tags.Contains("live")
Predicate = r => r.Tags.Contains("live"),
});
}

return app;
}

private static TBuilder AddOpenTelemetryExporters<TBuilder>(this TBuilder builder) where TBuilder : IHostApplicationBuilder
{
var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]);

if (useOtlpExporter)
{
builder.Services.AddOpenTelemetry().UseOtlpExporter();
}

// Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package)
//if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"]))
//{
// builder.Services.AddOpenTelemetry()
// .UseAzureMonitor();
//}

return builder;
}
}
2 changes: 1 addition & 1 deletion samples/UiPath.Caching.Sample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
builder.Services.AddRequestTimeouts(opt => opt.DefaultPolicy = new Microsoft.AspNetCore.Http.Timeouts.RequestTimeoutPolicy
{
Timeout = TimeSpan.FromMilliseconds(100),
TimeoutStatusCode = 503
TimeoutStatusCode = 503,
});

var app = builder.Build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ namespace UiPath.Caching.Broadcast;

public interface IChangeTokenFactory
{
public ICacheChangeToken Create(string token, ITopic<ICacheEvent> topic, string cacheName, Type entryType);
ICacheChangeToken Create(string token, ITopic<ICacheEvent> topic, string cacheName, Type entryType);
}
4 changes: 2 additions & 2 deletions src/UiPath.Caching.Abstractions/Broadcast/ITopic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ public interface ITopic<T> : IDisposable
{
TopicKey TopicKey { get; }

EventHandler? OnDisposed { get; set; }

IDisposable Subscribe(IObserver<T> observer);

ValueTask<bool> PublishAsync(T @event, CancellationToken token = default);

EventHandler? OnDisposed { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ namespace UiPath.Caching.Broadcast;
[ExcludeFromCodeCoverage]
public class NullCacheChangeToken : ICacheChangeToken
{
public static NullCacheChangeToken Instance { get; } = new NullCacheChangeToken();

private NullCacheChangeToken()
{
}
public static NullCacheChangeToken Instance { get; } = new NullCacheChangeToken();

public bool HasChanged => false;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ public sealed class NullCacheEventFactory : ICacheEventFactory
{
private static ICacheEvent NullEvent = new NullCacheEvent();

public static NullCacheEventFactory Instance { get; } = new NullCacheEventFactory();

private NullCacheEventFactory()
{
}

public static NullCacheEventFactory Instance { get; } = new NullCacheEventFactory();

public ICacheEvent Create(string cacheName, string eventType, CacheEventData eventData, string? id = null) =>
NullEvent;

Expand Down
34 changes: 19 additions & 15 deletions src/UiPath.Caching.Abstractions/Broadcast/TopicKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,22 @@ public TopicKey()
public TopicKey(string? name) =>
Name = name?.Trim().ToLowerInvariant() ?? string.Empty;

public string Name { get; }

public override bool Equals(object? obj) =>
obj is TopicKey topicKey && Equals(topicKey);
public static TopicKey Null { get; } = new TopicKey(null);

public bool Equals(TopicKey other) =>
string.Equals(Name, other.Name, StringComparison.InvariantCultureIgnoreCase);
public string Name { get; }

public bool IsNull => string.IsNullOrEmpty(Name);


public override string ToString() =>
Name;

public override int GetHashCode() =>
HashCode.Combine(Name, IsNull);

public static implicit operator string(TopicKey topicKey) =>
topicKey.Name;

public static implicit operator TopicKey(string? value)
{
if (value == null) return default;
if (value == null)
{
return default;
}

return new TopicKey(value);
}

Expand All @@ -42,5 +35,16 @@ public static implicit operator TopicKey(string? value)
public static bool operator !=(TopicKey left, TopicKey right) =>
!(left == right);

public static TopicKey Null { get; } = new TopicKey(null);
public override bool Equals(object? obj) =>
obj is TopicKey topicKey && Equals(topicKey);

public bool Equals(TopicKey other) =>
string.Equals(Name, other.Name, StringComparison.InvariantCultureIgnoreCase);


public override string ToString() =>
Name;

public override int GetHashCode() =>
HashCode.Combine(Name, IsNull);
}
Loading