It’s a very common scenario: some process wants to drop a file on a server every 30 minutes or so. Simple, right? Well, I can think of a bunch of ways this could go wrong.
For instance, processing a file may take more or less than 30 minutes, so it’s possible for a new file to arrive before I’m done with the previous one. I don’t want the source system to overwrite a file that I’m still processing.
On the other hand, the files are large, so it takes a few minutes to finish uploading them. I don’t want to start processing a partial file. The files are just tranferred with FTP or sftp (my preference), so OS-level locking isn’t an option.
Finally, I do need to keep the files around for a while, in case I need to manually inspect one of them (for debugging) or reprocess one.
I’ve seen a lot of ad-hoc approaches to shuffling upload files around, swapping filenames, using datestamps, touching ‘indicator’ files to assist in synchronization, and so on. What I haven’t seen yet is a comprehensive ‘algorithm’ for processing files that addresses concurrency, consistency, and completeness.
So, I’d like to tap into the wisdom of crowds here. Has anyone seen a really bulletproof way to juggle batch data files so they’re never processed too early, never overwritten before done, and safely kept after processing?
The key is to do the initial juggling at the sending end. All the sender needs to do is:
completed.Assuming there is only a single receiver process, all the receiver needs to do is:
completeddirectory for any files.completed, move it to a subdirectory called e.g.processed, and start working on it from there.On any sane filesystem, file moves are atomic provided they occur within the same filesystem/volume. So there are no race conditions.
Multiple Receivers
If processing could take longer than the period between files being delivered, you’ll build up a backlog unless you have multiple receiver processes. So, how to handle the multiple-receiver case?
Simple: Each receiver process operates exactly as before. The key is that we attempt to move a file to
processedbefore working on it: that, and the fact the same-filesystem file moves are atomic, means that even if multiple receivers see the same file incompletedand try to move it, only one will succeed. All you need to do is make sure you check the return value ofrename(), or whatever OS call you use to perform the move, and only proceed with processing if it succeeded. If the move failed, some other receiver got there first, so just go back and scan thecompleteddirectory again.