[Gluster-users] file size coherence issue
Jason Ferrara
jason.ferrara at jacquette.com
Sun Jan 19 01:05:36 UTC 2014
I'm having an issue where glusterfs is reporting the incorrect size for
recently changed files.
On client A, a process acquires a write lock on a file (using fcntl) and
begins to modify the file. On client B, a process attempts to a aquire a
write lock on the same file and blocks. The process on client A
finishes its modifications, calls fsync, and releases the lock. The
process on client B then successfully acquires the lock and reads the
file. At this point while the contents of the file match what was
written on client A, the size of the file is still reported as the old size.
The program below can be used to demonstrate the problem. It repeatedly
locks a file, reads in the whole file, checks that a string at the
beginning of the file that indicates the expected file size matches the
actual size read, and then truncates the file and writes out new data of
a random size. Running it on the same file from two different clients I
get things like...
Client A:
locked
Old size: 531
New size: 459
unlocked
Client B:
locked
Old size: 799
New size: 531
unlocked
locked
Short read. Expected 531 but got 459
File size now reported as 531
Size indicated in file is 459
Press enter to exit
After client A creates a 459 byte file and releases the lock, when
client B accesses the file, seeking to the end of the file reports the
end as 531 (the previous size created by client B), while a read of the
file only returns 459 bytes, and the string at the beginning of the file
is the 459 written by client A.
If I add a sleep(1) after acquiring the lock then everything works as
expected.
I'm using gluster 3.4.1, with a single server running CentOS 6.5, with
multiple bricks (no replication) formatted with xfs. The clients are
running Fedora 19. Filesystems mounted with
attribute-timeout=0,entry-timeout=0,acl,selinux. Is there some other
option I need to maintain client file metadata coherence?
#include <unistd.h>
#include <fcntl.h>
#include <iostream>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string>
#include <string.h>
#include <errno.h>
#include <sstream>
#include <vector>
void errorExit()
{
std::cout << "Press enter to exit" << std::endl;
std::cin.ignore();
exit(EXIT_FAILURE);
}
void makeFile(int fd, int fileSize)
{
if (ftruncate(fd, 0)==-1)
{
std::cerr << "Can't truncate file. " << strerror(errno) <<
std::endl;
errorExit();
}
std::ostringstream buf;
buf << fileSize << std::endl;
while(buf.str().size() < fileSize)
buf << "0";
if (lseek(fd, 0, SEEK_SET) == -1)
{
std::cerr << "Can't seek in file. " << strerror(errno) <<
std::endl;
errorExit();
}
if (write(fd, buf.str().c_str(), buf.str().size()) == -1)
{
std::cerr << "Can't write to file. " << strerror(errno) <<
std::endl;
errorExit();
}
if (fsync(fd) == -1)
{
std::cerr << "Can't write to file. " << strerror(errno) <<
std::endl;
errorExit();
}
}
int main(int argc, const char* argv[])
{
std::string filePath = std::string(argv[1]);
struct stat statBuf;
if (stat(filePath.c_str(), &statBuf) != 0)
{
int fd = open(filePath.c_str(), O_RDWR | O_CREAT, S_IRUSR |
S_IWUSR);
if (fd == -1)
{
std::cerr << "Couldn't create file" << strerror(errno) <<
std::endl;
return EXIT_FAILURE;
}
int s = (rand() % 1024) + 100;
makeFile(fd, s);
close(fd);
}
while(true)
{
int fd = open(filePath.c_str(), O_RDWR);
if (fd == -1)
{
std::cerr << "Couldn't open file" << std::endl;
errorExit();
}
struct flock lock;
lock.l_start = 0;
lock.l_whence = SEEK_SET;
lock.l_len = 0;
lock.l_type = F_WRLCK;
if (fcntl(fd, F_SETLKW, &lock)==-1)
{
std::cerr << "couldn't get lock" << std::endl;
errorExit();
}
std::cout << "locked" << std::endl;
off_t fileSize = lseek(fd, 0, SEEK_END);
if (fileSize == -1)
{
std::cerr << "Couldn't get file size" << strerror(errno) <<
std::endl;
errorExit();
}
if (lseek(fd, 0, SEEK_SET) == -1)
{
std::cerr << "Couldn't seek to beginning of file." <<
strerror(errno) << std::endl;
errorExit();
}
std::vector<char> buffer(fileSize);
ssize_t bytesRead = read(fd, &buffer[0], fileSize);
if (bytesRead == -1)
{
std::cerr << "Error reading file." << strerror(errno) <<
std::endl;
errorExit();
}
std::istringstream instream(std::string(&buffer[0],
buffer.size()));
if (bytesRead != fileSize)
{
std::cerr << "Short read. Expected " << fileSize << " but
got " << bytesRead << std::endl;
fileSize = lseek(fd, 0, SEEK_END);
std::cerr << "File size now reported as " << fileSize <<
std::endl;
int s;
instream >> s;
std::cerr << "Size indicated in file is " << s << std::endl;
errorExit();
}
int expectedSize;
instream >> expectedSize;
if (expectedSize != fileSize)
{
std::cerr << "Expected " << expectedSize << " bytes in file
but found " << fileSize << std::endl;
errorExit();
}
std::cout << "Old size: " << fileSize << std::endl;
int newSize = (rand() % 1024) + 100;
std::cout << "New size: " << newSize << std::endl;
makeFile(fd, newSize);
lock.l_type = F_UNLCK;
if (fcntl(fd, F_SETLKW, &lock)==-1)
{
std::cout << "couldn't release lock" << std::endl;
errorExit();
}
std::cout << "unlocked" << std::endl;
close(fd);
}
}
--
Jason Ferrara
Jacquette Consulting, Inc.
710 Providence Road
Malvern, PA 19355
jason.ferrara at jacquette.com
More information about the Gluster-users
mailing list