Skip to content

2i pagination support#540

Merged
slfritchie merged 23 commits intomasterfrom
pt29-2i-pagination
May 25, 2013
Merged

2i pagination support#540
slfritchie merged 23 commits intomasterfrom
pt29-2i-pagination

Conversation

@russelldb
Copy link
Contributor

Adds a max_results option to PB and HTTP 2i endpoints for limiting
the number of results returned for a 2i query. Will return an opaque
continuation if there are max_results results returned. A
subsequent query that includes the continuation as a parameter will
return the next max_results results.

The results will be returned sorted ascending. In the case of $key
and $bucket queries, and equals queries, the results will be sorted by
primary key In the case of range queries the results will be sorted by
index value, then primary key. Since the values must be returned from the backend for
sorting, there is a further option return_terms that will cause the
indexed terms to be returned to the user also, where relevant (range
queries on index fields.)

Sorting is performed by the module sms.erl originally written by Reid
Draper. It is used as a buffer by riak_kv_index_fsm.

A new vnode function stop_fold has been added so the fsm can tell
the backend to break out of the fold when enough results have been
gathered.

The option to stream 2i results has also been added. Adding the
stream option to either HTTP or PB queries will cause the API
endpoints to return chunks of results as they are received from the
fsm This can lead to much lower perceived latencies as the application
recieves the first results of a large set much sooner.

An example HTTP query:

curl "http://127.0.0.1:10018/buckets/2ibucket/index/field2_int/1/999?return_terms=true&max_results=100"

Will return the first 100 {value, key} pairs for the field2_int
index, sorted by value, then primary key. If there are 100 results returned, it will
also return an opaque continuation that may be sumbitted to continue
the query:

curl "http://127.0.0.1:10018/buckets/2ibucket/index/field2_int/1/999?return_terms=true&continuation=some_gibberish_looking_value&max_results=100"

A further continuation maybe returned with the results.


Riak tests can be found at https://github.com/basho/riak_test/tree/pt29-2i-pagination
Riak-Erlang-Client support at https://github.com/basho/riak-erlang-client/tree/pt29-2i-pagination

NOTE: requires
https://github.com/basho/riak_core/tree/pt29-2i-pagination
https://github.com/basho/riak_pb/tree/pt29-2i-pagination

russelldb added 9 commits May 1, 2013 17:41
When a 2i query's results are a list of keys. This change adds
an optional request parameter to pb and http that, when set, will
cause pairs of `key:index_value` to be returned as the result of
a 2i query.

Has no effect on MapReduce. Uses capabilities for rolling upgrade
situations.
Adds a `max_results` option to PB and HTTP 2i endpoints for limiting
the number of results returned for a 2i query.  Will return an opaque
`continuation` if there are at least `max_results` results returned. A
subsequent query that includes the `continuation` as a parameter will
return the next `max_results` results.

The results will be returned sorted ascending. In the case of $key
and $bucket queries, and equals queries, the results will be sorted by
primary key In the case of range queries the results will be sorted by
index value. Since the values must be returned from the backend for
sorting, there is a further option `return_terms` that will cause the
indexed terms to be returned to the user also, where relevant (range
queries on index fields.)

Sorting is performed by the module `sms.erl` originally written by Reid
Draper. It is used as a buffer by `riak_kv_index_fsm`.

A new vnode function `stop_fold` has been added so the fsm can tell
the backend to break out of the fold when enough results have been
gathered.

The option to stream 2i results has also been added. Adding the
`stream` option to either HTTP or PB queries will cause the API
endpoints to return chunks of results as they are received from the
fsm This can lead to much lower perceived latencies as the application
recieves the first results of a large set much sooner.

An example HTTP query: `curl
"http://127.0.0.1:10018/buckets/2ibucket/index/field2_int/1/100?return_terms=true"`
Will return the first 100 {value, key} pairs for the `field2_int`
index, sorted by value.  If there are 100 results returned, it will
also return an opaque `continuation` that may be sumbitted to continue
the query: `curl
"http://127.0.0.1:10018/buckets/2ibucket/index/field2_int/1/100?return_terms=true&continuation=some_gibberish_looking_value"`
A further `continuation` maybe returned with the results.
Spotted lager info message left over from dev, excised it.
I have no idea how. Tests passed, then they didn't. I guess a
rebase went wrong.
russelldb added 4 commits May 7, 2013 18:10
The wrong paramter was used to match in the function head for
to_first _key
Even though not required, backend feature parity makes sense,
more so if the features are to be added to `backend_eqc`.

Refactor `index | object _key_in_range` funs for common use.
Removed stray debugging `lager:info/1` message.
@ghost ghost assigned evanmcc and engelsanchez May 16, 2013
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't forget to stick a licence header or the lawyers will get you!

* Allow add_results to be called repeatedly with done, it's harmless.
* Allow sms to be called when all sources are done without crashing.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We now have a bit of a misnomer here. Consider changing to something like handle_coverage_fold instead now that we do both key only and key/value folds?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure. I get your meaning…but…hmmm…I think really a deeper refactor is needed. The query should dictate the fold type, probably the backend should decide. I see this as a hack / halfway house. Changing the name might even add to the confusion? I totally agree with you, but think there is some more discussion to be had.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is correct, but consider doing Count >= MaxResults for total future proofing paranoia. Same with riak_kv_pb_index actually.

@engelsanchez
Copy link
Contributor

There is one funny side effect of the way max results and continuations work: Currently we make no attempt to deduplicate results from a 2i query. So, if item 1 is indexed on field f1 for values 1,2,3, and you do a range query from 1 to 3, you will get the same item 3 times. Now, if you specify max results as 2, you get the value twice, plus a continuation. If you query the continuation, you get nothing. Since we are now sorting the results, deduplicating becomes very cheap. I'm wondering if we should start doing it. On the other hand, if users rely on this property and use the number of duplicates for something, continuations might break that. Thoughts?

@engelsanchez
Copy link
Contributor

More on the duplicates being dropped behaviour: if return_terms is true the duplicates that we are dropping may contain information useful to the client. Or at least it's easier to imagine they might care about that, since some terms will sometimes be retrieved, sometimes lost.

@russelldb
Copy link
Contributor Author

I don't understand. Can you provide a test or some minimal set of data and a query that reproduce the condition, please?

@engelsanchez
Copy link
Contributor

https://gist.github.com/engelsanchez/5630275
While I was playing with this I noticed the above: a single entry indexed by 3 values (1,2,3). A range query from 1-3 will return 3 items. With return_terms, each item is tagged with one of (1,2,3). With max results = 2, you get two of those, but of course the continuation won't get that key again, so you don't get the third one.

I'm not saying this is bad or unintended. Just that it might interfere with what the users of return_terms might want to do with that. Maybe ask them directly if it matters?

@russelldb
Copy link
Contributor Author

It is bad and unintended. I really hadn't noticed it or thought of it. I'm not sure what the answer it, except to expose the start_key_incl option to the end user, even then it is a bit of a guess on their part.

@russelldb
Copy link
Contributor Author

OK. I think this is a straight up bug from considering the key and the index value in isolation. Let me work on it this morning, I have some ideas.

A bug found by @engelsanchez, where `start_incl` being false
(when using a continuation, for example) led to in range values
being excluded as the key was checked in isolation.
@russelldb
Copy link
Contributor Author

Pooshed a fix for the dropped-duplicate-key-after-continuation bug, (good catch BTW! Glad we didn't ship that.) Pooshed test for same to riak_test.

Would you say now is a good time to address your other comments (i.e. are you through with the majority of the review?)

@engelsanchez
Copy link
Contributor

I've verified the fix works on my example. Good work man! Simple, small and effective fix. Will keep banging on it to see what else I can find.

russelldb added 2 commits May 24, 2013 13:53
Clean-up and slightly optimise the index fsm to not recalculate
the same list length more than once. Shortcut sending the `stop_fold`
message where possible (i.e. when we notice the have enough results).

Add JSON error to 2i HTTP strem
Update all copyright notices
Normalise boolean paramters to strict true | false
@engelsanchez
Copy link
Contributor

A couple more things: remember to look at my branch adding an EQC test for sms, which also contains a couple of proposed edge case fixes: https://github.com/basho/riak_kv/commits/eas-add-sms-eqc

And you will need to merge from master before the PR gets its green button again (there is a little conflict in riak_kv_app where capabilities are added). Please do that before the final 👍 so that I can go over the final version of the code.

@engelsanchez
Copy link
Contributor

I'm having trouble using max_results and return_terms together through the HTTP interface

curl http://localhost:10018/buckets/bucket/index/i1_int/1/4?return_terms=true&max_results=2
{"results":[{"1":"1"},{"2":"1"},{"2":"2"},{"3":"1"},{"3":"2"},{"4":"2"}]}
curl http://localhost:10018/buckets/bucket/index/i1_int/1/4?max_results=2&return_terms=true
{"keys":["1","1"],"continuation":"g2gCYQJtAAAAATE="}

Also, validation doesn't seem to kick in for the second parameter. Maybe I'm doing something stupid, but I haven't figured it out. I'll debug this more right after lunch.

@russelldb
Copy link
Contributor Author

This is why I hate changes on review. I tested this stuff in the past. Ok. I'll add some more tests to riak_test and see what is up. Thanks.

@russelldb
Copy link
Contributor Author

Huh. Works for me. I enquoted my url+query params thussly:

curl "http://localhost:8098/buckets/mybucket/index/field1_int/1/4?max_results=2&return_terms=true"
{"results":[{"1":"1"},{"2":"2"}],"continuation":"g2gCYQJtAAAAATI="}

and

curl "http://localhost:8098/buckets/mybucket/index/field1_int/1/4?return_terms=true&max_results=2"
{"results":[{"1":"1"},{"2":"2"}],"continuation":"g2gCYQJtAAAAATI="}

and

curl "http://localhost:8098/buckets/mybucket/index/field1_int/1/4?max_results=2&return_terms=bum"
Invalid "return_terms". "bum" is not a boolean

and

curl "http://localhost:8098/buckets/mybucket/index/field1_int/1/4?return_terms=false&max_results=hippo"
Invalid "max_results". "hippo" is not a positive integer

Are all your branches up-to-date? I'll push the latest, which merges master and your EQC test (thanks for that, great work!)

/me shrugs

@engelsanchez
Copy link
Contributor

Yep. I tried on the browser and it works fine too. Nothing to see here, move along, nevermind... Sorry

@russelldb
Copy link
Contributor Author

Heh, better safe, no worries.

@engelsanchez
Copy link
Contributor

Ok, I think we can call it a day. I believe all comments were addressed. New testing has not uncovered any new problems. Ship it!
👍 💃 ⛵

russelldb and others added 3 commits May 24, 2013 22:29
Conflicts:
	src/riak_kv_app.erl
	src/riak_kv_wm_raw.hrl
…place

Failing query test case:

    V1 = backend_eqc:init_backend(riak_kv_eleveldb_backend,false, [{data_root,"test/eleveldb-backend"}]).
    riak_kv_eleveldb_backend:put(<<"b2">>,<<"k1">>, [{add,<<"k_int">>,0}], <<110>>, V1).
    riak_kv_eleveldb_backend:fold_keys(fun(B, K, Acc) -> [{B, K}|Acc] end, [],

Return value:

    [{index,<<"b2">>,{range,<<"k_int">>,0,0}}], V1).

@rdb Please check to see how much effort vs. value might be required
to address the TODO comment.  If it's too much work now, then please
create an issue for fixing & removing that TODO item so that the matter
doesn't get lost.

After this commit, tested via:

    P = backend_eqc : property ( riak_kv_eleveldb_backend , false , [ { data_root , "test/eleveldb-backend" } ] ).
    eqc:quickcheck(eqc:testing_time(4*300, P)).
    [...]
    OK, passed 9670 tests
@slfritchie
Copy link
Contributor

From the commit log:

@rdb Please check to see how much effort vs. value might be required
to address the TODO comment. If it's too much work now, then please
create an issue for fixing & removing that TODO item so that the matter
doesn't get lost.

@slfritchie
Copy link
Contributor

Oh, and the bug fixed by commit 6934d65 is something that only pops up every few minutes, in my experience with the backend_eqc test. The namespace (bucket + key + index) is likely too big to hit a case where a bucket + key + index is written and then sometime later a fold_keys is attempted with a query on the exact same index.

So, whenever a developer plays with areas that would affect the code that this test exercises, it would be extremely wise to run the backend_eqc test for a much longer period of time than the usual "make test" does. See my comment on that commit for an example.

@slfritchie slfritchie merged commit 6934d65 into master May 25, 2013
@russelldb
Copy link
Contributor Author

Awesome @slfritchie. Thanks for finding and fixing. I think I'll fix that test properly during this testing cycle (add all the new query types, make it use V2 queries etc.) Thanks again. It was on the list to update the test, but time is cruel.

@engelsanchez engelsanchez deleted the pt29-2i-pagination branch May 28, 2013 12:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants