Wandering Thoughts archives

2015-07-15

Eating memory in Python the easy way

As a system administrator, every so often I need to put a machine under the stress of having a lot of its memory used. Sometimes this is for testing how things respond to this before it happens during live usage; sometimes this is because putting a system under memory stress can cause it to do important things it doesn't otherwise do (such as reclaim extra memory). The traditional way to do this is with a memory eater program, something that just allocates a controlled amount of memory and then (usually) puts actual data in it.

(If you merely allocate memory but don't use it, many systems don't consider themselves to be under memory stress. Generally you have to make them use up actual RAM.)

In the old days, memory eater programs tended to be one-off things written in C; you'd malloc() some amount of memory then carefully write data into it to force the system to give you RAM. People who needed this regularly might keep around a somewhat more general program for it. As it turns out, these days I don't need to go to all of that work because interactive Python will do just fine:

$ /usr/bin/amd64/python2.6
[...]
>>> GB = 1024*1024*1024
>>> a = "a" * (10 * GB)

Voila, 10 GB eaten. Doing this interactively gives me great flexibility; for instance, I can easily eat memory in smaller chunks, say 1 GB at a time, so that I have more control over exactly when the system gets pushed hard (instead of perhaps throwing it well out of its comfort zone all at once).

There are some minor quibbles you can make here; for example I'm not using only exactly 10 GB of memory, since Python has some small overhead for objects and so on. And you probably want to specifically use bytestrings in Python 3, not the default Unicode strings.

In practice I don't care about the quibbles because this is close enough for me and it's really convenient (and flexible), far more so than writing a C program or re-finding the last one I wrote for this.

(If CPython allocates much additional internal memory to create this 10 GB string, it's not enough to be visible on the scale of GBytes of RAM usage. I tried a smaller test and didn't see more than perhaps a megabyte or two of surprising memory usage, but in general if you need really fine control over memory eating you're not going to want to use Python for it.)

PS: It makes me unreasonably happy to able to use Python interactively for things like this, especially when they're things I might have had to write a C program for in the past. It's just so neat to be able to just type this stuff out on the fly, whether it's eating memory or testing UDP behavior.

EatingMemory written at 23:01:16; Add Comment

2015-07-06

My Django form field validation and cleanup pain

Our Django based account request system has quite a number of (HTTP) forms that all reflect and manipulate the same underlying model data. Because these are different forms (and some of them are in complex dynamic situations), they of course all have different form (Python) classes. Some of you may already be seeing my problem here: a certain number of these fields need to be cleaned up and validated.

There are two problems here. The first is that in Django, form field validation and cleanup is attached to the form, not to the field. If you have five different forms all using the same field, that means five different forms need a clean_<field> method, even if all of these methods call the same code. The state of the code right now is actually that most of the forms do not have field cleanup and validation because they were restricted to administrative users and I was either overlooked the issue originally or was lazy. The second problem is that some of the forms want to generate somewhat different validation error messages for certain validation failures.

(Specifically, normal outside people submitting account requests need different error messages than staff who are basically doing data entry.)

I was going to say that Django doesn't support clean_<field> functions on models, but that's kind of incorrect. You don't get individual field methods, but you can clean and adjust data in an overall model clean() method. This deals with some but not all of my issues (eg the different error messages problem still remains) and it creates new ones; I'm essentially enforcing user interface restrictions at the database layer. I'm also not sure how happy Django will be if I do database lookups in a model clean() method.

(My view layer is actually deliberately more restrictive than the model layer right now. To put it one way, the view layer is concerned with saving people from errors while the model layer is concerned with hard data integrity constraints.)

All of this suggests that what I need to do is pull out most of the current .clean_<field> validation code into standalone functions and then find some easy way to add them to forms while handling error messages. Probably I will experiment with mixin classes that just have appropriate .clean_<field> methods.

(Now I wonder what happens if you have a .clean_<field> method for a field not defined in your form. Probably nothing good can come of that in the long run even if it works today in current Django.)

On the whole I wish that Django allowed you to attach form field cleanup logic directly to the field and then easily reuse field definitions between forms (right now this probably causes issues). Overall validation is part of the form, but individual field validation and cleanup really feels like it belongs to the field instead, and validation is already partly a field responsibility.

(Possibly you actually can hijack form field validators in order to also do cleanup, but if so I can't find any documentation on it so I'm not going to touch it.)

DjangoFormCleanupPain written at 00:25:41; Add Comment

By day for July 2015: 6 15; before July; after July.

Page tools: See As Normal.
Search:
Login: Password:
Atom Syndication: Recent Pages, Recent Comments.

This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.