#59269 Add support for adding metadata in bulk
Trac ticket: https://core.trac.wordpress.org/ticket/59269
This introduces a bulk_add_metadata() function along with wrapper functions for post, user, site, term, and comment meta. This function accepts an array of key value pairs for inserting metadata in bulk. The metadata is inserted using a single database query and a single flush of the term cache, which is more performant than multiple calls to add_{type}_meta(). The performance benefit increases linearly as the number of meta fields increases.
Example
$meta = [
'key1' => 'value1',
'key2' => 'value2',
'key3' => 'value3',
];
- foreach ( $meta as $key => $value ) {
- add_user_meta( $user_id, $key, $value );
- }
+ bulk_add_user_meta( $user_id, $meta );
Performance
The below table compares the peak memory usage and time taken reported by PHPBench to add various sets of data using multiple calls to add_post_meta() versus one call to bulk_add_post_meta(). Absolute times vary depending on the system being used to run the tests (eg. my local Docker environment is slower than the GitHub Actions workflow runs), but the relative changes remain quite consistent.
Various data sizes have been tested and can be seen in the provideMetaData() method in the tests/phpbench/BulkAddMetaBench.php file. PHPBench is configured to run each test set 30 times and report the peak memory usage and the mode time taken.
Memory usage remains unchanged in the majority of cases. It peaks somewhat at the extreme end for multiple extra large data sets where meta values are between 10kb and 200kb in size. I have yet to investigate what causes this, but it's not a big concern.
Timing decreases significantly for all test cases, as expected.
| set | mem_peak | mode |
|---|---|---|
| 3 metas, 10-30 bytes | 30.383mb -0.00% | 928.586μs -61.37% |
| 20 metas, 10-200 bytes | 30.393mb -0.00% | 1.401ms -91.85% |
| 50 metas, 5-250 bytes | 30.405mb -0.00% | 2.018ms -95.30% |
| 100 metas, 5-500 bytes | 30.688mb -0.00% | 3.642ms -96.11% |
| 10 metas, 0.1-1 Kb | 30.396mb -0.00% | 1.193ms -86.82% |
| 10 metas, 1-10 Kb | 30.506mb -0.00% | 2.393ms -86.66% |
| 3 metas, 30-90 Kb | 30.934mb +1.99% | 4.162ms -51.67% |
| 10 metas, 10-100 Kb | 32.193mb +9.70% | 10.563ms -62.09% |
| 20 metas, 10-200 Kb | 37.426mb +35.22% | 36.750ms -45.43% |
Notes
- The PHPBench code and its workflow won't be included when this gets committed. It's there to demonstrate the performance improvements while working on this PR.
- Naming is hard. Perhaps
add_multiple_metadata()is a better name, there are a few other functions in core that usemultiplein their name. - The same actions and filters are fired as get fired when calling
add_{type}_meta()multiple times:-
add_{$meta_type}_metadatais called once for every key -
add_{$meta_type}_metaandadded_{$meta_type}_metaare called once for every key when the above filter hasn't short-circuited the corresponding value for a key
-
Test using WordPress Playground
The changes in this pull request can previewed and tested using a WordPress Playground instance.
WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.
Some things to be aware of
- The Plugin and Theme Directories cannot be accessed within Playground.
- All changes will be lost when closing a tab with a Playground instance.
- All changes will be lost when refreshing the page.
- A fresh instance is created each time the link below is clicked.
- Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance, it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.
For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.
Core Committers: Use this line as a base for the props when committing in SVN:
Props johnbillion, swissspidy, westonruter, jorbin, peterwilsoncc, timothyblynjacobs, wildworks.
To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.
Should we have a way to allow for adding multiple entries for the same meta key?
Maybe? I haven't had a need for it myself. Could be useful, but it would complicate the function signature as it wouldn't be able to support an associative array of meta key => meta value elements.
This ticket was featured in today's 6.9 Bug Scrub. I'd be happy if you could check out @aaronjorbin's feedback.