ignite-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Taras Ledkov <tled...@gridgain.com>
Subject Improve lazy mode SQL query execution (IGNITE-9171)
Date Wed, 21 Nov 2018 11:23:52 GMT
Hi community,

We will enhance lazy mode for SQL query execution.

Lazy mode overview:
Lazy mode is related to H2 lazy mode when the all query results are not 
copied to the RAM in some cases.
The mode it is applicable for SELECTs that doesn't not require 
materialize all results in memory, e.g.  simple scan plans, IDX lookup, 
merge join etc.
And not applicable for SORT by not indexed fields, aggregates, nested 
loops joins etc.

When mode is applicable it produces result with iterator-like behavior 
on server side and not consume RAM.
So the huge result set may be selected without OOME.

The current implementation.
The current implementation is start separate thread for each query with 
'lazy=true'.
This is caused by the our implementation of 'GridH2Table'. In details: 
the table's locks.
The table must be locked while result set  is completed.

When lazy is disabled a complete result is generated on the first step 
of a query execution (then tables unlock)
and result is stored on the node and sent to other node (or client) page 
by page.

When lazy is enabled tables are locked until result set delivery to client.

The start new thread causes overhead for requests that returns small 
result set.
But current table lock is used `ReentrantReadWriteLock` and we cannot 
lock tables from one thread
of QUERY thread pool and unlock in the other thread (when query is 
complete or cancel).

The trivial solve is using the 'StampedLock' it solve the lock behavior, 
but not solve the table DDL starvation / deadlock.
Example:
Lets the QUERY thread pool contains only one thread. The case is scaled 
for any thread pool size.
Write operation that require to exclusive table lock is DDL operation.

1. The query Q0 acquires the shared lock for the table T, send first 
page result and leave thread 'threadQP0' control.
2. DDL0 blocks on write lock the table T at the 'threadWP0 '
3. The query Q1 blocks on read lock  the 'threadQP0' (because the writer 
in the queue).
The deadlock happens. Q0 never can finish and unlock because query pool 
hasn't free thread.

The possible solution:

1. Don't use readlock at all. The lock is used only for write / 
exclusive (DDL) operations.
2. The DDL (exclusive) operation change the table version.
3. Each read operation (query execution, result page fetch) store the 
table version before start and compare with the table version on the 
end. If the version is changed the special retry exception is thrown.

CONS:
- The retry logic is less user-friendly. But the distributed SQL cannot 
protect the user from implement retry logic totally: e.g. cluster 
topology change must handled on user side by retry query implemented by 
user, because some data have been delivered to user and we don't track 
which data is delivered.

PROS:
- no deadlocks;
- no contention on table lock for SQL query.

What do you think?

-- 

Taras Ledkov
Mail-To: tledkov@gridgain.com


Mime
View raw message