From 82106797360c95ff9e29d5753ac11b8a7025eb0a Mon Sep 17 00:00:00 2001 From: John Luo Date: Wed, 8 Mar 2017 16:57:37 -0800 Subject: [PATCH 01/22] WIP EvictionStrategy based compaction for MemoryCache --- Caching.sln | 226 +++++++++--------- .../ICacheEntry.cs | 2 +- .../IMemoryCacheEvictionStrategy.cs | 13 + .../CacheEntry.cs | 6 +- .../DefaultEvictionStrategy.cs | 121 ++++++++++ .../Infrastructure/GcNotification.cs | 53 ---- .../MemoryCache.cs | 115 +-------- .../MemoryCacheOptions.cs | 2 - .../CacheEntryScopeExpirationTests.cs | 1 - .../CompactTests.cs | 87 ------- .../GcNotificationTests.cs | 64 ----- .../MemoryCacheSetAndRemoveTests.cs | 5 +- .../TimeExpirationTests.cs | 1 - .../TokenExpirationTests.cs | 1 - 14 files changed, 253 insertions(+), 444 deletions(-) create mode 100644 src/Microsoft.Extensions.Caching.Abstractions/IMemoryCacheEvictionStrategy.cs create mode 100644 src/Microsoft.Extensions.Caching.Memory/DefaultEvictionStrategy.cs delete mode 100644 src/Microsoft.Extensions.Caching.Memory/Infrastructure/GcNotification.cs delete mode 100644 test/Microsoft.Extensions.Caching.Memory.Tests/CompactTests.cs delete mode 100644 test/Microsoft.Extensions.Caching.Memory.Tests/GcNotificationTests.cs diff --git a/Caching.sln b/Caching.sln index ba5f513b..de673a90 100644 --- a/Caching.sln +++ b/Caching.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26127.0 +VisualStudioVersion = 15.0.26228.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Caching.Memory", "src\Microsoft.Extensions.Caching.Memory\Microsoft.Extensions.Caching.Memory.csproj", "{966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}" EndProject @@ -48,172 +48,172 @@ Global GlobalSection(ProjectConfigurationPlatforms) = postSolution {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Debug|x64.ActiveCfg = Debug|x64 - {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Debug|x64.Build.0 = Debug|x64 - {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Debug|x86.ActiveCfg = Debug|x86 - {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Debug|x86.Build.0 = Debug|x86 + {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Debug|x64.ActiveCfg = Debug|Any CPU + {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Debug|x64.Build.0 = Debug|Any CPU + {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Debug|x86.ActiveCfg = Debug|Any CPU + {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Debug|x86.Build.0 = Debug|Any CPU {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Release|Any CPU.ActiveCfg = Release|Any CPU {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Release|Any CPU.Build.0 = Release|Any CPU - {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Release|x64.ActiveCfg = Release|x64 - {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Release|x64.Build.0 = Release|x64 - {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Release|x86.ActiveCfg = Release|x86 - {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Release|x86.Build.0 = Release|x86 + {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Release|x64.ActiveCfg = Release|Any CPU + {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Release|x64.Build.0 = Release|Any CPU + {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Release|x86.ActiveCfg = Release|Any CPU + {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Release|x86.Build.0 = Release|Any CPU {10B3C418-184A-4A68-8C48-34ED1A02757C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {10B3C418-184A-4A68-8C48-34ED1A02757C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {10B3C418-184A-4A68-8C48-34ED1A02757C}.Debug|x64.ActiveCfg = Debug|x64 - {10B3C418-184A-4A68-8C48-34ED1A02757C}.Debug|x64.Build.0 = Debug|x64 - {10B3C418-184A-4A68-8C48-34ED1A02757C}.Debug|x86.ActiveCfg = Debug|x86 - {10B3C418-184A-4A68-8C48-34ED1A02757C}.Debug|x86.Build.0 = Debug|x86 + {10B3C418-184A-4A68-8C48-34ED1A02757C}.Debug|x64.ActiveCfg = Debug|Any CPU + {10B3C418-184A-4A68-8C48-34ED1A02757C}.Debug|x64.Build.0 = Debug|Any CPU + {10B3C418-184A-4A68-8C48-34ED1A02757C}.Debug|x86.ActiveCfg = Debug|Any CPU + {10B3C418-184A-4A68-8C48-34ED1A02757C}.Debug|x86.Build.0 = Debug|Any CPU {10B3C418-184A-4A68-8C48-34ED1A02757C}.Release|Any CPU.ActiveCfg = Release|Any CPU {10B3C418-184A-4A68-8C48-34ED1A02757C}.Release|Any CPU.Build.0 = Release|Any CPU - {10B3C418-184A-4A68-8C48-34ED1A02757C}.Release|x64.ActiveCfg = Release|x64 - {10B3C418-184A-4A68-8C48-34ED1A02757C}.Release|x64.Build.0 = Release|x64 - {10B3C418-184A-4A68-8C48-34ED1A02757C}.Release|x86.ActiveCfg = Release|x86 - {10B3C418-184A-4A68-8C48-34ED1A02757C}.Release|x86.Build.0 = Release|x86 + {10B3C418-184A-4A68-8C48-34ED1A02757C}.Release|x64.ActiveCfg = Release|Any CPU + {10B3C418-184A-4A68-8C48-34ED1A02757C}.Release|x64.Build.0 = Release|Any CPU + {10B3C418-184A-4A68-8C48-34ED1A02757C}.Release|x86.ActiveCfg = Release|Any CPU + {10B3C418-184A-4A68-8C48-34ED1A02757C}.Release|x86.Build.0 = Release|Any CPU {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Debug|x64.ActiveCfg = Debug|x64 - {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Debug|x64.Build.0 = Debug|x64 - {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Debug|x86.ActiveCfg = Debug|x86 - {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Debug|x86.Build.0 = Debug|x86 + {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Debug|x64.ActiveCfg = Debug|Any CPU + {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Debug|x64.Build.0 = Debug|Any CPU + {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Debug|x86.ActiveCfg = Debug|Any CPU + {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Debug|x86.Build.0 = Debug|Any CPU {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Release|Any CPU.ActiveCfg = Release|Any CPU {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Release|Any CPU.Build.0 = Release|Any CPU - {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Release|x64.ActiveCfg = Release|x64 - {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Release|x64.Build.0 = Release|x64 - {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Release|x86.ActiveCfg = Release|x86 - {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Release|x86.Build.0 = Release|x86 + {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Release|x64.ActiveCfg = Release|Any CPU + {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Release|x64.Build.0 = Release|Any CPU + {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Release|x86.ActiveCfg = Release|Any CPU + {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Release|x86.Build.0 = Release|Any CPU {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Debug|x64.ActiveCfg = Debug|x64 - {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Debug|x64.Build.0 = Debug|x64 - {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Debug|x86.ActiveCfg = Debug|x86 - {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Debug|x86.Build.0 = Debug|x86 + {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Debug|x64.ActiveCfg = Debug|Any CPU + {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Debug|x64.Build.0 = Debug|Any CPU + {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Debug|x86.ActiveCfg = Debug|Any CPU + {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Debug|x86.Build.0 = Debug|Any CPU {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Release|Any CPU.ActiveCfg = Release|Any CPU {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Release|Any CPU.Build.0 = Release|Any CPU - {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Release|x64.ActiveCfg = Release|x64 - {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Release|x64.Build.0 = Release|x64 - {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Release|x86.ActiveCfg = Release|x86 - {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Release|x86.Build.0 = Release|x86 + {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Release|x64.ActiveCfg = Release|Any CPU + {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Release|x64.Build.0 = Release|Any CPU + {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Release|x86.ActiveCfg = Release|Any CPU + {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Release|x86.Build.0 = Release|Any CPU {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Debug|x64.ActiveCfg = Debug|x64 - {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Debug|x64.Build.0 = Debug|x64 - {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Debug|x86.ActiveCfg = Debug|x86 - {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Debug|x86.Build.0 = Debug|x86 + {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Debug|x64.ActiveCfg = Debug|Any CPU + {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Debug|x64.Build.0 = Debug|Any CPU + {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Debug|x86.ActiveCfg = Debug|Any CPU + {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Debug|x86.Build.0 = Debug|Any CPU {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Release|Any CPU.ActiveCfg = Release|Any CPU {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Release|Any CPU.Build.0 = Release|Any CPU - {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Release|x64.ActiveCfg = Release|x64 - {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Release|x64.Build.0 = Release|x64 - {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Release|x86.ActiveCfg = Release|x86 - {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Release|x86.Build.0 = Release|x86 + {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Release|x64.ActiveCfg = Release|Any CPU + {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Release|x64.Build.0 = Release|Any CPU + {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Release|x86.ActiveCfg = Release|Any CPU + {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Release|x86.Build.0 = Release|Any CPU {02E23070-947C-4438-8E50-CC2456D237BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {02E23070-947C-4438-8E50-CC2456D237BA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {02E23070-947C-4438-8E50-CC2456D237BA}.Debug|x64.ActiveCfg = Debug|x64 - {02E23070-947C-4438-8E50-CC2456D237BA}.Debug|x64.Build.0 = Debug|x64 - {02E23070-947C-4438-8E50-CC2456D237BA}.Debug|x86.ActiveCfg = Debug|x86 - {02E23070-947C-4438-8E50-CC2456D237BA}.Debug|x86.Build.0 = Debug|x86 + {02E23070-947C-4438-8E50-CC2456D237BA}.Debug|x64.ActiveCfg = Debug|Any CPU + {02E23070-947C-4438-8E50-CC2456D237BA}.Debug|x64.Build.0 = Debug|Any CPU + {02E23070-947C-4438-8E50-CC2456D237BA}.Debug|x86.ActiveCfg = Debug|Any CPU + {02E23070-947C-4438-8E50-CC2456D237BA}.Debug|x86.Build.0 = Debug|Any CPU {02E23070-947C-4438-8E50-CC2456D237BA}.Release|Any CPU.ActiveCfg = Release|Any CPU {02E23070-947C-4438-8E50-CC2456D237BA}.Release|Any CPU.Build.0 = Release|Any CPU - {02E23070-947C-4438-8E50-CC2456D237BA}.Release|x64.ActiveCfg = Release|x64 - {02E23070-947C-4438-8E50-CC2456D237BA}.Release|x64.Build.0 = Release|x64 - {02E23070-947C-4438-8E50-CC2456D237BA}.Release|x86.ActiveCfg = Release|x86 - {02E23070-947C-4438-8E50-CC2456D237BA}.Release|x86.Build.0 = Release|x86 + {02E23070-947C-4438-8E50-CC2456D237BA}.Release|x64.ActiveCfg = Release|Any CPU + {02E23070-947C-4438-8E50-CC2456D237BA}.Release|x64.Build.0 = Release|Any CPU + {02E23070-947C-4438-8E50-CC2456D237BA}.Release|x86.ActiveCfg = Release|Any CPU + {02E23070-947C-4438-8E50-CC2456D237BA}.Release|x86.Build.0 = Release|Any CPU {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Debug|x64.ActiveCfg = Debug|x64 - {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Debug|x64.Build.0 = Debug|x64 - {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Debug|x86.ActiveCfg = Debug|x86 - {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Debug|x86.Build.0 = Debug|x86 + {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Debug|x64.ActiveCfg = Debug|Any CPU + {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Debug|x64.Build.0 = Debug|Any CPU + {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Debug|x86.ActiveCfg = Debug|Any CPU + {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Debug|x86.Build.0 = Debug|Any CPU {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Release|Any CPU.ActiveCfg = Release|Any CPU {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Release|Any CPU.Build.0 = Release|Any CPU - {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Release|x64.ActiveCfg = Release|x64 - {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Release|x64.Build.0 = Release|x64 - {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Release|x86.ActiveCfg = Release|x86 - {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Release|x86.Build.0 = Release|x86 + {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Release|x64.ActiveCfg = Release|Any CPU + {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Release|x64.Build.0 = Release|Any CPU + {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Release|x86.ActiveCfg = Release|Any CPU + {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Release|x86.Build.0 = Release|Any CPU {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Debug|x64.ActiveCfg = Debug|x64 - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Debug|x64.Build.0 = Debug|x64 - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Debug|x86.ActiveCfg = Debug|x86 - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Debug|x86.Build.0 = Debug|x86 + {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Debug|x64.ActiveCfg = Debug|Any CPU + {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Debug|x64.Build.0 = Debug|Any CPU + {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Debug|x86.ActiveCfg = Debug|Any CPU + {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Debug|x86.Build.0 = Debug|Any CPU {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Release|Any CPU.ActiveCfg = Release|Any CPU {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Release|Any CPU.Build.0 = Release|Any CPU - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Release|x64.ActiveCfg = Release|x64 - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Release|x64.Build.0 = Release|x64 - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Release|x86.ActiveCfg = Release|x86 - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Release|x86.Build.0 = Release|x86 + {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Release|x64.ActiveCfg = Release|Any CPU + {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Release|x64.Build.0 = Release|Any CPU + {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Release|x86.ActiveCfg = Release|Any CPU + {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Release|x86.Build.0 = Release|Any CPU {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Debug|x64.ActiveCfg = Debug|x64 - {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Debug|x64.Build.0 = Debug|x64 - {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Debug|x86.ActiveCfg = Debug|x86 - {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Debug|x86.Build.0 = Debug|x86 + {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Debug|x64.Build.0 = Debug|Any CPU + {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Debug|x86.Build.0 = Debug|Any CPU {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Release|Any CPU.ActiveCfg = Release|Any CPU {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Release|Any CPU.Build.0 = Release|Any CPU - {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Release|x64.ActiveCfg = Release|x64 - {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Release|x64.Build.0 = Release|x64 - {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Release|x86.ActiveCfg = Release|x86 - {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Release|x86.Build.0 = Release|x86 + {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Release|x64.ActiveCfg = Release|Any CPU + {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Release|x64.Build.0 = Release|Any CPU + {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Release|x86.ActiveCfg = Release|Any CPU + {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Release|x86.Build.0 = Release|Any CPU {4301BDAE-CD67-4C1F-B754-D979C7274305}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4301BDAE-CD67-4C1F-B754-D979C7274305}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4301BDAE-CD67-4C1F-B754-D979C7274305}.Debug|x64.ActiveCfg = Debug|x64 - {4301BDAE-CD67-4C1F-B754-D979C7274305}.Debug|x64.Build.0 = Debug|x64 - {4301BDAE-CD67-4C1F-B754-D979C7274305}.Debug|x86.ActiveCfg = Debug|x86 - {4301BDAE-CD67-4C1F-B754-D979C7274305}.Debug|x86.Build.0 = Debug|x86 + {4301BDAE-CD67-4C1F-B754-D979C7274305}.Debug|x64.ActiveCfg = Debug|Any CPU + {4301BDAE-CD67-4C1F-B754-D979C7274305}.Debug|x64.Build.0 = Debug|Any CPU + {4301BDAE-CD67-4C1F-B754-D979C7274305}.Debug|x86.ActiveCfg = Debug|Any CPU + {4301BDAE-CD67-4C1F-B754-D979C7274305}.Debug|x86.Build.0 = Debug|Any CPU {4301BDAE-CD67-4C1F-B754-D979C7274305}.Release|Any CPU.ActiveCfg = Release|Any CPU {4301BDAE-CD67-4C1F-B754-D979C7274305}.Release|Any CPU.Build.0 = Release|Any CPU - {4301BDAE-CD67-4C1F-B754-D979C7274305}.Release|x64.ActiveCfg = Release|x64 - {4301BDAE-CD67-4C1F-B754-D979C7274305}.Release|x64.Build.0 = Release|x64 - {4301BDAE-CD67-4C1F-B754-D979C7274305}.Release|x86.ActiveCfg = Release|x86 - {4301BDAE-CD67-4C1F-B754-D979C7274305}.Release|x86.Build.0 = Release|x86 + {4301BDAE-CD67-4C1F-B754-D979C7274305}.Release|x64.ActiveCfg = Release|Any CPU + {4301BDAE-CD67-4C1F-B754-D979C7274305}.Release|x64.Build.0 = Release|Any CPU + {4301BDAE-CD67-4C1F-B754-D979C7274305}.Release|x86.ActiveCfg = Release|Any CPU + {4301BDAE-CD67-4C1F-B754-D979C7274305}.Release|x86.Build.0 = Release|Any CPU {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Debug|x64.ActiveCfg = Debug|x64 - {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Debug|x64.Build.0 = Debug|x64 - {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Debug|x86.ActiveCfg = Debug|x86 - {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Debug|x86.Build.0 = Debug|x86 + {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Debug|x64.ActiveCfg = Debug|Any CPU + {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Debug|x64.Build.0 = Debug|Any CPU + {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Debug|x86.ActiveCfg = Debug|Any CPU + {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Debug|x86.Build.0 = Debug|Any CPU {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Release|Any CPU.ActiveCfg = Release|Any CPU {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Release|Any CPU.Build.0 = Release|Any CPU - {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Release|x64.ActiveCfg = Release|x64 - {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Release|x64.Build.0 = Release|x64 - {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Release|x86.ActiveCfg = Release|x86 - {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Release|x86.Build.0 = Release|x86 + {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Release|x64.ActiveCfg = Release|Any CPU + {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Release|x64.Build.0 = Release|Any CPU + {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Release|x86.ActiveCfg = Release|Any CPU + {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Release|x86.Build.0 = Release|Any CPU {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Debug|x64.ActiveCfg = Debug|x64 - {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Debug|x64.Build.0 = Debug|x64 - {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Debug|x86.ActiveCfg = Debug|x86 - {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Debug|x86.Build.0 = Debug|x86 + {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Debug|x64.ActiveCfg = Debug|Any CPU + {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Debug|x64.Build.0 = Debug|Any CPU + {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Debug|x86.ActiveCfg = Debug|Any CPU + {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Debug|x86.Build.0 = Debug|Any CPU {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Release|Any CPU.ActiveCfg = Release|Any CPU {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Release|Any CPU.Build.0 = Release|Any CPU - {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Release|x64.ActiveCfg = Release|x64 - {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Release|x64.Build.0 = Release|x64 - {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Release|x86.ActiveCfg = Release|x86 - {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Release|x86.Build.0 = Release|x86 + {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Release|x64.ActiveCfg = Release|Any CPU + {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Release|x64.Build.0 = Release|Any CPU + {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Release|x86.ActiveCfg = Release|Any CPU + {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Release|x86.Build.0 = Release|Any CPU {CD2A826A-A474-4491-94C8-43C557C68067}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CD2A826A-A474-4491-94C8-43C557C68067}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CD2A826A-A474-4491-94C8-43C557C68067}.Debug|x64.ActiveCfg = Debug|x64 - {CD2A826A-A474-4491-94C8-43C557C68067}.Debug|x64.Build.0 = Debug|x64 - {CD2A826A-A474-4491-94C8-43C557C68067}.Debug|x86.ActiveCfg = Debug|x86 - {CD2A826A-A474-4491-94C8-43C557C68067}.Debug|x86.Build.0 = Debug|x86 + {CD2A826A-A474-4491-94C8-43C557C68067}.Debug|x64.ActiveCfg = Debug|Any CPU + {CD2A826A-A474-4491-94C8-43C557C68067}.Debug|x64.Build.0 = Debug|Any CPU + {CD2A826A-A474-4491-94C8-43C557C68067}.Debug|x86.ActiveCfg = Debug|Any CPU + {CD2A826A-A474-4491-94C8-43C557C68067}.Debug|x86.Build.0 = Debug|Any CPU {CD2A826A-A474-4491-94C8-43C557C68067}.Release|Any CPU.ActiveCfg = Release|Any CPU {CD2A826A-A474-4491-94C8-43C557C68067}.Release|Any CPU.Build.0 = Release|Any CPU - {CD2A826A-A474-4491-94C8-43C557C68067}.Release|x64.ActiveCfg = Release|x64 - {CD2A826A-A474-4491-94C8-43C557C68067}.Release|x64.Build.0 = Release|x64 - {CD2A826A-A474-4491-94C8-43C557C68067}.Release|x86.ActiveCfg = Release|x86 - {CD2A826A-A474-4491-94C8-43C557C68067}.Release|x86.Build.0 = Release|x86 + {CD2A826A-A474-4491-94C8-43C557C68067}.Release|x64.ActiveCfg = Release|Any CPU + {CD2A826A-A474-4491-94C8-43C557C68067}.Release|x64.Build.0 = Release|Any CPU + {CD2A826A-A474-4491-94C8-43C557C68067}.Release|x86.ActiveCfg = Release|Any CPU + {CD2A826A-A474-4491-94C8-43C557C68067}.Release|x86.Build.0 = Release|Any CPU {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Debug|Any CPU.Build.0 = Debug|Any CPU - {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Debug|x64.ActiveCfg = Debug|x64 - {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Debug|x64.Build.0 = Debug|x64 - {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Debug|x86.ActiveCfg = Debug|x86 - {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Debug|x86.Build.0 = Debug|x86 + {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Debug|x64.ActiveCfg = Debug|Any CPU + {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Debug|x64.Build.0 = Debug|Any CPU + {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Debug|x86.ActiveCfg = Debug|Any CPU + {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Debug|x86.Build.0 = Debug|Any CPU {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Release|Any CPU.ActiveCfg = Release|Any CPU {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Release|Any CPU.Build.0 = Release|Any CPU - {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Release|x64.ActiveCfg = Release|x64 - {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Release|x64.Build.0 = Release|x64 - {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Release|x86.ActiveCfg = Release|x86 - {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Release|x86.Build.0 = Release|x86 + {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Release|x64.ActiveCfg = Release|Any CPU + {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Release|x64.Build.0 = Release|Any CPU + {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Release|x86.ActiveCfg = Release|Any CPU + {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Microsoft.Extensions.Caching.Abstractions/ICacheEntry.cs b/src/Microsoft.Extensions.Caching.Abstractions/ICacheEntry.cs index 56f32ba2..e19f6568 100644 --- a/src/Microsoft.Extensions.Caching.Abstractions/ICacheEntry.cs +++ b/src/Microsoft.Extensions.Caching.Abstractions/ICacheEntry.cs @@ -26,7 +26,7 @@ public interface ICacheEntry : IDisposable /// Gets or sets an absolute expiration date for the cache entry. /// DateTimeOffset? AbsoluteExpiration { get; set; } - + /// /// Gets or sets an absolute expiration time, relative to now. /// diff --git a/src/Microsoft.Extensions.Caching.Abstractions/IMemoryCacheEvictionStrategy.cs b/src/Microsoft.Extensions.Caching.Abstractions/IMemoryCacheEvictionStrategy.cs new file mode 100644 index 00000000..f62e95e3 --- /dev/null +++ b/src/Microsoft.Extensions.Caching.Abstractions/IMemoryCacheEvictionStrategy.cs @@ -0,0 +1,13 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; + +namespace Microsoft.Extensions.Caching.Memory +{ + public interface IMemoryCacheEvictionStrategy where TCacheEntry : ICacheEntry + { + // doc comments, bool returns whether any entry was removed + bool Compact(IEnumerable entries); + } +} diff --git a/src/Microsoft.Extensions.Caching.Memory/CacheEntry.cs b/src/Microsoft.Extensions.Caching.Memory/CacheEntry.cs index 4569ef1c..69732943 100644 --- a/src/Microsoft.Extensions.Caching.Memory/CacheEntry.cs +++ b/src/Microsoft.Extensions.Caching.Memory/CacheEntry.cs @@ -9,7 +9,7 @@ namespace Microsoft.Extensions.Caching.Memory { - internal class CacheEntry : ICacheEntry + public class CacheEntry : ICacheEntry { private bool _added = false; private static readonly Action ExpirationCallback = ExpirationTokensExpired; @@ -157,7 +157,7 @@ public IList PostEvictionCallbacks public object Value { get; set; } - internal DateTimeOffset LastAccessed { get; set; } + public DateTimeOffset LastAccessed { get; set; } internal EvictionReason EvictionReason { get; private set; } @@ -172,7 +172,7 @@ public void Dispose() } } - internal bool CheckExpired(DateTimeOffset now) + public bool CheckExpired(DateTimeOffset now) { return _isExpired || CheckForExpiredTime(now) || CheckForExpiredTokens(); } diff --git a/src/Microsoft.Extensions.Caching.Memory/DefaultEvictionStrategy.cs b/src/Microsoft.Extensions.Caching.Memory/DefaultEvictionStrategy.cs new file mode 100644 index 00000000..9d4d45f2 --- /dev/null +++ b/src/Microsoft.Extensions.Caching.Memory/DefaultEvictionStrategy.cs @@ -0,0 +1,121 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.Extensions.Caching.Memory +{ + public class DefaultMemoryCacheEvictionStrategy : IMemoryCacheEvictionStrategy + { + private readonly MemoryCache _cache; + private readonly MemoryCacheOptions _options; + + public DefaultMemoryCacheEvictionStrategy(MemoryCache cache, MemoryCacheOptions options) + { + // TODO: check nulls + _cache = cache; + _options = options; + } + + /// Remove at least the given percentage (0.10 for 10%) of the total entries (or estimated memory?), according to the following policy: + /// 1. Remove all expired items. + /// 2. Bucket by CacheItemPriority. + /// 3. Least recently used objects. + /// ?. Items with the soonest absolute expiration. + /// ?. Items with the soonest sliding expiration. + /// ?. Larger objects - estimated by object graph size, inaccurate. + public bool Compact(IEnumerable entries) + { + // For illustration, say remove 10% every compact + var percentage = 0.1; + + var entriesToRemove = new List(); + var lowPriEntries = new List(); + var normalPriEntries = new List(); + var highPriEntries = new List(); + + // Sort items by expired & priority status + var now = _options.Clock.UtcNow; + foreach (var entry in entries) + { + if (entry.CheckExpired(now)) + { + entriesToRemove.Add(entry); + } + else + { + switch (entry.Priority) + { + case CacheItemPriority.Low: + lowPriEntries.Add(entry); + break; + case CacheItemPriority.Normal: + normalPriEntries.Add(entry); + break; + case CacheItemPriority.High: + highPriEntries.Add(entry); + break; + case CacheItemPriority.NeverRemove: + break; + default: + throw new NotSupportedException("Not implemented: " + entry.Priority); + } + } + } + + int removalCountTarget = (int)(entries.Count() * percentage); + + ExpirePriorityBucket(removalCountTarget, entriesToRemove, lowPriEntries); + ExpirePriorityBucket(removalCountTarget, entriesToRemove, normalPriEntries); + ExpirePriorityBucket(removalCountTarget, entriesToRemove, highPriEntries); + + foreach (var entry in entriesToRemove) + { + _cache.RemoveEntry(entry); + } + + return entriesToRemove.Count > 0; + } + + /// Policy: + /// 1. Least recently used objects. + /// ?. Items with the soonest absolute expiration. + /// ?. Items with the soonest sliding expiration. + /// ?. Larger objects - estimated by object graph size, inaccurate. + private void ExpirePriorityBucket(int removalCountTarget, List entriesToRemove, List priorityEntries) + { + // Do we meet our quota by just removing expired entries? + if (removalCountTarget <= entriesToRemove.Count) + { + // No-op, we've met quota + return; + } + if (entriesToRemove.Count + priorityEntries.Count <= removalCountTarget) + { + // Expire all of the entries in this bucket + foreach (var entry in priorityEntries) + { + entry.SetExpired(EvictionReason.Capacity); + } + entriesToRemove.AddRange(priorityEntries); + return; + } + + // Expire enough entries to reach our goal + // TODO: Refine policy + + // LRU + foreach (var entry in priorityEntries.OrderBy(entry => entry.LastAccessed)) + { + entry.SetExpired(EvictionReason.Capacity); + entriesToRemove.Add(entry); + if (removalCountTarget <= entriesToRemove.Count) + { + break; + } + } + } + } +} diff --git a/src/Microsoft.Extensions.Caching.Memory/Infrastructure/GcNotification.cs b/src/Microsoft.Extensions.Caching.Memory/Infrastructure/GcNotification.cs deleted file mode 100644 index 0448cf3c..00000000 --- a/src/Microsoft.Extensions.Caching.Memory/Infrastructure/GcNotification.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; - -namespace Microsoft.Extensions.Internal -{ - /// - /// Registers a callback that fires each time a Gen2 garbage collection occurs, - /// presumably due to memory pressure. - /// For this to work no components can have a reference to the instance. - /// - public class GcNotification - { - private readonly Func _callback; - private readonly object _state; - private readonly int _initialCollectionCount; - - private GcNotification(Func callback, object state) - { - _callback = callback; - _state = state; - _initialCollectionCount = GC.CollectionCount(2); - } - - public static void Register(Func callback, object state) - { - var notification = new GcNotification(callback, state); - } - - ~GcNotification() - { - bool reRegister = true; - try - { - // Only invoke the callback after this instance has made it into gen2. - if (_initialCollectionCount < GC.CollectionCount(2)) - { - reRegister = _callback(_state); - } - } - catch (Exception) - { - // Never throw from the finalizer thread - } - - if (reRegister && !Environment.HasShutdownStarted) - { - GC.ReRegisterForFinalize(this); - } - } - } -} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs index e9097dc2..5fb2b278 100644 --- a/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Internal; @@ -48,10 +47,6 @@ public MemoryCache(IOptions optionsAccessor) _entryExpirationNotification = EntryExpired; _clock = options.Clock ?? new SystemClock(); - if (options.CompactOnMemoryPressure) - { - GcNotification.Register(DoMemoryPreassureCollection, state: null); - } _expirationScanFrequency = options.ExpirationScanFrequency; _lastExpirationScan = _clock.UtcNow; } @@ -236,7 +231,7 @@ public void Remove(object key) StartScanForExpiredItems(); } - private void RemoveEntry(CacheEntry entry) + public void RemoveEntry(CacheEntry entry) { if (EntriesCollection.Remove(new KeyValuePair(entry.Key, entry))) { @@ -276,114 +271,6 @@ private static void ScanForExpiredItems(MemoryCache cache) } } - /// This is called after a Gen2 garbage collection. We assume this means there was memory pressure. - /// Remove at least 10% of the total entries (or estimated memory?). - private bool DoMemoryPreassureCollection(object state) - { - if (_disposed) - { - return false; - } - - Compact(0.10); - - return true; - } - - /// Remove at least the given percentage (0.10 for 10%) of the total entries (or estimated memory?), according to the following policy: - /// 1. Remove all expired items. - /// 2. Bucket by CacheItemPriority. - /// 3. Least recently used objects. - /// ?. Items with the soonest absolute expiration. - /// ?. Items with the soonest sliding expiration. - /// ?. Larger objects - estimated by object graph size, inaccurate. - public void Compact(double percentage) - { - var entriesToRemove = new List(); - var lowPriEntries = new List(); - var normalPriEntries = new List(); - var highPriEntries = new List(); - - // Sort items by expired & priority status - var now = _clock.UtcNow; - foreach (var entry in _entries.Values) - { - if (entry.CheckExpired(now)) - { - entriesToRemove.Add(entry); - } - else - { - switch (entry.Priority) - { - case CacheItemPriority.Low: - lowPriEntries.Add(entry); - break; - case CacheItemPriority.Normal: - normalPriEntries.Add(entry); - break; - case CacheItemPriority.High: - highPriEntries.Add(entry); - break; - case CacheItemPriority.NeverRemove: - break; - default: - throw new NotSupportedException("Not implemented: " + entry.Priority); - } - } - } - - int removalCountTarget = (int)(_entries.Count * percentage); - - ExpirePriorityBucket(removalCountTarget, entriesToRemove, lowPriEntries); - ExpirePriorityBucket(removalCountTarget, entriesToRemove, normalPriEntries); - ExpirePriorityBucket(removalCountTarget, entriesToRemove, highPriEntries); - - foreach (var entry in entriesToRemove) - { - RemoveEntry(entry); - } - } - - /// Policy: - /// 1. Least recently used objects. - /// ?. Items with the soonest absolute expiration. - /// ?. Items with the soonest sliding expiration. - /// ?. Larger objects - estimated by object graph size, inaccurate. - private void ExpirePriorityBucket(int removalCountTarget, List entriesToRemove, List priorityEntries) - { - // Do we meet our quota by just removing expired entries? - if (removalCountTarget <= entriesToRemove.Count) - { - // No-op, we've met quota - return; - } - if (entriesToRemove.Count + priorityEntries.Count <= removalCountTarget) - { - // Expire all of the entries in this bucket - foreach (var entry in priorityEntries) - { - entry.SetExpired(EvictionReason.Capacity); - } - entriesToRemove.AddRange(priorityEntries); - return; - } - - // Expire enough entries to reach our goal - // TODO: Refine policy - - // LRU - foreach (var entry in priorityEntries.OrderBy(entry => entry.LastAccessed)) - { - entry.SetExpired(EvictionReason.Capacity); - entriesToRemove.Add(entry); - if (removalCountTarget <= entriesToRemove.Count) - { - break; - } - } - } - public void Dispose() { Dispose(true); diff --git a/src/Microsoft.Extensions.Caching.Memory/MemoryCacheOptions.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheOptions.cs index 6d79032e..4e591b77 100644 --- a/src/Microsoft.Extensions.Caching.Memory/MemoryCacheOptions.cs +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheOptions.cs @@ -11,8 +11,6 @@ public class MemoryCacheOptions : IOptions { public ISystemClock Clock { get; set; } - public bool CompactOnMemoryPressure { get; set; } = true; - public TimeSpan ExpirationScanFrequency { get; set; } = TimeSpan.FromMinutes(1); MemoryCacheOptions IOptions.Value diff --git a/test/Microsoft.Extensions.Caching.Memory.Tests/CacheEntryScopeExpirationTests.cs b/test/Microsoft.Extensions.Caching.Memory.Tests/CacheEntryScopeExpirationTests.cs index 9980ab7d..3ca069ad 100644 --- a/test/Microsoft.Extensions.Caching.Memory.Tests/CacheEntryScopeExpirationTests.cs +++ b/test/Microsoft.Extensions.Caching.Memory.Tests/CacheEntryScopeExpirationTests.cs @@ -22,7 +22,6 @@ private IMemoryCache CreateCache(ISystemClock clock) return new MemoryCache(new MemoryCacheOptions() { Clock = clock, - CompactOnMemoryPressure = false, }); } diff --git a/test/Microsoft.Extensions.Caching.Memory.Tests/CompactTests.cs b/test/Microsoft.Extensions.Caching.Memory.Tests/CompactTests.cs deleted file mode 100644 index 5934f175..00000000 --- a/test/Microsoft.Extensions.Caching.Memory.Tests/CompactTests.cs +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using Microsoft.Extensions.Internal; -using Xunit; - -namespace Microsoft.Extensions.Caching.Memory -{ - public class CompactTests - { - private MemoryCache CreateCache(ISystemClock clock = null) - { - return new MemoryCache(new MemoryCacheOptions() - { - Clock = clock, - CompactOnMemoryPressure = false, - }); - } - - [Fact] - public void CompactEmptyNoOps() - { - var cache = CreateCache(); - cache.Compact(0.10); - } - - [Fact] - public void Compact100PercentClearsAll() - { - var cache = CreateCache(); - cache.Set("key1", "value1"); - cache.Set("key2", "value2"); - Assert.Equal(2, cache.Count); - cache.Compact(1.0); - Assert.Equal(0, cache.Count); - } - - [Fact] - public void Compact100PercentClearsAllButNeverRemoveItems() - { - var cache = CreateCache(); - cache.Set("key1", "Value1", new MemoryCacheEntryOptions().SetPriority(CacheItemPriority.NeverRemove)); - cache.Set("key2", "Value2", new MemoryCacheEntryOptions().SetPriority(CacheItemPriority.NeverRemove)); - cache.Set("key3", "value3"); - cache.Set("key4", "value4"); - Assert.Equal(4, cache.Count); - cache.Compact(1.0); - Assert.Equal(2, cache.Count); - Assert.Equal("Value1", cache.Get("key1")); - Assert.Equal("Value2", cache.Get("key2")); - } - - [Fact] - public void CompactPrioritizesLowPriortyItems() - { - var cache = CreateCache(); - cache.Set("key1", "Value1", new MemoryCacheEntryOptions().SetPriority(CacheItemPriority.Low)); - cache.Set("key2", "Value2", new MemoryCacheEntryOptions().SetPriority(CacheItemPriority.Low)); - cache.Set("key3", "value3"); - cache.Set("key4", "value4"); - Assert.Equal(4, cache.Count); - cache.Compact(0.5); - Assert.Equal(2, cache.Count); - Assert.Equal("value3", cache.Get("key3")); - Assert.Equal("value4", cache.Get("key4")); - } - - [Fact] - public void CompactPrioritizesLRU() - { - var testClock = new TestClock(); - var cache = CreateCache(testClock); - cache.Set("key1", "value1"); - testClock.Add(TimeSpan.FromSeconds(1)); - cache.Set("key2", "value2"); - testClock.Add(TimeSpan.FromSeconds(1)); - cache.Set("key3", "value3"); - testClock.Add(TimeSpan.FromSeconds(1)); - cache.Set("key4", "value4"); - Assert.Equal(4, cache.Count); - cache.Compact(0.90); - Assert.Equal(1, cache.Count); - Assert.Equal("value4", cache.Get("key4")); - } - } -} \ No newline at end of file diff --git a/test/Microsoft.Extensions.Caching.Memory.Tests/GcNotificationTests.cs b/test/Microsoft.Extensions.Caching.Memory.Tests/GcNotificationTests.cs deleted file mode 100644 index 292d492d..00000000 --- a/test/Microsoft.Extensions.Caching.Memory.Tests/GcNotificationTests.cs +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Threading; -using Microsoft.AspNetCore.Testing.xunit; -using Microsoft.Extensions.Internal; -using Xunit; - -namespace Microsoft.Extensions.Caching.Memory -{ - public class GcNotificationTests - { - [ConditionalFact] - [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test fails on Mono (finalizer never called).")] - public void CallbackRegisteredAndInvoked() - { - var callbackInvoked = new ManualResetEvent(false); - GcNotification.Register(state => - { - callbackInvoked.Set(); - return false; - }, null); - - GcCollectAndWait(); - Assert.True(callbackInvoked.WaitOne(0)); - } - - [Fact] - public void CallbackInvokedMultipleTimes() - { - var reRegisterForFinalize = true; - var callbackInvoked = new ManualResetEvent(false); - GcNotification.Register(state => - { - callbackInvoked.Set(); - return reRegisterForFinalize; - }, null); - - GcCollectAndWait(); - Assert.True(callbackInvoked.WaitOne(0)); - - callbackInvoked.Reset(); - reRegisterForFinalize = false; - - GcCollectAndWait(); - Assert.True(callbackInvoked.WaitOne(0)); - - callbackInvoked.Reset(); - - // No callback expected the 3rd time - GcCollectAndWait(); - Assert.False(callbackInvoked.WaitOne(0)); - } - - private static void GcCollectAndWait() - { - // We need to collect twice for this test to work on Mono - GC.Collect(2, GCCollectionMode.Forced, blocking: true); - GC.Collect(2, GCCollectionMode.Forced, blocking: true); - GC.WaitForPendingFinalizers(); - } - } -} diff --git a/test/Microsoft.Extensions.Caching.Memory.Tests/MemoryCacheSetAndRemoveTests.cs b/test/Microsoft.Extensions.Caching.Memory.Tests/MemoryCacheSetAndRemoveTests.cs index b8f5d09e..93f95f04 100644 --- a/test/Microsoft.Extensions.Caching.Memory.Tests/MemoryCacheSetAndRemoveTests.cs +++ b/test/Microsoft.Extensions.Caching.Memory.Tests/MemoryCacheSetAndRemoveTests.cs @@ -12,10 +12,7 @@ public class MemoryCacheSetAndRemoveTests { private static IMemoryCache CreateCache() { - return new MemoryCache(new MemoryCacheOptions() - { - CompactOnMemoryPressure = false, - }); + return new MemoryCache(new MemoryCacheOptions()); } [Fact] diff --git a/test/Microsoft.Extensions.Caching.Memory.Tests/TimeExpirationTests.cs b/test/Microsoft.Extensions.Caching.Memory.Tests/TimeExpirationTests.cs index 0237ad6b..f4cbfd99 100644 --- a/test/Microsoft.Extensions.Caching.Memory.Tests/TimeExpirationTests.cs +++ b/test/Microsoft.Extensions.Caching.Memory.Tests/TimeExpirationTests.cs @@ -16,7 +16,6 @@ private IMemoryCache CreateCache(ISystemClock clock) return new MemoryCache(new MemoryCacheOptions() { Clock = clock, - CompactOnMemoryPressure = false, }); } diff --git a/test/Microsoft.Extensions.Caching.Memory.Tests/TokenExpirationTests.cs b/test/Microsoft.Extensions.Caching.Memory.Tests/TokenExpirationTests.cs index 6b1c6484..9c36ab26 100644 --- a/test/Microsoft.Extensions.Caching.Memory.Tests/TokenExpirationTests.cs +++ b/test/Microsoft.Extensions.Caching.Memory.Tests/TokenExpirationTests.cs @@ -23,7 +23,6 @@ private IMemoryCache CreateCache(ISystemClock clock) return new MemoryCache(new MemoryCacheOptions() { Clock = clock, - CompactOnMemoryPressure = false, }); } From 9be39b1fbd5fe6e6af106d2c89d562e22ba02499 Mon Sep 17 00:00:00 2001 From: John Luo Date: Wed, 8 Mar 2017 18:58:42 -0800 Subject: [PATCH 02/22] Test eviction with default timer --- .../IMemoryCacheEvictionStrategy.cs | 5 ++-- .../DefaultEvictionStrategy.cs | 24 +++--------------- .../MemoryCache.cs | 25 ++++++++++++++++--- .../MemoryCacheOptions.cs | 3 +++ 4 files changed, 32 insertions(+), 25 deletions(-) diff --git a/src/Microsoft.Extensions.Caching.Abstractions/IMemoryCacheEvictionStrategy.cs b/src/Microsoft.Extensions.Caching.Abstractions/IMemoryCacheEvictionStrategy.cs index f62e95e3..04dd35a5 100644 --- a/src/Microsoft.Extensions.Caching.Abstractions/IMemoryCacheEvictionStrategy.cs +++ b/src/Microsoft.Extensions.Caching.Abstractions/IMemoryCacheEvictionStrategy.cs @@ -1,13 +1,14 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Collections.Generic; namespace Microsoft.Extensions.Caching.Memory { public interface IMemoryCacheEvictionStrategy where TCacheEntry : ICacheEntry { - // doc comments, bool returns whether any entry was removed - bool Compact(IEnumerable entries); + // doc comments, returns a list of entries entry to evict + IEnumerable GetEntriesToEvict(IEnumerable entries, DateTimeOffset now); } } diff --git a/src/Microsoft.Extensions.Caching.Memory/DefaultEvictionStrategy.cs b/src/Microsoft.Extensions.Caching.Memory/DefaultEvictionStrategy.cs index 9d4d45f2..8df73986 100644 --- a/src/Microsoft.Extensions.Caching.Memory/DefaultEvictionStrategy.cs +++ b/src/Microsoft.Extensions.Caching.Memory/DefaultEvictionStrategy.cs @@ -9,16 +9,6 @@ namespace Microsoft.Extensions.Caching.Memory { public class DefaultMemoryCacheEvictionStrategy : IMemoryCacheEvictionStrategy { - private readonly MemoryCache _cache; - private readonly MemoryCacheOptions _options; - - public DefaultMemoryCacheEvictionStrategy(MemoryCache cache, MemoryCacheOptions options) - { - // TODO: check nulls - _cache = cache; - _options = options; - } - /// Remove at least the given percentage (0.10 for 10%) of the total entries (or estimated memory?), according to the following policy: /// 1. Remove all expired items. /// 2. Bucket by CacheItemPriority. @@ -26,10 +16,10 @@ public DefaultMemoryCacheEvictionStrategy(MemoryCache cache, MemoryCacheOptions /// ?. Items with the soonest absolute expiration. /// ?. Items with the soonest sliding expiration. /// ?. Larger objects - estimated by object graph size, inaccurate. - public bool Compact(IEnumerable entries) + public IEnumerable GetEntriesToEvict(IEnumerable entries, DateTimeOffset now) { - // For illustration, say remove 10% every compact - var percentage = 0.1; + // For testing, only remove expired entries + var percentage = 0D; var entriesToRemove = new List(); var lowPriEntries = new List(); @@ -37,7 +27,6 @@ public bool Compact(IEnumerable entries) var highPriEntries = new List(); // Sort items by expired & priority status - var now = _options.Clock.UtcNow; foreach (var entry in entries) { if (entry.CheckExpired(now)) @@ -71,12 +60,7 @@ public bool Compact(IEnumerable entries) ExpirePriorityBucket(removalCountTarget, entriesToRemove, normalPriEntries); ExpirePriorityBucket(removalCountTarget, entriesToRemove, highPriEntries); - foreach (var entry in entriesToRemove) - { - _cache.RemoveEntry(entry); - } - - return entriesToRemove.Count > 0; + return entriesToRemove; } /// Policy: diff --git a/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs index 5fb2b278..199987dd 100644 --- a/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs @@ -26,10 +26,13 @@ public class MemoryCache : IMemoryCache private readonly Action _entryExpirationNotification; private readonly ISystemClock _clock; + private readonly IMemoryCacheEvictionStrategy _evictionStrategy; private TimeSpan _expirationScanFrequency; private DateTimeOffset _lastExpirationScan; + private Timer _evictionTimer; + /// /// Creates a new instance. /// @@ -47,8 +50,11 @@ public MemoryCache(IOptions optionsAccessor) _entryExpirationNotification = EntryExpired; _clock = options.Clock ?? new SystemClock(); + _evictionStrategy = options.EvictionStrategy ?? new DefaultMemoryCacheEvictionStrategy(); _expirationScanFrequency = options.ExpirationScanFrequency; _lastExpirationScan = _clock.UtcNow; + + _evictionTimer = new Timer(ExecuteCacheEviction, null, dueTime: 0, period: 1000); //due time and period are set for testing } /// @@ -231,7 +237,7 @@ public void Remove(object key) StartScanForExpiredItems(); } - public void RemoveEntry(CacheEntry entry) + private void RemoveEntry(CacheEntry entry) { if (EntriesCollection.Remove(new KeyValuePair(entry.Key, entry))) { @@ -246,6 +252,16 @@ private void EntryExpired(CacheEntry entry) StartScanForExpiredItems(); } + private void ExecuteCacheEviction(object state) + { + foreach (var entry in _evictionStrategy.GetEntriesToEvict(_entries.Values, _clock.UtcNow)) + { + RemoveEntry(entry); + } + } + +#region Replace with eviction logic? + // Called by multiple actions to see how long it's been since we last checked for expired items. // If sufficient time has elapsed then a scan is initiated on a background task. private void StartScanForExpiredItems() @@ -254,8 +270,9 @@ private void StartScanForExpiredItems() if (_expirationScanFrequency < now - _lastExpirationScan) { _lastExpirationScan = now; - Task.Factory.StartNew(state => ScanForExpiredItems((MemoryCache)state), this, - CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); + // Disabled for testing + //Task.Factory.StartNew(state => ScanForExpiredItems((MemoryCache)state), this, + // CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); } } @@ -271,6 +288,8 @@ private static void ScanForExpiredItems(MemoryCache cache) } } +#endregion + public void Dispose() { Dispose(true); diff --git a/src/Microsoft.Extensions.Caching.Memory/MemoryCacheOptions.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheOptions.cs index 4e591b77..7fa920a8 100644 --- a/src/Microsoft.Extensions.Caching.Memory/MemoryCacheOptions.cs +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheOptions.cs @@ -11,8 +11,11 @@ public class MemoryCacheOptions : IOptions { public ISystemClock Clock { get; set; } + // May no longer be needed public TimeSpan ExpirationScanFrequency { get; set; } = TimeSpan.FromMinutes(1); + public IMemoryCacheEvictionStrategy EvictionStrategy { get; set; } + MemoryCacheOptions IOptions.Value { get { return this; } From 618ab069a7aff0ef8adf8111eea4d3df9668002c Mon Sep 17 00:00:00 2001 From: John Luo Date: Wed, 8 Mar 2017 19:10:11 -0800 Subject: [PATCH 03/22] Undo sln change --- Caching.sln | 226 ++++++++++++++++++++++++++-------------------------- 1 file changed, 113 insertions(+), 113 deletions(-) diff --git a/Caching.sln b/Caching.sln index de673a90..ba5f513b 100644 --- a/Caching.sln +++ b/Caching.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26228.0 +VisualStudioVersion = 15.0.26127.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Caching.Memory", "src\Microsoft.Extensions.Caching.Memory\Microsoft.Extensions.Caching.Memory.csproj", "{966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}" EndProject @@ -48,172 +48,172 @@ Global GlobalSection(ProjectConfigurationPlatforms) = postSolution {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Debug|x64.ActiveCfg = Debug|Any CPU - {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Debug|x64.Build.0 = Debug|Any CPU - {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Debug|x86.ActiveCfg = Debug|Any CPU - {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Debug|x86.Build.0 = Debug|Any CPU + {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Debug|x64.ActiveCfg = Debug|x64 + {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Debug|x64.Build.0 = Debug|x64 + {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Debug|x86.ActiveCfg = Debug|x86 + {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Debug|x86.Build.0 = Debug|x86 {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Release|Any CPU.ActiveCfg = Release|Any CPU {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Release|Any CPU.Build.0 = Release|Any CPU - {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Release|x64.ActiveCfg = Release|Any CPU - {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Release|x64.Build.0 = Release|Any CPU - {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Release|x86.ActiveCfg = Release|Any CPU - {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Release|x86.Build.0 = Release|Any CPU + {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Release|x64.ActiveCfg = Release|x64 + {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Release|x64.Build.0 = Release|x64 + {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Release|x86.ActiveCfg = Release|x86 + {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Release|x86.Build.0 = Release|x86 {10B3C418-184A-4A68-8C48-34ED1A02757C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {10B3C418-184A-4A68-8C48-34ED1A02757C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {10B3C418-184A-4A68-8C48-34ED1A02757C}.Debug|x64.ActiveCfg = Debug|Any CPU - {10B3C418-184A-4A68-8C48-34ED1A02757C}.Debug|x64.Build.0 = Debug|Any CPU - {10B3C418-184A-4A68-8C48-34ED1A02757C}.Debug|x86.ActiveCfg = Debug|Any CPU - {10B3C418-184A-4A68-8C48-34ED1A02757C}.Debug|x86.Build.0 = Debug|Any CPU + {10B3C418-184A-4A68-8C48-34ED1A02757C}.Debug|x64.ActiveCfg = Debug|x64 + {10B3C418-184A-4A68-8C48-34ED1A02757C}.Debug|x64.Build.0 = Debug|x64 + {10B3C418-184A-4A68-8C48-34ED1A02757C}.Debug|x86.ActiveCfg = Debug|x86 + {10B3C418-184A-4A68-8C48-34ED1A02757C}.Debug|x86.Build.0 = Debug|x86 {10B3C418-184A-4A68-8C48-34ED1A02757C}.Release|Any CPU.ActiveCfg = Release|Any CPU {10B3C418-184A-4A68-8C48-34ED1A02757C}.Release|Any CPU.Build.0 = Release|Any CPU - {10B3C418-184A-4A68-8C48-34ED1A02757C}.Release|x64.ActiveCfg = Release|Any CPU - {10B3C418-184A-4A68-8C48-34ED1A02757C}.Release|x64.Build.0 = Release|Any CPU - {10B3C418-184A-4A68-8C48-34ED1A02757C}.Release|x86.ActiveCfg = Release|Any CPU - {10B3C418-184A-4A68-8C48-34ED1A02757C}.Release|x86.Build.0 = Release|Any CPU + {10B3C418-184A-4A68-8C48-34ED1A02757C}.Release|x64.ActiveCfg = Release|x64 + {10B3C418-184A-4A68-8C48-34ED1A02757C}.Release|x64.Build.0 = Release|x64 + {10B3C418-184A-4A68-8C48-34ED1A02757C}.Release|x86.ActiveCfg = Release|x86 + {10B3C418-184A-4A68-8C48-34ED1A02757C}.Release|x86.Build.0 = Release|x86 {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Debug|x64.ActiveCfg = Debug|Any CPU - {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Debug|x64.Build.0 = Debug|Any CPU - {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Debug|x86.ActiveCfg = Debug|Any CPU - {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Debug|x86.Build.0 = Debug|Any CPU + {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Debug|x64.ActiveCfg = Debug|x64 + {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Debug|x64.Build.0 = Debug|x64 + {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Debug|x86.ActiveCfg = Debug|x86 + {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Debug|x86.Build.0 = Debug|x86 {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Release|Any CPU.ActiveCfg = Release|Any CPU {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Release|Any CPU.Build.0 = Release|Any CPU - {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Release|x64.ActiveCfg = Release|Any CPU - {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Release|x64.Build.0 = Release|Any CPU - {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Release|x86.ActiveCfg = Release|Any CPU - {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Release|x86.Build.0 = Release|Any CPU + {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Release|x64.ActiveCfg = Release|x64 + {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Release|x64.Build.0 = Release|x64 + {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Release|x86.ActiveCfg = Release|x86 + {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Release|x86.Build.0 = Release|x86 {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Debug|x64.ActiveCfg = Debug|Any CPU - {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Debug|x64.Build.0 = Debug|Any CPU - {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Debug|x86.ActiveCfg = Debug|Any CPU - {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Debug|x86.Build.0 = Debug|Any CPU + {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Debug|x64.ActiveCfg = Debug|x64 + {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Debug|x64.Build.0 = Debug|x64 + {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Debug|x86.ActiveCfg = Debug|x86 + {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Debug|x86.Build.0 = Debug|x86 {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Release|Any CPU.ActiveCfg = Release|Any CPU {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Release|Any CPU.Build.0 = Release|Any CPU - {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Release|x64.ActiveCfg = Release|Any CPU - {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Release|x64.Build.0 = Release|Any CPU - {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Release|x86.ActiveCfg = Release|Any CPU - {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Release|x86.Build.0 = Release|Any CPU + {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Release|x64.ActiveCfg = Release|x64 + {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Release|x64.Build.0 = Release|x64 + {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Release|x86.ActiveCfg = Release|x86 + {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Release|x86.Build.0 = Release|x86 {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Debug|x64.ActiveCfg = Debug|Any CPU - {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Debug|x64.Build.0 = Debug|Any CPU - {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Debug|x86.ActiveCfg = Debug|Any CPU - {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Debug|x86.Build.0 = Debug|Any CPU + {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Debug|x64.ActiveCfg = Debug|x64 + {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Debug|x64.Build.0 = Debug|x64 + {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Debug|x86.ActiveCfg = Debug|x86 + {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Debug|x86.Build.0 = Debug|x86 {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Release|Any CPU.ActiveCfg = Release|Any CPU {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Release|Any CPU.Build.0 = Release|Any CPU - {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Release|x64.ActiveCfg = Release|Any CPU - {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Release|x64.Build.0 = Release|Any CPU - {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Release|x86.ActiveCfg = Release|Any CPU - {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Release|x86.Build.0 = Release|Any CPU + {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Release|x64.ActiveCfg = Release|x64 + {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Release|x64.Build.0 = Release|x64 + {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Release|x86.ActiveCfg = Release|x86 + {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Release|x86.Build.0 = Release|x86 {02E23070-947C-4438-8E50-CC2456D237BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {02E23070-947C-4438-8E50-CC2456D237BA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {02E23070-947C-4438-8E50-CC2456D237BA}.Debug|x64.ActiveCfg = Debug|Any CPU - {02E23070-947C-4438-8E50-CC2456D237BA}.Debug|x64.Build.0 = Debug|Any CPU - {02E23070-947C-4438-8E50-CC2456D237BA}.Debug|x86.ActiveCfg = Debug|Any CPU - {02E23070-947C-4438-8E50-CC2456D237BA}.Debug|x86.Build.0 = Debug|Any CPU + {02E23070-947C-4438-8E50-CC2456D237BA}.Debug|x64.ActiveCfg = Debug|x64 + {02E23070-947C-4438-8E50-CC2456D237BA}.Debug|x64.Build.0 = Debug|x64 + {02E23070-947C-4438-8E50-CC2456D237BA}.Debug|x86.ActiveCfg = Debug|x86 + {02E23070-947C-4438-8E50-CC2456D237BA}.Debug|x86.Build.0 = Debug|x86 {02E23070-947C-4438-8E50-CC2456D237BA}.Release|Any CPU.ActiveCfg = Release|Any CPU {02E23070-947C-4438-8E50-CC2456D237BA}.Release|Any CPU.Build.0 = Release|Any CPU - {02E23070-947C-4438-8E50-CC2456D237BA}.Release|x64.ActiveCfg = Release|Any CPU - {02E23070-947C-4438-8E50-CC2456D237BA}.Release|x64.Build.0 = Release|Any CPU - {02E23070-947C-4438-8E50-CC2456D237BA}.Release|x86.ActiveCfg = Release|Any CPU - {02E23070-947C-4438-8E50-CC2456D237BA}.Release|x86.Build.0 = Release|Any CPU + {02E23070-947C-4438-8E50-CC2456D237BA}.Release|x64.ActiveCfg = Release|x64 + {02E23070-947C-4438-8E50-CC2456D237BA}.Release|x64.Build.0 = Release|x64 + {02E23070-947C-4438-8E50-CC2456D237BA}.Release|x86.ActiveCfg = Release|x86 + {02E23070-947C-4438-8E50-CC2456D237BA}.Release|x86.Build.0 = Release|x86 {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Debug|x64.ActiveCfg = Debug|Any CPU - {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Debug|x64.Build.0 = Debug|Any CPU - {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Debug|x86.ActiveCfg = Debug|Any CPU - {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Debug|x86.Build.0 = Debug|Any CPU + {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Debug|x64.ActiveCfg = Debug|x64 + {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Debug|x64.Build.0 = Debug|x64 + {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Debug|x86.ActiveCfg = Debug|x86 + {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Debug|x86.Build.0 = Debug|x86 {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Release|Any CPU.ActiveCfg = Release|Any CPU {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Release|Any CPU.Build.0 = Release|Any CPU - {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Release|x64.ActiveCfg = Release|Any CPU - {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Release|x64.Build.0 = Release|Any CPU - {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Release|x86.ActiveCfg = Release|Any CPU - {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Release|x86.Build.0 = Release|Any CPU + {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Release|x64.ActiveCfg = Release|x64 + {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Release|x64.Build.0 = Release|x64 + {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Release|x86.ActiveCfg = Release|x86 + {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Release|x86.Build.0 = Release|x86 {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Debug|x64.ActiveCfg = Debug|Any CPU - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Debug|x64.Build.0 = Debug|Any CPU - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Debug|x86.ActiveCfg = Debug|Any CPU - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Debug|x86.Build.0 = Debug|Any CPU + {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Debug|x64.ActiveCfg = Debug|x64 + {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Debug|x64.Build.0 = Debug|x64 + {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Debug|x86.ActiveCfg = Debug|x86 + {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Debug|x86.Build.0 = Debug|x86 {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Release|Any CPU.ActiveCfg = Release|Any CPU {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Release|Any CPU.Build.0 = Release|Any CPU - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Release|x64.ActiveCfg = Release|Any CPU - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Release|x64.Build.0 = Release|Any CPU - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Release|x86.ActiveCfg = Release|Any CPU - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Release|x86.Build.0 = Release|Any CPU + {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Release|x64.ActiveCfg = Release|x64 + {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Release|x64.Build.0 = Release|x64 + {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Release|x86.ActiveCfg = Release|x86 + {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Release|x86.Build.0 = Release|x86 {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Debug|x64.ActiveCfg = Debug|Any CPU - {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Debug|x64.Build.0 = Debug|Any CPU - {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Debug|x86.ActiveCfg = Debug|Any CPU - {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Debug|x86.Build.0 = Debug|Any CPU + {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Debug|x64.ActiveCfg = Debug|x64 + {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Debug|x64.Build.0 = Debug|x64 + {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Debug|x86.ActiveCfg = Debug|x86 + {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Debug|x86.Build.0 = Debug|x86 {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Release|Any CPU.ActiveCfg = Release|Any CPU {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Release|Any CPU.Build.0 = Release|Any CPU - {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Release|x64.ActiveCfg = Release|Any CPU - {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Release|x64.Build.0 = Release|Any CPU - {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Release|x86.ActiveCfg = Release|Any CPU - {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Release|x86.Build.0 = Release|Any CPU + {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Release|x64.ActiveCfg = Release|x64 + {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Release|x64.Build.0 = Release|x64 + {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Release|x86.ActiveCfg = Release|x86 + {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Release|x86.Build.0 = Release|x86 {4301BDAE-CD67-4C1F-B754-D979C7274305}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4301BDAE-CD67-4C1F-B754-D979C7274305}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4301BDAE-CD67-4C1F-B754-D979C7274305}.Debug|x64.ActiveCfg = Debug|Any CPU - {4301BDAE-CD67-4C1F-B754-D979C7274305}.Debug|x64.Build.0 = Debug|Any CPU - {4301BDAE-CD67-4C1F-B754-D979C7274305}.Debug|x86.ActiveCfg = Debug|Any CPU - {4301BDAE-CD67-4C1F-B754-D979C7274305}.Debug|x86.Build.0 = Debug|Any CPU + {4301BDAE-CD67-4C1F-B754-D979C7274305}.Debug|x64.ActiveCfg = Debug|x64 + {4301BDAE-CD67-4C1F-B754-D979C7274305}.Debug|x64.Build.0 = Debug|x64 + {4301BDAE-CD67-4C1F-B754-D979C7274305}.Debug|x86.ActiveCfg = Debug|x86 + {4301BDAE-CD67-4C1F-B754-D979C7274305}.Debug|x86.Build.0 = Debug|x86 {4301BDAE-CD67-4C1F-B754-D979C7274305}.Release|Any CPU.ActiveCfg = Release|Any CPU {4301BDAE-CD67-4C1F-B754-D979C7274305}.Release|Any CPU.Build.0 = Release|Any CPU - {4301BDAE-CD67-4C1F-B754-D979C7274305}.Release|x64.ActiveCfg = Release|Any CPU - {4301BDAE-CD67-4C1F-B754-D979C7274305}.Release|x64.Build.0 = Release|Any CPU - {4301BDAE-CD67-4C1F-B754-D979C7274305}.Release|x86.ActiveCfg = Release|Any CPU - {4301BDAE-CD67-4C1F-B754-D979C7274305}.Release|x86.Build.0 = Release|Any CPU + {4301BDAE-CD67-4C1F-B754-D979C7274305}.Release|x64.ActiveCfg = Release|x64 + {4301BDAE-CD67-4C1F-B754-D979C7274305}.Release|x64.Build.0 = Release|x64 + {4301BDAE-CD67-4C1F-B754-D979C7274305}.Release|x86.ActiveCfg = Release|x86 + {4301BDAE-CD67-4C1F-B754-D979C7274305}.Release|x86.Build.0 = Release|x86 {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Debug|x64.ActiveCfg = Debug|Any CPU - {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Debug|x64.Build.0 = Debug|Any CPU - {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Debug|x86.ActiveCfg = Debug|Any CPU - {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Debug|x86.Build.0 = Debug|Any CPU + {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Debug|x64.ActiveCfg = Debug|x64 + {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Debug|x64.Build.0 = Debug|x64 + {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Debug|x86.ActiveCfg = Debug|x86 + {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Debug|x86.Build.0 = Debug|x86 {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Release|Any CPU.ActiveCfg = Release|Any CPU {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Release|Any CPU.Build.0 = Release|Any CPU - {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Release|x64.ActiveCfg = Release|Any CPU - {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Release|x64.Build.0 = Release|Any CPU - {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Release|x86.ActiveCfg = Release|Any CPU - {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Release|x86.Build.0 = Release|Any CPU + {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Release|x64.ActiveCfg = Release|x64 + {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Release|x64.Build.0 = Release|x64 + {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Release|x86.ActiveCfg = Release|x86 + {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Release|x86.Build.0 = Release|x86 {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Debug|x64.ActiveCfg = Debug|Any CPU - {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Debug|x64.Build.0 = Debug|Any CPU - {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Debug|x86.ActiveCfg = Debug|Any CPU - {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Debug|x86.Build.0 = Debug|Any CPU + {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Debug|x64.ActiveCfg = Debug|x64 + {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Debug|x64.Build.0 = Debug|x64 + {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Debug|x86.ActiveCfg = Debug|x86 + {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Debug|x86.Build.0 = Debug|x86 {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Release|Any CPU.ActiveCfg = Release|Any CPU {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Release|Any CPU.Build.0 = Release|Any CPU - {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Release|x64.ActiveCfg = Release|Any CPU - {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Release|x64.Build.0 = Release|Any CPU - {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Release|x86.ActiveCfg = Release|Any CPU - {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Release|x86.Build.0 = Release|Any CPU + {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Release|x64.ActiveCfg = Release|x64 + {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Release|x64.Build.0 = Release|x64 + {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Release|x86.ActiveCfg = Release|x86 + {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Release|x86.Build.0 = Release|x86 {CD2A826A-A474-4491-94C8-43C557C68067}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CD2A826A-A474-4491-94C8-43C557C68067}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CD2A826A-A474-4491-94C8-43C557C68067}.Debug|x64.ActiveCfg = Debug|Any CPU - {CD2A826A-A474-4491-94C8-43C557C68067}.Debug|x64.Build.0 = Debug|Any CPU - {CD2A826A-A474-4491-94C8-43C557C68067}.Debug|x86.ActiveCfg = Debug|Any CPU - {CD2A826A-A474-4491-94C8-43C557C68067}.Debug|x86.Build.0 = Debug|Any CPU + {CD2A826A-A474-4491-94C8-43C557C68067}.Debug|x64.ActiveCfg = Debug|x64 + {CD2A826A-A474-4491-94C8-43C557C68067}.Debug|x64.Build.0 = Debug|x64 + {CD2A826A-A474-4491-94C8-43C557C68067}.Debug|x86.ActiveCfg = Debug|x86 + {CD2A826A-A474-4491-94C8-43C557C68067}.Debug|x86.Build.0 = Debug|x86 {CD2A826A-A474-4491-94C8-43C557C68067}.Release|Any CPU.ActiveCfg = Release|Any CPU {CD2A826A-A474-4491-94C8-43C557C68067}.Release|Any CPU.Build.0 = Release|Any CPU - {CD2A826A-A474-4491-94C8-43C557C68067}.Release|x64.ActiveCfg = Release|Any CPU - {CD2A826A-A474-4491-94C8-43C557C68067}.Release|x64.Build.0 = Release|Any CPU - {CD2A826A-A474-4491-94C8-43C557C68067}.Release|x86.ActiveCfg = Release|Any CPU - {CD2A826A-A474-4491-94C8-43C557C68067}.Release|x86.Build.0 = Release|Any CPU + {CD2A826A-A474-4491-94C8-43C557C68067}.Release|x64.ActiveCfg = Release|x64 + {CD2A826A-A474-4491-94C8-43C557C68067}.Release|x64.Build.0 = Release|x64 + {CD2A826A-A474-4491-94C8-43C557C68067}.Release|x86.ActiveCfg = Release|x86 + {CD2A826A-A474-4491-94C8-43C557C68067}.Release|x86.Build.0 = Release|x86 {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Debug|Any CPU.Build.0 = Debug|Any CPU - {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Debug|x64.ActiveCfg = Debug|Any CPU - {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Debug|x64.Build.0 = Debug|Any CPU - {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Debug|x86.ActiveCfg = Debug|Any CPU - {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Debug|x86.Build.0 = Debug|Any CPU + {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Debug|x64.ActiveCfg = Debug|x64 + {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Debug|x64.Build.0 = Debug|x64 + {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Debug|x86.ActiveCfg = Debug|x86 + {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Debug|x86.Build.0 = Debug|x86 {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Release|Any CPU.ActiveCfg = Release|Any CPU {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Release|Any CPU.Build.0 = Release|Any CPU - {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Release|x64.ActiveCfg = Release|Any CPU - {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Release|x64.Build.0 = Release|Any CPU - {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Release|x86.ActiveCfg = Release|Any CPU - {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Release|x86.Build.0 = Release|Any CPU + {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Release|x64.ActiveCfg = Release|x64 + {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Release|x64.Build.0 = Release|x64 + {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Release|x86.ActiveCfg = Release|x86 + {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 76c91a38c69da1235e0dd55998dd026fb95f9e08 Mon Sep 17 00:00:00 2001 From: John Luo Date: Thu, 9 Mar 2017 11:12:11 -0800 Subject: [PATCH 04/22] Remove CacheItemPriority and add sample eviction strategies --- Caching.sln | 226 +++++++++--------- samples/MemoryCacheSample/Program.cs | 6 - .../CacheEntryExtensions.cs | 13 - .../CacheItemPriority.cs | 17 -- .../ICacheEntry.cs | 6 - .../MemoryCacheEntryExtensions.cs | 13 - .../MemoryCacheEntryOptions.cs | 6 - .../CacheEntry.cs | 6 - .../DefaultEvictionStrategy.cs | 105 -------- .../DefaultMemoryCacheEvictionStrategy.cs | 27 +++ .../LRUMemoryCacheEvictionStrategy.cs | 46 ++++ 11 files changed, 186 insertions(+), 285 deletions(-) delete mode 100644 src/Microsoft.Extensions.Caching.Abstractions/CacheItemPriority.cs delete mode 100644 src/Microsoft.Extensions.Caching.Memory/DefaultEvictionStrategy.cs create mode 100644 src/Microsoft.Extensions.Caching.Memory/DefaultMemoryCacheEvictionStrategy.cs create mode 100644 src/Microsoft.Extensions.Caching.Memory/LRUMemoryCacheEvictionStrategy.cs diff --git a/Caching.sln b/Caching.sln index ba5f513b..de673a90 100644 --- a/Caching.sln +++ b/Caching.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26127.0 +VisualStudioVersion = 15.0.26228.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Caching.Memory", "src\Microsoft.Extensions.Caching.Memory\Microsoft.Extensions.Caching.Memory.csproj", "{966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}" EndProject @@ -48,172 +48,172 @@ Global GlobalSection(ProjectConfigurationPlatforms) = postSolution {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Debug|x64.ActiveCfg = Debug|x64 - {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Debug|x64.Build.0 = Debug|x64 - {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Debug|x86.ActiveCfg = Debug|x86 - {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Debug|x86.Build.0 = Debug|x86 + {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Debug|x64.ActiveCfg = Debug|Any CPU + {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Debug|x64.Build.0 = Debug|Any CPU + {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Debug|x86.ActiveCfg = Debug|Any CPU + {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Debug|x86.Build.0 = Debug|Any CPU {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Release|Any CPU.ActiveCfg = Release|Any CPU {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Release|Any CPU.Build.0 = Release|Any CPU - {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Release|x64.ActiveCfg = Release|x64 - {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Release|x64.Build.0 = Release|x64 - {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Release|x86.ActiveCfg = Release|x86 - {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Release|x86.Build.0 = Release|x86 + {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Release|x64.ActiveCfg = Release|Any CPU + {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Release|x64.Build.0 = Release|Any CPU + {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Release|x86.ActiveCfg = Release|Any CPU + {966D16D8-5D4E-4433-9DA7-F53EE44B7EE7}.Release|x86.Build.0 = Release|Any CPU {10B3C418-184A-4A68-8C48-34ED1A02757C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {10B3C418-184A-4A68-8C48-34ED1A02757C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {10B3C418-184A-4A68-8C48-34ED1A02757C}.Debug|x64.ActiveCfg = Debug|x64 - {10B3C418-184A-4A68-8C48-34ED1A02757C}.Debug|x64.Build.0 = Debug|x64 - {10B3C418-184A-4A68-8C48-34ED1A02757C}.Debug|x86.ActiveCfg = Debug|x86 - {10B3C418-184A-4A68-8C48-34ED1A02757C}.Debug|x86.Build.0 = Debug|x86 + {10B3C418-184A-4A68-8C48-34ED1A02757C}.Debug|x64.ActiveCfg = Debug|Any CPU + {10B3C418-184A-4A68-8C48-34ED1A02757C}.Debug|x64.Build.0 = Debug|Any CPU + {10B3C418-184A-4A68-8C48-34ED1A02757C}.Debug|x86.ActiveCfg = Debug|Any CPU + {10B3C418-184A-4A68-8C48-34ED1A02757C}.Debug|x86.Build.0 = Debug|Any CPU {10B3C418-184A-4A68-8C48-34ED1A02757C}.Release|Any CPU.ActiveCfg = Release|Any CPU {10B3C418-184A-4A68-8C48-34ED1A02757C}.Release|Any CPU.Build.0 = Release|Any CPU - {10B3C418-184A-4A68-8C48-34ED1A02757C}.Release|x64.ActiveCfg = Release|x64 - {10B3C418-184A-4A68-8C48-34ED1A02757C}.Release|x64.Build.0 = Release|x64 - {10B3C418-184A-4A68-8C48-34ED1A02757C}.Release|x86.ActiveCfg = Release|x86 - {10B3C418-184A-4A68-8C48-34ED1A02757C}.Release|x86.Build.0 = Release|x86 + {10B3C418-184A-4A68-8C48-34ED1A02757C}.Release|x64.ActiveCfg = Release|Any CPU + {10B3C418-184A-4A68-8C48-34ED1A02757C}.Release|x64.Build.0 = Release|Any CPU + {10B3C418-184A-4A68-8C48-34ED1A02757C}.Release|x86.ActiveCfg = Release|Any CPU + {10B3C418-184A-4A68-8C48-34ED1A02757C}.Release|x86.Build.0 = Release|Any CPU {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Debug|x64.ActiveCfg = Debug|x64 - {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Debug|x64.Build.0 = Debug|x64 - {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Debug|x86.ActiveCfg = Debug|x86 - {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Debug|x86.Build.0 = Debug|x86 + {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Debug|x64.ActiveCfg = Debug|Any CPU + {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Debug|x64.Build.0 = Debug|Any CPU + {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Debug|x86.ActiveCfg = Debug|Any CPU + {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Debug|x86.Build.0 = Debug|Any CPU {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Release|Any CPU.ActiveCfg = Release|Any CPU {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Release|Any CPU.Build.0 = Release|Any CPU - {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Release|x64.ActiveCfg = Release|x64 - {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Release|x64.Build.0 = Release|x64 - {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Release|x86.ActiveCfg = Release|x86 - {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Release|x86.Build.0 = Release|x86 + {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Release|x64.ActiveCfg = Release|Any CPU + {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Release|x64.Build.0 = Release|Any CPU + {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Release|x86.ActiveCfg = Release|Any CPU + {1C24C0CE-84B6-4D6C-B484-32741D704FD8}.Release|x86.Build.0 = Release|Any CPU {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Debug|x64.ActiveCfg = Debug|x64 - {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Debug|x64.Build.0 = Debug|x64 - {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Debug|x86.ActiveCfg = Debug|x86 - {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Debug|x86.Build.0 = Debug|x86 + {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Debug|x64.ActiveCfg = Debug|Any CPU + {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Debug|x64.Build.0 = Debug|Any CPU + {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Debug|x86.ActiveCfg = Debug|Any CPU + {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Debug|x86.Build.0 = Debug|Any CPU {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Release|Any CPU.ActiveCfg = Release|Any CPU {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Release|Any CPU.Build.0 = Release|Any CPU - {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Release|x64.ActiveCfg = Release|x64 - {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Release|x64.Build.0 = Release|x64 - {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Release|x86.ActiveCfg = Release|x86 - {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Release|x86.Build.0 = Release|x86 + {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Release|x64.ActiveCfg = Release|Any CPU + {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Release|x64.Build.0 = Release|Any CPU + {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Release|x86.ActiveCfg = Release|Any CPU + {FF7C7587-BBCA-4574-95AE-101A6FF63B8F}.Release|x86.Build.0 = Release|Any CPU {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Debug|x64.ActiveCfg = Debug|x64 - {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Debug|x64.Build.0 = Debug|x64 - {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Debug|x86.ActiveCfg = Debug|x86 - {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Debug|x86.Build.0 = Debug|x86 + {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Debug|x64.ActiveCfg = Debug|Any CPU + {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Debug|x64.Build.0 = Debug|Any CPU + {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Debug|x86.ActiveCfg = Debug|Any CPU + {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Debug|x86.Build.0 = Debug|Any CPU {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Release|Any CPU.ActiveCfg = Release|Any CPU {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Release|Any CPU.Build.0 = Release|Any CPU - {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Release|x64.ActiveCfg = Release|x64 - {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Release|x64.Build.0 = Release|x64 - {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Release|x86.ActiveCfg = Release|x86 - {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Release|x86.Build.0 = Release|x86 + {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Release|x64.ActiveCfg = Release|Any CPU + {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Release|x64.Build.0 = Release|Any CPU + {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Release|x86.ActiveCfg = Release|Any CPU + {FD9FD44E-50D7-4121-9DAB-87A88191CE9F}.Release|x86.Build.0 = Release|Any CPU {02E23070-947C-4438-8E50-CC2456D237BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {02E23070-947C-4438-8E50-CC2456D237BA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {02E23070-947C-4438-8E50-CC2456D237BA}.Debug|x64.ActiveCfg = Debug|x64 - {02E23070-947C-4438-8E50-CC2456D237BA}.Debug|x64.Build.0 = Debug|x64 - {02E23070-947C-4438-8E50-CC2456D237BA}.Debug|x86.ActiveCfg = Debug|x86 - {02E23070-947C-4438-8E50-CC2456D237BA}.Debug|x86.Build.0 = Debug|x86 + {02E23070-947C-4438-8E50-CC2456D237BA}.Debug|x64.ActiveCfg = Debug|Any CPU + {02E23070-947C-4438-8E50-CC2456D237BA}.Debug|x64.Build.0 = Debug|Any CPU + {02E23070-947C-4438-8E50-CC2456D237BA}.Debug|x86.ActiveCfg = Debug|Any CPU + {02E23070-947C-4438-8E50-CC2456D237BA}.Debug|x86.Build.0 = Debug|Any CPU {02E23070-947C-4438-8E50-CC2456D237BA}.Release|Any CPU.ActiveCfg = Release|Any CPU {02E23070-947C-4438-8E50-CC2456D237BA}.Release|Any CPU.Build.0 = Release|Any CPU - {02E23070-947C-4438-8E50-CC2456D237BA}.Release|x64.ActiveCfg = Release|x64 - {02E23070-947C-4438-8E50-CC2456D237BA}.Release|x64.Build.0 = Release|x64 - {02E23070-947C-4438-8E50-CC2456D237BA}.Release|x86.ActiveCfg = Release|x86 - {02E23070-947C-4438-8E50-CC2456D237BA}.Release|x86.Build.0 = Release|x86 + {02E23070-947C-4438-8E50-CC2456D237BA}.Release|x64.ActiveCfg = Release|Any CPU + {02E23070-947C-4438-8E50-CC2456D237BA}.Release|x64.Build.0 = Release|Any CPU + {02E23070-947C-4438-8E50-CC2456D237BA}.Release|x86.ActiveCfg = Release|Any CPU + {02E23070-947C-4438-8E50-CC2456D237BA}.Release|x86.Build.0 = Release|Any CPU {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Debug|x64.ActiveCfg = Debug|x64 - {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Debug|x64.Build.0 = Debug|x64 - {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Debug|x86.ActiveCfg = Debug|x86 - {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Debug|x86.Build.0 = Debug|x86 + {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Debug|x64.ActiveCfg = Debug|Any CPU + {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Debug|x64.Build.0 = Debug|Any CPU + {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Debug|x86.ActiveCfg = Debug|Any CPU + {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Debug|x86.Build.0 = Debug|Any CPU {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Release|Any CPU.ActiveCfg = Release|Any CPU {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Release|Any CPU.Build.0 = Release|Any CPU - {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Release|x64.ActiveCfg = Release|x64 - {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Release|x64.Build.0 = Release|x64 - {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Release|x86.ActiveCfg = Release|x86 - {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Release|x86.Build.0 = Release|x86 + {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Release|x64.ActiveCfg = Release|Any CPU + {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Release|x64.Build.0 = Release|Any CPU + {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Release|x86.ActiveCfg = Release|Any CPU + {4109BDF4-BFF4-4AB6-8ED1-881DE410EA57}.Release|x86.Build.0 = Release|Any CPU {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Debug|x64.ActiveCfg = Debug|x64 - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Debug|x64.Build.0 = Debug|x64 - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Debug|x86.ActiveCfg = Debug|x86 - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Debug|x86.Build.0 = Debug|x86 + {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Debug|x64.ActiveCfg = Debug|Any CPU + {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Debug|x64.Build.0 = Debug|Any CPU + {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Debug|x86.ActiveCfg = Debug|Any CPU + {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Debug|x86.Build.0 = Debug|Any CPU {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Release|Any CPU.ActiveCfg = Release|Any CPU {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Release|Any CPU.Build.0 = Release|Any CPU - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Release|x64.ActiveCfg = Release|x64 - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Release|x64.Build.0 = Release|x64 - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Release|x86.ActiveCfg = Release|x86 - {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Release|x86.Build.0 = Release|x86 + {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Release|x64.ActiveCfg = Release|Any CPU + {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Release|x64.Build.0 = Release|Any CPU + {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Release|x86.ActiveCfg = Release|Any CPU + {D1DCC5A4-3EC1-403B-B069-1F3FCB50E3A5}.Release|x86.Build.0 = Release|Any CPU {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Debug|x64.ActiveCfg = Debug|x64 - {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Debug|x64.Build.0 = Debug|x64 - {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Debug|x86.ActiveCfg = Debug|x86 - {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Debug|x86.Build.0 = Debug|x86 + {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Debug|x64.Build.0 = Debug|Any CPU + {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Debug|x86.Build.0 = Debug|Any CPU {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Release|Any CPU.ActiveCfg = Release|Any CPU {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Release|Any CPU.Build.0 = Release|Any CPU - {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Release|x64.ActiveCfg = Release|x64 - {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Release|x64.Build.0 = Release|x64 - {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Release|x86.ActiveCfg = Release|x86 - {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Release|x86.Build.0 = Release|x86 + {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Release|x64.ActiveCfg = Release|Any CPU + {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Release|x64.Build.0 = Release|Any CPU + {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Release|x86.ActiveCfg = Release|Any CPU + {60AAD8D1-33A3-4943-A4AF-C8A5BA418BC6}.Release|x86.Build.0 = Release|Any CPU {4301BDAE-CD67-4C1F-B754-D979C7274305}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4301BDAE-CD67-4C1F-B754-D979C7274305}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4301BDAE-CD67-4C1F-B754-D979C7274305}.Debug|x64.ActiveCfg = Debug|x64 - {4301BDAE-CD67-4C1F-B754-D979C7274305}.Debug|x64.Build.0 = Debug|x64 - {4301BDAE-CD67-4C1F-B754-D979C7274305}.Debug|x86.ActiveCfg = Debug|x86 - {4301BDAE-CD67-4C1F-B754-D979C7274305}.Debug|x86.Build.0 = Debug|x86 + {4301BDAE-CD67-4C1F-B754-D979C7274305}.Debug|x64.ActiveCfg = Debug|Any CPU + {4301BDAE-CD67-4C1F-B754-D979C7274305}.Debug|x64.Build.0 = Debug|Any CPU + {4301BDAE-CD67-4C1F-B754-D979C7274305}.Debug|x86.ActiveCfg = Debug|Any CPU + {4301BDAE-CD67-4C1F-B754-D979C7274305}.Debug|x86.Build.0 = Debug|Any CPU {4301BDAE-CD67-4C1F-B754-D979C7274305}.Release|Any CPU.ActiveCfg = Release|Any CPU {4301BDAE-CD67-4C1F-B754-D979C7274305}.Release|Any CPU.Build.0 = Release|Any CPU - {4301BDAE-CD67-4C1F-B754-D979C7274305}.Release|x64.ActiveCfg = Release|x64 - {4301BDAE-CD67-4C1F-B754-D979C7274305}.Release|x64.Build.0 = Release|x64 - {4301BDAE-CD67-4C1F-B754-D979C7274305}.Release|x86.ActiveCfg = Release|x86 - {4301BDAE-CD67-4C1F-B754-D979C7274305}.Release|x86.Build.0 = Release|x86 + {4301BDAE-CD67-4C1F-B754-D979C7274305}.Release|x64.ActiveCfg = Release|Any CPU + {4301BDAE-CD67-4C1F-B754-D979C7274305}.Release|x64.Build.0 = Release|Any CPU + {4301BDAE-CD67-4C1F-B754-D979C7274305}.Release|x86.ActiveCfg = Release|Any CPU + {4301BDAE-CD67-4C1F-B754-D979C7274305}.Release|x86.Build.0 = Release|Any CPU {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Debug|x64.ActiveCfg = Debug|x64 - {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Debug|x64.Build.0 = Debug|x64 - {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Debug|x86.ActiveCfg = Debug|x86 - {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Debug|x86.Build.0 = Debug|x86 + {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Debug|x64.ActiveCfg = Debug|Any CPU + {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Debug|x64.Build.0 = Debug|Any CPU + {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Debug|x86.ActiveCfg = Debug|Any CPU + {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Debug|x86.Build.0 = Debug|Any CPU {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Release|Any CPU.ActiveCfg = Release|Any CPU {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Release|Any CPU.Build.0 = Release|Any CPU - {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Release|x64.ActiveCfg = Release|x64 - {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Release|x64.Build.0 = Release|x64 - {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Release|x86.ActiveCfg = Release|x86 - {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Release|x86.Build.0 = Release|x86 + {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Release|x64.ActiveCfg = Release|Any CPU + {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Release|x64.Build.0 = Release|Any CPU + {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Release|x86.ActiveCfg = Release|Any CPU + {FED2ABB6-D0B3-4AC7-940F-250C9767EACB}.Release|x86.Build.0 = Release|Any CPU {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Debug|x64.ActiveCfg = Debug|x64 - {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Debug|x64.Build.0 = Debug|x64 - {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Debug|x86.ActiveCfg = Debug|x86 - {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Debug|x86.Build.0 = Debug|x86 + {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Debug|x64.ActiveCfg = Debug|Any CPU + {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Debug|x64.Build.0 = Debug|Any CPU + {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Debug|x86.ActiveCfg = Debug|Any CPU + {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Debug|x86.Build.0 = Debug|Any CPU {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Release|Any CPU.ActiveCfg = Release|Any CPU {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Release|Any CPU.Build.0 = Release|Any CPU - {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Release|x64.ActiveCfg = Release|x64 - {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Release|x64.Build.0 = Release|x64 - {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Release|x86.ActiveCfg = Release|x86 - {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Release|x86.Build.0 = Release|x86 + {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Release|x64.ActiveCfg = Release|Any CPU + {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Release|x64.Build.0 = Release|Any CPU + {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Release|x86.ActiveCfg = Release|Any CPU + {38EFEDCC-1076-4E1F-89C5-443C7FCCEE6A}.Release|x86.Build.0 = Release|Any CPU {CD2A826A-A474-4491-94C8-43C557C68067}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CD2A826A-A474-4491-94C8-43C557C68067}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CD2A826A-A474-4491-94C8-43C557C68067}.Debug|x64.ActiveCfg = Debug|x64 - {CD2A826A-A474-4491-94C8-43C557C68067}.Debug|x64.Build.0 = Debug|x64 - {CD2A826A-A474-4491-94C8-43C557C68067}.Debug|x86.ActiveCfg = Debug|x86 - {CD2A826A-A474-4491-94C8-43C557C68067}.Debug|x86.Build.0 = Debug|x86 + {CD2A826A-A474-4491-94C8-43C557C68067}.Debug|x64.ActiveCfg = Debug|Any CPU + {CD2A826A-A474-4491-94C8-43C557C68067}.Debug|x64.Build.0 = Debug|Any CPU + {CD2A826A-A474-4491-94C8-43C557C68067}.Debug|x86.ActiveCfg = Debug|Any CPU + {CD2A826A-A474-4491-94C8-43C557C68067}.Debug|x86.Build.0 = Debug|Any CPU {CD2A826A-A474-4491-94C8-43C557C68067}.Release|Any CPU.ActiveCfg = Release|Any CPU {CD2A826A-A474-4491-94C8-43C557C68067}.Release|Any CPU.Build.0 = Release|Any CPU - {CD2A826A-A474-4491-94C8-43C557C68067}.Release|x64.ActiveCfg = Release|x64 - {CD2A826A-A474-4491-94C8-43C557C68067}.Release|x64.Build.0 = Release|x64 - {CD2A826A-A474-4491-94C8-43C557C68067}.Release|x86.ActiveCfg = Release|x86 - {CD2A826A-A474-4491-94C8-43C557C68067}.Release|x86.Build.0 = Release|x86 + {CD2A826A-A474-4491-94C8-43C557C68067}.Release|x64.ActiveCfg = Release|Any CPU + {CD2A826A-A474-4491-94C8-43C557C68067}.Release|x64.Build.0 = Release|Any CPU + {CD2A826A-A474-4491-94C8-43C557C68067}.Release|x86.ActiveCfg = Release|Any CPU + {CD2A826A-A474-4491-94C8-43C557C68067}.Release|x86.Build.0 = Release|Any CPU {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Debug|Any CPU.Build.0 = Debug|Any CPU - {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Debug|x64.ActiveCfg = Debug|x64 - {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Debug|x64.Build.0 = Debug|x64 - {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Debug|x86.ActiveCfg = Debug|x86 - {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Debug|x86.Build.0 = Debug|x86 + {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Debug|x64.ActiveCfg = Debug|Any CPU + {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Debug|x64.Build.0 = Debug|Any CPU + {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Debug|x86.ActiveCfg = Debug|Any CPU + {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Debug|x86.Build.0 = Debug|Any CPU {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Release|Any CPU.ActiveCfg = Release|Any CPU {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Release|Any CPU.Build.0 = Release|Any CPU - {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Release|x64.ActiveCfg = Release|x64 - {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Release|x64.Build.0 = Release|x64 - {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Release|x86.ActiveCfg = Release|x86 - {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Release|x86.Build.0 = Release|x86 + {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Release|x64.ActiveCfg = Release|Any CPU + {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Release|x64.Build.0 = Release|Any CPU + {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Release|x86.ActiveCfg = Release|Any CPU + {17E332EB-D18D-4BF5-BCA5-989E36C78B79}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/samples/MemoryCacheSample/Program.cs b/samples/MemoryCacheSample/Program.cs index 1268a7e3..76795861 100644 --- a/samples/MemoryCacheSample/Program.cs +++ b/samples/MemoryCacheSample/Program.cs @@ -39,12 +39,6 @@ public static void Main() // Cache entry configuration: - // Stays in the cache as long as possible - result = cache.Set( - key, - new object(), - new MemoryCacheEntryOptions().SetPriority(CacheItemPriority.NeverRemove)); - // Automatically remove if not accessed in the given time result = cache.Set( key, diff --git a/src/Microsoft.Extensions.Caching.Abstractions/CacheEntryExtensions.cs b/src/Microsoft.Extensions.Caching.Abstractions/CacheEntryExtensions.cs index 84baa5d6..150ccd5b 100644 --- a/src/Microsoft.Extensions.Caching.Abstractions/CacheEntryExtensions.cs +++ b/src/Microsoft.Extensions.Caching.Abstractions/CacheEntryExtensions.cs @@ -8,18 +8,6 @@ namespace Microsoft.Extensions.Caching.Memory { public static class CacheEntryExtensions { - /// - /// Sets the priority for keeping the cache entry in the cache during a memory pressure tokened cleanup. - /// - /// - /// - public static ICacheEntry SetPriority( - this ICacheEntry entry, - CacheItemPriority priority) - { - entry.Priority = priority; - return entry; - } /// /// Expire the cache entry if the given expires. @@ -148,7 +136,6 @@ public static ICacheEntry SetOptions(this ICacheEntry entry, MemoryCacheEntryOpt entry.AbsoluteExpiration = options.AbsoluteExpiration; entry.AbsoluteExpirationRelativeToNow = options.AbsoluteExpirationRelativeToNow; entry.SlidingExpiration = options.SlidingExpiration; - entry.Priority = options.Priority; foreach (var expirationToken in options.ExpirationTokens) { diff --git a/src/Microsoft.Extensions.Caching.Abstractions/CacheItemPriority.cs b/src/Microsoft.Extensions.Caching.Abstractions/CacheItemPriority.cs deleted file mode 100644 index bba8e31a..00000000 --- a/src/Microsoft.Extensions.Caching.Abstractions/CacheItemPriority.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -namespace Microsoft.Extensions.Caching.Memory -{ - // TODO: Granularity? - /// - /// Specifies how items are prioritized for preservation during a memory pressure triggered cleanup. - /// - public enum CacheItemPriority - { - Low, - Normal, - High, - NeverRemove, - } -} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Caching.Abstractions/ICacheEntry.cs b/src/Microsoft.Extensions.Caching.Abstractions/ICacheEntry.cs index e19f6568..b9654e51 100644 --- a/src/Microsoft.Extensions.Caching.Abstractions/ICacheEntry.cs +++ b/src/Microsoft.Extensions.Caching.Abstractions/ICacheEntry.cs @@ -48,11 +48,5 @@ public interface ICacheEntry : IDisposable /// IList PostEvictionCallbacks { get; } - /// - /// Gets or sets the priority for keeping the cache entry in the cache during a - /// memory pressure triggered cleanup. The default is . - /// - CacheItemPriority Priority { get; set; } - } } \ No newline at end of file diff --git a/src/Microsoft.Extensions.Caching.Abstractions/MemoryCacheEntryExtensions.cs b/src/Microsoft.Extensions.Caching.Abstractions/MemoryCacheEntryExtensions.cs index 1a8a29cc..55d125ca 100644 --- a/src/Microsoft.Extensions.Caching.Abstractions/MemoryCacheEntryExtensions.cs +++ b/src/Microsoft.Extensions.Caching.Abstractions/MemoryCacheEntryExtensions.cs @@ -8,19 +8,6 @@ namespace Microsoft.Extensions.Caching.Memory { public static class MemoryCacheEntryExtensions { - /// - /// Sets the priority for keeping the cache entry in the cache during a memory pressure tokened cleanup. - /// - /// - /// - public static MemoryCacheEntryOptions SetPriority( - this MemoryCacheEntryOptions options, - CacheItemPriority priority) - { - options.Priority = priority; - return options; - } - /// /// Expire the cache entry if the given expires. /// diff --git a/src/Microsoft.Extensions.Caching.Abstractions/MemoryCacheEntryOptions.cs b/src/Microsoft.Extensions.Caching.Abstractions/MemoryCacheEntryOptions.cs index 4054cbf5..2554ef59 100644 --- a/src/Microsoft.Extensions.Caching.Abstractions/MemoryCacheEntryOptions.cs +++ b/src/Microsoft.Extensions.Caching.Abstractions/MemoryCacheEntryOptions.cs @@ -84,11 +84,5 @@ public TimeSpan? SlidingExpiration /// public IList PostEvictionCallbacks { get; } = new List(); - - /// - /// Gets or sets the priority for keeping the cache entry in the cache during a - /// memory pressure triggered cleanup. The default is . - /// - public CacheItemPriority Priority { get; set; } = CacheItemPriority.Normal; } } \ No newline at end of file diff --git a/src/Microsoft.Extensions.Caching.Memory/CacheEntry.cs b/src/Microsoft.Extensions.Caching.Memory/CacheEntry.cs index 69732943..7206a557 100644 --- a/src/Microsoft.Extensions.Caching.Memory/CacheEntry.cs +++ b/src/Microsoft.Extensions.Caching.Memory/CacheEntry.cs @@ -147,12 +147,6 @@ public IList PostEvictionCallbacks } } - /// - /// Gets or sets the priority for keeping the cache entry in the cache during a - /// memory pressure triggered cleanup. The default is . - /// - public CacheItemPriority Priority { get; set; } = CacheItemPriority.Normal; - public object Key { get; private set; } public object Value { get; set; } diff --git a/src/Microsoft.Extensions.Caching.Memory/DefaultEvictionStrategy.cs b/src/Microsoft.Extensions.Caching.Memory/DefaultEvictionStrategy.cs deleted file mode 100644 index 8df73986..00000000 --- a/src/Microsoft.Extensions.Caching.Memory/DefaultEvictionStrategy.cs +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Microsoft.Extensions.Caching.Memory -{ - public class DefaultMemoryCacheEvictionStrategy : IMemoryCacheEvictionStrategy - { - /// Remove at least the given percentage (0.10 for 10%) of the total entries (or estimated memory?), according to the following policy: - /// 1. Remove all expired items. - /// 2. Bucket by CacheItemPriority. - /// 3. Least recently used objects. - /// ?. Items with the soonest absolute expiration. - /// ?. Items with the soonest sliding expiration. - /// ?. Larger objects - estimated by object graph size, inaccurate. - public IEnumerable GetEntriesToEvict(IEnumerable entries, DateTimeOffset now) - { - // For testing, only remove expired entries - var percentage = 0D; - - var entriesToRemove = new List(); - var lowPriEntries = new List(); - var normalPriEntries = new List(); - var highPriEntries = new List(); - - // Sort items by expired & priority status - foreach (var entry in entries) - { - if (entry.CheckExpired(now)) - { - entriesToRemove.Add(entry); - } - else - { - switch (entry.Priority) - { - case CacheItemPriority.Low: - lowPriEntries.Add(entry); - break; - case CacheItemPriority.Normal: - normalPriEntries.Add(entry); - break; - case CacheItemPriority.High: - highPriEntries.Add(entry); - break; - case CacheItemPriority.NeverRemove: - break; - default: - throw new NotSupportedException("Not implemented: " + entry.Priority); - } - } - } - - int removalCountTarget = (int)(entries.Count() * percentage); - - ExpirePriorityBucket(removalCountTarget, entriesToRemove, lowPriEntries); - ExpirePriorityBucket(removalCountTarget, entriesToRemove, normalPriEntries); - ExpirePriorityBucket(removalCountTarget, entriesToRemove, highPriEntries); - - return entriesToRemove; - } - - /// Policy: - /// 1. Least recently used objects. - /// ?. Items with the soonest absolute expiration. - /// ?. Items with the soonest sliding expiration. - /// ?. Larger objects - estimated by object graph size, inaccurate. - private void ExpirePriorityBucket(int removalCountTarget, List entriesToRemove, List priorityEntries) - { - // Do we meet our quota by just removing expired entries? - if (removalCountTarget <= entriesToRemove.Count) - { - // No-op, we've met quota - return; - } - if (entriesToRemove.Count + priorityEntries.Count <= removalCountTarget) - { - // Expire all of the entries in this bucket - foreach (var entry in priorityEntries) - { - entry.SetExpired(EvictionReason.Capacity); - } - entriesToRemove.AddRange(priorityEntries); - return; - } - - // Expire enough entries to reach our goal - // TODO: Refine policy - - // LRU - foreach (var entry in priorityEntries.OrderBy(entry => entry.LastAccessed)) - { - entry.SetExpired(EvictionReason.Capacity); - entriesToRemove.Add(entry); - if (removalCountTarget <= entriesToRemove.Count) - { - break; - } - } - } - } -} diff --git a/src/Microsoft.Extensions.Caching.Memory/DefaultMemoryCacheEvictionStrategy.cs b/src/Microsoft.Extensions.Caching.Memory/DefaultMemoryCacheEvictionStrategy.cs new file mode 100644 index 00000000..d0d40d5a --- /dev/null +++ b/src/Microsoft.Extensions.Caching.Memory/DefaultMemoryCacheEvictionStrategy.cs @@ -0,0 +1,27 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; + +namespace Microsoft.Extensions.Caching.Memory +{ + // Just remove expired entries + public class DefaultMemoryCacheEvictionStrategy : IMemoryCacheEvictionStrategy + { + public IEnumerable GetEntriesToEvict(IEnumerable entries, DateTimeOffset now) + { + var entriesToEvict = new List(); + + foreach (var entry in entries) + { + if (entry.CheckExpired(now)) + { + entriesToEvict.Add(entry); + } + } + + return entriesToEvict; + } + } +} diff --git a/src/Microsoft.Extensions.Caching.Memory/LRUMemoryCacheEvictionStrategy.cs b/src/Microsoft.Extensions.Caching.Memory/LRUMemoryCacheEvictionStrategy.cs new file mode 100644 index 00000000..0bd4e830 --- /dev/null +++ b/src/Microsoft.Extensions.Caching.Memory/LRUMemoryCacheEvictionStrategy.cs @@ -0,0 +1,46 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.Extensions.Caching.Memory +{ + // LRU + public class LRUMemoryCacheEvictionStrategy : IMemoryCacheEvictionStrategy + { + private readonly int MaximumEntries; + + public LRUMemoryCacheEvictionStrategy(int maximumEntries) + { + MaximumEntries = maximumEntries; + } + + public IEnumerable GetEntriesToEvict(IEnumerable entries, DateTimeOffset now) + { + // Remove expired items first + + var removalTarget = entries.Count() - MaximumEntries; + + if (removalTarget <= 0) + { + return Enumerable.Empty(); + } + + var entriesToEvict = new List(); + + foreach (var entry in entries.OrderBy(e => e.LastAccessed)) + { + if (entriesToEvict.Count > removalTarget) + { + break; + } + entry.SetExpired(EvictionReason.Capacity); + entriesToEvict.Add(entry); + } + + return entriesToEvict; + } + } +} From 479809ba4b55c6439fa95e1978921c56b529498f Mon Sep 17 00:00:00 2001 From: John Luo Date: Thu, 9 Mar 2017 15:46:45 -0800 Subject: [PATCH 05/22] Relocate abstraction to Caching.Memory package --- .../DefaultMemoryCacheEvictionStrategy.cs | 2 +- .../IMemoryCacheEvictionStrategy.cs | 4 ++-- .../LRUMemoryCacheEvictionStrategy.cs | 2 +- src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs | 2 +- src/Microsoft.Extensions.Caching.Memory/MemoryCacheOptions.cs | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) rename src/{Microsoft.Extensions.Caching.Abstractions => Microsoft.Extensions.Caching.Memory}/IMemoryCacheEvictionStrategy.cs (63%) diff --git a/src/Microsoft.Extensions.Caching.Memory/DefaultMemoryCacheEvictionStrategy.cs b/src/Microsoft.Extensions.Caching.Memory/DefaultMemoryCacheEvictionStrategy.cs index d0d40d5a..c976aa64 100644 --- a/src/Microsoft.Extensions.Caching.Memory/DefaultMemoryCacheEvictionStrategy.cs +++ b/src/Microsoft.Extensions.Caching.Memory/DefaultMemoryCacheEvictionStrategy.cs @@ -7,7 +7,7 @@ namespace Microsoft.Extensions.Caching.Memory { // Just remove expired entries - public class DefaultMemoryCacheEvictionStrategy : IMemoryCacheEvictionStrategy + public class DefaultMemoryCacheEvictionStrategy : IMemoryCacheEvictionStrategy { public IEnumerable GetEntriesToEvict(IEnumerable entries, DateTimeOffset now) { diff --git a/src/Microsoft.Extensions.Caching.Abstractions/IMemoryCacheEvictionStrategy.cs b/src/Microsoft.Extensions.Caching.Memory/IMemoryCacheEvictionStrategy.cs similarity index 63% rename from src/Microsoft.Extensions.Caching.Abstractions/IMemoryCacheEvictionStrategy.cs rename to src/Microsoft.Extensions.Caching.Memory/IMemoryCacheEvictionStrategy.cs index 04dd35a5..16221f67 100644 --- a/src/Microsoft.Extensions.Caching.Abstractions/IMemoryCacheEvictionStrategy.cs +++ b/src/Microsoft.Extensions.Caching.Memory/IMemoryCacheEvictionStrategy.cs @@ -6,9 +6,9 @@ namespace Microsoft.Extensions.Caching.Memory { - public interface IMemoryCacheEvictionStrategy where TCacheEntry : ICacheEntry + public interface IMemoryCacheEvictionStrategy { // doc comments, returns a list of entries entry to evict - IEnumerable GetEntriesToEvict(IEnumerable entries, DateTimeOffset now); + IEnumerable GetEntriesToEvict(IEnumerable entries, DateTimeOffset now); } } diff --git a/src/Microsoft.Extensions.Caching.Memory/LRUMemoryCacheEvictionStrategy.cs b/src/Microsoft.Extensions.Caching.Memory/LRUMemoryCacheEvictionStrategy.cs index 0bd4e830..31011cff 100644 --- a/src/Microsoft.Extensions.Caching.Memory/LRUMemoryCacheEvictionStrategy.cs +++ b/src/Microsoft.Extensions.Caching.Memory/LRUMemoryCacheEvictionStrategy.cs @@ -8,7 +8,7 @@ namespace Microsoft.Extensions.Caching.Memory { // LRU - public class LRUMemoryCacheEvictionStrategy : IMemoryCacheEvictionStrategy + public class LRUMemoryCacheEvictionStrategy : IMemoryCacheEvictionStrategy { private readonly int MaximumEntries; diff --git a/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs index 199987dd..c1430bd3 100644 --- a/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs @@ -26,7 +26,7 @@ public class MemoryCache : IMemoryCache private readonly Action _entryExpirationNotification; private readonly ISystemClock _clock; - private readonly IMemoryCacheEvictionStrategy _evictionStrategy; + private readonly IMemoryCacheEvictionStrategy _evictionStrategy; private TimeSpan _expirationScanFrequency; private DateTimeOffset _lastExpirationScan; diff --git a/src/Microsoft.Extensions.Caching.Memory/MemoryCacheOptions.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheOptions.cs index 7fa920a8..92514e1f 100644 --- a/src/Microsoft.Extensions.Caching.Memory/MemoryCacheOptions.cs +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheOptions.cs @@ -14,7 +14,7 @@ public class MemoryCacheOptions : IOptions // May no longer be needed public TimeSpan ExpirationScanFrequency { get; set; } = TimeSpan.FromMinutes(1); - public IMemoryCacheEvictionStrategy EvictionStrategy { get; set; } + public IMemoryCacheEvictionStrategy EvictionStrategy { get; set; } MemoryCacheOptions IOptions.Value { From b11dfa06d21685abb1e95f9289fe29e3ebaffb00 Mon Sep 17 00:00:00 2001 From: John Luo Date: Thu, 9 Mar 2017 18:33:26 -0800 Subject: [PATCH 06/22] Add default time based trigger --- .../DefaultMemoryEvictionTrigger.cs | 94 +++++++++++++++++++ .../IMemoryCacheEvictionTrigger.cs | 17 ++++ .../MemoryCache.cs | 35 ++++--- .../MemoryCacheOptions.cs | 2 + 4 files changed, 133 insertions(+), 15 deletions(-) create mode 100644 src/Microsoft.Extensions.Caching.Memory/DefaultMemoryEvictionTrigger.cs create mode 100644 src/Microsoft.Extensions.Caching.Memory/IMemoryCacheEvictionTrigger.cs diff --git a/src/Microsoft.Extensions.Caching.Memory/DefaultMemoryEvictionTrigger.cs b/src/Microsoft.Extensions.Caching.Memory/DefaultMemoryEvictionTrigger.cs new file mode 100644 index 00000000..4f5b6bcc --- /dev/null +++ b/src/Microsoft.Extensions.Caching.Memory/DefaultMemoryEvictionTrigger.cs @@ -0,0 +1,94 @@ +using System; +using System.Threading; + +namespace Microsoft.Extensions.Caching.Memory +{ + public class DefaultMemoryEvictionTrigger : IMemoryCacheEvictionTrigger + { + // Might be overly strict with locking, need analysis + private readonly object _lock = new object(); + private readonly TimeSpan _evictionInterval; + private readonly int _intervalsWithoutEvictionUntilIdle; + + private volatile bool _isDisposed; + private volatile bool _timerIsRunning; + private int _intervalsWithoutEviction; + private Timer _timer; + + public DefaultMemoryEvictionTrigger() + : this(TimeSpan.FromSeconds(2), 3) // for testing + { } + + public DefaultMemoryEvictionTrigger(TimeSpan evictionInterval, int intervalsWithoutEvictionUntilIdel) + { + _evictionInterval = evictionInterval; + _intervalsWithoutEvictionUntilIdle = intervalsWithoutEvictionUntilIdel; + _timer = new Timer(TimerLoop, null, Timeout.Infinite, Timeout.Infinite); + } + + public Func EvictionCallback { get; set; } + + public void Dispose() + { + if (!_isDisposed) + { + _isDisposed = true; + + lock (_lock) + { + if (_timer != null) + { + _timerIsRunning = false; + _timer.Dispose(); + _timer = null; + } + } + } + } + + public void Start() + { + if (!_isDisposed && !_timerIsRunning) + { + lock (_lock) + { + if (!_timerIsRunning && _timer != null) + { + _timerIsRunning = true; + _timer.Change(_evictionInterval, _evictionInterval); + Interlocked.Exchange(ref _intervalsWithoutEviction, 0); + } + } + } + } + + public void Stop() + { + if (!_isDisposed && _timerIsRunning) + { + lock (_lock) + { + _timerIsRunning = false; + _timer.Change(Timeout.Infinite, Timeout.Infinite); + } + } + } + + private void TimerLoop(object state) + { + if (EvictionCallback()) + { + Interlocked.Exchange(ref _intervalsWithoutEviction, 0); + } + else + { + Interlocked.Increment(ref _intervalsWithoutEviction); + } + + if (Volatile.Read(ref _intervalsWithoutEviction) >= _intervalsWithoutEvictionUntilIdle) + { + Stop(); + } + } + } +} diff --git a/src/Microsoft.Extensions.Caching.Memory/IMemoryCacheEvictionTrigger.cs b/src/Microsoft.Extensions.Caching.Memory/IMemoryCacheEvictionTrigger.cs new file mode 100644 index 00000000..fb65e98f --- /dev/null +++ b/src/Microsoft.Extensions.Caching.Memory/IMemoryCacheEvictionTrigger.cs @@ -0,0 +1,17 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.Extensions.Caching.Memory +{ + public interface IMemoryCacheEvictionTrigger : IDisposable + { + // doc comments + Func EvictionCallback { get; set; } + + void Start(); + + void Stop(); + } +} diff --git a/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs index c1430bd3..47613d89 100644 --- a/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs @@ -4,8 +4,8 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Linq; using System.Threading; -using System.Threading.Tasks; using Microsoft.Extensions.Internal; using Microsoft.Extensions.Options; @@ -27,12 +27,11 @@ public class MemoryCache : IMemoryCache private readonly ISystemClock _clock; private readonly IMemoryCacheEvictionStrategy _evictionStrategy; + private readonly IMemoryCacheEvictionTrigger _evictionTrigger; private TimeSpan _expirationScanFrequency; private DateTimeOffset _lastExpirationScan; - private Timer _evictionTimer; - /// /// Creates a new instance. /// @@ -51,10 +50,10 @@ public MemoryCache(IOptions optionsAccessor) _clock = options.Clock ?? new SystemClock(); _evictionStrategy = options.EvictionStrategy ?? new DefaultMemoryCacheEvictionStrategy(); + _evictionTrigger = options.EvictionTrigger ?? new DefaultMemoryEvictionTrigger(); + _evictionTrigger.EvictionCallback = ExecuteCacheEviction; _expirationScanFrequency = options.ExpirationScanFrequency; _lastExpirationScan = _clock.UtcNow; - - _evictionTimer = new Timer(ExecuteCacheEviction, null, dueTime: 0, period: 1000); //due time and period are set for testing } /// @@ -252,12 +251,16 @@ private void EntryExpired(CacheEntry entry) StartScanForExpiredItems(); } - private void ExecuteCacheEviction(object state) + private bool ExecuteCacheEviction() { - foreach (var entry in _evictionStrategy.GetEntriesToEvict(_entries.Values, _clock.UtcNow)) + Console.WriteLine("Executing eviction logic"); + var entriesToEvict = _evictionStrategy.GetEntriesToEvict(_entries.Values, _clock.UtcNow); + foreach (var entry in entriesToEvict) { + Console.WriteLine($"Evicting {entry.Key} - {entry.Value}"); RemoveEntry(entry); } + return entriesToEvict.Any(); } #region Replace with eviction logic? @@ -266,14 +269,16 @@ private void ExecuteCacheEviction(object state) // If sufficient time has elapsed then a scan is initiated on a background task. private void StartScanForExpiredItems() { - var now = _clock.UtcNow; - if (_expirationScanFrequency < now - _lastExpirationScan) - { - _lastExpirationScan = now; - // Disabled for testing - //Task.Factory.StartNew(state => ScanForExpiredItems((MemoryCache)state), this, - // CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); - } + Console.WriteLine("Starting trigger"); + _evictionTrigger.Start(); + //var now = _clock.UtcNow; + //if (_expirationScanFrequency < now - _lastExpirationScan) + //{ + // _lastExpirationScan = now; + // // Disabled for testing + // //Task.Factory.StartNew(state => ScanForExpiredItems((MemoryCache)state), this, + // // CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); + //} } private static void ScanForExpiredItems(MemoryCache cache) diff --git a/src/Microsoft.Extensions.Caching.Memory/MemoryCacheOptions.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheOptions.cs index 92514e1f..47219780 100644 --- a/src/Microsoft.Extensions.Caching.Memory/MemoryCacheOptions.cs +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheOptions.cs @@ -16,6 +16,8 @@ public class MemoryCacheOptions : IOptions public IMemoryCacheEvictionStrategy EvictionStrategy { get; set; } + public IMemoryCacheEvictionTrigger EvictionTrigger { get; set; } + MemoryCacheOptions IOptions.Value { get { return this; } From 9759c5f4ef56cfe14f9b2375d94e81766e98118f Mon Sep 17 00:00:00 2001 From: John Luo Date: Thu, 9 Mar 2017 19:39:07 -0800 Subject: [PATCH 07/22] Remove duplicate expiration scans --- .../DefaultMemoryEvictionTrigger.cs | 2 +- .../IMemoryCacheEvictionTrigger.cs | 2 +- .../MemoryCache.cs | 43 ++----------------- 3 files changed, 6 insertions(+), 41 deletions(-) diff --git a/src/Microsoft.Extensions.Caching.Memory/DefaultMemoryEvictionTrigger.cs b/src/Microsoft.Extensions.Caching.Memory/DefaultMemoryEvictionTrigger.cs index 4f5b6bcc..4e8f01e2 100644 --- a/src/Microsoft.Extensions.Caching.Memory/DefaultMemoryEvictionTrigger.cs +++ b/src/Microsoft.Extensions.Caching.Memory/DefaultMemoryEvictionTrigger.cs @@ -46,7 +46,7 @@ public void Dispose() } } - public void Start() + public void Resume() { if (!_isDisposed && !_timerIsRunning) { diff --git a/src/Microsoft.Extensions.Caching.Memory/IMemoryCacheEvictionTrigger.cs b/src/Microsoft.Extensions.Caching.Memory/IMemoryCacheEvictionTrigger.cs index fb65e98f..0ea47371 100644 --- a/src/Microsoft.Extensions.Caching.Memory/IMemoryCacheEvictionTrigger.cs +++ b/src/Microsoft.Extensions.Caching.Memory/IMemoryCacheEvictionTrigger.cs @@ -10,7 +10,7 @@ public interface IMemoryCacheEvictionTrigger : IDisposable // doc comments Func EvictionCallback { get; set; } - void Start(); + void Resume(); void Stop(); } diff --git a/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs index 47613d89..3f533b5f 100644 --- a/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs @@ -5,7 +5,6 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; -using System.Threading; using Microsoft.Extensions.Internal; using Microsoft.Extensions.Options; @@ -173,7 +172,7 @@ private void SetEntry(CacheEntry entry) } } - StartScanForExpiredItems(); + _evictionTrigger.Resume(); } /// @@ -212,7 +211,7 @@ public bool TryGetValue(object key, out object result) } } - StartScanForExpiredItems(); + _evictionTrigger.Resume(); return found; } @@ -233,7 +232,7 @@ public void Remove(object key) entry.InvokeEvictionCallbacks(); } - StartScanForExpiredItems(); + _evictionTrigger.Resume(); } private void RemoveEntry(CacheEntry entry) @@ -248,53 +247,19 @@ private void EntryExpired(CacheEntry entry) { // TODO: For efficiency consider processing these expirations in batches. RemoveEntry(entry); - StartScanForExpiredItems(); + _evictionTrigger.Resume(); } private bool ExecuteCacheEviction() { - Console.WriteLine("Executing eviction logic"); var entriesToEvict = _evictionStrategy.GetEntriesToEvict(_entries.Values, _clock.UtcNow); foreach (var entry in entriesToEvict) { - Console.WriteLine($"Evicting {entry.Key} - {entry.Value}"); RemoveEntry(entry); } return entriesToEvict.Any(); } -#region Replace with eviction logic? - - // Called by multiple actions to see how long it's been since we last checked for expired items. - // If sufficient time has elapsed then a scan is initiated on a background task. - private void StartScanForExpiredItems() - { - Console.WriteLine("Starting trigger"); - _evictionTrigger.Start(); - //var now = _clock.UtcNow; - //if (_expirationScanFrequency < now - _lastExpirationScan) - //{ - // _lastExpirationScan = now; - // // Disabled for testing - // //Task.Factory.StartNew(state => ScanForExpiredItems((MemoryCache)state), this, - // // CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); - //} - } - - private static void ScanForExpiredItems(MemoryCache cache) - { - var now = cache._clock.UtcNow; - foreach (var entry in cache._entries.Values) - { - if (entry.CheckExpired(now)) - { - cache.RemoveEntry(entry); - } - } - } - -#endregion - public void Dispose() { Dispose(true); From 8f50c207576969751ac11193e60291adb4cdaaf8 Mon Sep 17 00:00:00 2001 From: John Luo Date: Thu, 9 Mar 2017 19:41:08 -0800 Subject: [PATCH 08/22] typo --- .../DefaultMemoryEvictionTrigger.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.Extensions.Caching.Memory/DefaultMemoryEvictionTrigger.cs b/src/Microsoft.Extensions.Caching.Memory/DefaultMemoryEvictionTrigger.cs index 4e8f01e2..5a140000 100644 --- a/src/Microsoft.Extensions.Caching.Memory/DefaultMemoryEvictionTrigger.cs +++ b/src/Microsoft.Extensions.Caching.Memory/DefaultMemoryEvictionTrigger.cs @@ -19,10 +19,10 @@ public DefaultMemoryEvictionTrigger() : this(TimeSpan.FromSeconds(2), 3) // for testing { } - public DefaultMemoryEvictionTrigger(TimeSpan evictionInterval, int intervalsWithoutEvictionUntilIdel) + public DefaultMemoryEvictionTrigger(TimeSpan evictionInterval, int intervalsWithoutEvictionUntilIdle) { _evictionInterval = evictionInterval; - _intervalsWithoutEvictionUntilIdle = intervalsWithoutEvictionUntilIdel; + _intervalsWithoutEvictionUntilIdle = intervalsWithoutEvictionUntilIdle; _timer = new Timer(TimerLoop, null, Timeout.Infinite, Timeout.Infinite); } From 9903c59e14d6d449327867e60479fa6d8c97ffe8 Mon Sep 17 00:00:00 2001 From: John Luo Date: Thu, 9 Mar 2017 19:51:13 -0800 Subject: [PATCH 09/22] Remove redundant options and make DefaultEvictionStrategy virtual --- .../DefaultMemoryCacheEvictionStrategy.cs | 2 +- .../LRUMemoryCacheEvictionStrategy.cs | 19 +++++++++---------- .../MemoryCache.cs | 5 ----- .../MemoryCacheOptions.cs | 3 --- 4 files changed, 10 insertions(+), 19 deletions(-) diff --git a/src/Microsoft.Extensions.Caching.Memory/DefaultMemoryCacheEvictionStrategy.cs b/src/Microsoft.Extensions.Caching.Memory/DefaultMemoryCacheEvictionStrategy.cs index c976aa64..62de0195 100644 --- a/src/Microsoft.Extensions.Caching.Memory/DefaultMemoryCacheEvictionStrategy.cs +++ b/src/Microsoft.Extensions.Caching.Memory/DefaultMemoryCacheEvictionStrategy.cs @@ -9,7 +9,7 @@ namespace Microsoft.Extensions.Caching.Memory // Just remove expired entries public class DefaultMemoryCacheEvictionStrategy : IMemoryCacheEvictionStrategy { - public IEnumerable GetEntriesToEvict(IEnumerable entries, DateTimeOffset now) + public virtual IEnumerable GetEntriesToEvict(IEnumerable entries, DateTimeOffset now) { var entriesToEvict = new List(); diff --git a/src/Microsoft.Extensions.Caching.Memory/LRUMemoryCacheEvictionStrategy.cs b/src/Microsoft.Extensions.Caching.Memory/LRUMemoryCacheEvictionStrategy.cs index 31011cff..7521a772 100644 --- a/src/Microsoft.Extensions.Caching.Memory/LRUMemoryCacheEvictionStrategy.cs +++ b/src/Microsoft.Extensions.Caching.Memory/LRUMemoryCacheEvictionStrategy.cs @@ -8,7 +8,7 @@ namespace Microsoft.Extensions.Caching.Memory { // LRU - public class LRUMemoryCacheEvictionStrategy : IMemoryCacheEvictionStrategy + public class LRUMemoryCacheEvictionStrategy : DefaultMemoryCacheEvictionStrategy { private readonly int MaximumEntries; @@ -17,30 +17,29 @@ public LRUMemoryCacheEvictionStrategy(int maximumEntries) MaximumEntries = maximumEntries; } - public IEnumerable GetEntriesToEvict(IEnumerable entries, DateTimeOffset now) + public override IEnumerable GetEntriesToEvict(IEnumerable entries, DateTimeOffset now) { - // Remove expired items first - + var expiredEntries = base.GetEntriesToEvict(entries, now); var removalTarget = entries.Count() - MaximumEntries; - if (removalTarget <= 0) + if (removalTarget <= expiredEntries.Count()) { - return Enumerable.Empty(); + return expiredEntries; } - var entriesToEvict = new List(); + var addtionalEntriesToEvict = new List(expiredEntries); foreach (var entry in entries.OrderBy(e => e.LastAccessed)) { - if (entriesToEvict.Count > removalTarget) + if (addtionalEntriesToEvict.Count > removalTarget) { break; } entry.SetExpired(EvictionReason.Capacity); - entriesToEvict.Add(entry); + addtionalEntriesToEvict.Add(entry); } - return entriesToEvict; + return addtionalEntriesToEvict; } } } diff --git a/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs index 3f533b5f..3a1e1e1a 100644 --- a/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs @@ -28,9 +28,6 @@ public class MemoryCache : IMemoryCache private readonly IMemoryCacheEvictionStrategy _evictionStrategy; private readonly IMemoryCacheEvictionTrigger _evictionTrigger; - private TimeSpan _expirationScanFrequency; - private DateTimeOffset _lastExpirationScan; - /// /// Creates a new instance. /// @@ -51,8 +48,6 @@ public MemoryCache(IOptions optionsAccessor) _evictionStrategy = options.EvictionStrategy ?? new DefaultMemoryCacheEvictionStrategy(); _evictionTrigger = options.EvictionTrigger ?? new DefaultMemoryEvictionTrigger(); _evictionTrigger.EvictionCallback = ExecuteCacheEviction; - _expirationScanFrequency = options.ExpirationScanFrequency; - _lastExpirationScan = _clock.UtcNow; } /// diff --git a/src/Microsoft.Extensions.Caching.Memory/MemoryCacheOptions.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheOptions.cs index 47219780..350cf4d7 100644 --- a/src/Microsoft.Extensions.Caching.Memory/MemoryCacheOptions.cs +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheOptions.cs @@ -11,9 +11,6 @@ public class MemoryCacheOptions : IOptions { public ISystemClock Clock { get; set; } - // May no longer be needed - public TimeSpan ExpirationScanFrequency { get; set; } = TimeSpan.FromMinutes(1); - public IMemoryCacheEvictionStrategy EvictionStrategy { get; set; } public IMemoryCacheEvictionTrigger EvictionTrigger { get; set; } From 3b9b1165fd7073a0a3fb4d0b2f34ce88420e4349 Mon Sep 17 00:00:00 2001 From: John Luo Date: Fri, 10 Mar 2017 15:29:39 -0800 Subject: [PATCH 10/22] Cleanup --- .../CacheEntry.cs | 11 +++--- .../DefaultMemoryCacheEvictionStrategy.cs | 11 ++++-- .../DefaultMemoryEvictionTrigger.cs | 9 +++-- .../IMemoryCacheEvictionStrategy.cs | 2 +- .../IMemoryCacheEvictionTrigger.cs | 2 +- .../LRUMemoryCacheEvictionStrategy.cs | 2 +- .../MemoryCache.cs | 12 +++---- .../CacheEntryScopeExpirationTests.cs | 34 +++++++++---------- .../TimeExpirationTests.cs | 1 + .../TokenExpirationTests.cs | 1 + 10 files changed, 48 insertions(+), 37 deletions(-) diff --git a/src/Microsoft.Extensions.Caching.Memory/CacheEntry.cs b/src/Microsoft.Extensions.Caching.Memory/CacheEntry.cs index 7206a557..fd0cad2a 100644 --- a/src/Microsoft.Extensions.Caching.Memory/CacheEntry.cs +++ b/src/Microsoft.Extensions.Caching.Memory/CacheEntry.cs @@ -19,9 +19,9 @@ public class CacheEntry : ICacheEntry private IList _postEvictionCallbacks; private bool _isExpired; - internal IList _expirationTokens; - internal DateTimeOffset? _absoluteExpiration; - internal TimeSpan? _absoluteExpirationRelativeToNow; + private IList _expirationTokens; + private DateTimeOffset? _absoluteExpiration; + private TimeSpan? _absoluteExpirationRelativeToNow; private TimeSpan? _slidingExpiration; private IDisposable _scope; @@ -151,7 +151,7 @@ public IList PostEvictionCallbacks public object Value { get; set; } - public DateTimeOffset LastAccessed { get; set; } + public DateTimeOffset LastAccessed { get; internal set; } internal EvictionReason EvictionReason { get; private set; } @@ -171,6 +171,7 @@ public bool CheckExpired(DateTimeOffset now) return _isExpired || CheckForExpiredTime(now) || CheckForExpiredTokens(); } + // TODO: expose this so custom strategies can set eviction reason? internal void SetExpired(EvictionReason reason) { if (EvictionReason == EvictionReason.None) @@ -199,7 +200,7 @@ private bool CheckForExpiredTime(DateTimeOffset now) return false; } - internal bool CheckForExpiredTokens() + private bool CheckForExpiredTokens() { if (_expirationTokens != null) { diff --git a/src/Microsoft.Extensions.Caching.Memory/DefaultMemoryCacheEvictionStrategy.cs b/src/Microsoft.Extensions.Caching.Memory/DefaultMemoryCacheEvictionStrategy.cs index 62de0195..d6119e83 100644 --- a/src/Microsoft.Extensions.Caching.Memory/DefaultMemoryCacheEvictionStrategy.cs +++ b/src/Microsoft.Extensions.Caching.Memory/DefaultMemoryCacheEvictionStrategy.cs @@ -3,25 +3,30 @@ using System; using System.Collections.Generic; +using System.Linq; namespace Microsoft.Extensions.Caching.Memory { - // Just remove expired entries public class DefaultMemoryCacheEvictionStrategy : IMemoryCacheEvictionStrategy { public virtual IEnumerable GetEntriesToEvict(IEnumerable entries, DateTimeOffset now) { - var entriesToEvict = new List(); + List entriesToEvict = null; foreach (var entry in entries) { if (entry.CheckExpired(now)) { + if (entriesToEvict == null) + { + entriesToEvict = new List(); + } + entriesToEvict.Add(entry); } } - return entriesToEvict; + return entriesToEvict ?? Enumerable.Empty(); } } } diff --git a/src/Microsoft.Extensions.Caching.Memory/DefaultMemoryEvictionTrigger.cs b/src/Microsoft.Extensions.Caching.Memory/DefaultMemoryEvictionTrigger.cs index 5a140000..1f0eec55 100644 --- a/src/Microsoft.Extensions.Caching.Memory/DefaultMemoryEvictionTrigger.cs +++ b/src/Microsoft.Extensions.Caching.Memory/DefaultMemoryEvictionTrigger.cs @@ -1,11 +1,14 @@ -using System; +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; using System.Threading; namespace Microsoft.Extensions.Caching.Memory { + // TODO: review disposable/locking logic public class DefaultMemoryEvictionTrigger : IMemoryCacheEvictionTrigger { - // Might be overly strict with locking, need analysis private readonly object _lock = new object(); private readonly TimeSpan _evictionInterval; private readonly int _intervalsWithoutEvictionUntilIdle; @@ -16,7 +19,7 @@ public class DefaultMemoryEvictionTrigger : IMemoryCacheEvictionTrigger private Timer _timer; public DefaultMemoryEvictionTrigger() - : this(TimeSpan.FromSeconds(2), 3) // for testing + : this(TimeSpan.FromMinutes(1), 10) // TODO: Need better defaults? { } public DefaultMemoryEvictionTrigger(TimeSpan evictionInterval, int intervalsWithoutEvictionUntilIdle) diff --git a/src/Microsoft.Extensions.Caching.Memory/IMemoryCacheEvictionStrategy.cs b/src/Microsoft.Extensions.Caching.Memory/IMemoryCacheEvictionStrategy.cs index 16221f67..322b9692 100644 --- a/src/Microsoft.Extensions.Caching.Memory/IMemoryCacheEvictionStrategy.cs +++ b/src/Microsoft.Extensions.Caching.Memory/IMemoryCacheEvictionStrategy.cs @@ -8,7 +8,7 @@ namespace Microsoft.Extensions.Caching.Memory { public interface IMemoryCacheEvictionStrategy { - // doc comments, returns a list of entries entry to evict + // TODO: doc comments IEnumerable GetEntriesToEvict(IEnumerable entries, DateTimeOffset now); } } diff --git a/src/Microsoft.Extensions.Caching.Memory/IMemoryCacheEvictionTrigger.cs b/src/Microsoft.Extensions.Caching.Memory/IMemoryCacheEvictionTrigger.cs index 0ea47371..6e1a2601 100644 --- a/src/Microsoft.Extensions.Caching.Memory/IMemoryCacheEvictionTrigger.cs +++ b/src/Microsoft.Extensions.Caching.Memory/IMemoryCacheEvictionTrigger.cs @@ -7,7 +7,7 @@ namespace Microsoft.Extensions.Caching.Memory { public interface IMemoryCacheEvictionTrigger : IDisposable { - // doc comments + // TODO: doc comments Func EvictionCallback { get; set; } void Resume(); diff --git a/src/Microsoft.Extensions.Caching.Memory/LRUMemoryCacheEvictionStrategy.cs b/src/Microsoft.Extensions.Caching.Memory/LRUMemoryCacheEvictionStrategy.cs index 7521a772..b84e143c 100644 --- a/src/Microsoft.Extensions.Caching.Memory/LRUMemoryCacheEvictionStrategy.cs +++ b/src/Microsoft.Extensions.Caching.Memory/LRUMemoryCacheEvictionStrategy.cs @@ -7,7 +7,7 @@ namespace Microsoft.Extensions.Caching.Memory { - // LRU + // TODO: remove this public class LRUMemoryCacheEvictionStrategy : DefaultMemoryCacheEvictionStrategy { private readonly int MaximumEntries; diff --git a/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs index 3a1e1e1a..99188a65 100644 --- a/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs @@ -91,13 +91,13 @@ private void SetEntry(CacheEntry entry) var utcNow = _clock.UtcNow; DateTimeOffset? absoluteExpiration = null; - if (entry._absoluteExpirationRelativeToNow.HasValue) + if (entry.AbsoluteExpirationRelativeToNow.HasValue) { - absoluteExpiration = utcNow + entry._absoluteExpirationRelativeToNow; + absoluteExpiration = utcNow + entry.AbsoluteExpirationRelativeToNow; } - else if (entry._absoluteExpiration.HasValue) + else if (entry.AbsoluteExpiration.HasValue) { - absoluteExpiration = entry._absoluteExpiration; + absoluteExpiration = entry.AbsoluteExpiration; } // Applying the option's absolute expiration only if it's not already smaller. @@ -105,9 +105,9 @@ private void SetEntry(CacheEntry entry) // it was set by cascading it to its parent. if (absoluteExpiration.HasValue) { - if (!entry._absoluteExpiration.HasValue || absoluteExpiration.Value < entry._absoluteExpiration.Value) + if (!entry.AbsoluteExpiration.HasValue || absoluteExpiration.Value < entry.AbsoluteExpiration.Value) { - entry._absoluteExpiration = absoluteExpiration; + entry.AbsoluteExpiration = absoluteExpiration; } } diff --git a/test/Microsoft.Extensions.Caching.Memory.Tests/CacheEntryScopeExpirationTests.cs b/test/Microsoft.Extensions.Caching.Memory.Tests/CacheEntryScopeExpirationTests.cs index 3ca069ad..5553ac14 100644 --- a/test/Microsoft.Extensions.Caching.Memory.Tests/CacheEntryScopeExpirationTests.cs +++ b/test/Microsoft.Extensions.Caching.Memory.Tests/CacheEntryScopeExpirationTests.cs @@ -41,8 +41,8 @@ public void SetPopulates_ExpirationTokens_IntoScopedLink() cache.Set(key, obj, new MemoryCacheEntryOptions().AddExpirationToken(expirationToken)); } - Assert.Equal(1, ((CacheEntry)entry)._expirationTokens.Count()); - Assert.Null(((CacheEntry)entry)._absoluteExpiration); + Assert.Equal(1, entry.ExpirationTokens.Count()); + Assert.Null(entry.AbsoluteExpiration); } [Fact] @@ -62,9 +62,9 @@ public void SetPopulates_AbsoluteExpiration_IntoScopeLink() cache.Set(key, obj, new MemoryCacheEntryOptions().SetAbsoluteExpiration(time)); } - Assert.Null(((CacheEntry)entry)._expirationTokens); - Assert.NotNull(((CacheEntry)entry)._absoluteExpiration); - Assert.Equal(time, ((CacheEntry)entry)._absoluteExpiration); + Assert.Empty(entry.ExpirationTokens); + Assert.NotNull(entry.AbsoluteExpiration); + Assert.Equal(time, entry.AbsoluteExpiration); } [Fact] @@ -339,8 +339,8 @@ public void GetWithImplicitLinkPopulatesExpirationTokens() Assert.Null(CacheEntryHelper.Current); - Assert.Equal(1, ((CacheEntry)entry)._expirationTokens.Count()); - Assert.Null(((CacheEntry)entry)._absoluteExpiration); + Assert.Equal(1, entry.ExpirationTokens.Count()); + Assert.Null(entry.AbsoluteExpiration); } [Fact] @@ -373,10 +373,10 @@ public void LinkContextsCanNest() Assert.Null(CacheEntryHelper.Current); - Assert.Equal(1, ((CacheEntry)entry1)._expirationTokens.Count()); - Assert.Null(((CacheEntry)entry1)._absoluteExpiration); - Assert.Equal(1, ((CacheEntry)entry)._expirationTokens.Count()); - Assert.Null(((CacheEntry)entry)._absoluteExpiration); + Assert.Equal(1, entry1.ExpirationTokens.Count()); + Assert.Null(entry1.AbsoluteExpiration); + Assert.Equal(1, entry.ExpirationTokens.Count()); + Assert.Null(entry.AbsoluteExpiration); } [Fact] @@ -410,13 +410,13 @@ public void NestedLinkContextsCanAggregate() } } - Assert.Equal(2, ((CacheEntry)entry1)._expirationTokens.Count()); - Assert.NotNull(((CacheEntry)entry1)._absoluteExpiration); - Assert.Equal(clock.UtcNow + TimeSpan.FromSeconds(10), ((CacheEntry)entry1)._absoluteExpiration); + Assert.Equal(2, entry1.ExpirationTokens.Count()); + Assert.NotNull(entry1.AbsoluteExpiration); + Assert.Equal(clock.UtcNow + TimeSpan.FromSeconds(10), entry1.AbsoluteExpiration); - Assert.Equal(1, ((CacheEntry)entry2)._expirationTokens.Count()); - Assert.NotNull(((CacheEntry)entry2)._absoluteExpiration); - Assert.Equal(clock.UtcNow + TimeSpan.FromSeconds(15), ((CacheEntry)entry2)._absoluteExpiration); + Assert.Equal(1, entry2.ExpirationTokens.Count()); + Assert.NotNull(entry2.AbsoluteExpiration); + Assert.Equal(clock.UtcNow + TimeSpan.FromSeconds(15), entry2.AbsoluteExpiration); } [Fact] diff --git a/test/Microsoft.Extensions.Caching.Memory.Tests/TimeExpirationTests.cs b/test/Microsoft.Extensions.Caching.Memory.Tests/TimeExpirationTests.cs index f4cbfd99..63afeb3c 100644 --- a/test/Microsoft.Extensions.Caching.Memory.Tests/TimeExpirationTests.cs +++ b/test/Microsoft.Extensions.Caching.Memory.Tests/TimeExpirationTests.cs @@ -16,6 +16,7 @@ private IMemoryCache CreateCache(ISystemClock clock) return new MemoryCache(new MemoryCacheOptions() { Clock = clock, + EvictionTrigger = new DefaultMemoryEvictionTrigger(TimeSpan.FromSeconds(1), 10) }); } diff --git a/test/Microsoft.Extensions.Caching.Memory.Tests/TokenExpirationTests.cs b/test/Microsoft.Extensions.Caching.Memory.Tests/TokenExpirationTests.cs index 9c36ab26..2fe69031 100644 --- a/test/Microsoft.Extensions.Caching.Memory.Tests/TokenExpirationTests.cs +++ b/test/Microsoft.Extensions.Caching.Memory.Tests/TokenExpirationTests.cs @@ -23,6 +23,7 @@ private IMemoryCache CreateCache(ISystemClock clock) return new MemoryCache(new MemoryCacheOptions() { Clock = clock, + EvictionTrigger = new DefaultMemoryEvictionTrigger(TimeSpan.FromSeconds(1), 10) }); } From a1b97028804bed0f71e576ce97522c16e5377696 Mon Sep 17 00:00:00 2001 From: John Luo Date: Wed, 15 Mar 2017 16:56:52 -0700 Subject: [PATCH 11/22] Untested WIP --- .../CacheEntry.cs | 7 ++-- .../DefaultMemoryCacheEvictionStrategy.cs | 32 -------------- .../IMemoryCacheEvictionStrategy.cs | 2 +- .../IMemoryCacheEvictionTrigger.cs | 0 .../Interfaces/IRetrievedCacheEntry.cs | 42 +++++++++++++++++++ .../LRUMemoryCacheEvictionStrategy.cs | 24 +++-------- .../MemoryCache.cs | 34 ++++++++++++--- .../MemoryCacheEvictionStrategy.cs | 13 ++++++ ...igger.cs => MemoryCacheEvictionTrigger.cs} | 6 +-- .../TimeExpirationTests.cs | 2 +- .../TokenExpirationTests.cs | 2 +- 11 files changed, 99 insertions(+), 65 deletions(-) delete mode 100644 src/Microsoft.Extensions.Caching.Memory/DefaultMemoryCacheEvictionStrategy.cs rename src/Microsoft.Extensions.Caching.Memory/{ => Interfaces}/IMemoryCacheEvictionStrategy.cs (77%) rename src/Microsoft.Extensions.Caching.Memory/{ => Interfaces}/IMemoryCacheEvictionTrigger.cs (100%) create mode 100644 src/Microsoft.Extensions.Caching.Memory/Interfaces/IRetrievedCacheEntry.cs create mode 100644 src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionStrategy.cs rename src/Microsoft.Extensions.Caching.Memory/{DefaultMemoryEvictionTrigger.cs => MemoryCacheEvictionTrigger.cs} (92%) diff --git a/src/Microsoft.Extensions.Caching.Memory/CacheEntry.cs b/src/Microsoft.Extensions.Caching.Memory/CacheEntry.cs index fd0cad2a..05403ee5 100644 --- a/src/Microsoft.Extensions.Caching.Memory/CacheEntry.cs +++ b/src/Microsoft.Extensions.Caching.Memory/CacheEntry.cs @@ -9,7 +9,7 @@ namespace Microsoft.Extensions.Caching.Memory { - public class CacheEntry : ICacheEntry + public class CacheEntry : ICacheEntry, IRetrievedCacheEntry { private bool _added = false; private static readonly Action ExpirationCallback = ExpirationTokensExpired; @@ -153,6 +153,8 @@ public IList PostEvictionCallbacks public DateTimeOffset LastAccessed { get; internal set; } + public bool IsExpired => _isExpired; + internal EvictionReason EvictionReason { get; private set; } public void Dispose() @@ -171,8 +173,7 @@ public bool CheckExpired(DateTimeOffset now) return _isExpired || CheckForExpiredTime(now) || CheckForExpiredTokens(); } - // TODO: expose this so custom strategies can set eviction reason? - internal void SetExpired(EvictionReason reason) + public void SetExpired(EvictionReason reason) { if (EvictionReason == EvictionReason.None) { diff --git a/src/Microsoft.Extensions.Caching.Memory/DefaultMemoryCacheEvictionStrategy.cs b/src/Microsoft.Extensions.Caching.Memory/DefaultMemoryCacheEvictionStrategy.cs deleted file mode 100644 index d6119e83..00000000 --- a/src/Microsoft.Extensions.Caching.Memory/DefaultMemoryCacheEvictionStrategy.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Microsoft.Extensions.Caching.Memory -{ - public class DefaultMemoryCacheEvictionStrategy : IMemoryCacheEvictionStrategy - { - public virtual IEnumerable GetEntriesToEvict(IEnumerable entries, DateTimeOffset now) - { - List entriesToEvict = null; - - foreach (var entry in entries) - { - if (entry.CheckExpired(now)) - { - if (entriesToEvict == null) - { - entriesToEvict = new List(); - } - - entriesToEvict.Add(entry); - } - } - - return entriesToEvict ?? Enumerable.Empty(); - } - } -} diff --git a/src/Microsoft.Extensions.Caching.Memory/IMemoryCacheEvictionStrategy.cs b/src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionStrategy.cs similarity index 77% rename from src/Microsoft.Extensions.Caching.Memory/IMemoryCacheEvictionStrategy.cs rename to src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionStrategy.cs index 322b9692..c1fd28da 100644 --- a/src/Microsoft.Extensions.Caching.Memory/IMemoryCacheEvictionStrategy.cs +++ b/src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionStrategy.cs @@ -9,6 +9,6 @@ namespace Microsoft.Extensions.Caching.Memory public interface IMemoryCacheEvictionStrategy { // TODO: doc comments - IEnumerable GetEntriesToEvict(IEnumerable entries, DateTimeOffset now); + void Evict(IList entries, DateTimeOffset now); } } diff --git a/src/Microsoft.Extensions.Caching.Memory/IMemoryCacheEvictionTrigger.cs b/src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionTrigger.cs similarity index 100% rename from src/Microsoft.Extensions.Caching.Memory/IMemoryCacheEvictionTrigger.cs rename to src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionTrigger.cs diff --git a/src/Microsoft.Extensions.Caching.Memory/Interfaces/IRetrievedCacheEntry.cs b/src/Microsoft.Extensions.Caching.Memory/Interfaces/IRetrievedCacheEntry.cs new file mode 100644 index 00000000..d4b554f5 --- /dev/null +++ b/src/Microsoft.Extensions.Caching.Memory/Interfaces/IRetrievedCacheEntry.cs @@ -0,0 +1,42 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.Extensions.Caching.Memory +{ + /// + /// Represents an entry retrieved from the implementation. + /// + public interface IRetrievedCacheEntry + { + /// + /// Gets the key of the cache entry. + /// + object Key { get; } + + /// + /// Gets the value of the cache entry. + /// + object Value { get; } + + /// + /// Gets the absolute expiration date of the cache entry. + /// + DateTimeOffset? AbsoluteExpiration { get; } + + /// + /// Gets how long a cache entry can be inactive (e.g. not accessed) before it will be removed. + /// This will not extend the entry lifetime beyond the absolute expiration (if set). + /// + TimeSpan? SlidingExpiration { get; } + + DateTimeOffset LastAccessed { get; } + + bool IsExpired { get; } + + bool CheckExpired(DateTimeOffset utcNow); + + void SetExpired(EvictionReason reason); + } +} \ No newline at end of file diff --git a/src/Microsoft.Extensions.Caching.Memory/LRUMemoryCacheEvictionStrategy.cs b/src/Microsoft.Extensions.Caching.Memory/LRUMemoryCacheEvictionStrategy.cs index b84e143c..a8a0a243 100644 --- a/src/Microsoft.Extensions.Caching.Memory/LRUMemoryCacheEvictionStrategy.cs +++ b/src/Microsoft.Extensions.Caching.Memory/LRUMemoryCacheEvictionStrategy.cs @@ -8,7 +8,7 @@ namespace Microsoft.Extensions.Caching.Memory { // TODO: remove this - public class LRUMemoryCacheEvictionStrategy : DefaultMemoryCacheEvictionStrategy + public class LRUMemoryCacheEvictionStrategy : IMemoryCacheEvictionStrategy { private readonly int MaximumEntries; @@ -17,29 +17,17 @@ public LRUMemoryCacheEvictionStrategy(int maximumEntries) MaximumEntries = maximumEntries; } - public override IEnumerable GetEntriesToEvict(IEnumerable entries, DateTimeOffset now) + public void Evict(IList entries, DateTimeOffset now) { - var expiredEntries = base.GetEntriesToEvict(entries, now); - var removalTarget = entries.Count() - MaximumEntries; + var removalTarget = entries.Count - MaximumEntries; - if (removalTarget <= expiredEntries.Count()) + if (removalTarget > 0) { - return expiredEntries; - } - - var addtionalEntriesToEvict = new List(expiredEntries); - - foreach (var entry in entries.OrderBy(e => e.LastAccessed)) - { - if (addtionalEntriesToEvict.Count > removalTarget) + foreach (var entry in entries.OrderBy(e => e.LastAccessed).Take(removalTarget)) { - break; + entry.SetExpired(EvictionReason.Capacity); } - entry.SetExpired(EvictionReason.Capacity); - addtionalEntriesToEvict.Add(entry); } - - return addtionalEntriesToEvict; } } } diff --git a/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs index 99188a65..730070cb 100644 --- a/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs @@ -45,8 +45,8 @@ public MemoryCache(IOptions optionsAccessor) _entryExpirationNotification = EntryExpired; _clock = options.Clock ?? new SystemClock(); - _evictionStrategy = options.EvictionStrategy ?? new DefaultMemoryCacheEvictionStrategy(); - _evictionTrigger = options.EvictionTrigger ?? new DefaultMemoryEvictionTrigger(); + _evictionStrategy = options.EvictionStrategy ?? new MemoryCacheEvictionStrategy(); + _evictionTrigger = options.EvictionTrigger ?? new MemoryCacheEvictionTrigger(); _evictionTrigger.EvictionCallback = ExecuteCacheEviction; } @@ -247,12 +247,34 @@ private void EntryExpired(CacheEntry entry) private bool ExecuteCacheEviction() { - var entriesToEvict = _evictionStrategy.GetEntriesToEvict(_entries.Values, _clock.UtcNow); - foreach (var entry in entriesToEvict) + var evictedEntries = false; + var utcNow = _clock.UtcNow; + var freshEntries = new List(); + + // TODO: evaluate the perf overhead of enumerators vs taking a snapshot + foreach (var entry in _entries) { - RemoveEntry(entry); + if (!entry.Value.CheckExpired(utcNow)) + { + freshEntries.Add(entry.Value); + } } - return entriesToEvict.Any(); + + _evictionStrategy.Evict(freshEntries, utcNow); // TODO: anything else eviction strategies need? + + foreach (var entry in _entries) + { + if (entry.Value.IsExpired) + { + if (evictedEntries == false) + { + evictedEntries = true; + } + RemoveEntry(entry.Value); + } + } + + return evictedEntries; } public void Dispose() diff --git a/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionStrategy.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionStrategy.cs new file mode 100644 index 00000000..f16c3afc --- /dev/null +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionStrategy.cs @@ -0,0 +1,13 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; + +namespace Microsoft.Extensions.Caching.Memory +{ + public class MemoryCacheEvictionStrategy : IMemoryCacheEvictionStrategy + { + public void Evict(IList entries, DateTimeOffset now) { } + } +} diff --git a/src/Microsoft.Extensions.Caching.Memory/DefaultMemoryEvictionTrigger.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionTrigger.cs similarity index 92% rename from src/Microsoft.Extensions.Caching.Memory/DefaultMemoryEvictionTrigger.cs rename to src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionTrigger.cs index 1f0eec55..3e0e197b 100644 --- a/src/Microsoft.Extensions.Caching.Memory/DefaultMemoryEvictionTrigger.cs +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionTrigger.cs @@ -7,7 +7,7 @@ namespace Microsoft.Extensions.Caching.Memory { // TODO: review disposable/locking logic - public class DefaultMemoryEvictionTrigger : IMemoryCacheEvictionTrigger + public class MemoryCacheEvictionTrigger : IMemoryCacheEvictionTrigger { private readonly object _lock = new object(); private readonly TimeSpan _evictionInterval; @@ -18,11 +18,11 @@ public class DefaultMemoryEvictionTrigger : IMemoryCacheEvictionTrigger private int _intervalsWithoutEviction; private Timer _timer; - public DefaultMemoryEvictionTrigger() + public MemoryCacheEvictionTrigger() : this(TimeSpan.FromMinutes(1), 10) // TODO: Need better defaults? { } - public DefaultMemoryEvictionTrigger(TimeSpan evictionInterval, int intervalsWithoutEvictionUntilIdle) + public MemoryCacheEvictionTrigger(TimeSpan evictionInterval, int intervalsWithoutEvictionUntilIdle) { _evictionInterval = evictionInterval; _intervalsWithoutEvictionUntilIdle = intervalsWithoutEvictionUntilIdle; diff --git a/test/Microsoft.Extensions.Caching.Memory.Tests/TimeExpirationTests.cs b/test/Microsoft.Extensions.Caching.Memory.Tests/TimeExpirationTests.cs index 63afeb3c..ddf3518d 100644 --- a/test/Microsoft.Extensions.Caching.Memory.Tests/TimeExpirationTests.cs +++ b/test/Microsoft.Extensions.Caching.Memory.Tests/TimeExpirationTests.cs @@ -16,7 +16,7 @@ private IMemoryCache CreateCache(ISystemClock clock) return new MemoryCache(new MemoryCacheOptions() { Clock = clock, - EvictionTrigger = new DefaultMemoryEvictionTrigger(TimeSpan.FromSeconds(1), 10) + EvictionTrigger = new MemoryCacheEvictionTrigger(TimeSpan.FromSeconds(1), 10) }); } diff --git a/test/Microsoft.Extensions.Caching.Memory.Tests/TokenExpirationTests.cs b/test/Microsoft.Extensions.Caching.Memory.Tests/TokenExpirationTests.cs index 2fe69031..33ba50d0 100644 --- a/test/Microsoft.Extensions.Caching.Memory.Tests/TokenExpirationTests.cs +++ b/test/Microsoft.Extensions.Caching.Memory.Tests/TokenExpirationTests.cs @@ -23,7 +23,7 @@ private IMemoryCache CreateCache(ISystemClock clock) return new MemoryCache(new MemoryCacheOptions() { Clock = clock, - EvictionTrigger = new DefaultMemoryEvictionTrigger(TimeSpan.FromSeconds(1), 10) + EvictionTrigger = new MemoryCacheEvictionTrigger(TimeSpan.FromSeconds(1), 10) }); } From 7a1f2cd3a98ed946939f1299e010b2cb1902c9b0 Mon Sep 17 00:00:00 2001 From: John Luo Date: Wed, 15 Mar 2017 17:41:39 -0700 Subject: [PATCH 12/22] Non re-entrant timer --- .../MemoryCacheEvictionTrigger.cs | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionTrigger.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionTrigger.cs index 3e0e197b..8eb9cad3 100644 --- a/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionTrigger.cs +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionTrigger.cs @@ -16,6 +16,7 @@ public class MemoryCacheEvictionTrigger : IMemoryCacheEvictionTrigger private volatile bool _isDisposed; private volatile bool _timerIsRunning; private int _intervalsWithoutEviction; + private int _evictionRunning; private Timer _timer; public MemoryCacheEvictionTrigger() @@ -79,18 +80,23 @@ public void Stop() private void TimerLoop(object state) { - if (EvictionCallback()) + if (Interlocked.CompareExchange(ref _evictionRunning, 1, 0) == 0) { - Interlocked.Exchange(ref _intervalsWithoutEviction, 0); - } - else - { - Interlocked.Increment(ref _intervalsWithoutEviction); - } + if (EvictionCallback()) + { + Interlocked.Exchange(ref _intervalsWithoutEviction, 0); + } + else + { + Interlocked.Increment(ref _intervalsWithoutEviction); + } - if (Volatile.Read(ref _intervalsWithoutEviction) >= _intervalsWithoutEvictionUntilIdle) - { - Stop(); + if (Volatile.Read(ref _intervalsWithoutEviction) >= _intervalsWithoutEvictionUntilIdle) + { + Stop(); + } + + Interlocked.Exchange(ref _evictionRunning, 0); } } } From 4ec49dc9f639c44ef8806d2aa30e0be29a8afba9 Mon Sep 17 00:00:00 2001 From: John Luo Date: Thu, 16 Mar 2017 15:23:45 -0700 Subject: [PATCH 13/22] Add EvictionMetadata --- .../CacheEntryExtensions.cs | 1 + .../ICacheEntry.cs | 3 ++- .../MemoryCacheEntryExtensions.cs | 8 ++++++++ .../MemoryCacheEntryOptions.cs | 2 ++ src/Microsoft.Extensions.Caching.Memory/CacheEntry.cs | 4 +++- .../Interfaces/IRetrievedCacheEntry.cs | 2 ++ src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs | 3 ++- 7 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.Extensions.Caching.Abstractions/CacheEntryExtensions.cs b/src/Microsoft.Extensions.Caching.Abstractions/CacheEntryExtensions.cs index 150ccd5b..716f8954 100644 --- a/src/Microsoft.Extensions.Caching.Abstractions/CacheEntryExtensions.cs +++ b/src/Microsoft.Extensions.Caching.Abstractions/CacheEntryExtensions.cs @@ -136,6 +136,7 @@ public static ICacheEntry SetOptions(this ICacheEntry entry, MemoryCacheEntryOpt entry.AbsoluteExpiration = options.AbsoluteExpiration; entry.AbsoluteExpirationRelativeToNow = options.AbsoluteExpirationRelativeToNow; entry.SlidingExpiration = options.SlidingExpiration; + entry.EvictionMetadata = options.EvictionMetadata; foreach (var expirationToken in options.ExpirationTokens) { diff --git a/src/Microsoft.Extensions.Caching.Abstractions/ICacheEntry.cs b/src/Microsoft.Extensions.Caching.Abstractions/ICacheEntry.cs index b9654e51..1f97ea70 100644 --- a/src/Microsoft.Extensions.Caching.Abstractions/ICacheEntry.cs +++ b/src/Microsoft.Extensions.Caching.Abstractions/ICacheEntry.cs @@ -38,6 +38,8 @@ public interface ICacheEntry : IDisposable /// TimeSpan? SlidingExpiration { get; set; } + object EvictionMetadata { get; set; } + /// /// Gets the instances which cause the cache entry to expire. /// @@ -47,6 +49,5 @@ public interface ICacheEntry : IDisposable /// Gets or sets the callbacks will be fired after the cache entry is evicted from the cache. /// IList PostEvictionCallbacks { get; } - } } \ No newline at end of file diff --git a/src/Microsoft.Extensions.Caching.Abstractions/MemoryCacheEntryExtensions.cs b/src/Microsoft.Extensions.Caching.Abstractions/MemoryCacheEntryExtensions.cs index 55d125ca..5fe45ef2 100644 --- a/src/Microsoft.Extensions.Caching.Abstractions/MemoryCacheEntryExtensions.cs +++ b/src/Microsoft.Extensions.Caching.Abstractions/MemoryCacheEntryExtensions.cs @@ -66,6 +66,14 @@ public static MemoryCacheEntryOptions SetSlidingExpiration( return options; } + public static MemoryCacheEntryOptions SetEvictionMetadata( + this MemoryCacheEntryOptions options, + object metadata) + { + options.EvictionMetadata = metadata; + return options; + } + /// /// The given callback will be fired after the cache entry is evicted from the cache. /// diff --git a/src/Microsoft.Extensions.Caching.Abstractions/MemoryCacheEntryOptions.cs b/src/Microsoft.Extensions.Caching.Abstractions/MemoryCacheEntryOptions.cs index 2554ef59..699e13a8 100644 --- a/src/Microsoft.Extensions.Caching.Abstractions/MemoryCacheEntryOptions.cs +++ b/src/Microsoft.Extensions.Caching.Abstractions/MemoryCacheEntryOptions.cs @@ -74,6 +74,8 @@ public TimeSpan? SlidingExpiration } } + public object EvictionMetadata { get; set; } + /// /// Gets the instances which cause the cache entry to expire. /// diff --git a/src/Microsoft.Extensions.Caching.Memory/CacheEntry.cs b/src/Microsoft.Extensions.Caching.Memory/CacheEntry.cs index 05403ee5..98cf7c74 100644 --- a/src/Microsoft.Extensions.Caching.Memory/CacheEntry.cs +++ b/src/Microsoft.Extensions.Caching.Memory/CacheEntry.cs @@ -9,7 +9,7 @@ namespace Microsoft.Extensions.Caching.Memory { - public class CacheEntry : ICacheEntry, IRetrievedCacheEntry + internal class CacheEntry : ICacheEntry, IRetrievedCacheEntry { private bool _added = false; private static readonly Action ExpirationCallback = ExpirationTokensExpired; @@ -157,6 +157,8 @@ public IList PostEvictionCallbacks internal EvictionReason EvictionReason { get; private set; } + public object EvictionMetadata { get; set; } + public void Dispose() { if (!_added) diff --git a/src/Microsoft.Extensions.Caching.Memory/Interfaces/IRetrievedCacheEntry.cs b/src/Microsoft.Extensions.Caching.Memory/Interfaces/IRetrievedCacheEntry.cs index d4b554f5..9fdecb1c 100644 --- a/src/Microsoft.Extensions.Caching.Memory/Interfaces/IRetrievedCacheEntry.cs +++ b/src/Microsoft.Extensions.Caching.Memory/Interfaces/IRetrievedCacheEntry.cs @@ -31,6 +31,8 @@ public interface IRetrievedCacheEntry /// TimeSpan? SlidingExpiration { get; } + object EvictionMetadata { get; } + DateTimeOffset LastAccessed { get; } bool IsExpired { get; } diff --git a/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs index 730070cb..5d4b2b1e 100644 --- a/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; @@ -249,7 +250,7 @@ private bool ExecuteCacheEviction() { var evictedEntries = false; var utcNow = _clock.UtcNow; - var freshEntries = new List(); + var freshEntries = new List(); // TODO: evaluate the perf overhead of enumerators vs taking a snapshot foreach (var entry in _entries) From 05c0b690417e4b0de1b5fc662c7c926fbeade595 Mon Sep 17 00:00:00 2001 From: John Luo Date: Thu, 16 Mar 2017 16:22:29 -0700 Subject: [PATCH 14/22] Store a dictionary of IRetrievedCacheEntry --- .../LRUMemoryCacheEvictionStrategy.cs | 8 +-- .../IMemoryCacheEvictionStrategy.cs | 3 +- .../Interfaces/IMemoryCacheEvictionTrigger.cs | 4 +- .../MemoryCache.cs | 58 ++++++++++--------- .../MemoryCacheEvictionStrategy.cs | 9 ++- .../MemoryCacheEvictionTrigger.cs | 2 +- 6 files changed, 44 insertions(+), 40 deletions(-) rename {src/Microsoft.Extensions.Caching.Memory => samples/MemoryCacheSample}/LRUMemoryCacheEvictionStrategy.cs (69%) diff --git a/src/Microsoft.Extensions.Caching.Memory/LRUMemoryCacheEvictionStrategy.cs b/samples/MemoryCacheSample/LRUMemoryCacheEvictionStrategy.cs similarity index 69% rename from src/Microsoft.Extensions.Caching.Memory/LRUMemoryCacheEvictionStrategy.cs rename to samples/MemoryCacheSample/LRUMemoryCacheEvictionStrategy.cs index a8a0a243..c43df443 100644 --- a/src/Microsoft.Extensions.Caching.Memory/LRUMemoryCacheEvictionStrategy.cs +++ b/samples/MemoryCacheSample/LRUMemoryCacheEvictionStrategy.cs @@ -17,15 +17,15 @@ public LRUMemoryCacheEvictionStrategy(int maximumEntries) MaximumEntries = maximumEntries; } - public void Evict(IList entries, DateTimeOffset now) + public void Evict(MemoryCache cache, DateTimeOffset utcNow) { - var removalTarget = entries.Count - MaximumEntries; + var removalTarget = cache.Count - MaximumEntries; if (removalTarget > 0) { - foreach (var entry in entries.OrderBy(e => e.LastAccessed).Take(removalTarget)) + foreach (var entry in cache.OrderBy(e => e.Value.LastAccessed).Take(removalTarget)) { - entry.SetExpired(EvictionReason.Capacity); + entry.Value.SetExpired(EvictionReason.Capacity); } } } diff --git a/src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionStrategy.cs b/src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionStrategy.cs index c1fd28da..9c7a9c33 100644 --- a/src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionStrategy.cs +++ b/src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionStrategy.cs @@ -2,13 +2,12 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Collections.Generic; namespace Microsoft.Extensions.Caching.Memory { public interface IMemoryCacheEvictionStrategy { // TODO: doc comments - void Evict(IList entries, DateTimeOffset now); + void Evict(MemoryCache cache, DateTimeOffset utcNow); } } diff --git a/src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionTrigger.cs b/src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionTrigger.cs index 6e1a2601..9488b73f 100644 --- a/src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionTrigger.cs +++ b/src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionTrigger.cs @@ -10,8 +10,6 @@ public interface IMemoryCacheEvictionTrigger : IDisposable // TODO: doc comments Func EvictionCallback { get; set; } - void Resume(); - - void Stop(); + void Resume(MemoryCache cache); } } diff --git a/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs index 5d4b2b1e..108c137a 100644 --- a/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs @@ -5,7 +5,6 @@ using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Linq; using Microsoft.Extensions.Internal; using Microsoft.Extensions.Options; @@ -15,9 +14,9 @@ namespace Microsoft.Extensions.Caching.Memory /// An implementation of using a dictionary to /// store its entries. /// - public class MemoryCache : IMemoryCache + public class MemoryCache : IMemoryCache, IEnumerable> { - private readonly ConcurrentDictionary _entries; + private readonly ConcurrentDictionary _entries; private bool _disposed; // We store the delegates locally to prevent allocations @@ -41,7 +40,7 @@ public MemoryCache(IOptions optionsAccessor) } var options = optionsAccessor.Value; - _entries = new ConcurrentDictionary(); + _entries = new ConcurrentDictionary(); _setEntry = SetEntry; _entryExpirationNotification = EntryExpired; @@ -67,7 +66,7 @@ public int Count get { return _entries.Count; } } - private ICollection> EntriesCollection => _entries; + private ICollection> EntriesCollection => _entries; /// public ICacheEntry CreateEntry(object key) @@ -115,7 +114,7 @@ private void SetEntry(CacheEntry entry) // Initialize the last access timestamp at the time the entry is added entry.LastAccessed = utcNow; - CacheEntry priorEntry; + IRetrievedCacheEntry priorEntry; if (_entries.TryGetValue(entry.Key, out priorEntry)) { priorEntry.SetExpired(EvictionReason.Replaced); @@ -156,7 +155,7 @@ private void SetEntry(CacheEntry entry) if (priorEntry != null) { - priorEntry.InvokeEvictionCallbacks(); + ((CacheEntry)priorEntry).InvokeEvictionCallbacks(); } } else @@ -168,7 +167,7 @@ private void SetEntry(CacheEntry entry) } } - _evictionTrigger.Resume(); + _evictionTrigger.Resume(this); } /// @@ -185,9 +184,11 @@ public bool TryGetValue(object key, out object result) var utcNow = _clock.UtcNow; var found = false; - CacheEntry entry; - if (_entries.TryGetValue(key, out entry)) + IRetrievedCacheEntry retrievedEntry; + if (_entries.TryGetValue(key, out retrievedEntry)) { + var entry = (CacheEntry)retrievedEntry; + // Check if expired due to expiration tokens, timers, etc. and if so, remove it. // Allow a stale Replaced value to be returned due to concurrent calls to SetExpired during SetEntry. if (entry.CheckExpired(utcNow) && entry.EvictionReason != EvictionReason.Replaced) @@ -207,7 +208,7 @@ public bool TryGetValue(object key, out object result) } } - _evictionTrigger.Resume(); + _evictionTrigger.Resume(this); return found; } @@ -221,21 +222,21 @@ public void Remove(object key) } CheckDisposed(); - CacheEntry entry; + IRetrievedCacheEntry entry; if (_entries.TryRemove(key, out entry)) { entry.SetExpired(EvictionReason.Removed); - entry.InvokeEvictionCallbacks(); + ((CacheEntry)entry).InvokeEvictionCallbacks(); } - _evictionTrigger.Resume(); + _evictionTrigger.Resume(this); } - private void RemoveEntry(CacheEntry entry) + private void RemoveEntry(IRetrievedCacheEntry entry) { - if (EntriesCollection.Remove(new KeyValuePair(entry.Key, entry))) + if (EntriesCollection.Remove(new KeyValuePair(entry.Key, entry))) { - entry.InvokeEvictionCallbacks(); + ((CacheEntry)entry).InvokeEvictionCallbacks(); } } @@ -243,25 +244,16 @@ private void EntryExpired(CacheEntry entry) { // TODO: For efficiency consider processing these expirations in batches. RemoveEntry(entry); - _evictionTrigger.Resume(); + _evictionTrigger.Resume(this); } private bool ExecuteCacheEviction() { var evictedEntries = false; var utcNow = _clock.UtcNow; - var freshEntries = new List(); // TODO: evaluate the perf overhead of enumerators vs taking a snapshot - foreach (var entry in _entries) - { - if (!entry.Value.CheckExpired(utcNow)) - { - freshEntries.Add(entry.Value); - } - } - - _evictionStrategy.Evict(freshEntries, utcNow); // TODO: anything else eviction strategies need? + _evictionStrategy.Evict(this, utcNow); // TODO: anything else eviction strategies need? foreach (var entry in _entries) { @@ -303,5 +295,15 @@ private void CheckDisposed() throw new ObjectDisposedException(typeof(MemoryCache).FullName); } } + + public IEnumerator> GetEnumerator() + { + return _entries.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } } } \ No newline at end of file diff --git a/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionStrategy.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionStrategy.cs index f16c3afc..058576da 100644 --- a/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionStrategy.cs +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionStrategy.cs @@ -2,12 +2,17 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Collections.Generic; namespace Microsoft.Extensions.Caching.Memory { public class MemoryCacheEvictionStrategy : IMemoryCacheEvictionStrategy { - public void Evict(IList entries, DateTimeOffset now) { } + public void Evict(MemoryCache cache, DateTimeOffset utcNow) + { + foreach (var entry in cache) + { + entry.Value.CheckExpired(utcNow); + } + } } } diff --git a/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionTrigger.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionTrigger.cs index 8eb9cad3..d005917c 100644 --- a/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionTrigger.cs +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionTrigger.cs @@ -50,7 +50,7 @@ public void Dispose() } } - public void Resume() + public void Resume(MemoryCache cache) { if (!_isDisposed && !_timerIsRunning) { From 25d4725c22c9f3dd65db908a179b4c1ac80658c8 Mon Sep 17 00:00:00 2001 From: John Luo Date: Thu, 16 Mar 2017 17:15:09 -0700 Subject: [PATCH 15/22] Rework Trigger --- .../MemoryCache.cs | 18 ++++---- .../MemoryCacheEvictionTrigger.cs | 42 ++++++++++--------- .../TimeExpirationTests.cs | 2 +- .../TokenExpirationTests.cs | 2 +- 4 files changed, 33 insertions(+), 31 deletions(-) diff --git a/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs index 108c137a..1b7cb11c 100644 --- a/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs @@ -46,7 +46,7 @@ public MemoryCache(IOptions optionsAccessor) _clock = options.Clock ?? new SystemClock(); _evictionStrategy = options.EvictionStrategy ?? new MemoryCacheEvictionStrategy(); - _evictionTrigger = options.EvictionTrigger ?? new MemoryCacheEvictionTrigger(); + _evictionTrigger = options.EvictionTrigger ?? new MemoryCacheEvictionTrigger(_clock); _evictionTrigger.EvictionCallback = ExecuteCacheEviction; } @@ -167,7 +167,7 @@ private void SetEntry(CacheEntry entry) } } - _evictionTrigger.Resume(this); + _evictionTrigger.Resume(this, _clock.UtcNow); } /// @@ -208,7 +208,7 @@ public bool TryGetValue(object key, out object result) } } - _evictionTrigger.Resume(this); + _evictionTrigger.Resume(this, _clock.UtcNow); return found; } @@ -229,7 +229,7 @@ public void Remove(object key) ((CacheEntry)entry).InvokeEvictionCallbacks(); } - _evictionTrigger.Resume(this); + _evictionTrigger.Resume(this, _clock.UtcNow); } private void RemoveEntry(IRetrievedCacheEntry entry) @@ -244,17 +244,15 @@ private void EntryExpired(CacheEntry entry) { // TODO: For efficiency consider processing these expirations in batches. RemoveEntry(entry); - _evictionTrigger.Resume(this); + _evictionTrigger.Resume(this, _clock.UtcNow); } private bool ExecuteCacheEviction() { - var evictedEntries = false; - var utcNow = _clock.UtcNow; - // TODO: evaluate the perf overhead of enumerators vs taking a snapshot - _evictionStrategy.Evict(this, utcNow); // TODO: anything else eviction strategies need? + _evictionStrategy.Evict(this, _clock.UtcNow); // TODO: anything else eviction strategies need? + var evictedEntries = false; foreach (var entry in _entries) { if (entry.Value.IsExpired) @@ -285,6 +283,8 @@ protected virtual void Dispose(bool disposing) } _disposed = true; + + _evictionTrigger.Dispose(); } } diff --git a/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionTrigger.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionTrigger.cs index d005917c..afd89e70 100644 --- a/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionTrigger.cs +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionTrigger.cs @@ -3,6 +3,7 @@ using System; using System.Threading; +using Microsoft.Extensions.Internal; namespace Microsoft.Extensions.Caching.Memory { @@ -12,19 +13,22 @@ public class MemoryCacheEvictionTrigger : IMemoryCacheEvictionTrigger private readonly object _lock = new object(); private readonly TimeSpan _evictionInterval; private readonly int _intervalsWithoutEvictionUntilIdle; + private readonly ISystemClock _clock; private volatile bool _isDisposed; private volatile bool _timerIsRunning; + private DateTimeOffset _lastEvictionCall; private int _intervalsWithoutEviction; private int _evictionRunning; private Timer _timer; - public MemoryCacheEvictionTrigger() - : this(TimeSpan.FromMinutes(1), 10) // TODO: Need better defaults? + public MemoryCacheEvictionTrigger(ISystemClock clock) + : this(clock, TimeSpan.FromMinutes(1), 2) // TODO: Need better defaults? { } - public MemoryCacheEvictionTrigger(TimeSpan evictionInterval, int intervalsWithoutEvictionUntilIdle) + public MemoryCacheEvictionTrigger(ISystemClock clock, TimeSpan evictionInterval, int intervalsWithoutEvictionUntilIdle) { + _clock = clock; _evictionInterval = evictionInterval; _intervalsWithoutEvictionUntilIdle = intervalsWithoutEvictionUntilIdle; _timer = new Timer(TimerLoop, null, Timeout.Infinite, Timeout.Infinite); @@ -52,32 +56,22 @@ public void Dispose() public void Resume(MemoryCache cache) { - if (!_isDisposed && !_timerIsRunning) + _lastEvictionCall = _clock.UtcNow; + Interlocked.Exchange(ref _intervalsWithoutEviction, 0); + + if (!_isDisposed) { lock (_lock) { if (!_timerIsRunning && _timer != null) { _timerIsRunning = true; - _timer.Change(_evictionInterval, _evictionInterval); - Interlocked.Exchange(ref _intervalsWithoutEviction, 0); + _timer.Change(_evictionInterval, TimeSpan.FromMilliseconds(-1)); } } } } - public void Stop() - { - if (!_isDisposed && _timerIsRunning) - { - lock (_lock) - { - _timerIsRunning = false; - _timer.Change(Timeout.Infinite, Timeout.Infinite); - } - } - } - private void TimerLoop(object state) { if (Interlocked.CompareExchange(ref _evictionRunning, 1, 0) == 0) @@ -91,9 +85,17 @@ private void TimerLoop(object state) Interlocked.Increment(ref _intervalsWithoutEviction); } - if (Volatile.Read(ref _intervalsWithoutEviction) >= _intervalsWithoutEvictionUntilIdle) + if (Volatile.Read(ref _intervalsWithoutEviction) >= _intervalsWithoutEvictionUntilIdle + && _clock.UtcNow - _lastEvictionCall > _evictionInterval) + { + lock (_lock) + { + _timerIsRunning = false; + } + } + else { - Stop(); + _timer.Change(_evictionInterval, TimeSpan.FromMilliseconds(-1)); } Interlocked.Exchange(ref _evictionRunning, 0); diff --git a/test/Microsoft.Extensions.Caching.Memory.Tests/TimeExpirationTests.cs b/test/Microsoft.Extensions.Caching.Memory.Tests/TimeExpirationTests.cs index ddf3518d..77de27e9 100644 --- a/test/Microsoft.Extensions.Caching.Memory.Tests/TimeExpirationTests.cs +++ b/test/Microsoft.Extensions.Caching.Memory.Tests/TimeExpirationTests.cs @@ -16,7 +16,7 @@ private IMemoryCache CreateCache(ISystemClock clock) return new MemoryCache(new MemoryCacheOptions() { Clock = clock, - EvictionTrigger = new MemoryCacheEvictionTrigger(TimeSpan.FromSeconds(1), 10) + EvictionTrigger = new MemoryCacheEvictionTrigger(clock, TimeSpan.FromSeconds(1), 10) }); } diff --git a/test/Microsoft.Extensions.Caching.Memory.Tests/TokenExpirationTests.cs b/test/Microsoft.Extensions.Caching.Memory.Tests/TokenExpirationTests.cs index 33ba50d0..e8fc3f72 100644 --- a/test/Microsoft.Extensions.Caching.Memory.Tests/TokenExpirationTests.cs +++ b/test/Microsoft.Extensions.Caching.Memory.Tests/TokenExpirationTests.cs @@ -23,7 +23,7 @@ private IMemoryCache CreateCache(ISystemClock clock) return new MemoryCache(new MemoryCacheOptions() { Clock = clock, - EvictionTrigger = new MemoryCacheEvictionTrigger(TimeSpan.FromSeconds(1), 10) + EvictionTrigger = new MemoryCacheEvictionTrigger(clock, TimeSpan.FromSeconds(1), 10) }); } From 4a4bda785d6e722bb689b0693a6bc338b29458b2 Mon Sep 17 00:00:00 2001 From: John Luo Date: Thu, 16 Mar 2017 19:00:28 -0700 Subject: [PATCH 16/22] Expose as readonly collection --- .../LRUMemoryCacheEvictionStrategy.cs | 6 +++--- .../CacheEntryExtensions.cs | 8 ++++++++ .../Interfaces/IMemoryCacheEvictionStrategy.cs | 3 ++- .../Interfaces/IMemoryCacheEvictionTrigger.cs | 3 ++- src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs | 10 +++++----- .../MemoryCacheEvictionStrategy.cs | 5 +++-- .../MemoryCacheEvictionTrigger.cs | 3 ++- 7 files changed, 25 insertions(+), 13 deletions(-) diff --git a/samples/MemoryCacheSample/LRUMemoryCacheEvictionStrategy.cs b/samples/MemoryCacheSample/LRUMemoryCacheEvictionStrategy.cs index c43df443..db191870 100644 --- a/samples/MemoryCacheSample/LRUMemoryCacheEvictionStrategy.cs +++ b/samples/MemoryCacheSample/LRUMemoryCacheEvictionStrategy.cs @@ -17,13 +17,13 @@ public LRUMemoryCacheEvictionStrategy(int maximumEntries) MaximumEntries = maximumEntries; } - public void Evict(MemoryCache cache, DateTimeOffset utcNow) + public void Evict(IReadOnlyCollection> entries, DateTimeOffset utcNow) { - var removalTarget = cache.Count - MaximumEntries; + var removalTarget = entries.Count - MaximumEntries; if (removalTarget > 0) { - foreach (var entry in cache.OrderBy(e => e.Value.LastAccessed).Take(removalTarget)) + foreach (var entry in entries.OrderBy(e => e.Value.LastAccessed).Take(removalTarget)) { entry.Value.SetExpired(EvictionReason.Capacity); } diff --git a/src/Microsoft.Extensions.Caching.Abstractions/CacheEntryExtensions.cs b/src/Microsoft.Extensions.Caching.Abstractions/CacheEntryExtensions.cs index 716f8954..1c9fc32a 100644 --- a/src/Microsoft.Extensions.Caching.Abstractions/CacheEntryExtensions.cs +++ b/src/Microsoft.Extensions.Caching.Abstractions/CacheEntryExtensions.cs @@ -121,6 +121,14 @@ public static ICacheEntry SetValue( return entry; } + public static ICacheEntry SetEvictionMetadata( + this ICacheEntry entry, + object metadata) + { + entry.EvictionMetadata = metadata; + return entry; + } + /// /// Applies the values of an existing to the entry. /// diff --git a/src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionStrategy.cs b/src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionStrategy.cs index 9c7a9c33..6450b019 100644 --- a/src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionStrategy.cs +++ b/src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionStrategy.cs @@ -2,12 +2,13 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; namespace Microsoft.Extensions.Caching.Memory { public interface IMemoryCacheEvictionStrategy { // TODO: doc comments - void Evict(MemoryCache cache, DateTimeOffset utcNow); + void Evict(IReadOnlyCollection> entries, DateTimeOffset utcNow); } } diff --git a/src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionTrigger.cs b/src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionTrigger.cs index 9488b73f..614eb702 100644 --- a/src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionTrigger.cs +++ b/src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionTrigger.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; namespace Microsoft.Extensions.Caching.Memory { @@ -10,6 +11,6 @@ public interface IMemoryCacheEvictionTrigger : IDisposable // TODO: doc comments Func EvictionCallback { get; set; } - void Resume(MemoryCache cache); + void Resume(IReadOnlyCollection> entries); } } diff --git a/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs index 1b7cb11c..fe9b14d6 100644 --- a/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs @@ -14,7 +14,7 @@ namespace Microsoft.Extensions.Caching.Memory /// An implementation of using a dictionary to /// store its entries. /// - public class MemoryCache : IMemoryCache, IEnumerable> + public class MemoryCache : IMemoryCache, IReadOnlyCollection> { private readonly ConcurrentDictionary _entries; private bool _disposed; @@ -167,7 +167,7 @@ private void SetEntry(CacheEntry entry) } } - _evictionTrigger.Resume(this, _clock.UtcNow); + _evictionTrigger.Resume(this); } /// @@ -208,7 +208,7 @@ public bool TryGetValue(object key, out object result) } } - _evictionTrigger.Resume(this, _clock.UtcNow); + _evictionTrigger.Resume(this); return found; } @@ -229,7 +229,7 @@ public void Remove(object key) ((CacheEntry)entry).InvokeEvictionCallbacks(); } - _evictionTrigger.Resume(this, _clock.UtcNow); + _evictionTrigger.Resume(this); } private void RemoveEntry(IRetrievedCacheEntry entry) @@ -244,7 +244,7 @@ private void EntryExpired(CacheEntry entry) { // TODO: For efficiency consider processing these expirations in batches. RemoveEntry(entry); - _evictionTrigger.Resume(this, _clock.UtcNow); + _evictionTrigger.Resume(this); } private bool ExecuteCacheEviction() diff --git a/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionStrategy.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionStrategy.cs index 058576da..915011c0 100644 --- a/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionStrategy.cs +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionStrategy.cs @@ -2,14 +2,15 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; namespace Microsoft.Extensions.Caching.Memory { public class MemoryCacheEvictionStrategy : IMemoryCacheEvictionStrategy { - public void Evict(MemoryCache cache, DateTimeOffset utcNow) + public void Evict(IReadOnlyCollection> entries, DateTimeOffset utcNow) { - foreach (var entry in cache) + foreach (var entry in entries) { entry.Value.CheckExpired(utcNow); } diff --git a/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionTrigger.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionTrigger.cs index afd89e70..a6667244 100644 --- a/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionTrigger.cs +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionTrigger.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; using System.Threading; using Microsoft.Extensions.Internal; @@ -54,7 +55,7 @@ public void Dispose() } } - public void Resume(MemoryCache cache) + public void Resume(IReadOnlyCollection> entries) { _lastEvictionCall = _clock.UtcNow; Interlocked.Exchange(ref _intervalsWithoutEviction, 0); From 78f2b40c8e26ca8971ae2588ab23db007fb1d8ee Mon Sep 17 00:00:00 2001 From: John Luo Date: Thu, 16 Mar 2017 19:23:24 -0700 Subject: [PATCH 17/22] Simplify trigger --- .../MemoryCache.cs | 2 +- .../MemoryCacheEvictionTrigger.cs | 13 ++++--------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs index fe9b14d6..ab50b2d3 100644 --- a/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs @@ -46,7 +46,7 @@ public MemoryCache(IOptions optionsAccessor) _clock = options.Clock ?? new SystemClock(); _evictionStrategy = options.EvictionStrategy ?? new MemoryCacheEvictionStrategy(); - _evictionTrigger = options.EvictionTrigger ?? new MemoryCacheEvictionTrigger(_clock); + _evictionTrigger = options.EvictionTrigger ?? new MemoryCacheEvictionTrigger(); _evictionTrigger.EvictionCallback = ExecuteCacheEviction; } diff --git a/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionTrigger.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionTrigger.cs index a6667244..5264b963 100644 --- a/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionTrigger.cs +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionTrigger.cs @@ -14,22 +14,19 @@ public class MemoryCacheEvictionTrigger : IMemoryCacheEvictionTrigger private readonly object _lock = new object(); private readonly TimeSpan _evictionInterval; private readonly int _intervalsWithoutEvictionUntilIdle; - private readonly ISystemClock _clock; private volatile bool _isDisposed; private volatile bool _timerIsRunning; - private DateTimeOffset _lastEvictionCall; private int _intervalsWithoutEviction; private int _evictionRunning; private Timer _timer; - public MemoryCacheEvictionTrigger(ISystemClock clock) - : this(clock, TimeSpan.FromMinutes(1), 2) // TODO: Need better defaults? + public MemoryCacheEvictionTrigger() + : this(TimeSpan.FromMinutes(1), 2) { } - public MemoryCacheEvictionTrigger(ISystemClock clock, TimeSpan evictionInterval, int intervalsWithoutEvictionUntilIdle) + public MemoryCacheEvictionTrigger(TimeSpan evictionInterval, int intervalsWithoutEvictionUntilIdle) { - _clock = clock; _evictionInterval = evictionInterval; _intervalsWithoutEvictionUntilIdle = intervalsWithoutEvictionUntilIdle; _timer = new Timer(TimerLoop, null, Timeout.Infinite, Timeout.Infinite); @@ -57,7 +54,6 @@ public void Dispose() public void Resume(IReadOnlyCollection> entries) { - _lastEvictionCall = _clock.UtcNow; Interlocked.Exchange(ref _intervalsWithoutEviction, 0); if (!_isDisposed) @@ -86,8 +82,7 @@ private void TimerLoop(object state) Interlocked.Increment(ref _intervalsWithoutEviction); } - if (Volatile.Read(ref _intervalsWithoutEviction) >= _intervalsWithoutEvictionUntilIdle - && _clock.UtcNow - _lastEvictionCall > _evictionInterval) + if (Volatile.Read(ref _intervalsWithoutEviction) >= _intervalsWithoutEvictionUntilIdle) { lock (_lock) { From d5629fbe9689cb9b02975e95370163f3997d9e16 Mon Sep 17 00:00:00 2001 From: John Luo Date: Sun, 19 Mar 2017 01:27:01 -0700 Subject: [PATCH 18/22] Update --- .../LRUMemoryCacheEvictionStrategy.cs | 4 +- .../IMemoryCacheEvictionStrategy.cs | 2 +- .../Interfaces/IMemoryCacheEvictionTrigger.cs | 2 +- .../MemoryCache.cs | 16 +++---- .../MemoryCacheEvictionStrategy.cs | 11 ++++- .../MemoryCacheEvictionTrigger.cs | 45 +++++++++++-------- .../TimeExpirationTests.cs | 2 +- .../TokenExpirationTests.cs | 2 +- 8 files changed, 48 insertions(+), 36 deletions(-) diff --git a/samples/MemoryCacheSample/LRUMemoryCacheEvictionStrategy.cs b/samples/MemoryCacheSample/LRUMemoryCacheEvictionStrategy.cs index db191870..078be075 100644 --- a/samples/MemoryCacheSample/LRUMemoryCacheEvictionStrategy.cs +++ b/samples/MemoryCacheSample/LRUMemoryCacheEvictionStrategy.cs @@ -17,7 +17,7 @@ public LRUMemoryCacheEvictionStrategy(int maximumEntries) MaximumEntries = maximumEntries; } - public void Evict(IReadOnlyCollection> entries, DateTimeOffset utcNow) + public int Evict(IReadOnlyCollection> entries, DateTimeOffset utcNow) { var removalTarget = entries.Count - MaximumEntries; @@ -28,6 +28,8 @@ public void Evict(IReadOnlyCollection entry.Value.SetExpired(EvictionReason.Capacity); } } + + return removalTarget; } } } diff --git a/src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionStrategy.cs b/src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionStrategy.cs index 6450b019..0b6b8a40 100644 --- a/src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionStrategy.cs +++ b/src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionStrategy.cs @@ -9,6 +9,6 @@ namespace Microsoft.Extensions.Caching.Memory public interface IMemoryCacheEvictionStrategy { // TODO: doc comments - void Evict(IReadOnlyCollection> entries, DateTimeOffset utcNow); + int Evict(IReadOnlyCollection> entries, DateTimeOffset utcNow); } } diff --git a/src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionTrigger.cs b/src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionTrigger.cs index 614eb702..2123e5ec 100644 --- a/src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionTrigger.cs +++ b/src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionTrigger.cs @@ -9,7 +9,7 @@ namespace Microsoft.Extensions.Caching.Memory public interface IMemoryCacheEvictionTrigger : IDisposable { // TODO: doc comments - Func EvictionCallback { get; set; } + void SetEvictionCallback(Func evictionCallback); void Resume(IReadOnlyCollection> entries); } diff --git a/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs index ab50b2d3..f35009e7 100644 --- a/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs @@ -47,7 +47,7 @@ public MemoryCache(IOptions optionsAccessor) _clock = options.Clock ?? new SystemClock(); _evictionStrategy = options.EvictionStrategy ?? new MemoryCacheEvictionStrategy(); _evictionTrigger = options.EvictionTrigger ?? new MemoryCacheEvictionTrigger(); - _evictionTrigger.EvictionCallback = ExecuteCacheEviction; + _evictionTrigger.SetEvictionCallback(ExecuteCacheEviction); } /// @@ -247,25 +247,19 @@ private void EntryExpired(CacheEntry entry) _evictionTrigger.Resume(this); } - private bool ExecuteCacheEviction() + private int ExecuteCacheEviction() { - // TODO: evaluate the perf overhead of enumerators vs taking a snapshot - _evictionStrategy.Evict(this, _clock.UtcNow); // TODO: anything else eviction strategies need? - - var evictedEntries = false; + var evictCount = _evictionStrategy.Evict(this, _clock.UtcNow); // TODO: anything else eviction strategies need? + foreach (var entry in _entries) { if (entry.Value.IsExpired) { - if (evictedEntries == false) - { - evictedEntries = true; - } RemoveEntry(entry.Value); } } - return evictedEntries; + return evictCount; } public void Dispose() diff --git a/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionStrategy.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionStrategy.cs index 915011c0..3085c93f 100644 --- a/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionStrategy.cs +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionStrategy.cs @@ -8,12 +8,19 @@ namespace Microsoft.Extensions.Caching.Memory { public class MemoryCacheEvictionStrategy : IMemoryCacheEvictionStrategy { - public void Evict(IReadOnlyCollection> entries, DateTimeOffset utcNow) + public int Evict(IReadOnlyCollection> entries, DateTimeOffset utcNow) { + var expiredEntries = 0; + foreach (var entry in entries) { - entry.Value.CheckExpired(utcNow); + if (entry.Value.CheckExpired(utcNow)) + { + expiredEntries++; + } } + + return expiredEntries; } } } diff --git a/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionTrigger.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionTrigger.cs index 5264b963..085f8c32 100644 --- a/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionTrigger.cs +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionTrigger.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Threading; -using Microsoft.Extensions.Internal; namespace Microsoft.Extensions.Caching.Memory { @@ -20,6 +19,7 @@ public class MemoryCacheEvictionTrigger : IMemoryCacheEvictionTrigger private int _intervalsWithoutEviction; private int _evictionRunning; private Timer _timer; + private Func _evictionCallback; public MemoryCacheEvictionTrigger() : this(TimeSpan.FromMinutes(1), 2) @@ -32,8 +32,6 @@ public MemoryCacheEvictionTrigger(TimeSpan evictionInterval, int intervalsWithou _timer = new Timer(TimerLoop, null, Timeout.Infinite, Timeout.Infinite); } - public Func EvictionCallback { get; set; } - public void Dispose() { if (!_isDisposed) @@ -52,6 +50,11 @@ public void Dispose() } } + public void SetEvictionCallback(Func evictionCallback) + { + _evictionCallback = evictionCallback; + } + public void Resume(IReadOnlyCollection> entries) { Interlocked.Exchange(ref _intervalsWithoutEviction, 0); @@ -73,28 +76,34 @@ private void TimerLoop(object state) { if (Interlocked.CompareExchange(ref _evictionRunning, 1, 0) == 0) { - if (EvictionCallback()) + try { - Interlocked.Exchange(ref _intervalsWithoutEviction, 0); - } - else - { - Interlocked.Increment(ref _intervalsWithoutEviction); - } - if (Volatile.Read(ref _intervalsWithoutEviction) >= _intervalsWithoutEvictionUntilIdle) - { - lock (_lock) + if (_evictionCallback() > 0) { - _timerIsRunning = false; + Interlocked.Exchange(ref _intervalsWithoutEviction, 0); + } + else + { + Interlocked.Increment(ref _intervalsWithoutEviction); + } + + if (Volatile.Read(ref _intervalsWithoutEviction) >= _intervalsWithoutEvictionUntilIdle) + { + lock (_lock) + { + _timerIsRunning = false; + } + } + else + { + _timer.Change(_evictionInterval, TimeSpan.FromMilliseconds(-1)); } } - else + finally { - _timer.Change(_evictionInterval, TimeSpan.FromMilliseconds(-1)); + Interlocked.Exchange(ref _evictionRunning, 0); } - - Interlocked.Exchange(ref _evictionRunning, 0); } } } diff --git a/test/Microsoft.Extensions.Caching.Memory.Tests/TimeExpirationTests.cs b/test/Microsoft.Extensions.Caching.Memory.Tests/TimeExpirationTests.cs index 77de27e9..ddf3518d 100644 --- a/test/Microsoft.Extensions.Caching.Memory.Tests/TimeExpirationTests.cs +++ b/test/Microsoft.Extensions.Caching.Memory.Tests/TimeExpirationTests.cs @@ -16,7 +16,7 @@ private IMemoryCache CreateCache(ISystemClock clock) return new MemoryCache(new MemoryCacheOptions() { Clock = clock, - EvictionTrigger = new MemoryCacheEvictionTrigger(clock, TimeSpan.FromSeconds(1), 10) + EvictionTrigger = new MemoryCacheEvictionTrigger(TimeSpan.FromSeconds(1), 10) }); } diff --git a/test/Microsoft.Extensions.Caching.Memory.Tests/TokenExpirationTests.cs b/test/Microsoft.Extensions.Caching.Memory.Tests/TokenExpirationTests.cs index e8fc3f72..33ba50d0 100644 --- a/test/Microsoft.Extensions.Caching.Memory.Tests/TokenExpirationTests.cs +++ b/test/Microsoft.Extensions.Caching.Memory.Tests/TokenExpirationTests.cs @@ -23,7 +23,7 @@ private IMemoryCache CreateCache(ISystemClock clock) return new MemoryCache(new MemoryCacheOptions() { Clock = clock, - EvictionTrigger = new MemoryCacheEvictionTrigger(clock, TimeSpan.FromSeconds(1), 10) + EvictionTrigger = new MemoryCacheEvictionTrigger(TimeSpan.FromSeconds(1), 10) }); } From 9cee4ff38a811c31afd1d8436f6cc02957a8a0c0 Mon Sep 17 00:00:00 2001 From: John Luo Date: Sun, 19 Mar 2017 02:17:49 -0700 Subject: [PATCH 19/22] Update sample --- .../CapacityMemoryCacheEvictionTrigger.cs | 60 +++++++++++++++++++ .../LRUMemoryCacheEvictionStrategy.cs | 31 +++++++--- 2 files changed, 83 insertions(+), 8 deletions(-) create mode 100644 samples/MemoryCacheSample/CapacityMemoryCacheEvictionTrigger.cs diff --git a/samples/MemoryCacheSample/CapacityMemoryCacheEvictionTrigger.cs b/samples/MemoryCacheSample/CapacityMemoryCacheEvictionTrigger.cs new file mode 100644 index 00000000..af3626b6 --- /dev/null +++ b/samples/MemoryCacheSample/CapacityMemoryCacheEvictionTrigger.cs @@ -0,0 +1,60 @@ + +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Caching.Memory; + +namespace MemoryCacheSample +{ + class CapacityMemoryCacheEvictionTrigger : IMemoryCacheEvictionTrigger + { + private Func _evictionCallback; + private int _entryLimit; + private object _evictionlock = new object(); + + public CapacityMemoryCacheEvictionTrigger(int entryLimit) + { + _entryLimit = entryLimit; + } + + public void Dispose() { } + + public void Resume(IReadOnlyCollection> entries) + { + if (entries.Count > _entryLimit) + { + Task.Factory.StartNew( + state => StartEviction((IReadOnlyCollection>)state), + entries, + CancellationToken.None, + TaskCreationOptions.DenyChildAttach, + TaskScheduler.Default); + } + } + + public void SetEvictionCallback(Func evictionCallback) + { + _evictionCallback = evictionCallback; + } + + private void StartEviction(IReadOnlyCollection> entries) + { + // Don't run too often + if (Monitor.TryEnter(_evictionlock)) + { + try + { + _evictionCallback(); + } + finally + { + Monitor.Exit(_evictionlock); + } + } + } + } +} diff --git a/samples/MemoryCacheSample/LRUMemoryCacheEvictionStrategy.cs b/samples/MemoryCacheSample/LRUMemoryCacheEvictionStrategy.cs index 078be075..d5ab9d72 100644 --- a/samples/MemoryCacheSample/LRUMemoryCacheEvictionStrategy.cs +++ b/samples/MemoryCacheSample/LRUMemoryCacheEvictionStrategy.cs @@ -4,32 +4,47 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.Extensions.Caching.Memory; -namespace Microsoft.Extensions.Caching.Memory +namespace MemoryCacheSample { // TODO: remove this public class LRUMemoryCacheEvictionStrategy : IMemoryCacheEvictionStrategy { - private readonly int MaximumEntries; + private readonly IMemoryCacheEvictionStrategy _evictExpiredStrategy; + private readonly int _entryLimit; - public LRUMemoryCacheEvictionStrategy(int maximumEntries) + public LRUMemoryCacheEvictionStrategy(int entryLimit) { - MaximumEntries = maximumEntries; + _entryLimit = entryLimit; + _evictExpiredStrategy = new MemoryCacheEvictionStrategy(); } public int Evict(IReadOnlyCollection> entries, DateTimeOffset utcNow) { - var removalTarget = entries.Count - MaximumEntries; + var expiredCount = _evictExpiredStrategy.Evict(entries, utcNow); + var removalTarget = entries.Count - expiredCount - _entryLimit; // assume underflow is handled - if (removalTarget > 0) + if (removalTarget <= 0) { - foreach (var entry in entries.OrderBy(e => e.Value.LastAccessed).Take(removalTarget)) + return expiredCount; + } + + var removedEntries = 0; + foreach (var entry in entries.OrderBy(e => e.Value.LastAccessed)) + { + if (!entry.Value.IsExpired) { entry.Value.SetExpired(EvictionReason.Capacity); + removedEntries++; + } + if (removedEntries == removalTarget) + { + break; } } - return removalTarget; + return removalTarget + expiredCount; } } } From f8fc2040c0d7dd1ce4d38e2f1279b73d1b985bb0 Mon Sep 17 00:00:00 2001 From: John Luo Date: Wed, 22 Mar 2017 21:38:20 -0700 Subject: [PATCH 20/22] Update --- .../MemoryCacheSample/LRUMemoryCacheEvictionStrategy.cs | 2 +- src/Microsoft.Extensions.Caching.Memory/CacheEntry.cs | 4 +++- .../Interfaces/IRetrievedCacheEntry.cs | 2 +- src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs | 7 ++++--- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/samples/MemoryCacheSample/LRUMemoryCacheEvictionStrategy.cs b/samples/MemoryCacheSample/LRUMemoryCacheEvictionStrategy.cs index d5ab9d72..2c7b6d56 100644 --- a/samples/MemoryCacheSample/LRUMemoryCacheEvictionStrategy.cs +++ b/samples/MemoryCacheSample/LRUMemoryCacheEvictionStrategy.cs @@ -35,7 +35,7 @@ public int Evict(IReadOnlyCollection> { if (!entry.Value.IsExpired) { - entry.Value.SetExpired(EvictionReason.Capacity); + entry.Value.Evict(); removedEntries++; } if (removedEntries == removalTarget) diff --git a/src/Microsoft.Extensions.Caching.Memory/CacheEntry.cs b/src/Microsoft.Extensions.Caching.Memory/CacheEntry.cs index 98cf7c74..17db2aef 100644 --- a/src/Microsoft.Extensions.Caching.Memory/CacheEntry.cs +++ b/src/Microsoft.Extensions.Caching.Memory/CacheEntry.cs @@ -175,7 +175,9 @@ public bool CheckExpired(DateTimeOffset now) return _isExpired || CheckForExpiredTime(now) || CheckForExpiredTokens(); } - public void SetExpired(EvictionReason reason) + public void Evict() => SetExpired(EvictionReason.Capacity); + + internal void SetExpired(EvictionReason reason) { if (EvictionReason == EvictionReason.None) { diff --git a/src/Microsoft.Extensions.Caching.Memory/Interfaces/IRetrievedCacheEntry.cs b/src/Microsoft.Extensions.Caching.Memory/Interfaces/IRetrievedCacheEntry.cs index 9fdecb1c..be88683d 100644 --- a/src/Microsoft.Extensions.Caching.Memory/Interfaces/IRetrievedCacheEntry.cs +++ b/src/Microsoft.Extensions.Caching.Memory/Interfaces/IRetrievedCacheEntry.cs @@ -39,6 +39,6 @@ public interface IRetrievedCacheEntry bool CheckExpired(DateTimeOffset utcNow); - void SetExpired(EvictionReason reason); + void Evict(); } } \ No newline at end of file diff --git a/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs index f35009e7..cd01b854 100644 --- a/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs @@ -117,7 +117,7 @@ private void SetEntry(CacheEntry entry) IRetrievedCacheEntry priorEntry; if (_entries.TryGetValue(entry.Key, out priorEntry)) { - priorEntry.SetExpired(EvictionReason.Replaced); + ((CacheEntry)priorEntry).SetExpired(EvictionReason.Replaced); } if (!entry.CheckExpired(utcNow)) @@ -225,8 +225,9 @@ public void Remove(object key) IRetrievedCacheEntry entry; if (_entries.TryRemove(key, out entry)) { - entry.SetExpired(EvictionReason.Removed); - ((CacheEntry)entry).InvokeEvictionCallbacks(); + var cacheEntry = (CacheEntry)entry; + cacheEntry.SetExpired(EvictionReason.Removed); + cacheEntry.InvokeEvictionCallbacks(); } _evictionTrigger.Resume(this); From 89aa0b8a175f7a958bdc34763018a981dab91795 Mon Sep 17 00:00:00 2001 From: John Luo Date: Wed, 22 Mar 2017 22:22:22 -0700 Subject: [PATCH 21/22] Box things nicely --- .../CapacityMemoryCacheEvictionTrigger.cs | 6 +-- .../LRUMemoryCacheEvictionStrategy.cs | 8 ++-- .../CacheEntry.cs | 2 +- .../IMemoryCacheEvictionStrategy.cs | 2 +- .../Interfaces/IMemoryCacheEvictionTrigger.cs | 2 +- ...edCacheEntry.cs => IReadOnlyCacheEntry.cs} | 2 +- .../MemoryCache.cs | 39 +++++++------------ .../MemoryCacheEvictionStrategy.cs | 4 +- .../MemoryCacheEvictionTrigger.cs | 2 +- .../ReadOnlyCacheEntries.cs | 34 ++++++++++++++++ 10 files changed, 62 insertions(+), 39 deletions(-) rename src/Microsoft.Extensions.Caching.Memory/Interfaces/{IRetrievedCacheEntry.cs => IReadOnlyCacheEntry.cs} (96%) create mode 100644 src/Microsoft.Extensions.Caching.Memory/ReadOnlyCacheEntries.cs diff --git a/samples/MemoryCacheSample/CapacityMemoryCacheEvictionTrigger.cs b/samples/MemoryCacheSample/CapacityMemoryCacheEvictionTrigger.cs index af3626b6..dd3d65ed 100644 --- a/samples/MemoryCacheSample/CapacityMemoryCacheEvictionTrigger.cs +++ b/samples/MemoryCacheSample/CapacityMemoryCacheEvictionTrigger.cs @@ -23,12 +23,12 @@ public CapacityMemoryCacheEvictionTrigger(int entryLimit) public void Dispose() { } - public void Resume(IReadOnlyCollection> entries) + public void Resume(IReadOnlyCollection entries) { if (entries.Count > _entryLimit) { Task.Factory.StartNew( - state => StartEviction((IReadOnlyCollection>)state), + state => StartEviction((IReadOnlyCollection)state), entries, CancellationToken.None, TaskCreationOptions.DenyChildAttach, @@ -41,7 +41,7 @@ public void SetEvictionCallback(Func evictionCallback) _evictionCallback = evictionCallback; } - private void StartEviction(IReadOnlyCollection> entries) + private void StartEviction(IReadOnlyCollection entries) { // Don't run too often if (Monitor.TryEnter(_evictionlock)) diff --git a/samples/MemoryCacheSample/LRUMemoryCacheEvictionStrategy.cs b/samples/MemoryCacheSample/LRUMemoryCacheEvictionStrategy.cs index 2c7b6d56..37d38fb4 100644 --- a/samples/MemoryCacheSample/LRUMemoryCacheEvictionStrategy.cs +++ b/samples/MemoryCacheSample/LRUMemoryCacheEvictionStrategy.cs @@ -20,7 +20,7 @@ public LRUMemoryCacheEvictionStrategy(int entryLimit) _evictExpiredStrategy = new MemoryCacheEvictionStrategy(); } - public int Evict(IReadOnlyCollection> entries, DateTimeOffset utcNow) + public int Evict(IReadOnlyCollection entries, DateTimeOffset utcNow) { var expiredCount = _evictExpiredStrategy.Evict(entries, utcNow); var removalTarget = entries.Count - expiredCount - _entryLimit; // assume underflow is handled @@ -31,11 +31,11 @@ public int Evict(IReadOnlyCollection> } var removedEntries = 0; - foreach (var entry in entries.OrderBy(e => e.Value.LastAccessed)) + foreach (var entry in entries.OrderBy(e => e.LastAccessed)) { - if (!entry.Value.IsExpired) + if (!entry.IsExpired) { - entry.Value.Evict(); + entry.Evict(); removedEntries++; } if (removedEntries == removalTarget) diff --git a/src/Microsoft.Extensions.Caching.Memory/CacheEntry.cs b/src/Microsoft.Extensions.Caching.Memory/CacheEntry.cs index 17db2aef..3fd4af7b 100644 --- a/src/Microsoft.Extensions.Caching.Memory/CacheEntry.cs +++ b/src/Microsoft.Extensions.Caching.Memory/CacheEntry.cs @@ -9,7 +9,7 @@ namespace Microsoft.Extensions.Caching.Memory { - internal class CacheEntry : ICacheEntry, IRetrievedCacheEntry + internal class CacheEntry : ICacheEntry, IReadOnlyCacheEntry { private bool _added = false; private static readonly Action ExpirationCallback = ExpirationTokensExpired; diff --git a/src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionStrategy.cs b/src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionStrategy.cs index 0b6b8a40..c644d54c 100644 --- a/src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionStrategy.cs +++ b/src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionStrategy.cs @@ -9,6 +9,6 @@ namespace Microsoft.Extensions.Caching.Memory public interface IMemoryCacheEvictionStrategy { // TODO: doc comments - int Evict(IReadOnlyCollection> entries, DateTimeOffset utcNow); + int Evict(IReadOnlyCollection entries, DateTimeOffset utcNow); } } diff --git a/src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionTrigger.cs b/src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionTrigger.cs index 2123e5ec..3df26c57 100644 --- a/src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionTrigger.cs +++ b/src/Microsoft.Extensions.Caching.Memory/Interfaces/IMemoryCacheEvictionTrigger.cs @@ -11,6 +11,6 @@ public interface IMemoryCacheEvictionTrigger : IDisposable // TODO: doc comments void SetEvictionCallback(Func evictionCallback); - void Resume(IReadOnlyCollection> entries); + void Resume(IReadOnlyCollection entries); } } diff --git a/src/Microsoft.Extensions.Caching.Memory/Interfaces/IRetrievedCacheEntry.cs b/src/Microsoft.Extensions.Caching.Memory/Interfaces/IReadOnlyCacheEntry.cs similarity index 96% rename from src/Microsoft.Extensions.Caching.Memory/Interfaces/IRetrievedCacheEntry.cs rename to src/Microsoft.Extensions.Caching.Memory/Interfaces/IReadOnlyCacheEntry.cs index be88683d..20068793 100644 --- a/src/Microsoft.Extensions.Caching.Memory/Interfaces/IRetrievedCacheEntry.cs +++ b/src/Microsoft.Extensions.Caching.Memory/Interfaces/IReadOnlyCacheEntry.cs @@ -8,7 +8,7 @@ namespace Microsoft.Extensions.Caching.Memory /// /// Represents an entry retrieved from the implementation. /// - public interface IRetrievedCacheEntry + public interface IReadOnlyCacheEntry { /// /// Gets the key of the cache entry. diff --git a/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs index cd01b854..b8ff7d35 100644 --- a/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using Microsoft.Extensions.Internal; @@ -14,9 +13,9 @@ namespace Microsoft.Extensions.Caching.Memory /// An implementation of using a dictionary to /// store its entries. /// - public class MemoryCache : IMemoryCache, IReadOnlyCollection> + public partial class MemoryCache : IMemoryCache { - private readonly ConcurrentDictionary _entries; + private readonly ConcurrentDictionary _entries; private bool _disposed; // We store the delegates locally to prevent allocations @@ -40,7 +39,7 @@ public MemoryCache(IOptions optionsAccessor) } var options = optionsAccessor.Value; - _entries = new ConcurrentDictionary(); + _entries = new ConcurrentDictionary(); _setEntry = SetEntry; _entryExpirationNotification = EntryExpired; @@ -66,7 +65,7 @@ public int Count get { return _entries.Count; } } - private ICollection> EntriesCollection => _entries; + private ICollection> EntriesCollection => _entries; /// public ICacheEntry CreateEntry(object key) @@ -114,7 +113,7 @@ private void SetEntry(CacheEntry entry) // Initialize the last access timestamp at the time the entry is added entry.LastAccessed = utcNow; - IRetrievedCacheEntry priorEntry; + IReadOnlyCacheEntry priorEntry; if (_entries.TryGetValue(entry.Key, out priorEntry)) { ((CacheEntry)priorEntry).SetExpired(EvictionReason.Replaced); @@ -167,7 +166,7 @@ private void SetEntry(CacheEntry entry) } } - _evictionTrigger.Resume(this); + _evictionTrigger.Resume(new ReadOnlyCacheEntries(_entries)); } /// @@ -184,7 +183,7 @@ public bool TryGetValue(object key, out object result) var utcNow = _clock.UtcNow; var found = false; - IRetrievedCacheEntry retrievedEntry; + IReadOnlyCacheEntry retrievedEntry; if (_entries.TryGetValue(key, out retrievedEntry)) { var entry = (CacheEntry)retrievedEntry; @@ -208,7 +207,7 @@ public bool TryGetValue(object key, out object result) } } - _evictionTrigger.Resume(this); + _evictionTrigger.Resume(new ReadOnlyCacheEntries(_entries)); return found; } @@ -222,7 +221,7 @@ public void Remove(object key) } CheckDisposed(); - IRetrievedCacheEntry entry; + IReadOnlyCacheEntry entry; if (_entries.TryRemove(key, out entry)) { var cacheEntry = (CacheEntry)entry; @@ -230,12 +229,12 @@ public void Remove(object key) cacheEntry.InvokeEvictionCallbacks(); } - _evictionTrigger.Resume(this); + _evictionTrigger.Resume(new ReadOnlyCacheEntries(_entries)); } - private void RemoveEntry(IRetrievedCacheEntry entry) + private void RemoveEntry(IReadOnlyCacheEntry entry) { - if (EntriesCollection.Remove(new KeyValuePair(entry.Key, entry))) + if (EntriesCollection.Remove(new KeyValuePair(entry.Key, entry))) { ((CacheEntry)entry).InvokeEvictionCallbacks(); } @@ -245,12 +244,12 @@ private void EntryExpired(CacheEntry entry) { // TODO: For efficiency consider processing these expirations in batches. RemoveEntry(entry); - _evictionTrigger.Resume(this); + _evictionTrigger.Resume(new ReadOnlyCacheEntries(_entries)); } private int ExecuteCacheEviction() { - var evictCount = _evictionStrategy.Evict(this, _clock.UtcNow); // TODO: anything else eviction strategies need? + var evictCount = _evictionStrategy.Evict(new ReadOnlyCacheEntries(_entries), _clock.UtcNow); // TODO: anything else eviction strategies need? foreach (var entry in _entries) { @@ -290,15 +289,5 @@ private void CheckDisposed() throw new ObjectDisposedException(typeof(MemoryCache).FullName); } } - - public IEnumerator> GetEnumerator() - { - return _entries.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } } } \ No newline at end of file diff --git a/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionStrategy.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionStrategy.cs index 3085c93f..8818e923 100644 --- a/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionStrategy.cs +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionStrategy.cs @@ -8,13 +8,13 @@ namespace Microsoft.Extensions.Caching.Memory { public class MemoryCacheEvictionStrategy : IMemoryCacheEvictionStrategy { - public int Evict(IReadOnlyCollection> entries, DateTimeOffset utcNow) + public int Evict(IReadOnlyCollection entries, DateTimeOffset utcNow) { var expiredEntries = 0; foreach (var entry in entries) { - if (entry.Value.CheckExpired(utcNow)) + if (entry.CheckExpired(utcNow)) { expiredEntries++; } diff --git a/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionTrigger.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionTrigger.cs index 085f8c32..26f4ea68 100644 --- a/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionTrigger.cs +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCacheEvictionTrigger.cs @@ -55,7 +55,7 @@ public void SetEvictionCallback(Func evictionCallback) _evictionCallback = evictionCallback; } - public void Resume(IReadOnlyCollection> entries) + public void Resume(IReadOnlyCollection entries) { Interlocked.Exchange(ref _intervalsWithoutEviction, 0); diff --git a/src/Microsoft.Extensions.Caching.Memory/ReadOnlyCacheEntries.cs b/src/Microsoft.Extensions.Caching.Memory/ReadOnlyCacheEntries.cs new file mode 100644 index 00000000..280907af --- /dev/null +++ b/src/Microsoft.Extensions.Caching.Memory/ReadOnlyCacheEntries.cs @@ -0,0 +1,34 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; + +namespace Microsoft.Extensions.Caching.Memory +{ + internal class ReadOnlyCacheEntries : IReadOnlyCollection + { + private readonly ConcurrentDictionary _entries; + + internal ReadOnlyCacheEntries(ConcurrentDictionary entries) + { + _entries = entries; + } + + public int Count => _entries.Count; + + public IEnumerator GetEnumerator() + { + foreach (var entry in _entries) + { + yield return entry.Value; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} \ No newline at end of file From f8548207b1d50f83756bc506b89f0523f00d2ff0 Mon Sep 17 00:00:00 2001 From: John Luo Date: Thu, 23 Mar 2017 10:18:20 -0700 Subject: [PATCH 22/22] Poor refactoring --- src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs b/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs index b8ff7d35..57fe0770 100644 --- a/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs +++ b/src/Microsoft.Extensions.Caching.Memory/MemoryCache.cs @@ -13,7 +13,7 @@ namespace Microsoft.Extensions.Caching.Memory /// An implementation of using a dictionary to /// store its entries. /// - public partial class MemoryCache : IMemoryCache + public class MemoryCache : IMemoryCache { private readonly ConcurrentDictionary _entries; private bool _disposed;