-
Notifications
You must be signed in to change notification settings - Fork 365
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/kvao flush to deleteAll #1214
Conversation
5e1615d
to
b998dd4
Compare
ad9c691
to
6c3d63b
Compare
@rmg I think something is wrong with CI, PTAL. Could be NPM though because I see |
@slnode test please |
6c3d63b
to
43aa4c8
Compare
if (typeof connector.deleteAll === 'function') { | ||
connector.deleteAll(this.modelName, options, callback); | ||
} else if (typeof connector.delete === 'function') { | ||
console.warn('falling back to unoptimized `deleteAll`'); |
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 will print a ton of warnings in a real app. Please use debug
instead.
connector.deleteAll(this.modelName, options, callback); | ||
} else if (typeof connector.delete === 'function') { | ||
console.warn('falling back to unoptimized `deleteAll`'); | ||
let it = connector.iterateKeys(this.modelName, {}); |
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 think we should pass options
instead of {}
as the second arg.
} else if (typeof connector.delete === 'function') { | ||
console.warn('falling back to unoptimized `deleteAll`'); | ||
let it = connector.iterateKeys(this.modelName, {}); | ||
asyncIterators.toArray(it, function(err, keys) { |
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 you have 100k keys in your store, this will create an array of 100k, which seems like a huge waste of memory to me.
Is there a reason against iterating the async-iterator directly? See how .keys()
is implemented in lib/kvao/keys.js.
This is what I am thinking about:
var iter = connector.iterateKeys(this.modelName, {});
var keys = [];
iter.next(onNextKey);
function onNextKey(err, key) {
if (err) return callback(err);
if (key === undefined) return callback();
connector.delete(self.modelName, key, options, onDeleted);
}
function onDeleted(err) {
if (err) return callback(err);
iter.next(onNextKey);
}
Also since the code is more that a couple of lines, please move it to an new internal method, e.g. iterateAndDelete
.
}, callback); | ||
}); | ||
} else { | ||
console.warn('connector does not support `deleteAll`'); |
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 should return an Error
with statusCode=501
.
assert(typeof key === 'string' && key, 'key must be a non-empty string'); | ||
|
||
callback = callback || utils.createPromiseCallback(); | ||
this.getConnector().delete(this.modelName, key, options, callback); |
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.
Check that the connector provides delete
method and report a descriptive 501 error otherwise.
"eslint": "^3.11.1", | ||
"eslint-config-loopback": "^6.0.0", | ||
"mocha": "^2.1.0", | ||
"should": "^8.0.2" | ||
}, | ||
"dependencies": { | ||
"async": "~1.0.0", | ||
"async-iterators": "^0.2.2", |
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'd rather not depend on pre-1.0 versions in production. We are already implementing our own iteration in keys
, I think we should do the same in the fall-back version of deleteAll
(see my earlier comment for details).
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 simply moved this from a devDep to a dep. It's actually not needed as a full on dep anymore with your suggested changes for deleteAll, so removing this as it's unused now.
|
||
if (excludeFromConnector) { | ||
// `delete connector.deleteAll` doesn't work | ||
dataSource.connector[excludeFromConnector] = undefined; |
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 is not the way how we force a datasource to use fall-back (unoptimized) methods.
See memory
connector tests for the correct solution: https://github.com/strongloop/loopback-datasource-juggler/blob/66c54a96467b39609fec07f41b6d8018c5fc3819/test/memory.test.js#L887-L906.
The idea is to run the shared test suite twice for the built-in kv-memory
connector. Once we run against the "full" connector, the second time we delete optimized functions from the connector instance after the datasource was created.
Th change in this file should be reverted.
const should = require('should'); | ||
|
||
module.exports = function(dataSourceFactory, connectorCapabilities) { | ||
// override injected data source factory |
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.
See my comment above, the decision fallback vs regular deleteAll is made by the connector. There is no point to test how connectors implementing optimized deleteAll are handling the fall-back variant, because the fall-back variant will be never invoked.
This whole file should be deleted and test/kv-memory.test.js should be modified to run the shared test suite twice.
return helpers.givenKeys(CacheItem, ['key1', 'key2']) | ||
.then(() => helpers.givenKeys(AnotherModel, ['key3', 'key4'])) | ||
.then(() => CacheItem.deleteAll()) | ||
.then(() => CacheItem.keys()) |
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.
As I commented in your earlier pull request, there is no need to verify that CacheItem.deleteAll
deletes CacaheItem keys - that has been already verified by the previous test.
@slnode test please |
0faeed3
to
52877bf
Compare
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.
Looks mostly good, see few minor comments below. No need for further review, unless you feel so.
debug('falling back to unoptimized `deleteAll`'); | ||
iterateAndDelete(this.modelName, connector, options, callback); | ||
} else { | ||
debug('connector does not support `deleteAll`'); |
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 think this is not entirely correct. How about Connector does not support deleting key/values.
(Also notice the error message is a proper sentence, starting with a capital letter and ending with a dot. I don't mind the missing dot, but I believe we are starting error messages with a capital letter in other places.)
} else { | ||
debug('connector does not support `deleteAll`'); | ||
process.nextTick(function() { | ||
var err = new Error('connector does not support `deleteAll`'); |
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.
Ditto, new Error('Connector does not support deleting key/values')
.
(Feel free to use a different wording for "key/values", just stay consistent in the error message and the debug log.)
return callback.promise; | ||
}; | ||
|
||
function iterateAndDelete(modelName, connector, options, callback) { |
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.
Nitpick: I would personally change the order of arguments from (modelName, connector,...)
to (connector, modelName, ...)
, Start with the thing that's changing the least (connector remains the same for the lifetime of a datasource), follow with things that change more often.
if (typeof connector.delete === 'function') { | ||
connector.delete(this.modelName, key, options, callback); | ||
} else { | ||
debug('connector does not support `delete`'); |
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 think it may be better to use the same message I proposed for "deleteAll" above, i.e. Connector does not support deleting key/values
.
} else { | ||
debug('connector does not support `delete`'); | ||
process.nextTick(function() { | ||
var err = new Error('connector does not support `delete`'); |
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.
ditto
52877bf
to
c387580
Compare
- Rename `flush` to `deleteAll` - Add `delete` - Detect `delete/deleteAll` before running downstream test suites - Fall back to unoptimized `deleteAll` when connector does not support `deleteAll` but supports `delete` - Return 501 for connectors not supporting `delete` or `deleteAll`
c387580
to
2320df1
Compare
@slnode test please |
@slnode test please |
Created follow up issue for long term Oracle fail at loopbackio/loopback-connector-oracle#99. Merging as everything else is green. |
Aggregate pull request for refactoring
kvao.flush
tokvao.deleteAll
. Implementation consists of 3 parts:delete
deleteAll
deleteAll