Skip to content

MSC4242: State DAGs (serving) - #20133

Open
kegsay wants to merge 4 commits into
kegan/4242-storagefrom
kegan/4242-serving
Open

MSC4242: State DAGs (serving)#20133
kegsay wants to merge 4 commits into
kegan/4242-storagefrom
kegan/4242-serving

Conversation

@kegsay

@kegsay kegsay commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Adds the serving functions needed for MSC4242: State DAGs. This PR adds MSC4242 support to /make_join, /send_join and /get_missing_events, as well as calculates the destinations for /send events correctly using prev_state_events.

Built on top of #19718 for the storage functions it makes.

Split out from #19425

Part of a series of 5x PRs to land the federation part of MSC4242 (storage, fedclient, serving (this PR), inbound-joins, inbound-pulls).

Whilst this is mostly a port of the code in #19425 there are a few changes:

  • /get_missing_events accepts message events when walking the state DAG, in which case it resolves the first hop to be that event's prev_state_events. The original PR made the client /event the message event and then set latest=[prev_state_events] on its own. This is not very efficient (extra round trip to fetch the event) and there's no reason why the server can't do the message->prev_state_events lookup, so we do so. This matches the MSC examples.
  • We cap the amount of events fetched via /get_missing_events. The MSC allows it, so it's a good safety check.
  • We sort the returned state DAG in /send_join by depth then event ID so it's "mostly" sorted. This is more a formality than anything else, the MSC does not mandate this, but it makes /send_join responses deterministic.
  • notify_on_event_delivered_over_federation is a new thing since MSC4242: State DAGs (Federation) #19425, so we include state DAG events in it like we do with state/auth_chain.

This PR does remove the forced m.federate: false setting for MSC4242 rooms, so it makes it possible for federated MSC4242 rooms to be made. This is mostly so we can test via the endpoints. Given you must opt-in to MSC4242 via the experimental features config option, it seems reasonable to loosen this setting. The forced no-federation flag existed prior to review saying that the MSC4242 room version could itself be gated behind an experimental feature.

Reviewable commit-by-commit.

Pull Request Checklist

  • Pull request is based on the develop branch
  • Pull request includes a changelog file. The entry should:
    • Be a short description of your change which makes sense to users. "Fixed a bug that prevented receiving messages from other servers." instead of "Moved X method from EventStore to EventWorkerStore.".
    • Use markdown where necessary, mostly for code blocks.
    • End with either a period (.) or an exclamation mark (!).
    • Start with a capital letter.
    • Feel free to credit yourself, by adding a sentence "Contributed by @github_username." or "Contributed by [Your Name]." to the end of the entry.
  • Code style is correct (run the linters)

@kegsay
kegsay requested a review from a team as a code owner August 21, 2026 07:05
Comment thread synapse/api/constants.py
class StateDag:
GET_MISSING_EVENTS_FIELD: Final = "org.matrix.msc4242.state_dag"

MAX_MISSING_EVENTS: Final = 1000

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why 1000?

If arbitrary, that's fine (comment)

Comment on lines +870 to +871
if supports_msc4242_state_dag(event):
caller_supports_partial_state = False

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explain why (comment)

Comment on lines +907 to +910
# Sort by depth, though this is just a nicety, MSC4242 does not require it
state_dag = sorted(
state_dag_map.values(), key=lambda ev: (ev.depth, ev.event_id)
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why bother?

Comment on lines +1504 to +1507
if walk_state_dag:
return await self.on_get_missing_events_state_dag(
room_id, earliest_events, latest_events, limit
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we should just raise this logic up one level

if prev_state_event_id not in earliest_event_set
)

first_hop_event_ids.difference_update(seed_event_ids)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment why

Comment on lines +1266 to +1271
create_event_ids = [
ev
for ev in state_dag
if (ev["type"], ev["state_key"]) == ("m.room.create", "")
]
self.assertEqual(len(create_event_ids), 1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we care about checking this?


@skip_test("requires MSC4242 inbound event auth")
@override_config({"experimental_features": {"msc4242_enabled": True}})
def test_send_join_state_dag_ignores_partial_state(self) -> None:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where in the test are we expecting to ignore partial state? (needs comment to point it out)

)
channel = self.make_signed_federation_request(
"PUT",
f"/_matrix/federation/v2/send_join/{room_id}/x?omit_members=true",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment about ?omit_members=true being the thing causing partial state, etc

self.assertEqual(channel.code, HTTPStatus.OK, channel.json_body)

event = channel.json_body["event"]
self.assertNotIn("auth_events", event)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment why

Comment on lines +1292 to +1299
extremities = self.get_success(
self.hs.get_datastores().main.get_state_dag_extremities(room_id)
)
self.assertGreater(len(extremities), 0)
self.assertCountEqual(event["prev_state_events"], extremities)
self.assertIncludes(
set(event["prev_state_events"]), set(extremities), exact=True
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs comment

@kegsay
kegsay force-pushed the kegan/4242-serving branch from c4a53fc to 6d12161 Compare September 1, 2026 14:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants