Skip to content

Commit

Permalink
Merge branch 'master' into writeback-cache
Browse files Browse the repository at this point in the history
  • Loading branch information
jacobsa committed Aug 13, 2015
2 parents e5e08e0 + 8ecfe19 commit 9033692
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 29 deletions.
18 changes: 16 additions & 2 deletions docs/semantics.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ behavior is controlled by the `--stat-cache-ttl` flag, which can be set to a
value like `10s` or `1.5h`. (The default is one minute.) Positive and negative
stat results will be cached for the specified amount of time.

`--stat-cache-ttl` also controls the duration for which gcsfuse allows the
kernel to cache inode attributes. Caching these can help with file system
performance, since otherwise the kernel must send a request for inode attributes
to gcsfuse for each call to `write(2)`, `stat(2)`, and others.

**Warning**: Using stat caching breaks the consistency guarantees discussed in
this document. It is safe only in the following situations:

Expand Down Expand Up @@ -224,11 +229,20 @@ actor in the meantime.) There are no guarantees about whether local
modifications are reflected in GCS after writing but before syncing or closing.

Modification time (`stat::st_mtime` on Linux) is tracked for file inodes, and
can be updated in usual the usual way using `utimes(2)` or `futimens(2)`.
(Special case: mtime updates may be silently lost for unlinked inodes.) When
can be updated in usual the usual way using `utimes(2)` or `futimens(2)`. When
dirty inodes are written out to GCS objects, mtime is stored in the custom
metadata key `gcsfuse_mtime` in an unspecified format.

There are two special cases worth mentioning:

* mtime updates to unlinked inodes may be silently lost. (Of course content
updates to these inodes will also be lost once the file is closed.)

* If an mtime update is made to a GCS object remotely (e.g. due to another
system running gcsfuse calling `utimes(2)`) and an inode has already been
assigned for that object locally, the mtime update will not be reflected
locally.

<a name="file-inode-identity"></a>
### Identity

Expand Down
2 changes: 1 addition & 1 deletion flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ func newApp() (app *cli.App) {
cli.DurationFlag{
Name: "stat-cache-ttl",
Value: time.Minute,
Usage: "How long to cache StatObject results from GCS.",
Usage: "How long to cache StatObject results and inode attributes.",
},

cli.DurationFlag{
Expand Down
78 changes: 61 additions & 17 deletions internal/fs/fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,18 @@ type ServerConfig struct {
// See docs/semantics.md for more info.
ImplicitDirectories bool

// How long to allow the kernel to cache inode attributes.
//
// Any given object generation in GCS is immutable, and a new generation
// results in a new inode number. So every update from a remote system results
// in a new inode number, and it's therefore safe to allow the kernel to cache
// inode attributes.
//
// The one exception to the above logic is that objects can be _deleted_, in
// which case stat::st_nlink changes. So choosing this value comes down to
// whether you care about that field being up to date.
InodeAttributeCacheTTL time.Duration

// If non-zero, each directory will maintain a cache from child name to
// information about whether that name exists as a file and/or directory.
// This may speed up calls to look up and stat inodes, especially when
Expand Down Expand Up @@ -128,6 +140,7 @@ func NewServer(cfg *ServerConfig) (server fuse.Server, err error) {
syncer: syncer,
tempDir: cfg.TempDir,
implicitDirs: cfg.ImplicitDirectories,
inodeAttributeCacheTTL: cfg.InodeAttributeCacheTTL,
dirTypeCacheTTL: cfg.DirTypeCacheTTL,
uid: cfg.Uid,
gid: cfg.Gid,
Expand Down Expand Up @@ -215,9 +228,10 @@ type fileSystem struct {
// Constant data
/////////////////////////

tempDir string
implicitDirs bool
dirTypeCacheTTL time.Duration
tempDir string
implicitDirs bool
inodeAttributeCacheTTL time.Duration
dirTypeCacheTTL time.Duration

// The user and group owning everything in the file system.
uid uint32
Expand Down Expand Up @@ -825,6 +839,30 @@ func (fs *fileSystem) unlockAndMaybeDisposeOfInode(
fs.unlockAndDecrementLookupCount(in, 1)
}

// Fetch attributes for the supplied inode and fill in an appropriate
// expiration time for them.
//
// LOCKS_REQUIRED(in)
func (fs *fileSystem) getAttributes(
ctx context.Context,
in inode.Inode) (
attr fuseops.InodeAttributes,
expiration time.Time,
err error) {
// Call through.
attr, err = in.Attributes(ctx)
if err != nil {
return
}

// Set up the expiration time.
if fs.inodeAttributeCacheTTL > 0 {
expiration = time.Now().Add(fs.inodeAttributeCacheTTL)
}

return
}

////////////////////////////////////////////////////////////////////////
// fuse.FileSystem methods
////////////////////////////////////////////////////////////////////////
Expand All @@ -851,8 +889,11 @@ func (fs *fileSystem) LookUpInode(
defer fs.unlockAndMaybeDisposeOfInode(child, &err)

// Fill out the response.
op.Entry.Child = child.ID()
if op.Entry.Attributes, err = child.Attributes(ctx); err != nil {
e := &op.Entry
e.Child = child.ID()
e.Attributes, e.AttributesExpiration, err = fs.getAttributes(ctx, child)

if err != nil {
return
}

Expand All @@ -872,7 +913,7 @@ func (fs *fileSystem) GetInodeAttributes(
defer in.Unlock()

// Grab its attributes.
op.Attributes, err = in.Attributes(ctx)
op.Attributes, op.AttributesExpiration, err = fs.getAttributes(ctx, in)
if err != nil {
return
}
Expand Down Expand Up @@ -925,9 +966,9 @@ func (fs *fileSystem) SetInodeAttributes(
}

// Fill in the response.
op.Attributes, err = in.Attributes(ctx)
op.Attributes, op.AttributesExpiration, err = fs.getAttributes(ctx, in)
if err != nil {
err = fmt.Errorf("Attributes: %v", err)
err = fmt.Errorf("getAttributes: %v", err)
return
}

Expand Down Expand Up @@ -993,11 +1034,12 @@ func (fs *fileSystem) MkDir(
defer fs.unlockAndMaybeDisposeOfInode(child, &err)

// Fill out the response.
op.Entry.Child = child.ID()
op.Entry.Attributes, err = child.Attributes(ctx)
e := &op.Entry
e.Child = child.ID()
e.Attributes, e.AttributesExpiration, err = fs.getAttributes(ctx, child)

if err != nil {
err = fmt.Errorf("Attributes: %v", err)
err = fmt.Errorf("getAttributes: %v", err)
return
}

Expand Down Expand Up @@ -1057,11 +1099,12 @@ func (fs *fileSystem) CreateFile(
fs.mu.Unlock()

// Fill out the response.
op.Entry.Child = child.ID()
op.Entry.Attributes, err = child.Attributes(ctx)
e := &op.Entry
e.Child = child.ID()
e.Attributes, e.AttributesExpiration, err = fs.getAttributes(ctx, child)

if err != nil {
err = fmt.Errorf("Attributes: %v", err)
err = fmt.Errorf("getAttributes: %v", err)
return
}

Expand Down Expand Up @@ -1107,11 +1150,12 @@ func (fs *fileSystem) CreateSymlink(
defer fs.unlockAndMaybeDisposeOfInode(child, &err)

// Fill out the response.
op.Entry.Child = child.ID()
op.Entry.Attributes, err = child.Attributes(ctx)
e := &op.Entry
e.Child = child.ID()
e.Attributes, e.AttributesExpiration, err = fs.getAttributes(ctx, child)

if err != nil {
err = fmt.Errorf("Attributes: %v", err)
err = fmt.Errorf("getAttributes: %v", err)
return
}

Expand Down
19 changes: 10 additions & 9 deletions mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,15 +83,16 @@ func mount(

// Create a file system server.
serverCfg := &fs.ServerConfig{
CacheClock: timeutil.RealClock(),
Bucket: bucket,
TempDir: flags.TempDir,
ImplicitDirectories: flags.ImplicitDirs,
DirTypeCacheTTL: flags.TypeCacheTTL,
Uid: uid,
Gid: gid,
FilePerms: os.FileMode(flags.FileMode),
DirPerms: os.FileMode(flags.DirMode),
CacheClock: timeutil.RealClock(),
Bucket: bucket,
TempDir: flags.TempDir,
ImplicitDirectories: flags.ImplicitDirs,
InodeAttributeCacheTTL: flags.StatCacheTTL,
DirTypeCacheTTL: flags.TypeCacheTTL,
Uid: uid,
Gid: gid,
FilePerms: os.FileMode(flags.FileMode),
DirPerms: os.FileMode(flags.DirMode),

AppendThreshold: 1 << 21, // 2 MiB, a total guess.
TmpObjectPrefix: ".gcsfuse_tmp/",
Expand Down

0 comments on commit 9033692

Please sign in to comment.