From 0f7b22bb04e0e58d18cfb20f593b91e74a5604bf Mon Sep 17 00:00:00 2001 From: Jason Robinson Date: Thu, 28 Nov 2019 21:02:13 +0200 Subject: [PATCH] Improve performance of generating ActivityPub payloads For a large number of receivers in `handle_send`. Only generate payload once and then just copy and attach receivers. --- CHANGELOG.md | 4 ++++ federation/outbound.py | 24 +++++++++++++++--------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a793e98..a1312a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,10 @@ Previously requirement was 30 seconds, which caused loss of messages where signature validation didn't happen immediately, but in a background worker which didn't immediately process the job. +### Internal changes + +* Improve performance of generating ActivityPub payloads for a large number of receivers in `handle_send`. + ## [0.18.1] - 2019-10-06 ### Changed diff --git a/federation/outbound.py b/federation/outbound.py index d112bb1..38ffbd5 100644 --- a/federation/outbound.py +++ b/federation/outbound.py @@ -1,3 +1,4 @@ +import copy import importlib import json import logging @@ -114,7 +115,7 @@ def handle_send( be generated. If given, the payload will be sent as this user. """ payloads = [] - public_payloads = { + ready_payloads = { "activitypub": { "auth": None, "payload": None, @@ -143,7 +144,12 @@ def handle_send( if protocol == "activitypub": try: - payload = handle_create_payload(entity, author_user, protocol, parent_user=parent_user) + if not ready_payloads[protocol]["payload"]: + # noinspection PyTypeChecker + ready_payloads[protocol]["payload"] = handle_create_payload( + entity, author_user, protocol, parent_user=parent_user, + ) + payload = copy.copy(ready_payloads[protocol]["payload"]) if public: payload["to"] = [NAMESPACE_PUBLIC] payload["cc"] = [fid] @@ -154,13 +160,13 @@ def handle_send( payload["to"] = [fid] if isinstance(payload.get("object"), dict): payload["object"]["to"] = [fid] - payload = json.dumps(payload).encode("utf-8") + rendered_payload = json.dumps(payload).encode("utf-8") except Exception as ex: logger.error("handle_send - failed to generate payload for %s, %s: %s", fid, endpoint, ex) continue payloads.append({ "auth": get_http_authentication(author_user.rsa_private_key, f"{author_user.id}#main-key"), - "payload": payload, + "payload": rendered_payload, "content_type": 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"', "urls": {endpoint}, }) @@ -168,15 +174,15 @@ def handle_send( if public: if public_key: raise ValueError("handle_send - Diaspora recipient cannot be public and use encrypted delivery") - if not public_payloads[protocol]["payload"]: + if not ready_payloads[protocol]["payload"]: try: # noinspection PyTypeChecker - public_payloads[protocol]["payload"] = handle_create_payload( + ready_payloads[protocol]["payload"] = handle_create_payload( entity, author_user, protocol, parent_user=parent_user, ) except Exception as ex: logger.error("handle_send - failed to generate public payload for %s: %s", endpoint, ex) - public_payloads["diaspora"]["urls"].add(endpoint) + ready_payloads["diaspora"]["urls"].add(endpoint) else: if not public_key: raise ValueError("handle_send - Diaspora recipient cannot be private without a public key for " @@ -195,9 +201,9 @@ def handle_send( }) # Add public diaspora payload - if public_payloads["diaspora"]["payload"]: + if ready_payloads["diaspora"]["payload"]: payloads.append({ - "urls": public_payloads["diaspora"]["urls"], "payload": public_payloads["diaspora"]["payload"], + "urls": ready_payloads["diaspora"]["urls"], "payload": ready_payloads["diaspora"]["payload"], "content_type": "application/magic-envelope+xml", "auth": None, })