zilencer: Lock the RemoteZulipServer row when inserting data.

This does not ensure that we do not mix data from multiple servers
sharing a UUID -- if one has more `RemoteRealmCount` rows,
and the other has more `RemoteInstalltionCount` rows, the end result
will still be some rows from each server, across the two tables.

It does ensure that we will not alternate rows between two servers
if both requests are processed at the same time.

It also causes submissions to be all-or-nothing in the event of
integrity errors.  This is not necessarily beneficial, as forward
progress is generally useful -- but the integrity errors are resolved
in the subsequent commit.
This commit is contained in:
Alex Vandiver 2023-11-21 16:55:46 +00:00 committed by Tim Abbott
parent ae836ae007
commit c6ae3e7242
1 changed files with 5 additions and 0 deletions

View File

@ -617,6 +617,7 @@ class InstallationCountDataForAnalytics(BaseModel):
@typed_endpoint @typed_endpoint
@transaction.atomic
def remote_server_post_analytics( def remote_server_post_analytics(
request: HttpRequest, request: HttpRequest,
server: RemoteZulipServer, server: RemoteZulipServer,
@ -627,6 +628,10 @@ def remote_server_post_analytics(
realms: Optional[Json[List[RealmDataForAnalytics]]] = None, realms: Optional[Json[List[RealmDataForAnalytics]]] = None,
version: Optional[Json[str]] = None, version: Optional[Json[str]] = None,
) -> HttpResponse: ) -> HttpResponse:
# Lock the server, preventing this from racing with other
# duplicate submissions of the data
server = RemoteZulipServer.objects.select_for_update().get(id=server.id)
if version is not None: if version is not None:
version = version[0 : RemoteZulipServer.VERSION_MAX_LENGTH] version = version[0 : RemoteZulipServer.VERSION_MAX_LENGTH]
if version != server.last_version: if version != server.last_version: