Support sending and receiving MSC4354 Sticky Event metadata.#19365
Support sending and receiving MSC4354 Sticky Event metadata.#19365reivilibre wants to merge 47 commits intodevelopfrom
Conversation
2b83332 to
53d4486
Compare
e49d4bd to
78ee6e8
Compare
Including delayed events
78ee6e8 to
fb67e9a
Compare
synapse/api/constants.py
Outdated
|
|
||
| class StickyEvent: | ||
| QUERY_PARAM_NAME: Final = "org.matrix.msc4354.sticky_duration_ms" | ||
| FIELD_NAME: Final = "msc4354_sticky" |
There was a problem hiding this comment.
The separation between StickyEventField and StickyEvent.FIELD_NAME is slightly mind bending.
Co-authored-by: Eric Eastwood <erice@element.io>
1c2a9ad to
beead8a
Compare
| expr_soft_failed = "COALESCE(((ej.internal_metadata::jsonb)->>'soft_failed')::boolean, FALSE)" | ||
| else: | ||
| expr_soft_failed = "COALESCE(ej.internal_metadata->>'soft_failed', FALSE)" |
There was a problem hiding this comment.
Ugh! Future stuff but we should consider making soft_failed a dedicated column of the events table or some table. Is this performant enough?
There was a problem hiding this comment.
I can't say I'm entirely happy with it, but it feels like it shouldn't be a very common case and the table is altogether small.
I don't think it is super expensive to fish out of the small internal_metadata JSON field in the event_json table (other than doing that heap lookup).
(It would be nicer if we didn't have a freeform internal_metadata and instead had nullable fields IMO, but I'm not sure that would greatly improve much.)
At this point I think it's better than risking stale denormalised data.
Co-authored-by: Eric Eastwood <erice@element.io>
89b20d3 to
5a5efc5
Compare
| expr_soft_failed = "COALESCE(((ej.internal_metadata::jsonb)->>'soft_failed')::boolean, FALSE)" | ||
| else: | ||
| expr_soft_failed = "COALESCE(ej.internal_metadata->>'soft_failed', FALSE)" |
There was a problem hiding this comment.
trial-olddeps -> sqlite3.OperationalError: near ">>": syntax error
trial-olddeps CI failing ❌, https://github.com/element-hq/synapse/actions/runs/21635340214/job/62359543300?pr=19365#step:10:7282
tests.storage.test_sticky_events.StickyEventsTestCase.test_get_updated_sticky_events
===============================================================================
[FAIL]
Traceback (most recent call last):
File "/home/runner/work/synapse/synapse/tests/storage/test_sticky_events.py", line 153, in test_get_updated_sticky_events_with_limit
updates = self.get_success(
File "/home/runner/work/synapse/synapse/tests/unittest.py", line 707, in get_success
return self.successResultOf(deferred)
File "/opt/hostedtoolcache/Python/3.10.19/x64/lib/python3.10/site-packages/twisted/trial/_synctest.py", line 706, in successResultOf
self.fail(
twisted.trial.unittest.FailTest: Success result expected on <Deferred at 0x7f36aca5efe0 current result: None>, found failure result instead:
Traceback (most recent call last):
File "/opt/hostedtoolcache/Python/3.10.19/x64/lib/python3.10/site-packages/twisted/internet/defer.py", line 517, in errback
self._startRunCallbacks(fail)
File "/opt/hostedtoolcache/Python/3.10.19/x64/lib/python3.10/site-packages/twisted/internet/defer.py", line 580, in _startRunCallbacks
self._runCallbacks()
File "/opt/hostedtoolcache/Python/3.10.19/x64/lib/python3.10/site-packages/twisted/internet/defer.py", line 662, in _runCallbacks
current.result = callback(current.result, *args, **kw)
File "/opt/hostedtoolcache/Python/3.10.19/x64/lib/python3.10/site-packages/twisted/internet/defer.py", line 1514, in gotResult
current_context.run(_inlineCallbacks, r, g, status)
--- <exception caught here> ---
File "/opt/hostedtoolcache/Python/3.10.19/x64/lib/python3.10/site-packages/twisted/internet/defer.py", line 1443, in _inlineCallbacks
result = current_context.run(result.throwExceptionIntoGenerator, g)
File "/opt/hostedtoolcache/Python/3.10.19/x64/lib/python3.10/site-packages/twisted/python/failure.py", line 500, in throwExceptionIntoGenerator
return g.throw(self.type, self.value, self.tb)
File "/opt/hostedtoolcache/Python/3.10.19/x64/lib/python3.10/site-packages/synapse/storage/databases/main/sticky_events.py", line 144, in get_updated_sticky_events
return await self.db_pool.runInteraction(
File "/opt/hostedtoolcache/Python/3.10.19/x64/lib/python3.10/site-packages/synapse/storage/database.py", line 1015, in runInteraction
return await delay_cancellation(_runInteraction())
File "/opt/hostedtoolcache/Python/3.10.19/x64/lib/python3.10/site-packages/twisted/internet/defer.py", line 1443, in _inlineCallbacks
result = current_context.run(result.throwExceptionIntoGenerator, g)
File "/opt/hostedtoolcache/Python/3.10.19/x64/lib/python3.10/site-packages/twisted/python/failure.py", line 500, in throwExceptionIntoGenerator
return g.throw(self.type, self.value, self.tb)
File "/opt/hostedtoolcache/Python/3.10.19/x64/lib/python3.10/site-packages/synapse/storage/database.py", line 981, in _runInteraction
result: R = await self.runWithConnection(
File "/opt/hostedtoolcache/Python/3.10.19/x64/lib/python3.10/site-packages/synapse/storage/database.py", line 1117, in runWithConnection
return await make_deferred_yieldable(
File "/opt/hostedtoolcache/Python/3.10.19/x64/lib/python3.10/site-packages/twisted/internet/defer.py", line 662, in _runCallbacks
current.result = callback(current.result, *args, **kw)
File "/home/runner/work/synapse/synapse/tests/server.py", line 814, in <lambda>
d.addCallback(lambda x: function(*args, **kwargs))
File "/opt/hostedtoolcache/Python/3.10.19/x64/lib/python3.10/site-packages/twisted/enterprise/adbapi.py", line 293, in _runWithConnection
compat.reraise(excValue, excTraceback)
File "/opt/hostedtoolcache/Python/3.10.19/x64/lib/python3.10/site-packages/twisted/python/deprecate.py", line 298, in deprecatedFunction
return function(*args, **kwargs)
File "/opt/hostedtoolcache/Python/3.10.19/x64/lib/python3.10/site-packages/twisted/python/compat.py", line 403, in reraise
raise exception.with_traceback(traceback)
File "/opt/hostedtoolcache/Python/3.10.19/x64/lib/python3.10/site-packages/twisted/enterprise/adbapi.py", line 284, in _runWithConnection
result = func(conn, *args, **kw)
File "/opt/hostedtoolcache/Python/3.10.19/x64/lib/python3.10/site-packages/synapse/storage/database.py", line 1110, in inner_func
return func(db_conn, *args, **kwargs)
File "/opt/hostedtoolcache/Python/3.10.19/x64/lib/python3.10/site-packages/synapse/storage/database.py", line 819, in new_transaction
r = func(cursor, *args, **kwargs)
File "/opt/hostedtoolcache/Python/3.10.19/x64/lib/python3.10/site-packages/synapse/storage/databases/main/sticky_events.py", line 160, in _get_updated_sticky_events_txn
txn.execute(
File "/opt/hostedtoolcache/Python/3.10.19/x64/lib/python3.10/site-packages/synapse/storage/database.py", line 458, in execute
self._do_execute(self.txn.execute, sql, parameters)
File "/opt/hostedtoolcache/Python/3.10.19/x64/lib/python3.10/site-packages/synapse/storage/database.py", line 520, in _do_execute
return func(sql, *args, **kwargs)
sqlite3.OperationalError: near ">>": syntax error
| # The de-outliered event is sticky. Update the sticky events table to ensure | ||
| # we deliver this down /sync. | ||
| self.store.insert_sticky_events_txn(txn, [event]) |
There was a problem hiding this comment.
Test for this scenario?
|
|
||
| content: JsonDict = attr.Factory(dict) | ||
| unsigned: JsonDict = attr.Factory(dict) | ||
| sticky: StickyEventField | None = None |
There was a problem hiding this comment.
Probably fine as-is. We don't have any great options ⏩
Part of: MSC4354 whose experimental feature tracking issue is #19409
Follows: #19340 (a necessary bugfix for
/event/to set this metadata)Partially supersedes: #18968
This PR implements the first batch of work to support MSC4354 Sticky Events.
Sticky events are events that have been configured with a finite 'stickiness' duration,
capped to 1 hour per current MSC draft.
Whilst an event is sticky, we provide stronger delivery guarantees for the event, both to
our clients and to remote homeservers, essentially making it reliable delivery as long as we
have a functional connection to the client/server and until the stickiness expires.
This PR merely supports creating sticky events and receiving the sticky TTL metadata in clients.
It is not suitable for trialling sticky events since none of the other semantics are implemented.
The current plan is to follow this PR up with more PRs, roughly parcelled up as follows:
Add MSC4354 experimental feature flag
Expose MSC4354 enablement on /versions
Add constants for sticky events
Add sticky_events table
Add sticky events store and stream
EventBase: add the concept of sticky_duration
EventBuilder: allow building events with sticky event fields
store method: insert_sticky_events_txn
When persisting currently-sticky events, add to sticky event stream
Allow clients to send sticky events
Including delayed events
Add test helper for sending sticky events
Expose the sticky event TTL to clients
Add a test for sticky TTL calculation and exposure to clients