Skip to content

Commit ccfd08e

Browse files
committed
fix(events): preserve native JSON batch parameters
1 parent 05f00a1 commit ccfd08e

7 files changed

Lines changed: 53 additions & 14 deletions

File tree

sqlspec/adapters/psqlpy/events/backend.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,8 +311,8 @@ async def _publish_durable(
311311
{
312312
"event_id": event_id,
313313
"channel": channel,
314-
"payload_json": to_json(payload),
315-
"metadata_json": to_json(metadata) if metadata is not None else None,
314+
"payload_json": payload,
315+
"metadata_json": metadata,
316316
"status": "pending",
317317
"available_at": now,
318318
"lease_expires_at": None,

sqlspec/extensions/events/_queue.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from sqlspec.extensions.events._names import normalize_queue_table_name
1515
from sqlspec.extensions.events._payload import parse_event_timestamp
1616
from sqlspec.utils.logging import get_logger
17-
from sqlspec.utils.serializers import from_json, to_json
17+
from sqlspec.utils.serializers import from_json
1818
from sqlspec.utils.uuids import uuid4
1919

2020
if TYPE_CHECKING:
@@ -190,8 +190,8 @@ def _batch_insert_parameters(
190190
records.append({
191191
"event_id": event_id,
192192
"channel": channel,
193-
"payload_json": to_json(payload),
194-
"metadata_json": to_json(metadata) if metadata is not None else None,
193+
"payload_json": payload,
194+
"metadata_json": metadata,
195195
"status": _PENDING_STATUS,
196196
"available_at": now,
197197
"lease_expires_at": None,

tests/integration/adapters/asyncpg/extensions/events/test_listen_notify.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,24 @@ async def _handler(message: Any) -> None:
6666
event_id = await channel.publish("alerts", {"action": "hybrid_async"})
6767
await _wait_for_message(received)
6868

69+
batch_ids = await channel.publish_many([
70+
("alerts", {"index": 1}, None),
71+
("alerts", {"index": 2}, {"source": "batch"}),
72+
])
73+
for _ in range(_MAX_POLL_ATTEMPTS):
74+
if len(received) >= 3:
75+
break
76+
await asyncio.sleep(_POLL_INTERVAL)
77+
6978
await channel.stop_listener(listener.id)
7079

71-
assert received, "listener did not receive message"
80+
assert len(received) >= 3, "listener did not receive the single event and complete batch"
7281
message = received[0]
7382
assert message.event_id == event_id
7483
assert message.payload["action"] == "hybrid_async"
84+
assert [message.event_id for message in received[1:3]] == batch_ids
85+
assert [message.payload["index"] for message in received[1:3]] == [1, 2]
86+
assert received[2].metadata == {"source": "batch"}
7587

7688
await channel.shutdown()
7789
if config.connection_instance:

tests/integration/adapters/psqlpy/extensions/events/test_listen_notify.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,24 @@ async def _handler(message: Any) -> None:
100100
if received:
101101
break
102102
await asyncio.sleep(0.05)
103+
104+
batch_ids = await channel.publish_many([
105+
("alerts", {"index": 1}, None),
106+
("alerts", {"index": 2}, {"source": "batch"}),
107+
])
108+
for _ in range(200):
109+
if len(received) >= 3:
110+
break
111+
await asyncio.sleep(0.05)
103112
await channel.stop_listener(listener.id)
104113

105-
assert received, "listener did not receive message"
114+
assert len(received) >= 3, "listener did not receive the single event and complete batch"
106115
message = received[0]
107116
assert message.event_id == event_id
108117
assert message.payload["action"] == "hybrid"
118+
assert [message.event_id for message in received[1:3]] == batch_ids
119+
assert [message.payload["index"] for message in received[1:3]] == [1, 2]
120+
assert received[2].metadata == {"source": "batch"}
109121
finally:
110122
backend = getattr(channel, "_backend", None)
111123
if backend and hasattr(backend, "shutdown"):

tests/integration/adapters/psycopg/extensions/events/test_listen_notify.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,10 +172,22 @@ async def _handler(message: Any) -> None:
172172
if received:
173173
break
174174
await asyncio.sleep(0.05)
175+
176+
batch_ids = await channel.publish_many([
177+
("alerts", {"index": 1}, None),
178+
("alerts", {"index": 2}, {"source": "batch"}),
179+
])
180+
for _ in range(200):
181+
if len(received) >= 3:
182+
break
183+
await asyncio.sleep(0.05)
175184
await channel.stop_listener(listener.id)
176185

177-
assert received, "listener did not receive message"
186+
assert len(received) >= 3, "listener did not receive the single event and complete batch"
178187
message = received[0]
179188

180189
assert message.event_id == event_id
181190
assert message.payload["action"] == "hybrid-async"
191+
assert [message.event_id for message in received[1:3]] == batch_ids
192+
assert [message.payload["index"] for message in received[1:3]] == [1, 2]
193+
assert received[2].metadata == {"source": "batch"}

tests/unit/extensions/test_events/test_batch_publish.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -573,8 +573,9 @@ async def test_async_postgres_hybrid_single_and_batch_preserve_empty_metadata(
573573
batch_backend = backend_factory(batch_config)
574574
await batch_backend.publish_many([("events", {"value": 1}, {})])
575575

576-
assert single_config.driver.execute_calls[0].parameters["metadata_json"] == to_json({})
577-
assert batch_config.driver.execute_many_calls[0][1][0]["metadata_json"] == to_json({})
576+
expected_single_metadata = {} if config_type is _PsqlpyConfig else to_json({})
577+
assert single_config.driver.execute_calls[0].parameters["metadata_json"] == expected_single_metadata
578+
assert batch_config.driver.execute_many_calls[0][1][0]["metadata_json"] == {}
578579

579580

580581
def test_psycopg_sync_hybrid_publish_many_bulk_inserts_and_marks_each_channel_once() -> None:
@@ -630,7 +631,7 @@ def test_psycopg_sync_hybrid_single_and_batch_preserve_empty_metadata() -> None:
630631
batch_backend.publish_many([("events", {"value": 1}, {})])
631632

632633
assert single_config.driver.execute_calls[0].parameters["metadata_json"] == to_json({})
633-
assert batch_config.driver.execute_many_calls[0][1][0]["metadata_json"] == to_json({})
634+
assert batch_config.driver.execute_many_calls[0][1][0]["metadata_json"] == {}
634635

635636

636637
def _notify_event_ids(parameters: "list[Any]") -> list[str]:

tests/unit/extensions/test_events/test_queue.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,13 +103,15 @@ def test_table_queue_empty_poll_backoff_state_is_bounded(tmp_path: Any) -> None:
103103

104104

105105
def test_table_queue_batch_records_have_deterministic_created_order() -> None:
106-
_, records = SyncTableEventQueue._batch_insert_parameters([
107-
("events", {"index": index}, None) for index in range(3)
108-
])
106+
payloads = [{"index": index} for index in range(3)]
107+
metadata = {"source": "test"}
108+
_, records = SyncTableEventQueue._batch_insert_parameters([("events", payload, metadata) for payload in payloads])
109109

110110
created_at = [record["created_at"] for record in records]
111111
assert created_at == sorted(created_at)
112112
assert len(set(created_at)) == 3
113+
assert [record["payload_json"] for record in records] == payloads
114+
assert all(record["metadata_json"] is metadata for record in records)
113115

114116

115117
def test_table_event_queue_default_table_name(tmp_path) -> None:

0 commit comments

Comments
 (0)