-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add concurrency to addIndexes(CodecReader…) API [LUCENE-10216] #11253
Comments
Adrien Grand (@jpountz) (migrated from JIRA) One bit from this proposal I'm not fully comfortable with is the fact that Lucene would merge each reader independently when the flag is set. While I understand why this makes sense for Amazon product search since you want to have many segments to make search as concurrent as possible, this is also a bit wasteful since you'd be paying the cost of doing a merge but without getting the benefits of reducing the segment count. What about a slightly different proposal where merges would always be executed via the configured merge scheduler, and addIndexes(CodecReader[]) would get an additional Then you could still do what you want by using |
Vigya Sharma (@vigyasharma) (migrated from JIRA) Thanks for going through the proposal, @jpountz.
I understand your concern, I am also a bit iffy about losing the opportunity to merge segments.. Ideally, I would like to land on a sweet middle ground - not run single segment merges, but also not add all provided segments into a single merge. Leverage some concurrency within addIndexes, and let background merges bring the segment count down further. How do you feel about making this configurable in the API with a param that defines the number of segments merged together (
The doWait flag would make the API non-blocking. But it would add additional steps for users to track when the merges triggered by addIndexes have completed. For segment replication, addIndexes is not useable until its merges complete. Merge within addIndexes(), is what creates segments with recomputed ordinal values. Until that is done, there are no segments available to copy to searcher (and replica) hosts. We also want to bring the segment count down to as low as possible at Amazon product search. The tradeoff we are making here, is to first go with a higher segment count to make documents quickly available for search, then run background merges and bring down the segment count. This is similar to the other variant of add indexes - the I feel making segments available quickly would be useful for anyone who has multiple replica search hosts and uses segment replication.
|
Adrien Grand (@jpountz) (migrated from JIRA)
I worry that it would make the API too complicated. Thinking about it more: Ideally addIndexes would do the right thing based on the configured MergePolicy (to know how to group CodecReaders together) and MergeScheduler (to know how many threads it may use) so that we could hopefully keep the same API as today and experts users like Amazon product search could use a MergePolicy that favors many segments for search concurrency, plus possibly a ConcurrentMergeScheduler that is allowed to use many threads, and you would get the desired behavior? This might be a high-hanging fruit though. |
Vigya Sharma (@vigyasharma) (migrated from JIRA) I like the idea of handling everything via MergePolicy and MergeScheduler, without changing the API. One wrinkle is that the existing merge framework (policy and scheduler) is written to work with segments, while the API only has readers. I have a high level plan for handling this based on early code reading. Let me know your thoughts and if you have any ideas to simplify/generalize this. If it makes sense, I can follow it up with a draft PR... – We could create a new API in MergePolicy that does findMerges using readers - While Once we have a mergeSpec from policy, we can either use the existing We want to wait for all merges triggered by addIndexes to complete before we return, which we could do by calling I was also thinking that we could add a new |
Michael McCandless (@mikemccand) (migrated from JIRA) I like this plan (extending Reference counting might get tricky, if To improve testing we could create a new |
Vigya Sharma (@vigyasharma) (migrated from JIRA) I took a shot at the MergePolicy + MergeScheduler idea. Have raised a draft, work-in-progress PR - #633, to get some high level feedback on the approach. It has existing tests passing, and is pending new tests specific to this change. |
Vigya Sharma (@vigyasharma) (migrated from JIRA) Had some thoughts and questions about the transaction model for this API... Currently, it either succeeds and adds all provided readers, or fails and adds none of them. With a merge policy splitting provided readers into groups of smaller One approach could be to expose this failure information to user - the exception can contain the list of readers merged and pending. This could simplify the overall implementation. My current thoughts, however, are that the present transaction logic is important. It is hard for users to parse the exception message, figure out which readers are pending and retry them. As opposed to retrying an entire API call (with all the readers), which their upstream system probably understands as a single unit. However, I wanted to check if loosening the transaction model for this API is a palatable approach. To retain the all or none, single transaction model, I am thinking that we can join on all merges at the end of the Would like to hear more thoughts or suggestions on this. |
Vigya Sharma (@vigyasharma) (migrated from JIRA) Updated the PR to retain transactionality, while using the MergePolicy and configured MergeScheduler to trigger merges for this API |
Vigya Sharma (@vigyasharma) (migrated from JIRA) While making this change, I found an existing TODO, which I think we're able to address now.. // TODO: somehow we should fix this merge so it's
// abortable so that IW.close(false) is able to stop it Earlier, Now, merges are defined via |
Vigya Sharma (@vigyasharma) (migrated from JIRA) I think the PR is ready for review, with existing tests passing and added tests for new changes.
|
Robert Muir (@rmuir) (migrated from JIRA)
Is this going to make 50% of our test runs non-reproducible? Please, let's consider the need to reproduce failing runs and not pick such highly concurrent stuff for the unit tests. We should be able to test it another way. |
ASF subversion and git services (migrated from JIRA) Commit 698f40a in lucene's branch refs/heads/main from Vigya Sharma LUCENE-10216: Use MergeScheduler and MergePolicy to run addIndexes(CodecReader[]) merges. (#633)
Aborted pending merges were slipping through the merge exception check in
|
Adrien Grand (@jpountz) (migrated from JIRA) Can this issue be resolved? |
Michael McCandless (@mikemccand) (migrated from JIRA) I think we can backport to 9.x? But we should not rush it for 9.3. It has baked for quite a while in |
Vigya Sharma (@vigyasharma) (migrated from JIRA) Raised a PR for backporting changes to 9x - #1051 Commits included are the main PR (#633) and a follow on related bug fix (#1012) |
ASF subversion and git services (migrated from JIRA) Commit 5dd8e9b in lucene's branch refs/heads/branch_9x from Vigya Sharma LUCENE-10216: Use MergeScheduler and MergePolicy to run addIndexes(CodecReader[]) merges. (#1051) Use merge policy and merge scheduler to run addIndexes merges. This is a back port of the following commits from main:
|
Michael McCandless (@mikemccand) (migrated from JIRA) Awesome! I think we can close this now @vigyasharma? |
I work at Amazon Product Search, and we use Lucene to power search for the e-commerce platform. I’m working on a project that involves applying metadata+ETL transforms and indexing documents on n different indexing boxes, combining them into a single index on a separate reducer box, and making it available for queries on m different search boxes (replicas). Segments are asynchronously copied from indexers to reducers to searchers as they become available for the next layer to consume.
I am using the addIndexes API to combine multiple indexes into one on the reducer boxes. Since we also have taxonomy data, we need to remap facet field ordinals, which means I need to use the
addIndexes(CodecReader…)
version of this API. The API leveragesSegmentMerger.merge()
to create segments with new ordinal values while also merging all provided segments in the process.This is however a blocking call that runs in a single thread. Until we have written segments with new ordinal values, we cannot copy them to searcher boxes, which increases the time to make documents available for search.
I was playing around with the API by creating multiple concurrent merges, each with only a single reader, creating a concurrently running 1:1 conversion from old segments to new ones (with new ordinal values). We follow this up with non-blocking background merges. This lets us copy the segments to searchers and replicas as soon as they are available, and later replace them with merged segments as background jobs complete. On the Amazon dataset I profiled, this gave us around 2.5 to 3x improvement in addIndexes() time. Each call was given about 5 readers to add on average.
This might be useful add to Lucene. We could create another
addIndexes()
API with aboolean
flag for concurrency, that internally submits multiple merge jobs (each with a single reader) to theConcurrentMergeScheduler
, and waits for them to complete before returning.While this is doable from outside Lucene by using your thread pool, starting multiple addIndexes() calls and waiting for them to complete, I felt it needs some understanding of what addIndexes does, why you need to wait on the merge and why it makes sense to pass a single reader in the addIndexes API.
Out of box support in Lucene could simplify this for folks a similar use case.
Migrated from LUCENE-10216 by Vigya Sharma (@vigyasharma), resolved Aug 01 2022
Sub-tasks:
Pull requests: #633
The text was updated successfully, but these errors were encountered: