Conversation
| (bool isRevOnServer, int serverRevCount, string serverLastRev) = CheckPushRevisionOnServer(pushRepo, localTip); | ||
| if (!isRevOnServer) | ||
| { | ||
| throw new InvalidOperationException( | ||
| $"Push verification failed for project PT ID {pushRepo.SendReceiveId.Id}. " | ||
| + $"Expected revision {localTip} was not found in the server's revision history. " | ||
| + $"Server has {serverRevCount} revisions. Last server revision: {serverLastRev}. " | ||
| ); | ||
| } | ||
|
|
||
| _hgWrapper.MarkSharedChangeSetsPublic(repositoryPath); |
There was a problem hiding this comment.
🚩 Push verification failure prevents MarkSharedChangeSetsPublic from running
In the new Push method at src/SIL.XForge.Scripture/Services/JwtInternetSharedRepositorySource.cs:143-153, if CheckIfRevisionIsOnServer returns false (or throws an unexpected exception like a network error or JSON parse failure), the InvalidOperationException is thrown and MarkSharedChangeSetsPublic at line 153 is never called. This is a behavioral change from the old code where MarkSharedChangeSetsPublic was always called after a successful PostStreaming. If the push actually succeeded on the server but verification produces a false negative (e.g. eventual consistency delay, unexpected JSON structure, transient network issue), the local changesets remain in 'draft' phase, causing the next sync to re-bundle and re-push the same data. This appears to be the intended design (fail loudly so the issue can be investigated), but the risk of false negatives from CheckIfRevisionIsOnServer should be considered — especially since the projrevhist endpoint is queried immediately after the push completes.
Was this helpful? React with 👍 or 👎 to provide feedback.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #3778 +/- ##
==========================================
- Coverage 81.30% 81.27% -0.03%
==========================================
Files 622 622
Lines 39273 39322 +49
Branches 6407 6415 +8
==========================================
+ Hits 31929 31958 +29
- Misses 6350 6366 +16
- Partials 994 998 +4 ☔ View full report in Codecov by Sentry. |
758623f to
e1401a2
Compare
| internal (bool isRevOnServer, int serverRevCount, string? serverLastRev) CheckIfRevisionIsOnServer( | ||
| SharedRepository serverRepo, | ||
| string expectedRevision | ||
| ) | ||
| { | ||
| string projRevHistResponse = GetClient() | ||
| .Get("projrevhist", "proj", serverRepo.ScrTextName, "projid", serverRepo.SendReceiveId.Id, "all", "1"); | ||
|
|
||
| JObject jsonResult = JObject.Parse(projRevHistResponse); | ||
| JArray? revisions = jsonResult["project"]?["revision_history"]?["revisions"] as JArray; | ||
|
|
||
| bool isRevOnServer = | ||
| revisions?.Any(r => string.Equals(r["id"]?.ToString(), expectedRevision, StringComparison.Ordinal)) | ||
| ?? false; | ||
| int serverRevCount = revisions?.Count ?? 0; | ||
| string? serverLastRev = GetFirstElementId(revisions); | ||
|
|
||
| return (isRevOnServer, serverRevCount, serverLastRev); | ||
| } |
There was a problem hiding this comment.
🚩 Verification assumes specific JSON structure from projrevhist API
The CheckIfRevisionIsOnServer method at line 156 assumes the projrevhist API returns JSON with the structure {"project":{"revision_history":{"revisions":[{"id":"..."},...]}}}. If the actual API response differs (e.g., different field names or nesting), the as JArray cast at line 165 would return null, causing isRevOnServer to be false and the push to be incorrectly reported as failed. Since this is calling an external Paratext server API, the assumed structure should be validated against actual API documentation or integration tests. The unit tests only verify the parsing logic with the assumed structure.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
I wonder if it would be useful to log a warning if revisions is null, as if the API changes (or fails), as it might help to diagnose the issue. Maybe even logging the JSON to the console in this case?
There was a problem hiding this comment.
Good thinking. How's this? Unfortunately these aren't included in SyncMetrics. (And I think couldn't without more elaborate enhancements.)
Hmm; it occurs to me that if the json output does include the revisions, but they are in a different format than we expect, logging the JSON might include thousands of revision IDs :). I adjusted the logging so it should truncate the output.
pmachapman
left a comment
There was a problem hiding this comment.
@pmachapman reviewed 6 files and all commit messages, made 4 comments, and resolved 1 discussion.
Reviewable status: all files reviewed, 3 unresolved discussions (waiting on marksvc).
src/SIL.XForge.Scripture/Services/HgWrapper.cs line 29 at r2 (raw file):
} public string[] Pull(string repositoryPath, byte[] bundle)
I would argue we should keep the old parameter names, as these are what is used by Paratext. I think when we just exposing a PT method, we should keep method and parameter naming as close as possible to PT?
I'm not marking this as blocking, as if you feel strongly about renaming these, that's OK - I just thought I'd raise this.
Code quote:
public string[] Pull(string repositoryPath, byte[] bundle)src/SIL.XForge.Scripture/Services/HgWrapper.cs line 89 at r2 (raw file):
/// Mark all changesets as public. /// </summary> public void MarkSharedChangeSetsPublic(string repositoryPath) => RunCommand(repositoryPath, "phase -p -r 'tip'");
Can please we keep the codedoc param comment?
/// <param name="repositoryPath">The path to the repository.</param>
Visual Studio's intellisense uses these.
Code quote:
public void MarkSharedChangeSetsPublic(string repositoryPath)src/SIL.XForge.Scripture/Services/IHgWrapper.cs line 18 at r2 (raw file):
void MarkSharedChangeSetsPublic(string repositoryPath); string[] Pull(string repositoryPath, byte[] bundle); byte[] Bundle(string repositoryPath, params string[] baseRevisions);
I think for consistency these should all be either string repository or string repositoryPath. I would argue for string repository to keep consistency with Paratext (and so your PR doesn't change existing methods it doesn't need to)
Code quote:
void Init(string repository);
void Update(string repository);
void Update(string repositoryPath, string rev);
void BackupRepository(string repository, string backupFile);
void RestoreRepository(string destination, string backupFile);
string GetLastPublicRevision(string repository);
string GetRepoRevision(string repositoryPath);
void MarkSharedChangeSetsPublic(string repositoryPath);
string[] Pull(string repositoryPath, byte[] bundle);
byte[] Bundle(string repositoryPath, params string[] baseRevisions);| internal (bool isRevOnServer, int serverRevCount, string? serverLastRev) CheckIfRevisionIsOnServer( | ||
| SharedRepository serverRepo, | ||
| string expectedRevision | ||
| ) | ||
| { | ||
| string projRevHistResponse = GetClient() | ||
| .Get("projrevhist", "proj", serverRepo.ScrTextName, "projid", serverRepo.SendReceiveId.Id, "all", "1"); | ||
|
|
||
| JObject jsonResult = JObject.Parse(projRevHistResponse); | ||
| JArray? revisions = jsonResult["project"]?["revision_history"]?["revisions"] as JArray; | ||
|
|
||
| bool isRevOnServer = | ||
| revisions?.Any(r => string.Equals(r["id"]?.ToString(), expectedRevision, StringComparison.Ordinal)) | ||
| ?? false; | ||
| int serverRevCount = revisions?.Count ?? 0; | ||
| string? serverLastRev = GetFirstElementId(revisions); | ||
|
|
||
| return (isRevOnServer, serverRevCount, serverLastRev); | ||
| } |
There was a problem hiding this comment.
I wonder if it would be useful to log a warning if revisions is null, as if the API changes (or fails), as it might help to diagnose the issue. Maybe even logging the JSON to the console in this case?
e1401a2 to
20ecdf0
Compare
marksvc
left a comment
There was a problem hiding this comment.
@marksvc made 3 comments.
Reviewable status: all files reviewed, 3 unresolved discussions (waiting on pmachapman).
src/SIL.XForge.Scripture/Services/HgWrapper.cs line 89 at r2 (raw file):
Previously, pmachapman (Peter Chapman) wrote…
Can please we keep the codedoc param comment?
/// <param name="repositoryPath">The path to the repository.</param>Visual Studio's intellisense uses these.
Oh; yes. Please bring to my attention other places where I'm pruning too much as well.
src/SIL.XForge.Scripture/Services/IHgWrapper.cs line 18 at r2 (raw file):
Previously, pmachapman (Peter Chapman) wrote…
I think for consistency these should all be either
string repositoryorstring repositoryPath. I would argue forstring repositoryto keep consistency with Paratext (and so your PR doesn't change existing methods it doesn't need to)
Done.
| internal (bool isRevOnServer, int serverRevCount, string? serverLastRev) CheckIfRevisionIsOnServer( | ||
| SharedRepository serverRepo, | ||
| string expectedRevision | ||
| ) | ||
| { | ||
| string projRevHistResponse = GetClient() | ||
| .Get("projrevhist", "proj", serverRepo.ScrTextName, "projid", serverRepo.SendReceiveId.Id, "all", "1"); | ||
|
|
||
| JObject jsonResult = JObject.Parse(projRevHistResponse); | ||
| JArray? revisions = jsonResult["project"]?["revision_history"]?["revisions"] as JArray; | ||
|
|
||
| bool isRevOnServer = | ||
| revisions?.Any(r => string.Equals(r["id"]?.ToString(), expectedRevision, StringComparison.Ordinal)) | ||
| ?? false; | ||
| int serverRevCount = revisions?.Count ?? 0; | ||
| string? serverLastRev = GetFirstElementId(revisions); | ||
|
|
||
| return (isRevOnServer, serverRevCount, serverLastRev); | ||
| } |
There was a problem hiding this comment.
Good thinking. How's this? Unfortunately these aren't included in SyncMetrics. (And I think couldn't without more elaborate enhancements.)
Hmm; it occurs to me that if the json output does include the revisions, but they are in a different format than we expect, logging the JSON might include thousands of revision IDs :). I adjusted the logging so it should truncate the output.
pmachapman
left a comment
There was a problem hiding this comment.
@pmachapman reviewed 4 files and all commit messages, made 1 comment, and resolved 3 discussions.
Reviewable status:complete! all files reviewed, all discussions resolved (waiting on marksvc).
- When pushing a bundle to the PT SendReceive server, we can incorrectly think we gave commits to the server and that they were retained. This leads to additional problems later when pushing again, because the server doesn't have a commit that we are expecting it to have. - This patch checks if the PT SendReceive server has the commit we intended to push, and fails the sync if not. - The outgoing bundle size is also logged for help in investigating problems. - This patch does not fix the situation where pushing a commit to the SR server doesn't work. But it should prevent SF from (1) incorrectly communicating to the user that the sync succeeded, and (2) getting into a state where the SF project is stuck and can not sync without manual intervention. The SF project is left in a state where the sync can at least be re-tried.
20ecdf0 to
9f04e10
Compare
incorrectly think we gave commits to the server and that they were
retained. This leads to additional problems later when pushing again,
because the server doesn't have a commit that we are expecting it to
have.
intended to push, and fails the sync if not.
problems.
SR server doesn't work. But it should prevent SF from (1) incorrectly
communicating to the user that the sync succeeded, and (2) getting
into a state where the SF project is stuck and can not sync without
manual intervention. The SF project is left in a state where the sync
can at least be re-tried.
This change is