Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatically create folder for KV cache #1757

Merged
merged 13 commits into from
Jul 15, 2019

Conversation

obitech
Copy link
Contributor

@obitech obitech commented Jun 20, 2019

What this PR does / why we need it:

Fixes #1751

Special notes for your reviewer:

NONE

Does this PR introduce a user-facing and/or backwards incompatible change?:

NONE

Does this PR require updating code package or user-facing documentation?:

NONE

@obitech
Copy link
Contributor Author

obitech commented Jun 20, 2019

@richardartoul

@@ -616,7 +617,32 @@ func (c *client) writeCacheToFile() error {
return nil
}

func (c *client) createCacheDir() error {
path := path.Dir(c.opts.CacheFileFn()(c.opts.Prefix()))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think os.MkdirAll is what you're looking for here.

This might be helpful as an example: https://github.com/m3db/m3/blob/master/src/dbnode/server/server.go#L188

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah good point with os.MkdirAll, thanks for the hint.

I tried using lockfile.CreateAndAcquire but somehow the other tests are failing then:

--- FAIL: TestCache (0.70s)
        Error Trace:    store_test.go:198
        Error:          Received unexpected error "grpc: the client connection is closing"

--- FAIL: TestStaleDelete__FromGet (61.54s)
        Error Trace:    store_test.go:832
        Error:          Should be true
        Messages:       timed out waiting for client to read values from cache

--- FAIL: TestStaleDelete__FromWatch (62.06s)
        Error Trace:    store_test.go:917
        Error:          Should be true
        Messages:       timed out waiting for client to read values from cache

I'll just leave it with os.MkDirAll for now, but happy to take advice on how to properly implement the fs lock, if required.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I wasn't suggesting that you use lockfile.CreateAndAcquire I was just pointing to its implementation which uses os.MkdirAll (and passes the right permissions as well)

defer os.RemoveAll(tdir)
require.NoError(t, err)

cdir := path.Join(tdir, randSeq(10))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure why you need the randSeq when you're already using a new random test directory

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think when calling ioutil.TempDir, it will already create a random directory. So I'm using TempDir and then choose a random dir name cdir be created by NewStore into TempDir. Then I can test for the existence of cdir.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this feels like an overkill, a const name would do here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I can just use a const. I was thinking that there might be a case where this test could be run in parallel on the same machine, which might lead to multiple tests trying to create the same directory. But maybe I was overthinking :)

@richardartoul
Copy link
Contributor

@cw9 Mind taking a look at this? I know you had some experience with the caching

@@ -125,6 +126,28 @@ func TestNoCache(t *testing.T) {
require.Equal(t, 0, len(store.(*client).cacheUpdatedCh))
}

func TestCacheDirCreation(t *testing.T) {
ec, opts, closeFn := testStore(t)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move defer closeFn() here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

defer os.RemoveAll(tdir)
require.NoError(t, err)

cdir := path.Join(tdir, randSeq(10))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this feels like an overkill, a const name would do here.

ec, opts, closeFn := testStore(t)

tdir, err := ioutil.TempDir("", "m3tests")
defer os.RemoveAll(tdir)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this defer be called after require.NoError?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed


cdir := path.Join(tdir, randSeq(10))
fname := path.Join(cdir, "mk3kv.json")
opts = opts.SetCacheFileFn(func(string) string {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of this, can you test to make sure the dir you created is compatible with c.opts.Prefix()?, try some writes and then read from the cache maybe.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@codecov
Copy link

codecov bot commented Jun 22, 2019

Codecov Report

Merging #1757 into master will decrease coverage by 1.3%.
The diff coverage is n/a.

Impacted file tree graph

@@            Coverage Diff            @@
##           master   #1757      +/-   ##
=========================================
- Coverage    72.3%   70.9%    -1.4%     
=========================================
  Files         986      76     -910     
  Lines       83010   10472   -72538     
=========================================
- Hits        60028    7433   -52595     
+ Misses      19006    2536   -16470     
+ Partials     3976     503    -3473
Flag Coverage Δ
#aggregator 82.5% <ø> (ø) ⬆️
#cluster ?
#collector 32.2% <ø> (-31.5%) ⬇️
#dbnode 71.7% <ø> (-8.4%) ⬇️
#m3em ?
#m3ninx ?
#m3nsch ?
#metrics ?
#msg ?
#query ?
#x ?

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 0811843...e95c6f7. Read the comment docs.

path := path.Dir(c.opts.CacheFileFn()(c.opts.Prefix()))
if _, err := os.Stat(path); err != nil {
if os.IsNotExist(err) {
if err := os.MkdirAll(path, 0755); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I was a little vague about this. I think if you use os.MkdirAll you can avoid the os.Stat and os.IsNotExist. If the path is already a directory then it will just return nil so I think the existing checks are redundant.

The permissions thing is gonna be kind of annoying :/

We actually flow the new directory permissions around the codebase as options, so if you take a look at server.go you'll see that when we call lockfile.CreateAndAcquire(lockPath, newDirectoryMode) (again, you dont need to use that function, just using it as an example that does something similar) the newDirectoryMode is passed in based on whatever its configured in our FilesystemOptions so I think we'll need to allow setting a NewDirectoryMode on whatever options are used to construct this store and then flow that into here if that makes sense.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh I didn't get it the first time, sorry about that. Fixed it now.

And the permission thing was a bit annoying indeed haha. I think I got it now but it was quite the treasure hunt and I had to touch a couple of files along my way (tried to recap it in my commit message of 0d53e7e). Let me know if there is anything wrong or if I forgot anything

@robskillington
Copy link
Collaborator

Nw, triggering a build on this now for CI.

@@ -111,7 +113,8 @@ func (cfg Configuration) NewOptions() Options {
SetCacheDir(cfg.CacheDir).
SetClusters(cfg.etcdClusters()).
SetServicesOptions(cfg.SDConfig.NewOptions()).
SetWatchWithRevision(cfg.WatchWithRevision)
SetWatchWithRevision(cfg.WatchWithRevision).
SetNewDirectoryMode(cfg.NewDirectoryMode)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, you might want to only set this if it's been set, maybe make it a pointer type?

Otherwise the option will always be set to os.FileMode(0) which is not desirable, we'd prefer the default of 0755 I believe?

NewDirectoryMode  *os.FileMode` yaml:"newDirectoryMode"`


if v := cfg.NewDirectoryMode; v != nil {
  opts = opts.SetNewDirectoryMode(*v)
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True. I made it a pointer type and included some unit tests as well.

zap.String("path", path),
zap.Error(err),
)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you want to return err here, otherwise it will flow through to the create successful path no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely! Forgot that to put back in after refactoring, fixed now.

- `os.MkDir` -> `os.MkDirAll`
- Test is now checking if created directory is indeed a directory
- Caught error of function in `initCache()`
- Move from random dir name to constant
- Use `c.opts.Prefix()` for file name
- Test cache is working with `Set` and `Get`
In `dbnode/server.go` `envCfg` is holding `kv.Store`. In `dbnode/environment/config.go`
during configuration `etcdclient` gets initiated, so its options had to be extended that
`etcdclient.opts.NewDirMode()` yields the necessary dirMode (which gets passed to `envCfg`).
`NewStore()` in `cluster/kv/etcd/store.go` can then access the dirMode through the client
options.
Copy link
Collaborator

@robskillington robskillington left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@richardartoul richardartoul added ci Triggers CI (useful for external contributors) and removed ci Triggers CI (useful for external contributors) labels Jul 15, 2019
@robskillington robskillington merged commit e21933a into m3db:master Jul 15, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ci Triggers CI (useful for external contributors)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

M3DB should automatically create the folder for KV cache
4 participants