Skip to content

Commit

Permalink
preview v3.0.0
Browse files Browse the repository at this point in the history
Fernando Cerqueira committed Nov 28, 2023
1 parent 46e3607 commit 4303958
Showing 41 changed files with 1,278 additions and 848 deletions.
179 changes: 24 additions & 155 deletions NugetREADME.md
Original file line number Diff line number Diff line change
@@ -7,9 +7,21 @@
**[Visit the official page for more documentation of RingBufferPlus](https://fracerqueira.github.io/RingBufferPlus)**

## What's new in the latest version
### V2.0.0

- Release G.A with .NET8
### V3.0.0
[**Top**](#table-of-contents)

- Added command 'FactoryHealth'
- Check health item before accquire buffer.
- Renamed Method 'SwithToScaleDefinitions' to 'MasterScale'
- Added master-slave feature(2 Ring Buffer with synchronization)
- Added command set 'SlaveScale' to set report handler, Minimum and maximum capacity
- Added 'MasterSlave' enum item in SourceTrigger
- Added 'None' enum item in ScaleMode
- Revised to have greater performance without 'lock'
- Removed Method 'Counters'
- data was not relevant and inaccurate
- Revised 'RingBufferMetric'
- Now only propreties 'Trigger', 'FromCapacity', 'ToCapacity' and 'MetricDate'

## Features

@@ -19,22 +31,27 @@ The implementation follows the basic principle. The principle was expanded to ha

### Key Features

- Conscious use of resources
- Set unique name for same buffer type
- Set the buffer capacity
- Set buffer integrity (validate if the buffer is valid)
- Verified with each acquiring
- Set the minimum and maximum capacity (optional)
- Set the conditions for scaling to maximum and minimum (required)
- Automatic condition values ​​based on capacity (value not required)
- Define a user role to receive capacity change events to log/save (optional)
- Executed in a separate thread asynchronously
- Set master-slave (2 Ring Buffer with synchronization)
- Master controls slave scale
- Event with scale change information
- Executed in a separate thread asynchronously
- Associate the logger interface (optional)
- Define a user role for generated errors (optional)
- Executed in a separate thread asynchronously
- Invalidate the buffer when it is in an invalid state
- Command to Invalidate the buffer when it is in an invalid state
- Warm up to full capacity before starting application
- Receive item from buffer with success/failure information and elapsed time for acquisition
- Sets a time limit for acquiring the item in the buffer
- Detailed information about operations when the minimum log is Debug
- Simple and clear fluent syntaxx
- Simple and clear fluent syntax

## Installing

@@ -56,154 +73,6 @@ See folder [**Samples**](https://github.com/FRACerqueira/RingBufferPlus/tree/mai
dotnet run --project [name of sample]
```

## Usage

The **RingBufferPlus** use **fluent interface**; an object-oriented API whose design relies extensively on method chaining. Its goal is to increase code legibility. The term was coined in 2005 by Eric Evans and Martin Fowler.

### Sample-Console Usage (Full features)

```csharp
using var loggerFactory = LoggerFactory.Create(builder =>
{
builder
.SetMinimumLevel(LogLevel.Information)
.AddFilter("Microsoft", LogLevel.Warning)
.AddFilter("System", LogLevel.Warning)
.AddConsole();
});
logger = loggerFactory.CreateLogger<Program>();
```

```csharp
var rb = RingBuffer<int>.New("MyBuffer", cts.Token)
.Capacity(8)
.Logger(logger!)
.OnError((log, error) =>
{
log?.LogError("{error}",error);
})
.Factory((cts) => { return rnd.Next(1, 10); })
.SwithToScaleDefinitions()
.SampleUnit(TimeSpan.FromSeconds(10), 10)
.ReportScale((mode, log, metric, _) =>
{
log.LogInformation($"{connectionRingBuffer!.Name} Report: [{metric.MetricDate}] Trigger {metric.Trigger} : {mode} from {metric.FromCapacity} to {metric.ToCapacity} ({metric.Capacity}/{metric.MinCapacity}/{metric.MaxCapacity}) : {metric.FreeResource}");
})
.MinCapacity(4)
// Defaut = Max (Min = 1, Max = Capacity)
.ScaleWhenFreeGreaterEq()
// Defaut = Min (Min = 1, Max = MinCapacity)
.RollbackWhenFreeLessEq()
// Defaut = Max-1 (Min = 1, Max = MinCapacity)
//.TriggerByAccqWhenFreeLessEq()
.MaxCapacity(20)
// Default = Min (Min = 1, Max = Capacity)
.ScaleWhenFreeLessEq()
// Default = Min (Min = MaxCapacity-Capacity, Max = MaxCapacity)
.RollbackWhenFreeGreaterEq()
// Default = Min (Min = MaxCapacity-Capacity, Max = MaxCapacity)
//.TriggerByAccqWhenFreeGreaterEq()
.BuildWarmup(out var completed);
```

```csharp
using (var buffer = rb.Accquire())
{
if (bufferedItem.Successful)
{
try
{
//do something
}
catch
{
buffer.Invalidate();
}
}
}
```


### Sample-api/webUsage

```csharp
builder.Services.AddRingBuffer<int>("Mybuffer",(ringbuf, _) =>
{
return ringbuf
.Capacity(8)
.Factory((cts) => { return 10; })
.AccquireTimeout(TimeSpan.FromMilliseconds(1500))
.OnError((log, error) =>
{
log?.LogError("{error}",error);
})
.Build();
});

...
//If you do not use the 'Warmup Ring Buffer' command, the first access to acquire the buffer will be Warmup (not recommended)
app.WarmupRingBuffer<int>("Mybuffer");
```

```csharp
[ApiController]
[Route("[controller]")]
public class MyController(IRingBufferService<int> ringBufferService) : ControllerBase
{
private readonly IRingBufferService<int> _ringBufferService = ringBufferService;

[HttpGet]
public ActionResult Get()
{
using (var buffer = _ringBufferService.Accquire())
{
if (bufferedItem.Successful)
{
try
{
//do something
}
catch
{
buffer.Invalidate();
}
}
}
}
}
```

## Performance

The BenchmarkDotNet test was done on the local machine, with **'RabbitMQ' (over wsl)**. The measures are **about publisher** action (Scenario where Ringbuffer makes sense and brings significant performance gains).

**The gain can be much greater for real machines in production!**

See folder [**Samples/RingBufferPlusBenchmarkSample**](https://github.com/FRACerqueira/RingBufferPlus/tree/main/Samples/RingBufferPlusBenchmarkSample).

```
BenchmarkDotNet v0.13.10, Windows 10 (10.0.19044.3693/21H2/November2021Update)
Intel Core i7-8565U CPU 1.80GHz (Whiskey Lake), 1 CPU, 8 logical and 4 physical cores
.NET SDK 8.0.100
[Host] : .NET 8.0.0 (8.0.23.53103), X64 RyuJIT AVX2
Job-IMTEVT : .NET 8.0.0 (8.0.23.53103), X64 RyuJIT AVX2
Dry : .NET 8.0.0 (8.0.23.53103), X64 RyuJIT AVX2
| Method | Mean | StdErr | StdDev | Min | Q1 | Median | Q3 | Max | Op/s | Rank |
|------------------ |------------:|----------:|------------:|------------:|------------:|------------:|------------:|------------:|-------:|-----:|
| WithRingBuffer | 589.8 ms | 16.90 ms | 169.01 ms | 191.2 ms | 487.2 ms | 555.4 ms | 659.0 ms | 1,324.8 ms | 1.6955 | 1 |
| WithoutRingBuffer | 15,441.5 ms | 154.39 ms | 1,543.90 ms | 13,785.4 ms | 14,562.8 ms | 15,071.1 ms | 15,981.9 ms | 24,595.2 ms | 0.0648 | 2 |
```

## Credits

This work was inspired by the project by [**Luis Carlos Farias**](https://github.com/luizcarlosfaria/Oragon.Common.RingBuffer).
My thanks for your great work of bringing knowledge to the community!

**API documentation generated by**

- [xmldoc2md](https://github.com/FRACerqueira/xmldoc2md), Copyright (c) 2022 Charles de Vandière.

## License

Copyright 2022 @ Fernando Cerqueira
110 changes: 75 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
@@ -25,10 +25,22 @@
- [API Reference](https://fracerqueira.github.io/RingBufferPlus/apis/apis.html)

## What's new in the latest version
### V2.0.0

### V3.0.0
[**Top**](#table-of-contents)

- Release G.A with .NET8
- Added command 'FactoryHealth'
- Check health item before accquire buffer.
- Renamed Method 'SwithToScaleDefinitions' to 'MasterScale'
- Added master-slave feature(2 Ring Buffer with synchronization)
- Added command set 'SlaveScale' to set report handler, Minimum and maximum capacity
- Added 'MasterSlave' enum item in SourceTrigger
- Added 'None' enum item in ScaleMode
- Revised to have greater performance without 'lock'
- Removed Method 'Counters'
- data was not relevant and inaccurate
- Revised 'RingBufferMetric'
- Now only propreties 'Trigger', 'FromCapacity', 'ToCapacity' and 'MetricDate'

## Features

@@ -49,17 +61,22 @@ The implementation follows the basic principle. The principle was expanded to ha
### Key Features
[**Top**](#table-of-contents)

- Conscious use of resources
- Set unique name for same buffer type
- Set the buffer capacity
- Set buffer integrity (validate if the buffer is valid)
- Verified with each acquiring
- Set the minimum and maximum capacity (optional)
- Set the conditions for scaling to maximum and minimum (required)
- Automatic condition values ​​based on capacity (value not required)
- Define a user role to receive capacity change events to log/save (optional)
- Executed in a separate thread asynchronously
- Set master-slave (2 Ring Buffer with synchronization)
- Master controls slave scale
- Event with scale change information
- Executed in a separate thread asynchronously
- Associate the logger interface (optional)
- Define a user role for generated errors (optional)
- Executed in a separate thread asynchronously
- Invalidate the buffer when it is in an invalid state
- Command to Invalidate the buffer when it is in an invalid state
- Warm up to full capacity before starting application
- Receive item from buffer with success/failure information and elapsed time for acquisition
- Sets a time limit for acquiring the item in the buffer
@@ -93,7 +110,7 @@ dotnet run --project [name of sample]

The **RingBufferPlus** use **fluent interface**; an object-oriented API whose design relies extensively on method chaining. Its goal is to increase code legibility. The term was coined in 2005 by Eric Evans and Martin Fowler.

### Sample-Console Usage (Full features)
### Sample-Console Usage (Minimal features with auto-scale)

```csharp
using var loggerFactory = LoggerFactory.Create(builder =>
@@ -108,34 +125,18 @@ logger = loggerFactory.CreateLogger<Program>();
```

```csharp
Random rnd = new();
var rb = RingBuffer<int>.New("MyBuffer", cts.Token)
.Capacity(8)
.Logger(logger!)
.OnError((log, error) =>
{
log?.LogError("{error}",error);
})
.Factory((cts) => { return rnd.Next(1, 10); })
.SwithToScaleDefinitions()
.MasterScale()
.SampleUnit(TimeSpan.FromSeconds(10), 10)
.ReportScale((mode, log, metric, _) =>
{
log.LogInformation($"{connectionRingBuffer!.Name} Report: [{metric.MetricDate}] Trigger {metric.Trigger} : {mode} from {metric.FromCapacity} to {metric.ToCapacity} ({metric.Capacity}/{metric.MinCapacity}/{metric.MaxCapacity}) : {metric.FreeResource}");
})
.MinCapacity(4)
// Defaut = Max (Min = 1, Max = Capacity)
.ScaleWhenFreeGreaterEq()
// Defaut = Min (Min = 1, Max = MinCapacity)
.RollbackWhenFreeLessEq()
// Defaut = Max-1 (Min = 1, Max = MinCapacity)
//.TriggerByAccqWhenFreeLessEq()
.MaxCapacity(20)
// Default = Min (Min = 1, Max = Capacity)
.ScaleWhenFreeLessEq()
// Default = Min (Min = MaxCapacity-Capacity, Max = MaxCapacity)
.RollbackWhenFreeGreaterEq()
// Default = Min (Min = MaxCapacity-Capacity, Max = MaxCapacity)
//.TriggerByAccqWhenFreeGreaterEq()
.BuildWarmup(out var completed);
```

@@ -157,7 +158,7 @@ using (var buffer = rb.Accquire())
```


### Sample-api/webUsage
### Sample-api/web Usage (Minimal features without auto-scale)
[**Top**](#table-of-contents)

```csharp
@@ -166,11 +167,6 @@ builder.Services.AddRingBuffer<int>("Mybuffer",(ringbuf, _) =>
return ringbuf
.Capacity(8)
.Factory((cts) => { return 10; })
.AccquireTimeout(TimeSpan.FromMilliseconds(1500))
.OnError((log, error) =>
{
log?.LogError("{error}",error);
})
.Build();
});

@@ -207,6 +203,47 @@ public class MyController(IRingBufferService<int> ringBufferService) : Controlle
}
```

### Sample-Console Master-Slave feature using RabbitMq (basic usage)
[**Top**](#table-of-contents)

For more details see [**Complete-Samples**](https://github.com/FRACerqueira/RingBufferPlus/tree/main/samples/RingBufferPlusBenchmarkSample).

```csharp
connectionRingBuffer = RingBuffer<IConnection>.New("RabbitCnn")
.Capacity(2)
.Logger(applogger!)
.AccquireTimeout(TimeSpan.FromMilliseconds(500))
.OnError((log, error) =>
{
log?.LogError("{error}", error);
})
.Factory((cts) => ConnectionFactory.CreateConnection())
.FactoryHealth((item) => item.IsOpen)
.SlaveScale()
.MaxCapacity(10)
.MinCapacity(1)
.BuildWarmup(out completedCnn);

modelRingBuffer = RingBuffer<IModel>.New("RabbitChanels")
.Capacity(10)
.Logger(applogger!)
.OnError((log, error) =>
{
log?.LogError("{error}", error);
})
.Factory((cts) => ModelFactory(cts))
.FactoryHealth((item) => item.IsOpen)
.MasterScale(connectionRingBuffer)
.SampleUnit(TimeSpan.FromSeconds(10), 10)
.MaxCapacity(50)
.ScaleWhenFreeLessEq()
.RollbackWhenFreeGreaterEq()
.MinCapacity(2)
.ScaleWhenFreeGreaterEq()
.RollbackWhenFreeLessEq()
.BuildWarmup(out completedChanels);
```

## Performance
[**Top**](#table-of-contents)

@@ -223,11 +260,14 @@ Intel Core i7-8565U CPU 1.80GHz (Whiskey Lake), 1 CPU, 8 logical and 4 physical
[Host] : .NET 8.0.0 (8.0.23.53103), X64 RyuJIT AVX2
Job-IMTEVT : .NET 8.0.0 (8.0.23.53103), X64 RyuJIT AVX2
Dry : .NET 8.0.0 (8.0.23.53103), X64 RyuJIT AVX2
| Method | Mean | StdErr | StdDev | Min | Q1 | Median | Q3 | Max | Op/s | Rank |
|------------------ |------------:|----------:|------------:|------------:|------------:|------------:|------------:|------------:|-------:|-----:|
| WithRingBuffer | 589.8 ms | 16.90 ms | 169.01 ms | 191.2 ms | 487.2 ms | 555.4 ms | 659.0 ms | 1,324.8 ms | 1.6955 | 1 |
| WithoutRingBuffer | 15,441.5 ms | 154.39 ms | 1,543.90 ms | 13,785.4 ms | 14,562.8 ms | 15,071.1 ms | 15,981.9 ms | 24,595.2 ms | 0.0648 | 2 |
+------------------ +-------:+-----:+------------:+----------:+------------:+-------------:+------------:+------------:+------------:+------------+|
| Method | Op/s | Rank | Mean | StdErr | StdDev | Min | Q1 | Median | Q3 | Max |
|------------------ |-------:|-----:|------------:|----------:|------------:|-------------:|------------:|------------:|------------:|------------:|
| WithRingBuffer | 6.8218 | 1 | 146.6 ms | 0.00 ms | 0.00 ms | 146.59 ms | 146.6 ms | 146.6 ms | 146.6 ms | 146.6 ms |
| WithRingBuffer | 1.9411 | 2 | 515.2 ms | 101.72 ms | 1,017.24 ms | 72.76 ms | 300.1 ms | 439.2 ms | 508.2 ms | 10,426.5 ms |
| WithoutRingBuffer | 0.0676 | 3 | 14,797.6 ms | 133.34 ms | 1,333.36 ms | 13,306.95 ms | 14,061.5 ms | 14,497.3 ms | 15,098.1 ms | 21,286.4 ms |
| WithoutRingBuffer | 0.0662 | 4 | 15,109.9 ms | 0.00 ms | 0.00 ms | 15,109.93 ms | 15,109.9 ms | 15,109.9 ms | 15,109.9 ms |115,109.9 ms |
+------------------ +-------:+-----:+------------:+----------:+------------:+-------------:+------------:+------------:+------------:+------------+|
```

## Code of Conduct
Loading

0 comments on commit 4303958

Please sign in to comment.