| Index: appengine/swarming/server/task_queues.py
|
| diff --git a/appengine/swarming/server/task_queues.py b/appengine/swarming/server/task_queues.py
|
| index 2e5f4068ca1971e8e56a888736ca33086e1cc4ad..dff2d9ee023f6ac50961a43a8e9a2c05c9e31a4c 100644
|
| --- a/appengine/swarming/server/task_queues.py
|
| +++ b/appengine/swarming/server/task_queues.py
|
| @@ -238,8 +238,8 @@ class TaskDimensions(ndb.Model):
|
|
|
| def match_request(self, dimensions):
|
| """Confirms that this instance actually stores this set."""
|
| - return self._match_request_flat(
|
| - u'%s:%s' % (k, v) for k, v in dimensions.iteritems())
|
| + assert isinstance(dimensions, list), repr(dimensions)
|
| + return self._match_request_flat(u'%s:%s' % (k, v) for k, v in dimensions)
|
|
|
| def match_bot(self, bot_dimensions):
|
| """Returns the TaskDimensionsSet that matches this bot_dimensions, if any.
|
| @@ -405,13 +405,28 @@ def _rebuild_bot_cache(bot_dimensions, bot_root_key):
|
|
|
|
|
| def _get_task_dims_key(dimensions_hash, dimensions):
|
| - if u'id' in dimensions:
|
| - return ndb.Key(
|
| - TaskDimensionsRoot, u'id:%s' % dimensions['id'],
|
| - TaskDimensions, dimensions_hash)
|
| - return ndb.Key(
|
| - TaskDimensionsRoot, u'pool:%s' % dimensions['pool'],
|
| - TaskDimensions, dimensions_hash)
|
| + """Return the ndb.Key() for the request dimensions.
|
| +
|
| + The dimensions may have at most one 'id' key but may have multiple 'pool'. In
|
| + the case of 'pool', return the first value in sorted order. This means that
|
| + for a request with both 'pool:bar' and 'pool:foo', the request is rooted at
|
| + 'pool:bar'.
|
| +
|
| + Arguments:
|
| + - dimensions: sorted list(tuple(unicode, unicode))
|
| + """
|
| + assert isinstance(dimensions, list), repr(dimensions)
|
| + # Search for ID.
|
| + for key, value in dimensions:
|
| + if key == u'id':
|
| + return ndb.Key(
|
| + TaskDimensionsRoot, u'id:%s' % value, TaskDimensions, dimensions_hash)
|
| + # 'pool' comes after 'id'.
|
| + if key == u'pool':
|
| + return ndb.Key(
|
| + TaskDimensionsRoot, u'pool:%s' % value,
|
| + TaskDimensions, dimensions_hash)
|
| + raise datastore_errors.BadValueError(u'invalid request')
|
|
|
|
|
| def _hash_data(data):
|
| @@ -507,14 +522,15 @@ def hash_dimensions(dimensions):
|
| """Returns a 32 bits int that is a hash of the request dimensions specified.
|
|
|
| Arguments:
|
| - dimensions: dict(str, str)
|
| + dimensions: sorted list(tuple(unicode, unicode))
|
|
|
| The return value is guaranteed to be non-zero so it can be used as a key id in
|
| a ndb.Key.
|
| """
|
| + assert isinstance(dimensions, list), repr(dimensions)
|
| # This horrible code is the product of micro benchmarks.
|
| data = ''
|
| - for k, v in sorted(dimensions.items()):
|
| + for k, v in dimensions:
|
| data += k.encode('utf8')
|
| data += '\000'
|
| data += v.encode('utf8')
|
| @@ -613,7 +629,7 @@ def assert_task(request):
|
| # If this task specifies an 'id' value, updates the cache inline since we know
|
| # there's only one bot that can run it, so it won't take long. This permits
|
| # tasks like 'terminate' tasks to execute faster.
|
| - if request.properties.dimensions.get(u'id'):
|
| + if any(k == u'id' for k, _ in request.properties.dimensions):
|
| rebuild_task_cache(payload)
|
| return
|
|
|
| @@ -688,7 +704,7 @@ def rebuild_task_cache(payload):
|
| dimensions = data[u'dimensions']
|
| dimensions_hash = int(data[u'dimensions_hash'])
|
| valid_until_ts = utils.parse_datetime(data[u'valid_until_ts'])
|
| - dimensions_flat = sorted(u'%s:%s' % (k, v) for k, v in dimensions.iteritems())
|
| + dimensions_flat = sorted(u'%s:%s' % (k, v) for k, v in dimensions)
|
|
|
| now = utils.utcnow()
|
| updated = 0
|
|
|