Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

Is there an easy way to ensure that external feeds (via fetch_feed()) are only fetched via cron, and not when a regular user visits the site? This is for performance reasons, I want to minimize the page load time.

The only time a feed should load in a normal request are probably when the cache is empty (the first time it is loaded) and maybe when a logged in user is visiting the page (because I will be the only one with a user account).

share|improve this question
    
I had looked into exactly this while and tried to code it, but failed. SimplePie is coded for file cache, WP extends cache class and fakes files with transients... I got lost in that rather quickly. Maybe worth revisiting... –  Rarst Feb 21 '11 at 18:20
    
Looking for some clarification here. By default, fetch_feed() is only used on the admin dashboard and in the front-end RSS widget. In a vanilla WP installation, it won't be called anywhere else. So are you using fetch_feed() in a plug-in and need to enable some form of caching? –  EAMann Feb 24 '11 at 17:06
    
@EAMann: Indeed, I use it in some widgets that I created. So it's all for code I control, and it's no problem to change the fetch_feed() to another function call. –  Jan Fabry Feb 24 '11 at 17:51
    
@EAMann There are ways to handle this manually (such as wrappers, etc) but holy grail here would be to make native fetch_feed() get all feeds asynchronously in cron and never when user loads front-end page (as per native mechanics). Between complex caching logic, filter-able lifetime for individuals feeds and no centralized tracking of which feeds are requested this is quite tricky. –  Rarst Feb 24 '11 at 20:19

3 Answers 3

My recommendation would be to set up a wrapper for fetch_feed(). Call the wrapper function through WordPress' cron and you shouldn't have an issue.

So something like:

function schedule_fetch_feeds() {
    if ( ! wp_next_scheduled( 'cron_fetch' ) ) {
        wp_schedule_event( time(), 'hourly', 'cron_fetch', 'http://blog1.url/feed' );
        wp_schedule_event( time(), 'hourly', 'cron_fetch', 'http://blog2.url/feed' );
    }
}

function fetch_feed_on_cron( $url ) {
    $feed = fetch_feed( $url );

    delete_transient( "feed-" . $url );

    set_transient( "feed-" . $url, $feed, 60*60 );
}

add_action( 'wp', 'schedule_fetch_feeds' );
add_action( 'cron_fetch', 'fetch_feed_on_cron', 10, 1 );

Keep in mind, I haven't had a chance to test this yet! But it should create cron jobs to grab each of your feeds and store the feeds temporarily in transients. The transients have 1-hour expirations, because the cron should realistically be updating them every hour anyway.

You can pull the feeds out of the transients using:

function get_cached_feed( $url ) {
    $feed = get_transient( "feed-" . $url );
    if ( $feed ) return $feed;

    $feed = fetch_feed ( $url );
    set_transient( "feed-" . $url, $feed, 60*60 );
    return $feed;
}

If the transient exists, the function grabs it and returns it. If it doesn't, the function will grab it manually, cache it, and still return it.

share|improve this answer

Breaking my own rule and adding a second answer ... but for a very specific reason ...

I took a closer look at the actual core code for fetch_feed() in response to Rarst's comment on the original question:

holy grail here would be to make native fetch_feed() get all feeds asynchronously in cron and never when user loads front-end page

The actual function code is:

function fetch_feed($url) {
    require_once  (ABSPATH . WPINC . '/class-feed.php');

    $feed = new SimplePie();
    $feed->set_feed_url($url);
    $feed->set_cache_class('WP_Feed_Cache');
    $feed->set_file_class('WP_SimplePie_File');
    $feed->set_cache_duration(apply_filters('wp_feed_cache_transient_lifetime', 43200, $url));
    do_action_ref_array( 'wp_feed_options', array( &$feed, $url ) );
    $feed->init();
    $feed->handle_content_type();

    if ( $feed->error() )
        return new WP_Error('simplepie-error', $feed->error());

    return $feed;
}

To save you the trouble of reading up on the SimplePie() object ... the $feed->init() function first checks to see if we're caching the feed and, if so, retrieves from the cache rather than re-requesting the feed from the original source.

Each feed it cached for 43200 seconds (or 12 hours) which is the lifetime of the transient. You could modify this up or down by using the 'wp_feed_cache_transient_lifetime' filter.

To Address the Original Question

Feeds aren't re-fetched via cron. They're fetched once, then cached for future use. The only time a feed is loaded when a user first visits the page is when the cache is empty. For a high-traffic site, this should be relatively rare.

So if you're facing performance issues related to feeds, you've probably got some other issue going on.

share|improve this answer
    
Love that answer. I have not dug into the whole feed thing at all yet, and this answer is well formatted, easy to follow, gives great background info and provides a very clear pointer to an easy solution path. Splendid. –  wyrfel Feb 24 '11 at 20:51
    
The idea is to make them be fetched via cron. You are assuming feed with long cache lifetime and high traffic site. Which is not uncommon, but not set in stone either. What if feed has minutes cache lifetime? What if seconds? Not typical, but to make a point. Current mechanics are basically a compromise - let some random page load be slow for the sake of simpler feed fetching logic. The idea is to remove that compromise and never let any page load be slow because of a feed. –  Rarst Feb 24 '11 at 20:55
    
@Rarst I agree that would be an improvement, but I'm trying to address the question Jan originally asked which was to not load a feed on each page load for the sake of performance. Still, loading through cron would be a great idea, and I think we should look into it (on Trac, not here). –  EAMann Feb 24 '11 at 21:01
    
the title of the question is literally "Refresh external feeds only in cron?" so I think moving fetch to cron is quite relevant issue here. :) Anyway I will shut up for now and will try to find time on weekend to review my attempts at asynchronous code for this and may be come up with some practical examples on topic. –  Rarst Feb 24 '11 at 21:05
    
I know they are cached, but as Rarst pointed out, I have some feeds with a short cache lifetime and I want to load them always in the background. A solution in core would be nice, but it can take a while before this makes it in a new release. A plugin can be realized in a much shorter time. –  Jan Fabry Feb 24 '11 at 21:51

How about you store & keep them updating in the options table by the native linux cron and just fetch in on pages (database read). This way page load times won't be affected at all.

Edit: Alrite! If you are familiar with how alternate cron works in WordPress by creating another process and then continue with its work. Adapting that approach would seem to be a good solution to your case along with transients to initiate such a request.

share|improve this answer
    
This assumes that you have a list of feeds to fetch. And such list is tricky to get and maintain by code, because WP doesn't put together anything like that. Feeds are fetched individually when code gets to them one by one. –  Rarst Feb 25 '11 at 19:23
    
Ok! So Jan is looking for doing this for all fetch_feed() calls. Right? But still it would do for a specific case. I agree it can adapt for different setups though. –  Ashfame Feb 25 '11 at 19:27
    
I would use the existing WP cron service for maximum compatibility, you can still drive this via the OS cron if you want. And indeed, the "tricky" part is making this simple to setup. The ideal solution would hook into the fetch_feed() option and swap out the cache and/or file class for SimplePie, so the caller doesn't have to change their code. –  Jan Fabry Feb 26 '11 at 12:54
    
Yeah, I got it when Rarst replied. At first I didn't realise that you want a solution which hooks up with WordPress. Interesting question though! –  Ashfame Feb 26 '11 at 13:05
    
@Jan I updated the answer, please take a look –  Ashfame Feb 26 '11 at 13:10

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.