Currently fontyourface_preprocess_page() re-writes fonts.css on every page load. The file only needs to be re-written when there's something new. Maybe delete the file when fontyourface_enable_font(), fontyourface_disable_font(), or fontyourface_set_css_selector() are called, and then write it in fontyourface_preprocess_page() only if it's not already there?

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

sreynen’s picture

Version: 7.x-1.x-dev » 7.x-2.x-dev

This should happen in 7.x-2.x now.

BarisW’s picture

Component: Code » Code (general)
Assigned: Unassigned » BarisW

I'll have a look ;)

BarisW’s picture

Assigned: BarisW » Unassigned

I'm currently very busy, so unassigning this issue for now.

sreynen’s picture

Category: feature » bug

Marked #1571002: performance problem when using Amazon S3 storage as a duplicate of this. Since that's causing actual problems, changing this to a bug.

BTMash’s picture

Have you looked at using the ctools css plugin (http://drupalcode.org/project/ctools.git/blob/refs/heads/7.x-1.x:/includ...)? I think it might be able to help on a few different fronts for you on this aspect (write to the css store, retrieve from the css store which will do all the necessary checks for you).

sreynen’s picture

Title: Only re-write font.css when fonts change. » Only re-write font.css when fonts change

I hadn't looked at that previously, but I just looked at it now. I think you're right that it could handle the writing and reading of CSS for us. Unfortunately it doesn't handle detecting changes in CSS, which is the big thing we're currently missing. It would have been good to use that when we started, but I'm not eager to add a new dependency now just to offload functionality we've already written.

I think what we need to do here is move the CSS re-writing to a function, and call that function whenever a font is changed in any way (enabling, disabling, changing selectors, etc.). The trick will be tracking down everywhere we're changing fonts.

BarisW’s picture

Status: Active » Needs review

I've added a small check to determine if we need to overwrite the css file:

    // Only overwrite the CSS when it differs.
    if ($css != file_get_contents($destination)) {
      file_unmanaged_save_data($css, $destination, FILE_EXISTS_REPLACE);
    }

This is certainly not the prettiest solution, but it should solve this issue until we have a better solution.

I've committed it to dev. Please let me know it this works for you.

sreynen’s picture

Title: Only re-write font.css when fonts change » Only re-write font.css when CSS changes
Status: Needs review » Fixed

Good idea tracking changes based on the actual file content, BarisW. I changed this a bit so it saves MD5 hashes of the CSS it writes, so it can compare the MD5 hash to see if there's a change and no longer needs to read the file. I think this should be faster, though I didn't do any formal testing.

I also split up the functionality in separate functions, fontyourface_generate_css() and fontyourface_rewrite_css(), so we could eventually trigger rewrites from somewhere other than fontyourface_preprocess_page(), e.g. when a font is enabled or on cache clear.

While there's still more we could do here, it's likely to be very small improvements (e.g. removing a variable) relative to what we've already done (removing unnecessary file writes). So I'm marking this as fixed and we can do more in separate issues.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

sreynen’s picture

Version: 7.x-2.x-dev » 6.x-2.x-dev
Status: Closed (fixed) » Patch (to be ported)

This stil needs to be ported to D6. Should be pretty similar to the D7 version, which can be seen here:

http://drupalcode.org/project/fontyourface.git/commit/0779ceb

sreynen’s picture

To be clearer, I'm not planning on writing the D6 change myself, as D6 is basically in maintenance mode now and it still works even without this fix. But I will commit a patch if someone else wants to write it.

JacobSingh’s picture

The patch in D7 (if I understand correctly) goes from writing a file every request, to reading a file every request and writing when it changes. Wouldn't it be better to just write this once? It never changes unless someone changes the enabled fonts, so just clear the cache everytime someone changes the enabled fonts, yeah?

sreynen’s picture

There were two patches here, one that changed from always-write to always-read, then another that switched to hash comparison. So the current D7 code, which should form the basis of a D6 patch, determines if a write is necessary by saving a hash of the previous write, and only writing when the hash changes. This means one write for all changes between page loads, no writes on no changes, and no reads.

I said in #8 that we might move to a rewrite-on-change model in a separate issue, but after thinking about that more, I think it's actually a bad approach, with a lot of drawbacks and little real benefit.

There's no single change event we could use to trigger a re-write. Enabling and disabling fonts is a change, but changing selectors and fallback fonts is as well. In some cases, those changes happen individually, so we'd want the individual change to trigger the re-write, while in other changes they happen in bulk, so we wouldn't want to trigger a series of re-writes on each individual change. And because the preview pages need to load fonts that aren't enabled, moving to an on-change re-write approach would force us to use a completely separate method of loading those fonts.

And all of that additional complication would really only save us some simple string comparison, which is very fast. We'd still need to check which fonts are enabled on every page load, since we need to pass that info to the provider submodules.

So I think we should stick with the current D7 approach.

JacobSingh’s picture

I haven't looked at the D7 code, but there were numerous issues with the D6 code.

1). The race condition that could occur if two different pages try to write to font.css with different fonts.
2). The file locking that occurs when two different threads try to write to the same file name every request (this is why I'm here, it was bring down a huge site).
3). We were using 100% remote webfonts and it was writing an empty file every time.

In the case where it is 100% remote, it should never write (obviously). This is the 95% case IMO. For people storing local fonts, I would just write the entire thing on cron / on change to simplify. If people need to load different fonts on different pages, I would let them figure out how to hardcode it.

Best,
Jacob

JacobSingh’s picture

Oh sorry, the point of my comment was that hashing is good, but writing to the same filename everytime is bad because it can cause file locking if several threads open up concurrently and race to write to the file. Not as bad as without hashing, but better to write the file as the hash $long_ass_hash.css

-J

sreynen’s picture

That's a good point on potential conflicting writes. I opened a separate issue for it: #1655066: Two users editing fonts at the same time could cause conflicting writes to fonts.css

Drave Robber’s picture

Status: Patch (to be ported) » Needs review
FileSize
1.22 KB

Here's a patch against 6.x-2.x-dev.

Neslee Canil Pinto’s picture

Issue summary: View changes
Status: Needs review » Closed (outdated)