Allow setting per task memory limit#10308
Conversation
arhimondr
left a comment
There was a problem hiding this comment.
The change looks reasonable to me. Though I'm not an expert here. I wonder if there's anybody else who might want to take a look?
There was a problem hiding this comment.
Should this be @Min("1B")?
There was a problem hiding this comment.
Actually 0 has special meaning: "no-limit". Added description.
There was a problem hiding this comment.
Should it be allowed to be empty instead?
There was a problem hiding this comment.
It can be. I somewhat prefer it to be possible to explicitly set the value to "unlimited" in the config. Via session variable we can make unlimited if value is set to null - but I am not very convinced it is easier to comprehend that special 0.
@findepi any opinion here?
There was a problem hiding this comment.
I remember generally we prefer not to have values with a special meaning. To avoid enforcing this limit not setting the limit seems to be more intuitive than setting it to 0.
There was a problem hiding this comment.
Why would anyone set the property to 0 if default is no-limit?
There was a problem hiding this comment.
Currently no. I was more thinking of case when (if) we decide that default is not no-limit. I can change it though.
There was a problem hiding this comment.
I was more thinking of case when (if) we decide that default is not no-limit.
0 is odd, but then if we decide there should be some limit by default, should there be an option to run query without any per-task memory limit? User probably could set it to query mam memory per node.
lib/trino-memory-context/src/main/java/io/trino/memory/context/MemoryAllocationValidator.java
Outdated
Show resolved
Hide resolved
02918ac to
b4d0365
Compare
|
Rebase |
b4d0365 to
6621ee4
Compare
|
@findepi PTAL |
There was a problem hiding this comment.
This is really similar to MemoryReservationHandler. Could that interface do the job? It seems that memory allocations via reserveMemory should return Future as it's caller intention is to wait for memory. The other method tryReserveMemory is for trying to allocate memory.
With interface like this, I don't see a reason for two methods to exist (other than exception customization), but then probably single callback is fine: #10308 (comment)
There was a problem hiding this comment.
Future<> as a return type does not suite me well. No use for that. Sole purpose of validator is to "reject" the memory allocation if limit is exceeded. It is not handling the case when the limit is not exceeded but node run out of memory (pool is depleted).
There was a problem hiding this comment.
Future<> as a return type does not suite me well. No use for that. Sole purpose of validator is to "reject" the memory allocation if limit is exceeded. It is not handling the case when the limit is not exceeded but node run out of memory (pool is depleted).
Ok, so why do we need an interface with two methods?
Two methods there so we do not need to do exception driven logic in ChildAggregatedMemoryContext.tryUpdateBytes where MemoryAllocationValidator.tryReserveMemory is used.
If I just had reserveMemory I would need to try/catch there. It is an option, but I think it is not ellegant.
It seems that we leak implementation details to interface. I don't think two methods are needed now that you have ValidatingLocalMemoryContext
There was a problem hiding this comment.
I can make it one method which would return Optional<String>
- empty if allocation is valid
- containing allocation error description if allocation violates constraints
Then I can do the throwing inValidatingLocalMemoryContext
There was a problem hiding this comment.
Actually, I take that back. Then the ValidatingLocalMemoryContext need to decide on exception type to throw. I am not a big fan of hardcoding the ExceededMemoryLimitException(EXCEEDED_LOCAL_MEMORY_LIMIT, there. It makes class less reusable. And by design nothing is enforcing us to use the class only for TaskContext.
I suggest we keep interface as proposed.
lib/trino-memory-context/src/main/java/io/trino/memory/context/MemoryAllocationValidator.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
I wonder if this shouldn't be solved locally at task level via combination of newRootAggregatedMemoryContext and MemoryReservationHandler. Are there more use cases for newAggregatedMemoryContext(MemoryAllocationValidator memoryAllocationValidator) other than task allocation?
For example both OperatorContext and WorkProcessorPipelineSourceOperator both have their own InternalAggregatedMemoryContext. I'm not sure we should mix MemoryAllocationValidator memoryAllocationValidator into that picture (this adds complexity to the allocators structure)
lib/trino-memory-context/src/main/java/io/trino/memory/context/MemoryAllocationValidator.java
Outdated
Show resolved
Hide resolved
...no-memory-context/src/main/java/io/trino/memory/context/AbstractAggregatedMemoryContext.java
Outdated
Show resolved
Hide resolved
6621ee4 to
3828952
Compare
There was a problem hiding this comment.
do I need synchronization here and in trySetBytes?
core/trino-main/src/main/java/io/trino/SystemSessionProperties.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Why would anyone set the property to 0 if default is no-limit?
There was a problem hiding this comment.
Future<> as a return type does not suite me well. No use for that. Sole purpose of validator is to "reject" the memory allocation if limit is exceeded. It is not handling the case when the limit is not exceeded but node run out of memory (pool is depleted).
Ok, so why do we need an interface with two methods?
Two methods there so we do not need to do exception driven logic in ChildAggregatedMemoryContext.tryUpdateBytes where MemoryAllocationValidator.tryReserveMemory is used.
If I just had reserveMemory I would need to try/catch there. It is an option, but I think it is not ellegant.
It seems that we leak implementation details to interface. I don't think two methods are needed now that you have ValidatingLocalMemoryContext
There was a problem hiding this comment.
you have absolute value at hand, just pass it to validator (single method) as in:
void enforceUserMemoryLimit(long allocated, long delta)
There was a problem hiding this comment.
I do not have absolute value. Here I just know how many bytes are allocated by this LocalMemoryContext. And memory validator is accounting on AggregatedMemoryContext level.
There was a problem hiding this comment.
I think this should always throw exception if limit is exceeded (and not return false). This is how io.trino.memory.QueryContext#enforceUserMemoryLimit works and it makes sense.
Query needs X memory and X exceeds limit, so try should also fail
There was a problem hiding this comment.
Actually enforceUserMemoryLimit is not called from trySetBytes. I think it should on successful allocation, but it is not. Or am I reading something wrong.
core/trino-main/src/main/java/io/trino/operator/TaskAllocationValidator.java
Outdated
Show resolved
Hide resolved
3828952 to
ee9eb86
Compare
core/trino-main/src/main/java/io/trino/SystemSessionProperties.java
Outdated
Show resolved
Hide resolved
core/trino-main/src/main/java/io/trino/operator/TaskAllocationValidator.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
I was more thinking of case when (if) we decide that default is not no-limit.
0 is odd, but then if we decide there should be some limit by default, should there be an option to run query without any per-task memory limit? User probably could set it to query mam memory per node.
core/trino-main/src/main/java/io/trino/SystemSessionProperties.java
Outdated
Show resolved
Hide resolved
core/trino-main/src/main/java/io/trino/operator/TaskAllocationValidator.java
Outdated
Show resolved
Hide resolved
core/trino-main/src/main/java/io/trino/operator/TaskContext.java
Outdated
Show resolved
Hide resolved
core/trino-main/src/main/java/io/trino/operator/TaskAllocationValidator.java
Outdated
Show resolved
Hide resolved
core/trino-main/src/main/java/io/trino/operator/TaskAllocationValidator.java
Outdated
Show resolved
Hide resolved
core/trino-main/src/main/java/io/trino/operator/TaskAllocationValidator.java
Outdated
Show resolved
Hide resolved
...trino-memory-context/src/main/java/io/trino/memory/context/ValidatingLocalMemoryContext.java
Outdated
Show resolved
Hide resolved
...trino-memory-context/src/main/java/io/trino/memory/context/ValidatingLocalMemoryContext.java
Outdated
Show resolved
Hide resolved
ee9eb86 to
34a6352
Compare
This is just parameter rename; no semantic changes
34a6352 to
5abc758
Compare
A building block for better memory management to be used mostly together with tasks level retries (#9818)