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 }