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

Refactor js-repo away from callbacks leaning on async/await #189

Closed
wants to merge 8 commits into from

Conversation

zcstarr
Copy link

@zcstarr zcstarr commented Feb 11, 2019

Initial pull request for the async await refactor. Some notes.

I don't anticipate test to past just yet. There are upstream dependencies on async-multihashing and the various datastore interfaces. I modeled the interface changes based off of the current inflight async api refactor PRs for the dependencies(multiformats/js-multihashing-async#37, ipfs/interface-datastore#25, ipfs/js-datastore-level#12).

Another thing to note is that some of the error handling for opening/deleting a repo, might be up for question. It was a bit unclear what errors should be handled, given the ongoing refactor.

(cb) => this.root.open(ignoringAlreadyOpened(cb)),

Let me know what you think and what needs to be patched up or changed, happy to jam on it. If the interface seems good. I'll try to push testing the interface as far as I can go prior to the upstream package changes landing.

Copy link
Contributor

@jacobheun jacobheun left a comment

Choose a reason for hiding this comment

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

Overall I think the updates look good so far

src/index.js Outdated
return this.root.open()
.then(() => this.config.set(buildConfig(config)))
.then(() => this.spec.set(buildDatastoreSpec(config)))
.then(() => this.version.set(repoVersion))
Copy link
Contributor

Choose a reason for hiding this comment

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

This stack could probably just become await calls to make it cleaner.

Copy link
Member

@alanshaw alanshaw left a comment

Choose a reason for hiding this comment

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

Apologies, I haven't finished reviewing this but there's enough here that I thought I'd submit it and re-review when I have another moment.

store.put(k, block.data, callback)
return store.has(k).then((exists) => {
if (exists) { return }
return store.put(k, block.data)
})
Copy link
Member

Choose a reason for hiding this comment

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

Please await instead:

const exists = await store.has(k)
if (exists) return
return store.put(k, block.data)

})

batch.commit(callback)
const batch = await store.batch()
Copy link
Member

Choose a reason for hiding this comment

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

We don't need to await a batch object - this should be sync.


batch.commit(callback)
const batch = await store.batch()
const newKeys = await Promise.all(keys.filter((k) => { store.has(k.key) }))
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
const newKeys = await Promise.all(keys.filter((k) => { store.has(k.key) }))
const newKeys = (await Promise.all(keys.map(async k => {
const exists = await store.has(k.key)
return exists ? null : k
}))).filter(Boolean)

const otherCid = cidToOtherVersion(cid)
if (!otherCid) return false
return store.has(cidToDsKey(otherCid))
})
Copy link
Member

Choose a reason for hiding this comment

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

Please use await

* @param {Block} block
* @returns {Promise<void>}
*/
put (block) {
Copy link
Member

Choose a reason for hiding this comment

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

Please use async keyword since this is an async API. If the caller is using traditional then/catch and the function throws then it'll cause an uncaught exception. e.g.

const b = 'INVALID BLOCK'
repo.put(b).catch(errHandler)
// uncaught exception, errHandler is not called

*/
has (cid, callback) {
has (cid) {
Copy link
Member

Choose a reason for hiding this comment

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

Please use async keyword since this is an async API. See previous comment for explanation.

*/
delete (cid, callback) {
delete (cid) {
Copy link
Member

Choose a reason for hiding this comment

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

Please use async keyword since this is an async API. See previous comment for explanation.

src/index.js Outdated
*/
_isInitialized (callback) {
async _isInitialized () {
Copy link
Member

Choose a reason for hiding this comment

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

should now return true/false

src/index.js Outdated
await this.root.open()
await this.config.set(buildConfig(config))
await this.spec.set(buildDatastoreSpec(config))
await this.version.set(repoVersion)
}

/**
* Open the repo. If the repo is already open no action will be taken.
* If the repo is not initialized it will return an error.
Copy link
Member

Choose a reason for hiding this comment

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

I believe that "If the repo is already open no action will be taken." is not really correct, because according to the L77, it will throw an error. Maybe lets update it?

Copy link
Author

Choose a reason for hiding this comment

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

Good catch, the comment is definitely off 👍

Copy link
Member

Choose a reason for hiding this comment

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

Maybe the error thrown should be bit more custom (eq. creating a new category in ./errors/index.js) so people could detect if the opening of repo failed because it is locked and handle it? But not sure if it is in the scope of this PR though...

src/version.js Outdated
callback(null, parseInt(buf.toString().trim(), 10))
})
async get () {
const buf = await this.get(versionKey)
Copy link

Choose a reason for hiding this comment

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

This line causes a stack overflow, I think it should be

Suggested change
const buf = await this.get(versionKey)
const buf = await store.get(versionKey)

Copy link
Author

Choose a reason for hiding this comment

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

It does, I have some pending changes stored locally for this, that I haven't pushed back up yet. Do you mind if I drop a PTAL when I push the various fixes I have going locally, this PR was a bit tricker to write and is probably more error prone in places, because I wrote it before the other PRs. Thanks for looking into it!

Copy link

Choose a reason for hiding this comment

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

No worries, I just noticed this when testing some code that has this as a dependency. The rest of it looks great to me.

src/index.js Outdated
cb()
}
], (err) => callback(err))
await this.apiAddr.delete()
Copy link
Member

Choose a reason for hiding this comment

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

This refactored part will fail if the /api does not exists; see original this.apiAddr.delete(ignoringNotFound(cb)).

src/index.js Outdated
(cb) => this.spec.set(buildDatastoreSpec(config), cb),
(cb) => this.version.set(repoVersion, cb)
], callback)
await this.root.open()
Copy link

@dirkmc dirkmc Mar 14, 2019

Choose a reason for hiding this comment

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

Just a reminder to make sure we cover the case where the repo is already open

src/index.js Outdated
this.keys = backends.create('keys', path.join(this.path, 'keys'), this.options)
this.closed = false
log('all opened')
} catch (err) {
if (err && this.lockfile) {
Copy link

Choose a reason for hiding this comment

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

I think you still want to throw here if there is no lock file.

} catch (err2) {
log('error removing lock', err2)
}
this.lockfile = null
Copy link

Choose a reason for hiding this comment

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

I think you only want to set this.lockfile to null if there was not an error thrown from this._closeLock()

src/config.js Outdated
}

if (value === undefined || Buffer.isBuffer(value)) {
return callback(new Error('Invalid value type'))
throw new Error('Invalid value type')
}

setQueue.push({
Copy link

Choose a reason for hiding this comment

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

I'd suggest using a p-queue so that you can return a Promise here

package.json Outdated
@@ -49,15 +49,14 @@
"rimraf": "^2.6.2"
},
"dependencies": {
"async": "^2.6.1",
"base32.js": "~0.1.0",
"bignumber.js": "^8.0.2",
"cids": "~0.5.7",
"datastore-core": "~0.6.0",
"datastore-fs": "~0.7.0",
Copy link
Member

Choose a reason for hiding this comment

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

Could you please add a dependency on ipfs/js-datastore-fs#22 ?

"dlv": "^1.1.2",
"interface-datastore": "~0.6.0",
"err-code": "^1.1.2",
"interface-datastore": "git://github.com/ipfs/interface-datastore.git#refactor/async-iterators",
Copy link
Member

Choose a reason for hiding this comment

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

As the refactor/async-iterators PR was merged and the branch deleted this is outdated and breaks things.

@achingbrain achingbrain mentioned this pull request May 30, 2019
@achingbrain
Copy link
Member

Superseded by #199

@achingbrain achingbrain closed this Jun 4, 2019
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.

6 participants