forked from nhibernate/nhibernate-core
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathUpdateTimestampsCache.cs
111 lines (101 loc) · 3.41 KB
/
UpdateTimestampsCache.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
using System;
using System.Collections;
using System.Runtime.CompilerServices;
using Iesi.Collections;
using log4net;
using NHibernate.Cfg;
namespace NHibernate.Cache
{
/// <summary>
/// Tracks the timestamps of the most recent updates to particular tables. It is
/// important that the cache timeout of the underlying cache implementation be set
/// to a higher value than the timeouts of any of the query caches. In fact, we
/// recommend that the the underlying cache not be configured for expiry at all.
/// Note, in particular, that an LRU cache expiry policy is never appropriate.
/// </summary>
public class UpdateTimestampsCache
{
private static readonly ILog log = LogManager.GetLogger(typeof(UpdateTimestampsCache));
private ICache updateTimestamps;
private readonly string regionName = typeof(UpdateTimestampsCache).Name;
public void Clear()
{
updateTimestamps.Clear();
}
public UpdateTimestampsCache(Settings settings, IDictionary props)
{
string prefix = settings.CacheRegionPrefix;
regionName = prefix == null ? regionName : prefix + '.' + regionName;
log.Info("starting update timestamps cache at region: " + regionName);
this.updateTimestamps = settings.CacheProvider.BuildCache(regionName, props);
}
[MethodImpl(MethodImplOptions.Synchronized)]
public void PreInvalidate(object[] spaces)
{
//TODO: to handle concurrent writes correctly, this should return a Lock to the client
long ts = updateTimestamps.NextTimestamp() + updateTimestamps.Timeout;
for (int i = 0; i < spaces.Length; i++)
{
updateTimestamps.Put(spaces[i], ts);
}
//TODO: return new Lock(ts);
}
/// <summary></summary>
[MethodImpl(MethodImplOptions.Synchronized)]
public void Invalidate(object[] spaces)
{
//TODO: to handle concurrent writes correctly, the client should pass in a Lock
long ts = updateTimestamps.NextTimestamp();
//TODO: if lock.getTimestamp().equals(ts)
for (int i = 0; i < spaces.Length; i++)
{
log.Debug(string.Format("Invalidating space [{0}]", spaces[i]));
updateTimestamps.Put(spaces[i], ts);
}
}
[MethodImpl(MethodImplOptions.Synchronized)]
public bool IsUpToDate(ISet spaces, long timestamp /* H2.1 has Long here */)
{
foreach (object space in spaces)
{
object lastUpdate = updateTimestamps.Get(space);
if (lastUpdate == null)
{
//the last update timestamp was lost from the cache
//(or there were no updates since startup!)
//NOTE: commented out, since users found the "safe" behavior
// counter-intuitive when testing, and we couldn't deal
// with all the forum posts :-(
//updateTimestamps.put( space, new Long( updateTimestamps.nextTimestamp() ) );
//result = false; // safer
//OR: put a timestamp there, to avoid subsequent expensive
// lookups to a distributed cache - this is no good, since
// it is non-threadsafe (could hammer effect of an actual
// invalidation), and because this is not the way our
// preferred distributed caches work (they work by
// replication)
//updateTimestamps.put( space, new Long(Long.MIN_VALUE) );
}
else
{
if ((long) lastUpdate >= timestamp)
{
return false;
}
}
}
return true;
}
public void Destroy()
{
try
{
updateTimestamps.Destroy();
}
catch (Exception e)
{
log.Warn("could not destroy UpdateTimestamps cache", e);
}
}
}
}