Skip to content

Conversation

msohailhussain
Copy link
Contributor

Summary

  • Only downloaded datafiles should notify for OnConfigUpdate notification
  • Hardcoded datafile should set ProjectConfig on initialization
  • Remote datafile shouldn't block NewPollingConfigManager

Test plan

  • Added unit tests
  • Revised few unit tests

Issues

  • Any application was using sdk-key, should have the code to block execution until ProjectConfig is available or register OnProjectConfigUpdate notification

@msohailhussain msohailhussain changed the title fix(notification): Remote notification only fix(notification): Remote notification only - IN PROGRESS - failing on travis Dec 19, 2019
configManager.SyncConfig(mockDatafile2)
optimizelyConfig = configManager.GetOptimizelyConfig()
assert.Equal(t, "43", optimizelyConfig.Revision)
//mockRequester.AssertExpectations(t)
Copy link
Contributor

Choose a reason for hiding this comment

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

@msohailhussain , not sure why I could not check for requester assert expectation. Can you take a look ?

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 don't know how it was working before. When SyncConfig is called with payload in the argument, it means no Get call should be asserted. I have removed explicit SyncConfig call and now dependent on Start scheduled call. Very first call is called immediately, so no major wait in this unit test.

@msohailhussain msohailhussain changed the title fix(notification): Remote notification only - IN PROGRESS - failing on travis fix(notification): Remote notification only Dec 20, 2019
projectConfigUpdateNotification := notification.ProjectConfigUpdateNotification{
Type: notification.ProjectConfigUpdate,
Revision: cm.projectConfig.GetRevision(),
Revision: projectConfig.GetRevision(),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

FYI. race condition fix

mockDatafile2 := []byte(`{"revision":"43"}`)
sdkKey := "test_sdk_key"

mockRequester := new(MockRequester)
Copy link
Contributor

Choose a reason for hiding this comment

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

This mockRequester isn't passed as an option to NewPollingProjectConfigManager via WithRequester, so the assertion on 316 isn't useful, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks @mjc1283 for pointing out, i have added requester there.


// Send sends the notification to the registered handlers
func (am *AtomicManager) Send(notification interface{}) {
am.lock.Lock()
Copy link
Contributor

Choose a reason for hiding this comment

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

If the user calls Remove in their notification callback, does that cause deadlock?

Copy link
Contributor Author

@msohailhussain msohailhussain Dec 26, 2019

Choose a reason for hiding this comment

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

I can try a sample code, and can check.

@msohailhussain
Copy link
Contributor Author

One more question here is, if user calls Activate or any other API before projectconfig initialization. What should be the behavior? Right now it returns error.

Copy link
Contributor

@mikecdavis mikecdavis left a comment

Choose a reason for hiding this comment

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

What this is making pretty clear is that the SyncConfig method is overloaded and causes confusion. We should have distinct methods for SetConfig(datafile []byte) and PollConfig(). I think this will simplify the logic and avoid the branching and checking that currently going on.

configManager := NewPollingProjectConfigManager(sdkKey, WithRequester(mockRequester))
eg.Go(configManager.Start)

m := sync.RWMutex{}
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd be surprised if the ++ operator was not already atomic, but there is an atomic increment as part of the sync package https://gobyexample.com/atomic-counters that you should be able to use instead. Since you're just confirming that the notification was triggered, you can also use a sync.WaitGroup and block on completion of the group. I found the WaitGroup approach to be less flaky when testing with goroutines.

}

func TestNewPollingProjectConfigManagerPullImmediatelyOnStart(t *testing.T) {
m := sync.RWMutex{}
Copy link
Contributor

Choose a reason for hiding this comment

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

Similar comment here about using the Mutex as opposed to other synchronization options.

type AtomicManager struct {
handlers map[uint32]func(interface{})
counter uint32
lock sync.Mutex
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be a RWMutex?

Comment on lines 77 to 79
for _, handler := range am.handlers {
handler(notification)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Per our group discussion, we can first create an array of handlers, outside of the map, then trigger the handlers. That way we are not potentially modifying the Map while we're iterating.

@msohailhussain
Copy link
Contributor Author

Closing this PR since
#222
#224
are already merged.

@mikeproeng37 mikeproeng37 deleted the sohail/pollingmanager branch March 6, 2020 17:33
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.

5 participants