Skip to content

Commit 5301252

Browse files
jodydonettiJoeShookJoseph Shook
authored
Feature: Events (ZiggyCreatures#22)
* Added the events system * Added events tests * Docs updated Co-authored-by: Joe Shook <joeshook@gmail.com> Co-authored-by: Joseph Shook <Joseph.Shook@surescripts.com>
1 parent 25ae781 commit 5301252

51 files changed

Lines changed: 2007 additions & 298 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ These are the **key features** of FusionCache:
4343
- **:dark_sunglasses: Background factory completion**: when you specify a factory timeout and it actually occurs, the timed-out factory can keep running in the background and, if and when it successfully complete, the cache will be immediately updated with the new value to be used right away ([read more](docs/Timeouts.md))
4444
- **:zap: High performance**: FusionCache is optimized to minimize CPU usage and memory allocations to get better performance and lower the cost of your infrastructure all while obtaining a more stable, error resilient application
4545
- **:dizzy: Natively sync/async**: full native support for both the synchronous and asynchronous programming model, with sync/async methods working togheter harmoniously ([read more](docs/CoreMethods.md))
46+
- **:telephone_receiver: Events**: there's a comprehensive set of events to subscribe to regarding core events inside of a FusioCache instance, both at a high level and at lower levels (memory/distributed layers) ([read more](docs/Events.md))
4647
- **:page_with_curl: Extensive logging**: comprehensive, structured, detailed and customizable logging via the standard `ILogger<T>` interface (you can use Serilog, NLog, etc)
4748

4849
Also, FusionCache has some other nice **additional features**:
@@ -59,11 +60,11 @@ Also, FusionCache has some other nice **additional features**:
5960

6061
Available packages on Nuget:
6162

62-
| Package Name | Release (NuGet) |
63-
|--------------------------------|-----------------|
64-
| `ZiggyCreatures.FusionCache` | [![NuGet](https://img.shields.io/nuget/v/ZiggyCreatures.FusionCache.svg)](https://www.nuget.org/packages/ZiggyCreatures.FusionCache/) |
65-
| `ZiggyCreatures.FusionCache.Serialization.NewtonsoftJson` | [![NuGet](https://img.shields.io/nuget/v/ZiggyCreatures.FusionCache.Serialization.NewtonsoftJson.svg)](https://www.nuget.org/packages/ZiggyCreatures.FusionCache.Serialization.NewtonsoftJson/) |
66-
| `ZiggyCreatures.FusionCache.Serialization.SystemTextJson` | [![NuGet](https://img.shields.io/nuget/v/ZiggyCreatures.FusionCache.Serialization.SystemTextJson.svg)](https://www.nuget.org/packages/ZiggyCreatures.FusionCache.Serialization.SystemTextJson/) |
63+
| Package Name | Release (NuGet) | Downloads |
64+
|--------------------------------|:---------------:|:---------:|
65+
| `ZiggyCreatures.FusionCache` | [![NuGet](https://img.shields.io/nuget/v/ZiggyCreatures.FusionCache.svg)](https://www.nuget.org/packages/ZiggyCreatures.FusionCache/) | ![Nuget](https://img.shields.io/nuget/dt/ZiggyCreatures.FusionCache) |
66+
| `ZiggyCreatures.FusionCache.Serialization.NewtonsoftJson` | [![NuGet](https://img.shields.io/nuget/v/ZiggyCreatures.FusionCache.Serialization.NewtonsoftJson.svg)](https://www.nuget.org/packages/ZiggyCreatures.FusionCache.Serialization.NewtonsoftJson/) | ![Nuget](https://img.shields.io/nuget/dt/ZiggyCreatures.FusionCache.Serialization.NewtonsoftJson) |
67+
| `ZiggyCreatures.FusionCache.Serialization.SystemTextJson` | [![NuGet](https://img.shields.io/nuget/v/ZiggyCreatures.FusionCache.Serialization.SystemTextJson.svg)](https://www.nuget.org/packages/ZiggyCreatures.FusionCache.Serialization.SystemTextJson/) | ![Nuget](https://img.shields.io/nuget/dt/ZiggyCreatures.FusionCache.Serialization.SystemTextJson) |
6768

6869
## :star: Quick Start
6970

@@ -199,7 +200,10 @@ The documentation is available in the :open_file_folder: [docs](docs/README.md)
199200
- [**:stopwatch: Timeouts**](docs/Timeouts.md): the various types of timeouts at your disposal (calling a factory, using the distributed cache, etc)
200201
- [**:level_slider: Options**](docs/Options.md): everything about the available options, both cache-wide and per-call
201202
- [**:joystick: Core Methods**](docs/CoreMethods.md): what you need to know about the core methods available
202-
- [**:trophy: Step By Step**](docs/StepByStep.md): a complete step by step example of why a cache is useful, why FusionCache could be even more so, how to apply most of the options available and what results you can expect to obtain
203+
- [**:telephone_receiver: Events**](docs/Events.md): the events hub and how to use it
204+
205+
## **:trophy: Step By Step**
206+
If you are in for a ride you can read a complete [step by step example](docs/StepByStep.md) of why a cache is useful, why FusionCache could be even more so, how to apply most of the options available and what **results** you can expect to obtain.
203207

204208
## :ab: Comparison
205209

benchmarks/ZiggyCreatures.FusionCache.Benchmarks/AsyncComparisonBenchmark.cs

Lines changed: 56 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -79,16 +79,16 @@ public async Task FusionCache()
7979
{
8080
Parallel.For(0, Accessors, _ =>
8181
{
82-
var t = cache.GetOrSetAsync<SamplePayload>(
83-
key,
84-
async ct =>
85-
{
86-
await Task.Delay(FactoryDurationMs).ConfigureAwait(false);
87-
return new SamplePayload();
88-
}
89-
);
90-
tasks.Add(t);
91-
});
82+
var t = cache.GetOrSetAsync<SamplePayload>(
83+
key,
84+
async ct =>
85+
{
86+
await Task.Delay(FactoryDurationMs).ConfigureAwait(false);
87+
return new SamplePayload();
88+
}
89+
);
90+
tasks.Add(t);
91+
});
9292
});
9393

9494
await Task.WhenAll(tasks).ConfigureAwait(false);
@@ -109,20 +109,20 @@ public async Task CacheManager()
109109
{
110110
Parallel.For(0, Accessors, _ =>
111111
{
112-
cache.GetOrAdd(
113-
key,
114-
_ =>
115-
{
116-
Thread.Sleep(FactoryDurationMs);
117-
return new CacheItem<SamplePayload>(
118-
key,
119-
new SamplePayload(),
120-
global::CacheManager.Core.ExpirationMode.Absolute,
121-
CacheDuration
122-
);
123-
}
124-
);
125-
});
112+
cache.GetOrAdd(
113+
key,
114+
_ =>
115+
{
116+
Thread.Sleep(FactoryDurationMs);
117+
return new CacheItem<SamplePayload>(
118+
key,
119+
new SamplePayload(),
120+
global::CacheManager.Core.ExpirationMode.Absolute,
121+
CacheDuration
122+
);
123+
}
124+
);
125+
});
126126
});
127127
}
128128

@@ -146,17 +146,17 @@ public async Task CacheTower()
146146
{
147147
Parallel.For(0, Accessors, _ =>
148148
{
149-
var t = cache.GetOrSetAsync<SamplePayload>(
150-
key,
151-
async (old) =>
152-
{
153-
await Task.Delay(FactoryDurationMs).ConfigureAwait(false);
154-
return new SamplePayload();
155-
},
156-
cacheSettings
157-
).AsTask();
158-
tasks.Add(t);
159-
});
149+
var t = cache.GetOrSetAsync<SamplePayload>(
150+
key,
151+
async (old) =>
152+
{
153+
await Task.Delay(FactoryDurationMs).ConfigureAwait(false);
154+
return new SamplePayload();
155+
},
156+
cacheSettings
157+
).AsTask();
158+
tasks.Add(t);
159+
});
160160
});
161161

162162
await Task.WhenAll(tasks).ConfigureAwait(false);
@@ -182,17 +182,17 @@ public async Task EasyCaching()
182182
{
183183
Parallel.For(0, Accessors, _ =>
184184
{
185-
var t = cache.GetAsync<SamplePayload>(
186-
key,
187-
async () =>
188-
{
189-
await Task.Delay(FactoryDurationMs).ConfigureAwait(false);
190-
return new SamplePayload();
191-
},
192-
CacheDuration
193-
);
194-
tasks.Add(t);
195-
});
185+
var t = cache.GetAsync<SamplePayload>(
186+
key,
187+
async () =>
188+
{
189+
await Task.Delay(FactoryDurationMs).ConfigureAwait(false);
190+
return new SamplePayload();
191+
},
192+
CacheDuration
193+
);
194+
tasks.Add(t);
195+
});
196196
});
197197

198198
await Task.WhenAll(tasks).ConfigureAwait(false);
@@ -219,16 +219,16 @@ public async Task LazyCache()
219219
{
220220
Parallel.For(0, Accessors, _ =>
221221
{
222-
var t = appcache.GetOrAddAsync<SamplePayload>(
223-
key,
224-
async () =>
225-
{
226-
await Task.Delay(FactoryDurationMs).ConfigureAwait(false);
227-
return new SamplePayload();
228-
}
229-
);
230-
tasks.Add(t);
231-
});
222+
var t = appcache.GetOrAddAsync<SamplePayload>(
223+
key,
224+
async () =>
225+
{
226+
await Task.Delay(FactoryDurationMs).ConfigureAwait(false);
227+
return new SamplePayload();
228+
}
229+
);
230+
tasks.Add(t);
231+
});
232232
});
233233

234234
await Task.WhenAll(tasks).ConfigureAwait(false);

benchmarks/ZiggyCreatures.FusionCache.Benchmarks/SyncComparisonBenchmark.cs

Lines changed: 43 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -74,15 +74,15 @@ public void FusionCache()
7474
{
7575
Parallel.For(0, Accessors, _ =>
7676
{
77-
cache.GetOrSet<SamplePayload>(
78-
key,
79-
ct =>
80-
{
81-
Thread.Sleep(FactoryDurationMs);
82-
return new SamplePayload();
83-
}
84-
);
85-
});
77+
cache.GetOrSet<SamplePayload>(
78+
key,
79+
ct =>
80+
{
81+
Thread.Sleep(FactoryDurationMs);
82+
return new SamplePayload();
83+
}
84+
);
85+
});
8686
});
8787
}
8888

@@ -101,21 +101,21 @@ public void CacheManager()
101101
{
102102
Parallel.For(0, Accessors, _ =>
103103
{
104-
cache.GetOrAdd(
105-
key,
106-
_ =>
107-
{
108-
Thread.Sleep(FactoryDurationMs);
109-
return new CacheItem<SamplePayload>(
110-
key,
111-
new SamplePayload(),
112-
global::CacheManager.Core.ExpirationMode.Absolute,
113-
CacheDuration
114-
);
115-
}
116-
);
117-
118-
});
104+
cache.GetOrAdd(
105+
key,
106+
_ =>
107+
{
108+
Thread.Sleep(FactoryDurationMs);
109+
return new CacheItem<SamplePayload>(
110+
key,
111+
new SamplePayload(),
112+
global::CacheManager.Core.ExpirationMode.Absolute,
113+
CacheDuration
114+
);
115+
}
116+
);
117+
118+
});
119119
});
120120
}
121121

@@ -138,16 +138,16 @@ public void EasyCaching()
138138
{
139139
Parallel.For(0, Accessors, _ =>
140140
{
141-
cache.Get<SamplePayload>(
142-
key,
143-
() =>
144-
{
145-
Thread.Sleep(FactoryDurationMs);
146-
return new SamplePayload();
147-
},
148-
CacheDuration
149-
);
150-
});
141+
cache.Get<SamplePayload>(
142+
key,
143+
() =>
144+
{
145+
Thread.Sleep(FactoryDurationMs);
146+
return new SamplePayload();
147+
},
148+
CacheDuration
149+
);
150+
});
151151
});
152152
}
153153

@@ -172,15 +172,15 @@ public void LazyCache()
172172
{
173173
Parallel.For(0, Accessors, _ =>
174174
{
175-
appcache.GetOrAdd<SamplePayload>(
176-
key,
177-
() =>
178-
{
179-
Thread.Sleep(FactoryDurationMs);
180-
return new SamplePayload();
181-
}
182-
);
183-
});
175+
appcache.GetOrAdd<SamplePayload>(
176+
key,
177+
() =>
178+
{
179+
Thread.Sleep(FactoryDurationMs);
180+
return new SamplePayload();
181+
}
182+
);
183+
});
184184
});
185185
}
186186

docs/AGentleIntroduction.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,12 @@ Everything is natively available for both the **sync** and **async** programming
117117

118118
Any operation works seamlessly with any other, even if one is **sync** and the other is **async**: an example is multiple concurrent factory calls for the same cache key, some of them **sync** while others **async**, all coordinated togheter at the same time with no problems and a guarantee that only one will be executed at the same time.
119119

120+
## :telephone_receiver: Events
121+
122+
There's a comprehensive set of events to subscribe to regarding core events inside of a FusioCache instance, both at a high level and at lower levels (memory/distributed layers).
123+
124+
You can read more [**here**](Events.md).
125+
120126
## :page_with_curl: Logging
121127
FusionCache can log extensively to help you pinpoint any possible problem in your production environment.
122128

docs/Comparison.md

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,17 @@ The general features I've identified as significants are:
4545

4646
- **Multi-level**: the ability to handle more than one caching level, transparently. This can give you - at the same time - the benefits of a local in-memory cache (high performance + data locality) and the benefits of a distributed cache (sharing of cached data + better cold start) without having to handle them separately
4747

48-
- **Distributed synchronization**: when a change occurs in a distributed cache, local memory caches are notified of that so they can react
48+
- **Backplane**: available with different names, it allows a change in a distributed cache to be reflected in the local memory cache
49+
50+
- **Events**: the ability to be notified when certain events happen in he cache, useful to collect custom metrics, etc
4951

5052
- **Logging**: when things go bad you would like to have some help investigating what went wrong, and logging is key
5153

5254
- **Portable**: the ability to run on both the older **.NET Framework** (full fx) and the new **.NET Core**. As time goes by .NET Core (from v5 now simply **.NET**) is the platform to be on, but it's a nice plus to be able to run on the older one as well
5355

5456
- **Tests**: having a suite of tests covering most of the library can greatly reduce the probabilty of bugs or regressions so, in theory, you can count on a more solid and stable library
5557

56-
- **Xml comments**: having informations always available at your fingertips [while you type](https://docs.microsoft.com/en-us/dotnet/csharp/codedoc) (Intellisense :tm: or similar) is fundamental for learning as you code and to avoid common pitfalls
58+
- **Xml Comments**: having informations always available at your fingertips [while you type](https://docs.microsoft.com/en-us/dotnet/csharp/codedoc) (Intellisense :tm: or similar) is fundamental for learning as you code and to avoid common pitfalls
5759

5860
- **Documentation**: an expanded documentation, a getting started guide or maybe some samples can greatly improve your learning
5961

@@ -64,22 +66,23 @@ This is how they compare:
6466
| | FusionCache (1) | CacheManager | CacheTower (2) | EasyCaching (3) | LazyCache (4) |
6567
| ---: | :---: | :---: | :---: | :---: |:---: |
6668
| **Factory call opt.** ||||||
67-
| **Sync api** ||||||
68-
| **Async api** ||||||
69+
| **Sync Api** ||||||
70+
| **Async Api** ||||||
6971
| **Stale data re-use** ||||||
7072
| **Multi-provider** ||||||
7173
| **Multi-level** ||||||
72-
| **Distributed sync.** ||||||
74+
| **Backplane** ||||||
75+
| **Events** ||||||
7376
| **Logging** ||||||
7477
| **Portable** ||||||
7578
| **Tests** ||||||
76-
| **Xml comments** ||||||
79+
| **Xml Comments** ||||||
7780
| **Documentation** ||||||
7881
| **License** | `MIT` | `Apache 2.0` | `MIT` | `MIT` | `MIT` |
7982

8083
:information_source: **NOTES**
81-
- (1): **FusionCache** support for distributed synchronization is being designed and will be available soon. Also, simply setting a lower cache duration and enabling [fail-safe](FailSafe.md) + [soft timeouts](Timeouts.md), in most cases would give you similar results
82-
- (2): **CacheTower** does not support stale data-reuse, but offers a way for cache data to be marked as stale before it expires as a way to kick off background refresh ahead of time. It may be what you want or not, but it's good to know
83-
- (3): **EasyCaching** supports an `HybridCachingProvider` to handle 2 layers transparently, but it's implemented in a way that checks the distributed cache before the in-memory one, kind of invalidating the benefits of the latter, which is important to know
84-
- (4): **LazyCache** does have both sync and async support, but not for all the available methods (eg. `Remove`). This may be perfectly fine for you or not, but it's good to know
84+
- (1): **FusionCache** support for a backplane is being designed and will be available soon.
85+
- (2): **CacheTower** does not support stale data-reuse, but offers a way for cache data to be marked as stale before it expires as a way to kick off background refresh ahead of time. It may be what you want or not, but it's good to know.
86+
- (3): **EasyCaching** supports an `HybridCachingProvider` to handle 2 layers transparently, but it's implemented in a way that checks the distributed cache before the in-memory one, kind of invalidating the benefits of the latter, which is important to know.
87+
- (4): **LazyCache** does have both sync and async support, but not for all the available methods (eg. `Remove`). This may be perfectly fine for you or not, but it's good to know.
8588

docs/CoreMethods.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ All of them work **on both the memory cache and the distributed cache** (if any)
1818

1919
All of them are available in both a **sync** and an **async** version.
2020

21-
Finally, most of them have a set of overloads for a better ease of use.
21+
Finally, most of them have a set of **overloads** for a better ease of use.
2222

23-
If you are thinking **"which one should I use 🤔 ?"** please keep reading.
23+
If you are thinking *"which one should I use?"* please keep reading.
2424

2525

2626
## Set[Async]

0 commit comments

Comments
 (0)