Skip to content

Content resolve batching #4499

@Leinnan

Description

@Leinnan

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);
		}
	}
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions