blob: 5be6a39af209bf1b7dab44e88082268c54d2d34e [file] [log] [blame]
// Copyright (C) 2020 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.googlesource.gerrit.plugins.events.fsstore;
import java.io.IOException;
import java.nio.file.Path;
/**
* Helper file for serialzing and storing a single type in a file on NFS.
*
* <p>Sacrifice read speed and success rates to avoid triggering bad NFS behavior when updating the
* file which can result in the file disappearing.
*
* <p>If a process on a Linux NFS client machine wants to perform an NFS rename while a process on
* the same machine has a handle to the target file (even just to read it), the Linux NFS driver
* will performa a NFS_silly_rename (it will rename the file to hopefully unique .nfs000000000xxx
* looking file) on the target file before performing the actual rename requested. The silly rename
* is to allow the process holding the file handle to not fail. This NFS_silly_rename is risky as it
* can result in the target file disappearing permanently if either a) the Linux NFS client machine
* dies before the second rename, or b) another Linux NFS client machine tries to do the same thing
* right after the source file was renamed to the target file by the first Linux NFS client.
*
* <p>This class tries to avoid this bad situation by never reading the 'path' file while updating
* it from the same process and thus never triggering the NFS_silly_rename on the 'path' file on
* ourselves. As long as the value file is only accessed by one java process per physical machine,
* using the same object, the 'path' file should never get deleted unexpectedly.
*/
public class NfsFileValue<T> extends FileValue<T> {
protected final Object pathLock = new Object(); // Use pathLock when reading or updating.
/**
* Use this constructor to use a builtin serializer (String or Long), and be sure to call init(T)
* with a value of your type for the serializer auto identification to happen.
*/
public NfsFileValue(Path path) {
this(path, (Serializer<T>) null);
}
public NfsFileValue(Path path, Serializer<T> serializer) {
super(path, serializer);
}
@Override
protected String read() throws IOException {
synchronized (pathLock) {
return super.read();
}
}
@Override
protected boolean update(Path src) throws IOException {
synchronized (pathLock) {
return Fs.tryAtomicMove(src, path);
}
}
}