2

I made cache_form table work (see How to keep cache_form small). But cache_form, though expired cache is now cleared, is enormous anyway (nearly 2 Gb, and we have only 1000 unique users in a day). How can I change expire time? Is this caching really needed?

BTW, can you recommend any module for caching handling in Drupal? Seems there are lot of them but mostly obsolete.

  • Form cache is not a cache at all, it's a bad and misleading name. It's a storage for forms that are being processed, but - opposite to a true cache - it's not disposable. If you delete real cache prematurely, all you get is a performance hit. If you delete form cache prematurely, form being filed breaks and user gets some bizarre errors like "illegal choice", or his form contend missing between steps. More elaborate explanation I have posted here. So be very careful about decreasing form's lifetime. – Mołot Dec 10 '13 at 8:16
5

Look at the function that's used to save form "cache", form_set_cache:

function form_set_cache($form_build_id, $form, $form_state) {
  // 6 hours cache life time for forms should be plenty.
  $expire = 21600;

As you can see, it's hardcoded and you can't really change it without hacking core. Now the question is - should you?

{form_cache} as cache

Form data is kept as a cache bin, but it is not a "real" cache. Let's look at the definition from Wikipedia:

In computer science, a cache (/ˈkæʃ/ KASH) is a component that transparently stores data so that future requests for that data can be served faster.

Now, that's pretty much what {form_cache} is not - it is kept to save temporary data that needs to be consistent, and that are never stored anywhere else during form processing. It means that when someone is using APC or Memcache, he needs to take extra care to prevent their "it can be deleted without any reason, for performance purposes" approach from corrupting forms. Most Drupal admins I know simply exclude form cache bin from them.

If you will shrink the time data is kept, you risk all kinds of errors. So keep in mind that your form cache bin should preserve data significantly longer than you expect anyone to need between first form display and the end of it's processing.

  • Thanks, really great answer. The point that bothers me is that now cache_form takes 2 Gb in DB, and we are looking towards 3x, 4x or even 5x more users.. It seems that we are going towards really huge db – Lambrusco.Newbie Dec 10 '13 at 8:56
  • @Lambrusco.Newbie I agree with your concern, really. But you asked about this particular solution, so I answered about it to the best of my knowledge. To figure out what really got wrong we would need to see what form (or forms) uses {form_cache} most (I bet there are up to 3 that makes more than 50% of rows, but that's just a feeling), and then see their structure. And when you know that, it's the material for another question. – Mołot Dec 10 '13 at 8:58
  • 1
    Here's an issue to help mitigate this: drupal.org/node/1286154 – cam8001 Jun 7 '16 at 12:03
1

To contradict the accepted answer given by Mołot , I'll note that hacking core to change this expiry-time is not necessary; I was able to achieve this using a database-trigger. (I actually increase the expiry-time by 18 hours in the code I show here, but you can modify it to suit your needs).

Note that you will need to check settings.php to determine what <PREFIX> should actually be below.

If the connected database is MySQL:

delimiter //
CREATE TRIGGER form_state_lifetime_hack BEFORE INSERT ON <PREFIX>_cache_form FOR EACH ROW 
BEGIN
    SET NEW.expire = NEW.expire + (18*3600);
END;//
delimiter ;

For PostgreSQL it would be more like:

CREATE OR REPLACE FUNCTION form_state_lifetime_hack() RETURNS trigger AS
$$
    BEGIN
        NEW.expire = NEW.expire + (18*3600);
        RETURN NEW;
    END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER form_state_lifetime_hack BEFORE INSERT ON <PREFIX>_cache_form 
FOR EACH ROW EXECUTE PROCEDURE form_state_lifetime_hack();

Note also that Drupal stores data for both $form and $form_state variables in cache_form, so you might want to add an IF block within your trigger, depending on which one you want to target. You can do this based off of the "cid" column:

+-------------------------------------------------------------+
| cid                                                         |
+-------------------------------------------------------------+
| form_form-bCxaf_A-P5-IRAmXv6K7gadd0zFF3C-UoLsIyovQMvI       |
| form_form-VV8a20AjZHWE9TkhXb1urlNNjzLmnibHulO3oH8QJiw       |
| form_form-ZQGYHDpBMqrNsQlxDoVovuiqcH_oVduYHnaqf4VijAI       |
| form_state_form-4mn85-8W5mDpsqXcTVHNsNy2Jk5G20onUt1CgropnY8 |
| form_state_form-ZQGYHDpBMqrNsQlxDoVovuiqcH_oVduYHnaqf4VijAI |
+-------------------------------------------------------------+

In an ideal world, changing this expiry-time could be done in a more native way using db_update() from a hook_form_alter() or hook_submit() implementor-function, but unfortunately these calls occur before calls to form_set_cache(), meaning the relevant row you want to modify won't yet be present.... even if it were, you wouldn't have the new build-ID which would allow you to identify it.

For modules you plan to distribute to others, you'd need to include the appropriate CREATE TRIGGER language in the your_module_name.install-file, and decide which SQL-flavor to use based off of the local settings.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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