Skip to content

Conversation

@junaed-optimizely
Copy link
Contributor

Summary

  • Decision service change: propagate CMAB decisions through DecisionService, bucketer, decision metadata, and related core flows.
  • Impression event change: include CMAB variation IDs and UUIDs in impression event payloads.
  • Exposed CMAB configuration: add CmabConfig, factory setters, and initialization plumbing to pass cache size, TTL, and custom caches into CMAB service creation.

Test Plan

  • Extend CMAB-related coverage in service, factory, impression event, integration, and user-context test suites.

Issues

  • FSSDK-11177
  • FSSDK-11186

/// <param name="userId">User identifier</param>
/// <param name="trafficAllocations">Traffic allocations to use for bucketing</param>
/// <returns>Entity ID (string) if user is bucketed, null otherwise</returns>
public virtual Result<string> BucketToEntityId(ProjectConfig config, Experiment experiment,

Choose a reason for hiding this comment

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

We can accept ExperimentCore parameter, and reuse the part upto BucketToEntityId for the Bucket metheod below. Logic for bucket would be:

  1. get entityId by calling BucketToEntityId()
  2. get the variation from the config and return using the entityId from step 1


if (config.CustomCache != null)
{
_cmabCache = config.CustomCache as LruCache<CmabCacheEntry>;

Choose a reason for hiding this comment

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

we should not assume that customCache must be LruCache. It could be any implementation

Choose a reason for hiding this comment

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

the class field _cmabCache should also be of type ICache<>, not LruCache

/// <param name="cmabConfig">Configuration for CMAB cache. If null, default values are used.</param>
/// <param name="cmabClient">Client for fetching decisions from the CMAB prediction service. If null, a default client is created.</param>
/// <param name="logger">Optional logger for recording service operations.</param>
public DefaultCmabService(CmabConfig cmabConfig = null,

Choose a reason for hiding this comment

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

instead of taking in the config here, we can take in a ICache<> here and push the resposibility of supplying the cache to the caller, similar to cmabClient

Choose a reason for hiding this comment

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

I see all of them can be null. We can just make them required and put the construction responsibility to the caller

void Save(string key, T value);
T Lookup(string key);
void Reset();
void Remove(string key);

Choose a reason for hiding this comment

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

This is a breaking change. We cannot just add the remove method to the old Odp ICache interface. We need a new interface that has the same methods with the remove method added. We can extend this ICache like so
interface ICacheWithRemove extends ICache.

Also, it would be better to put the new extended interface in a separate common package instead of the odp package

/// <summary>
/// CMAB UUID associated with the decision for contextual multi-armed bandit experiments.
/// </summary>
public string CmabUuid { get; set; }

Choose a reason for hiding this comment

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

THe CmabUuid should not be in the DecisionReasons class

var userId = user.GetUserId();

// Check if CMAB is properly configured
if (experiment.Cmab == null)

Choose a reason for hiding this comment

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

can we get rid of this check, the caller can ensure this this is called only with cmab (in our case, it is done already)

new TrafficAllocation
{
EntityId = "$",
EndOfRange = experiment.Cmab.TrafficAllocation ?? 0,

Choose a reason for hiding this comment

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

can we just use experiment.Cmab.TrafficAllocation (without the ??)

}

// User is in CMAB traffic allocation, fetch decision from CMAB service
if (CmabService == null)

Choose a reason for hiding this comment

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

when the sdk is released with cmab, the cmabService must be non null cause we are instantiating it ourself, so no reason for it to be null. We can remove this check

NotificationCenter = notificationCenter ?? new NotificationCenter(Logger);

#if USE_CMAB
if (cmabService == null)

Choose a reason for hiding this comment

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

We will always need a cmabService. We can remove this check and just construct a cmab service

cache = new LruCache<CmabCacheEntry>(cacheSize, cacheTtl, Logger);
}

var cmabRetryConfig = new CmabRetryConfig(1, TimeSpan.FromMilliseconds(100));

Choose a reason for hiding this comment

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

can we define these constants (1, 100) instead of hardcoding here

reasonsToReport);
reasonsToReport
#if USE_CMAB
, flagDecision.CmabUuid

Choose a reason for hiding this comment

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

the publicly returned value from decide() should not have the cmabUuid, it's just an internal detail. THe OptimizelyDecision type should not change

/// <summary>
/// CMAB UUID associated with the decision for contextual multi-armed bandit experiments.
/// </summary>
public string CmabUuid { get; private set; }

Choose a reason for hiding this comment

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

I see that this is the public return value for decide(). We should not add the cmabUuid here, it's only for internal purposes

/// </summary>
/// <param name="cacheSize">Maximum number of entries in the CMAB cache.</param>
/// <param name="cacheTtl">Time-to-live for CMAB cache entries.</param>
public static void SetCmabCacheConfig(int cacheSize, TimeSpan cacheTtl)

Choose a reason for hiding this comment

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

can we change to SetCmabConfig instead?

Choose a reason for hiding this comment

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

instead of passing each cmab config option separately as the method parameter, we can have a single method that takes a single CmabConfig parameter. This publicly exposed CmabConfig should be in this same package/namespace.

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.

3 participants