-
Notifications
You must be signed in to change notification settings - Fork 38
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
Conversation
There was a problem hiding this 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()) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove this comment line
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this 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:
- it does not break when multiple downloads happen simultaneously
- 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()) { |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: updateConfigOnNewDatafile
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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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); |
There was a problem hiding this comment.
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?
There was a problem hiding this 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); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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("{}"); |
There was a problem hiding this comment.
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
verify(datafileLoadedListener, atMost(2)).onDatafileLoaded("{}"); | |
verify(datafileLoadedListener, atMost(2)).onDatafileLoaded("{}"); | |
verify(datafileLoadedListener, atLeast(1)).onDatafileLoaded("{}"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
I updated after your remarks. thanks! releasing now.
Summary
Test plan