Add a limit on total number of bytes read from storage in table scan#14739
Add a limit on total number of bytes read from storage in table scan#14739tdcmeehan merged 1 commit intoprestodb:masterfrom
Conversation
mbasmanova
left a comment
There was a problem hiding this comment.
@fgwang7w Some initial comments. Would it make sense to have both soft and hard limit? E.g. sort limit would generate a warning, while hard limit will fail the query. See query_max_scan_physical_bytes for an example.
There was a problem hiding this comment.
getQueryMaxScanPhysicalBytes(session) returns a value that can be used as is without further checking.
This code ensures that this property defaults to what's specified in config:
dataSizeProperty(
QUERY_MAX_SCAN_PHYSICAL_BYTES,
"Maximum scan physical bytes of a query",
queryManagerConfig.getQueryMaxScanPhysicalBytes(),
false),
and this code defines the default value that is used if nothing is specified in config file:
private DataSize queryMaxScanPhysicalBytes = DataSize.succinctDataSize(1, PETABYTE);
Hence, the code can be simplified like this:
for (QueryExecution query : queryTracker.getAllQueries()) {
DataSize limit = getQueryMaxScanPhysicalBytes(query.getSession());
DataSize scan = query.getQueryInfo().getQueryStats().getRawInputDataSize();
if (scan.compareTo(limit) >= 0) {
query.fail(new ExceededScanLimitException(limit));
}
}
There was a problem hiding this comment.
Many thanks for the proposal. I have made another version of how to implement this limit based on your suggestion. The new version is now includes a method defines in QueryExecution to collect scanned data size which is implemented in SqlQueryExecution to collect finalQueryInfo's rawInputDataSize. Please help review again.
There was a problem hiding this comment.
Let's clarify what does the limit applies to? E.g. whether it applies to amount of data read from storage before or after compression?
There was a problem hiding this comment.
This is the amount of consumed input bytes read from storage via QueryInfo's getQueryStats().getRawInputDataSize()
There was a problem hiding this comment.
Concurring @mbasmanova's comment - we should be clear about what scan limit has been exceeded. I will recommend EXCEEDED_SCAN_RAW_BYTES_READ_LIMIT. The message can also be made more explicit
There was a problem hiding this comment.
the message has been fixed to indicate the scan bytes limit has exceeded for this exception
presto-main/src/main/java/com/facebook/presto/SystemSessionProperties.java
Outdated
Show resolved
Hide resolved
viczhang861
left a comment
There was a problem hiding this comment.
Release note can be improved, see instruction here https://github.com/prestodb/presto/wiki/Release-Notes-Guidelines
There was a problem hiding this comment.
Is QueryStats::rawInputDataSize equivalent to scanPhysicalBytes? does reading from materialized intermediate table included? cc @arhimondr
There was a problem hiding this comment.
Same comment as in SystemSessionProperties - lets use the same name as the actual metric - queryMaxRawInputBytes. In accordance with that lets change the config name as well - query.max-raw-input-bytes.
presto-main/src/main/java/com/facebook/presto/execution/SqlQueryManager.java
Outdated
Show resolved
Hide resolved
|
@fgwang7w Would you squash all 5 commits into one? |
mayankgarg1990
left a comment
There was a problem hiding this comment.
Overall, lets ensure that the logic works and in addition to that, lets use the same term rawInputBytes instead of scannedBytes so that it is easy for the users to understand which metric did they exceed.
presto-main/src/main/java/com/facebook/presto/execution/QueryExecution.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Concurring @mbasmanova's comment - we should be clear about what scan limit has been exceeded. I will recommend EXCEEDED_SCAN_RAW_BYTES_READ_LIMIT. The message can also be made more explicit
presto-main/src/main/java/com/facebook/presto/SystemSessionProperties.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
I can see a lot of issues with this logic:
- We are reading
finalQueryInfowhich is published only when the query is finished - can you test and see if this number is actually published when the query is running ? - For every stage that is a scan stage, we take the whole query's bytes read and add it. So if there are 2 scan stages - in that case, you will just return
2 * bytesreadbyquery. getAllStagesdoes a DFS traversal every time - and given that it will be called in a loop, this might be an expensive operation and we don't really care about the ordering here.
In my opinion - we should do something similar to the existing logic that exists for getTotalCpuTime. That will help ensure that the logics are similar and it is easy to change them together if we ever decide to head that path.
There was a problem hiding this comment.
ok so this is newly implemented, the original proposal was a simple approach which is to obtain the scanned data size from queryInfo::queryStats if the final resolution is to do something similiar to the existing logic with getTotalCpuTime.
DataSize scan = query.getQueryInfo().getQueryStats().getRawInputDataSize();
@mbasmanova what's your suggestion on this one?
There was a problem hiding this comment.
query.getQueryInfo().getQueryStats() is a sort of expensive method since it aggregates all the stats and not just bytes read. We should keep this data collection as light as possible in my opinion.
There was a problem hiding this comment.
Agree with @mayankgarg1990 -- this can be made to be less expensive and consistent with how we calculate CPU time.
There was a problem hiding this comment.
thank you @mayankgarg1990 @tdcmeehan for the comment, will revise this code accordingly to align with general data collection logic
There was a problem hiding this comment.
Were these comments addressed?
There was a problem hiding this comment.
yes this code block has been removed as to make it simple and similar to how cpu limits enforcement is handled
presto-main/src/main/java/com/facebook/presto/execution/SqlQueryManager.java
Outdated
Show resolved
Hide resolved
presto-main/src/main/java/com/facebook/presto/execution/SqlQueryManager.java
Outdated
Show resolved
Hide resolved
8f3188d to
0bb4527
Compare
done, squashed into 1 commit now |
|
@fgwang7w I'm in meetings all day today and won't have time to review/answer questions on this PR before the long weekend. I'll take a look early next week. |
Sure thank you, and in the meantime I will do more testing to ensure quality of the code |
|
hi @mbasmanova please help review the revised commit, many thanks~ |
|
@fgwang7w Looks good to, but I'll defer to @mayankgarg1990 and @tdcmeehan to confirm that their comments have been addressed properly. Commit message needs to be updated to match the guidelines at https://chris.beams.io/posts/git-commit/ . For example, |
|
@mayankgarg1990 @tdcmeehan Mayank, Tim, would you take another look? |
|
@mbasmanova , this is on my radar, I will get to it by tomorrow (7/22) :) |
|
Thank you, Mayank. |
3196c11 to
2f7fffb
Compare
|
@mayankgarg1990 @tdcmeehan Hi Could you please help review the commit for the upcoming release merge? many thanks! |
|
@fgwang7w - As @tdcmeehan pointed out - my comments from my previous review are not addressed yet. @tdcmeehan commented 2 days ago |
yes I resubmitted the squashed commit code just now and replied to both you @mayankgarg1990 and @tdcmeehan regarding the querystats perf issue for getScannedBytes method and default scan limit. Basically I have simplified the code and reduce unnecessary method calls per suggestions. The default scan limit is also removed for now. Please give another round of review, many thanks! |
There was a problem hiding this comment.
Putting a new comment since the old comment was marked as resolved. Lets match this with the actual metric (rawInputBytes) and the exception name - QUERY_MAX_SCAN_RAW_INPUT_BYTES
There was a problem hiding this comment.
sure actually I would synchronize all method call names and varilables to QueryMaxScanRawInputBytes to map with rawInputBytes
There was a problem hiding this comment.
Same comment as in SystemSessionProperties - lets use the same name as the actual metric - queryMaxRawInputBytes. In accordance with that lets change the config name as well - query.max-raw-input-bytes.
There was a problem hiding this comment.
these 2 are not being used - remove these
There was a problem hiding this comment.
This is still a bit expensive since getting querystats involves -> for all stages, for all tasks, for all metrics.
The way CPU does it is still cheaper -> all stages, all tasks, just the cpu - so we can do something similar here and make it even cheaper -
We can follow a similar flow here
SqlQueryManager -> SqlQuerySchedulerInterface#getTotalCpuTime -> SqlStageExecution#getTotalCpuTime
and just sum up the bytes involved.
There was a problem hiding this comment.
I'm not certain if your approach about summing up bytes from SqlStageExecution#getTotalScanBytes is accurate... it's always the best practice to inherit scanned byte size from existing querystats to ensure the result is correct. I suggest we maintain current implementation with SqlQueryManager:getBasicQueryInfo-> BasicQueryStats:getQueryStats . How much cheaper are we suggesting to reduce if we want to bypass the existing logic with a risk that we might have a false result?
My option is that rawInputDataSize should be the only reliable source of truth when setting against with the hard scan limit.
There was a problem hiding this comment.
I don't agree that BasicQueryStats:getQueryStats is the only trusted source. As you can see, we are already doing this for total cpu ms and memory limits so I don't see why raw input bytes will be any different here. Again, my only concern is that this is a single thread already doing cpu and memory enforcements and we should keep it as light weight as possible and by keeping this trend, we will ensure that the new entries that are added also follow this lighter weight approach.
There was a problem hiding this comment.
understood, thank you! I have implemented your approach, I think it is safer and cheaper to acquire actual number via SqlQuerySchedulerInterface::getRawInputDataSize -> QueryExecution::getRawInputDataSize, and sum up all task's stats via SqlStageExecution::getRawInputDataSize. Please review the revised commit again, many thanks!
0d8cff0 to
7837b6b
Compare
mayankgarg1990
left a comment
There was a problem hiding this comment.
looks good - just last comment
There was a problem hiding this comment.
nit - lets rename this as rawInputSize
There was a problem hiding this comment.
Lets add this check at the top to ensure that we are only considering table scan nodes -
if (planFragment.getTableScanSchedulingOrder().isEmpty()) {
return new DataSize(0, BYTE);
}
There was a problem hiding this comment.
agree, we need to bypass when source is empty, fixed
mayankgarg1990
left a comment
There was a problem hiding this comment.
looks good - one last comment
There was a problem hiding this comment.
Lets set the default to a higher number to avoid unexpected failures when people deploy this new version. Every sys admin should be able to set a reasonable value in their configurations. How about an exabyte (1000, PETABYTE)?
There was a problem hiding this comment.
sure, this field is adjustable anytime by DBAs. Default is revised to 1000PB.
Add query.max-scan-physical-bytes configuration and query_max_scan_physical_bytes session properties to limit the total number of bytes reads from storage during table scan. The default limit is 1PB.
| object -> object); | ||
| } | ||
|
|
||
| public static PropertyMetadata<DataSize> dataSizeProperty(String name, String description, DataSize defaultValue, boolean hidden) |
Fixes #14701