Skip to content

Commit 882918e

Browse files
committed
drafting metrics: record SF draft generation request id to ease matching
1 parent 0b0bae6 commit 882918e

File tree

6 files changed

+138
-36
lines changed

6 files changed

+138
-36
lines changed

src/SIL.XForge.Scripture/Services/IMachineApiService.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,13 +146,11 @@ CancellationToken cancellationToken
146146
Task<LanguageDto> IsLanguageSupportedAsync(string languageCode, CancellationToken cancellationToken);
147147

148148
[Mutex]
149-
[LogEventMetric(EventScope.Drafting, projectId: nameof(sfProjectId), captureReturnValue: true)]
150149
Task<string> RetrievePreTranslationStatusAsync(string sfProjectId, CancellationToken cancellationToken);
151150

152151
[LogEventMetric(EventScope.Drafting, nameof(curUserId), nameof(sfProjectId))]
153152
Task StartBuildAsync(string curUserId, string sfProjectId, CancellationToken cancellationToken);
154153

155-
[LogEventMetric(EventScope.Drafting, nameof(curUserId), projectId: "buildConfig.ProjectId")]
156154
Task StartPreTranslationBuildAsync(string curUserId, BuildConfig buildConfig, CancellationToken cancellationToken);
157155
Task TrainSegmentAsync(
158156
string curUserId,

src/SIL.XForge.Scripture/Services/IMachineProjectService.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ Task BuildProjectForBackgroundJobAsync(
1515
string curUserId,
1616
BuildConfig buildConfig,
1717
bool preTranslate,
18-
CancellationToken cancellationToken
18+
CancellationToken cancellationToken,
19+
string? draftGenerationRequestId
1920
);
2021
Task<string> GetProjectZipAsync(string sfProjectId, Stream outputStream, CancellationToken cancellationToken);
2122
Task RemoveProjectAsync(string sfProjectId, bool preTranslate, CancellationToken cancellationToken);

src/SIL.XForge.Scripture/Services/MachineApiService.cs

Lines changed: 91 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -773,6 +773,13 @@ public async Task ExecuteWebhookAsync(string json, string signature)
773773
{ "event", delivery.Event },
774774
{ "translationEngineId", delivery.Payload.Engine.Id },
775775
};
776+
777+
string? draftGenerationRequestId = await GetDraftGenerationRequestIdForBuildAsync(buildId);
778+
if (!string.IsNullOrEmpty(draftGenerationRequestId))
779+
{
780+
arguments["draftGenerationRequestId"] = draftGenerationRequestId;
781+
}
782+
776783
await eventMetricService.SaveEventMetricAsync(
777784
projectId,
778785
userId: null,
@@ -1750,6 +1757,9 @@ public async Task<LanguageDto> IsLanguageSupportedAsync(string languageCode, Can
17501757
CancellationToken cancellationToken
17511758
)
17521759
{
1760+
string? buildId = null;
1761+
Exception? caughtException = null;
1762+
17531763
try
17541764
{
17551765
// Record the SF Project id to help with debugging
@@ -1779,6 +1789,8 @@ await translationEnginesClient.GetAllBuildsAsync(translationEngineId, cancellati
17791789
.Where(b => b.State == JobState.Completed)
17801790
.MaxBy(b => b.DateFinished);
17811791

1792+
buildId = translationBuild?.Id;
1793+
17821794
// Set the retrieved flag as in progress
17831795
await projectSecrets.UpdateAsync(
17841796
sfProjectId,
@@ -1802,21 +1814,15 @@ await projectSecrets.UpdateAsync(
18021814
// Notify any SignalR clients subscribed to the project
18031815
await hubContext.NotifyBuildProgress(
18041816
sfProjectId,
1805-
new ServalBuildState
1806-
{
1807-
BuildId = translationBuild?.Id,
1808-
State = nameof(ServalData.PreTranslationsRetrieved),
1809-
}
1817+
new ServalBuildState { BuildId = buildId, State = nameof(ServalData.PreTranslationsRetrieved) }
18101818
);
1811-
1812-
// Return the build id
1813-
return translationBuild?.Id;
18141819
}
18151820
}
18161821
catch (TaskCanceledException e) when (e.InnerException is not TimeoutException)
18171822
{
18181823
// Do not log error - the job was cancelled
18191824
// Exclude TaskCanceledException with an inner TimeoutException, as this generated by an HttpClient timeout
1825+
caughtException = e;
18201826

18211827
// Ensure that the retrieved flag is cleared
18221828
await projectSecrets.UpdateAsync(
@@ -1827,6 +1833,8 @@ await projectSecrets.UpdateAsync(
18271833
}
18281834
catch (Exception e)
18291835
{
1836+
caughtException = e;
1837+
18301838
// Log the error and report to bugsnag
18311839
const string message =
18321840
"Retrieve pre-translation status exception occurred for project {sfProjectId} running in background job.";
@@ -1847,8 +1855,34 @@ await projectSecrets.UpdateAsync(
18471855
throw;
18481856
}
18491857
}
1858+
finally
1859+
{
1860+
var payload = new Dictionary<string, object> { { "sfProjectId", sfProjectId } };
1861+
if (buildId is not null)
1862+
{
1863+
string? draftGenerationRequestId = await GetDraftGenerationRequestIdForBuildAsync(buildId);
1864+
if (!string.IsNullOrEmpty(draftGenerationRequestId))
1865+
{
1866+
payload["draftGenerationRequestId"] = draftGenerationRequestId;
1867+
}
1868+
}
1869+
if (!string.IsNullOrEmpty(buildId))
1870+
{
1871+
payload["buildId"] = buildId;
1872+
}
18501873

1851-
return null;
1874+
await eventMetricService.SaveEventMetricAsync(
1875+
sfProjectId,
1876+
userId: null,
1877+
eventType: nameof(RetrievePreTranslationStatusAsync),
1878+
EventScope.Drafting,
1879+
payload,
1880+
result: buildId,
1881+
exception: caughtException
1882+
);
1883+
}
1884+
1885+
return buildId;
18521886
}
18531887

18541888
public async Task StartBuildAsync(string curUserId, string sfProjectId, CancellationToken cancellationToken)
@@ -1879,7 +1913,8 @@ public async Task StartBuildAsync(string curUserId, string sfProjectId, Cancella
18791913
curUserId,
18801914
new BuildConfig { ProjectId = sfProjectId },
18811915
false,
1882-
CancellationToken.None
1916+
CancellationToken.None,
1917+
null
18831918
),
18841919
null,
18851920
JobContinuationOptions.OnAnyFinishedState
@@ -1904,6 +1939,22 @@ public async Task StartPreTranslationBuildAsync(
19041939
CancellationToken cancellationToken
19051940
)
19061941
{
1942+
// SF-specific id of the draft generation request
1943+
string draftGenerationRequestId = ObjectId.GenerateNewId().ToString();
1944+
1945+
await eventMetricService.SaveEventMetricAsync(
1946+
buildConfig.ProjectId,
1947+
curUserId,
1948+
eventType: nameof(StartPreTranslationBuildAsync),
1949+
EventScope.Drafting,
1950+
new Dictionary<string, object>
1951+
{
1952+
{ "curUserId", curUserId },
1953+
{ "buildConfig", buildConfig },
1954+
{ "draftGenerationRequestId", draftGenerationRequestId },
1955+
}
1956+
);
1957+
19071958
// Load the project from the realtime service
19081959
await using IConnection conn = await realtimeService.ConnectAsync(curUserId);
19091960
IDocument<SFProject> projectDoc = await conn.FetchAsync<SFProject>(buildConfig.ProjectId);
@@ -2018,7 +2069,14 @@ await projectDoc.SubmitJson0OpAsync(op =>
20182069
// so that the interceptor functions for BuildProjectAsync().
20192070
jobId = backgroundJobClient.ContinueJobWith<MachineProjectService>(
20202071
jobId,
2021-
r => r.BuildProjectForBackgroundJobAsync(curUserId, buildConfig, true, CancellationToken.None)
2072+
r =>
2073+
r.BuildProjectForBackgroundJobAsync(
2074+
curUserId,
2075+
buildConfig,
2076+
true,
2077+
CancellationToken.None,
2078+
draftGenerationRequestId
2079+
)
20222080
);
20232081

20242082
// Set the pre-translation queued date and time, and hang fire job id
@@ -2586,6 +2644,28 @@ CancellationToken cancellationToken
25862644
return project;
25872645
}
25882646

2647+
/// <summary>
2648+
/// Gets the SF-specific draft generation request identifier for a build by looking up the BuildProjectAsync event.
2649+
/// </summary>
2650+
/// <param name="buildId">The Serval build identifier.</param>
2651+
/// <returns>The draft generation request identifier, or null if not found.</returns>
2652+
private async Task<string?> GetDraftGenerationRequestIdForBuildAsync(string buildId)
2653+
{
2654+
// BuildProjectAsync events serve as a record of what Serval build id corresponds to what draft generation
2655+
// request id.
2656+
QueryResults<EventMetric> buildProjectEvents = await eventMetricService.GetEventMetricsAsync(
2657+
projectId: null,
2658+
scopes: [EventScope.Drafting],
2659+
eventTypes: [nameof(MachineProjectService.BuildProjectAsync)]
2660+
);
2661+
EventMetric? buildEvent = buildProjectEvents.Results.FirstOrDefault(e => e.Result?.ToString() == buildId);
2662+
if (buildEvent?.Payload.TryGetValue("draftGenerationRequestId", out BsonValue? requestId) == true)
2663+
{
2664+
return requestId.ToString();
2665+
}
2666+
return null;
2667+
}
2668+
25892669
private async Task<string> GetTranslationIdAsync(
25902670
string sfProjectId,
25912671
bool preTranslate,

src/SIL.XForge.Scripture/Services/MachineProjectService.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ public async Task<string> AddSmtProjectAsync(string sfProjectId, CancellationTok
107107
/// <param name="buildConfig">The build configuration.</param>
108108
/// <param name="preTranslate">If <c>true</c> use NMT; otherwise if <c>false</c> use SMT.</param>
109109
/// <param name="cancellationToken">The cancellation token.</param>
110+
/// <param name="draftGenerationRequestId">The SF-specific NMT draft generation request id. (Or null when SMT.)</param>
110111
/// <returns>An asynchronous task.</returns>
111112
/// <remarks>
112113
/// This cannot be run multiple times in different threads.
@@ -115,12 +116,13 @@ public async Task BuildProjectForBackgroundJobAsync(
115116
string curUserId,
116117
BuildConfig buildConfig,
117118
bool preTranslate,
118-
CancellationToken cancellationToken
119+
CancellationToken cancellationToken,
120+
string? draftGenerationRequestId
119121
)
120122
{
121123
try
122124
{
123-
await BuildProjectAsync(curUserId, buildConfig, preTranslate, cancellationToken);
125+
await BuildProjectAsync(curUserId, buildConfig, preTranslate, cancellationToken, draftGenerationRequestId);
124126
}
125127
catch (TaskCanceledException e) when (e.InnerException is not TimeoutException)
126128
{
@@ -599,7 +601,8 @@ await projectDoc.SubmitJson0OpAsync(op =>
599601
/// <param name="buildConfig">The build configuration.</param>
600602
/// <param name="preTranslate">If <c>true</c> use NMT; otherwise if <c>false</c> use SMT.</param>
601603
/// <param name="cancellationToken">The cancellation token.</param>
602-
/// <returns>An asynchronous task.</returns>
604+
/// <param name="draftGenerationRequestId">The SF-specific draft generation request id</param>
605+
/// <returns>Serval build id</returns>
603606
/// <exception cref="DataNotFoundException">The project or project secret could not be found.</exception>
604607
/// <exception cref="InvalidDataException">The language of the source project was not specified.</exception>
605608
/// <remarks>
@@ -616,7 +619,8 @@ public virtual async Task<string> BuildProjectAsync(
616619
string curUserId,
617620
BuildConfig buildConfig,
618621
bool preTranslate,
619-
CancellationToken cancellationToken
622+
CancellationToken cancellationToken,
623+
string? draftGenerationRequestId
620624
)
621625
{
622626
// Load the target project secrets, so we can get the translation engine ID

src/SIL.XForge.Scripture/Services/SyncService.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,8 @@ await projectSecrets.UpdateAsync(
198198
syncConfig.UserId,
199199
new BuildConfig { ProjectId = syncConfig.ProjectId },
200200
false,
201-
CancellationToken.None
201+
CancellationToken.None,
202+
null
202203
),
203204
null,
204205
JobContinuationOptions.OnAnyFinishedState

0 commit comments

Comments
 (0)