| OLD | NEW |
| 1 # coding=utf-8 | 1 # coding=utf-8 |
| 2 # Copyright 2014 The LUCI Authors. All rights reserved. | 2 # Copyright 2014 The LUCI Authors. All rights reserved. |
| 3 # Use of this source code is governed under the Apache License, Version 2.0 | 3 # Use of this source code is governed under the Apache License, Version 2.0 |
| 4 # that can be found in the LICENSE file. | 4 # that can be found in the LICENSE file. |
| 5 | 5 |
| 6 """Task entity that describe when a task is to be scheduled. | 6 """Task entity that describe when a task is to be scheduled. |
| 7 | 7 |
| 8 This module doesn't do the scheduling itself. It only describes the tasks ready | 8 This module doesn't do the scheduling itself. It only describes the tasks ready |
| 9 to be scheduled. | 9 to be scheduled. |
| 10 | 10 |
| (...skipping 409 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 420 request_key = task_to_run_key_to_request_key(task_key) | 420 request_key = task_to_run_key_to_request_key(task_key) |
| 421 key_id = task_key.integer_id() | 421 key_id = task_key.integer_id() |
| 422 if not key_id or key_id >= 2**32: | 422 if not key_id or key_id >= 2**32: |
| 423 raise ValueError( | 423 raise ValueError( |
| 424 'TaskToRun key id should be between 1 and 2**32, found %s' % | 424 'TaskToRun key id should be between 1 and 2**32, found %s' % |
| 425 task_key.id()) | 425 task_key.id()) |
| 426 task_request.validate_request_key(request_key) | 426 task_request.validate_request_key(request_key) |
| 427 | 427 |
| 428 | 428 |
| 429 def match_dimensions(request_dimensions, bot_dimensions): | 429 def match_dimensions(request_dimensions, bot_dimensions): |
| 430 """Returns True if the bot dimensions satisfies the request dimensions.""" | 430 """Returns True if the bot dimensions satisfies the request dimensions. |
| 431 assert isinstance(request_dimensions, dict), request_dimensions | 431 |
| 432 Arguments: |
| 433 request_dimensions: list(tuple(unicode, unicode)). |
| 434 bot_dimensions: dict(unicode: list(unicode)). |
| 435 """ |
| 436 assert isinstance(request_dimensions, list), request_dimensions |
| 432 assert isinstance(bot_dimensions, dict), bot_dimensions | 437 assert isinstance(bot_dimensions, dict), bot_dimensions |
| 433 if frozenset(request_dimensions).difference(bot_dimensions): | 438 for key, required in request_dimensions: |
| 434 return False | 439 bot_value = bot_dimensions.get(key) |
| 435 for key, required in request_dimensions.iteritems(): | 440 if not bot_value or required not in bot_value: |
| 436 bot_value = bot_dimensions[key] | |
| 437 if isinstance(bot_value, (list, tuple)): | |
| 438 if required not in bot_value: | |
| 439 return False | |
| 440 elif required != bot_value: | |
| 441 return False | 441 return False |
| 442 return True | 442 return True |
| 443 | 443 |
| 444 | 444 |
| 445 def set_lookup_cache(task_key, is_available_to_schedule): | 445 def set_lookup_cache(task_key, is_available_to_schedule): |
| 446 """Updates the quick lookup cache to mark an item as available or not. | 446 """Updates the quick lookup cache to mark an item as available or not. |
| 447 | 447 |
| 448 This cache is a blacklist of items that are already reaped, so it is not worth | 448 This cache is a blacklist of items that are already reaped, so it is not worth |
| 449 trying to reap it with a DB transaction. This saves on DB contention when a | 449 trying to reap it with a DB transaction. This saves on DB contention when a |
| 450 high number (>1000) of concurrent bots with similar dimension are reaping | 450 high number (>1000) of concurrent bots with similar dimension are reaping |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 515 """Yields all the expired TaskToRun still marked as available.""" | 515 """Yields all the expired TaskToRun still marked as available.""" |
| 516 # The reason it is done this way as an iteration over all the pending entities | 516 # The reason it is done this way as an iteration over all the pending entities |
| 517 # instead of using a composite index with 'queue_number' and 'expiration_ts' | 517 # instead of using a composite index with 'queue_number' and 'expiration_ts' |
| 518 # is that TaskToRun entities are very hot and it is important to not require | 518 # is that TaskToRun entities are very hot and it is important to not require |
| 519 # composite indexes on it. It is expected that the number of pending task is | 519 # composite indexes on it. It is expected that the number of pending task is |
| 520 # 'relatively low', in the orders of 100,000 entities. | 520 # 'relatively low', in the orders of 100,000 entities. |
| 521 now = utils.utcnow() | 521 now = utils.utcnow() |
| 522 for task in TaskToRun.query(TaskToRun.queue_number > 0): | 522 for task in TaskToRun.query(TaskToRun.queue_number > 0): |
| 523 if task.expiration_ts < now: | 523 if task.expiration_ts < now: |
| 524 yield task | 524 yield task |
| OLD | NEW |