sillsdev / sillsdev/hgresume

Larges pushes fail, because they take too long

Open
#11 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C#
Stars
0
Forks
1
Avg merge
3d 17h
Merged PRs (30d)
1

Description

We recently had a push of almost 2GB (surprisingly it only took about 3 min for the client to upload it).
Here's an overview of when things broken:
image

It failed on the last chunk, because that's when the server actually starts doing the heavy lifting: trying to apply the commit.

And here's what I think/know happened:

  • Chorus sends the last chunk
    • The resumable server detects that it's the last chunk
    • It calls unbundle
    • Which calls hg incoming, which takes ~3.5m (it gets logged to /var/cache/hgresume/<transaction-ID>.bundle.incoming.async_run)
  • Because the request takes so long
  • So, presumably the PHP script gets torn down while it's waiting for the hg incoming command to finish (which does finish, because it's in its own process)
  • Because the PHP script gets torn down, no work actually happens: it never gets to running the command hg unbundle and creating a lockfile for that command (A lockfile is created for hg incoming, but that's a seperate file)
  • Because the lockfile doesn't get created, when Chorus retries the push-bundle, the server throws an Exception

Do we want to allow big pushes like this? I think so! So how:

  • It's fine if Chorus times out as long as the job actually happens and we returns a more meaningful response. The code tries to return a 200, but fails, because the lockfile it's expecting doesn't exist. The exception is good, because a missing lockfile means nothing is happening.

So we either need to:

  1. Move more stuff into an external command that doesn't get torn down 🙁
  2. Prevent the PHP script from getting torn down (e.g. move large pushes to a Lexbox Job and make sure we turn off everything that might kill a long PHP script)
  3. Make the retries smarter and have them pick up where the last one died

I think 3 sounds like the best bet. Something like:

  • Replace the exception-throwing isComplete check, with something that anticipates this senario:
    • If there's no lock file retry the unbundle
    • In the unbundle, detect if hg incoming already ran and if so:
      • Do the necessary validation
      • Then start hg unbundle

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with HgResumeApi.php, especially the unbundle flow and isComplete check, then read HgRunner.php and AsyncRunner.php around incoming, unbundle, and lockfile handling. Review the Chorus timeout reference to understand the retry path. Done means a large push can retry after the request times out without an exception and the unbundle work completes or returns a meaningful response.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp, php
Domain
api, backend
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.