Skip to content

Introduce in-memory MediaModel cache#15215

Merged
wzieba merged 27 commits intotrunkfrom
remove_media_model_from_db
Jan 23, 2026
Merged

Introduce in-memory MediaModel cache#15215
wzieba merged 27 commits intotrunkfrom
remove_media_model_from_db

Conversation

@wzieba
Copy link
Contributor

@wzieba wzieba commented Jan 19, 2026

Closes: AINFRA-542: Migrate MediaModel

Description

This PR migrates MediaModel from being an ORM model to a stored in-memory POJO. This reduces configuration overhead, while not degrading user experience. See: p91TBi-dHl-p2

It also removes a decent portion of unused properties and classes, that happened to not be used anymore.

The goal of this PR was not to improve every aspect of handling MediaModel as it'd simply require too many changes for a single PR. The list of remaining improvements, while probably not exhousted, can be found in AINFRA-1764: Address remaining Media* improvements in Woo Android

Test Steps

We need to test media upload for two connection types: WPCOM and Application Password.

  • WPCOM is when users log via WPCOM account
  • Application Passwords can be easily tested by creating a Jurassic page with Woo but without Jetpack and log in via site credentials

Scenarios to test

Images/gif

There are no UI changes.

  • I have considered if this change warrants release notes and have added them to RELEASE-NOTES.txt if necessary. Use the "[Internal]" label for non-user-facing changes.

@dangermattic
Copy link
Collaborator

dangermattic commented Jan 19, 2026

1 Warning
⚠️ This PR is larger than 300 lines of changes. Please consider splitting it into smaller PRs for easier and faster reviews.

Generated by 🚫 Danger

@wzieba wzieba force-pushed the remove_media_model_from_db branch from 7f13942 to 8ce2bf2 Compare January 19, 2026 12:23
Base automatically changed from remove_upload_store to remove_media_xmlrpc_logic January 19, 2026 12:53
@wzieba wzieba force-pushed the remove_media_model_from_db branch from 8ce2bf2 to bdf752e Compare January 19, 2026 12:57
@wpmobilebot
Copy link
Collaborator

wpmobilebot commented Jan 19, 2026

📲 You can test the changes from this Pull Request in WooCommerce-Wear Android by scanning the QR code below to install the corresponding build.
App NameWooCommerce-Wear Android
Platform⌚️ Wear OS
FlavorJalapeno
Build TypeDebug
Commitf10272b
Direct Downloadwoocommerce-wear-prototype-build-pr15215-f10272b.apk

@wzieba wzieba force-pushed the remove_media_model_from_db branch 2 times, most recently from 6b8ad25 to 1c52bac Compare January 19, 2026 14:23
@wpmobilebot
Copy link
Collaborator

wpmobilebot commented Jan 19, 2026

📲 You can test the changes from this Pull Request in WooCommerce Android by scanning the QR code below to install the corresponding build.

App NameWooCommerce Android
Platform📱 Mobile
FlavorJalapeno
Build TypeDebug
Commitf10272b
Direct Downloadwoocommerce-prototype-build-pr15215-f10272b.apk

@wzieba wzieba force-pushed the remove_media_xmlrpc_logic branch from 1d719b1 to 74146c2 Compare January 19, 2026 14:52
@wzieba wzieba force-pushed the remove_media_model_from_db branch from 1c52bac to af3ce94 Compare January 19, 2026 14:54
@wzieba wzieba mentioned this pull request Jan 19, 2026
1 task
@codecov-commenter
Copy link

codecov-commenter commented Jan 19, 2026

Codecov Report

❌ Patch coverage is 40.74074% with 32 lines in your changes missing coverage. Please review.
✅ Project coverage is 38.69%. Comparing base (6ffc1d9) to head (f10272b).
⚠️ Report is 108 commits behind head on trunk.

Files with missing lines Patch % Lines
...dpress/android/fluxc/store/MediaCacheOperations.kt 0.00% 25 Missing ⚠️
...rdpress/android/fluxc/persistence/WellSqlConfig.kt 0.00% 4 Missing ⚠️
...n/com/woocommerce/android/media/FileUploadUtils.kt 0.00% 1 Missing ⚠️
.../woocommerce/android/media/MediaFilesRepository.kt 0.00% 1 Missing ⚠️
...xc/network/rest/wpapi/media/MediaWPRESTResponse.kt 0.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##              trunk   #15215      +/-   ##
============================================
+ Coverage     38.68%   38.69%   +0.01%     
- Complexity    10546    10558      +12     
============================================
  Files          2193     2195       +2     
  Lines        124761   124767       +6     
  Branches      17247    17246       -1     
============================================
+ Hits          48258    48277      +19     
+ Misses        71624    71609      -15     
- Partials       4879     4881       +2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Base automatically changed from remove_media_xmlrpc_logic to trunk January 20, 2026 08:29
@wzieba wzieba force-pushed the remove_media_model_from_db branch from af3ce94 to 45c1791 Compare January 20, 2026 10:09
wzieba added 12 commits January 20, 2026 11:55
We don't need these to be stored in a database.
More internally: paaHJt-9qo-p2

`MediaIdGenerator` will be necessary for a temporary id during the media upload
…ry cache

`MediaCacheOperations` is created only to delegate as much as possible to a Kotlin class instead of modifying `MediaStore.java`
It wasn't used anywhere
We don't use database anymore for storing `MediaModel`
The constructor was used only in tests. This commit also refactors and moves `MediaTestUtils` to testFixtures
Refactor `MediaTestUtils` to use Java-friendly builder pattern
`fetch` was for me very misleading as, at least for me, it strongly indicates a remote source (like backend/API). This method only calls `FileUploadUtils.mediaModelFromLocalUri` which is purely local operation, to prepare a file for upload.
`MediaStore` can't conflict with FluxC anymore since 41b4c5d
@wzieba wzieba force-pushed the remove_media_model_from_db branch from 45c1791 to cfe92ff Compare January 20, 2026 10:55
return filterByMimeType(siteId, "application")
}

fun searchSiteImages(siteId: Int, searchTerm: String): List<MediaModel> {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We can't test search* methods because the app doesn't enable searching

As it's like this for years, we should probably update contract with MediaPicker library to not being forced to provide search methods if searching is hidden.

fun addOrUpdate(localSiteId: Int, media: MediaModel) {
val currentList = cache[localSiteId] ?: emptyList()
val mutableList = currentList.toMutableList()
val existingIndex = mutableList.indexOfFirst { it.mediaId == media.mediaId }
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This class operates on mediaId (remote id) because we only cache MediaModel that we fetched from remote.

public MediaModel getSiteMediaWithId(@NonNull SiteModel siteModel, long mediaId) {
List<MediaModel> media = MediaSqlUtils.getSiteMediaWithId(siteModel, mediaId);
return media.size() > 0 ? media.get(0) : null;
media.setId(mMediaIdGenerator.generate(media.getFilePath()).getValue());
Copy link
Contributor Author

Choose a reason for hiding this comment

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

instantiateMediaModel is only used for upload, hence a generated, local-only id. We should probably remove instantiateMediaModel method - there's no point to inject a heavy MediaStore only to call this constructor. I've added it to the list of improvements.

After all of the clean ups it came out clearly, that `mUploadState` isn't really used in any meaningful way in the Woo app. The progress and state of uploads is tracked via different mechanisms.
@wzieba wzieba force-pushed the remove_media_model_from_db branch from 288a185 to 521e2e4 Compare January 20, 2026 15:19
@wzieba wzieba added the type: technical debt Represents or solves tech debt of the project. label Jan 20, 2026
@wzieba wzieba added this to the 24.0 milestone Jan 20, 2026
@wzieba wzieba marked this pull request as ready for review January 20, 2026 16:24
@wzieba wzieba requested a review from ParaskP7 January 20, 2026 16:24
@ParaskP7 ParaskP7 requested a review from Copilot January 21, 2026 14:07
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request migrates MediaModel from being a database-backed ORM model to an in-memory POJO, introducing a new cache-based architecture. The change removes database persistence overhead and eliminates unused properties like upload state tracking, alt text, and thumbnail URLs.

Changes:

  • Introduced MediaLibraryCache for in-memory storage of media items per site
  • Added MediaCacheOperations to handle filtering and searching operations
  • Implemented TimestampMediaIdGenerator for generating unique local media IDs
  • Removed database operations from MediaStore and replaced with cache operations
  • Simplified MediaModel by removing unused fields and upload state tracking
  • Updated database schema to drop the MediaModel table (migration 239)

Reviewed changes

Copilot reviewed 30 out of 30 changed files in this pull request and generated 14 comments.

Show a summary per file
File Description
MediaLibraryCache.kt New singleton cache using ConcurrentHashMap to store media lists per site
MediaCacheOperations.kt Operations layer for filtering and searching cached media by mime type
TimestampMediaIdGenerator.kt Generates local IDs using timestamp and filepath hash
MediaIdGenerator.kt Interface for ID generation strategy
MediaModule.kt Dagger module providing MediaIdGenerator and Clock dependencies
MediaStore.java Replaced database operations with cache operations, simplified upload handling
MediaModel.java Removed ORM annotations, upload state, alt text, thumbnail URL, and made most fields final
WellSqlConfig.kt Added migration 239 to drop MediaModel table
MediaTestUtils.kt New test fixture utilities with builder pattern for creating test media
MediaLibraryCacheTest.kt Unit tests for cache operations
TimestampMediaIdGeneratorTest.kt Unit tests for ID generation
MediaStoreTest.java Updated to use cache instead of database
MediaSqlUtils.java Deleted - database operations no longer needed
RestUploadRequestBody.java Deleted - replaced by WPRestUploadRequestBody
MediaWPComRestResponse.java Deleted - no longer used
MediaResponseUtils.kt Deleted - no longer used
WPRestUploadRequestBody.kt Removed alt_text parameter from upload
MediaWPRESTResponse.kt Removed alt_text field from response parsing
BaseWPV2MediaRestClient.kt Removed upload state setting on failure
FileUploadUtils.kt Removed null MediaUploadState parameter
FluxCModule.kt Added MediaModule to Dagger component
Product/Media test files Updated to use MediaTestUtils builders

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

… now

Using a shared builder is both cleaner and safer.
I simply forgot to do this in 521e2e4
To maintain the equals-hashCode contract
In this tests, we invoke `addOrUpdate` 10 times, on 10 different threads, at the same moment.

Without making this method atomic, `MediaLibraryCache` won't record all 10 updates.

This test should fail.
This asserts that accessing a specific `, List<MediaModel>` is locked when one of the threads operates on it.
Copy link
Contributor

@ParaskP7 ParaskP7 left a comment

Choose a reason for hiding this comment

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

👋 @wzieba !

I have reviewed and tested this PR as per the instructions, everything works as expected, good job! 🌟 x 🌟 ^ 🌟


I have left few questions (❓), a couple of suggestions (💡) and some minor (🔍) comments for you to consider. I am going to approve this PR anyway, since none is blocking. I am NOT going to merge this PR yet to give you some time to apply any of my suggestions. However, feel free to ignore them and merge the PR yourself.

Also move tests from `fluxc-tests` module to the appropriate module (where SUT is declared)
We actually don't ever call this method in production code. We don't have to, as it's only in-memory cache and identified via local site id (so when user changes sites, the old cache is not reused).
@wpmobilebot wpmobilebot modified the milestones: 24.0, 24.1 Jan 23, 2026
@wpmobilebot
Copy link
Collaborator

Version 24.0 has now entered code-freeze, so the milestone of this PR has been updated to 24.1.

@wzieba wzieba merged commit ccea3f5 into trunk Jan 23, 2026
20 of 21 checks passed
@wzieba wzieba deleted the remove_media_model_from_db branch January 23, 2026 11:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: technical debt Represents or solves tech debt of the project.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants