How to Use HTTP Caching Like an Idiot

Posted by code_monkey_steve on May 12, 2013 May 12

I’ve found that it’s pretty easy to run your own custom blog for with zero hosting cost, and even survive some significant traffic spikes, with liberal usage of HTTP Caching in Rails. So it’s somewhat ironic that when I come to update this blog this morning (after giving the last post a good year to really take root in the Collective Unconscious), I noticed that my caching fetish had shot me in the foot.

You see1, for logged-in users (i.e. me) the blog pages have an extra panel in the sidebar for creating new posts. Since all of the blog pages are cached (with last_modified set to the update time of the most recent post), that admin panel was getting cached along with the rest of the page, and served to all public visitors (both of them). D’oh!

The quick solution was pretty simple:

  fresh_when(...)  unless current_user

That will at least keep from polluting the publicly-cached page with user- (and, especially, admin-) specific HTML. If that seems like such an obvious thing to do that only an idiot wouldn’t think to do it, then you’re probably reading the wrong blog.

For the rest of us, I’m contemplating more complicated (but safer!) solutions, to keep us from spamming the internet with our private data. The first step might just be a mechanism where the partial that renders users-specific HTML could just cancel the HTTP caching. A more scalable solution would involve Fragment Caching and Memcached. But part of me wants to go Full Paranoid with some sort of Frankensteinian melding of SafeBuffers and CanCan, so no string ever makes it to the response body unless it’s Certified Cache Safe.

You know, for idiots!

1 That’s a retroactively-foreshadowing meta-pun. You’re welcome.

comments

Small Footprint MongoDB

Posted by code_monkey_steve on Mar 25, 2012 Mar 25

MongoDB comes configured out of the box for maximum performance and reliability on production databases. But it can be a bit of a disk hog, and if you’re using a development environment with an SSD like me (which I highly recommend), disk space might be scarce. After doing a little research, I found configuration settings that significantly reduce MongoDB’s disk usage.

Edit your MongoDB configuration file (/etc/mongod.conf) and add some/all of the following:

smallfiles = true

Uses smaller data file sizes — starting at 16MB instead of 128MB — and create fewer files initially. This can save almost 200MB on small collections (each!).

oplogsize = 100 (MB)

If you’re using an oplog for replication (or just for update notifications), you can set the size smaller than the default of “5% of all disk space”.

nojournal = true

MongoDB 2.0 introduced journaling, which is great for production environments, but not very useful in development. You can disable it and save several GB.

comments

Excluding a bad RPM package

Posted by code_monkey_steve on May 12, 2011 May 12

I’m a big fan of KDE, as both a user and a developer, and Akregator is my RSS feed reader of choice. I’m also a big fan of RSS feeds, using them for almost all my regular daily information consumption.

So imagine my notable lack of delight when, after doing a regular YUM update, I discovered that the latest version of Akregator has a serious bug that makes it almost unusable. And I didn’t even want the new version anyway.

Ah, but since I’m using RPM and YUM, the fix for this sort of thing is actually pretty simple, although it took me a few minutes of reading man pages to work it out, so I thought I should share the fruits of my labor. Here’s how you exclude a bad RPM package:

First, find the last good version of the appropriate package:


$ rpm -qf `which akregator`
kdepim-4.4.11.1-2.fc14.x86_64

$ sudo yum list kdepim --showduplicates
...
Installed Packages
kdepim.x86_64        7:4.4.11.1-2.fc14
Available Packages
kdepim.x86_64        6:4.4.6-2.fc14
kdepim.x86_64        7:4.4.11.1-2.fc14

In this case, the desired version is 4.4.6 (the “6:” is the epoch, the “-2” is the release, and the “fc14” is the architecture).

Next, downgrade the package. If there are any dependency errors, you’ll also need to downgrade those packages too.


$ sudo yum downgrade kdepim-4.4.6
...
Error: Package: 7:kdepim-libs-4.4.11.1-2.fc14.x86_64
...

$ sudo yum downgrade kdepim-4.4.6 kdepim-libs
...
Removed:
  kdepim.x86_64 7:4.4.11.1-2.fc14                                                   kdepim-libs.x86_64 7:4.4.11.1-2.fc14

Installed:
  kdepim.x86_64 6:4.4.6-2.fc14                                                      kdepim-libs.x86_64 6:4.4.6-2.fc14

Complete!

And finally, edit /etc/yum.conf and add the offending package version to an exclude line:


[main]
...
exclude=kdepim-4.4.11.1 kdepim-libs-4.4.11.1

Take that, kdepims-4.4.11. plonk

comments | Tags: rpm and tips

AudioBook Log

Posted by code_monkey_steve on May 8, 2011 May 8

For over a year I’ve been commuting for almost 2 hours a day, and in an attempt to stave-off potentially lethal boredom, I’ve been passing the time listening to audiobooks. Here’s what I’ve been reading/listening to:

Note, format is: title (year), authors (readers)

While all of the books were good, the quality of the audiobook depends almost entirely on the voice of the person reading it. So far my favorites are Tom Weiner’s rich baritone and Richard Dawkins’ pleasant english accent. By far the worst, though, is anything read by Carl Sagan: I can highly recommend Carl’s plodding pace and odd word emphasis to insomniacs who don’t respond to strong drugs.

comments | Tags: books

Toward a More Perfect Mongo ODM

Posted by code_monkey_steve on Apr 23, 2011 Apr 23

MongoDB, MongoMapper, and Mongoid

I’m now well into my third Rails project using the MongoDB document database, and while I’m still a big fan of Mongo, I’ve been underwhelmed by the ODMs I’ve used. On the first project, I started with MongoMapper, which is very mature and well-supported, but was a little klunky and tried a little too hard to be like ActiveRecord.

For the second project, I switched to Mongoid, which was a huge improvement. It played nicely with ActiveSupport and ActiveModel, and had better support for doing things the Mongo way. But in the end, it had several nasty bugs related to associations and embedded documents, and the better I understood what I wanted from an ODM, the more I realized that Mongoid wasn’t it.

The Alternatives

Candy

I looked into Candy, and found its approach intriguingly fresh. Models don’t have to specify field names or types, and can be Arrays or Hashes or any other Ruby type. But I don’t like the lack of control over the serialization process (e.g. find, save, callbacks, validations, etc.), nor the absence of any sort of relational mechanism. Like Mongoid, it does have a nice query Criteria DSL, though.

Mongomatic

I’m not sure Mongomatic even qualifies as an ODM, as it doesn’t seem to do any mapping. From what I’ve seen, it’s just a thin wrapper around the Ruby MongoDB driver, adding little. I don’t know why anyone would bother using it.

MongoODM (my fork)

It could use a better name, but it’s a nice ODM, if a bit immature. I especially like its support for embedded documents, i.e. you don’t have to do anything special, just assign a variable of the specified Mongo-serializeable type (Document or otherwise) to a field, and it Just Works. It also supports Arrays and Hashes that can take any heterogeneous collection of types.

It’s also better designed under the hood than Mongoid or MongoMapper, taking full advantage of Ruby conventions to be easily hackable. MongoODM is definitely the best candidate for Perfect ODM I’ve yet seen.

The Perfect ODM

Here’s what I really in my perfect Mongo ODM:

Plays Well with Rails

Like it or not, the ActiveRecord API is the standard convention for performing DB operations. And to the extent that SQL and MongoDB are conceptually similar, they should maintain the same API. This makes it easier to integrate with other software that may assume AR conventions, but more importantly, it keeps me from having to learn and remember a whole new set of only-slightly-different APIs.

Duck typing and Other Ruby-isms

This is one big feature that ActiveRecord does not (and cannot) have, but which Mongo gives us almost for free — dynamic typing, just like native Ruby. Mongoid supports this for polymorphism, but MongoODM also supports dynamic types in Hashes and Arrays, and it was this fact that original attracted me to it. I have no problem with declaring document fields, but why should I have to specify the type? For that matter, why should I be constrained to a static type?

Schema DSL

Even though I want the freedom to store any value of any type in any field, I know that schemas are still important, both for validation and configuration management. All ODMs provide ActiveRecord-style type-specifiers and validations (Mongoid and MongoODM also use ActiveModel), but I’d like to see document schemata become a top-level object, some superset of JSON Schema, with a friendly and extensible DSL. Something like this:

class Person
    schema do
      property(:name) {
        type   String
        length 1..20
        required
      }
      property(:phone) {
        type Phone
        optional
      }
      property(:aliases) {
        type Array.of(String)
        optional
      }
      property(:vehicles) {
        type Array.of(Car, Boat, Spaceship)
        required
        default []
      }
      additional_properties false
    end
  end

Once the schema is nestled into object form, there’s a whole bunch of interesting things you can do, in addition to validations:

  • Schema versioning and heterogeneous collections
  • Data migrations and schema management
  • Client-side validations (via JSON Schema)
  • Automatic form generation (think: ActiveScaffold on steroids)
References and Associations

This area gets a bit tricky, partially because of SQL’s wretched legacy of foriegn keys and join tables, but also because the problem is just inherently difficult. Ideally, the database or ODM would provide an equivalent to Ruby’s garbage-collected memory management system, where any document field could be a reference to any other object of any type, and all objects would be automatically destroyed when no longer used.

MongoDB actually comes pretty close with their support for Database References. These allow you to assign to a document field a reference to any document in any collection. I’ve expanded MongoODM with a transparent Reference proxy, and assigning a reference to a field is as simple as calling .reference (or .ref) on a document. I’ve been looking at adding something similar for GridFS attachments, but with a reference count to allow easy sharing of large binary objects between documents.

The Future — No ODM?

This post is mostly just a dump of ideas I’ve had while working to expand MongoODM. But I find myself working more and more in Javascript, these days, and taking advantage of things like jQuery and Backbone to build rich client applications in the browser. In this situation, which I think will become more common, I don’t need so much ODM support in Ruby/Rails, and more-so in Javascript. So now I’m contemplating a MongoDB interface in JavaScript, passing through some sort of Rack proxy to perform access control. I’ll let you know how it turns out …

comments | Tags: mongodb