-
Notifications
You must be signed in to change notification settings - Fork 3
Content resolve batching #4499
Description
Reason for the Feature
Current API and implementations are resolving content one by one. public Promise<IContentObject> GetContent(string contentId, Type contentType, string manifestID = "") contains multiple checks that are done each time the content is requested and easily could be done once before processing chunk of contents. That could be improved if instead of that the approach would be focused more about resolving content in chunks. It would be also useful if new approach would also allow specifying policy for retrying requests for content if one fail.
Proposed Solution/Requirements
New API for IContentApi that will be focused around resolving content refs arrays. It should also allow specifying policy for retrying the failed request.
Alternatives or Workarounds
I've created helper methods and class that solves part of the problem (retrying) and uses current API, but that does not solve checks done in GetContent. Code:
using Beamable.Common;
using Beamable.Common.Content;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace Beamable.Runtime.Content
{
public class ContentResolveResult
{
public bool Success => contentObject != null;
public IContentObject contentObject;
public IContentRef contentRef;
}
public static class ManifestResolveResult
{
/// <summary>
/// Returns a list of failed content refs.
/// </summary>
/// <param name="results"></param>
/// <returns></returns>
public static List<IContentRef> GetFailedContentRefs(this IList<ContentResolveResult> results)
{
var count = results.Count;
var failed = new List<IContentRef>();
for (int i = 0; i < count; i++)
{
if (!results[i].Success)
{
failed.Add(results[i].contentRef);
}
}
return failed;
}
/// <summary>
/// Returns a list of content objects that were successfully resolved.
/// </summary>
/// <param name="results"></param>
/// <returns></returns>
public static List<IContentObject> GetValidContent(this IList<ContentResolveResult> results)
{
var count = results.Count;
var valid = new List<IContentObject>();
for (int i = 0; i < count; i++)
{
if (results[i].Success)
{
valid.Add(results[i].contentObject);
}
}
return valid;
}
/// <summary>
/// Checks if all content resolve results are successful.
/// </summary>
/// <param name="results"></param>
/// <returns></returns>
public static bool AllContentResolved(this IList<ContentResolveResult> results)
{
var size = results.Count;
for (int i = 0; i < size; i++)
{
if (!results[i].Success)
{
return false;
}
}
return true;
}
public static SequencePromise<ContentResolveResult> ResolveNew(this ClientManifest set,
int batchSize = 50, int maxAttemptsPerContent = 5)
{
return set.entries.ResolveNew(batchSize, maxAttemptsPerContent);
}
public static SequencePromise<ContentResolveResult> ResolveNew(this IEnumerable<ClientContentInfo> set,
int batchSize = 50, int maxAttemptsPerContent = 5)
{
return set.ToContentRefs().ResolveNew(batchSize, maxAttemptsPerContent);
}
public static SequencePromise<ContentResolveResult> ResolveNew(this IEnumerable<IContentRef> refs, int batchSize = 50, int maxAttemptsPerContent = 5)
{
var theRefs = refs.ToList();
SequencePromise<ContentResolveResult> seqPromise = null;
var promise = BeamContext.Default.Instance.Map(api =>
{
var contentApi = api.Content;
var promiseGenerators =
theRefs.Select(r => new Func<Promise<ContentResolveResult>>(() =>
{
return Promise.RetryPromise(() => contentApi.GetContent(r).Map(c => new ContentResolveResult(){contentObject = c, contentRef = r}), (ex) =>
{
Debug.LogWarning($"[Content] Failed to resolve {r.GetId()}, retrying");
return true;
}, maxAttempts: maxAttemptsPerContent).Recover(_ =>
{
Debug.LogWarning($"[Content] Permanently failed to resolve {r.GetId()} after {maxAttemptsPerContent} tries.");
return new ContentResolveResult {contentObject = null, contentRef = r};
});
})).ToList();
seqPromise = contentApi.ConvertPromisesIntoSequence(batchSize, promiseGenerators);
return seqPromise;
});
return promise.FlatMap<SequencePromise<ContentResolveResult>, IList<ContentResolveResult>>(_ => seqPromise, () => seqPromise);
}
}
}