132
|
1 /*******************************************************************************
|
|
2
|
|
3 copyright: Copyright (c) 2004 Kris Bell. All rights reserved
|
|
4
|
|
5 license: BSD style: $(LICENSE)
|
|
6
|
|
7 version: July 2004: Initial release
|
|
8
|
|
9 author: Kris
|
|
10
|
|
11 *******************************************************************************/
|
|
12
|
|
13 module tango.net.cluster.NetworkCache;
|
|
14
|
|
15 private import tango.core.Thread;
|
|
16
|
|
17 private import tango.net.cluster.model.IMessage;
|
|
18
|
|
19 private import tango.net.cluster.QueuedCache,
|
|
20 tango.net.cluster.CacheInvalidator,
|
|
21 tango.net.cluster.CacheInvalidatee;
|
|
22
|
|
23 /*******************************************************************************
|
|
24
|
|
25 A gateway to the network cache. From here you can easily place
|
|
26 IMessage objects into the network cluster, copy them and remove
|
|
27 them. A cluster cache is spread out across many servers within
|
|
28 the network. Each cache entry is associated with a 'channel',
|
|
29 which is effectively the name of a cache instance within the
|
|
30 cluster. See ComboCache also. The basic procedure is so:
|
|
31 ---
|
|
32 import tango.net.cluster.NetworkCache;
|
|
33 import tango.net.cluster.tina.Cluster;
|
|
34
|
|
35 auto cluster = new Cluster (...);
|
|
36 auto cache = new NetworkCache (cluster, ...);
|
|
37
|
|
38 cache.put (...);
|
|
39 cache.get ();
|
|
40 cache.invalidate (...);
|
|
41 ---
|
|
42
|
|
43 Note that any content placed into the cache must implement the
|
|
44 IMessage interface, and must be enrolled with the Registry, as
|
|
45 it may be frozen and thawed as it travels around the network.
|
|
46
|
|
47 *******************************************************************************/
|
|
48
|
|
49 class NetworkCache : CacheInvalidator
|
|
50 {
|
|
51 /***********************************************************************
|
|
52
|
|
53 Construct a NetworkCache using the QOS (cluster) provided,
|
|
54 and hook it onto the specified channel. Each subsequent
|
|
55 operation is tied to this channel.
|
|
56
|
|
57 ***********************************************************************/
|
|
58
|
|
59 this (ICluster cluster, char[] channel)
|
|
60 {
|
|
61 super (cluster, channel);
|
|
62 }
|
|
63
|
|
64 /***********************************************************************
|
|
65
|
|
66 Returns a copy of the cluster cache entry corresponding to
|
|
67 the provided key. Returns null if there is no such entry.
|
|
68
|
|
69 ***********************************************************************/
|
|
70
|
|
71 IMessage get (char[] key)
|
|
72 {
|
|
73 assert (key.length);
|
|
74 return channel.getCache (key, false);
|
|
75 }
|
|
76
|
|
77 /***********************************************************************
|
|
78
|
|
79 Remove and return the cache entry corresponding to the
|
|
80 provided key.
|
|
81
|
|
82 ***********************************************************************/
|
|
83
|
|
84 IMessage extract (char[] key)
|
|
85 {
|
|
86 assert (key.length);
|
|
87 return channel.getCache (key, true);
|
|
88 }
|
|
89
|
|
90 /***********************************************************************
|
|
91
|
|
92 Set a cluster cache entry.
|
|
93
|
|
94 Place an entry into the network cache, replacing the
|
|
95 entry with the identical key. Where message.time is
|
|
96 set, it will be used to test for newer cache entries
|
|
97 than the one being sent i.e. if someone else placed
|
|
98 a newer entry into the cache, that one will remain.
|
|
99
|
|
100 The msg will be placed into one or more cluster hosts
|
|
101 (depending upon QOS)
|
|
102
|
|
103 Returns true if the cache entry was inserted, false if
|
|
104 the cache server already has an exiting key with a more
|
|
105 recent timestamp (where message.time is set).
|
|
106
|
|
107 ***********************************************************************/
|
|
108
|
|
109 bool put (char[] key, IMessage message)
|
|
110 {
|
|
111 assert (message);
|
|
112 assert (key.length);
|
|
113
|
|
114 return channel.putCache (key, message);
|
|
115 }
|
|
116 }
|
|
117
|
|
118
|
|
119 /*******************************************************************************
|
|
120
|
|
121 A combination of a local cache, cluster cache, and CacheInvalidatee.
|
|
122 The two cache instances are combined such that they represent a
|
|
123 classic level1/level2 cache. The CacheInvalidatee ensures that the
|
|
124 level1 cache maintains coherency with the cluster.
|
|
125
|
|
126 *******************************************************************************/
|
|
127
|
|
128 class NetworkCombo : NetworkCache
|
|
129 {
|
|
130 private QueuedCache!(char[], IMessage) cache;
|
|
131 private CacheInvalidatee invalidatee;
|
|
132
|
|
133 /***********************************************************************
|
|
134
|
|
135 Construct a ComboCache for the specified local cache, and
|
|
136 on the given cluster channel.
|
|
137
|
|
138 ***********************************************************************/
|
|
139
|
|
140 this (ICluster cluster, char[] channel, uint capacity)
|
|
141 {
|
|
142 super (cluster, channel);
|
|
143
|
|
144 cache = new QueuedCache!(char[], IMessage) (capacity);
|
|
145 invalidatee = new CacheInvalidatee (cluster, channel, cache);
|
|
146 }
|
|
147
|
|
148 /***********************************************************************
|
|
149
|
|
150 Get an IMessage from the local cache, and revert to the
|
|
151 cluster cache if it's not found.
|
|
152
|
|
153 Cluster lookups will *not* place new content into the
|
|
154 local cache without confirmation: the supplied delegate
|
|
155 must perform the appropriate cloning of cluster entries
|
|
156 before they will be placed into the local cache. This
|
|
157 delegate would typically invoke the clone() method on
|
|
158 the provided network message; behaviour is undefined
|
|
159 where a delegate simply returns a message without the
|
|
160 appropriate cloning steps.
|
|
161
|
|
162 Returns null if the entry does not exist in either the
|
|
163 local or remote cache, or if the delegate returned null.
|
|
164 Returns the cache entry otherwise.
|
|
165
|
|
166 ***********************************************************************/
|
|
167
|
|
168 IMessage get (char[] key, IMessage delegate(IMessage) dg)
|
|
169 {
|
|
170 auto cached = cache.get (key);
|
|
171 if (cached is null)
|
|
172 {
|
|
173 cached = super.get (key);
|
|
174
|
|
175 // if delegate cloned the entry,
|
|
176 // place said clone into the cache
|
|
177 if (cached && (cached = dg(cached)) !is null)
|
|
178 cache.put (key, cached, cached.time);
|
|
179 }
|
|
180 return cached;
|
|
181 }
|
|
182
|
|
183 /***********************************************************************
|
|
184
|
|
185 Place a new entry into the cache. This will also place
|
|
186 the entry into the cluster, and optionally invalidate
|
|
187 all other local cache instances across the network. If
|
|
188 a cache entry exists with the same key, it is replaced.
|
|
189
|
|
190 Where message.time is set, it will be used to test for
|
|
191 newer cache entries than the one being sent i.e. if a
|
|
192 newer entry exists in the cache, that one will remain.
|
|
193
|
|
194 Note that when using the coherency option you should
|
|
195 ensure your IMessage has a valid time stamp, since that
|
|
196 is used to invalidate appropriate cache listeners in the
|
|
197 cluster. You can use the getTime() method to retrieve a
|
|
198 current millisecond count.
|
|
199
|
|
200 Returns true if the cache entry was inserted, false if
|
|
201 the cache server already has an exiting key with a more
|
|
202 recent timestamp (where message.time is set).
|
|
203
|
|
204 ***********************************************************************/
|
|
205
|
|
206 bool put (char[] key, IMessage message, bool coherent = false)
|
|
207 {
|
|
208 // this will throw an exception if there's a problem
|
|
209 if (super.put (key, message))
|
|
210 {
|
|
211 // place into local cache also
|
|
212 cache.put (key, message, message.time);
|
|
213
|
|
214 // invalidate all other cache instances except this new one,
|
|
215 // such that no other listening cache has the same key
|
|
216 if (coherent)
|
|
217 invalidate (key, message.time);
|
|
218
|
|
219 return true;
|
|
220 }
|
|
221 return false;
|
|
222 }
|
|
223
|
|
224 /***********************************************************************
|
|
225
|
|
226 Remove and return the cache entry corresponding to the
|
|
227 provided key.
|
|
228
|
|
229 Synchronously extracts the entry from the cluster, and
|
|
230 returns the entry from the local cache if there is one
|
|
231 there; null otherwise
|
|
232
|
|
233 ***********************************************************************/
|
|
234
|
|
235 IMessage extract (char[] key)
|
|
236 {
|
|
237 // do this first, since its return value may have to be cloned
|
|
238 super.extract (key);
|
|
239
|
|
240 // return the local entry if there is one
|
|
241 return cache.remove (key);
|
|
242 }
|
|
243 }
|
|
244
|
|
245
|
|
246
|