Quick.MemoryCache.pas 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764
  1. { ***************************************************************************
  2. Copyright (c) 2016-2021 Kike Pérez
  3. Unit : Quick.MemoryCache
  4. Description : Cache objects with expiration control
  5. Author : Kike Pérez
  6. Version : 1.0
  7. Created : 14/07/2019
  8. Modified : 17/05/2021
  9. This file is part of QuickLib: https://github.com/exilon/QuickLib
  10. ***************************************************************************
  11. Licensed under the Apache License, Version 2.0 (the "License");
  12. you may not use this file except in compliance with the License.
  13. You may obtain a copy of the License at
  14. http://www.apache.org/licenses/LICENSE-2.0
  15. Unless required by applicable law or agreed to in writing, software
  16. distributed under the License is distributed on an "AS IS" BASIS,
  17. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18. See the License for the specific language governing permissions and
  19. limitations under the License.
  20. *************************************************************************** }
  21. unit Quick.MemoryCache;
  22. {$i QuickLib.inc}
  23. interface
  24. uses
  25. System.SysUtils,
  26. System.Generics.Collections,
  27. System.DateUtils,
  28. System.TypInfo,
  29. RTTI,
  30. Quick.Commons,
  31. Quick.Value,
  32. Quick.Threads,
  33. Quick.Cache.Intf,
  34. Quick.MemoryCache.Types,
  35. Quick.MemoryCache.Serializer.Json,
  36. Quick.MemoryCache.Compressor.GZip;
  37. type
  38. TCacheFlushedEvent = reference to procedure(aRemovedEntries : Integer);
  39. TBeginPurgerJobEvent = reference to procedure;
  40. TEndPurgerJobEvent = reference to procedure(aPurgedEntries : Integer);
  41. TPurgerJobErrorEvent = reference to procedure(const aErrorMsg : string);
  42. IMemoryCache<T> = interface
  43. ['{57927AD7-C993-4C3C-B552-43A39F99E73A}']
  44. function GetCompression: Boolean;
  45. procedure SetCompression(const Value: Boolean);
  46. function GetCachedObjects: Integer;
  47. function GetCacheSize: Integer;
  48. procedure SetOnBeginPurgerJob(const Value: TBeginPurgerJobEvent);
  49. procedure SetOnCacheFlushed(const Value: TCacheFlushedEvent);
  50. procedure SetOnEndPurgerJob(const Value: TEndPurgerJobEvent);
  51. procedure SetOnPurgerJobError(const Value: TPurgerJobErrorEvent);
  52. property Compression : Boolean read GetCompression write SetCompression;
  53. property CachedObjects : Integer read GetCachedObjects;
  54. property CacheSize : Integer read GetCacheSize;
  55. property OnCacheFlushed : TCacheFlushedEvent write SetOnCacheFlushed;
  56. property OnBeginPurgerJob : TBeginPurgerJobEvent write SetOnBeginPurgerJob;
  57. property OnEndPurgerJob : TEndPurgerJobEvent write SetOnEndPurgerJob;
  58. property OnPurgeJobError : TPurgerJobErrorEvent write SetOnPurgerJobError;
  59. procedure SetValue(const aKey : string; aValue : T; aExpirationMillisecons : Integer = 0); overload;
  60. procedure SetValue(const aKey : string; aValue : T; aExpirationDate : TDateTime); overload;
  61. function GetValue(const aKey : string) : T;
  62. function TryGetValue(const aKey : string; out aValue : T) : Boolean;
  63. procedure RemoveValue(const aKey : string);
  64. procedure Refresh(const aKey: string; aExpirationMilliseconds : Integer);
  65. procedure Flush;
  66. end;
  67. IMemoryCache = interface(ICache)
  68. ['{F109AE78-43D7-4983-B8ED-52A41533EEED}']
  69. function GetCompression: Boolean;
  70. procedure SetCompression(const Value: Boolean);
  71. function GetCachedObjects: Integer;
  72. function GetCacheSize: Integer;
  73. procedure SetOnBeginPurgerJob(const Value: TBeginPurgerJobEvent);
  74. procedure SetOnCacheFlushed(const Value: TCacheFlushedEvent);
  75. procedure SetOnEndPurgerJob(const Value: TEndPurgerJobEvent);
  76. procedure SetOnPurgerJobError(const Value: TPurgerJobErrorEvent);
  77. property Compression : Boolean read GetCompression write SetCompression;
  78. property CachedObjects : Integer read GetCachedObjects;
  79. property CacheSize : Integer read GetCacheSize;
  80. property OnCacheFlushed : TCacheFlushedEvent write SetOnCacheFlushed;
  81. property OnBeginPurgerJob : TBeginPurgerJobEvent write SetOnBeginPurgerJob;
  82. property OnEndPurgerJob : TEndPurgerJobEvent write SetOnEndPurgerJob;
  83. property OnPurgeJobError : TPurgerJobErrorEvent write SetOnPurgerJobError;
  84. procedure SetValue(const aKey : string; aValue : TObject; aExpirationMilliseconds : Integer = 0); overload;
  85. procedure SetValue(const aKey : string; aValue : TObject; aExpirationDate : TDateTime); overload;
  86. procedure SetValue(const aKey, aValue : string; aExpirationMilliseconds : Integer = 0); overload;
  87. procedure SetValue(const aKey, aValue : string; aExpirationDate : TDateTime); overload;
  88. procedure SetValue(const aKey : string; aValue : TArray<string>; aExpirationMilliseconds : Integer = 0); overload;
  89. procedure SetValue(const aKey : string; aValue : TArray<string>; aExpirationDate : TDateTime); overload;
  90. procedure SetValue(const aKey : string; aValue : TArray<TObject>; aExpirationMilliseconds : Integer = 0); overload;
  91. procedure SetValue(const aKey : string; aValue : TArray<TObject>; aExpirationDate : TDateTime); overload;
  92. function GetValue(const aKey : string) : string; overload;
  93. function TryGetValue(const aKey : string; aValue : TObject) : Boolean; overload;
  94. function TryGetValue(const aKey : string; out aValue : string) : Boolean; overload;
  95. function TryGetValue(const aKey : string; out aValue : TArray<string>) : Boolean; overload;
  96. function TryGetValue(const aKey : string; out aValue : TArray<TObject>) : Boolean; overload;
  97. procedure RemoveValue(const aKey : string);
  98. procedure Refresh(const aKey: string; aExpirationMilliseconds : Integer);
  99. procedure Flush;
  100. end;
  101. TCacheEntry = class(TInterfacedObject,ICacheEntry)
  102. private
  103. fCreationDate : TDateTime;
  104. fExpiration : Cardinal;
  105. fExpirationDate : TDateTime;
  106. fCompression : Boolean;
  107. fCompressor : ICacheCompressor;
  108. fData : string;
  109. fIsCompressed : Boolean;
  110. function GetCreationDate: TDateTime;
  111. function GetData: string;
  112. function GetExpiration: Cardinal;
  113. procedure SetCreationDate(const Value: TDateTime);
  114. procedure SetData(const Value: string);
  115. procedure SetExpiration(aMilliseconds : Cardinal);
  116. function GetExpirationDate: TDateTime;
  117. procedure SetExpirationDate(const Value: TDateTime);
  118. public
  119. constructor Create(aCompression : Boolean; aCacheCompressor : ICacheCompressor);
  120. property CreationDate : TDateTime read GetCreationDate write SetCreationDate;
  121. property Expiration : Cardinal read GetExpiration write SetExpiration;
  122. property ExpirationDate : TDateTime read GetExpirationDate write SetExpirationDate;
  123. property Data : string read GetData write SetData;
  124. function Size : Integer;
  125. function IsExpired : Boolean;
  126. end;
  127. TMemoryCacheBase = class(TInterfacedObject)
  128. private
  129. fPurgerInterval : Integer;
  130. fMaxSize : Integer;
  131. fCachedObjects : Integer;
  132. fCacheSize : Integer;
  133. fCompression : Boolean;
  134. fLock : TMultiReadExclusiveWriteSynchronizer;
  135. fCacheJobs : TScheduledTasks;
  136. fOnCacheFlushed : TCacheFlushedEvent;
  137. fOnPurgerJobError : TPurgerJobErrorEvent;
  138. fOnBeginPurgerJob : TBeginPurgerJobEvent;
  139. fOnEndPurgerJob : TEndPurgerJobEvent;
  140. procedure CreatePurgerJobs;
  141. procedure RemoveExpiredCacheEntries; virtual;
  142. procedure SetPurgerInterval(const Value: Integer);
  143. protected
  144. fItems : TDictionary<string,ICacheEntry>;
  145. fSerializer : ICacheSerializer;
  146. fCompressor : ICacheCompressor;
  147. function GetCachedObjects: Integer;
  148. function GetCacheSize: Integer;
  149. procedure SetOnBeginPurgerJob(const Value: TBeginPurgerJobEvent);
  150. procedure SetOnCacheFlushed(const Value: TCacheFlushedEvent);
  151. procedure SetOnEndPurgerJob(const Value: TEndPurgerJobEvent);
  152. procedure SetOnPurgerJobError(const Value: TPurgerJobErrorEvent);
  153. function GetCompression: Boolean;
  154. procedure SetCompression(const Value: Boolean);
  155. public
  156. constructor Create(aPurgerInterval : Integer = 20; aCacheSerializer : ICacheSerializer = nil; aCacheCompressor : ICacheCompressor = nil); virtual;
  157. destructor Destroy; override;
  158. property MaxSize : Integer read fMaxSize write fMaxSize;
  159. property PurgerInterval : Integer read fPurgerInterval;
  160. property Compression : Boolean read GetCompression write SetCompression;
  161. property CachedObjects : Integer read GetCachedObjects;
  162. property CacheSize : Integer read GetCacheSize;
  163. property OnCacheFlushed : TCacheFlushedEvent read fOnCacheFlushed write SetOnCacheFlushed;
  164. property OnBeginPurgerJob : TBeginPurgerJobEvent read fOnBeginPurgerJob write SetOnBeginPurgerJob;
  165. property OnEndPurgerJob : TEndPurgerJobEvent read fOnEndPurgerJob write SetOnEndPurgerJob;
  166. property OnPurgeJobError : TPurgerJobErrorEvent read fOnPurgerJobError write SetOnPurgerJobError;
  167. procedure RemoveValue(const aKey : string); virtual;
  168. procedure Refresh(const aKey: string; aExpirationMilliseconds : Integer);
  169. procedure Flush; virtual;
  170. end;
  171. TMemoryCache<T> = class(TMemoryCacheBase,IMemoryCache<T>)
  172. private
  173. procedure SetValue(const aKey : string; aValue : T; aExpirationMilliseconds : Integer; aExpirationDate : TDateTime); overload;
  174. public
  175. constructor Create(aPurgerInterval : Integer = 20; aCacheSerializer : ICacheSerializer = nil; aCacheCompressor : ICacheCompressor = nil); override;
  176. procedure SetValue(const aKey : string; aValue : T; aExpirationMillisecons : Integer = 0); overload;
  177. procedure SetValue(const aKey : string; aValue : T; aExpirationDate : TDateTime); overload;
  178. function GetValue(const aKey : string) : T;
  179. function TryGetValue(const aKey : string; out oValue : T) : Boolean;
  180. procedure RemoveValue(const aKey : string); override;
  181. end;
  182. TMemoryCache = class(TMemoryCacheBase,IMemoryCache)
  183. private
  184. procedure SetValue(const aKey: string; aValue: TObject; aExpirationMilliseconds : Integer; aExpirationDate : TDateTime); overload;
  185. procedure SetValue(const aKey, aValue: string; aExpirationMilliseconds : Integer; aExpirationDate : TDateTime); overload;
  186. public
  187. procedure SetValue(const aKey, aValue : string; aExpirationMilliseconds : Integer = 0); overload;
  188. procedure SetValue(const aKey, aValue : string; aExpirationDate : TDateTime); overload;
  189. procedure SetValue(const aKey : string; aValue : TObject; aExpirationMilliseconds : Integer = 0); overload;
  190. procedure SetValue(const aKey : string; aValue : TObject; aExpirationDate : TDateTime); overload;
  191. procedure SetValue(const aKey : string; aValue : TArray<string>; aExpirationMilliseconds : Integer = 0); overload;
  192. procedure SetValue(const aKey : string; aValue : TArray<string>; aExpirationDate : TDateTime); overload;
  193. procedure SetValue(const aKey : string; aValue : TArray<TObject>; aExpirationMilliseconds : Integer = 0); overload;
  194. procedure SetValue(const aKey : string; aValue : TArray<TObject>; aExpirationDate : TDateTime); overload;
  195. function GetValue(const aKey : string) : string; overload;
  196. function TryGetValue(const aKey : string; out aValue : string) : Boolean; overload;
  197. function TryGetValue(const aKey : string; aValue : TObject) : Boolean; overload;
  198. function TryGetValue<T>(const aKey : string; out oValue : T) : Boolean; overload;
  199. function TryGetValue(const aKey : string; out aValue : TArray<string>) : Boolean; overload;
  200. function TryGetValue(const aKey : string; out aValue : TArray<TObject>) : Boolean; overload;
  201. end;
  202. EMemoryCacheConfigError = class(Exception);
  203. EMemoryCacheSetError = class(Exception);
  204. EMemoryCacheGetError = class(Exception);
  205. EMemoryCacheFlushError = class(Exception);
  206. implementation
  207. { TMemoryCacheBase }
  208. constructor TMemoryCacheBase.Create(aPurgerInterval : Integer = 20; aCacheSerializer : ICacheSerializer = nil; aCacheCompressor : ICacheCompressor = nil);
  209. begin
  210. fCompression := True;
  211. SetPurgerInterval(aPurgerInterval);
  212. fCachedObjects := 0;
  213. fCacheSize := 0;
  214. fLock := TMultiReadExclusiveWriteSynchronizer.Create;
  215. if aCacheSerializer <> nil then fSerializer := aCacheSerializer
  216. else fSerializer := TCacheJsonSerializer.Create;
  217. if aCacheCompressor <> nil then fCompressor := aCacheCompressor
  218. else fCompressor := TCacheCompressorGZip.Create;
  219. fItems := TDictionary<string,ICacheEntry>.Create;
  220. fCacheJobs := TScheduledTasks.Create;
  221. CreatePurgerJobs;
  222. fCacheJobs.Start;
  223. end;
  224. procedure TMemoryCacheBase.CreatePurgerJobs;
  225. begin
  226. fCacheJobs.AddTask('RemoveExpired',procedure (task : ITask)
  227. begin
  228. RemoveExpiredCacheEntries;
  229. end
  230. ).OnException(procedure(task : ITask; aException : Exception)
  231. begin
  232. if Assigned(fOnPurgerJobError) then fOnPurgerJobError(aException.Message);
  233. end
  234. ).StartInSeconds(fPurgerInterval).RepeatEvery(fPurgerInterval,TTimeMeasure.tmSeconds);
  235. end;
  236. destructor TMemoryCacheBase.Destroy;
  237. begin
  238. fItems.Free;
  239. fLock.Free;
  240. fCacheJobs.Stop;
  241. fCacheJobs.Free;
  242. inherited;
  243. end;
  244. procedure TMemoryCacheBase.Flush;
  245. begin
  246. fLock.BeginWrite;
  247. try
  248. fItems.Clear;
  249. if Assigned(fOnCacheFlushed) then fOnCacheFlushed(fCachedObjects);
  250. fCachedObjects := 0;
  251. fCacheSize := 0;
  252. finally
  253. fLock.EndWrite;
  254. end;
  255. end;
  256. procedure TMemoryCacheBase.Refresh(const aKey: string; aExpirationMilliseconds : Integer);
  257. var
  258. cacheitem : ICacheEntry;
  259. begin
  260. if fItems.TryGetValue(aKey,cacheitem) then
  261. begin
  262. cacheitem.CreationDate := Now;
  263. cacheitem.Expiration := aExpirationMilliseconds;
  264. end;
  265. end;
  266. procedure TMemoryCacheBase.RemoveExpiredCacheEntries;
  267. var
  268. pair : TPair<string,ICacheEntry>;
  269. removedentries : Integer;
  270. begin
  271. if Assigned(fOnBeginPurgerJob) then fOnBeginPurgerJob;
  272. removedentries := 0;
  273. fLock.BeginRead;
  274. try
  275. for pair in fItems do
  276. begin
  277. if pair.Value.IsExpired then
  278. begin
  279. fLock.BeginWrite;
  280. try
  281. //decrease cacheitem size to cachesize
  282. AtomicDecrement(fCacheSize,pair.Value.Size);
  283. //remove cacheitem from cache
  284. fItems.Remove(pair.Key);
  285. //decrease cachedobjects
  286. AtomicDecrement(fCachedObjects,1);
  287. Inc(removedentries);
  288. finally
  289. fLock.EndWrite;
  290. end;
  291. end;
  292. end;
  293. finally
  294. fLock.EndRead;
  295. if Assigned(fOnEndPurgerJob) then fOnEndPurgerJob(removedentries);
  296. end;
  297. end;
  298. procedure TMemoryCacheBase.RemoveValue(const aKey: string);
  299. var
  300. cacheitem : ICacheEntry;
  301. begin
  302. if fItems.TryGetValue(aKey,cacheitem) then
  303. begin
  304. //decrease cacheitem size to cachesize
  305. AtomicDecrement(fCacheSize,cacheitem.Size);
  306. //remove cacheitem from cache
  307. fItems.Remove(aKey);
  308. //decrease cachedobjects
  309. AtomicDecrement(fCachedObjects,1);
  310. end;
  311. end;
  312. function TMemoryCacheBase.GetCachedObjects: Integer;
  313. begin
  314. Result := fCachedObjects;
  315. end;
  316. function TMemoryCacheBase.GetCacheSize: Integer;
  317. begin
  318. Result := fCacheSize;
  319. end;
  320. function TMemoryCacheBase.GetCompression: Boolean;
  321. begin
  322. Result := fCompression;
  323. end;
  324. procedure TMemoryCacheBase.SetCompression(const Value: Boolean);
  325. begin
  326. fCompression := Value;
  327. end;
  328. procedure TMemoryCacheBase.SetOnBeginPurgerJob(const Value: TBeginPurgerJobEvent);
  329. begin
  330. fOnBeginPurgerJob := Value;
  331. end;
  332. procedure TMemoryCacheBase.SetOnCacheFlushed(const Value: TCacheFlushedEvent);
  333. begin
  334. fOnCacheFlushed := Value;
  335. end;
  336. procedure TMemoryCacheBase.SetOnEndPurgerJob(const Value: TEndPurgerJobEvent);
  337. begin
  338. fOnEndPurgerJob := Value;
  339. end;
  340. procedure TMemoryCacheBase.SetOnPurgerJobError(const Value: TPurgerJobErrorEvent);
  341. begin
  342. fOnPurgerJobError := Value;
  343. end;
  344. procedure TMemoryCacheBase.SetPurgerInterval(const Value: Integer);
  345. begin
  346. if Value > 5 then
  347. begin
  348. fPurgerInterval := Value;
  349. end
  350. else raise EMemoryCacheConfigError.Create('Purger Interval must be greater than 5 seconds');
  351. end;
  352. { TCacheItem }
  353. constructor TCacheEntry.Create(aCompression : Boolean; aCacheCompressor : ICacheCompressor);
  354. begin
  355. fIsCompressed := False;
  356. fCompression := aCompression;
  357. fCompressor := aCacheCompressor;
  358. end;
  359. function TCacheEntry.GetCreationDate: TDateTime;
  360. begin
  361. Result := fCreationDate;
  362. end;
  363. function TCacheEntry.GetData: string;
  364. begin
  365. if fIsCompressed then Result := fCompressor.Decompress(fData)
  366. else Result := fData;
  367. end;
  368. function TCacheEntry.GetExpiration: Cardinal;
  369. begin
  370. Result := fExpiration;
  371. end;
  372. function TCacheEntry.GetExpirationDate: TDateTime;
  373. begin
  374. Result := fExpirationDate;
  375. end;
  376. procedure TCacheEntry.SetCreationDate(const Value: TDateTime);
  377. begin
  378. fCreationDate := Value;
  379. end;
  380. procedure TCacheEntry.SetExpiration(aMilliseconds: Cardinal);
  381. begin
  382. fExpiration := aMilliseconds;
  383. fExpirationDate := IncMilliSecond(fCreationDate,fExpiration);
  384. end;
  385. procedure TCacheEntry.SetExpirationDate(const Value: TDateTime);
  386. begin
  387. fExpiration := MilliSecondOf(Value);
  388. fExpirationDate := Value;
  389. end;
  390. function TCacheEntry.IsExpired: Boolean;
  391. begin
  392. if fExpiration = 0 then Result := False
  393. else Result := Now() > fExpirationDate;
  394. end;
  395. procedure TCacheEntry.SetData(const Value: string);
  396. begin
  397. fIsCompressed := False;
  398. //var a := value;
  399. //var b := value.Length;
  400. if fCompression then
  401. begin
  402. if ((Value.Length + 1) * 2) > 1024 then
  403. begin
  404. fData := fCompressor.Compress(Value);
  405. fIsCompressed := True;
  406. end
  407. else
  408. begin
  409. fData := Value;
  410. end;
  411. end
  412. else fData := Value;
  413. end;
  414. function TCacheEntry.Size: Integer;
  415. begin
  416. //Result := (fData.Length + 1) * SizeOf(Char);
  417. Result := (fData.Length + 1) * StringElementSize(fData);
  418. end;
  419. { TMemoryCache<T> }
  420. constructor TMemoryCache<T>.Create(aPurgerInterval: Integer; aCacheSerializer: ICacheSerializer; aCacheCompressor: ICacheCompressor);
  421. begin
  422. inherited Create(aPurgerInterval,aCacheSerializer,aCacheCompressor);
  423. end;
  424. function TMemoryCache<T>.GetValue(const aKey: string): T;
  425. begin
  426. TryGetValue(aKey,Result);
  427. end;
  428. procedure TMemoryCache<T>.RemoveValue(const aKey: string);
  429. begin
  430. inherited RemoveValue(aKey);
  431. end;
  432. procedure TMemoryCache<T>.SetValue(const aKey: string; aValue: T; aExpirationDate: TDateTime);
  433. begin
  434. SetValue(aKey,aValue,0,aExpirationDate);
  435. end;
  436. procedure TMemoryCache<T>.SetValue(const aKey: string; aValue: T; aExpirationMillisecons: Integer);
  437. begin
  438. SetValue(aKey,aValue,aExpirationMillisecons,0.0);
  439. end;
  440. procedure TMemoryCache<T>.SetValue(const aKey: string; aValue: T; aExpirationMilliseconds : Integer; aExpirationDate : TDateTime);
  441. var
  442. cacheitem : TCacheEntry;
  443. begin
  444. fLock.BeginWrite;
  445. try
  446. cacheitem := TCacheEntry.Create(fCompression,fCompressor);
  447. cacheitem.CreationDate := Now();
  448. cacheitem.Expiration := aExpirationMilliseconds;
  449. if aExpirationDate > 0.0 then cacheitem.ExpirationDate := aExpirationDate;
  450. //add object to cache
  451. case PTypeInfo(TypeInfo(T))^.Kind of
  452. tkClass, tkPointer :
  453. begin
  454. //object type need to be serialized
  455. cacheitem.Data := fSerializer.Serialize(PObject(@aValue)^);
  456. end;
  457. tkString, tkWideString, tkUString, tkChar, tkWideChar : cacheitem.Data := string((@aValue)^);
  458. {$IFNDEF NEXTGEN}
  459. tkAnsiString : cacheitem.Data := string(AnsiString((@aValue)^));
  460. {$ENDIF}
  461. else
  462. begin
  463. raise EMemoryCacheSetError.Create('Type not supported as cache');
  464. end;
  465. end;
  466. RemoveValue(aKey);
  467. fItems.Add(aKey,cacheitem);
  468. //add cacheitem size to cachesize
  469. AtomicIncrement(fCacheSize,cacheitem.Size);
  470. //increment cacheobjects
  471. AtomicIncrement(fCachedObjects,1);
  472. finally
  473. fLock.EndWrite;
  474. end;
  475. end;
  476. function TMemoryCache<T>.TryGetValue(const aKey: string; out oValue: T): Boolean;
  477. var
  478. cacheitem : ICacheEntry;
  479. flexvalue : TFlexValue;
  480. obj : TObject;
  481. begin
  482. fLock.BeginRead;
  483. try
  484. Result := fItems.TryGetValue(aKey,cacheitem);
  485. //check if cacheitem already expired
  486. if Result and cacheitem.IsExpired then Exit(False);
  487. finally
  488. fLock.EndRead;
  489. end;
  490. if Result then
  491. begin
  492. flexvalue.AsString := cacheitem.Data;
  493. case PTypeInfo(TypeInfo(T))^.Kind of
  494. tkInteger : oValue := TValue.From(flexvalue.AsInteger).AsType<T>;
  495. tkInt64 : oValue := TValue.From(flexvalue.AsInt64).AsType<T>;
  496. tkFloat :
  497. begin
  498. if TypeInfo(T) = TypeInfo(TDateTime) then oValue := TValue.From(flexvalue.AsDateTime).AsType<T>
  499. else oValue := TValue.From(flexvalue.AsExtended).AsType<T>;
  500. end;
  501. tkString,
  502. tkUString : oValue := TValue.From(flexvalue.AsString).AsType<T>;
  503. {$IFDEF MSWINDOWS}
  504. tkAnsiString : oValue := TValue.From(flexvalue.AsAnsiString).AsType<T>;
  505. tkWideString : oValue := TValue.From(flexvalue.AsWideString).AsType<T>;
  506. {$ENDIF}
  507. tkEnumeration :
  508. begin
  509. if TypeInfo(T) = TypeInfo(Boolean) then oValue := TValue.From(flexvalue.AsBoolean).AsType<T>
  510. else oValue := TValue.From(flexvalue.AsInteger).AsType<T>;
  511. end;
  512. tkClass, tkPointer :
  513. begin
  514. obj := PTypeInfo(TypeInfo(T))^.TypeData.ClassType.Create;
  515. fSerializer.Deserialize(cacheitem.Data,obj);
  516. oValue := TValue.From(obj).AsType<T>;
  517. //oValue := T((@obj)^);
  518. end
  519. else raise EMemoryCacheGetError.Create('Error casting value from cache');
  520. end;
  521. end;
  522. end;
  523. { TMemoryCache }
  524. function TMemoryCache.GetValue(const aKey: string): string;
  525. begin
  526. TryGetValue(aKey,Result);
  527. end;
  528. procedure TMemoryCache.SetValue(const aKey, aValue: string; aExpirationMilliseconds: Integer);
  529. begin
  530. SetValue(aKey,aValue,aExpirationMilliseconds,0.0);
  531. end;
  532. procedure TMemoryCache.SetValue(const aKey, aValue: string; aExpirationDate: TDateTime);
  533. begin
  534. SetValue(aKey,aValue,0,aExpirationDate);
  535. end;
  536. procedure TMemoryCache.SetValue(const aKey: string; aValue: TObject; aExpirationMilliseconds: Integer);
  537. begin
  538. SetValue(aKey,aValue,aExpirationMilliseconds,0.0);
  539. end;
  540. procedure TMemoryCache.SetValue(const aKey: string; aValue: TObject; aExpirationDate: TDateTime);
  541. begin
  542. SetValue(aKey,aValue,0,aExpirationDate);
  543. end;
  544. procedure TMemoryCache.SetValue(const aKey: string; aValue: TObject; aExpirationMilliseconds : Integer; aExpirationDate : TDateTime);
  545. begin
  546. SetValue(aKey,fSerializer.Serialize(aValue),aExpirationMilliseconds,aExpirationDate);
  547. end;
  548. procedure TMemoryCache.SetValue(const aKey, aValue: string; aExpirationMilliseconds : Integer; aExpirationDate : TDateTime);
  549. var
  550. cacheitem : TCacheEntry;
  551. begin
  552. fLock.BeginWrite;
  553. try
  554. cacheitem := TCacheEntry.Create(fCompression,fCompressor);
  555. cacheitem.CreationDate := Now();
  556. cacheitem.Expiration := aExpirationMilliseconds;
  557. if aExpirationDate > 0.0 then cacheitem.ExpirationDate := aExpirationDate;
  558. //add object to cache
  559. cacheitem.Data := aValue;
  560. RemoveValue(aKey);
  561. fItems.Add(aKey,cacheitem);
  562. //add cacheitem size to cachesize
  563. AtomicIncrement(fCacheSize,cacheitem.Size);
  564. //increment cacheobjects
  565. AtomicIncrement(fCachedObjects,1);
  566. finally
  567. fLock.EndWrite;
  568. end;
  569. end;
  570. procedure TMemoryCache.SetValue(const aKey: string; aValue: TArray<string>; aExpirationDate: TDateTime);
  571. begin
  572. SetValue(aKey,fSerializer.Serialize(aValue),0,aExpirationDate);
  573. end;
  574. procedure TMemoryCache.SetValue(const aKey: string; aValue: TArray<string>; aExpirationMilliseconds: Integer);
  575. begin
  576. SetValue(aKey,fSerializer.Serialize(aValue),aExpirationMilliseconds,0.0);
  577. end;
  578. procedure TMemoryCache.SetValue(const aKey: string; aValue: TArray<TObject>; aExpirationDate: TDateTime);
  579. begin
  580. SetValue(aKey,fSerializer.Serialize(aValue),0,aExpirationDate);
  581. end;
  582. procedure TMemoryCache.SetValue(const aKey: string; aValue: TArray<TObject>; aExpirationMilliseconds: Integer);
  583. begin
  584. SetValue(aKey,fSerializer.Serialize(aValue),aExpirationMilliseconds,0.0);
  585. end;
  586. function TMemoryCache.TryGetValue(const aKey: string; aValue : TObject): Boolean;
  587. var
  588. cacheitem : ICacheEntry;
  589. begin
  590. fLock.BeginRead;
  591. try
  592. if aValue = nil then raise EMemoryCacheGetError.Create('Cannot passed a nil object as param');
  593. Result := fItems.TryGetValue(aKey,cacheitem);
  594. //check if cacheitem already expired
  595. if (not Result) or (cacheitem.IsExpired) then Exit(False);
  596. finally
  597. fLock.EndRead;
  598. end;
  599. fSerializer.Deserialize(cacheitem.Data,aValue);
  600. end;
  601. function TMemoryCache.TryGetValue(const aKey: string; out aValue: string): Boolean;
  602. begin
  603. Result := TryGetValue<string>(aKey,aValue);
  604. end;
  605. function TMemoryCache.TryGetValue<T>(const aKey: string; out oValue: T): Boolean;
  606. var
  607. cacheitem : ICacheEntry;
  608. flexvalue : TFlexValue;
  609. obj : TObject;
  610. begin
  611. fLock.BeginRead;
  612. try
  613. Result := fItems.TryGetValue(aKey,cacheitem);
  614. //check if cacheitem already expired
  615. if Result and cacheitem.IsExpired then Exit(False);
  616. finally
  617. fLock.EndRead;
  618. end;
  619. if Result then
  620. begin
  621. flexvalue.AsString := cacheitem.Data;
  622. case PTypeInfo(TypeInfo(T))^.Kind of
  623. tkInteger : oValue := TValue.From(flexvalue.AsInteger).AsType<T>;
  624. tkInt64 : oValue := TValue.From(flexvalue.AsInt64).AsType<T>;
  625. tkFloat :
  626. begin
  627. if TypeInfo(T) = TypeInfo(TDateTime) then oValue := TValue.From(flexvalue.AsDateTime).AsType<T>
  628. else oValue := TValue.From(flexvalue.AsExtended).AsType<T>;
  629. end;
  630. tkString,
  631. tkUString : oValue := TValue.From(flexvalue.AsString).AsType<T>;
  632. {$IFDEF MSWINDOWS}
  633. tkAnsiString : oValue := TValue.From(flexvalue.AsAnsiString).AsType<T>;
  634. tkWideString : oValue := TValue.From(flexvalue.AsWideString).AsType<T>;
  635. {$ENDIF}
  636. tkEnumeration :
  637. begin
  638. if TypeInfo(T) = TypeInfo(Boolean) then oValue := TValue.From(flexvalue.AsBoolean).AsType<T>
  639. else oValue := TValue.From(flexvalue.AsInteger).AsType<T>;
  640. end;
  641. tkClass, tkPointer :
  642. begin
  643. obj := PTypeInfo(TypeInfo(T))^.TypeData.ClassType.Create;
  644. fSerializer.Deserialize(flexvalue.AsString,obj);
  645. oValue := TValue.From(obj).AsType<T>;
  646. end;
  647. else raise EMemoryCacheGetError.Create('Error casting value from cache');
  648. end;
  649. end;
  650. end;
  651. function TMemoryCache.TryGetValue(const aKey: string; out aValue: TArray<string>): Boolean;
  652. var
  653. cacheitem : ICacheEntry;
  654. begin
  655. fLock.BeginRead;
  656. try
  657. Result := fItems.TryGetValue(aKey,cacheitem);
  658. //check if cacheitem already expired
  659. if Result and cacheitem.IsExpired then Exit(False);
  660. finally
  661. fLock.EndRead;
  662. end;
  663. if Result then fSerializer.Deserialize(cacheitem.Data,aValue);
  664. end;
  665. function TMemoryCache.TryGetValue(const aKey: string; out aValue: TArray<TObject>): Boolean;
  666. var
  667. cacheitem : ICacheEntry;
  668. begin
  669. fLock.BeginRead;
  670. try
  671. Result := fItems.TryGetValue(aKey,cacheitem);
  672. //check if cacheitem already expired
  673. if Result and cacheitem.IsExpired then Exit(False);
  674. finally
  675. fLock.EndRead;
  676. end;
  677. if Result then fSerializer.Deserialize(cacheitem.Data,aValue);
  678. end;
  679. end.