Live data from Hacker News

Things Unix can do atomically (2010)

rcrowley.org

61–68 of 68 posts

Re: Things Unix can do atomically (2010)

#61

Earlier quoted context omitted.

That's what people would expect. But there was some drama around ext4, renames and fsync a few years ago.

You mean around truncation and rewrite?

It is not necessarily about truncation. IIRC the problem was that rename doesn't (didn't?) act as a full barrier on ext4 and the metadata write that updates the name from the old file to the new file can be committed to disk before the updates to the new file. This means that after a crash the new name might point to a corrupted file.

The barrier behavior wasn't explicitly mandated by POSIX, but it is an intuitive release consistency-like model which was implicitly expected by most programmers.

edit: spurious words and parens.

Re: Things Unix can do atomically (2010)

#64
post #59
post #43

Earlier quoted context omitted.

> It is a common mistake to try to use files for locking, for example, instead of using the more robust flock(1). Why is this a mistake? It is my understanding that, if all the locking you need is a simple mutex, creating a file with a well-defined name with O_CREAT | O_EXCL is atomic -- the file will either be created or not (in which case the call will fail with EEXIST), and no two processes can possibly both succe…

The flock() method is preferable when you don't need to use NFS because as you say it'll automatically clean the lock up if the process holding it dies. This gets rid of all the edge cases with stale locks in one fell swoop. But as you point out if you want to do this e.g. over NFS you should create a file, but then you need to deal with stale locks. If you can at all avoid that using flock() is generally better.

http://0pointer.de/blog/projects/locking.html claims that flock() is less reliable over NFS (returns true without actually locking anything on Linux And my instinct is that in a networked scenario, you're at least as worried about a machine dying as a process on the machine (i.e. a network partition). A flock()-based lock doesn't clean itself up if the client is unreachable, does it?

Re: Things Unix can do atomically (2010)

#65
post #2

In a few simple words, can someone explain what does "atomically" mean? I personally used this term when talking about some Redis operations, but never knew the real gist of the word and concepts behind it. I have a very brief understanding of the term and if I'd have to explain it to a person, I'd say it's "the operation that does not have any side effects when performing its unit of work". Is my understanding even…

Why one would like to have an atomic operation is easier to understand.

For example, one can use the atomic nature of creating a symbolic link on nix to create a lock file to prevent a race condition in a forking shell script. Say you have two or more processes wanting to do something that can (or should) only be done by one process at a time; one naive solution is to manage access of each process to said action by using a lock file. However, writing or touching a file itself is not atomic.

The answer is to throw a symbolic link into the mix. In this scenario, the lock file already exists. However, the lock is not the file itself, but a symbolic link to the file. The protocol for each process to follow is:

1. try to create a symbolic link to lock file (any file really)

2. if successful, proceed; if failed, wait (or exit)

3. when process is done, delete symbolic link to lock file

Simply checking for the existence of the symlink is not sufficient since there is a period of time between checking for the symlink (or file) and proceeding with said action where another process can think it has the lock.

The OS ensures that one and only one symlink (of the same name) can exist; attempts to create it again (even simultaneously) will result in a failure of one process to create the symlink. There is one winner; all others are losers. This is to say, the kernel ensures that the operation is atomic. As a result, the OS is now arbitrating what process can proceed to action, at the very lowest level. Another way to think about it is that it provides a way to make competing processes serialize - or get in line so that they may complete their action one at a time.

In my experience, it is important to experiment and test to make sure that the atomic primitive you're using is actually working as expected. I've run up against some inconsistent implementations of symlink creation that make this action not as straightforward to use as one is lead to believe.

Re: Things Unix can do atomically (2010)

#66
post #64
post #59

Earlier quoted context omitted.

The flock() method is preferable when you don't need to use NFS because as you say it'll automatically clean the lock up if the process holding it dies. This gets rid of all the edge cases with stale locks in one fell swoop. But as you point out if you want to do this e.g. over NFS you should create a file, but then you need to deal with stale locks. If you can at all avoid that using flock() is generally better.

http://0pointer.de/blog/projects/locking.html claims that flock() is less reliable over NFS (returns true without actually locking anything on Linux And my instinct is that in a networked scenario, you're at least as worried about a machine dying as a process on the machine (i.e. a network partition). A flock()-based lock doesn't clean itself up if the client is unreachable, does it?

Yes as I pointed out you don't want this if you're doing NFS.

Personally I prefer something like a MySQL table with GET_LOCK() to process things instead of NFS if I need multiple machines. It gives you flock() like semantics in that if a machine or client goes away the GET_LOCK() is automatically freed, i.e. it survives as long as the connection to the database survives.

Not having to deal with stale locks generally sucks way less than the extra overhead of a database.

For any NFS-based scenario you usually end up creating a "task" "task.underway" and "task.done" files as locks, and re-enqueuing tasks if you have a "underway" file that's too old without a "done" file.

You'd do the same with a MySQL table that you GET_LOCK() on, except you can safely re-enqueue "underway" tasks if you acquire the lock on them, since you know their consumers have gone away.

Re: Things Unix can do atomically (2010)

#67
post #43
post #24

Earlier quoted context omitted.

edit: I just realized you said "renaming." Original comment left below, but I edited before I get downvoted for a classic reading comprehension fail. Atomicity requires that the leakage mentioned shall not occur from any context aside from its own internal context. That makes your example somewhat of a simplification because these state transitions are visible to other processes. It is a common mistake to try to use…

> It is a common mistake to try to use files for locking, for example, instead of using the more robust flock(1). Why is this a mistake? It is my understanding that, if all the locking you need is a simple mutex, creating a file with a well-defined name with O_CREAT | O_EXCL is atomic -- the file will either be created or not (in which case the call will fail with EEXIST), and no two processes can possibly both succe…

Technically, you're right that it's atomic to create a file. But creating a lock using a file can be deceptive and is a common pitfall in my experience. I have seen a lot of shell scripts take this form:

  if [ ! -f $FILE ] ; then
   touch $FILE
   # do something dangerous, assuming I have a lock
   rm $FILE
  fi
The problem here is, of course, that I've checked whether the file exists, but another process (even a concurrent execution of the same script) could remove $FILE after I've checked that it doesn't exist. Now I (or any other process) can happily proceed to create $FILE, thinking that no one else is executing simultaneously. Actually, if I ran two executions of this script at about the same time, they could both pass this check and executed the (mistakenly expectedly) "synchronized" block.

Of course, you don't have to use flock(1) to make this operation atomic. It just handles a lot of the extra work that I don't want to have to think about, even if I did set `noclobber` or something like that.

Re: Things Unix can do atomically (2010)

#68
post #10
post #3

Earlier quoted context omitted.

Not really. It just means that it's indivisible (the original meaning of "atom"). Either it succeeds or fails, you never have to worry about it being half-finished. This includes actions which are so small they are literally indivisible, or actions which roll back to the original state if they fail.

Got it. Now it makes more sense to me. Now I know people tend to talk about atomicity when it comes to low-level-ish things. But say I create some sort of a web service with a bunch of business logic. Does it worth to follow this principle in that case? For instance, client sends an API request (let's say "Add user to friends"), is it even possible to apply atomicity for these type of things? Edit: Thanks everyone fo…

(A bit late, but hoping that you see this:)

If possible you should probably go for an even stronger property, namely Idempotence[1]. (This can be relatively easy if you can force clients to provide some sort of unique token for every operation.)

It's usually makes this even easier to reason about for clients since they can just retry anything while knowing that it doesn't matter if they retry an already "applied" operation.

[1] https://en.wikipedia.org/wiki/Idempotence

Post reply on HN