Skip to content

fix: update to latest build tools. slight refactor of datafile service. #335

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 11 commits into from
Jun 18, 2020

Conversation

thomaszurkan-optimizely
Copy link
Contributor

Summary

  • Eliminate piling up of datafile calls. Use latest tools.

Test plan

  • Lots of manual testing including taking the device offline for long periods of time.

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.

A great catch!
I suggested to add one more sync for safety.
Also we need test cases to cover the changes.

@@ -228,7 +228,7 @@ public OptimizelyClient initialize(@NonNull Context context, @Nullable String da
logger.error("Unable to build OptimizelyClient instance", e);
}

if (downloadToCache) {
if (downloadToCache && !datafileDownloadEnabled()) {
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 remove this extra flag check "datafileDownloadEnabled"?
Clients still want to see datafile immediately updated even when they turn on polling interval (15min+).
I see initial background fetch and periodic polling won't be called at the same time because of the big interval.

I think you found why we have a potential conflict in "startService" and setting the minLetency to one minute will fix it.

Copy link
Contributor Author

@thomaszurkan-optimizely thomaszurkan-optimizely Jun 15, 2020

Choose a reason for hiding this comment

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

No. This causes a double download. It makes sense to have this here. Please use the test-app with master and you can easily see the double download.

Copy link
Contributor

Choose a reason for hiding this comment

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

I observed the double downloads. It looks like un-deterministic behavior of JobScheduler - polling download can be triggered any time between 0..15mins and in simulator, it's fired instantly (I think this will behave differently in real devices).

This code fix will remove the double download issue when app starts.
But in real devices, if clients expect immediate download with sync-initialization, it may behave differently (like 15min delayed) and can bring back the initialization issue we fixed before.

Can we find alt solution still supporting instant download even when polling enabled?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think I don't see any issue here. Agree with Tom.

@@ -95,11 +96,11 @@ void dispatch(Intent intent) {
for (DatafileConfig datafileConfig : datafileConfigs) {
// for scheduled jobs Android O and above, we use the JobScheduler and persistent periodic jobs
// so, we don't need to do anything.
// if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Remove this comment line

Copy link
Contributor Author

Choose a reason for hiding this comment

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

that's what the red means.

@@ -139,7 +140,7 @@ public void startBackgroundUpdates(Context context, DatafileConfig datafileConfi
enableUpdateConfigOnNewDatafile(context, datafileConfig, listener);
}

public void enableUpdateConfigOnNewDatafile(Context context, DatafileConfig datafileConfig, DatafileLoadedListener listener) {
synchronized public void enableUpdateConfigOnNewDatafile(Context context, DatafileConfig datafileConfig, DatafileLoadedListener listener) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This sync looks good.
For safety, let's add a synchronizer for event handler as well (line 160-173).

Copy link
Contributor Author

@thomaszurkan-optimizely thomaszurkan-optimizely Jun 15, 2020

Choose a reason for hiding this comment

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

No. Not relevant to this PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

It's good to have sync on event handler but we already have sync on this method and disable. So chances for cross-thread is very low on event handler.

@@ -109,7 +108,11 @@ private void setRepeating(long interval, PendingIntent pendingIntent, Intent int
ScheduledJobService.class.getName()));
builder.setPeriodic(interval, interval);
builder.setPersisted(true);
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this is the one you mentioned in the slack - removed to avoid accumulated calls when network is back up.
Is this a bug in new Android OS or a new feature?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It has an unwanted side effect of just queuing up jobs rather than not running them. So, when a connection becomes available, it runs all queued.

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.

I still have concern about changing init download timing. See my comments.
Also a question is how to test the cases we try to fix here:

  1. it does not break when multiple downloads happen simultaneously
  2. when network is down long time, it won't accumulate the request and cause out-of-memory issues.

It'll be great if we can invent good test cases for (2), which must be tough. We can add tests for (1).

@@ -228,7 +228,7 @@ public OptimizelyClient initialize(@NonNull Context context, @Nullable String da
logger.error("Unable to build OptimizelyClient instance", e);
}

if (downloadToCache) {
if (downloadToCache && !datafileDownloadEnabled()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I observed the double downloads. It looks like un-deterministic behavior of JobScheduler - polling download can be triggered any time between 0..15mins and in simulator, it's fired instantly (I think this will behave differently in real devices).

This code fix will remove the double download issue when app starts.
But in real devices, if clients expect immediate download with sync-initialization, it may behave differently (like 15min delayed) and can bring back the initialization issue we fixed before.

Can we find alt solution still supporting instant download even when polling enabled?

@Test
public void initializeSyncWithUpdateOnNewDatafileDisabledWithPeriodicPollingDisabled() {
boolean downloadToCache = true;
boolean updateConfigOnNewDatafiel = false;
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: updateConfigOnNewDatafile

Suggested change
boolean updateConfigOnNewDatafiel = false;
boolean updateConfigOnNewDatafile = false;

}
}).when(manager.getDatafileHandler()).downloadDatafile(any(Context.class), any(DatafileConfig.class), any(DatafileLoadedListener.class));

OptimizelyClient client = manager.initialize(context, defaultDatafile, downloadToCache, updateConfigOnNewDatafiel);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
OptimizelyClient client = manager.initialize(context, defaultDatafile, downloadToCache, updateConfigOnNewDatafiel);
OptimizelyClient client = manager.initialize(context, defaultDatafile, downloadToCache, updateConfigOnNewDatafile);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
builder.setPrefetch(true);
}
//builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: remove?
Is there any reason to remove this line?

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.

It looks good!
One small change and a new test case suggested

// Execute tasks in order
requestDatafileFromClientTask.executeOnExecutor(executor);
// set the download time and don't allow downloads to overlap less than a minute
saveDownloadTime(datafileUrl);
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's move this to line 95 to maximize filtering.

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 really don't see a reason to move this. Can you explain?

@@ -170,4 +170,29 @@ public void warningsAreLogged() throws IOException {
verify(logger).warn("Unable to save new datafile");
verify(datafileLoadedListener, atMost(1)).onDatafileLoaded("{}");
}

@Test
public void debugLogged() throws IOException {
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 add one more test to check if back-to-back download works fine for both requests when they're apart 61secs?

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 want to add another an extra minute to the tests to prove this point.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yep it's not great to add a minute wait time for testing. But this test must be important to make sure we do not freeze polling permanently by error. We can change it to inject the required gap time so can test with a reasonable wait time of a few seconds.
I'm ok to release it as is for now and then add the extra test with the next coming release.

}

verify(logger).debug("Last download happened under 1 minute ago. Throttled to be at least 1 minute apart.");
verify(datafileLoadedListener, atMost(2)).onDatafileLoaded("{}");
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's also check if it's called at least once

Suggested change
verify(datafileLoadedListener, atMost(2)).onDatafileLoaded("{}");
verify(datafileLoadedListener, atMost(2)).onDatafileLoaded("{}");
verify(datafileLoadedListener, atLeast(1)).onDatafileLoaded("{}");

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

@thomaszurkan-optimizely thomaszurkan-optimizely dismissed mnoman09’s stale review June 18, 2020 20:34

I updated after your remarks. thanks! releasing now.

@thomaszurkan-optimizely thomaszurkan-optimizely merged commit a00a540 into master Jun 18, 2020
@thomaszurkan-optimizely thomaszurkan-optimizely deleted the updateToLatestBuildTools branch June 18, 2020 20:35
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.

4 participants