mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-10-25 10:24:38 +08:00
Fix the following error:
```
____ TestCeleryUserRateLimitIntegrationNoLimit.test_mock_pass_user_id_task _____
self = <integration.test_celery_user_rate_limit.TestCeleryUserRateLimitIntegrationNoLimit object at 0x7f978fc0e400>
def test_mock_pass_user_id_task(self):
> self._test_mock_pass_user_id_task(2, 3, self._user_rate_limit)
test/integration/test_celery_user_rate_limit.py:161:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <integration.test_celery_user_rate_limit.TestCeleryUserRateLimitIntegrationNoLimit object at 0x7f978fc0e400>
num_users = 2, num_calls = 3, tasks_per_user_per_sec = 0.0
for user_results in results.values():
last_task_end_time = start_time
for result in user_results:
> if result.date_done > last_task_end_time:
E TypeError: can't compare offset-naive and offset-aware datetimes
test/integration/test_celery_user_rate_limit.py:110: TypeError
```
Also https://docs.python.org/3/library/datetime.html#datetime.datetime.utcnow
is deprecated since Python 3.12 .
162 lines
6.3 KiB
Python
162 lines
6.3 KiB
Python
import datetime
|
|
import tempfile
|
|
from functools import lru_cache
|
|
from typing import (
|
|
Dict,
|
|
List,
|
|
)
|
|
|
|
from celery.result import AsyncResult
|
|
from sqlalchemy import text
|
|
|
|
from galaxy.celery import galaxy_task
|
|
from galaxy.model.database_utils import sqlalchemy_engine
|
|
from galaxy.util import ExecutionTimer
|
|
from galaxy_test.driver.driver_util import init_database
|
|
from galaxy_test.driver.integration_util import (
|
|
IntegrationTestCase,
|
|
skip_unless_postgres,
|
|
)
|
|
|
|
|
|
@galaxy_task
|
|
def mock_user_id_task(task_user_id: int):
|
|
return task_user_id
|
|
|
|
|
|
@lru_cache
|
|
def sqlite_url():
|
|
path = tempfile.NamedTemporaryFile().name
|
|
dburl = f"sqlite:///{path}"
|
|
init_database(dburl)
|
|
return dburl
|
|
|
|
|
|
@lru_cache
|
|
def setup_users(dburl: str, num_users: int = 2):
|
|
"""
|
|
Setup test users in galaxy_user table with user id's starting from 2.
|
|
This is because the new celery_user_rate_limit table has
|
|
a user_id with a foreign key pointing to galaxy_user table.
|
|
"""
|
|
expected_user_ids = list(range(2, num_users + 2))
|
|
with sqlalchemy_engine(dburl) as engine:
|
|
with engine.begin() as conn:
|
|
found_user_ids = conn.scalars(
|
|
text("select id from galaxy_user where id between 2 and :high"), {"high": num_users + 1}
|
|
).all()
|
|
if len(expected_user_ids) > len(found_user_ids):
|
|
user_ids_to_add = set(expected_user_ids).difference(found_user_ids)
|
|
for user_id in user_ids_to_add:
|
|
conn.execute(
|
|
text("insert into galaxy_user(id, active, email, password) values (:id, :active, :email, :pw)"),
|
|
[{"id": user_id, "active": True, "email": "e", "pw": "p"}],
|
|
)
|
|
|
|
|
|
class TestCeleryUserRateLimitIntegration(IntegrationTestCase):
|
|
"""
|
|
Test feature that limits the number of celery task executions
|
|
per user per second. This is implemented by the celery_user_rate_limit
|
|
config parameter whose default value is 0.0 meaning no rate limit.
|
|
For each of a set of users it submits runs a task num_calls times.
|
|
For the test to succeed the total duration of completion of all task
|
|
executions should be equal to the tasks_per_user_per_sec times
|
|
num_calls-1. This is because tasks for different users should be
|
|
executed in parallel. In addition, we verify that the total
|
|
duration for execution of tasks for each user equals the total
|
|
task duration, again because tasks for different users should
|
|
run in parallel.
|
|
"""
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
|
|
def _test_mock_pass_user_id_task(self, num_users: int, num_calls: int, tasks_per_user_per_sec: float):
|
|
users = list(range(2, num_users + 2))
|
|
expected_duration: float
|
|
if tasks_per_user_per_sec == 0.0:
|
|
expected_duration = 0.0
|
|
expected_duration_lbound = 0.0
|
|
else:
|
|
secs_between_tasks_per_user = 1 / tasks_per_user_per_sec
|
|
expected_duration = secs_between_tasks_per_user * (num_calls - 1)
|
|
expected_duration_lbound = expected_duration - 4
|
|
expected_duration_hbound = expected_duration + 4
|
|
start_time = datetime.datetime.now(datetime.timezone.utc)
|
|
timer = ExecutionTimer()
|
|
# Invoke test task num_calls times for each user
|
|
results: Dict[int, List[AsyncResult]] = {}
|
|
for user in users:
|
|
user_results: List[AsyncResult] = []
|
|
for _ in range(num_calls):
|
|
user_results.append(mock_user_id_task.delay(task_user_id=user))
|
|
results[user] = user_results
|
|
# Collect results of each call
|
|
for user, user_results in results.items():
|
|
for result in user_results:
|
|
val = result.get(timeout=1000)
|
|
assert val == user
|
|
elapsed = timer.elapsed
|
|
# Verify that total elapsed time for all executions match expection
|
|
# of num_calls * scheduling delay between task executions
|
|
assert elapsed >= expected_duration_lbound and elapsed <= expected_duration_hbound
|
|
# Verify that total elapsed time for execution of all tasks for
|
|
# an individual user is the same as total expected duration.
|
|
# Tasks run for different users run in parallel.
|
|
for user_results in results.values():
|
|
last_task_end_time = start_time
|
|
for result in user_results:
|
|
if result.date_done > last_task_end_time:
|
|
last_task_end_time = result.date_done
|
|
user_elapsed = (last_task_end_time - start_time).total_seconds()
|
|
assert user_elapsed >= expected_duration_lbound and user_elapsed <= expected_duration_hbound
|
|
|
|
|
|
@skip_unless_postgres()
|
|
class TestCeleryUserRateLimitIntegrationPostgres(TestCeleryUserRateLimitIntegration):
|
|
_user_rate_limit = 0.1
|
|
|
|
@classmethod
|
|
def handle_galaxy_config_kwds(cls, config):
|
|
super().handle_galaxy_config_kwds(config)
|
|
config["celery_user_rate_limit"] = cls._user_rate_limit
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
dburl = self._app.config.database_connection # that's how you get to galaxy app instance
|
|
setup_users(dburl)
|
|
|
|
def test_mock_pass_user_id_task(self):
|
|
self._test_mock_pass_user_id_task(2, 3, self._user_rate_limit)
|
|
|
|
|
|
class TestCeleryUserRateLimitIntegrationSqlite(TestCeleryUserRateLimitIntegration):
|
|
_user_rate_limit = 0.1
|
|
|
|
@classmethod
|
|
def handle_galaxy_config_kwds(cls, config):
|
|
super().handle_galaxy_config_kwds(config)
|
|
config["check_migrate_databases"] = False
|
|
config["database_connection"] = sqlite_url()
|
|
if config.get("database_engine_option_pool_size"):
|
|
config.pop("database_engine_option_pool_size")
|
|
if config.get("database_engine_option_max_overflow"):
|
|
config.pop("database_engine_option_max_overflow")
|
|
config["celery_user_rate_limit"] = cls._user_rate_limit
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
dburl = self._app.config.database_connection # that's how you get to galaxy app instance
|
|
setup_users(dburl)
|
|
|
|
def test_mock_pass_user_id_task(self):
|
|
self._test_mock_pass_user_id_task(2, 3, self._user_rate_limit)
|
|
|
|
|
|
class TestCeleryUserRateLimitIntegrationNoLimit(TestCeleryUserRateLimitIntegration):
|
|
_user_rate_limit = 0.0
|
|
|
|
def test_mock_pass_user_id_task(self):
|
|
self._test_mock_pass_user_id_task(2, 3, self._user_rate_limit)
|