Mercurial > hg > blitz_condensed
comparison src/org/dancres/blitz/entry/ci/CacheIndexerImpl.java @ 0:3dc0c5604566
Initial checkin of blitz 2.0 fcs - no installer yet.
author | Dan Creswell <dan.creswell@gmail.com> |
---|---|
date | Sat, 21 Mar 2009 11:00:06 +0000 |
parents | |
children | 4b2f23b6feca |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:3dc0c5604566 |
---|---|
1 package org.dancres.blitz.entry.ci; | |
2 | |
3 import java.util.Set; | |
4 | |
5 import java.util.logging.*; | |
6 | |
7 import org.dancres.blitz.entry.EntrySleeve; | |
8 import org.dancres.blitz.entry.TupleLocator; | |
9 | |
10 import org.dancres.blitz.mangler.MangledField; | |
11 import org.dancres.blitz.mangler.MangledEntry; | |
12 | |
13 import org.dancres.blitz.cache.Identifiable; | |
14 | |
15 import org.dancres.blitz.oid.OID; | |
16 | |
17 /** | |
18 * A skeleton implementation of a cache indexer. | |
19 * Requires that the underlying index mechanism can support the CacheLines interface. | |
20 * | |
21 * @see org.dancres.blitz.entry.ci.CacheLines | |
22 * | |
23 */ | |
24 public abstract class CacheIndexerImpl extends CacheIndexer { | |
25 | |
26 /** | |
27 Because fields are ordered and always present, even if they are null | |
28 we don't need to use a HashMap here. | |
29 */ | |
30 private CacheLines[] theCacheLines; | |
31 | |
32 /** | |
33 Contains all ids we know about which is required for a wildcard | |
34 search. A possibly better solution would iterate across all indexes but | |
35 then there's the need to do intersection to prevent repeated passes over | |
36 the same id which means much more complex code and might perform worse | |
37 than this simple solution. | |
38 */ | |
39 private Set theAllIds; | |
40 | |
41 private String theType; | |
42 | |
43 CacheIndexerImpl(String aType) { | |
44 theType = aType; | |
45 theAllIds = newIds(); | |
46 } | |
47 | |
48 abstract Set newIds(); | |
49 | |
50 public void loaded(Identifiable anIdentifiable) { | |
51 // System.err.println("CI:Loaded: " + anIdentifiable.getId() + "," + this); | |
52 | |
53 EntrySleeve mySleeve = (EntrySleeve) anIdentifiable; | |
54 | |
55 /* | |
56 No point indexing something that's already deleted - we only want | |
57 to generate load requests in response to explicit address'ing via | |
58 UID. | |
59 */ | |
60 if (mySleeve.isDeleted()) | |
61 return; | |
62 | |
63 initBarrier(mySleeve); | |
64 | |
65 insert(mySleeve); | |
66 } | |
67 | |
68 public void flushed(Identifiable anIdentifiable) { | |
69 // System.err.println("CI:Flushed: " + anIdentifiable.getId() + "," + this); | |
70 | |
71 EntrySleeve mySleeve = (EntrySleeve) anIdentifiable; | |
72 initBarrier(mySleeve); | |
73 | |
74 remove(mySleeve); | |
75 } | |
76 | |
77 public void dirtied(Identifiable anIdentifiable) { | |
78 // System.err.println("CI:Dirtied: " + anIdentifiable.getId() + "," + this); | |
79 | |
80 EntrySleeve mySleeve = (EntrySleeve) anIdentifiable; | |
81 initBarrier(mySleeve); | |
82 | |
83 if (mySleeve.isDeleted()) | |
84 remove(mySleeve); | |
85 } | |
86 | |
87 public TupleLocator find(MangledEntry anEntry) { | |
88 try { | |
89 return findImpl(anEntry); | |
90 } catch (ArrayIndexOutOfBoundsException anE) { | |
91 theLogger.log(Level.SEVERE, "Find broke - did you add/remove fields in your Entry?", anE); | |
92 theLogger.log(Level.SEVERE, "CacheIndexer for: " + | |
93 theType); | |
94 theLogger.log(Level.SEVERE, "Entry looks like"); | |
95 theLogger.log(Level.SEVERE, "Entry type: " + | |
96 anEntry.getType()); | |
97 for (int i = 0; i < anEntry.getFields().length; i++) { | |
98 MangledField myField = anEntry.getField(i); | |
99 | |
100 theLogger.log(Level.SEVERE, "Name: " + | |
101 myField.getName() + ", hashcode:" + | |
102 myField.hashCode() + ", offset:" + | |
103 i + ", isNull: " + | |
104 myField.isNull()); | |
105 } | |
106 theLogger.log(Level.SEVERE, "Cachelines looks like"); | |
107 theLogger.log(Level.SEVERE, "Cachelines size:" + | |
108 theCacheLines.length); | |
109 for (int i = 0; i < theCacheLines.length; i++) { | |
110 theLogger.log(Level.SEVERE, "Name: " + | |
111 theCacheLines[i].getName() + | |
112 " Offset: " + i + | |
113 " size: " + | |
114 theCacheLines[i].getSize()); | |
115 } | |
116 | |
117 throw anE; | |
118 } | |
119 } | |
120 | |
121 private TupleLocator findImpl(MangledEntry anEntry) { | |
122 /* | |
123 Unlike the cache listener methods which can only be called with | |
124 an entry of this indexers type, there's a possibility this method's | |
125 first entry will be a different type. If it is, it can't be used | |
126 to init the barrier - thus, if we fail to init the barrier we should | |
127 return an empty locator | |
128 */ | |
129 if (!initBarrier(anEntry)) | |
130 return ArrayLocatorImpl.EMPTY_LOCATOR; | |
131 | |
132 if ((anEntry == null) || (anEntry.isWildcard())) { | |
133 // Handle wildcard requests | |
134 // | |
135 if (theLogger.isLoggable(Level.FINE)) | |
136 theLogger.log(Level.FINE, "Wildcard match"); | |
137 | |
138 synchronized(theAllIds) { | |
139 if (theAllIds.size() == 0) | |
140 return ArrayLocatorImpl.EMPTY_LOCATOR; | |
141 else { | |
142 OID[] myUids = new OID[theAllIds.size()]; | |
143 myUids = (OID[]) theAllIds.toArray(myUids); | |
144 | |
145 return new ArrayLocatorImpl(myUids); | |
146 } | |
147 } | |
148 } else { | |
149 // Proper indexing effort | |
150 // | |
151 if (theLogger.isLoggable(Level.FINE)) | |
152 theLogger.log(Level.FINE, "Specific match"); | |
153 | |
154 MangledField myChoice = null; | |
155 int myChoicesSize = 0; | |
156 int myChoicesOffset = 0; | |
157 | |
158 // Find the smallest index available | |
159 for (int i = 0; i < anEntry.getFields().length; i++) { | |
160 MangledField myField = anEntry.getField(i); | |
161 int mySize; | |
162 | |
163 if (theLogger.isLoggable(Level.FINE)) | |
164 theLogger.log(Level.FINE, "Consider: " + myField.getName()); | |
165 | |
166 // If field is empty, we can't search on it | |
167 if (myField.isNull()) | |
168 continue; | |
169 | |
170 mySize = getSize(myField, i); | |
171 | |
172 /* | |
173 Field is searchable - but, if we get no hits from cache | |
174 indexes, that means we have no matches in memory. This | |
175 works because for a match to succeed it must match on | |
176 all fields in the template. Thus if the template has a | |
177 field with a hashcode that produces no hits then whilst | |
178 we may have Entry's that match other fields of the template | |
179 we know that none of these Entry's will match this field | |
180 of the template so we should stop now. | |
181 */ | |
182 if (mySize == 0) { | |
183 if (theLogger.isLoggable(Level.FINE)) | |
184 theLogger.log(Level.FINE, | |
185 "One key not indexed - abort"); | |
186 return ArrayLocatorImpl.EMPTY_LOCATOR; | |
187 } | |
188 | |
189 if (theLogger.isLoggable(Level.FINE)) | |
190 theLogger.log(Level.FINE, "Available size: " + mySize); | |
191 | |
192 if (myChoice == null) { | |
193 myChoice = myField; | |
194 myChoicesSize = mySize; | |
195 myChoicesOffset = i; | |
196 } else { | |
197 if (mySize < myChoicesSize) { | |
198 myChoice = myField; | |
199 myChoicesSize = mySize; | |
200 myChoicesOffset = i; | |
201 } | |
202 } | |
203 } | |
204 | |
205 if (myChoice == null) { | |
206 return ArrayLocatorImpl.EMPTY_LOCATOR; | |
207 } else { | |
208 if (theLogger.isLoggable(Level.FINE)) | |
209 theLogger.log(Level.FINE, "Chose: " + myChoicesSize + | |
210 myChoice.getName()); | |
211 return getIds(myChoice, myChoicesOffset); | |
212 } | |
213 } | |
214 } | |
215 | |
216 private TupleLocator getIds(MangledField aField, int anOffset) { | |
217 CacheLines myLine = getCacheLines(anOffset); | |
218 return myLine.getIds(aField.hashCode()); | |
219 } | |
220 | |
221 private int getSize(MangledField aField, int anOffset) { | |
222 CacheLines myLine = getCacheLines(anOffset); | |
223 return myLine.getSize(aField.hashCode()); | |
224 } | |
225 | |
226 private boolean initBarrier(EntrySleeve aSleeve) { | |
227 synchronized(this) { | |
228 if (theCacheLines == null) | |
229 return initBarrier(aSleeve.getEntry()); | |
230 else | |
231 return true; | |
232 } | |
233 } | |
234 | |
235 /** | |
236 @return true if the barrier is already inited or if | |
237 the barrier was successfully inited, false otherwise. | |
238 */ | |
239 private boolean initBarrier(MangledEntry anEntry) { | |
240 synchronized(this) { | |
241 if (theCacheLines == null) { | |
242 theLogger.log(Level.FINE, | |
243 "Initing cache indexer: " + theType); | |
244 | |
245 if (!anEntry.getType().equals(theType)) { | |
246 theLogger.log(Level.FINE, | |
247 "Can't init indexer with this: " + | |
248 anEntry.getType()); | |
249 return false; | |
250 } | |
251 | |
252 theLogger.log(Level.FINE, "Entry looks like"); | |
253 theLogger.log(Level.FINE, "Entry type: " + | |
254 anEntry.getType()); | |
255 for (int i = 0; i < anEntry.getFields().length; i++) { | |
256 MangledField myField = anEntry.getField(i); | |
257 | |
258 theLogger.log(Level.FINE, "Name: " + | |
259 myField.getName() + ", hashcode:" + | |
260 myField.hashCode() + ", offset:" + | |
261 i + ", isNull: " + | |
262 myField.isNull()); | |
263 } | |
264 | |
265 MangledField[] myFields = anEntry.getFields(); | |
266 theCacheLines = newLinesArray(myFields.length); | |
267 | |
268 for (int i = 0; i < myFields.length; i++) { | |
269 theCacheLines[i] = newLines(i, myFields[i].getName()); | |
270 } | |
271 } | |
272 | |
273 return true; | |
274 } | |
275 } | |
276 | |
277 abstract CacheLines[] newLinesArray(int aSize); | |
278 | |
279 abstract CacheLines newLines(int anIndex, String aFieldName); | |
280 | |
281 private CacheLines getCacheLines(int anOffset) { | |
282 return theCacheLines[anOffset]; | |
283 } | |
284 | |
285 /** | |
286 Add a sleeve to the indexer | |
287 */ | |
288 private void insert(EntrySleeve aSleeve) { | |
289 synchronized(theAllIds) { | |
290 | |
291 if (theAllIds.add(aSleeve.getOID())) { | |
292 for (int i = 0; i < theCacheLines.length; i++) { | |
293 theCacheLines[i].insert(aSleeve); | |
294 } | |
295 } else { | |
296 // System.err.println("Already added"); | |
297 } | |
298 } | |
299 } | |
300 | |
301 /** | |
302 Remove a sleeve from the indexer | |
303 */ | |
304 private void remove(EntrySleeve aSleeve) { | |
305 synchronized(theAllIds) { | |
306 | |
307 if (theAllIds.remove(aSleeve.getOID())) { | |
308 for (int i = 0; i < theCacheLines.length; i++) { | |
309 theCacheLines[i].remove(aSleeve); | |
310 } | |
311 /* | |
312 new RuntimeException( | |
313 "Removed: " + aSleeve.getOID()).printStackTrace( | |
314 System.err); | |
315 */ | |
316 } else { | |
317 // new RuntimeException("Already removed: " + aSleeve.getOID()).printStackTrace(System.err); | |
318 } | |
319 } | |
320 } | |
321 } |