Skip to content
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

REPLAY-1943: Refactor caches from singletons to class instances #1564

Conversation

jonathanmos
Copy link
Member

What does this PR do?

Refactor the base64 drawable cache and the bitmap pool to be class instances instead of singletons.

Motivation

Make things more straightforward to test and remove mutable variables

Additional Notes

Anything else we should know when reviewing?

Review checklist (to be filled by reviewers)

  • Feature or bugfix MUST have appropriate tests (unit, integration, e2e)
  • Make sure you discussed the feature or bugfix with the maintaining team in an Issue
  • Make sure each commit and the PR mention the Issue number (cf the CONTRIBUTING doc)

@jonathanmos jonathanmos changed the base branch from feature/base64-jmoskovich to jmoskovich/replay-1890/bitmap-pool July 31, 2023 12:03
@jonathanmos jonathanmos force-pushed the jmoskovich/replay-1943/refactor-cache-singletons branch from f58d3ee to d6a4547 Compare July 31, 2023 12:26
@jonathanmos jonathanmos force-pushed the jmoskovich/replay-1890/bitmap-pool branch from c0a9969 to 227afe9 Compare August 8, 2023 08:35
@jonathanmos jonathanmos force-pushed the jmoskovich/replay-1943/refactor-cache-singletons branch from 93329d5 to 29f2c89 Compare August 8, 2023 09:58
@jonathanmos jonathanmos marked this pull request as ready for review August 8, 2023 10:21
@jonathanmos jonathanmos requested a review from a team as a code owner August 8, 2023 10:21
@@ -49,17 +49,30 @@ internal class BitmapPoolTest {

@BeforeEach
fun setup(forge: Forge) {
internalCache = LruCache(MAX_CACHE_MEMORY_SIZE_BYTES)
testedCache.setBackingCache(internalCache)
internalCache = object : LruCache<String, Bitmap>(MAX_CACHE_MEMORY_SIZE_BYTES) {
Copy link
Member

Choose a reason for hiding this comment

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

what is the purpose of providing an implementation here and not a Mock ? I see that you are not doing anything in the entryRemoved method ?

Copy link
Member Author

Choose a reason for hiding this comment

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

It's a slightly more accurate representation of the lrucache we're using. The default implementation calculates size by number of objects, and here we're calculating size by bitmap allocation the same as in the class. Regarding mocking, we discussed before to use the actual lrucache and not a mock in order to make use of the cache in the tests

Copy link
Member

Choose a reason for hiding this comment

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

but if you provide this custom implementation in your tests this means that you are altering the class functionality ? This would be acceptable if you want to mock other methods in order to isolate one test case but if you do this for the whole test - suite how is this going to be relevant anymore ? You say here: It's a slightly more accurate representation of the lrucache we're using . Would this mean that you are changing your internal logic ?

@codecov-commenter
Copy link

codecov-commenter commented Aug 9, 2023

Codecov Report

Merging #1564 (0f66105) into feature/base64-jmoskovich (8002635) will decrease coverage by 0.14%.
Report is 1 commits behind head on feature/base64-jmoskovich.
The diff coverage is 68.80%.

@@                      Coverage Diff                      @@
##           feature/base64-jmoskovich    #1564      +/-   ##
=============================================================
- Coverage                      83.32%   83.19%   -0.14%     
=============================================================
  Files                            443      444       +1     
  Lines                          15070    15095      +25     
  Branches                        2272     2279       +7     
=============================================================
  Hits                           12557    12557              
- Misses                          1923     1941      +18     
- Partials                         590      597       +7     
Files Changed Coverage Δ
...android/sessionreplay/internal/utils/CacheUtils.kt 15.79% <18.75%> (+15.79%) ⬆️
...roid/sessionreplay/internal/utils/DrawableUtils.kt 96.97% <50.00%> (ø)
...ay/internal/recorder/mapper/BaseWireframeMapper.kt 70.59% <58.33%> (-7.98%) ⬇️
...eplay/internal/recorder/base64/BitmapPoolHelper.kt 62.50% <62.50%> (ø)
...nreplay/internal/recorder/base64/Base64LRUCache.kt 76.74% <66.67%> (-8.10%) ⬇️
...eplay/internal/recorder/base64/Base64Serializer.kt 84.51% <78.95%> (-4.23%) ⬇️
...ssionreplay/internal/recorder/base64/BitmapPool.kt 87.27% <83.33%> (+25.30%) ⬆️
...adog/android/sessionreplay/SessionReplayPrivacy.kt 100.00% <100.00%> (ø)
...id/sessionreplay/internal/utils/InvocationUtils.kt 100.00% <100.00%> (ø)

... and 15 files with indirect coverage changes

@jonathanmos jonathanmos force-pushed the jmoskovich/replay-1890/bitmap-pool branch from 227afe9 to 8002635 Compare August 9, 2023 09:27
@jonathanmos jonathanmos force-pushed the jmoskovich/replay-1943/refactor-cache-singletons branch from 29f2c89 to 97f7fd3 Compare August 9, 2023 09:58
Base automatically changed from jmoskovich/replay-1890/bitmap-pool to feature/base64-jmoskovich August 9, 2023 10:13
val bitmapPool = BitmapPool()
val base64LRUCache = Base64LRUCache()

val builder = Base64Serializer.Builder()
Copy link
Member

Choose a reason for hiding this comment

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

Builder methods should be chainable, it's the pattern we've been using in the SDK.
E.g. Configuration.kt allows a use case such as this SampleApplication.kt .

Copy link
Member Author

@jonathanmos jonathanmos Aug 14, 2023

Choose a reason for hiding this comment

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

I'm modifying the builder in order to pass the dependencies in the builder constructor, so the setters won't exist anymore

)

private companion object {
private const val BITMAP_OPERATION_FAILED = "operation failed for bitmap pool"
Copy link
Member

Choose a reason for hiding this comment

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

This seems like a highly generic and non actionable message, could we improve it to give more context of what failed?

Copy link
Member Author

Choose a reason for hiding this comment

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

I've added the stacktrace to the log to give additional context


internal companion object {
@Suppress("MagicNumber")
internal val MAX_CACHE_MEMORY_SIZE_BYTES = 4 * 1024 * 1024 // 4MB
Copy link
Member

Choose a reason for hiding this comment

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

Curiosity: Did we do any study into the optimal cache size?

Copy link
Member Author

@jonathanmos jonathanmos Aug 14, 2023

Choose a reason for hiding this comment

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

This is more of a starting point to improve upon later. All images are downscaled to have a maximum size of around 15kb, so we can calculate for 4mb a capacity of around 270 cached images - which is more than enough even for an app with only images in a grid. Working out the optimal cache size would be quite complicated, given the different use cases of customers - apps with lots of images vs apps with few.

We could think about implementing a cache that autoadjusts its size based upon recorded performance from previous runs (record the maximum percentage utilization and adjust the size of the cache accordingly) but this will add some complexity.

@jonathanmos jonathanmos force-pushed the jmoskovich/replay-1943/refactor-cache-singletons branch from 97f7fd3 to 1d39248 Compare August 14, 2023 09:11
mariusc83
mariusc83 previously approved these changes Aug 16, 2023
Copy link
Member

@mariusc83 mariusc83 left a comment

Choose a reason for hiding this comment

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

nicely done !!

plousada
plousada previously approved these changes Aug 21, 2023
@jonathanmos jonathanmos dismissed stale reviews from plousada and mariusc83 via d0f2fa2 August 23, 2023 07:46
@jonathanmos jonathanmos force-pushed the jmoskovich/replay-1943/refactor-cache-singletons branch 2 times, most recently from c7b5c2f to 0f66105 Compare August 23, 2023 09:41
@jonathanmos jonathanmos force-pushed the jmoskovich/replay-1943/refactor-cache-singletons branch from 0f66105 to be9a3a8 Compare August 24, 2023 11:23
@jonathanmos jonathanmos force-pushed the jmoskovich/replay-1943/refactor-cache-singletons branch from be9a3a8 to 5be1cc0 Compare August 24, 2023 12:03
@jonathanmos jonathanmos merged commit 69f88d0 into feature/base64-jmoskovich Aug 24, 2023
@jonathanmos jonathanmos deleted the jmoskovich/replay-1943/refactor-cache-singletons branch August 24, 2023 13:08
@xgouchet xgouchet added this to the 2.2.0 milestone Dec 13, 2023
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