kiba-core 0.5.3.dev92__tar.gz → 0.5.3.dev94__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/CHANGELOG.md +1 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/PKG-INFO +1 -1
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/queues/sql.py +21 -6
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/kiba_core.egg-info/PKG-INFO +1 -1
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/makefile +1 -5
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/pyproject.toml +2 -2
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/queues/test_sql_queue.py +27 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/uv.lock +4 -4
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/.github/pull_request_template.md +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/.github/workflows/deploy.yml +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/.github/workflows/publish.yml +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/.github/workflows/pull-request.yml +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/.github/workflows/release.yml +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/.gitignore +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/Dockerfile +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/README.md +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/__init__.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/api/__init__.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/api/api_request.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/api/api_response.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/api/authorizer.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/api/default_routes.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/api/health.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/api/json_route.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/api/middleware/__init__.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/api/middleware/database_connection_middleware.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/api/middleware/exception_handling_middleware.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/api/middleware/logging_middleware.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/api/middleware/origin_ip_middleware.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/api/middleware/server_headers_middleware.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/api/openapi.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/api/rate_limit.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/api/request_body.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/api/request_context.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/api/route.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/api/route_auth.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/api/route_metadata.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/api/streaming_json_route.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/api/uploaded_file.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/aws_requester.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/caching/__init__.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/caching/cache.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/caching/dict_cache.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/caching/file_cache.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/cron/__init__.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/cron/api_request_cron_job_processor.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/cron/cron_executor.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/cron/cron_job_processor.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/cron/model.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/exceptions.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/http/__init__.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/http/basic_authentication.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/http/jwt.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/http/rest_method.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/locks/__init__.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/locks/lock.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/locks/model.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/locks/sql.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/logging.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/notifications/__init__.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/notifications/discord_client.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/notifications/notification_client.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/notifications/slack_client.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/py.typed +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/queues/__init__.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/queues/aqs.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/queues/cosmos.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/queues/message_queue.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/queues/message_queue_processor.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/queues/model.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/queues/sqs.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/requester/__init__.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/requester/requester.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/s3_manager.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/service_client.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/store/__init__.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/store/database.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/store/entity_repository.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/store/retriever.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/store/saver.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/util/__init__.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/util/async_util.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/util/chain_util.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/util/date_util.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/util/dict_util.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/util/file_util.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/util/hashing_util.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/util/http_util.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/util/json_util.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/util/list_util.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/util/process_util.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/util/string_util.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/util/typing_util.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/util/url_util.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/util/value_holder.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/web3/__init__.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/web3/eth_client.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/web3/eth_client_registry.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/web3/multicall3.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/kiba_core.egg-info/SOURCES.txt +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/kiba_core.egg-info/dependency_links.txt +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/kiba_core.egg-info/requires.txt +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/kiba_core.egg-info/top_level.txt +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/setup.cfg +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/__init__.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/api/test_api_response.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/api/test_authorizer.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/api/test_database_connection_middleware.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/api/test_exception_handling_middleware.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/api/test_json_route.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/api/test_openapi.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/api/test_origin_ip_middleware.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/api/test_rate_limit.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/api/test_request_context.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/api/test_route.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/api/test_streaming_json_route.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/caching/test_dict_cache.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/caching/test_file_cache.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/cron/__init__.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/cron/test_cron_executor.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/locks/test_sql_lock.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/queues/conftest.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/queues/test_aqs_queue.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/queues/test_cosmos_queue.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/queues/test_message_queue_processor.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/queues/test_sqs_queue.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/requester/__init__.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/requester/test_requester.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/test_database.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/test_entity_repository.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/test_exceptions.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/test_logging.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/util/__init__.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/util/test_async_util.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/util/test_chain_util.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/util/test_date_util.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/util/test_dict_util.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/util/test_file_util.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/util/test_json_util.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/util/test_list_util.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/util/test_process_util.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/util/test_string_util.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/util/test_url_util.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/web3/__init__.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/web3/test_eth_client_registry.py +0 -0
- {kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/web3/test_rest_eth_client.py +0 -0
|
@@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
10
|
### Added
|
|
11
|
+
- [MAJOR] Added `priority` to `SqlMessageQueue.send_message` and `send_messages` (default 0): visible messages are claimed highest priority first, then by `visibleDate`, and a duplicate send, or a retry folded into a pending duplicate, keeps the higher priority. `tbl_queue_messages` has a new `priority` column (`INTEGER NOT NULL DEFAULT 0`), so apps must add it with a migration before upgrading
|
|
11
12
|
- [PATCH] Fixed `DatabaseConnectionMiddleware`'s commit when the response starts failing ("another operation is in progress") for streaming responses behind a `BaseHTTPMiddleware`: `Database.execute` and `Database.commit_context_connection` now wait for each other on the same connection
|
|
12
13
|
- [MINOR] Added `Database.commit_context_connection` to commit the context connection's writes so far; anything run on it afterwards is committed when the context connection ends
|
|
13
14
|
- [PATCH] Updated `DatabaseConnectionMiddleware` to commit before the response starts, so a client can't get the response and make its next request before the request's writes are visible (writes made while streaming are still committed when the request ends)
|
|
@@ -52,6 +52,7 @@ QueueMessagesTable = sqlalchemy.Table(
|
|
|
52
52
|
sqlalchemy.Column(key='startedDate', name='started_date', type_=sqlalchemy.DateTime(timezone=True), nullable=True),
|
|
53
53
|
sqlalchemy.Column(key='completedDate', name='completed_date', type_=sqlalchemy.DateTime(timezone=True), nullable=True),
|
|
54
54
|
sqlalchemy.Column(key='lastError', name='last_error', type_=sqlalchemy.Text, nullable=True),
|
|
55
|
+
sqlalchemy.Column(key='priority', name='priority', type_=sqlalchemy.Integer, nullable=False, server_default='0'),
|
|
55
56
|
sqlalchemy.Index('tbl_queue_messages_idx_queue_name_status_visible_date', 'queueName', 'status', 'visibleDate'),
|
|
56
57
|
sqlalchemy.Index('tbl_queue_messages_ux_queue_name_deduplication_id_pending', 'queueName', 'deduplicationId', unique=True, postgresql_where=_PENDING_PREDICATE, sqlite_where=_PENDING_PREDICATE),
|
|
57
58
|
)
|
|
@@ -109,10 +110,10 @@ class SqlMessageQueue(MessageQueue[SqlMessage]):
|
|
|
109
110
|
return sqlalchemy_sqlite.insert(self.table)
|
|
110
111
|
raise InternalServerErrorException(message=f'SqlMessageQueue does not support dialect: {dialectName}')
|
|
111
112
|
|
|
112
|
-
async def send_message(self, message: Message, delaySeconds: int = 0) -> None:
|
|
113
|
-
await self.send_messages(messages=[message], delaySeconds=delaySeconds)
|
|
113
|
+
async def send_message(self, message: Message, delaySeconds: int = 0, priority: int = 0) -> None:
|
|
114
|
+
await self.send_messages(messages=[message], delaySeconds=delaySeconds, priority=priority)
|
|
114
115
|
|
|
115
|
-
async def send_messages(self, messages: Sequence[Message], delaySeconds: int = 0) -> None:
|
|
116
|
+
async def send_messages(self, messages: Sequence[Message], delaySeconds: int = 0, priority: int = 0) -> None:
|
|
116
117
|
if not messages:
|
|
117
118
|
return
|
|
118
119
|
now = self.database.now()
|
|
@@ -136,13 +137,17 @@ class SqlMessageQueue(MessageQueue[SqlMessage]):
|
|
|
136
137
|
'status': MESSAGE_STATUS_PENDING,
|
|
137
138
|
'visibleDate': self.database.now(seconds=delaySeconds),
|
|
138
139
|
'createdDate': now,
|
|
140
|
+
'priority': priority,
|
|
139
141
|
}
|
|
140
142
|
)
|
|
141
143
|
async with self.database.create_transaction() as connection:
|
|
142
144
|
insertQuery = self._create_insert(dialectName=connection.dialect.name).values(rows)
|
|
143
145
|
earliest = sqlalchemy.func.least if connection.dialect.name == 'postgresql' else sqlalchemy.func.min
|
|
146
|
+
highest = sqlalchemy.func.greatest if connection.dialect.name == 'postgresql' else sqlalchemy.func.max
|
|
144
147
|
upsertQuery = insertQuery.on_conflict_do_update(
|
|
145
|
-
index_elements=[self.table.c.queueName, self.table.c.deduplicationId],
|
|
148
|
+
index_elements=[self.table.c.queueName, self.table.c.deduplicationId],
|
|
149
|
+
index_where=_PENDING_PREDICATE,
|
|
150
|
+
set_={self.table.c.visibleDate: earliest(self.table.c.visibleDate, insertQuery.excluded.visibleDate), self.table.c.priority: highest(self.table.c.priority, insertQuery.excluded.priority)},
|
|
146
151
|
)
|
|
147
152
|
await self.database.execute(query=upsertQuery, connection=connection)
|
|
148
153
|
|
|
@@ -175,7 +180,7 @@ class SqlMessageQueue(MessageQueue[SqlMessage]):
|
|
|
175
180
|
.where(self.table.c.queueName == self.queueName)
|
|
176
181
|
.where(self.table.c.status.in_([MESSAGE_STATUS_PENDING, MESSAGE_STATUS_RUNNING]))
|
|
177
182
|
.where(self.table.c.visibleDate <= now)
|
|
178
|
-
.order_by(self.table.c.visibleDate, self.table.c.id)
|
|
183
|
+
.order_by(self.table.c.priority.desc(), self.table.c.visibleDate, self.table.c.id)
|
|
179
184
|
.limit(limit)
|
|
180
185
|
.with_for_update(skip_locked=True)
|
|
181
186
|
)
|
|
@@ -194,7 +199,7 @@ class SqlMessageQueue(MessageQueue[SqlMessage]):
|
|
|
194
199
|
)
|
|
195
200
|
result = await self.database.execute(query=claimQuery, connection=connection)
|
|
196
201
|
rows = result.mappings().all()
|
|
197
|
-
return
|
|
202
|
+
return [SqlMessage.from_row(row=row, table=self.table) for row in sorted(rows, key=lambda row: (-row[self.table.c.priority], row[self.table.c.id]))]
|
|
198
203
|
|
|
199
204
|
def _owned_update(self, message: SqlMessage) -> sqlalchemy.Update:
|
|
200
205
|
return sqlalchemy.update(self.table).where(self.table.c.id == message.id).where(self.table.c.lockToken == message.lockToken).where(self.table.c.status == MESSAGE_STATUS_RUNNING)
|
|
@@ -237,6 +242,16 @@ class SqlMessageQueue(MessageQueue[SqlMessage]):
|
|
|
237
242
|
await self.database.execute(query=query, connection=connection)
|
|
238
243
|
except sqlalchemy.exc.IntegrityError:
|
|
239
244
|
# NOTE(krishan711): an identical message was enqueued while this one ran, so it will do the work instead
|
|
245
|
+
highest = sqlalchemy.func.greatest if connection.dialect.name == 'postgresql' else sqlalchemy.func.max
|
|
246
|
+
runningPriority = sqlalchemy.select(self.table.c.priority).where(self.table.c.id == message.id).scalar_subquery()
|
|
247
|
+
duplicateQuery = (
|
|
248
|
+
sqlalchemy.update(self.table)
|
|
249
|
+
.where(self.table.c.queueName == self.queueName)
|
|
250
|
+
.where(self.table.c.deduplicationId == message.deduplicationId)
|
|
251
|
+
.where(self.table.c.status == MESSAGE_STATUS_PENDING)
|
|
252
|
+
.values(priority=highest(self.table.c.priority, runningPriority))
|
|
253
|
+
)
|
|
254
|
+
await self.database.execute(query=duplicateQuery, connection=connection)
|
|
240
255
|
query = self._owned_update(message=message).values(status=MESSAGE_STATUS_DEDUPLICATED, lockToken=None, completedDate=self.database.now())
|
|
241
256
|
await self.database.execute(query=query, connection=connection)
|
|
242
257
|
|
|
@@ -45,11 +45,7 @@ clean:
|
|
|
45
45
|
publish: build
|
|
46
46
|
@ uv publish
|
|
47
47
|
|
|
48
|
-
GIT_LAST_TAG=$(shell git describe --tags --abbrev=0)
|
|
49
|
-
GIT_COUNT=$(shell git rev-list $(GIT_LAST_TAG)..HEAD --count)
|
|
50
48
|
publish-dev:
|
|
51
|
-
@ uv run
|
|
52
|
-
@ uv build
|
|
53
|
-
@ uv publish
|
|
49
|
+
@ uv run publish-dev
|
|
54
50
|
|
|
55
51
|
.PHONY: *
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "kiba-core"
|
|
3
|
-
version = "0.5.3.
|
|
3
|
+
version = "0.5.3.dev94"
|
|
4
4
|
description = "Kiba Labs' python utilities"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12, <4"
|
|
@@ -60,7 +60,7 @@ types = [
|
|
|
60
60
|
|
|
61
61
|
[dependency-groups]
|
|
62
62
|
dev = [
|
|
63
|
-
"kiba-build==0.1.11.
|
|
63
|
+
"kiba-build==0.1.11.dev23",
|
|
64
64
|
]
|
|
65
65
|
|
|
66
66
|
[project.urls]
|
|
@@ -116,6 +116,24 @@ async def test_duplicate_send_keeps_the_earliest_visibility(workers: tuple[SqlMe
|
|
|
116
116
|
assert await workerA.get_message() is not None
|
|
117
117
|
|
|
118
118
|
|
|
119
|
+
async def test_higher_priority_messages_are_claimed_before_older_ones(workers: tuple[SqlMessageQueue, SqlMessageQueue]):
|
|
120
|
+
workerA, _ = workers
|
|
121
|
+
await workerA.send_messages(messages=[_message(deduplicationId=None, userId=str(index)) for index in range(3)])
|
|
122
|
+
await workerA.send_message(message=_message(deduplicationId=None, userId='urgent'), priority=1)
|
|
123
|
+
messages = await workerA.get_messages(limit=2)
|
|
124
|
+
assert [message.content['userId'] for message in messages] == ['urgent', '0']
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
async def test_duplicate_send_keeps_the_highest_priority(workers: tuple[SqlMessageQueue, SqlMessageQueue]):
|
|
128
|
+
workerA, _ = workers
|
|
129
|
+
await workerA.send_message(message=_message(deduplicationId=None, userId='other'))
|
|
130
|
+
await workerA.send_message(message=_message())
|
|
131
|
+
await workerA.send_message(message=_message(), priority=1)
|
|
132
|
+
await workerA.send_message(message=_message())
|
|
133
|
+
assert [(row['content']['userId'], row['priority']) for row in await _rows(queue=workerA)] == [('other', 0), ('user-1', 1)]
|
|
134
|
+
assert (await _claim(queue=workerA)).content['userId'] == 'user-1'
|
|
135
|
+
|
|
136
|
+
|
|
119
137
|
async def test_processor_fails_message_even_when_notifications_fail(workers: tuple[SqlMessageQueue, SqlMessageQueue]):
|
|
120
138
|
workerA, _ = workers
|
|
121
139
|
|
|
@@ -376,6 +394,15 @@ async def test_retry_is_folded_into_an_identical_pending_message(workers: tuple[
|
|
|
376
394
|
assert [row['status'] for row in await _rows(queue=workerA)] == ['deduplicated', 'pending']
|
|
377
395
|
|
|
378
396
|
|
|
397
|
+
async def test_retry_folded_into_a_lower_priority_duplicate_raises_its_priority(workers: tuple[SqlMessageQueue, SqlMessageQueue]):
|
|
398
|
+
workerA, _ = workers
|
|
399
|
+
await workerA.send_message(message=_message(), priority=1)
|
|
400
|
+
message = await _claim(queue=workerA)
|
|
401
|
+
await workerA.send_message(message=_message())
|
|
402
|
+
await workerA.retry_message(message=message)
|
|
403
|
+
assert [(row['status'], row['priority']) for row in await _rows(queue=workerA)] == [('deduplicated', 1), ('pending', 1)]
|
|
404
|
+
|
|
405
|
+
|
|
379
406
|
async def test_release_hands_the_message_back_without_using_an_attempt(workers: tuple[SqlMessageQueue, SqlMessageQueue]):
|
|
380
407
|
workerA, workerB = workers
|
|
381
408
|
await workerA.send_message(message=_message())
|
|
@@ -1668,7 +1668,7 @@ wheels = [
|
|
|
1668
1668
|
|
|
1669
1669
|
[[package]]
|
|
1670
1670
|
name = "kiba-build"
|
|
1671
|
-
version = "0.1.11.
|
|
1671
|
+
version = "0.1.11.dev23"
|
|
1672
1672
|
source = { registry = "https://pypi.org/simple" }
|
|
1673
1673
|
dependencies = [
|
|
1674
1674
|
{ name = "asyncclick" },
|
|
@@ -1684,9 +1684,9 @@ dependencies = [
|
|
|
1684
1684
|
{ name = "ty" },
|
|
1685
1685
|
{ name = "wheel" },
|
|
1686
1686
|
]
|
|
1687
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
1687
|
+
sdist = { url = "https://files.pythonhosted.org/packages/61/6e/95194ff5f14cfd0738c753439421edae2250985e37840eed930ac06b88e1/kiba_build-0.1.11.dev23.tar.gz", hash = "sha256:17c08e9231fe5b463df132195079f0efced4dfe48cf16e5c09b0831a44b1e4e6", size = 99616, upload-time = "2026-11-05T15:32:28.417Z" }
|
|
1688
1688
|
wheels = [
|
|
1689
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
1689
|
+
{ url = "https://files.pythonhosted.org/packages/d9/0c/3e341790a689c8365ad7575451ca936adb9f1c6f7ae05d98704b728c5b1d/kiba_build-0.1.11.dev23-py3-none-any.whl", hash = "sha256:03fec44ef8dc282147940771521a1e53f83dc2caf2758fdc48c78de613bfd857", size = 13128, upload-time = "2026-11-05T15:32:27.481Z" },
|
|
1690
1690
|
]
|
|
1691
1691
|
|
|
1692
1692
|
[[package]]
|
|
@@ -1779,7 +1779,7 @@ requires-dist = [
|
|
|
1779
1779
|
provides-extras = ["api", "core-api", "storage", "queues", "queue-sqs", "queue-aqs", "queue-cosmos", "database-psql", "database-sqlite", "requester", "web3", "cron", "types"]
|
|
1780
1780
|
|
|
1781
1781
|
[package.metadata.requires-dev]
|
|
1782
|
-
dev = [{ name = "kiba-build", specifier = "==0.1.11.
|
|
1782
|
+
dev = [{ name = "kiba-build", specifier = "==0.1.11.dev23" }]
|
|
1783
1783
|
|
|
1784
1784
|
[[package]]
|
|
1785
1785
|
name = "librt"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/api/middleware/exception_handling_middleware.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/core/api/middleware/server_headers_middleware.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/api/test_database_connection_middleware.py
RENAMED
|
File without changes
|
{kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/api/test_exception_handling_middleware.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kiba_core-0.5.3.dev92 → kiba_core-0.5.3.dev94}/tests/queues/test_message_queue_processor.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|