-
-
Notifications
You must be signed in to change notification settings - Fork 818
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
Supporting for user functions, fixes #140 #448
Conversation
So the last thing that I wanted to figure out in this is how to know when a function call has produced an exception. |
src/database.cc
Outdated
uv_async_init(uv_default_loop(), &baton->async, (uv_async_cb)Database::AsyncFunctionExecute); | ||
uv_async_send(&baton->async); | ||
uv_mutex_lock(&mutex); | ||
uv_cond_wait(&baton->condition, &mutex); |
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.
uv_cond_wait
may produce spurious wakeups. We need to check whether the function call is actually complete.
28de83f
to
78fd0bb
Compare
@kkaefer I had trouble enabling C++11 features to get Each function call is now being addd to a queue & the queue is being processed on the main thread. I'm not sure whether this is necessary or not. The SQLite3 docs don't really specify how callbacks are invoked with regards to threading. I'm not sure whether SQLite3 will make multiple simultaneous calls to a callback registered with The failures with iojs on Linux seem to be addressed, so I assume that was related to the improper use of I'd still be interested in any advice on how to know when a function call has produced an exception. |
@springmeyer thanks for the heads up. It's hard to test all those configurations, but I probably shouldn't have pushed each of those commits either. I didn't really realize until later that each build was taking about an hour to complete/hanging. |
@wbyoung nothing to worry about on your end - it was just tough timing with travis being slow. |
@springmeyer let me know if you're able to re-enable at any point or any other ideas to test all configurations. I can't enable Travis on my own fork because of the use of |
I believe this is the most appropriate way to handle exceptions. Using v8::Function::Call rather than node::MakeCallback (via NanMakeCallback) means that these functions will not work with domains, but that seems appropriate. I don't know if nodejs/node-v0.x-archive#9245 or nodejs/nan#284 should be a concern here or not.
A faster version of this with some issues can be found at wbyoung/node-sqlite3 user-functions-isolate that creates a new v8 isolate. |
@wbyoung interesting; what implications does the use of isolates have? Can the user provided functions still access other parts of the script? |
@kkaefer as of right now, that branch won't even let you Anyway, even if it it were loading the node environment, v8 stipulates that you can't access objects from a different isolate. The function has been stringified and copied to the new isolate, so the following would produce a var outofscope = 0;
db.registerFunction('MY_FUNC', function(value) {
return value + outofscope;
}); I'm torn between which approach seems better. dispatch to default loop ✓ Function executes in expected environment. Closures & scoping work as expected. v8 isolate ✓ Faster. |
This has many problems which are discussed in TryGhost#448.
@kkaefer an update on isolates… wbyoung/node-sqlite3@f8cd018 creates an isolate for a user defined function. It does not load the node environment. wbyoung/node-sqlite3@79756a4 actually loads the node environment (and loads of functions from a separate file). At this point, I'm pretty convinced that a separate v8 isolate is a bad idea.
The point to drive home is that if what you want is performance, you should create a user defined function via a native extension. Most who are looking for performance are probably going to look to another database anyway. If you're not looking for performance, then it's okay to take the performance hit that comes with dispatching to the default loop & executing the function in the original node isolate. Plus, there'd be no weird scoping issues. At this point, I'm hoping that this pull request may be sufficient to be incorporated into the project. Thanks for the tips so far! |
Thanks for all the exploratory work; very insightful and informative. As for using isolates, what we could do is the following: Instead of using the libuv work pool, all SQlite functions could be run in a custom thread that we spawn. This thread could also run a |
var db = new sqlite3.Database(':memory:', done);
db.loadEnvironment('./functions.js');
db.all('SELECT FOO() AS txt', function(err, rows) {
console.warn(rows);
}); functions.js: exports.FOO = function() {
return 'bar';
}; |
This has many problems which are discussed in TryGhost#448.
wbyoung/node-sqlite3@c24fc5e loads all functions from a single file. |
@kkaefer by running all SQLite functions, you mean that all queries would be run on a single thread rather than being run as they are right now (in a threadpool via Wouldn't that approach mean that all queries would potentially block each other? Currently, as I'm understanding the code, all Ramblings/Thoughts The way that I'm reading the SQLite3 docs, it seems that it has a few different thread modes. If my understanding above is correct, then I'm also led to believe that this library simply assumes that the SQLite library it's linking to has at least multi-thread if not serialized (the default). What I'm getting at is that adding the ability to switch modes into one operating only in a single thread could allow So the following would be how the library worked:
I'm not convinced that the tradeoffs are worth it. With the pull request as it is (dispatching user functions to the default loop), only queries that are using user defined functions are affected. Running all queries on a single thread would affect all queries even if you want to add a user function that only runs occasionally. Also, the SQLite3 docs doesn't actually specify on which thread user defined function callbacks are made. I think it's safe to assume that it's the same thread as the one on which the statement was executed, but I just wanted to throw that out there. Please correct me if I've overlooked something in the docs. I actually hope that I'm wrong on all of this and that there's a way to make it work in its own isolate. Regardless, the back and forth with you, @kkaefer has given me a far better understanding of Node & v8, so thanks! |
A single SQLite database can never be used from more than one thread at a time (unless in serialized mode, but that one is, well, serialized), so giving each Database handle its own thread wouldn't slow down performance. In fact, the current code may produce |
@kkaefer ok, I was somehow confused into thinking that multiple queries could run at once for any given connection, but the SQLite3 docs are clear that's not possible. Just to be sure we're on the same page (since this could be a lot of work to get working), the process of opening a connection ( I'll give it a shot if I have some time. |
Hi Guys, When can we expect this to be merged in master ? @wbyoung i tried your branch user-functions, but it kills the application as soon as the registerFunction is called. |
@suchitpuri try using revision 3db73b9. I haven't had time to work on this and am not sure if/when I will again. |
Hi, any update on this? Regards |
@SamuelBrucksch I re-did most of the work in this branch in #1200. Still waiting for a review though. |
This allows the registration of functions via JavaScript that can be called in queries.
I have a little left to do to handle errors, but I wanted to check that this is on the right track before continuing.I've not worked with C++ in a long time and this is the first that I've worked with v8, so I did my best.It takes the approach of calling on the Node.js thread, so this would certainly have a huge impact on performance (though in some cases that may not matter).
The suggestion in #140 to create a new v8 isolate and transfer the function there seems like a far better way to make this work, but is way beyond what I'd be able to do.This seems like a start at least.