-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Multiple queries hang silently #395
Comments
@CorvusCorrax You need to release connections when you're done with them: Also, keep in mind that you only have 4 background threads by default in Node.js. If you need more you'll need to set UV_THREADPOOL_SIZE higher. Look into using async to better control execution flow in Node.js. |
Thanks for your help, UV_THREADPOOL_SIZE solved the problem. |
I don't think it's failing, I think it's waiting for a thread to become available to handle your request for another connection. Again, you need to release connections when you're done with them. Upping the UV_THREADPOOL_SIZE for your example has only masked a much larger problem. |
This code fails too...
Connections are never released, it hangs at commit.
|
I believe it hangs for the same reason @dmcghan mentioned https://github.com/oracle/node-oracledb/blob/master/doc/api.md#number-of-threads |
Yeah, that makes sense. This is a unique case since the update statements are updating the same rows. The first update goes to the table and updates the rows but doesn't commit (that means it's placed row level locks on them). Then 3 more updates are executed on the same set of rows but they are blocked waiting on the first transaction to commit. But the first transaction can't commit because all of the threads to do so are used up by execute statements. It's the Node.js version of a deadlock. :) The code should be updated to serialize the statements or at least provide and utilize threads sufficiently to handle this "unique" workload. |
Another option, now that I think about it, would be to have the update statements first attempt to lock the rows with select for update. Then you could use the |
The issue was indeed quite specific. A more careful thread handling solved the problem. |
Hi all, we have just had experience the same problem. So for this straightforward task you simply do // ... user is found and authorized already
db.execute(
'UPDATE users SET last_logon = :0 WHERE id = :1',
{ '0': new Date(), '1': user.id },
commitAndPassTheUserBack
);` Then whole application stops responding, because someone have just opened Chrome with your app loaded in multiple tabs. This is a potential way for causing denial of service. |
@timon What is your pool config? What is your UV_THREADPOOL_SIZE? This is not an Oracle Database problem, but rather a problem of resource utilization (threads) with Node.js. Here are some thoughts:
|
There are a few notes on how you can end up locking all the threads and on setting You should commit when appropriate for your data integrity but use the |
@dmcghan you are right :
so this is a node-oracledb problem ... |
If you know how all the moving pieces work (node.js, node-oracledb, and Oracle Database) and you design an application in a way that it requires additional resources to finish work (by not using autoCommit, PL/SQL, etc.) AND you don't ensure those additional resources are available to finish the work, then I would see it as an application design problem. Are you aware of other RDBMS drivers that offer solutions for this? The only solution I could imagine would be to ensure that the driver never uses all the background threads for async operations. In theory, that would mean we could always have some threads to process commit or rollback operations (which unlock rows). Perhaps it would be possible to accomplish this in the JS layer. We'd have to route all async requests through a queue with a concurrency set no higher than (UV_THREADPOOL_SIZE - 1). Then we could let commit and rollback operations to bypass the queue. One problem I see is that you'd be limiting overall concurrency on systems where people leave the default UV_THREADPOOL_SIZE of 4. It's worth thinking about some more... |
@dmcghan never seen this problem with other RDBMS drivers... |
@atiertant Just because you haven't seen it doesn't mean it doesn't exist. ;)
By "acquire a ressource", I assume you mean a lock on the rows that someone else has already locked, right? In that case we don't need to make any changes to the driver. One could change the transaction isolation level from READ COMMITTED to SERIALIZABLE or use a little PL/SQL with the NOWAIT option. But then you just get an error that you have to handle - what would you do with that? Retry??? Why would we do any of that when the driver already provides this super simple autoCommit feature which solves this problem beautifully? This is a single statement transaction, so autoCommit is perfect. If this were a multiple statement transaction then a little PL/SQL could solve the problem an only use 1 thread/connection/round trip for the transaction. |
only known bug can be fixed ! if this bug whould produce only once a century noone talk you about... By "acquire a ressource", i mean check that we can use a thead.(thread count is lower than UV_THREADPOOL_SIZE)
say user the action he does is cancelled and free memory. |
@atiertant thanks for pushing us on this. We will, of course, improve where we can. I don't know whether this particular scenario can be resolved: how will the n-1 thread be known to be blocking or not until it is actually used? |
@cjbj where does this threads are created in js code? |
@atiertant threading is handled by the C++ layer |
hummm there is one big C++ exec who do all job and interact with nodejs... maybe not the best way. |
As I understand it, threading it handled by libuv. libuv creates a pool of threads then C applications (such as the driver) interface with it using uv_queue_work. In that sense, you're just asking libuv to add your "work" to the queue and it will run it as soon as a thread is available. The only way to prevent the driver from using more than n-1 threads, is to not add more than n-1 requests to the queue at any time. I think it would be possible to do this in the JS layer by routing all async calls through a new queue that doesn't go beyond n-1 at any point in time. The exception to the rule would be for commit and rollback operations, which would skip the new queue. In theory, unless some some other background job was using the 1 open thread (say writing to a large file), the commit/rollback operations should be able to use the open thread to release locks, thus allowing the blocked connections to finish their work and ensure that deadlock doesn't occur. The biggest downside to this approach is that you limit the driver to n-1 threads for most work. With the default of 4, this means you're reducing the number by 25%, which could have an adverse affect on overall performance. |
doesn't commit and rolback done before operations on which it should operate ?
in fact with the actual code without changing UV_THREADPOOL_SIZE, perf are already low... |
I'm not sure what you're asking here. Could you please clarify?
Yeah, but defaults are defaults, and many people aren't aware of them and how to adjust them. I'd be in favor of some kind of "warning messages" feature that tries to help users with these kinds of things. |
well, if i use more than n-1 threads for insert,delete,update and i commit after.
every other products i use work well with defaults...
install oracledb is complex, ps: sorry to be ironic ;) |
You wouldn't be able to as the queue would prevent it. You'd just call normal methods like getConnection and execute - the queue would make sure that no more than n-1 operations are running at any given point in time.
That wouldn't happen as your code wouldn't invoke commit unless the other work had already been completed.
I don't see where you're going with this. The default UV_THREADPOOL_SIZE is not under our control - it's set by Node.js. At any rate, a colleague recently explained to me why the n-1 concept wouldn't work as the complexity of the work scales up - so that's not a solution that would work anyway. |
https://github.com/Atlantis-Software/knex/blob/2a1c33b69eb3446b045b33a2ae7be15f966d4d51/src/dialects/oracledb/index.js#L22 |
UV_THREADPOOL_SIZE must be set very early on, before async calls are made. As such, there's no guarantee your code will work. Also, you shouldn't be doing this. What if some other module did this too, only they set it lower than you did? Finally, this doesn't truly solve the problem. What if I have multiple pools?
What do you mean by this?
This isn't not the problem. The problem is hitting the limit of background threads available in libuv. |
you are right ! i just made this to make my code work before you fix.
when you know you don't have enougth available thread, you got a choice:
i don't think writting threading app with nodejs is good idea.
exacly the same ! if node-oacledb need more thread then add one...
there is one thing worst than a app crashing: a system crash !
if you use multiple synchronous process instead of multiple theads in a single process. |
There's a difference between writing code as an end user and for an end user. Knex meant for an end user. That's why you can't guarantee it will work - you don't know if the end user will have done any async calls prior to using your module. The fact that it works for you because you know how it needs to be used doesn't help end users. We don't yet know if there is a reasonable fix that can be done on our side save the tools everyone already has at their disposal, such as:
We could kick around the idea of an execute method that works for an entire transaction based on a queue. But even that would require proper understanding and use.
This issue is not about a "process crash", it's about hanging/waiting due to a client induced deadlock. The crashing reported earlier was a separate issue.
I suggested a "warning messages" feature earlier but you didn't seem to like the idea. |
@atiertant, If I understand correctly, you are trying to work out the best way to stop a deadlock when Node runs out of threads. The basic premise is that a node-oracledb worker thread will get blocked when it is doing DB work; this isn't going to change in the foreseeable future. And since I can't solve the Halting Problem, I don't know how node-oracledb can be expected to predict what DB operations the app is going to do, and when not to allow the app to block itself. |
@cjbj why do you need to lock thread? |
Hi,
This code crash before commits
Here is the only result :
This is working when nbCnx is lower than 8, but higher than that it just silently hangs.
Where am i wrong ? Could you help me ?
The text was updated successfully, but these errors were encountered: