Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v0.9.0 - 📢 Backplane #42

Merged
merged 77 commits into from
Feb 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
0be10f8
Better local cache eviction with fail-safe support
jodydonetti Jan 21, 2022
5cef9fe
Minor
jodydonetti Jan 21, 2022
a4adc7a
Minor
jodydonetti Jan 21, 2022
85e299d
Editorconfig
jodydonetti Jan 22, 2022
f38fffc
Created SimpleCircuitBreaker
jodydonetti Jan 22, 2022
070702a
Made CacheKeyPrefix obsolete+error
jodydonetti Jan 23, 2022
750d92d
Initial backplane impl
jodydonetti Jan 23, 2022
7f28bbe
Minor
jodydonetti Jan 23, 2022
f7d2392
Minor
jodydonetti Jan 23, 2022
7bafbff
Minor
jodydonetti Jan 23, 2022
438767d
Better backplane impl (via backplane accessor) + more
jodydonetti Jan 27, 2022
0362b04
Docs (minor)
jodydonetti Jan 27, 2022
da24a69
Docs
jodydonetti Jan 27, 2022
0306e72
Minor perf tweak
jodydonetti Jan 27, 2022
80fa7ab
Docs
jodydonetti Jan 27, 2022
d7ea296
Nuget release stuff
jodydonetti Jan 27, 2022
b1a842d
Docs
jodydonetti Jan 27, 2022
5c0f475
Minor cleanup
jodydonetti Jan 28, 2022
be3e8c8
Added overloads calls tryouts
jodydonetti Jan 29, 2022
1b07c26
Events refactoring
jodydonetti Jan 30, 2022
eb65aee
Obsolete stuff cleanup
jodydonetti Jan 30, 2022
78d6a7b
Backplane message processing refactoring
jodydonetti Jan 30, 2022
dc649c9
Added FusionCacheOptions.BackplaneErrorsLogLevel
jodydonetti Jan 30, 2022
628b8ab
Better backplane events
jodydonetti Jan 30, 2022
3176b94
Changed a log level from warning to trace for when no fail-safe activ…
jodydonetti Jan 30, 2022
c833e22
v0.1.10-alpha2
jodydonetti Jan 30, 2022
a7fbc98
Better FCEO log string
jodydonetti Feb 3, 2022
f85dd97
Better log message when no DistributedCacheEntryOptions
jodydonetti Feb 3, 2022
776efb7
Change memory event for Evict
jodydonetti Feb 5, 2022
e35638f
Minor cleanup
jodydonetti Feb 5, 2022
f74b28b
More granular backplane messages
jodydonetti Feb 5, 2022
11eb8c7
Added BackplaneMessage.InstantTicks + better tests
jodydonetti Feb 6, 2022
8304e5c
Backplane refactoring: SendNotifications -> Publish (shorter, better)
jodydonetti Feb 8, 2022
fc3f741
Added IFusionCache.HasBackplane prop
jodydonetti Feb 8, 2022
acd9a07
Cleanup
jodydonetti Feb 8, 2022
af26fc7
Cleanup
jodydonetti Feb 8, 2022
0bec932
More backplane refactoring
jodydonetti Feb 8, 2022
25fa922
Cleanup
jodydonetti Feb 8, 2022
4103919
Added BackplaneMessageAction.Unknown
jodydonetti Feb 8, 2022
787e2f0
Better BackplaneMessage validation check
jodydonetti Feb 8, 2022
0916dc8
Playground: switched to a default memory dist. cache/backplane
jodydonetti Feb 8, 2022
0fe1be9
Changed some accessibility modifiers
jodydonetti Feb 8, 2022
dd5fd2b
Finally a decent backplane message conversion for Redis
jodydonetti Feb 8, 2022
b4accf0
Docs
jodydonetti Feb 8, 2022
45a93be
v0.1.10-beta1
jodydonetti Feb 9, 2022
c4f7dcf
Better backplane subscription handling
jodydonetti Feb 10, 2022
7d457c0
Minor
jodydonetti Feb 10, 2022
6631133
Minor
jodydonetti Feb 10, 2022
19813ac
Oh dear if I'm dumb as a rock: I forgot to add the code for the backp…
jodydonetti Feb 10, 2022
4c25fb2
Docs
jodydonetti Feb 10, 2022
1fbf644
Deps bump
jodydonetti Feb 11, 2022
9386679
v0.1.10-beta2
jodydonetti Feb 11, 2022
2c30561
Docs (typos and stuff)
jodydonetti Feb 13, 2022
3af29f9
Made the Evict method NOT public anymore and added a bool param to su…
jodydonetti Feb 13, 2022
0cdf27f
Better sealed keyword usage
jodydonetti Feb 13, 2022
2527c33
More readonly keyword usage
jodydonetti Feb 13, 2022
52d4da1
Minor
jodydonetti Feb 13, 2022
d43a483
Better obsolescence of Evict IFusionCache.Evict method
jodydonetti Feb 13, 2022
d0fea2c
Better obsolescence of Evict IFusionCache.Evict method: reprise
jodydonetti Feb 13, 2022
f34cb46
Better backplane message handling
jodydonetti Feb 13, 2022
e3c2822
Hidden IFusionCache.Publish[Async] method
jodydonetti Feb 13, 2022
0140fdc
Minor
jodydonetti Feb 13, 2022
74f446f
Backplane docs
jodydonetti Feb 13, 2022
ef8f552
Docs
jodydonetti Feb 13, 2022
9edc3d7
Docs
jodydonetti Feb 13, 2022
88b8901
Docs
jodydonetti Feb 14, 2022
45ce7d2
v0.1.10-beta3
jodydonetti Feb 14, 2022
507467e
Docs
jodydonetti Feb 14, 2022
629cec2
Remove the "-Beta2" suffix from the backplane channel name, in prepar…
jodydonetti Feb 17, 2022
0b88189
Better obsolescence (error flag + EditorBrowsable)
jodydonetti Feb 17, 2022
20e2f68
Minor
jodydonetti Feb 17, 2022
620b70c
Docs
jodydonetti Feb 17, 2022
ff1e3ef
Docs
jodydonetti Feb 17, 2022
141b6b3
v0.9.0
jodydonetti Feb 17, 2022
38a1642
Minor
jodydonetti Feb 17, 2022
0a4a3eb
Docs
jodydonetti Feb 17, 2022
d3c504b
Docs
jodydonetti Feb 17, 2022
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
2 changes: 2 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ csharp_space_between_method_call_empty_parameter_list_parentheses = false
# Wrapping preferences
csharp_preserve_single_line_statements = true
csharp_preserve_single_line_blocks = true
# CS1998: Async method lacks 'await' operators and will run synchronously
dotnet_diagnostic.CS1998.severity = suggestion
###############################
# VB Coding Conventions #
###############################
Expand Down
2 changes: 1 addition & 1 deletion LICENSE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
### MIT License

Copyright (c) 2020-2021 Jody Donetti
Copyright (c) 2020-2022 Jody Donetti

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
80 changes: 55 additions & 25 deletions README.md

Large diffs are not rendered by default.

28 changes: 21 additions & 7 deletions ZiggyCreatures.FusionCache.sln
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,17 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ZiggyCreatures.FusionCache.
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ZiggyCreatures.FusionCache.Tests", "tests\ZiggyCreatures.FusionCache.Tests\ZiggyCreatures.FusionCache.Tests.csproj", "{25C0D76C-E7D2-43CC-8D0E-BECB0438527B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ZiggyCreatures.FusionCache.LoggingVisualTester", "tests\ZiggyCreatures.FusionCache.LoggingVisualTester\ZiggyCreatures.FusionCache.LoggingVisualTester.csproj", "{F29AD55C-39F3-47B3-80AE-04130DB5F1ED}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{BDC18C05-F0DF-4B40-90D8-4BE559EA0C23}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ZiggyCreatures.FusionCache.Playground", "tests\ZiggyCreatures.FusionCache.Playground\ZiggyCreatures.FusionCache.Playground.csproj", "{7063CEBE-CE42-4E4D-8EE6-EB719CEF2F2F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZiggyCreatures.FusionCache.Backplane.Memory", "src\ZiggyCreatures.FusionCache.Backplane.Memory\ZiggyCreatures.FusionCache.Backplane.Memory.csproj", "{AEBFD35A-48BF-45ED-8AB4-F9DB1882D12D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ZiggyCreatures.FusionCache.Backplane.StackExchangeRedis", "src\ZiggyCreatures.FusionCache.Backplane.StackExchangeRedis\ZiggyCreatures.FusionCache.Backplane.StackExchangeRedis.csproj", "{5659A831-8170-4398-A130-D521844BB4C0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -52,10 +56,18 @@ Global
{25C0D76C-E7D2-43CC-8D0E-BECB0438527B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{25C0D76C-E7D2-43CC-8D0E-BECB0438527B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{25C0D76C-E7D2-43CC-8D0E-BECB0438527B}.Release|Any CPU.Build.0 = Release|Any CPU
{F29AD55C-39F3-47B3-80AE-04130DB5F1ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F29AD55C-39F3-47B3-80AE-04130DB5F1ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F29AD55C-39F3-47B3-80AE-04130DB5F1ED}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F29AD55C-39F3-47B3-80AE-04130DB5F1ED}.Release|Any CPU.Build.0 = Release|Any CPU
{7063CEBE-CE42-4E4D-8EE6-EB719CEF2F2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7063CEBE-CE42-4E4D-8EE6-EB719CEF2F2F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7063CEBE-CE42-4E4D-8EE6-EB719CEF2F2F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7063CEBE-CE42-4E4D-8EE6-EB719CEF2F2F}.Release|Any CPU.Build.0 = Release|Any CPU
{AEBFD35A-48BF-45ED-8AB4-F9DB1882D12D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AEBFD35A-48BF-45ED-8AB4-F9DB1882D12D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AEBFD35A-48BF-45ED-8AB4-F9DB1882D12D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AEBFD35A-48BF-45ED-8AB4-F9DB1882D12D}.Release|Any CPU.Build.0 = Release|Any CPU
{5659A831-8170-4398-A130-D521844BB4C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5659A831-8170-4398-A130-D521844BB4C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5659A831-8170-4398-A130-D521844BB4C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5659A831-8170-4398-A130-D521844BB4C0}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -66,7 +78,9 @@ Global
{ED5B2080-C706-4150-897D-59482F41BC34} = {34B53F49-F5C5-4850-B79E-59AD130379C6}
{EA8020A5-B936-415A-8A1B-0DC8CA7914B1} = {407E264A-43D3-4139-BF62-D505EECA36F8}
{25C0D76C-E7D2-43CC-8D0E-BECB0438527B} = {C6F3C570-C68C-4A95-960E-82778306BDBA}
{F29AD55C-39F3-47B3-80AE-04130DB5F1ED} = {C6F3C570-C68C-4A95-960E-82778306BDBA}
{7063CEBE-CE42-4E4D-8EE6-EB719CEF2F2F} = {C6F3C570-C68C-4A95-960E-82778306BDBA}
{AEBFD35A-48BF-45ED-8AB4-F9DB1882D12D} = {34B53F49-F5C5-4850-B79E-59AD130379C6}
{5659A831-8170-4398-A130-D521844BB4C0} = {34B53F49-F5C5-4850-B79E-59AD130379C6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {92916FA2-FCAC-406E-BF3F-0A2CE9512EF0}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
<PackageReference Include="CacheManager.Microsoft.Extensions.Caching.Memory" Version="1.2.0" />
<PackageReference Include="CacheTower" Version="0.11.3" />
<PackageReference Include="CacheTower" Version="0.11.6" />
<PackageReference Include="EasyCaching.InMemory" Version="1.2.0" />
<PackageReference Include="LazyCache" Version="2.4.0" />
</ItemGroup>
Expand Down
80 changes: 53 additions & 27 deletions docs/AGentleIntroduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@

</div>

# :unicorn: A Gentle Introduction

FusionCache is an easy to use, high performance and robust **multi-level cache** with some advanced features.
# 🦄 A Gentle Introduction

It uses a memory cache (any impl of the standard `IMemoryCache` interface) as the **primary** backing store and optionally a distributed, 2nd level cache (any impl of the standard `IDistributedCache` interface) as a **secondary** backing store for better resilience and higher performance, for example in a multi-node scenario or to avoid the typical effects of a cold start (initial empty cache, maybe after a restart).
FusionCache is an easy to use, high performance and robust cache with an optional distributed 2nd layer and some advanced features.

FusionCache includes some advanced features like a **fail-safe** mechanism, **cache stampede** prevention, fine grained **soft/hard timeouts** with **background factory completion**, customizable **extensive logging** and more (see below).
It uses a memory cache (any impl of the standard `IMemoryCache` interface) as the **primary** backing store and, optionally, a distributed cache (any impl of the standard `IDistributedCache` interface) as a **secondary** backing store for better resilience and higher performance, for example in a multi-node scenario or to avoid the typical effects of a cold start (initial empty cache, maybe after a restart).

Optionally, it can also use a **backplane**: in a multi-node scenario this will send notifications to the other nodes to keep each node's memory cache perfectly synchronized, without any additional work.

FusionCache also includes some advanced features like a **fail-safe** mechanism, **cache stampede** prevention, fine grained **soft/hard timeouts** with **background factory completion**, customizable **extensive logging** and more (see below).


<div style="text-align:center;">
Expand All @@ -19,7 +22,22 @@ FusionCache includes some advanced features like a **fail-safe** mechanism, **ca

</div>

## :twisted_rightwards_arrows: Cache Levels ([more](CacheLevels.md))

## 🏡 Feels Like Home

FusionCache tries to feel like a native part of .NET by adhering to the naming conventions of the standard **memory** and **distributed** cache components:

| | Memory Cache | Distributed Cache | Fusion Cache |
| ---: | :---: | :---: | :---: |
| **Cache Interface** | `IMemoryCache` | `IDistributedCache` | `IFusionCache` |
| **Cache Implementation** | `MemoryCache` | `[Various]Cache` | `FusionCache` |
| **Cache Options** | `MemoryCacheOptions` | `[Various]CacheOptions` | `FusionCacheOptions` |
| **Cache Entry Options** | `MemoryCacheEntryOptions` | `DistributedCacheEntryOptions` | `FusionCacheEntryOptions` |

If you've ever used one of those you'll feel at home with FusionCache.


## 🔀 Cache Levels ([more](CacheLevels.md))

There are 2 caching levels, transparently handled by FusionCache for you.

Expand All @@ -30,22 +48,22 @@ These are:
Everything is handled transparently for you.

You can read more [**here**](CacheLevels.md), or enjoy the complete [**step by step**](StepByStep.md) guide.
## :house_with_garden: Feels Like Home

FusionCache tries to feel like a native part of .NET by adhering to the naming conventions of the standard **memory** and **distributed** cache components:

| | Memory Cache | Distributed Cache | Fusion Cache |
| ---: | :---: | :---: | :---: |
| **Cache Interface** | `IMemoryCache` | `IDistributedCache` | `IFusionCache` |
| **Cache Implementation** | `MemoryCache` | `[Various]Cache` | `FusionCache` |
| **Cache Options** | `MemoryCacheOptions` | `[Various]CacheOptions` | `FusionCacheOptions` |
| **Cache Entry Options** | `MemoryCacheEntryOptions` | `DistributedCacheEntryOptions` | `FusionCacheEntryOptions` |
## 📢 Backplane ([more](Backplane.md))

If you've ever used one of those you'll feel at home with FusionCache.
If you are in a scenario with multiple nodes, each with their own local memory cache, you typically also use a distributed cache as a secondary layer (see above).

Even using that, you may find that each memory cache may not be necessarily in-sync with the others, because when a value is cached locally it will stay the same until the `Duration` passes and expiration occurs.

To avoid this and have everything always synchronized you can use a backplane, a shared message bus where change notifications will be automatically sent to all other connected nodes each time a value changes in the cache, without you having to do anything.

You can read more [**here**](Backplane.md), or enjoy the complete [**step by step**](StepByStep.md) guide.

## :rocket: Factory ([more](FactoryOptimization.md))

A factory is just a function that you specify when using the main `GetOrSet[Async]` method: basically it's the way you specify **how to get a value** when it is not in the cache or is expired.
## 🚀 Factory ([more](FactoryOptimization.md))

A factory is just a function that you specify when using the main `GetOrSet[Async]` method: basically it's the way you specify **how to get a value** when it's needed.

Here's an example:

Expand All @@ -61,19 +79,21 @@ var product = cache.GetOrSet<Product>(

FusionCache will search for the value in the cache (*memory* and *distributed*, if available) and, if nothing is there, will call the factory to obtain the value: it then saves it into the cache with the specified options, and returns it to the caller, all transparently.

Special care has been put into ensuring that only 1 factory per-key will be executed concurrently, to avoid what is known as [Cache Stampede](https://en.wikipedia.org/wiki/Cache_stampede).
Special care has been put into ensuring that **only 1** factory per-key will be executed concurrently, to avoid what is known as [Cache Stampede](https://en.wikipedia.org/wiki/Cache_stampede).

You can read more [**here**](FactoryOptimization.md), or enjoy the complete [**step by step**](StepByStep.md) guide.

## :bomb: Fail-Safe ([more](FailSafe.md))

## 💣 Fail-Safe ([more](FailSafe.md))

Sometimes things can go wrong, and calling a factory for an expired cache entry can lead to exceptions because the database or the network is temporarily down: normally in this case the exception will cause an error page in your website, a failure status code in your api or something like that.

By enabling the fail-safe mechanism you can simply tell FusionCache to ignore those errors and **temporarily use the expired cache entry**: your website or service will remain online, and your users would not notice anything.

You can read more [**here**](FailSafe.md), or enjoy the complete [**step by step**](StepByStep.md) guide.

## :stopwatch: Timeouts ([more](Timeouts.md))

## ⏱️ Timeouts ([more](Timeouts.md))

Sometimes your data source (database, webservice, etc) is overloaded, the network is congested or something else is happening, and the end result is a **long wait** for a fresh piece of data.

Expand All @@ -87,16 +107,18 @@ In both cases it is possible (and enabled *by default*, so you don't have to do

You can read more [**here**](Timeouts.md), or enjoy the complete [**step by step**](StepByStep.md) guide.

## :level_slider: Options ([more](Options.md))

## 🎚️ Options ([more](Options.md))

There are 2 kinds of options:

- `FusionCacheOptions`: cache-wide options, related to the entire FusionCache instance
- `FusionCacheEntryOptions`: per-entry options, related to each call/entry
- `FusionCacheEntryOptions`: per-entry options, related to each method call/entry

You can read more [**here**](Options.md), or enjoy the complete [**step by step**](StepByStep.md) guide.

## :joystick: Core Methods ([more](CoreMethods.md))

## 🕹️ Core Methods ([more](CoreMethods.md))

At a high level there are 5 core methods:

Expand All @@ -114,27 +136,31 @@ Finally, most of them have a set of ♻ overloads for a better ease of use.

You can read more [**here**](CoreMethods.md).

## :dizzy: Natively Sync and Async

## 💫 Natively Sync and Async

Everything is natively available for both the **sync** and **async** programming models.

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.
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 together at the same time with no problems and a guarantee that only one will be executed at the same time.


## :telephone_receiver: Events
## 📞 Events ([more](Events.md))

There's a comprehensive set of events to subscribe to regarding core events inside of a FusionCache instance, both at a high level and at lower levels (memory/distributed layers).

You can read more [**here**](Events.md).

## :jigsaw: Plugins

## 🧩 Plugins ([more](Plugins.md))

FusionCache supports extensibility via plugins: it is possible for example to listen to [events](Events.md) and react in any way you want.

In time, the most useful plugins will be listed directly in the homepage.

You can read more [**here**](Plugins.md).

## :page_with_curl: Logging

## 📃 Logging
FusionCache can log extensively to help you pinpoint any possible problem in your production environment.

It uses the standard `ILogger<T>` interface and a structured logging approach so it fits well in the .NET ecosystem, allowing you to use any implementation you want that is compatible with it (Serilog, NLog, etc).
Expand Down
Loading