6

I have a directory that contains data files served to clients, say, /srv/data. While making a series of updates, I am working on /srv/data_tmp, and at the end of the operation, I would like to atomically replace data with data_tmp. File.renameTo() always returns false for me when the destination is an existing directory. How can I do this?

Jean-Philippe Pellet
  • 59,296
  • 21
  • 173
  • 234
  • Do you have any constraints on the underlying file system. Obviously, if it's FAT, there's no way since the file system doesn't support it. Do you know whether the two directories are on the same physical volume? Can you use the new JDK7 java.nio.file APIs or does it have to work on a JDK <= 6? – Mike Samuel Dec 08 '10 at 17:06
  • 1
    I don't think that two file move operations can be done "atomically", in the strict sense of the word. Some filesystems guarantee some [atomic operations](http://www.softpanorama.org/Internals/Filesystems/ntfs.shtml) but I don't know of any way to request to the OS that multiple FS actions are done atomically to all processes; certainly not in Java. – maerics Dec 08 '10 at 17:13
  • @Mike: I'll be using HFS+, ext3 or NTFS, never FAT. It should work with JDK 6. – Jean-Philippe Pellet Dec 08 '10 at 17:53
  • @Mike: and, the two directories will always be on the same physical volume – Jean-Philippe Pellet Dec 08 '10 at 17:59

4 Answers4

1

You could replace the /srv/data directory with a symbolic link (or a junction in Windows XP), and change the link's target when appropriate. You won't be able to do that with a Java 6 API though - You'd have to rely on a library or write the command line commands yourself.

NB: I don't guarantee anything about the atomicity of that operation.

Community
  • 1
  • 1
Jean Hominal
  • 16,518
  • 5
  • 56
  • 90
  • The symbolik link approach is interesting, but you're wrong saying that you can't rename a file to a file that already exists. If both a and b exist in `/home/me`, then this successfully overwrites b with a (on Mac OS X/HFS+): `new File("/home/me/a").renameTo(new File("/home/me/b")` – Jean-Philippe Pellet Dec 08 '10 at 18:24
1

The Linux rename system call doesn't allow this (the rename system call can only overwrite an empty directory), so I doubt it's possible to do in Java on Linux.

Ken Bloom
  • 57,498
  • 14
  • 111
  • 168
  • Is the call to `rename` atomic? And if so, also in the case of overwriting? – Jean-Philippe Pellet Dec 08 '10 at 18:54
  • Yes, but according to the [man page](http://linux.die.net/man/2/rename) "when overwriting there will probably be a window in which both oldpath and newpath refer to the file being renamed." – Ken Bloom Dec 08 '10 at 19:02
1

I am afraid you can't. Not at the SO level at least. So even if you manage "atomicity" in the context of your java application, you have no guarantee about some other "rogue" process interfering at the actual filesystem level.

If I were you, I'd read this article (quite old, but should give you some ideas) and then see if you can port the suggested approach to a more modern version .

Oh, wait, someone did this already!

And apparently your aren't the first one to ask here, either

Best of luck...

Community
  • 1
  • 1
p.marino
  • 6,244
  • 3
  • 25
  • 36
  • The Apache Commons Transactions allow me to do this in a very neat way, albeit only in the context of my Java app. Which is enough for me now. Thanks for your link! – Jean-Philippe Pellet Dec 09 '10 at 10:22
0

Achieving this goal is entirely possible using a combination of "symlink" and "rename", along with an intermediate tmp directory. The following example is in shell, but you could easily translate the functionality here to use the underlying calls:

mkdir -p tmp/real_dir1 tmp/real_dir2
touch tmp/real_dir1/a tmp/real_dir2/a
# start with ./target_dir pointing to tmp/real_dir1
ln -s tmp/real_dir1 target_dir
# create a symlink named target_dir in tmp, pointing to real_dir2
ln -sf tmp/real_dir2 tmp/target_dir
# atomically mv it into ./ replacing ./target_dir
mv tmp/target_dir ./

Example here taken from: http://axialcorps.wordpress.com/2013/07/03/atomically-replacing-files-and-directories/

This boils down to (in pseudo-code):

mkdir('./tmp');
mkdir('./tmp/real_dir1');
mkdir('./tmp/real_dir2');
symlink('./tmp/real_dir1', './target_dir')
symlink('./tmp/real_dir2', './tmp/target_dir')
rename('./tmp/target_dir', './target_dir')

The final rename here is atomic, so the action will either succeed or fail entirely, from the point of any process using the directory, the action is atomic.

mssaxm
  • 191
  • 1
  • 2