-
Notifications
You must be signed in to change notification settings - Fork 13
Conversation
Codecov Report
@@ Coverage Diff @@
## master #7 +/- ##
===========================================
- Coverage 100.00% 90.75% -9.25%
===========================================
Files 1 2 +1
Lines 33 119 +86
===========================================
+ Hits 33 108 +75
- Misses 0 6 +6
- Partials 0 5 +5
Continue to review full report at Codecov.
|
bf47165
to
7bda9e0
Compare
4efa717
to
b4b2524
Compare
pkg/storage/store.go
Outdated
OpenStore(name string) (Store, error) | ||
|
||
// CloseStore closes the store with the given name. | ||
CloseStore(name string) error | ||
|
||
// Close closes all stores created under this store provider. | ||
Close() error | ||
|
||
// StoreExists checks whether a store exists under the given name already. | ||
StoreExists(name string) (bool, error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@fqutishat If you recall, I had an issue in the EDV repo where I needed the ability to know if a store is open without actually opening it. The solution we had before was to track the open stores in a map in the operations.go file. However, I realized while working on this story that that strategy wouldn't work for a persistent store, since now there can be stores already created from running this executable previously. Thus, I need to ask the provider if a store is open first. So I added this "StoreExists" method to the Provider interface. Let me know if there's another way to do this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
where you use Provider.StoreExists
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Per our discussion, I added the CreateStore method and made the OpenStore method only open existing stores.
pkg/storage/couchdb/couchdbstore.go
Outdated
} | ||
|
||
// CloseStore closes a previously opened store. | ||
// CouchDB and Kivik don't really have "close" mechanisms for individual databases, so there's nothing to do here. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems strange... but I couldn't find a way to close a CouchDB database. Is there a way?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this what you're looking for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@llorllale Yep, that's it. The older version of Kivik I used initially didn't have that call, so that's why I thought it didn't exist.
I updated the method.
|
||
"github.com/sirupsen/logrus" | ||
|
||
"github.com/trustbloc/edge-core/pkg/storage" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wrong location
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
couchDBURL = "localhost:5984" | ||
testStoreName = "teststore" | ||
testKey = "sampleDBKey" | ||
testValue = `{"JSONKey":"JSONValue"}` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you assume the value always JSON
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Per our discussion on Friday with @bstasyszyn , I updated the CouchDB store Put method so that a non-JSON payload will be saved as an attachment. I verified that JSON payloads do get merged with the CouchDB document (this should answer @bstasyszyn's question from Friday)
require ( | ||
github.com/flimzy/diff v0.1.6 // indirect | ||
github.com/flimzy/testy v0.1.16 // indirect | ||
github.com/go-kivik/couchdb v2.0.0+incompatible |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why it's incompatible
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we have a choice. The go-kivik module has the same and (i believe) go mod tidy will put it back if you try to take it out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@fqutishat The "incompatible" thing has something to do with the way the go modules are set up in the Go Kivik library. I don't think I can do anything about it until they update it. According to what I've read it shouldn't have any impact on us though. See https://stackoverflow.com/questions/57355929/what-incompatible-in-go-mod-mean-will-it-make-harm for more info
The author of the Go Kivik repo has an open issue for go.mod support, so I think when that's resolved then this "incompatible" thing will go away (after we update the dependency of course): go-kivik/kivik#417
pkg/storage/couchdb/couchdbstore.go
Outdated
|
||
// Put stores the given key-value pair in the store. | ||
func (c *CouchDBStore) Put(k string, v []byte) error { | ||
_, err := c.db.Put(context.Background(), k, v) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you need to create doc and create key inside doc called data
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doc := map[string]interface{}{
"_id": k,
"data":v
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we ever want to save the value as an attachment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Per our discussion on Friday with @bstasyszyn , I updated the CouchDB store Put method so that a non-JSON payload will be saved as an attachment. I verified that JSON payloads do get merged with the CouchDB document (this should answer @bstasyszyn's question from Friday)
pkg/storage/couchdb/couchdbstore.go
Outdated
|
||
// Put stores the given key-value pair in the store. | ||
func (c *CouchDBStore) Put(k string, v []byte) error { | ||
_, err := c.db.Put(context.Background(), k, v) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
} | ||
|
||
// Get retrieves the value in the store associated with the given key. | ||
func (c *CouchDBStore) Get(k string) ([]byte, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when you retrieve value from couchdb you don't need to return the whole doc just return doc["data"]
pkg/storage/couchdb/couchdbstore.go
Outdated
|
||
// OpenStore opens and returns a store for the given name. | ||
func (p *Provider) OpenStore(name string) (storage.Store, error) { | ||
exists, err := p.couchDBClient.DBExists(context.Background(), name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why are we still doing this? This stuff gave us lots of headaches in the past. The solution back then was to have a "leader" create the DB if it doesn't exist.
Why isn't this a devops issue?
pkg/storage/couchdb/couchdbstore.go
Outdated
|
||
// Close closes the provider. | ||
func (p *Provider) Close() error { | ||
p.couchDBClient = nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@llorllale The older version of Kivik I used initially didn't have that call, so that's why I didn't use it before. I see the newer one does - I've updated the method.
require ( | ||
github.com/flimzy/diff v0.1.6 // indirect | ||
github.com/flimzy/testy v0.1.16 // indirect | ||
github.com/go-kivik/couchdb v2.0.0+incompatible |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we have a choice. The go-kivik module has the same and (i believe) go mod tidy will put it back if you try to take it out.
|
||
err = p.couchDBClient.CreateDB(context.Background(), name) | ||
if err != nil { | ||
return nil, err |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There could be a race condition in which two clients are attempting to create the same DB. At the time you check if the DB exists it may not be there but then when you try to create it you'll get an error because it exists. You'll need to check again if the error means that the DB is there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code has been changed so now there are separate create store and open store methods (per our discussion with @fqutishat on Friday), so the DB exists check is no longer there. Let me know if you still think there's a race condition.
pkg/storage/couchdb/couchdbstore.go
Outdated
|
||
// Put stores the given key-value pair in the store. | ||
func (c *CouchDBStore) Put(k string, v []byte) error { | ||
_, err := c.db.Put(context.Background(), k, v) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if it's a good idea to always use context.Background(). Usually it's a good idea to expose context in the API to allow the client to pass whatever type of context they want (i.e. with timeout, cancel, etc.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bstasyszyn @fqutishat So would this mean adding a third argument to the Put() function for context? Or should I perhaps add it in to the Store/Provider struct and then grab the context whenever I need it? Just wondering what's the best way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The context is for one time use, i.e. per request. So it would need to be in Put. The convention is to add it as the first arg.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Per our discussion, I created a follow-up issue for this: #8
pkg/storage/couchdb/couchdbstore.go
Outdated
|
||
// Put stores the given key-value pair in the store. | ||
func (c *CouchDBStore) Put(k string, v []byte) error { | ||
_, err := c.db.Put(context.Background(), k, v) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we ever want to save the value as an attachment?
1cbd5e6
to
0cd563e
Compare
3be481a
to
ac557e0
Compare
scripts/check_unit.sh
Outdated
exit 1 | ||
fi | ||
|
||
DOCKER_RM_EXIT_CODE=0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
duplicate code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I refactored the error code check into a separate function. Take a look and let me know what you think.
// We want to do it all in one step, hence this manual stuff below. | ||
func wrapTextAsCouchDBAttachment(textToWrap []byte) []byte { | ||
encodedTextToWrap := base64.StdEncoding.EncodeToString(textToWrap) | ||
return []byte(`{"_attachments": {"data": {"data": "` + encodedTextToWrap + `", "content_type": "text/plain"}}}`) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will these need indexing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, they shouldn't. This whole attachment thing is just to allow the CouchDB store to work with non-JSON payloads. The primary use case for the CouchDB store is to store EDV EncryptedDocuments, which are JSON. Those EncryptedDocuments will need indexing, but they won't be stored as attachments, so we should be okay.
|
||
store := &CouchDBStore{db: db} | ||
|
||
p.dbs[name] = store |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need a write lock here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After looking through the OpenStore method, I think that I actually need a write lock for the whole method since it first checks to see if it exists, and then opens the store in the map. Let me know if you think I'm locking more than I need to here.
pkg/storage/couchdb/couchdbstore.go
Outdated
} | ||
|
||
func (p *Provider) storeExists(name string) (bool, error) { | ||
_, existsInCache := p.dbs[name] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs a read lock. (Also in other places below.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since I added a write lock to the OpenStore method (which is the only place that calls the private method storeExists), I don't need to (and can't) add a read lock here. Let me know if this seems ok.
return nil, dbErr | ||
} | ||
|
||
store := &CouchDBStore{db: db} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the store exists in cache then why not return it instead of creating a new one every time?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
a60f216
to
599441b
Compare
pkg/storage/couchdb/couchdbstore.go
Outdated
|
||
// Put stores the given key-value pair in the store. | ||
func (c *CouchDBStore) Put(k string, v []byte) error { | ||
c.mux.Lock() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need a lock here since you're accessing an immutable field.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. (I also got rid of that line in the close method that clears the cache by remaking the map, as discussed)
pkg/storage/couchdb/couchdbstore.go
Outdated
func (c *CouchDBStore) Get(k string) ([]byte, error) { | ||
destinationData := make(map[string]interface{}) | ||
|
||
c.mux.RLock() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't need a lock.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
pkg/storage/couchdb/couchdbstore.go
Outdated
} | ||
|
||
// Check cache first | ||
store, exists := p.dbs[name] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems a bit awkward that you check if the store exists using storeExists and then you check again if it exists here. Maybe it would be better to get rid of storeExists, unless it's being used elsewhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
Signed-off-by: Derek Trider <[email protected]>
closes #4
Signed-off-by: Derek Trider [email protected]