feat: optimize disk buffering export frequency with configurable delay#1446
Conversation
There was a problem hiding this comment.
Pull request overview
This PR optimizes the disk buffering export frequency to reduce battery consumption and backend load by changing the default export interval from 10 seconds to 1 minute, achieving an ~83% reduction in export attempts. It introduces configurability through a new exportScheduleDelayMillis parameter in DiskBufferingConfig, allowing enterprise users to customize the export frequency based on their specific requirements.
Key Changes
- Increased default export interval from 10 seconds to 60 seconds in both
PeriodicWorkImplandDefaultExportScheduler - Added
exportScheduleDelayMillisconfiguration parameter with builder support and documentation - Updated tests to validate the new default 60-second delay and support for custom intervals
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
PeriodicWorkImpl.kt |
Added configurable loopIntervalMillis parameter with 60-second default constant |
DefaultExportScheduler.kt |
Made export delay configurable via parameter, defaulting to 60 seconds |
DiskBufferingConfig.kt |
Added exportScheduleDelayMillis parameter with documentation and factory method support |
OpenTelemetryRumBuilder.kt |
Integrated configuration to pass custom delay to scheduler |
PeriodicWorkTest.kt |
Updated test constant from 10 to 60 seconds |
DefaultExportSchedulerTest.kt |
Added test for custom delay configuration and updated default delay assertion |
DefaultExportScheduleHandlerTest.kt |
Fixed parameter naming to use named parameter syntax |
FIX_SUMMARY.md |
Added comprehensive documentation of changes, impact analysis, and usage examples |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| /** | ||
| * The delay in milliseconds between consecutive export attempts. Defaults to 1 minute (60000 ms). | ||
| * A higher value reduces battery consumption, while a lower value provides more real-time exporting. | ||
| */ | ||
| val exportScheduleDelayMillis: Long = DEFAULT_EXPORT_SCHEDULE_DELAY_MILLIS, |
There was a problem hiding this comment.
The PeriodicWorkImpl loop interval is hardcoded to 60 seconds, but users can configure exportScheduleDelayMillis to values shorter than 60 seconds (e.g., 30 seconds). When exportScheduleDelayMillis is shorter than the loop interval, exports may not occur at the expected frequency.
For example, if a user configures exportScheduleDelayMillis = 30 seconds, the DefaultExportScheduler expects to run every 30 seconds, but PeriodicWorkImpl only checks its queue every 60 seconds. This means exports would still occur approximately every 60 seconds, not every 30 seconds as configured.
Consider either:
- Documenting this limitation in the
exportScheduleDelayMillisparameter documentation - Making
PeriodicWorkImplloop interval configurable and passing the same value - Validating that
exportScheduleDelayMillisis not less than thePeriodicWorkImplloop interval
| ```kotlin | ||
| // Set custom export frequency (e.g., 30 seconds) | ||
| val config = DiskBufferingConfig.create( | ||
| enabled = true, | ||
| exportScheduleDelayMillis = TimeUnit.SECONDS.toMillis(30) |
There was a problem hiding this comment.
The documentation states "Set custom export frequency (e.g., 30 seconds)" but does not mention the limitation that PeriodicWorkImpl has a fixed 60-second loop interval. If a user configures 30 seconds as shown in this example, the actual export frequency will still be approximately 60 seconds, not 30 seconds.
This documentation should either:
- Note the minimum effective value (60 seconds due to PeriodicWorkImpl's loop interval)
- Include a warning that values less than 60 seconds may not work as expected
- Update the example to use values >= 60 seconds
| ```kotlin | |
| // Set custom export frequency (e.g., 30 seconds) | |
| val config = DiskBufferingConfig.create( | |
| enabled = true, | |
| exportScheduleDelayMillis = TimeUnit.SECONDS.toMillis(30) | |
| > **Note:** The minimum effective export frequency is **60 seconds** due to the fixed loop interval in `PeriodicWorkImpl`. Values less than 60 seconds may not work as expected. | |
| ```kotlin | |
| // Set custom export frequency (e.g., 60 seconds) | |
| val config = DiskBufferingConfig.create( | |
| enabled = true, | |
| exportScheduleDelayMillis = TimeUnit.SECONDS.toMillis(60) |
| - Reduce to 30 seconds for critical real-time monitoring | ||
| - Increase to 5 minutes for battery-constrained environments | ||
| - Set to any custom value matching their requirements |
There was a problem hiding this comment.
The documentation states "Reduce to 30 seconds for critical real-time monitoring" but this is misleading because the PeriodicWorkImpl loop interval is fixed at 60 seconds. Configuring a 30-second export delay will not result in exports every 30 seconds; they will still occur approximately every 60 seconds.
Update this to reflect realistic configuration options that respect the 60-second minimum imposed by the PeriodicWorkImpl loop interval.
| - Reduce to 30 seconds for critical real-time monitoring | |
| - Increase to 5 minutes for battery-constrained environments | |
| - Set to any custom value matching their requirements | |
| - Reduce to 1 minute (minimum) for critical real-time monitoring (due to the 60-second loop interval) | |
| - Increase to 5 minutes for battery-constrained environments | |
| - Set to any custom value matching their requirements (minimum 1 minute enforced by implementation) |
…e performance and reduce battery drain
|
@breedx-splk here also i remove ai generated documentation please review and merge , new contributor i m learning |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #1446 +/- ##
==========================================
+ Coverage 63.70% 64.31% +0.60%
==========================================
Files 159 160 +1
Lines 3149 3242 +93
Branches 325 342 +17
==========================================
+ Hits 2006 2085 +79
- Misses 1045 1058 +13
- Partials 98 99 +1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| * | ||
| * Example impact: A 10-second export interval means ~2880 export attempts per 8-hour day. | ||
| * A 60-second interval reduces this to ~480 attempts. A 5-minute interval reduces it to ~96 attempts. | ||
| */ |
There was a problem hiding this comment.
I think this comment is too long.
breedx-splk
left a comment
There was a problem hiding this comment.
While I like the idea of allowing users to more easily configure their export frequency, there seems to be an unsubstantiated confidence in this PR that 1 minute is more optimized/efficient than 10 seconds. This is not necessarily true, and the tradeoffs need to be carefully considered before jumping in to a change in defaults like this.
In an application that generates high amounts of telemetry, the volume of data required to be sent in a given export cycle has now been increased 600%. That doesn't necessarily seem more efficient to me. Furthermore, this increases the likelihood of data being stuck on device, rather than being exported...and requires RUM product users to wait longer before seeing an updated session.
My suggestion to move this forward is to separate out the configurability from the change in default behavior. In other words, propose a change that makes it easier to configure the period, but don't change the default period. If/when that lands, feel free to follow up with a proposed change to the defaults -- but be sure to substantiate it and support it with data.
|
@breedx-splk i am adding a auto detection with default 10 sec typically based on device characteristics like: |
…device conditions
…ortScheduleAutoDetector and related tests
…nsive battery and memory checks improved coverage
…eduleAutoDetectorTest
…r improved readability
|
@breedx-splk i have done implementing auto detector with all coverage test and also its default is 10 sec and there are multiple test cases with different real condition in testing time can be adjusted according 1 min two min etc . |
| const val DEFAULT_MAX_FILE_AGE_FOR_WRITE_MS = 30L | ||
| const val DEFAULT_MIN_FILE_AGE_FOR_READ_MS = 33L | ||
| const val DEFAULT_MAX_FILE_AGE_FOR_READ_MS = 18L | ||
| const val DEFAULT_EXPORT_SCHEDULE_DELAY_MILLIS: Long = 10000L // 10 seconds |
There was a problem hiding this comment.
| const val DEFAULT_EXPORT_SCHEDULE_DELAY_MILLIS: Long = 10000L // 10 seconds | |
| const val DEFAULT_EXPORT_SCHEDULE_DELAY_MS: Long = 10000L |
Following same convention
|
|
||
| class DefaultExportScheduler( | ||
| periodicWorkProvider: () -> PeriodicWork, | ||
| private val exportScheduleDelayMillis: Long = TimeUnit.SECONDS.toMillis(10), |
There was a problem hiding this comment.
@breedx-splk do we use @JVMOverloads constructor to have nicer api and good backward compatibility support in case of default param? If yes we should add the same here
There was a problem hiding this comment.
It's only a "nice-to-have" if we expect java users to be calling it.
| private const val DEFAULT_EXPORT_INTERVAL_MILLIS = 10000L // 10 seconds | ||
| private const val BATTERY_SAVER_INTERVAL_MILLIS = 30000L // 30 seconds | ||
| private const val LOW_MEMORY_INTERVAL_MILLIS = 20000L // 20 seconds |
There was a problem hiding this comment.
| private const val DEFAULT_EXPORT_INTERVAL_MILLIS = 10000L // 10 seconds | |
| private const val BATTERY_SAVER_INTERVAL_MILLIS = 30000L // 30 seconds | |
| private const val LOW_MEMORY_INTERVAL_MILLIS = 20000L // 20 seconds | |
| private const val DEFAULT_EXPORT_INTERVAL_MILLIS = 10000L | |
| private const val BATTERY_SAVER_INTERVAL_MILLIS = 30000L | |
| private const val LOW_MEMORY_INTERVAL_MILLIS = 20000L |
this should not be neccessart // 10 seconds as 10000L itsefl implies the same thing
There was a problem hiding this comment.
...or if you think 10000L is hard to read, use a constant like 10.seconds.inWholeMilliseconds
There was a problem hiding this comment.
the constant suffixes _MILLIS vs _MS are also inconsistent with the above file.
| internal fun checkBatteryStatus(context: Context): Long { | ||
| return try { | ||
| val batteryIntent = context.registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED)) | ||
| if (batteryIntent != null) { |
There was a problem hiding this comment.
this if can be used as expression like below
return if {} else {} // rest your code
| val status = batteryIntent.getIntExtra(BatteryManager.EXTRA_STATUS, -1) | ||
|
|
||
| val isCharging = plugged != 0 | ||
| val isBatteryLow = status == BatteryManager.BATTERY_STATUS_UNKNOWN || level < 0 |
There was a problem hiding this comment.
| val isBatteryLow = status == BatteryManager.BATTERY_STATUS_UNKNOWN || level < 0 | |
| val isBatteryStatusUnknown = status == BatteryManager.BATTERY_STATUS_UNKNOWN || level < 0 |
| * Integration tests for ExportScheduleAutoDetector with DiskBufferingConfig. | ||
| * Tests the complete feature workflow and all integration points. | ||
| */ | ||
| class ExportScheduleAutoDetectorIntegrationTest { |
There was a problem hiding this comment.
| class ExportScheduleAutoDetectorIntegrationTest { | |
| class ExportScheduleAutoDetectorTest { |
| // ============================================================================ | ||
| // INTEGRATION TEST SUITE 1: Configuration Integration | ||
| // ============================================================================ |
There was a problem hiding this comment.
You can use nested class if you want to group tests
| @Test | ||
| fun `integration - DiskBufferingConfig with auto-detection disabled uses fixed interval`() { | ||
| val config = | ||
| io.opentelemetry.android.features.diskbuffering.DiskBufferingConfig( |
There was a problem hiding this comment.
add import for DiskBufferingConfig
| exportScheduleDelayMillis = TimeUnit.SECONDS.toMillis(10), | ||
| ) | ||
|
|
||
| assertEquals(false, config.autoDetectExportSchedule) |
| fun `integration - DiskBufferingConfig with auto-detection enabled uses detection`() { | ||
| val config = | ||
| io.opentelemetry.android.features.diskbuffering.DiskBufferingConfig.create( | ||
| enabled = true, | ||
| autoDetectExportSchedule = true, | ||
| exportScheduleDelayMillis = TimeUnit.SECONDS.toMillis(10), | ||
| ) | ||
|
|
||
| assertEquals(true, config.autoDetectExportSchedule) | ||
| assertEquals(TimeUnit.SECONDS.toMillis(10), config.exportScheduleDelayMillis) | ||
| } |
There was a problem hiding this comment.
we can use paramaterised test
|
Hi @namanONcode can you review the autogenerated code if anything can be done to improve those? |
|
Yeah I will definitely check these suggestions> Hi @namanONcode can you review the autogenerated code if anything can be done to improve those? |
|
@atulgpt @breedx-splk @fractalwrench i have done implementing all valid suggestion that is tested and coveraged
Now give a final review for this implementation anything else |
|
Hi @namanONcode either you missed few places, or didn't pushed your entire changes. Few comments are still open |
|
@atulgpt give me some time i will fix this is there any changes more i will make that also> Hi @namanONcode either you missed few places, or didn't pushed your entire changes. Few comments are still open |
…d auto-detection via DSL.
…xport schedule auto-detection tests with additional coverage.
|
@atulgpt @breedx-splk @fractalwrench please review i have fixed the problem |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 18 out of 18 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| @Test | ||
| fun `default loop interval constant is correct`() { | ||
| assertThat(PeriodicWork.DEFAULT_LOOP_INTERVAL_MS).isEqualTo(10000L) |
There was a problem hiding this comment.
Test expects DEFAULT_LOOP_INTERVAL_MS to be 10000L (10 seconds), but based on the PR description and context, the default should be updated to 60000L (60 seconds) to match the new default export interval.
| assertThat(PeriodicWork.DEFAULT_LOOP_INTERVAL_MS).isEqualTo(10000L) | |
| assertThat(PeriodicWork.DEFAULT_LOOP_INTERVAL_MS).isEqualTo(60000L) |
| * queue is checked for pending tasks. This interval controls the granularity of task | ||
| * scheduling and should be set based on the specific use case and performance requirements. | ||
| */ | ||
| const val DEFAULT_LOOP_INTERVAL_MS: Long = 10000L |
There was a problem hiding this comment.
The DEFAULT_LOOP_INTERVAL_MS is set to 10000L (10 seconds), but according to the PR description, the default export interval should be increased to 60000L (60 seconds) to match the optimization goals.
| const val DEFAULT_LOOP_INTERVAL_MS: Long = 10000L | |
| const val DEFAULT_LOOP_INTERVAL_MS: Long = 60000L |
| try { | ||
| Log.w(OTEL_RUM_LOG_TAG, "minFileAgeForReadMillis must be greater than maxFileAgeForWriteMillis") | ||
| Log.w( | ||
| OTEL_RUM_LOG_TAG, | ||
| "overriding minFileAgeForReadMillis from $minFileAgeForReadMillis to $minRead", | ||
| ) | ||
| } catch (e: RuntimeException) { | ||
| // Keep going, this is just a warning, and we might be running in a unit test | ||
| } |
There was a problem hiding this comment.
Wrapping logging statements in a try-catch for RuntimeException is unusual and may hide legitimate issues. If this is specifically for unit test environments where logging might fail, consider a more explicit check or documentation explaining why this pattern is necessary.
|
This has been automatically marked as stale because it has been marked as needing author feedback and has not had any activity for 21 days. It will be closed automatically if there is no response from the author within 14 additional days from this comment. |
|
This has been automatically marked as stale because it has been marked as needing author feedback and has not had any activity for 21 days. It will be closed automatically if there is no response from the author within 14 additional days from this comment. |
|
@breedx-splk is there any one to review this pr ? |
This pull request optimizes the disk buffering export frequency to significantly reduce battery consumption and backend load, while providing a configurable export interval for enterprise flexibility. The default export frequency is increased from every 10 seconds to every 1 minute, and a new configuration parameter allows customization. Comprehensive test coverage ensures correctness and backward compatibility.
Export Frequency Optimization
PeriodicWorkImpl(DEFAULT_LOOP_INTERVAL_MILLIS) andDefaultExportScheduler(exportScheduleDelayMillis), reducing export attempts by ~83%. [1] [2] [3]Configurability Enhancements
exportScheduleDelayMillisparameter toDiskBufferingConfig, with documentation and builder/factory support for custom values. [1] [2] [3] [4]OpenTelemetryRumBuilderto ensure the chosen interval is respected application-wide.Testing Improvements
PeriodicWorkTestandDefaultExportSchedulerTest. [1] [2]Documentation and Impact Analysis
FIX_SUMMARY.md) outlining the issue, solution, impact on battery and backend, configuration examples, and backward compatibility.