Skip to content

Fix fireperf RateLimiter replenishment behavior and unit alignment. #2662

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 5 commits into from
May 20, 2021

Conversation

jfcong
Copy link
Contributor

@jfcong jfcong commented May 13, 2021

Fixed two bugs in the RateLimiter:

  • Rate was represented by a long, which causes confusion around the units (per second vs per minute)
  • the token replenishment behavior is wrong and keeps track of lastTimeTokenConsumed instead of lastTimeTokenReplenished, causing less tokens to be generated overtime

@googlebot googlebot added the cla: yes Override cla label May 13, 2021
@google-oss-bot
Copy link
Contributor

google-oss-bot commented May 13, 2021

Coverage Report

Affected SDKs

  • firebase-perf

    SDK overall coverage changed from ? (4bc47df) to 70.68% (1b057bff) by ?.

    Click to show coverage changes in 96 files.
    Filename Base (4bc47df) Head (1b057bff) Diff
    AddTrace.java ? 0.00% ?
    AndroidApplicationInfo.java ? 34.71% ?
    AndroidApplicationInfoOrBuilder.java ? 0.00% ?
    AndroidLogger.java ? 97.78% ?
    AndroidMemoryReading.java ? 38.36% ?
    AndroidMemoryReadingOrBuilder.java ? 0.00% ?
    AppStartTrace.java ? 85.71% ?
    AppStateMonitor.java ? 86.78% ?
    AppStateUpdateHandler.java ? 92.59% ?
    ApplicationInfo.java ? 45.00% ?
    ApplicationInfoOrBuilder.java ? 0.00% ?
    ApplicationProcessState.java ? 73.91% ?
    Clock.java ? 100.00% ?
    ConfigResolver.java ? 97.23% ?
    ConfigurationConstants.java ? 99.21% ?
    ConfigurationFlag.java ? 100.00% ?
    Constants.java ? 95.65% ?
    Counter.java ? 90.91% ?
    CpuGaugeCollector.java ? 93.10% ?
    CpuMetricReading.java ? 39.33% ?
    CpuMetricReadingOrBuilder.java ? 0.00% ?
    DaggerFirebasePerformanceComponent.java ? 100.00% ?
    DeviceCacheManager.java ? 76.03% ?
    FirebasePerfApplicationInfoValidator.java ? 92.86% ?
    FirebasePerfGaugeMetricValidator.java ? 100.00% ?
    FirebasePerfHttpClient.java ? 93.85% ?
    FirebasePerfMetricProto.java ? 0.00% ?
    FirebasePerfNetworkValidator.java ? 86.67% ?
    FirebasePerfOkHttpClient.java ? 44.90% ?
    FirebasePerfProvider.java ? 76.92% ?
    FirebasePerfRegistrar.java ? 100.00% ?
    FirebasePerfTraceValidator.java ? 88.24% ?
    FirebasePerfUrlConnection.java ? 44.26% ?
    FirebasePerformance.java ? 82.95% ?
    FirebasePerformanceAttributable.java ? 0.00% ?
    FirebasePerformanceComponent.java ? 0.00% ?
    FirebasePerformanceInitializer.java ? 33.33% ?
    FirebasePerformanceModule.java ? 100.00% ?
    FirebasePerformanceModule_ProvidesConfigResolverFactory.java ? 100.00% ?
    FirebasePerformanceModule_ProvidesFirebaseAppFactory.java ? 100.00% ?
    FirebasePerformanceModule_ProvidesFirebaseInstallationsFactory.java ? 100.00% ?
    FirebasePerformanceModule_ProvidesGaugeManagerFactory.java ? 100.00% ?
    FirebasePerformanceModule_ProvidesRemoteConfigComponentFactory.java ? 100.00% ?
    FirebasePerformanceModule_ProvidesRemoteConfigManagerFactory.java ? 100.00% ?
    FirebasePerformanceModule_ProvidesTransportFactoryProviderFactory.java ? 100.00% ?
    FirebasePerformance_Factory.java ? 100.00% ?
    FlgTransport.java ? 83.33% ?
    GaugeManager.java ? 98.39% ?
    GaugeMetadata.java ? 32.21% ?
    GaugeMetadataManager.java ? 84.21% ?
    GaugeMetadataOrBuilder.java ? 0.00% ?
    GaugeMetric.java ? 39.47% ?
    GaugeMetricOrBuilder.java ? 0.00% ?
    HttpMetric.java ? 91.78% ?
    ImmutableBundle.java ? 100.00% ?
    InstrHttpInputStream.java ? 92.86% ?
    InstrHttpOutputStream.java ? 98.00% ?
    InstrHttpURLConnection.java ? 93.42% ?
    InstrHttpsURLConnection.java ? 94.32% ?
    InstrURLConnectionBase.java ? 95.24% ?
    InstrumentApacheHttpResponseHandler.java ? 100.00% ?
    InstrumentOkHttpEnqueueCallback.java ? 100.00% ?
    LogWrapper.java ? 23.08% ?
    MemoryGaugeCollector.java ? 90.00% ?
    NetworkConnectionInfo.java ? 0.00% ?
    NetworkConnectionInfoOrBuilder.java ? 0.00% ?
    NetworkRequestMetric.java ? 49.16% ?
    NetworkRequestMetricBuilder.java ? 95.97% ?
    NetworkRequestMetricBuilderUtil.java ? 75.00% ?
    NetworkRequestMetricOrBuilder.java ? 0.00% ?
    Optional.java ? 86.67% ?
    PendingPerfEvent.java ? 100.00% ?
    PerfMetric.java ? 33.67% ?
    PerfMetricOrBuilder.java ? 0.00% ?
    PerfMetricValidator.java ? 90.32% ?
    PerfSession.java ? 93.33% ?
    PerfSessionOrBuilder.java ? 0.00% ?
    Rate.java ? 100.00% ?
    RateLimiter.java ? 90.60% ?
    RemoteConfigManager.java ? 92.24% ?
    ResourceType.java ? 0.00% ?
    SessionAwareObject.java ? 0.00% ?
    SessionManager.java ? 100.00% ?
    SessionVerbosity.java ? 68.42% ?
    StorageUnit.java ? 57.89% ?
    Timer.java ? 93.75% ?
    Trace.java ? 96.69% ?
    TraceMetric.java ? 43.42% ?
    TraceMetricBuilder.java ? 100.00% ?
    TraceMetricOrBuilder.java ? 0.00% ?
    TransportInfo.java ? 0.00% ?
    TransportInfoOrBuilder.java ? 0.00% ?
    TransportManager.java ? 94.12% ?
    URLAllowlist.java ? 94.44% ?
    URLWrapper.java ? 0.00% ?
    Utils.java ? 78.57% ?

Test Logs

Notes

HTML coverage reports can be produced locally with ./gradlew <product>:checkCoverage.
Report files are located at <product-build-dir>/reports/jacoco/.

Head commit (1b057bff) is created by Prow via merging commits: 4bc47df a2525a1.

@google-oss-bot
Copy link
Contributor

google-oss-bot commented May 13, 2021

Binary Size Report

Affected SDKs

  • firebase-perf

    Type Base (4bc47df) Head (1b057bff) Diff
    aar 296 kB 298 kB +1.58 kB (+0.5%)
    apk (aggressive) 968 kB 968 kB +416 B (+0.0%)
    apk (release) 2.43 MB 2.43 MB +104 B (+0.0%)

Test Logs

Notes

Head commit (1b057bff) is created by Prow via merging commits: 4bc47df a2525a1.

@jfcong jfcong requested review from visumickey and jposuna May 13, 2021 19:36

private long foregroundCapacity;
private long backgroundCapacity;

RateLimiterImpl(
final double rate,
final Rate rate,
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm noticing further down that there is a separate foreground and background rate, but they seem to be ignoring this initial one and just blindly overwriting it. Do you understand how it works?

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 think the foreground and background rate is set during initilization and read from remote config, but the passed in rate is still in use, until changeRate() is called. changeRate() is called by the TransportManager when an app updates its state (i.e., changes from foreground to background). At least that's my understanding.

Copy link
Contributor

Choose a reason for hiding this comment

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

But shouldn't the passed-in rate be either foreground or background? Why would there be a 3rd rate that is only used to initialize and then overwritten?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Perhaps because we don't know whether the app will be in foreground or background when it finishes initializing? I am not sure if there were other reasons that I am not aware of. We could remove the passed in rate and set it to either foreground or background on initialization if that makes more sense (but which one?)

Copy link
Contributor

Choose a reason for hiding this comment

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

@visumickey do you know why this is the way it is and whether it makes sense to keep it or change it?

Copy link
Contributor

Choose a reason for hiding this comment

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

This seems weird. I don't see a need for a third rate. My recommendation would be not to pass the rate as a parameter since we anyways extract the rate from the configResolver.

As a part of the initialization, we can check on the state of the application to get the rate and use that for the token generation. Eg: on iOS, currently we do not pass the rate as a parameter, but use the state of the application to get the rate from config resolver as specified above.

Copy link
Contributor

Choose a reason for hiding this comment

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

That sounds good - I'd recommend leaving it out of this PR though to not continue changing more things in a single PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds good, I will leave that out of this PR. Although, is there a possibility that the app has not finished initializing yet so it's not in either foreground or background?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, that is a possibility. But in those situations, it is fine to default it to the foreground rate.

@jfcong jfcong requested a review from jposuna May 17, 2021 17:18

private long foregroundCapacity;
private long backgroundCapacity;

RateLimiterImpl(
final double rate,
final Rate rate,
Copy link
Contributor

Choose a reason for hiding this comment

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

But shouldn't the passed-in rate be either foreground or background? Why would there be a 3rd rate that is only used to initialize and then overwritten?

@jfcong jfcong requested a review from jposuna May 17, 2021 19:51
* @param numTimeUnits The number of specified time unit
* @param timeUnit The specified time unit
*/
public Rate(long numTokensPerTotalTimeUnit, long numTimeUnits, TimeUnit timeUnit) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Curious if we can unify the second and third parameter together and be prescriptive about the timeUnit?

Eg. new Rate(3, 60) - Would mean, 3 tokens in 60 seconds.

Any reason to keep the timeUnit as a separate parameter?

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 to prevent bugs like the existing one; if you only pass an int or long, it's very easy to get the units wrong (before we were mixing up minutes and seconds). By passing this, it's impossible to get it wrong.

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 think the third parameter is there mainly for readability reasons. Before in this class, there are two conflicting comments about what the rate is: one says it's token per minute and the other one says it's token per second. By explicitly passing in the TimeUnit, we make it very clear that new Rate(3, 60, SECONDS) means 3 tokens per 60 seconds, and not 3 tokens per 60 minutes or vice versa.

Besides, by keeping the third parameter, we can also make testing more readable, by specifying rates of units other than SECONDS. For example, we can specify new Rate(2, 1, MINUTE) for 2 tokens per minute.

Copy link
Contributor

Choose a reason for hiding this comment

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

I definitely see the need for the readability part. My suggestion was to create the readability by calling the time unit to be always in seconds. Eg. (numberOfTokensForTimeLimit, timeLimitInSeconds)

The current is definitely super verbose and helpful. Was just feeling that verbosity can come by qualifying the parameter with better names.

Anyways, not too tied to my thoughts on this. Fine with this change as well.


private long foregroundCapacity;
private long backgroundCapacity;

RateLimiterImpl(
final double rate,
final Rate rate,
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems weird. I don't see a need for a third rate. My recommendation would be not to pass the rate as a parameter since we anyways extract the rate from the configResolver.

As a part of the initialization, we can check on the state of the application to get the rate and use that for the token generation. Eg: on iOS, currently we do not pass the rate as a parameter, but use the state of the application to get the rate from config resolver as specified above.

@jfcong jfcong merged commit 6357bbe into master May 20, 2021
@jfcong jfcong deleted the perfRateLimiter branch May 20, 2021 18:34
@firebase firebase locked and limited conversation to collaborators Jun 20, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants