Skip to content

Adds visible Content Manager progress during realm sync/revert operations and improves content download resilience for large syncs.#4604

Closed
mhijaziB wants to merge 4 commits into
mainfrom
feature/ssl-client-fix
Closed

Adds visible Content Manager progress during realm sync/revert operations and improves content download resilience for large syncs.#4604
mhijaziB wants to merge 4 commits into
mainfrom
feature/ssl-client-fix

Conversation

@mhijaziB
Copy link
Copy Markdown
Contributor

@mhijaziB mhijaziB commented May 18, 2026

Ticket

Fixes #4605

Hubspot ticket

Brief Description

Details

Added an in-window Content Manager progress display for sync/revert flows.

Shows operation title, processed item count, total item count, progress bar fill, and current content item name.
Keeps sync progress inside the Content Manager instead of relying on Unity’s modal progress UI.
Disables revert/action buttons while a sync operation is active to avoid overlapping operations.
Uses delayed window-state transitions to avoid IMGUI layout mismatches during repaint.
Improved CLI content download behavior during content sync.

Added a dedicated content-file download path in ContentService.
Uses a shared HttpClient for content file downloads instead of routing every CDN file through the generic verbose CLI requester.
Adds transient retry handling with jittered backoff for socket resets, timeouts, and retryable HTTP responses.
Removes per-file verbose “Downloading content with id...” sync log noise from the Unity CLI Debugger.
Keeps large syncs faster and cleaner while avoiding the SSL/socket reset failures seen during high-content-count tests.
Added sync download concurrency control.

New CLI flag: --download-max-parallel-count
New Unity content configuration field: EditorSyncDownloadMaxParallelCount
The editor passes this value into content sync when configured.
CLI default is 64.
Unity unset means the CLI default is used.
0 means unbounded parallelism.

Tests:

  • stress testing with 2k+ content amount
  • confirmed consistently that no SSL/socket reset failures are happening

Checklist

  • Have you added appropriate text to the CHANGELOG.md files?

Add those to list or remove the list below altogether:

Notes

When you are merging a feature branch into main, please squash merge and make sure the final commit contains any relevent JIRA ticket number. If you are merging from main to staging, or staging to production, please use a regular merge commit.

Does this introduce tech-debt? If so, have you added an entry to the Tech-debt document?

Move the ambient microservice log-level AsyncLocal into beamable.tooling.common so AdminRoutes and runtime logging share the same context without introducing a runtime dependency.

Scope OpenAPI docs generation to temporarily raise the log threshold to Warning and restore the previous level afterward. Preserve the existing MicroserviceBootstrapper.ContextLogLevel API as an obsolete forwarder for compatibility.
- SSL error fix during content sync
- updated changelog
@mhijaziB mhijaziB self-assigned this May 18, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Lightbeam link

Copy link
Copy Markdown
Contributor

@allister-beamable allister-beamable left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's be very careful about separation of concerns here. I see four mostly-unrelated categories of changes, three beneficial and one probably by mistake:

  • reuse HttpClients for content downloads and add configurable parallelism
  • add a rich progress bar to content downloading in Unity Editor
  • suppress overly verbose logs
  • remove OTel-related setup in BeamableMicroservice.cs

The first two seem fine to bundle together as a single feature addition. The third and fourth are a little more dubious. I think the third has value but might not belong here, and the fourth is either a mistake or a diff-unfriendly way of making changes. I would like to see some discussion of the latter two before this lands on main.

Comment on lines +87 to +90
// Suppress info-level logs during OpenAPI doc generation to avoid noise
//Wrapping with try/finally to ensure log level is restored even if an exception occurs
var previousLogLevel = MicroserviceLogLevelContext.CurrentLogLevel.Value;
MicroserviceLogLevelContext.CurrentLogLevel.Value = LogLevel.Warning;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to introduce stuff that is not directly germane to the content downloading part. Are we okay with that?

I think my vote is "yes, this is an okay digression" as the value of this addition outweighs the bad smell (violation of "separation of concerns"), but I think it is worth making the decision consciously.

Comment on lines -799 to -840

MicroserviceRequestContext ctx = null;
using (var requestActivity = _activityProvider.Create(Otel.TRACE_CONSTRUCT_CTX, importance: TelemetryImportance.VERBOSE))
{
if (!document.TryBuildRequestContext(InstanceArgs, out ctx))
{
requestActivity.SetStatus(ActivityStatusCode.Error);
Log.Debug("WS Message contains no data. Cannot handle. Skipping message.");
return;
}
requestActivity.SetStatus(ActivityStatusCode.Ok);
}

BeamActivity parentActivity = BeamActivity.Noop;
if (_socketRequesterContext.TryGetListener(ctx.Id, out var existingListener))
{
parentActivity = existingListener.Activity;
}


using var activity = _activityProvider.Create(Otel.TRACE_WS, parentActivity);
MicroserviceRequester.ContextActivity.Value = activity;
ctx.ActivityContext = activity;


var extraOtelTags = _telemetryProviders.CreateRequestAttributes(InstanceArgs, ctx, ConnectionId);
activity.SetTags(extraOtelTags);


// First get the Global Realm Config Log Level and apply it by running UpdateLogLevel
var configService = InstanceArgs.ServiceScope.GetService<IRealmConfigService>();
if (ctx.Path?.StartsWith(_adminPrefix) ?? false)
{
// when the path starts with admin, use warning.
MicroserviceBootstrapper.ContextLogLevel.Value = LogLevel.Warning;
}
else
{
// otherwise, allow default behaviour.
configService.UpdateLogLevel();
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This removal looks wrong. Did your branch get cross-contaminated with another branch that is dealing with OTel stuff @mhijaziB ?

Copy link
Copy Markdown
Contributor

@DiasAtBeamable DiasAtBeamable left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Editor and Content changes seems fine for me. I just added a few comments to check a few things. Also, I think that it may have some files commited by mistake as they are not related to the content manager change.

/// <remarks>
/// Returning the default int omits the CLI argument, allowing the CLI default to apply. A configured value of 0 maps to -1 so the generated command includes an explicit unbounded setting.
/// </remarks>
private int GetEditorSyncDownloadMaxParallelCountCliOption()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need to set it to -1? The CLI can accept 0 the same way it would handle -1. Also, you can use _contentConfiguration.EditorSyncDownloadMaxParallelCount.GetOrElse(0);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, the CLI default is 64. Shouldn't we use that value as the default for Unity as well? What are the benefits of having Unity use unbounded by default?

var docs = new ServiceDocGenerator();
var ctx = GlobalProvider.GetService<StartupContext>();
var doc = docs.Generate(ctx, GlobalProvider);
// Suppress info-level logs during OpenAPI doc generation to avoid noise
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is related to the Content Manager changes. Are you sure about adding this change to this PR?

DEFAULT, STRUCTURED, UNSTRUCTURED, FILE, STRUCTURED_AND_FILE
}

public enum AdminRouteLogPolicy
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, seems unrelated to the ContentManager changes.

{
throw new NotImplementedException();
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, seems unrelated to the ContentManager changes.

{
public static class MicroserviceLogLevelContext
{
public static readonly AsyncLocal<LogLevel> CurrentLogLevel = new();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, seems unrelated to the ContentManager changes.

{
LogUtil.TryParseSystemLogLevel(configuredArgs.OapiGenLogLevel, out var defaultLevel, LogLevel.Information);
MicroserviceBootstrapper.ContextLogLevel.Value = defaultLevel;
MicroserviceLogLevelContext.CurrentLogLevel.Value = defaultLevel;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, seems unrelated to the ContentManager changes.

@@ -0,0 +1,226 @@
using Beamable.Api.Autogenerated.Models;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, seems unrelated to the ContentManager changes.

}
}

public class RecordingTelemetryAttributeProvider : ITelemetryAttributeProvider
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, seems unrelated to the ContentManager changes.

{
if (exception is RequesterException requesterException)
{
return requesterException.Status is 408 or 429 || requesterException.Status >= 500 && requesterException.Status < 600;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add comments here on why we consider those requester status code errors transient?

{
genBeamCommandArgs.Add(("--target=" + this.target));
}
// If the downloadMaxParallelCount value was not default, then add it to the list of args.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this autogenerated?

@mhijaziB mhijaziB marked this pull request as draft May 18, 2026 18:16
@mhijaziB
Copy link
Copy Markdown
Contributor Author

I have converted this to a draft-PR. Git has pushed changes were not supposed to appear here! I was wondering about the OTEL talk here! and now i know why!

@mhijaziB mhijaziB closed this May 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SSL/socket failures when syncing high number of contents

3 participants