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

Display Posts Widget: speed and stability improvements. #3207

Merged

Conversation

bisko
Copy link
Contributor

@bisko bisko commented Dec 24, 2015

This Pull Request contains speed and reliability improvements of the Display Posts Widget.

The following things have changed since the previous version:

  • Blog information and posts are collected in the background through wp_cron, so there would be no page load blocks when the cache expires and new information is pulled.
  • Widget instance information is now unified and stored permanently in an option and transients usage has been dropped from the main widget functionality.
  • Cache storage size has been greatly reduced.
  • Widget load and render speed has been improved and made more consistent.
  • Errors are now more informative and are shown in the widget configuration.
  • Errors shown to visitors in the widget area have been reduced. If we have blog posts cached, we always show the last copy we have, not an error.
  • Some general code improvements and HTML fixes.

@jeherve jeherve added [Type] Enhancement Changes to an existing feature — removing, adding, or changing parts of it [Feature] Extra Sidebar Widgets [Status] Needs Review This PR is ready for review. labels Jan 4, 2016
@jeherve jeherve added this to the 3.9 milestone Jan 4, 2016
* Add a 10 minute interval to cron intervals.
*/
add_filter( 'cron_schedules', 'jetpack_display_posts_widget_cron_intervals' );
function jetpack_display_posts_widget_cron_intervals() {
Copy link
Member

Choose a reason for hiding this comment

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

This looks wrong. A filter should accept a value, modify (or replace it), and then return it.

What would happen if another plugin were adding a minutes_30 interval via the cron_schedules hook?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

All previously set custom intervals would have been rewritten if left as such.

I updated the method (34df2dfd43e26d520e5a09c8b441913a921bedfc) to work as expected and not overwrite the schedules.

@mdawaffe
Copy link
Member

mdawaffe commented Jan 6, 2016

Jetpack uses cron in a couple other places. If the Jetpack plugin is disabled (or if all the Display Posts Widgets are removed from the active sidebars), it'd be nice if the this widget's cron task were unscheduled.

See, for example, Jetpack_Heartbeat::deactivate() and it's use at

Jetpack_Heartbeat::init()->deactivate();

@mdawaffe
Copy link
Member

mdawaffe commented Jan 6, 2016

Can you make sure the brace style is correct in this code?

/**
* No body has been set in the response. This should be pretty bad.
*/
if ( ! isset( $service_response['body'] ) ) {
Copy link
Member

Choose a reason for hiding this comment

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

It's better to use wp_remote_retrieve_body() to access this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated it in 5a546e02815c75fab298b59651542d055e52e713.

@mdawaffe
Copy link
Member

mdawaffe commented Jan 6, 2016

What will happen if a site already has one (or more) of these widgets active in their sidebar with the current version of Jetpack and then upgrades Jetpack. Will the widget still work?

@mdawaffe
Copy link
Member

mdawaffe commented Jan 6, 2016

I suspect this has always been a problem, but:

If I leave the "Blog URL" field blank in the widget's form, there's no error message in the form, and the widget just always says "Cannot load blog information at this time." in the sidebar.

* Prepare the error messages.
*/

$where_message = __( 'An error occurred while downloading ', 'jetpack' );
Copy link
Member

Choose a reason for hiding this comment

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

It's always best to put the full sentence/phrase/thought into a single translation call. Can we rephrase this/these somehow?

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 updated the code relating to that in 9a154e831b5823626dc787b780607b06a84a5579. Is this a better way to handle such situation?

@bisko
Copy link
Contributor Author

bisko commented Jan 7, 2016

What will happen if a site already has one (or more) of these widgets active in their sidebar with the current version of Jetpack and then upgrades Jetpack. Will the widget still work?

The widgets will still work after the update as there were no changes to the configuration format and how it is saved.

There will be a bit of time after the update that the widgets will show an error, as the cron needs to be run at least once, in order to gather data for the widget and store it in the new format. Is this a good trade-off?

@bisko
Copy link
Contributor Author

bisko commented Jan 7, 2016

I suspect this has always been a problem, but:

If I leave the "Blog URL" field blank in the widget's form, there's no error message in the form, and the widget just always says "Cannot load blog information at this time." in the sidebar.

Yes, no error was shown if there was no URL specified. I added an error message in 1795ca3de8a394400081a98003e77ce625ff4767.

@bisko
Copy link
Contributor Author

bisko commented Jan 7, 2016

Jetpack uses cron in a couple other places. If the Jetpack plugin is disabled (or if all the Display Posts Widgets are removed from the active sidebars), it'd be nice if the this widget's cron task were unscheduled.

See, for example, Jetpack_Heartbeat::deactivate() and it's use at

Jetpack_Heartbeat::init()->deactivate();

I added functionality (in c3e1fc28771ccd626357984fdb896ec49397aa1e) to disable the cron when the widget is disabled in Jetpack settings and if there are no active instances of the widget.

Currently it is not very optimal, as it relies on the shutdown hook, which may not be executed if Jetpack is completely disabled, but I have yet to find a better way to do it.

@zinigor
Copy link
Member

zinigor commented Jan 12, 2016

Overall the changes look very nice, thanks for the awesome effort!

I can confirm that the existing widgets work fine after an upgrade, but when you add new ones, you have to wait a considerable amount of time. The problem right now is that if the user does not know the inner workings, to him/her this looks like an error. Should we perhaps:

  • trigger a synchronous update if there have been no previously stored options for that URL?
  • tell a user that they will see the error for some time? (That's probably not the best option)
  • hide the widget if there's nothing in cache? (That's probably even worse than the previous)

I'm happy to merge this as it is now on the beta stage, but only if we figure out a solution to the problem and implement it quickly enough before 3.9 hits.

@bisko
Copy link
Contributor Author

bisko commented Jan 14, 2016

@zinigor I improved the case of adding a new widget as per your suggestion.

It seems the most optimal way (transparent to the user) would be to force-fetch new data when saving new widget instance.

With the update, when a user adds a widget instance, it checks if there is a cache entry for the specified URL. If there isn't any entry in the cache, a forced fetch is done. This way the new widget data is shown immediately.

If an error occurs with the data fetch, it will be shown when the widget is saved, so the user will have immediate feedback if the remote fetch fails.

@zinigor
Copy link
Member

zinigor commented Jan 15, 2016

Thank you for making changes! It works great! Now in order to clean up the branch, can you please fast-forward the master branch in your repo and rebase this branch so it wouldn't have any merge commits? Let me know if you need help with that.

@zinigor zinigor added [Status] Ready to Merge Go ahead, you can push that green button! and removed [Status] Needs Review This PR is ready for review. labels Jan 15, 2016
bisko added 19 commits January 15, 2016 16:51
…or_storage()`

Added tests for `format_posts_for_storage()`.
Added error handling in format_posts_for_storage, because it was missing and it was relying on being supplied with proper data.
…dling in some methods.

Added tests for `cron_task()`, `get_instances_sites()` and `update_instance()` methods.
Improved error handling in `get_instances_sites()`.
Removed error handling in `update_instance()` method, because it was not necessary there.
…s in `extract_errors_from_blog_data()` method.

Wrong key, which was undefined in the context, was used in `reset()` call. It has been replaced with the proper key.

Unit tests have been added to check for this case.
…log_data()` method with tests.

Covered `extract_errors_from_blog_data()` with tests.

Improved the error debug concatenation, to show a more readable message.
The widget was accessible by directly calling the PHP file. Check was added at the top of the file in order to prevent direct access.
…als() now respects other custom cron schedules.

`jetpack_display_posts_widget_cron_intervals()` missed function parameter that was passed from `wp_get_schedules()`, containing other custom schedules. This caused all other schedules that were defined beforehand to be overwritten. Schedules, passed to the function are now properly handled and updated.

Unit tests were added to make sure that it won't break everything again.
…on_update` action to be more clear that it is part of Jetpack.

 `display_posts_widget_cron_update` was missing the `jetpack_` prefix, which could have lead to some confusion that it was part of Jetpack. It has been properly prefixed now.
…outside widget constructor.

 `check_for_cron()` call was made inside the constructor of the widget. Since there can be multiple instances of the widget, the check would have been called multiple times, which is not very optimal.

 The check is now done when the `wp_loaded` is fired.
…pdate cron name to a static class property.

The cron name will be used in several more places in the future (deactivation and checks) and it is better to be a variable, not hardcoded string.
…instead of directly accessing `body` property of service response.

It is better to use `wp_remote_retrieve_body()` to extract request body and use it, instead of directly accessing the response property. This way we are safe of eventual future changes of service response format.
…better.

Functions that related to fetching information for the remote blog were not ordered properly, which could have lead to confusion on how are they called and related to each other.

Now they are grouped together and properly ordered.
…ation when there is no blog URL specified.

There was no error when an empty URL field was left in the widget configuration. Now an error is shown if the URL field is empty.
…sabled if it is not needed.

Previously if the widget was activated once, there was no way to stop the update cron and it would have been running in the background forever. Functionality has been added to allow cron disabling when it is not needed.

A check is run on every shutdown to see if the cron should be running or not and the appropriate action is taken.
…ted and deactivated in more consistent way.

Code was run on every shutdown of WordPress to check if the cron should have been running or not. This was a great overhead in cases where the widget wasn't needed to be shown.

Now the cron is activated on the proper activation points - When Jetpack gets updated, when Extra Sidebar Widgets module gets activated and when Jetpack gets activated (if it was not activated before).

Deactivation remains what it was before - when Extra Sidebar Widgets module gets deactivated and when Jetpack gets deactivated.
… widget save if it was not in cache.

After saving the widget, widget data had to rely on the cron to be fetched. That could have lead to a user saving the widget and waiting for 10+ minutes, before seeing valid data on the sidebar and not an error.

 Now when the user saves widget configuration, if the specified site URL doesn't have cache entry, it is force-fetched and put in the cache, so the user can see the result immediately.

 Additionally remote requests are now cached in a static variable cache, to compensate for multiple requests that can happen when the widget is saved. It sheds a few seconds load time.
@bisko bisko force-pushed the update/display_posts_widget_improvement branch from fe7989c to fff5b6c Compare January 15, 2016 15:00
@zinigor
Copy link
Member

zinigor commented Jan 18, 2016

Tested again after a rebase, works like a charm, thanks! :shipit:

dereksmart added a commit that referenced this pull request Jan 18, 2016
…vement

Display Posts Widget: speed and stability improvements.
@dereksmart dereksmart merged commit 5f5b4b5 into Automattic:master Jan 18, 2016
@dereksmart dereksmart removed the [Status] Ready to Merge Go ahead, you can push that green button! label Jan 18, 2016
* If Jetpack is deactivated, the cron is not needed.
*/
add_action( 'jetpack_deactivate_module_widgets', 'Jetpack_Display_Posts_Widget::deactivate_cron_static' );
register_deactivation_hook( 'jetpack/jetpack.php', 'Jetpack_Display_Posts_Widget::deactivate_cron_static' );
Copy link
Member

Choose a reason for hiding this comment

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

Jetpack may not be installed at jetpack/jetpack.php.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in #3303

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants