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

feat(interface) ThreadSafeDatastoreCloser #9

Closed
wants to merge 1 commit into from
Closed

feat(interface) ThreadSafeDatastoreCloser #9

wants to merge 1 commit into from

Conversation

btc
Copy link

@btc btc commented Nov 16, 2014

Expose Close() to make it possible to clean up LevelDB instances.

License: MIT
Signed-off-by: Brian Tiger Chow brian@perfmode.com

License: MIT
Signed-off-by: Brian Tiger Chow <brian@perfmode.com>
btc pushed a commit to ipfs/kubo that referenced this pull request Nov 16, 2014
@jbenet need to merge ipfs/go-datastore#9 and
re-vendor

License: MIT
Signed-off-by: Brian Tiger Chow <brian@perfmode.com>
// interface
type ThreadSafeDatastoreCloser interface {
ThreadSafeDatastore
io.Closer
Copy link
Member

Choose a reason for hiding this comment

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

I want to keep the Closer interface outside of the Datastore* interfaces. I think the leveldb instance (or whatever you want the Close func in) can just conform to io.Closer.

Copy link
Author

Choose a reason for hiding this comment

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

LevelDB returns a ThreadSafeDatastore. Prefer to expose the concrete struct?

@jbenet
Copy link
Member

jbenet commented Nov 17, 2014

@maybebtc i think the Closer interface doesnt belong in the datastore interfaces themselves. I think the concrete types (e.g. leveldb.Datastore) can just implement Closer where needed.

@btc
Copy link
Author

btc commented Nov 17, 2014

I'll have the LevelDB constructor return a concrete instance then.

@jbenet
Copy link
Member

jbenet commented Nov 17, 2014

@maybebtc yeah that makes sense. feel free to break up leveldb.Datastore into two parts: an interface, and an unexported concrete type

@jbenet
Copy link
Member

jbenet commented Nov 17, 2014

// Datastore wraps a levelDB instance with a Datastore interface
type Datastore interface {
    ds.ThreadSafeDatastore
    io.Closer

    DB() *leveldb.DB
}

// datastore uses a standard Go map for internal storage.
type datastore struct {
    db *leveldb.DB
}

@btc
Copy link
Author

btc commented Nov 17, 2014

closed in favor of #10

@btc btc closed this Nov 17, 2014
@btc
Copy link
Author

btc commented Nov 17, 2014

@jbenet

One minor note...

Since the datastore interfaces to not expose Close(), the client is left to implement her own wrapper. This is perfectly fine, but it might be nice for the library to anticipate this need and provide this behavior just as it does with the mutex wrapper.

Example:

// ClientAppStore returns a Datastore that may need to be closed in Production
func ClientAppStore(in_test_env bool) MyThreadSafeDatastoreCloser {
  if in_test_env {
    return ds.NewMapDatastore() // compiler error
  }
  return leveldb.NewDatastore()
}

To fix this, each go-datastore client must implement a trivial Wrapper around datastores.

type ThreadSafeDatastoreCloserWrapper struct {
  ds.ThreadSafeDatastore
}

func (w *ThreadSafeDatastoreCloserWrapper) Close() error {
return nil // no-op
}

For now, I'll implement a wrapper for my specific use case.

@jbenet
Copy link
Member

jbenet commented Nov 17, 2014

@maybebtc i hear you. It's a tough call. A few things to bear in mind:

  1. I'm worried by interface composition explosion. Every datastore sub-interface composing with all others would quickly yield rote work in writing out permutations, and awful ThreadSafeCloserNonBlockingSymlinkDatastore javarrhea. I dont see this as a viable route.

  2. Close is a common enough thing that it may be ok to expand the datastore interface with. So if we reaaaally want it, i'd rather implement explicit nil Close funcs than permuations.

  3. the ThreadSafeDatastore is a different case, in that there is a wrapper that implements more functionality around non-ThreadSafeDatastores, and there is an interface datastores can signal when they implement it internally. (perhaps i shouldn't have put it inside the main Datastore lib, but inside the datastore/sync pkg...)

  4. This is not as nice in languages with inheritance, but in Go, the user maybe should be specifying the interface of her own datastores explicitly. I think it's easier to pick and choose what you want all your datastores to conform to:

    // in go-ipfs/core/datastore.go
    
    // Datastore is the common interface all our datastore backends must conform to
    type Datastore interface {
      ds.Datastore
      syncds.ThreadSafeDatastore
      ktds.KeyTransformDatastore
      symds.SymlinkDatastore
      io.Closer
      sync.Locker
      log.Loggable
    }

So i think for now let's keep as is? We can revisit this if a better strategy is found.

@btc
Copy link
Author

btc commented Nov 17, 2014

I agree with all points. There's no harm in being careful about over-extending the library.

as an aside, here's a link to the wrapper: https://github.com/jbenet/go-ipfs/blob/feat/daemon-init/util/datastore_closer.go

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants