Skip to content

feat: Integrated ODPManager with UserContext and Optimizely client #323

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 69 commits into from
Feb 14, 2023

Conversation

mikechu-optimizely
Copy link
Contributor

Summary

This PR integrates ODP functionality with UserContext and Optimizely client to make it available for users. There are two ways to integrate ODPManager.

😬 Sorry about the numerous auto-format changes polluting the core enhancements. Need to satisfy the linter.

Test plan

  1. Manually tested thoroughly
  2. Will add unit tests after the first review passes.

Issues

FSSDK-8476

Copy link
Contributor

@jaeopt jaeopt left a comment

Choose a reason for hiding this comment

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

Overall it looks good. See my first-round comments. Key changes suggested -

  • a logic to extract segments from audiences is missing. This is a key to ODP integration.
  • a fix for async fetchQualifiedSegments.

WithLogger(Logger).
WithErrorHandler(ErrorHandler).
Build(!SdkSettings.DisableOdp);
OdpManager.EventManager.Start();
Copy link
Contributor

Choose a reason for hiding this comment

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

I see "eventManager.start()" inside OdpManagher.Build()", which looks good. Do we need it here again?

@@ -373,6 +408,7 @@ private void Initialize()
var integration = Integrations.FirstOrDefault(i => i.Key.ToLower() == "odp");
HostForOdp = integration?.Host;
PublicKeyForOdp = integration?.PublicKey;
Segments = TypedAudiences.Select(a => a.Name).ToArray();
Copy link
Contributor

Choose a reason for hiding this comment

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

This is not correct to get all segments from audiences. This is critical for ODP integration.
You can check with other SDKs and need more testing for this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Looking 👀 into this....

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm still working on how to parse odp. typed audiences based on other sdk work.

Can you review your other notes to see if they're ok before you go on holiday?

Comment on lines 247 to 249
var optimizelyConfig =
new OptimizelyConfigService(config).GetOptimizelyConfig();
var allSegments = optimizelyConfig.Audiences.Select(a => a.Name).ToList();
Copy link
Contributor

Choose a reason for hiding this comment

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

You already have "Segments" in DatafileProjectConfig. Can't we use it directly.

/// <param name="userId">FS User ID</param>
/// <param name="segmentOptions">Options used during segment cache handling</param>
/// <returns>Qualified segments for the user from the cache or the ODP server</returns>
public string[] FetchQualifiedSegments(string userId, List<OdpSegmentOption> segmentOptions)
Copy link
Contributor

Choose a reason for hiding this comment

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

This should not be public. It'll be called from OptimizelyUserContext only. "internal"?

/// Send identification event to Optimizely Data Platform
/// </summary>
/// <param name="userId">FS User ID to send</param>
public void IdentifyUser(string userId)
Copy link
Contributor

Choose a reason for hiding this comment

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

This should not be public. It'll be called from OptimizelyUserContext only. "internal"?

Comment on lines 1394 to 1398
if (OdpManager == null)
{
Logger.Log(LogLevel.ERROR, Constants.ODP_NOT_ENABLED_MESSAGE);
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Why don't we have this guard for "fetchQualifiedSegments" and "identifyUser" above?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oops, I meant to remove this guard here in SendOdpEvent too. Instantiating OdpManager should be guaranteed via InitializeComponents call be constructor.

Should add the guards back, just in case things change? What's your thought?

Copy link
Contributor

Choose a reason for hiding this comment

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

No need for guard here if it's guaranteed non-null.

List<OdpSegmentOption> segmentOptions = null
)
{
var segments = Optimizely.FetchQualifiedSegments(userId,
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be non-blocking if callback is provided (async API).
We can consider splitting the API into 2 (sync and async).

  • bool FetchQualifiedSegments(userId, options)
  • void FetchQaulifiedSegments(userId, options, callback)

@mikechu-optimizely
Copy link
Contributor Author

@mnoman09 I see where the FSC is failing. Is that what #325 is for?

Copy link
Contributor

@jaeopt jaeopt left a comment

Choose a reason for hiding this comment

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

Changes look good! One more suggestion to consider for compatibility support.


private HttpProjectConfigManager(TimeSpan period, string url, TimeSpan blockingTimeout,
bool autoUpdate, ILogger logger, IErrorHandler errorHandler, string sdkKey
bool autoUpdate, ILogger logger, IErrorHandler errorHandler, string sdkKey = null
Copy link
Contributor

Choose a reason for hiding this comment

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

This default null sdkKey parameter will make existing clients compile ok but it'll fail to run because of missing sdkKey for ODP notification.
What about removing the default null value (breaking changes note in release), so they are all forced to change to use ODP?

Copy link
Contributor

Choose a reason for hiding this comment

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

Can we also keep the list of all breaking changes, so we won't miss them in the release note?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Great concept. Are we planning to release ATS as a v4.0 or v3.12? For breaking changes, I'd like to go 4.0 with @trishahanlon 's documentation to back up the reasoning.

This default null sdkKey parameter will make existing clients compile ok...

If we're planning to release ODP as a minor semantic version, I'd like to keep the default. I guess this is a point of internal clarification in standup maybe.

Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like we have a few breaking changes (new interface methods and new sdkKey constructor param) so we have to bump a major version to 4.0.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jaeopt Thanks for the guidance. I hope this looks better now.

/// <summary>
/// SDK key in use for this project
/// </summary>
string SdkKey { get; }
Copy link
Contributor

Choose a reason for hiding this comment

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

@mikechu-optimizely in addition to doc update, we should list all these breaking changes in release note.

@mikechu-optimizely
Copy link
Contributor Author

Still holding for @msohailhussain 's approval (maybe if he's time).

@mnoman09 I'll hold for your merge and Approval too.

Copy link
Contributor

@jaeopt jaeopt left a comment

Choose a reason for hiding this comment

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

LGTM
@msohailhussain can you double check if you see any concerns about compatibility?

mnoman09 and others added 4 commits January 25, 2023 10:17
* Some suggested changes to fix null pointer exception

* - Removed batchsize support from ODP event manager
- Made some general fixes

* Flush previous event

* reverting this change

Co-authored-by: mnoman09 <m.nomanshoaib09@gmail.com>
@mikechu-optimizely
Copy link
Contributor Author

@msohailhussain @mnoman09 It looks like some FSC tests are erroring for this branch. Shall I start investigating the test app?

@mnoman09
Copy link
Contributor

@msohailhussain @mnoman09 It looks like some FSC tests are erroring for this branch. Shall I start investigating the test app?

I can look at the testapp. Most of these tests are failing because we haven't merged the testapp PR: 79. Once we merge that PR FSC should pass.

Copy link
Contributor

@mnoman09 mnoman09 left a comment

Choose a reason for hiding this comment

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

Overall, these changes lgtm.

Copy link
Contributor Author

Great. Thank you.

@mikechu-optimizely
Copy link
Contributor Author

Is there something I can be helping with for getting FSC/test-app scenarios corrected?

@mnoman09 @msohailhussain

@msohailhussain
Copy link
Contributor

Is there something I can be helping with for getting FSC/test-app scenarios corrected?

@mnoman09 @msohailhussain

Is there something I can be helping with for getting FSC/test-app scenarios corrected?

@mnoman09 @msohailhussain

Is there something I can be helping with for getting FSC/test-app scenarios corrected?

@mnoman09 @msohailhussain

I will do by tomorrow.

Copy link
Contributor

@msohailhussain msohailhussain left a comment

Choose a reason for hiding this comment

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

have few more comments. please address.

[TestFixtureSetUp]
public void Setup()
{
_qualifiedMatchCondition = new BaseCondition
Copy link
Contributor

Choose a reason for hiding this comment

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

not addressed.

@@ -67,7 +67,7 @@ public void ShouldSaveAndLookupMultipleItems()
cache.Save("user2", _segments3And4);
cache.Save("user3", _segments5And6);

var cacheKeys = cache._readCurrentCacheKeys();
var cacheKeys = cache.CurrentCacheKeysForTesting();
Copy link
Contributor

Choose a reason for hiding this comment

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

Please use reflection to get private properties.

@@ -151,6 +153,9 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="..\keypair.snk">
Copy link
Contributor

Choose a reason for hiding this comment

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

why this added?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This allows for signing the Test project so InternalsVisibleTo would work when testing runs in GitHub Actions.

private string _sdkKey = string.Empty;

private HttpProjectConfigManager(TimeSpan period, string url, TimeSpan blockingTimeout,
bool autoUpdate, ILogger logger, IErrorHandler errorHandler, string sdkKey
Copy link
Contributor

Choose a reason for hiding this comment

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

constructor is private, not a breaking change.

@optimizely optimizely deleted a comment from msohailhussain Jan 31, 2023
@mikechu-optimizely
Copy link
Contributor Author

I'm starting to understand FSC and the C# testapp. Working to figure out failing tests...

Copy link
Contributor

@msohailhussain msohailhussain left a comment

Choose a reason for hiding this comment

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

thanks. lgtm

@mikechu-optimizely
Copy link
Contributor Author

Thanks for the approval. FSC and the test app are my last blocker

@mnoman09 mnoman09 closed this Feb 8, 2023
@mnoman09 mnoman09 reopened this Feb 8, 2023
* Refactored OdpEventManager flushEvent function.

* To wait for events using flushInterval when eventBatch is not empty.

* Or wait indefinitely if no event is in batch until any event arrive.

* Avoid calling flush on shutdown if no events are present

* Avoid calling unnecessary flushQueue if _currentBatch.Count is zero.

* Fixed the test as when there are no remaining items in queue then odpEventManager should not  flush.

* Removing mutex lock from notification registry instead using concurrentDictionary

* Revert "Removing mutex lock from notification registry instead using concurrentDictionary"

This reverts commit 64cdf80.

* Added Thread.sleep which somehow enabling thread to shutdown gracefully.

---------

Co-authored-by: mnoman09 <m.nomanshoaib09@gmail.com>
@msohailhussain msohailhussain merged commit 2d8b71a into master Feb 14, 2023
@msohailhussain msohailhussain deleted the mike/odp-usercontext-optimizelyclient branch February 14, 2023 06:13
@mikechu-optimizely
Copy link
Contributor Author

OMG @msohailhussain and @mnoman09 You guys are the best.

Valuable lesson for me is to keep PR's much smaller.

I'll start working on the full-repo linting.

Thanks again, you guys.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants