Commit 892e9c26 authored by Andrii Shyshkalov's avatar Andrii Shyshkalov Committed by Commit Bot

gerrit_util and my_activity: fix fetching >400 changes from Gerrit.

BUG=698625

Change-Id: I892f09e8b7ff752b6a45c556a594f486000530e8
Reviewed-on: https://chromium-review.googlesource.com/451383Reviewed-by: 's avatarPaweł Hajdan Jr. <phajdan.jr@chromium.org>
Commit-Queue: Andrii Shyshkalov <tandrii@chromium.org>
parent 6c895882
...@@ -386,7 +386,7 @@ def ReadHttpJsonResponse(conn, expect_status=200, ignore_404=True): ...@@ -386,7 +386,7 @@ def ReadHttpJsonResponse(conn, expect_status=200, ignore_404=True):
def QueryChanges(host, param_dict, first_param=None, limit=None, o_params=None, def QueryChanges(host, param_dict, first_param=None, limit=None, o_params=None,
sortkey=None): start=None):
""" """
Queries a gerrit-on-borg server for changes matching query terms. Queries a gerrit-on-borg server for changes matching query terms.
...@@ -395,6 +395,7 @@ def QueryChanges(host, param_dict, first_param=None, limit=None, o_params=None, ...@@ -395,6 +395,7 @@ def QueryChanges(host, param_dict, first_param=None, limit=None, o_params=None,
http://gerrit-documentation.googlecode.com/svn/Documentation/2.6/user-search.html http://gerrit-documentation.googlecode.com/svn/Documentation/2.6/user-search.html
first_param: A change identifier first_param: A change identifier
limit: Maximum number of results to return. limit: Maximum number of results to return.
start: how many changes to skip (starting with the most recent)
o_params: A list of additional output specifiers, as documented here: o_params: A list of additional output specifiers, as documented here:
https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#list-changes https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#list-changes
Returns: Returns:
...@@ -404,8 +405,8 @@ def QueryChanges(host, param_dict, first_param=None, limit=None, o_params=None, ...@@ -404,8 +405,8 @@ def QueryChanges(host, param_dict, first_param=None, limit=None, o_params=None,
if not param_dict and not first_param: if not param_dict and not first_param:
raise RuntimeError('QueryChanges requires search parameters') raise RuntimeError('QueryChanges requires search parameters')
path = 'changes/?q=%s' % _QueryString(param_dict, first_param) path = 'changes/?q=%s' % _QueryString(param_dict, first_param)
if sortkey: if start:
path = '%s&N=%s' % (path, sortkey) path = '%s&start=%s' % (path, start)
if limit: if limit:
path = '%s&n=%d' % (path, limit) path = '%s&n=%d' % (path, limit)
if o_params: if o_params:
...@@ -415,30 +416,49 @@ def QueryChanges(host, param_dict, first_param=None, limit=None, o_params=None, ...@@ -415,30 +416,49 @@ def QueryChanges(host, param_dict, first_param=None, limit=None, o_params=None,
def GenerateAllChanges(host, param_dict, first_param=None, limit=500, def GenerateAllChanges(host, param_dict, first_param=None, limit=500,
o_params=None, sortkey=None): o_params=None, start=None):
""" """
Queries a gerrit-on-borg server for all the changes matching the query terms. Queries a gerrit-on-borg server for all the changes matching the query terms.
WARNING: this is unreliable if a change matching the query is modified while
this function is being called.
A single query to gerrit-on-borg is limited on the number of results by the A single query to gerrit-on-borg is limited on the number of results by the
limit parameter on the request (see QueryChanges) and the server maximum limit parameter on the request (see QueryChanges) and the server maximum
limit. This function uses the "_more_changes" and "_sortkey" attributes on limit.
the returned changes to iterate all of them making multiple queries to the
server, regardless the query limit.
Args: Args:
param_dict, first_param: Refer to QueryChanges(). param_dict, first_param: Refer to QueryChanges().
limit: Maximum number of requested changes per query. limit: Maximum number of requested changes per query.
o_params: Refer to QueryChanges(). o_params: Refer to QueryChanges().
sortkey: The value of the "_sortkey" attribute where starts from. None to start: Refer to QueryChanges().
start from the first change.
Returns: Returns:
A generator object to the list of returned changes, possibly unbound. A generator object to the list of returned changes.
""" """
already_returned = set()
def at_most_once(cls):
for cl in cls:
if cl['_number'] not in already_returned:
already_returned.add(cl['_number'])
yield cl
start = start or 0
cur_start = start
more_changes = True more_changes = True
while more_changes: while more_changes:
page = QueryChanges(host, param_dict, first_param, limit, o_params, sortkey) # This will fetch changes[start..start+limit] sorted by most recently
for cl in page: # updated. Since the rank of any change in this list can be changed any time
# (say user posting comment), subsequent calls may overalp like this:
# > initial order ABCDEFGH
# query[0..3] => ABC
# > E get's updated. New order: EABCDFGH
# query[3..6] => CDF # C is a dup
# query[6..9] => GH # E is missed.
page = QueryChanges(host, param_dict, first_param, limit, o_params,
cur_start)
for cl in at_most_once(page):
yield cl yield cl
more_changes = [cl for cl in page if '_more_changes' in cl] more_changes = [cl for cl in page if '_more_changes' in cl]
...@@ -448,11 +468,18 @@ def GenerateAllChanges(host, param_dict, first_param=None, limit=500, ...@@ -448,11 +468,18 @@ def GenerateAllChanges(host, param_dict, first_param=None, limit=500,
'Received %d changes with a _more_changes attribute set but should ' 'Received %d changes with a _more_changes attribute set but should '
'receive at most one.' % len(more_changes)) 'receive at most one.' % len(more_changes))
if more_changes: if more_changes:
sortkey = more_changes[0]['_sortkey'] cur_start += len(page)
# If we paged through, query again the first page which in most circumstances
# will fetch all changes that were modified while this function was run.
if start != cur_start:
page = QueryChanges(host, param_dict, first_param, limit, o_params, start)
for cl in at_most_once(page):
yield cl
def MultiQueryChanges(host, param_dict, change_list, limit=None, o_params=None, def MultiQueryChanges(host, param_dict, change_list, limit=None, o_params=None,
sortkey=None): start=None):
"""Initiate a query composed of multiple sets of query parameters.""" """Initiate a query composed of multiple sets of query parameters."""
if not change_list: if not change_list:
raise RuntimeError( raise RuntimeError(
...@@ -462,8 +489,8 @@ def MultiQueryChanges(host, param_dict, change_list, limit=None, o_params=None, ...@@ -462,8 +489,8 @@ def MultiQueryChanges(host, param_dict, change_list, limit=None, o_params=None,
q.append(_QueryString(param_dict)) q.append(_QueryString(param_dict))
if limit: if limit:
q.append('n=%d' % limit) q.append('n=%d' % limit)
if sortkey: if start:
q.append('N=%s' % sortkey) q.append('S=%s' % start)
if o_params: if o_params:
q.extend(['o=%s' % p for p in o_params]) q.extend(['o=%s' % p for p in o_params])
path = 'changes/?%s' % '&'.join(q) path = 'changes/?%s' % '&'.join(q)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment