Merge lp:~tjoneslo/akiban-server/fix-bug-1172013 into lp:~akiban-technologies/akiban-server/trunk

Proposed by Thomas Jones-Low
Status: Rejected
Rejected by: Thomas Jones-Low
Proposed branch: lp:~tjoneslo/akiban-server/fix-bug-1172013
Merge into: lp:~akiban-technologies/akiban-server/trunk
Diff against target: 1030 lines (+450/-101)
19 files modified
src/main/java/com/akiban/http/HttpConductorImpl.java (+8/-4)
src/main/java/com/akiban/qp/persistitadapter/OperatorStore.java (+4/-3)
src/main/java/com/akiban/rest/RestResponseBuilder.java (+0/-1)
src/main/java/com/akiban/rest/resources/EntityResource.java (+3/-0)
src/main/java/com/akiban/rest/resources/ModelResource.java (+4/-2)
src/main/java/com/akiban/rest/resources/ResourceHelper.java (+9/-0)
src/main/java/com/akiban/server/service/BackgroundWorkBase.java (+8/-7)
src/main/java/com/akiban/server/service/dxl/BasicDDLFunctions.java (+1/-1)
src/main/java/com/akiban/server/service/dxl/HookableDDLFunctions.java (+0/-1)
src/main/java/com/akiban/server/service/text/FullTextIndexService.java (+1/-1)
src/main/java/com/akiban/server/service/text/FullTextIndexServiceImpl.java (+87/-52)
src/main/java/com/akiban/server/store/PersistitStore.java (+13/-7)
src/main/java/com/akiban/server/store/PersistitStoreSchemaManager.java (+1/-4)
src/main/java/com/akiban/sql/aisddl/AISDDL.java (+1/-1)
src/main/java/com/akiban/sql/aisddl/IndexDDL.java (+5/-11)
src/main/java/com/akiban/sql/aisddl/TableDDL.java (+1/-2)
src/test/java/com/akiban/server/rowdata/SchemaFactory.java (+1/-1)
src/test/java/com/akiban/server/service/text/FullTextIndexServiceBug1172013IT.java (+296/-0)
src/test/java/com/akiban/server/service/text/FullTextIndexServiceIT.java (+7/-3)
To merge this branch: bzr merge lp:~tjoneslo/akiban-server/fix-bug-1172013
Reviewer Review Type Date Requested Status
Akiban Technologies Pending
Review via email: mp+163144@code.launchpad.net

Description of the change

Fix the w-w conflict in the testing system for the full text indexes. The testing has uncovered a problem which can (if circumstances are right) the end user too.

The core problem is the interaction of the delete index (which tries to delete the full text index) and the background thread populating the index. In order to get this to work correctly, I did two things. One, moved the point at which the index creation process calls the FT service to schedule the background process. Second, update the FT service to make the delete and populate aware of each other, so they are not stepping on each other.

BackgroundWorkBase - Update the internal set of background listeners from a HahsSet to a ConcurrentHashMap. Adding or removing listeners from different threads causes concurrent access exceptions at the worst possible time.

PersistitStore - This is where the FT service schedulePopulate now gets called. This in in the transaction to create the index itself, so the populate process gets flagged at the same time as the index creation, and doesn't if the index creation fails.

PersistitStoreSchemaManager - Part of the process of managing the index creation means the FT indexes need to be passed to the PeristitStore for processing.

IndexDDL - This is where previously the FT service sechedulePopulate was called. Since the only reason for the ServiceManager to be passed through to here was to get the FT Service, remove the now unused parameter. This has a cascade on the callers to remove the unused parameter as well.

FullTextIndexServiceBug1172013IT - This is the test for the bug, with three variations.

To post a comment you must log in.
Revision history for this message
Nathan Williams (nwilliams) wrote :

There seems to be unrelated changes related to REST and session handling. Are those ready to go or shall we revert for now?

Otherwise looks as described, still thinking through though.

Revision history for this message
Mike McMahon (mmcm) wrote :

There is no semantic difference, but lines 81-87 of the diff could be just one switch on getIndexType(), I believe.

Unmerged revisions

2658. By tjoneslo

Update the FullTextIndexServiceImpl code to handle the background task populating the full text indexes and managing the process of deleteing the index while the populate is going on. Plus test.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/main/java/com/akiban/http/HttpConductorImpl.java'
2--- src/main/java/com/akiban/http/HttpConductorImpl.java 2013-04-30 21:19:31 +0000
3+++ src/main/java/com/akiban/http/HttpConductorImpl.java 2013-05-09 13:22:31 +0000
4@@ -21,6 +21,7 @@
5 import com.akiban.server.service.config.ConfigurationService;
6 import com.akiban.server.service.monitor.MonitorService;
7 import com.akiban.server.service.monitor.ServerMonitor;
8+import com.akiban.server.service.monitor.SessionMonitor;
9 import com.akiban.server.service.security.SecurityService;
10 import com.akiban.server.service.session.Session;
11 import com.akiban.server.service.session.SessionService;
12@@ -387,9 +388,9 @@
13 ServerSessionMonitor sessionMonitor = new ServerSessionMonitor(SERVER_TYPE,
14 monitorService.allocateSessionId());
15
16- conn.setAssociatedObject(sessionMonitor);
17 this.session = sessionService.createSession();
18 monitorService.registerSessionMonitor(sessionMonitor, session);
19+ conn.setAssociatedObject(session);
20 return conn;
21 }
22
23@@ -397,10 +398,12 @@
24 protected void connectionClosed(Connection connection) {
25 if (connection instanceof AsyncHttpConnection) {
26 AsyncHttpConnection conn = (AsyncHttpConnection)connection;
27- ServerSessionMonitor monitor = (ServerSessionMonitor)conn.getAssociatedObject();
28+
29+ SessionMonitor monitor = monitorService.getSessionMonitor(session);
30 if (monitor != null) {
31 monitorService.deregisterSessionMonitor(monitor, session);
32 conn.setAssociatedObject(null);
33+ session.close();
34 }
35 }
36 super.connectionClosed(connection);
37@@ -420,9 +423,9 @@
38 ServerSessionMonitor sessionMonitor = new ServerSessionMonitor(SERVER_TYPE,
39 monitorService.allocateSessionId());
40
41- conn.setAssociatedObject(sessionMonitor);
42 this.session = sessionService.createSession();
43 monitorService.registerSessionMonitor(sessionMonitor, session);
44+ conn.setAssociatedObject(session);
45 return conn;
46 }
47
48@@ -430,10 +433,11 @@
49 protected void connectionClosed (Connection connection) {
50 if (connection instanceof SslConnection) {
51 AsyncHttpConnection conn = (AsyncHttpConnection)((SslConnection) connection).getSslEndPoint().getConnection();
52- ServerSessionMonitor monitor = (ServerSessionMonitor)conn.getAssociatedObject();
53+ SessionMonitor monitor = monitorService.getSessionMonitor(session);
54 if (monitor != null) {
55 monitorService.deregisterSessionMonitor(monitor, session);
56 conn.setAssociatedObject(null);
57+ session.close();
58 }
59 }
60 super.connectionClosed(connection);
61
62=== modified file 'src/main/java/com/akiban/qp/persistitadapter/OperatorStore.java'
63--- src/main/java/com/akiban/qp/persistitadapter/OperatorStore.java 2013-04-30 22:35:43 +0000
64+++ src/main/java/com/akiban/qp/persistitadapter/OperatorStore.java 2013-05-09 13:22:31 +0000
65@@ -18,6 +18,7 @@
66 package com.akiban.qp.persistitadapter;
67
68 import com.akiban.ais.model.*;
69+import com.akiban.ais.model.Index.IndexType;
70 import com.akiban.qp.exec.UpdatePlannable;
71 import com.akiban.qp.exec.UpdateResult;
72 import com.akiban.qp.expression.IndexBound;
73@@ -222,11 +223,11 @@
74
75 @Override
76 public void buildIndexes(Session session, Collection<? extends Index> indexes, boolean defer) {
77- List<TableIndex> tableIndexes = new ArrayList<>();
78+ List<Index> tableIndexes = new ArrayList<>();
79 List<GroupIndex> groupIndexes = new ArrayList<>();
80 for(Index index : indexes) {
81- if(index.isTableIndex()) {
82- tableIndexes.add((TableIndex)index);
83+ if(index.isTableIndex() || index.getIndexType() == IndexType.FULL_TEXT) {
84+ tableIndexes.add(index);
85 }
86 else if(index.isGroupIndex()) {
87 groupIndexes.add((GroupIndex)index);
88
89=== modified file 'src/main/java/com/akiban/rest/RestResponseBuilder.java'
90--- src/main/java/com/akiban/rest/RestResponseBuilder.java 2013-04-26 15:25:38 +0000
91+++ src/main/java/com/akiban/rest/RestResponseBuilder.java 2013-05-09 13:22:31 +0000
92@@ -128,7 +128,6 @@
93 builder.append("\"}");
94 }
95
96-
97 private String formatErrorWithJsonp(String code, String message) {
98 StringBuilder builder = new StringBuilder();
99 if(isJsonp) {
100
101=== modified file 'src/main/java/com/akiban/rest/resources/EntityResource.java'
102--- src/main/java/com/akiban/rest/resources/EntityResource.java 2013-04-23 14:20:47 +0000
103+++ src/main/java/com/akiban/rest/resources/EntityResource.java 2013-05-09 13:22:31 +0000
104@@ -36,6 +36,9 @@
105 import javax.ws.rs.core.MediaType;
106 import javax.ws.rs.core.Response;
107 import javax.ws.rs.core.UriInfo;
108+
109+import org.eclipse.jetty.server.Request;
110+
111 import java.io.PrintWriter;
112
113 import static com.akiban.rest.resources.ResourceHelper.IDENTIFIERS_MULTI;
114
115=== modified file 'src/main/java/com/akiban/rest/resources/ModelResource.java'
116--- src/main/java/com/akiban/rest/resources/ModelResource.java 2013-04-23 11:04:53 +0000
117+++ src/main/java/com/akiban/rest/resources/ModelResource.java 2013-05-09 13:22:31 +0000
118@@ -35,6 +35,8 @@
119 import com.akiban.util.tap.InOutTap;
120 import com.akiban.util.tap.Tap;
121 import com.fasterxml.jackson.databind.JsonNode;
122+
123+import org.eclipse.jetty.server.Request;
124 import org.slf4j.Logger;
125 import org.slf4j.LoggerFactory;
126
127@@ -88,14 +90,14 @@
128 @PathParam("schema") String schemaParam) {
129 final String schema = getSchemaName(request, schemaParam);
130 checkSchemaAccessible(reqs.securityService, request, schema);
131+ final Session session = (Session)request.getAttribute(Session.class.getName());
132 return RestResponseBuilder
133 .forRequest(request)
134 .body(new RestResponseBuilder.BodyGenerator() {
135 @Override
136 public void write(PrintWriter writer) throws Exception {
137 MODEL_VIEW.in();
138- try (Session session = reqs.sessionService.createSession();
139- CloseableTransaction txn = reqs.transactionService.beginCloseableTransaction(session)) {
140+ try ( CloseableTransaction txn = reqs.transactionService.beginCloseableTransaction(session)) {
141 Space space = spaceForAIS(session, schema);
142 String json = space.toJson();
143 writer.write(json);
144
145=== modified file 'src/main/java/com/akiban/rest/resources/ResourceHelper.java'
146--- src/main/java/com/akiban/rest/resources/ResourceHelper.java 2013-03-26 18:08:04 +0000
147+++ src/main/java/com/akiban/rest/resources/ResourceHelper.java 2013-05-09 13:22:31 +0000
148@@ -19,12 +19,16 @@
149
150 import com.akiban.ais.model.TableName;
151 import com.akiban.server.service.security.SecurityService;
152+import com.akiban.server.service.session.Session;
153
154 import javax.servlet.http.HttpServletRequest;
155 import javax.ws.rs.WebApplicationException;
156 import javax.ws.rs.core.MediaType;
157 import javax.ws.rs.core.Response;
158 import javax.ws.rs.core.UriInfo;
159+
160+import org.eclipse.jetty.server.Request;
161+
162 import java.security.Principal;
163
164 public class ResourceHelper {
165@@ -61,6 +65,11 @@
166 }
167 }
168
169+ public static Session getSession (Request request) {
170+ Object obj = request.getConnection().getAssociatedObject();
171+ assert obj instanceof Session;
172+ return (Session)obj;
173+ }
174 /** Expected to be used along with {@link #IDENTIFIERS_MULTI} */
175 public static String getPKString(UriInfo uri) {
176 String pks[] = uri.getPath(false).split("/");
177
178=== modified file 'src/main/java/com/akiban/server/service/BackgroundWorkBase.java'
179--- src/main/java/com/akiban/server/service/BackgroundWorkBase.java 2013-04-08 19:34:41 +0000
180+++ src/main/java/com/akiban/server/service/BackgroundWorkBase.java 2013-05-09 13:22:31 +0000
181@@ -18,12 +18,11 @@
182 package com.akiban.server.service;
183
184 import java.util.Collection;
185-import java.util.HashSet;
186-import java.util.Set;
187+import java.util.concurrent.ConcurrentHashMap;
188
189 public abstract class BackgroundWorkBase implements BackgroundWork<BackgroundObserver, BackgroundWork>
190 {
191- private final Set<BackgroundObserver> observers = new HashSet<>();
192+ private final ConcurrentHashMap<BackgroundObserver,Boolean> observers = new ConcurrentHashMap<>();
193
194 public BackgroundWorkBase()
195 {
196@@ -34,7 +33,7 @@
197 public void addObserver(BackgroundObserver observer)
198 {
199 // two observers are equal IFF they are the same object.
200- observers.add(observer);
201+ observers.put(observer, true);
202 }
203
204 @Override
205@@ -45,19 +44,21 @@
206
207 public void removeObsevers(Collection<BackgroundObserver> os)
208 {
209- observers.removeAll(os);
210+ for (BackgroundObserver key : os) {
211+ observers.remove(key);
212+ }
213 }
214
215 @Override
216 public void removeAllObservers()
217 {
218- observers.removeAll(observers);
219+ observers.clear();
220 }
221
222 @Override
223 public void notifyObservers()
224 {
225- for (BackgroundObserver o : observers)
226+ for (BackgroundObserver o : observers.keySet())
227 o.update(this);
228 }
229 }
230
231=== modified file 'src/main/java/com/akiban/server/service/dxl/BasicDDLFunctions.java'
232--- src/main/java/com/akiban/server/service/dxl/BasicDDLFunctions.java 2013-04-22 02:00:55 +0000
233+++ src/main/java/com/akiban/server/service/dxl/BasicDDLFunctions.java 2013-05-09 13:22:31 +0000
234@@ -959,8 +959,8 @@
235 // Cannot use Index.getAllTableIDs(), stub AIS only has to be name-correct
236 for(Index index : indexesToAdd) {
237 switch(index.getIndexType()) {
238+ case FULL_TEXT: // TODO: More IDs?
239 case TABLE:
240- case FULL_TEXT: // TODO: More IDs?
241 UserTable table = ais.getUserTable(index.getIndexName().getFullTableName());
242 if(table != null) {
243 tableIDs.add(table.getTableId());
244
245=== modified file 'src/main/java/com/akiban/server/service/dxl/HookableDDLFunctions.java'
246--- src/main/java/com/akiban/server/service/dxl/HookableDDLFunctions.java 2013-04-11 00:05:29 +0000
247+++ src/main/java/com/akiban/server/service/dxl/HookableDDLFunctions.java 2013-05-09 13:22:31 +0000
248@@ -39,7 +39,6 @@
249
250 import static com.akiban.ais.util.TableChangeValidator.ChangeLevel;
251 import static com.akiban.util.Exceptions.throwAlways;
252-import static com.akiban.util.Exceptions.throwIfInstanceOf;
253
254 public final class HookableDDLFunctions implements DDLFunctions {
255
256
257=== modified file 'src/main/java/com/akiban/server/service/text/FullTextIndexService.java'
258--- src/main/java/com/akiban/server/service/text/FullTextIndexService.java 2013-04-22 18:37:21 +0000
259+++ src/main/java/com/akiban/server/service/text/FullTextIndexService.java 2013-05-09 13:22:31 +0000
260@@ -35,7 +35,7 @@
261 * created.
262 * @param name
263 */
264- public void schedulePopulate(String schema, String table, String index);
265+ public void schedulePopulate(Session session, IndexName name);
266
267 /**
268 * Update the given index based on the changedRow
269
270=== modified file 'src/main/java/com/akiban/server/service/text/FullTextIndexServiceImpl.java'
271--- src/main/java/com/akiban/server/service/text/FullTextIndexServiceImpl.java 2013-04-29 15:59:33 +0000
272+++ src/main/java/com/akiban/server/service/text/FullTextIndexServiceImpl.java 2013-05-09 13:22:31 +0000
273@@ -34,6 +34,7 @@
274 import com.akiban.qp.rowtype.HKeyRowType;
275 import com.akiban.qp.util.HKeyCache;
276 import com.akiban.server.error.AkibanInternalException;
277+import com.akiban.server.error.QueryCanceledException;
278 import com.akiban.server.service.BackgroundWork;
279 import com.akiban.server.service.BackgroundWorkBase;
280 import com.akiban.server.service.Service;
281@@ -47,6 +48,7 @@
282 import com.akiban.server.store.PersistitStore;
283 import com.akiban.server.store.PersistitStore.HKeyBytesStream;
284 import com.akiban.server.store.Store;
285+import com.akiban.server.types3.mcompat.mfuncs.WaitFunctionHelpers;
286
287 import org.apache.lucene.index.IndexWriter;
288 import org.apache.lucene.search.Query;
289@@ -64,6 +66,7 @@
290 import java.util.List;
291 import java.util.Timer;
292 import java.util.TimerTask;
293+import java.util.concurrent.ConcurrentHashMap;
294
295
296 public class FullTextIndexServiceImpl extends FullTextIndexInfosImpl implements FullTextIndexService, Service {
297@@ -92,7 +95,8 @@
298
299 private volatile Timer populateTimer;
300 private long populateDelayInterval;
301-
302+ private ConcurrentHashMap<IndexName,Session> populating;
303+
304 private static final Logger logger = LoggerFactory.getLogger(FullTextIndexServiceImpl.class);
305
306 @Inject
307@@ -105,6 +109,7 @@
308 this.store = store;
309 this.transactionService = transactionService;
310 this.treeService = treeService;
311+ this.populating = new ConcurrentHashMap<> ();
312
313 }
314
315@@ -140,8 +145,24 @@
316
317 @Override
318 public void dropIndex(Session session, FullTextIndex idx) {
319- // delete 'promise' for population, if any
320- deleteFromTree(session, idx.getIndexName());
321+
322+ logger.error("Delete {}", idx.getIndexName());
323+
324+ Session populatingSession = populating.putIfAbsent(idx.getIndexName(), session);
325+ if (populatingSession != null) {
326+ // if the population process is running, cancel it
327+ populatingSession.cancelCurrentQuery(true);
328+ // wait for the thread to complete
329+ try {
330+ WaitFunctionHelpers.waitOn(getBackgroundWorks());
331+ } catch (InterruptedException e) {
332+ ;// TODO: do nothing
333+ }
334+ } else {
335+ // delete 'promise' for population, if any
336+ deleteFromTree(session, idx.getIndexName());
337+ populating.remove(idx.getIndexName());
338+ }
339
340 // delete documents
341 FullTextIndexInfo idxInfo = getIndex(session, idx.getIndexName(), idx.getIndexedTable().getAIS());
342@@ -149,6 +170,7 @@
343 synchronized (indexes) {
344 indexes.remove(idx.getIndexName());
345 }
346+
347 }
348
349 @Override
350@@ -321,33 +343,26 @@
351
352
353 @Override
354- public void schedulePopulate(String schema, String table, String index)
355+ public void schedulePopulate(Session session, IndexName name)
356 {
357- Session session = sessionService.createSession();
358- boolean success = false;
359+ logger.debug("Scheduled populate {}", name.toString());
360+
361 try
362 {
363- transactionService.beginTransaction(session);
364- if(addPopulate(session, schema, table, index) && !hasScheduled && populateEnabled)
365- {
366- populateTimer.schedule(populateWorker(), populateDelayInterval);
367- hasScheduled = true;
368+ // Add the index to the list (in persisit) of indexes to build
369+ addPopulate(session, name);
370+
371+ // if there are no scheduled populate workers running,
372+ // add one to run shortly.
373+ if (populateEnabled && !hasScheduled) {
374+ populateTimer.schedule(populateWorker(), populateDelayInterval);
375+ hasScheduled = true;
376 }
377-
378- success = true;
379 }
380 catch (PersistitException ex)
381 {
382 throw new AkibanInternalException("Error while scheduling index population", ex);
383 }
384- finally
385- {
386- if (success)
387- transactionService.commitTransaction(session);
388- else
389- transactionService.rollbackTransaction(session);
390- session.close();
391- }
392 }
393
394 private final List<? extends BackgroundWork> backgroundWorks
395@@ -457,26 +472,45 @@
396
397
398 //----------- private helpers -----------
399- private synchronized void runPopulate()
400+ protected boolean populateNextIndex(Session session) throws PersistitException
401+ {
402+ IndexName toPopulate = null;
403+ transactionService.beginTransaction(session);
404+ Exchange ex = null;
405+ try {
406+ ex = getPopulateExchange(session);
407+
408+ toPopulate = nextInQueue(session, ex, false);
409+ if (toPopulate != null && stillExists (session, toPopulate)) {
410+ createIndex(session, toPopulate);
411+ ex.remove();
412+ transactionService.commitTransaction(session);
413+ populating.remove(toPopulate);
414+ return true;
415+ }
416+ } catch (QueryCanceledException e) {
417+ // The query could be canceled if the user drops the index
418+ // while this thread is populating the index
419+ // Clean up after ourselves.
420+ if (ex != null) ex.remove();
421+ populating.remove(toPopulate);
422+ transactionService.commitTransaction(session);
423+ // start another thread to make sure we're not missing anything
424+ populateTimer.schedule(populateWorker(), populateDelayInterval);
425+ hasScheduled = true;
426+ } finally {
427+ transactionService.rollbackTransactionIfOpen(session);
428+ }
429+ return false;
430+ }
431+
432+ protected synchronized void runPopulate()
433 {
434 populateRunning = true;
435 Session session = sessionService.createSession();
436- boolean transaction = true;
437 try
438 {
439- transactionService.beginTransaction(session);
440- Exchange ex = getPopulateExchange(session);
441- IndexName toPopulate;
442- while ((toPopulate = nextInQueue(ex)) != null)
443- {
444- if (stillExists(session, toPopulate))
445- createIndex(session, toPopulate);
446- else
447- logger.debug("FullTextIndex " + toPopulate + " deleted before population");
448- }
449- ex.removeAll();
450- hasScheduled = false;
451- transaction = false;
452+ while (populateNextIndex(session)) {}
453 }
454 catch (PersistitException ex1)
455 {
456@@ -484,11 +518,7 @@
457 }
458 finally
459 {
460- if (transaction)
461- transactionService.rollbackTransaction(session);
462- else
463- transactionService.commitTransaction(session);
464- session.close();
465+ hasScheduled = false;
466 backgroundWorks.get(populateWork).notifyObservers();
467 populateRunning = false;
468 }
469@@ -595,20 +625,28 @@
470 return new DefaultPopulateWorker();
471 }
472
473- protected IndexName nextInQueue(Exchange ex) throws PersistitException
474+ protected IndexName nextInQueue(Session session, Exchange ex, boolean traversing) throws PersistitException
475 {
476 Key key = ex.getKey();
477
478- if (ex.next(true)) // empty tree?
479+ while (ex.next(true))
480 {
481 key.reset();
482 IndexName ret = new IndexName(new TableName(key.decodeString(),
483 key.decodeString()),
484 key.decodeString());
485+ // The populating map contains the indexes currently being built
486+ // if this name is already in the tree, skip this one, and try
487+ // the next.
488+ if (!traversing) {
489+ if (populating.putIfAbsent(ret, session) != null) {
490+ continue;
491+ }
492+ }
493+
494 return ret;
495 }
496- else
497- return null;
498+ return null;
499 }
500
501 protected Exchange getPopulateExchange(Session session) throws PersistitException
502@@ -624,10 +662,7 @@
503 private volatile boolean hasScheduled = false;
504 private volatile boolean populateEnabled = false;
505
506- private synchronized boolean addPopulate(Session session,
507- String schema,
508- String table,
509- String index) throws PersistitException
510+ private synchronized boolean addPopulate(Session session, IndexName name) throws PersistitException
511 {
512 Exchange ex = getPopulateExchange(session);
513
514@@ -635,9 +670,9 @@
515 // (Because they should have been removed in dropIndex())
516 // KEY: schema | table | indexName
517 ex.getKey().clear()
518- .append(schema)
519- .append(table)
520- .append(index);
521+ .append(name.getSchemaName())
522+ .append(name.getTableName())
523+ .append(name.getName());
524
525 // VALUE: <empty>
526
527
528=== modified file 'src/main/java/com/akiban/server/store/PersistitStore.java'
529--- src/main/java/com/akiban/server/store/PersistitStore.java 2013-05-03 00:59:49 +0000
530+++ src/main/java/com/akiban/server/store/PersistitStore.java 2013-05-09 13:22:31 +0000
531@@ -1824,14 +1824,20 @@
532 Map<Integer,RowDef> userRowDefs = new HashMap<>();
533 Set<Index> indexesToBuild = new HashSet<>();
534 for(Index index : indexes) {
535- IndexDef indexDef = index.indexDef();
536- if(indexDef == null) {
537- throw new IllegalArgumentException("indexDef was null for index: " + index);
538+ if (index.getIndexType() == IndexType.FULL_TEXT) {
539+ // This schedules a deferred process to populate the
540+ // full text index at a later date (starting in a few seconds).
541+ fullTextService.schedulePopulate(session, index.getIndexName());
542+ } else {
543+ IndexDef indexDef = index.indexDef();
544+ if(indexDef == null) {
545+ throw new IllegalArgumentException("indexDef was null for index: " + index);
546+ }
547+ indexesToBuild.add(index);
548+ RowDef rowDef = indexDef.getRowDef();
549+ userRowDefs.put(rowDef.getRowDefId(), rowDef);
550+ groups.add(rowDef.table().getGroup());
551 }
552- indexesToBuild.add(index);
553- RowDef rowDef = indexDef.getRowDef();
554- userRowDefs.put(rowDef.getRowDefId(), rowDef);
555- groups.add(rowDef.table().getGroup());
556 }
557 PersistitIndexRowBuffer indexRow = new PersistitIndexRowBuffer(adapter(session));
558 for (Group group : groups) {
559
560=== modified file 'src/main/java/com/akiban/server/store/PersistitStoreSchemaManager.java'
561--- src/main/java/com/akiban/server/store/PersistitStoreSchemaManager.java 2013-04-11 18:35:20 +0000
562+++ src/main/java/com/akiban/server/store/PersistitStoreSchemaManager.java 2013-05-09 13:22:31 +0000
563@@ -389,10 +389,7 @@
564 if(keepTree) {
565 newIndex.setTreeName(proposed.getTreeName());
566 }
567- if (newIndex.getIndexType() != Index.IndexType.FULL_TEXT) {
568- // TODO: For now, do not build.
569- newIndexes.add(newIndex);
570- }
571+ newIndexes.add(newIndex);
572 tableIDs.addAll(newIndex.getAllTableIDs());
573 schemas.add(DefaultNameGenerator.schemaNameForIndex(newIndex));
574 }
575
576=== modified file 'src/main/java/com/akiban/sql/aisddl/AISDDL.java'
577--- src/main/java/com/akiban/sql/aisddl/AISDDL.java 2013-03-27 04:42:10 +0000
578+++ src/main/java/com/akiban/sql/aisddl/AISDDL.java 2013-05-09 13:22:31 +0000
579@@ -84,7 +84,7 @@
580 server.getBinderContext(), context);
581 return;
582 case NodeTypes.CREATE_INDEX_NODE:
583- IndexDDL.createIndex(ddlFunctions, session, schema, (CreateIndexNode)ddl, server.getServiceManager());
584+ IndexDDL.createIndex(ddlFunctions, session, schema, (CreateIndexNode)ddl);
585 return;
586 case NodeTypes.DROP_INDEX_NODE:
587 IndexDDL.dropIndex(ddlFunctions, session, schema, (DropIndexNode)ddl, context);
588
589=== modified file 'src/main/java/com/akiban/sql/aisddl/IndexDDL.java'
590--- src/main/java/com/akiban/sql/aisddl/IndexDDL.java 2013-04-15 17:21:44 +0000
591+++ src/main/java/com/akiban/sql/aisddl/IndexDDL.java 2013-05-09 13:22:31 +0000
592@@ -36,8 +36,6 @@
593 import com.akiban.server.service.session.Session;
594
595 import com.akiban.qp.operator.QueryContext;
596-import com.akiban.server.service.ServiceManager;
597-import com.akiban.server.service.text.FullTextIndexService;
598
599 /** DDL operations on Indices */
600 public class IndexDDL
601@@ -150,18 +148,17 @@
602 public static void createIndex(DDLFunctions ddlFunctions,
603 Session session,
604 String defaultSchemaName,
605- CreateIndexNode createIndex,
606- ServiceManager sm) {
607+ CreateIndexNode createIndex) {
608 AkibanInformationSchema ais = ddlFunctions.getAIS(session);
609
610 Collection<Index> indexesToAdd = new LinkedList<>();
611
612- indexesToAdd.add(buildIndex(ais, defaultSchemaName, createIndex, sm));
613+ indexesToAdd.add(buildIndex(ais, defaultSchemaName, createIndex));
614
615 ddlFunctions.createIndexes(session, indexesToAdd);
616 }
617
618- protected static Index buildIndex (AkibanInformationSchema ais, String defaultSchemaName, CreateIndexNode index, ServiceManager sm) {
619+ protected static Index buildIndex (AkibanInformationSchema ais, String defaultSchemaName, CreateIndexNode index){
620 final String schemaName = index.getObjectName().getSchemaName() != null ? index.getObjectName().getSchemaName() : defaultSchemaName;
621 final String indexName = index.getObjectName().getTableName();
622
623@@ -176,7 +173,7 @@
624
625 if (index.getColumnList().functionType() == IndexColumnList.FunctionType.FULL_TEXT) {
626 logger.debug ("Building Full text index on table {}", tableName) ;
627- tableIndex = buildFullTextIndex (builder, tableName, indexName, index, sm);
628+ tableIndex = buildFullTextIndex (builder, tableName, indexName, index);
629 } else if (checkIndexType (index, tableName) == Index.IndexType.TABLE) {
630 logger.debug ("Building Table index on table {}", tableName) ;
631 tableIndex = buildTableIndex (builder, tableName, indexName, index);
632@@ -331,9 +328,8 @@
633 return builder.akibanInformationSchema().getGroup(groupName).getIndex(indexName);
634 }
635
636- protected static Index buildFullTextIndex (AISBuilder builder, TableName tableName, String indexName, IndexDefinition index, ServiceManager sm) {
637+ protected static Index buildFullTextIndex (AISBuilder builder, TableName tableName, String indexName, IndexDefinition index) {
638 UserTable table = builder.akibanInformationSchema().getUserTable(tableName);
639- FullTextIndexService fullTextService = sm.getServiceByClass(FullTextIndexService.class);
640
641 if (index.getJoinType() != null) {
642 throw new TableIndexJoinTypeException();
643@@ -367,8 +363,6 @@
644 }
645
646 builder.fullTextIndexColumn(tableName, indexName, schemaName, columnTable.getTableName(), columnName, i);
647- // populate index
648- fullTextService.schedulePopulate(schemaName, tableName.getTableName(), indexName);
649 i++;
650 }
651 return builder.akibanInformationSchema().getUserTable(tableName).getFullTextIndex(indexName);
652
653=== modified file 'src/main/java/com/akiban/sql/aisddl/TableDDL.java'
654--- src/main/java/com/akiban/sql/aisddl/TableDDL.java 2013-04-28 04:20:16 +0000
655+++ src/main/java/com/akiban/sql/aisddl/TableDDL.java 2013-05-09 13:22:31 +0000
656@@ -48,7 +48,6 @@
657 import com.akiban.sql.parser.ConstraintDefinitionNode;
658 import com.akiban.sql.parser.CreateTableNode;
659 import com.akiban.sql.parser.CurrentDatetimeOperatorNode;
660-import com.akiban.sql.parser.DefaultNode;
661 import com.akiban.sql.parser.DropGroupNode;
662 import com.akiban.sql.parser.DropTableNode;
663 import com.akiban.sql.parser.ExistenceCheck;
664@@ -487,7 +486,7 @@
665
666 if (columnList.functionType() == IndexColumnList.FunctionType.FULL_TEXT) {
667 logger.debug ("Building Full text index on table {}", table.getName()) ;
668- tableIndex = IndexDDL.buildFullTextIndex (builder, table.getName(), indexName, id, context.getServiceManager());
669+ tableIndex = IndexDDL.buildFullTextIndex (builder, table.getName(), indexName, id);
670 } else if (IndexDDL.checkIndexType (id, table.getName()) == Index.IndexType.TABLE) {
671 logger.debug ("Building Table index on table {}", table.getName()) ;
672 tableIndex = IndexDDL.buildTableIndex (builder, table.getName(), indexName, id);
673
674=== modified file 'src/test/java/com/akiban/server/rowdata/SchemaFactory.java'
675--- src/test/java/com/akiban/server/rowdata/SchemaFactory.java 2013-04-15 16:20:25 +0000
676+++ src/test/java/com/akiban/server/rowdata/SchemaFactory.java 2013-05-09 13:22:31 +0000
677@@ -125,7 +125,7 @@
678 if (stmt instanceof CreateTableNode) {
679 TableDDL.createTable(ddlFunctions , session , defaultSchema, (CreateTableNode) stmt, null);
680 } else if (stmt instanceof CreateIndexNode) {
681- IndexDDL.createIndex(ddlFunctions, session, defaultSchema, (CreateIndexNode) stmt, sm);
682+ IndexDDL.createIndex(ddlFunctions, session, defaultSchema, (CreateIndexNode) stmt);
683 } else if (stmt instanceof CreateViewNode) {
684 ViewDDL.createView(ddlFunctions, session, defaultSchema, (CreateViewNode) stmt,
685 new AISBinderContext(ddlFunctions.getAIS(session), defaultSchema), null);
686
687=== added file 'src/test/java/com/akiban/server/service/text/FullTextIndexServiceBug1172013IT.java'
688--- src/test/java/com/akiban/server/service/text/FullTextIndexServiceBug1172013IT.java 1970-01-01 00:00:00 +0000
689+++ src/test/java/com/akiban/server/service/text/FullTextIndexServiceBug1172013IT.java 2013-05-09 13:22:31 +0000
690@@ -0,0 +1,296 @@
691+/**
692+ * Copyright (C) 2009-2013 Akiban Technologies, Inc.
693+ *
694+ * This program is free software: you can redistribute it and/or modify
695+ * it under the terms of the GNU Affero General Public License as published by
696+ * the Free Software Foundation, either version 3 of the License, or
697+ * (at your option) any later version.
698+ *
699+ * This program is distributed in the hope that it will be useful,
700+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
701+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
702+ * GNU Affero General Public License for more details.
703+ *
704+ * You should have received a copy of the GNU Affero General Public License
705+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
706+ */
707+package com.akiban.server.service.text;
708+
709+import static org.junit.Assert.assertEquals;
710+import static org.junit.Assert.assertTrue;
711+import java.sql.Connection;
712+import java.sql.DriverManager;
713+import java.sql.SQLException;
714+
715+import org.junit.Before;
716+import org.junit.Test;
717+
718+import org.slf4j.Logger;
719+import org.slf4j.LoggerFactory;
720+
721+import com.akiban.ais.model.IndexName;
722+import com.akiban.ais.model.TableName;
723+import com.akiban.qp.operator.QueryContext;
724+import com.akiban.qp.persistitadapter.PersistitAdapter;
725+import com.akiban.qp.rowtype.Schema;
726+import com.akiban.qp.util.SchemaCache;
727+import com.akiban.server.error.DuplicateIndexColumnException;
728+import com.akiban.server.error.DuplicateIndexException;
729+import com.akiban.server.service.servicemanager.GuicedServiceManager;
730+import com.akiban.server.service.session.Session;
731+import com.akiban.server.service.session.SessionServiceImpl;
732+import com.akiban.server.test.it.ITBase;
733+import com.akiban.server.types3.mcompat.mfuncs.WaitFunctionHelpers;
734+import com.akiban.sql.embedded.EmbeddedJDBCService;
735+import com.akiban.sql.embedded.EmbeddedJDBCServiceImpl;
736+import com.persistit.Exchange;
737+import com.persistit.exception.PersistitException;
738+
739+public class FullTextIndexServiceBug1172013IT extends ITBase {
740+ public static final String SCHEMA = "test";
741+ protected FullTextIndexService fullText;
742+ protected Schema schema;
743+ protected PersistitAdapter adapter;
744+ protected QueryContext queryContext;
745+ private int c;
746+ private int o;
747+ private int i;
748+ private int a;
749+ private static final Logger logger = LoggerFactory.getLogger(FullTextIndexServiceBug1172013IT.class);
750+
751+ @Override
752+ protected GuicedServiceManager.BindingsConfigurationProvider serviceBindingsProvider() {
753+ return super.serviceBindingsProvider()
754+ .bindAndRequire(FullTextIndexService.class, FullTextIndexServiceImpl.class)
755+ .bindAndRequire(EmbeddedJDBCService.class, EmbeddedJDBCServiceImpl.class);
756+ }
757+
758+ @Before
759+ public void createData() {
760+ c = createTable(SCHEMA, "c",
761+ "cid INT PRIMARY KEY NOT NULL",
762+ "name VARCHAR(128) COLLATE en_us_ci");
763+ o = createTable(SCHEMA, "o",
764+ "oid INT PRIMARY KEY NOT NULL",
765+ "cid INT NOT NULL",
766+ "c1 VARCHAR(128) COLLATE en_us_ci",
767+ "c2 VARCHAR(128) COLLATE en_us_ci",
768+ "c3 VARCHAR(128) COLLATE en_us_ci",
769+ "c4 VARCHAR(128) COLLATE en_us_ci",
770+ "GROUPING FOREIGN KEY(cid) REFERENCES c(cid)",
771+ "order_date DATE");
772+ i = createTable(SCHEMA, "i",
773+ "iid INT PRIMARY KEY NOT NULL",
774+ "oid INT NOT NULL",
775+ "GROUPING FOREIGN KEY(oid) REFERENCES o(oid)",
776+ "sku VARCHAR(10) NOT NULL");
777+ a = createTable(SCHEMA, "a",
778+ "aid INT PRIMARY KEY NOT NULL",
779+ "cid INT NOT NULL",
780+ "GROUPING FOREIGN KEY(cid) REFERENCES c(cid)",
781+ "state CHAR(2)");
782+ writeRow(c, 1, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam ultrices justo in sapien ullamcorper eu mattis massa pretium.");
783+ writeRow(o, 101, 1, "c1", "c2", "c3", "c4", "2012-12-12");
784+ writeRow(i, 10101, 101, "ABCD");
785+ writeRow(i, 10102, 101, "1234");
786+ writeRow(o, 102, 1, "c1", "c2", "c3", "c4","2013-01-01");
787+ writeRow(a, 101, 1, "MA");
788+ writeRow(c, 2, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam ultrices justo in sapien ullamcorper eu mattis massa pretium.");
789+ writeRow(a, 201, 2, "NY");
790+ writeRow(c, 3, "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam ultrices justo in sapien ullamcorper eu mattis massa pretium.");
791+ writeRow(o, 301, 3, "c1", "c2", "c3", "c4", "2010-04-01");
792+ writeRow(a, 301, 3, "MA");
793+ writeRow(a, 302, 3, "ME");
794+
795+ fullText = serviceManager().getServiceByClass(FullTextIndexService.class);
796+
797+ schema = SchemaCache.globalSchema(ais());
798+ adapter = persistitAdapter(schema);
799+ queryContext = queryContext(adapter);
800+ }
801+
802+ @Test
803+ public void testDelete1 () throws InterruptedException, PersistitException {
804+ logger.error("Running test delete 1");
805+ // This test is specifically for FullTextIndexServiceImpl.java
806+ assertEquals(FullTextIndexServiceImpl.class, fullText.getClass());
807+ FullTextIndexServiceImpl fullTextImpl = (FullTextIndexServiceImpl)fullText;
808+
809+ // disable the populate worker (so it doesn't read all the entries
810+ // out before we get a chance to look at the tree.
811+ createFullTextIndex(serviceManager(),
812+ SCHEMA, "o", "idx3_o",
813+ "oid", "c1", "c2", "c3", "c4");
814+ //fullTextImpl.getBackgroundWorks().get(0).forceExecution();
815+ //WaitFunctionHelpers.waitOn(fullText.getBackgroundWorks());
816+
817+ new Thread(new DropIndex()).start();
818+
819+ try {
820+ createFullTextIndex(serviceManager(),
821+ SCHEMA, "o", "idx3_o",
822+ "oid", "c1", "c2", "c3", "c4");
823+ } catch (DuplicateIndexException ex) {
824+ logger.error("Got Duplicate Index");
825+ ; // an expected possible outcome.
826+ } catch (DuplicateIndexColumnException ex) {
827+ logger.error("Got Duplicate Column");
828+ ; // an expected possible outcome
829+ }
830+
831+ WaitFunctionHelpers.waitOn(fullText.getBackgroundWorks());
832+
833+ traverse(fullTextImpl,
834+ new Visitor()
835+ {
836+ int n = 0;
837+
838+ @Override
839+ public void visit(IndexName idx)
840+ {
841+ ++n;
842+ }
843+
844+ @Override
845+ public void endOfTree()
846+ {
847+ assertEquals (0, n);
848+ }
849+ });
850+
851+ }
852+
853+ @Test
854+ public void testDelete2() throws InterruptedException, PersistitException {
855+ logger.error("Running test delete 2");
856+ // This test is specifically for FullTextIndexServiceImpl.java
857+ assertEquals(FullTextIndexServiceImpl.class, fullText.getClass());
858+ FullTextIndexServiceImpl fullTextImpl = (FullTextIndexServiceImpl)fullText;
859+
860+ createFullTextIndex(serviceManager(),
861+ SCHEMA, "o", "idx3_o",
862+ "oid", "c1", "c2", "c3", "c4");
863+ fullTextImpl.getBackgroundWorks().get(0).forceExecution();
864+ WaitFunctionHelpers.waitOn(fullText.getBackgroundWorks());
865+
866+ new Thread(new DropIndex()).start();
867+
868+ try {
869+ createFullTextIndex(serviceManager(),
870+ SCHEMA, "o", "idx3_o",
871+ "oid", "c1", "c2", "c3", "c4");
872+ } catch (DuplicateIndexException ex) {
873+ logger.error("Got Duplicate Index");
874+ ; // an expected possible outcome.
875+ } catch (DuplicateIndexColumnException ex) {
876+ logger.error("Got Duplicate Column");
877+ ; // an expected possible outcome
878+ }
879+
880+ WaitFunctionHelpers.waitOn(fullText.getBackgroundWorks());
881+
882+ traverse(fullTextImpl,
883+ new Visitor()
884+ {
885+ int n = 0;
886+
887+ @Override
888+ public void visit(IndexName idx)
889+ {
890+ ++n;
891+ }
892+
893+ @Override
894+ public void endOfTree()
895+ {
896+ assertEquals (0, n);
897+ }
898+ });
899+
900+
901+ }
902+
903+ @Test
904+ public void testDelete3() throws InterruptedException, PersistitException {
905+ logger.error("Running test delete 3");
906+ // This test is specifically for FullTextIndexServiceImpl.java
907+ assertEquals(FullTextIndexServiceImpl.class, fullText.getClass());
908+ FullTextIndexServiceImpl fullTextImpl = (FullTextIndexServiceImpl)fullText;
909+
910+ createFullTextIndex(serviceManager(),
911+ SCHEMA, "o", "idx3_o",
912+ "oid", "c1", "c2", "c3", "c4");
913+ fullTextImpl.getBackgroundWorks().get(0).forceExecution();
914+ //WaitFunctionHelpers.waitOn(fullText.getBackgroundWorks());
915+
916+ deleteFullTextIndex(serviceManager(), new IndexName(new TableName(SCHEMA, "o"), "idx3_o"));
917+
918+ WaitFunctionHelpers.waitOn(fullText.getBackgroundWorks());
919+
920+ traverse(fullTextImpl,
921+ new Visitor()
922+ {
923+ int n = 0;
924+
925+ @Override
926+ public void visit(IndexName idx)
927+ {
928+ ++n;
929+ }
930+
931+ @Override
932+ public void endOfTree()
933+ {
934+ assertEquals (0, n);
935+ }
936+ });
937+
938+
939+ }
940+
941+
942+ private static interface Visitor
943+ {
944+ void visit(IndexName idx);
945+ void endOfTree();
946+ }
947+
948+ private class DropIndex implements Runnable
949+ {
950+ @Override
951+ public void run()
952+ {
953+ Connection conn;
954+ try {
955+ conn = DriverManager.getConnection("jdbc:default:connection", "test", "");
956+ conn.createStatement().execute("DROP INDEX test.o.idx3_o");
957+ } catch (SQLException e) {
958+ logger.error("drop index failed; {}", e.getMessage());
959+ assertTrue ("Drop index failed", false);
960+ }
961+ //IndexName name = new IndexName (new TableName(SCHEMA, "o"), "idx3_o");
962+ //deleteFullTextIndex(serviceManager(), name);
963+ }
964+ };
965+
966+
967+
968+ private static void traverse(FullTextIndexServiceImpl serv,
969+ Visitor visitor) throws PersistitException
970+ {
971+ Session session = new SessionServiceImpl().createSession();
972+
973+ try
974+ {
975+ Exchange ex = serv.getPopulateExchange(session);
976+ IndexName toPopulate;
977+ while ((toPopulate = serv.nextInQueue(session, ex, true)) != null)
978+ visitor.visit(toPopulate);
979+ visitor.endOfTree();
980+ }
981+ finally
982+ {
983+ session.close();
984+ }
985+ }
986+}
987
988=== modified file 'src/test/java/com/akiban/server/service/text/FullTextIndexServiceIT.java'
989--- src/test/java/com/akiban/server/service/text/FullTextIndexServiceIT.java 2013-04-22 19:01:34 +0000
990+++ src/test/java/com/akiban/server/service/text/FullTextIndexServiceIT.java 2013-05-09 13:22:31 +0000
991@@ -38,10 +38,11 @@
992 import com.persistit.exception.PersistitException;
993 import org.junit.Before;
994 import org.junit.Test;
995+import org.slf4j.Logger;
996+import org.slf4j.LoggerFactory;
997+
998 import static org.junit.Assert.*;
999
1000-import java.util.*;
1001-
1002 public class FullTextIndexServiceIT extends ITBase
1003 {
1004 public static final String SCHEMA = "test";
1005@@ -49,6 +50,8 @@
1006 protected Schema schema;
1007 protected PersistitAdapter adapter;
1008 protected QueryContext queryContext;
1009+ private static final Logger logger = LoggerFactory.getLogger(FullTextIndexServiceIT.class);
1010+
1011
1012 private int c;
1013 private int o;
1014@@ -108,6 +111,7 @@
1015 assertEquals(FullTextIndexServiceImpl.class, fullText.getClass());
1016 FullTextIndexServiceImpl fullTextImpl = (FullTextIndexServiceImpl)fullText;
1017
1018+ logger.error ("DDL Service: {}", ddl().getClass().getCanonicalName());
1019 // disable the populate worker (so it doesn't read all the entries
1020 // out before we get a chance to look at the tree.
1021 fullTextImpl.disablePopulateWorker();
1022@@ -246,7 +250,7 @@
1023 {
1024 Exchange ex = serv.getPopulateExchange(session);
1025 IndexName toPopulate;
1026- while ((toPopulate = serv.nextInQueue(ex)) != null)
1027+ while ((toPopulate = serv.nextInQueue(session, ex, true)) != null)
1028 visitor.visit(toPopulate);
1029 visitor.endOfTree();
1030 }

Subscribers

People subscribed via source and target branches