Prelude from freblasty:
I’ve committed a change to the tracking logic. Indexed items are marked using changed timestamp of “0”. Also inserted items use REQUEST_TIME. This will ensure all items are processed in the order that they got tracked. The usage of getLastIndexed() and setLastIndexed() are commented out and left there until the now tracker flow is reviewed.
Because of this commit the index batch process is operational now. On a side note I’m wondering whether Index::clear() shouldn’t take the index status and read-only value into account?
Current implementation:
/**
* {@inheritdoc}
*/
public function clear() {
$this->getServer()->deleteAllItems($this);
$this->reindex();
return TRUE;
}
Proposed implementation:
/**
* {@inheritdoc}
*/
public function clear() {
if ($this->hasValidServer() && $this->reindex()) {
$this->getServer()->deleteAllItems($this);
return TRUE;
}
return FALSE;
}
The proposed implementation allows the clear function to reuse the same conditions used by reindex. This way the IndexClearConfirmForm could reuse clear() instead of defining its own logic.
Comment | File | Size | Author |
---|---|---|---|
#43 | interdiff.txt | 5.29 KB | amateescu |
#43 | 2232253-42.patch | 92.07 KB | amateescu |
#41 | interdiff.txt | 27.14 KB | amateescu |
#39 | 2232253-39.patch | 90.93 KB | freblasty |
#32 | 2232253-32.patch | 84.69 KB | freblasty |
Comments
Comment #1
Nick_vhI'm a little scared you reverted a design decision we made during dev days. The goal is to reduce the writes so whenever an item is tracked it should not update that row in the table. Indexing should be a read-only thing and using the variable we should keep track of where we were in the process.
From what I remember, the getRemainingItems() function does not incorporate the "new" items yet but it was still an item in the todo list.
I'll review it but I think we might need to revert it and talk more before we make those kind of changes.
Comment #2
Nick_vhAlso, we agreed we would not add functionality without tests so I'm going to revert your change and perhaps we can meet during the day to discuss it?
I'll be on IRC until 5:30PM.
Comment #3
Nick_vhComment #4
Nick_vhUploaded patch works but is far from perfect. But it does show that we do not need the reset to 0 after indexing. It just does not make sense in a large scale site to update millions of rows during an indexing phase if all you want is to keep track of where you were.
Comment #5
aspilicious CreditAttribution: aspilicious commentedI agree with nick on this one, such changes should be handled by patches in stead of a push, in order to maintain a clear overview of the end product.
And how the end result should work frontend and backend wise.
Comment #6
freblasty CreditAttribution: freblasty commentedThe patch in #3 ensure all items are processed in chronological order and the index simply keeps track of the last processed item. However new items are being tracked using changed '0' which breaks the chronological order. The patch attached is based on #3 with two minor changes:
- inserting an item for tracking uses REQUEST_TIME;
- as changed with value '0' is no longer possible, i removed it from the getRemainingItemsQuery().
I've tested the workflow by hand for now and everything seems to work. Only thing I noticed is that when using the batch process it reports items not being indexed while everything indexed. But i'll fix that problem once this issue is fixed.
PS: Sorry for reverting a design decision that was made during dev days.
Comment #7
freblasty CreditAttribution: freblasty commentedNotes from brief hangout between me and Nick
It has come to my attention that newly tracked items were set to timestamp '0' deliberately. This to ensure new items get processed before changed items. However this causes a problem in how items are being processed using the new design.
In the new design tracking indexed items should never perform any database writes. To keep track of what was indexed and what is pending we use a state per index. As all items are sorted chronologically we only need to save the last indexed item from this chronological list to keep track of the progress (FIFO).
As a result adding tracked items with timestamp '0' causes the chronological list to enter a corrupt state. In a normal sens everything up to the last indexed state have been processed but when an item with timestamp '0' is added the meaning of processed breaks and is no longer guaranteed.
Action
Before we create some more advanced queue to ensure new items get processed before changed items, we're wondering if processing new items before changed is really that important? It seems more logical that everything gets processed in the same order as they were add/modified.
@Thomas: Could you add some feedback?
Comment #8
freblasty CreditAttribution: freblasty commentedComment #9
drunken monkeyIt was a design decision already in Drupal 7 that it makes sense to index newly created items as soon as possible, since they otherwise won't show up at all in searches, while changed items' indexed state will usually be already very similar to the new one. Implementing this with the new system is trivial, just using
WHERE changed = 0 OR changed > :last_time OR (changed = :last_time AND item_id > :last_id)
. It would of course necessitate a second write per item in its total lifecycle (a third, if you count an eventual delete), but I don't think that's such a big problem.I'm prepared to let myself be overruled here, though, if most people feel indexing items in the order of the changes makes more sense than indexing new items first.
So, what's everyone else's take on this?
Comment #10
Nick_vhWhat we are building here is a priority tracking table and I'm sure there are better ways for building such systems then to "fake" the created timestamp in the tracking table.
As I see it we have some options here:
1) Use a first in first out approach and clean up the special case for the new items. This would make it simpler to understand what is going on in the tracking table + you avoid all writes during the indexing phase
2) Use a first in first out approach but add prioritization to this system. It should be a proper prioritization since other systems should be able to leverage this. An idea would be an extra column with priority and integer values when adding it. Similar to "boosts". We can sort on those values so that the order of changed is always the same. It does make it much more difficult to then get the next set of values because now we need to store the prioirty and the item id to get a grip on where we were last time but the priority could have changed. I don't think this system is stable enough to be reliable for large scale deployments.
3) Option 1 + an option to index new items directly on creation. This would be similar to the index items immediately but only for new items. This allows the user to choose if new items should be indexed instantly
I'm a big fan of option 3, as it does not remove the priority system but it leverages different systems.
Comment #11
alarcombe CreditAttribution: alarcombe commentedAgree with nick about faking timestamps. Does the job, but whilst we've got the space to Do It Right, then we probably should. Also there will be different requirements based on use cases. I'm a big fan of 'let the user decide but provide sensible defaults for the 90%' - we shouldn't enforce one way of doing things at the exclusion of others which may be more appropriate.
So I'm in favour of number 3 :)
Comment #12
freblasty CreditAttribution: freblasty commentedComment #13
freblasty CreditAttribution: freblasty commentedFirst attempt at implementing option 3. This patch will allow users to select three types of direct indexing: "Disabled", "New items" and "New and changed items". Depending on the direct indexing type the datasource plugin will react differently when
DatasourceInterface::trackInsert()
orDatasourceInterface::trackUpdated()
is invoked.Once an index has queued items it will register itself for processing. Processing itself is done by using a shutdown function. All queues and list are implemented using
Drupal::state()
.Currently there is still one problem I could find with this implementation. When last indexed is set to its initial state "id:0 and changed:0" then adding items to the queue fails and nothing is being processed or added to the tracking table. Should we update last indexed in this edge case?
Edit: This patch is based on the previous patches.
Comment #14
aspilicious CreditAttribution: aspilicious commentedWhen creating yml files it's important that they are human readable.
I think we should switch to "text based" options for the indexing options in stead of numbers.
Makes it very easy for site builders and search api maintainers to investigate the config.
Comment #15
Nick_vhIf we index all directly. Is there a need to keep track of the items in the table? Might be a silly question, but we would only need to keep track of this in case we want to switch back to a tracking system? I guess I'm a little confused on the purpose of index all directly and its impact on the tracking table & performance
Might need more explanations so that people understand why it goes to -1. This changes the timestamp to 1 second before the last updated date of the index pointer. This means that we will trick the indexer into thinking it already indexed it by putting a timestamp in the table that is not 0 but also not one of the values that will be chosen next.
one liners are neat but unreadable. Please make it more readable
Actually there is no queue anymore, it's a tracking table. Can we rename this?
The queue is only used for tasks, not really for items. I guess this addition of the queue might not even belong to this patch
So in short, the queue that you added here really is another issue and we're making this too complex. In D7 Search API it was decided to not use queue's anymore for those purposes OR I'm confused why you are using the queue here. Queue's are inflexible for tracking items. Saving an entity twice adds it twice to the queue as you cannot inspect and iterate over a queue.
Comment #16
Nick_vhComment #17
freblasty CreditAttribution: freblasty commentedThe queue is being used to hold items that need processing. Once processed they get removed from the queue. Note that queue might not be the correct in this context. It is simply a storage not a real Drupal Queue.
In D7 Search API a static cache was used to hold the items which need direct processing.
Comment #18
Nick_vhHmm, so this "queue" will never live out of the page request because we're using the subscriber to execute the processQueueItems I see. So if we have a drush script that updates 1000 items this will add all those items to the list/queue and process them at the end of the update process?
The whole logic makes sense but I'd be careful calling it queue. as it is not in line with a "drupal" queue. It's a page-request-static-cache using the Drupal 8 state system.
let's rename it so it's clear. itemsToIndexList, staticQueue?
Comment #19
Nick_vhAnother suggestion for the name:
ephemeralTracking
addToEphemeralTracking
removeFromEphemeralTracking
processEphemeralTracking
...
ephemeral is often used to show a static state that does not live outside of the current scope. Such as cloud storage, .... See http://en.wikipedia.org/wiki/Ephemeral
Comment #20
Nick_vhMaking it work with both the test services
Comment #21
Nick_vhReroll so it applies again after the options of the index were fixed.
Comment #22
drunken monkeyWhat is that supposed to mean? Also, I don't see any change in the code relating to this.
This code doesn't belong into the datasource controller. This logic is completely generic for the index, the datasource should only need to know about new/changed/indexed/deleted items.
If you need to use such complicated logic here, and even set a wrong timestamp after all, then this might show the limit of the new tracking system. (See also below.)
This also makes me worry a lot about the capabilities of the new tracking system. You don't check at all which items were successfully indexed, or whether indexing failed for some of them – you only remember a single ID (implicitly assuming the IDs are in the same order they were returned in from
getRemainingItems()
, which is also not guaranteed in any way).While I understand that reducing writes is a good goal, the system has to be correct and error-proof foremost. If this isn't possible or too complicated with the write-optimized system, we should probably switch back to the old one. (Minus the magic "1" for new items.)
I don't think the usage of the State API is needed here. If you only save the items for the request, you can just use a property on the entity object, or a static property on the utility class, or something similar.
I'm also not sure that the index entity class needs to know about this functionality at all. I think reacting to CUD of items should be handled in the utility class.
It also doesn't make sense, in my opinion, to add a
$limit
parameter to this method.This code more or less proves that the current code in this patch is way too tightly coupled. Changing an option just for one method call is usually a strong sign for that.
Dequeueing items should also not be necessary – it will only be relevant if someone programmatically does both an "Index now" and a "Queue all items for reindexing" in the same page request; and if they do, it shouldn't be our problem.
$this
is not defined here (but, as said, I don't think this code is needed anyways).Does it need the "SearchApi" prefix?
Also, what does it do? It should definitely have a class documentation explaining exactly that (because I really have no clue).
Obviously missing documentation here, at least a
{@inheritdoc}
.Trailing whitespace.
Also, it's "null" in doc comments, all lower case.
I think these should now use
const
instead ofdefine()
.Also, if we are keeping integers instead of names (though I'm also more in favor of names), I would make them bitmasks. So, create an additional "*_CHANGED" constant with 2, and set "*_ALL" to 3 (i.e., ALL | CHANGED).
This method doesn't exist in the patch.
Also, I don't like that name at all, "ephemeral" is a much too complicated word here. I also don't think I've ever seen it used in code.
In D7 this was called "index_delayed", maybe that's good enough? Otherwise, "static queue" might be a better name. I also was very confused by the use of "queue" here.
rememberForIndexing()
orindexAtEndOfRequest()
? All pretty horrible, unfortunately …Comment #23
Nick_vhRE: 2)
I'm not sure if the datasource should even know about new versus changed. Keep in mind that we're trying to mimic the same behavior as Drupal 7 but by being more generic. Adding the "Index new items directly" adds this -1 complexity. But perhaps there is even a better way. Maybe the index should then not even send the item to the tracking table but only add it when we're doing an update. This keeps the tracking table from additional complexity while maintaining the "index new items directly".
FWIW I'm not even sure if new items should be indexed directly and perhaps that could even be a separate patch. Based on my experience this is not something that people really care much about. It's either all directly or all queued.
3)
If an item failed, the indexing should probably stop. If we decide to redo the item we need to re-add the item to the tracking table meaning that the changed value needs to be updated to the current REQUEST_TIME. Having the pointer be accurate is a critical part of the system indeed but it is very possible to make that fool proof. A failed item does not mean that the tracking table is incorrect. It all depends on the behavior on failure what to do with that item. getRemainingItems should guarantee that the items are returned in the same order and gives you back the next set to where we need to point the pointer to.
5)
Or we are trying to do too many things at once. If we can start with a good tracking system, we can add the index directly option in a follow-up once the tracking is stabilized? If not, we have some iterations to do :)
Regarding the name of this queue mechanism, ephemeral is perhaps complex but it does say what it is. A short-lived storage. It's used in servers and fwiw I also use it in professional contexts.
Comment #24
freblasty CreditAttribution: freblasty commentedSecond attempt at option 3. I've tried to decouple the code for indexing more and added a seperate utility class "EphemeralTracking". All defines are replaced with constants.
Current implementation for direct indexing will follow normal indexing flow. That is an item will be added or updated in the tracking table. Then added to the ephemeral tracking. Because of this when an error would occur during direct index processing the items will still get picked up by the cron.
Note: patch is based on changes from #6 and also includes changes made by Nick in #22 and #23.
Comment #25
Nick_vhAlso, before this can go in we need to add tests so we know this code is robust and is working.
Comment #26
drunken monkeySo one faulty/problematic item could stop, or at least seriously slow down, the entire indexing process? That doesn't seem reasonable. Marking the failed items like they were newly changed, like you suggest afterwards, makes a lot more sense to me. This would at least preserve that capability of the old system. However, that should then be included in this patch. (See the D7 tests, where I also test an item which fails to be indexed. This should work in D8, too, or we'd have to redefine that part of the API.)
However, I don't think that your system would (cleanly) support indexing individual items either, right? You could only mark them as changed and then wait until they are indexed based on the normal order of indexing. (Or just index them without informing the datasource and risking to index it a second time in the same state.)
This would also pose a problem for any "Index immediately" functionality: you would either have to insert/update the item with a wrong "changed" value, or risk losing updates to still unindexed items. (It's not guaranteed that just because "Index immediately" is enabled, no items will be "dirty" in the tracking system – not even if we assume that items will always be indexed without fail.) Or just index it without "telling" the tracking system, ending up with a wrong "changed" value after all and risking an inconsistent/wrong state if indexing fails.
As said,
getRemainingItems()
is not the problem – that's in the same class, we can let it guarantee anything we want.However, all the rest of the indexing pipeline does not guarantee in any way that the order of items would be the same, and we'd have to add quite a bit of either code or method contracts to change that.
I think the problem is rather a case of "premature optimization". The D7 system worked perfectly fine, has now been tested for years (and refined, with your and Acquia's help) and was pretty self-explanatory. To improve the latter, we could even add a "state" column, as Frédéric initially did in the D8 port, making the whole system really simple. (Dropping the "new items: changed = 1" special case would be fine for me, too.) With the current new approach, it seems that we trade a very minor performance improvement against either reduced functionality and correctness or largely increased complexity (and thus, error-proneness).
Furthermore, since the D7 system is very much decoupled, it would even be possible to just override this system if the few extra DB writes really are a problem for someone. We could even try to take Frédéric's initial approach a bit further and decouple tracking (more or less) completely from the datasource (which might make much sense in any case for #2230931: Supporting multiple item types per index). That way, overriding this part would become even easier.
In contrast, at least in the current state or from what I could come up with, the new system would be more tightly coupled, making it harder to completely replace with a custom solution. The less we assume of any part of the system, the better for flexibility.
So, I agree that we can very well add and/or refine "Index items immediately" later. But a "good tracking system" as the basis would be one which we don't have to adapt when adding such features that don't/shouldn't impact the tracking directly.
And, something else entirely:
This will also change the "changed" column of items that would already be re-indexed, thus just delaying their being indexed. While unlikely, this might/will even lead to some cases where a frequently updated item never gets indexed.
Since we don't store explicitly which items still need to be indexed, we'd probably have to add the same logic here as for
getRemainingItems()
to avoid this scenario.Comment #27
Nick_vhTotally agree. I'm all for flexibility and perhaps we went quite far here in trying to optimize - blame engineering-minds right?
I would suggest we look at http://en.wikipedia.org/wiki/Priority_queue and try to get the most basic implementation in as a generic system so that they can be overridden with priority queues of all sorts when that might become a necessity later on.
My thoughts after reading all of the above
I'm not fully aware of your thoughts on the tracking system in regards to the multiple entity types per index but I'd love to hear/read them.
Comment #28
Nick_vhComment #29
drunken monkeyExcellent, great to hear. Your plan looks very sound, too.
The good thing is that this wouldn't even be necessary. We only define the interface, implementations can track the items any way they want. We wouldn't need a priority column for our basic implementation, but if someone wants to use one, they can just use a different table with that additional column. (Same if we later want to use a priority queue ourselves after all – we just add a new column to the table, but nothing about the interface changes.)
Re 3: Deleting items is, I think, out of the question simply because we then couldn't show the total amount of items (and the number of indexed ones) anymore. A small feature, sure, but still an important one, I think. Probably it would be possible to keep track of the total or indexed number in some other way, but altogether I think these are also optimizations we should leave to others, if they want them. (What would be the reasoning behind it, anyways? Just the smaller table, or are deletes less expensive than updates?)
Regarding the "state"/"status" (TBD) column: what would be the advantage compared to the current system of using "changed" as a dual-purpose column? Just increased clarity, or is there another advantage, too? In any case I wouldn't be opposed to that change, probably that decision was a premature optimization, too.
Well, Frédéric's system wasn't really decoupled, it was kind of half-way. What I'd envision now would be that these two systems become completely separate, and that the index just retrieves both a datasource (or several, (hopefully) soon) and a tracker to use for their respective purposes. The tracker would then probably be globally pluggable in some way – maybe by using the utility class to create it and then allowing it there to be overridden in some way. Or maybe we should make it a service? But we can't inject that into the index anyways, right? It might still make sense as a service, though. That's probably a question for Bram or Andrei.
In any case, I don't see a use case for making that overridable on a per-index base, right? (If you really want, it would still be possible, by either overriding the entity class or by creating a proxy tracker class that uses a different implementation for each index.)
One problem with this would be that the tracker would then be unaware of any datasource settings restricting the indexed items. That means, the logic for only tracking items of the selected bundles (in our case) would have to live in
search_api_entity_(insert/update/delete)()
, before calling the respective tracking methods.It's no conceptual problem, really, as that code is completely specific to our content entity datasource and not at all part of the framework itself. But it would still be nice to have as little such code in the module, and as much in the datasource class, as possible.
However, as I think hooks are currently the only way to react to entity CRUD events, there's hardly a (clean) way around it.
With a decoupled tracker, this would probably even be way easier than before. With datasource also responsible for tracking, an index's status would have been tracked across multiple datasources, so getting the index status would have necessitated looping over all of them. This way, we just introduce an additional column, "item_type", and pass the type to the tracker as well when tracking.
Comment #30
freblasty CreditAttribution: freblasty commentedMy original idea behind the seperate tracker was to decouple it from the datasource. However as Thomas mentioned it was only half way done due to lack of time and needed more collaboration/modeling.
Personally I would allow a per index override. That way the Search API becomes a robust and flexible module and would in theory cover all use cases for tracking. Each tracker implementation can be provided as plugin and each index defaulting to the search api tracker.
Comment #31
drunken monkeyOK, we settled on having it as a plugin, configurable (in advanced options) per index.
Comment #32
freblasty CreditAttribution: freblasty commentedAttached patch contains the following:
What needs to happen with the "index immediately" option on an index? I think it should be removed as a more advanced tracker plugin which provides priority to items could replace this option. Even the different modes could be provided as a configuration setting of the more advanced tracker plugin ("disabled", "only new items", "new and updated items").
Comment #33
amateescu CreditAttribution: amateescu commentedPlugin ids don't usually have a prefix that contains the module name and a suffix with the plugin type, so we should rename this one to just 'default'.
I see this pattern a lot in the patch, where a local variable is created even when it's only used once after that. I think the code would be a lot more readable if we inline the call (and drop the unnecessary code comments) :)
Same here, $tracker_plugin_id and $tracker_plugin_manager are only used once, yet they have their own comment and separate line of code.
Should we add a reference here to https://drupal.org/node/2230931 ?
Same here, unneeded comment and local variable initialisation for $datasource :)
It's not clear from the method name
reindex()
that it returns a boolean saying if the items can be reindexed. It would be cleaner if we split the new checks added to reindex() into a new canBeReindexed() method.This method should be moved to the plugin manager, with the simpler name of
getOptions()
. At least that's what core usually does :).. missing tracker plugin ...
$tracker is only used once..
Same here.
Extra spaces.
The doc standard is 'bool' :)
Shouldn't this be
return ($items) ? reset($items) : NULL;
?Also, the code comments here are really not necessary, this load() pattern that calls loadMultiple() is used everywhere.
The standard (core) naming convention for plugin manager's service is plugin.manager.[module_name].[plugin_type]. In our case, it would be
plugin.manager.search_api.tracker
, but maybe we can leave this as a followup issue because all our services have to be renamed.In a manual test of this patch, I get these notices when saving the 'default_node_index' index form:
which means either that we need to update the default config file with the new configuration options and default values or that there's a bug in the form related code from the patch.
Overall, this looks quite good to me. The number of methods from
DatasourceInterface
was really scary, it was doing waaay too many things, so splitting out the tracking functionality is a great thing to do :)Edit: fixed some typos.
Comment #34
Berdir1. Hm. default is a special case but I've discussed with @alexpott before that our not-namespacing of plugins could result in problems. And we do more or less require for various plugin types, like entity_types, menu local tasks/actions and so on. The suffix is certainly unnecessary and in the case of the default and this being in the module where the plugin type is defined, I guess leaving it out is OK too. That said, when implementing plugins for other modules, I would recommend to prefix it with the module.
Comment #35
amateescu CreditAttribution: amateescu commentedOpened #2242127: Rename plugin manager service names for #33.14
Comment #36
drunken monkeySo, are you working on this now, Andrei? If you don't have other important tasks, I think this one has priority right now, and since Frédéric doesn't have time during the day, you can probably feel free to try and fix these issues.
As far as the architectural design goes, the patch looks great to me, more or less exactly what I had imagined. Great job, Frédéric, thanks!
Comment #37
freblasty CreditAttribution: freblasty commentedShould the namespace of the default tracker plugin stay as "search_api_default_tracker" or be changed to "default_tracker"?
Comment #38
Nick_vhPlease write "Note:" or "Note that".
The minimum is for a status during the batch process I assume?
Is this only to make sure that if the limit is higher than the remaining it does not exceed the amount it still needs to index? I assume it is. I was a little confused reading this so probably this could be improved.
If you put @todo's in a separate line it is easier to pick them up in an IDE I think
How do you get the remaining now? We are no longer using the 0 for unindexed items so we still need that tracking pointer to go from there.
Aside of that it is looking good. Are the tests that aspilicious wrote working still?
Comment #39
freblasty CreditAttribution: freblasty commentedAttached patch includes remarks from #33 and #38.
Overview of test results:
Fatal error: Call to a member function getDatasource() on a non-object in /var/www/mydev8/modules/sandbox/search_api/lib/Drupal/search_api/Tests/SearchApiIntegrationTest.php on line 328
FATAL Drupal\search_api\Tests\SearchApiIntegrationTest: test runner returned a non-zero error code (255).
Edit: @Nick_vh the current default tracker implementation uses changed value 0 as mark for indexed items. Currently the tracker uses no optimalization.
Edit: Updated test results.
Comment #40
Nick_vhWell, we don't want to keep that 0 thing. Let's make it conform with everything. Since we have a FIFO now and you are tracking the new items with their real changed value we do need the pointer don't we? Or we have to start writing 0's again for each item we index. I don't like that approach :)
Comment #41
amateescu CreditAttribution: amateescu commentedHere's an interdiff between #32 and #39 for who wants to see exactly what changed.
Also, I'm starting to look at those test fails.
Comment #42
freblasty CreditAttribution: freblasty commented@Nick_vh: See #2242321: Optimize the tracker plugin (or add a new one) for a followup on the default tracker so that this patch can get in and work for other depending issues can start.
Comment #43
amateescu CreditAttribution: amateescu commentedThe thing is.. I don't get those failures locally and I bet travis will pass as well. It must be something related to your local environment (and Thomas' too).
I could only find a few other minor things to fix, and given the green light we got for the architecture, I think you should feel free to commit it :)
Comment #44
amateescu CreditAttribution: amateescu commented@freblasty committed this in http://drupalcode.org/sandbox/daeron/2091893.git/commit/1764eaf6aaa9d787... , we can move forward now :)
Comment #45
amateescu CreditAttribution: amateescu commentedSearchApiIntegrationTest
was marked as broken in #39 but I skipped it when reporting test passes in #43 :( Committed a small followup to fix it: http://drupalcode.org/sandbox/daeron/2091893.git/commitdiff/b3dc093a8168...Comment #46
drunken monkeyGreat work, and thanks for committing! You are right, discussion about details really shouldn't stop this, since it's blocking other issues.
So, continuing the discussion in #2242321: Optimize the tracker plugin (or add a new one).
Comment #51
drunken monkey