-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Add NativeExecutionProcess and facilities #18681
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
Add NativeExecutionProcess and facilities #18681
Conversation
db5f9da to
69204b3
Compare
|
@bot kick off tests |
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.
Why do we need this new class? It does not seem to have additional functionality compared to its parent
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.
The reason is I want to introduce the any native execution concept into the RequestErrorTracker in the presto-main module.
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.
I don't quite understand. Can you clarify?
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.
Removed the PrestoSparkRequestErrorTracker and use the RequestErrorTracker directly.
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.
If we choose to use the RequestErrorTracker to handle failure, it will be nice to also change result fetch and info fetch to use the same, to be consistent.
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.
Or we probably won't be needing that fancy backoff logic in the tracker at all. That IMO works better in a multi-tenant environment when resource contention is a commonly happening scenario. It could just be simple logic of n times retry with interval of m seconds. Up to you.
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.
This method does not seem to be used publicly, or more than one place. Shall we remove this method, or change it to private?
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.
Moved the retry method out to the caller, so keep this method public for the caller.
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.
The retry logic should not be inside of http client class. Can we move it to the caller class? Or somewhere better
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.
Yeah so is this one. I think this one calls the below private method. client class should only deal with simple http calls, sync or async.
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.
Are we trying to use port to compose the path?
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.
Yes, we're locating the config file with the presto_cpp binary in the same folder (to leverage the file cleanup mechanism), the binary path will be shared by all the containers running on the same host, so use the port number in the path to isolate the config for different tasks.
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.
I thought we had isolated file systems for each container?
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.
Would be nice if we can have the commands configurable through. That way we can enable/disable any additional features dynamically.
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.
Sounds good, I can add that in my next PR.
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.
Should we throw presto exception?
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.
Why do we put this here? It does not seem like been used.
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.
it will be used by caller (e.g native operator) by calling public NativeExecutionTask getTask()
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.
This field can be marked as final.
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.
Why do we want to combine the factories? They seem logically separated. NativeExecutionProcess is responsible for launching the process and maintain health, more like a worker role. NativeExecutionTask is responsible for scheduling task and retrieving results from external process, more like a coordinator role.
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.
Agreed with what you said but I think that's the exact reason we have separated classes for NativeExecutionProcess and NativeExecutionTask, but for the factory class, IMO the factory is used to control the creation of the object, in our case, the NativeExecutionProcess should naturally be responsible to create the NativeExecutionTask since the NativeExecutionTask can only work after the process launch/initialization having been finished.
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.
Initially we created this factory as a workaround to take in injected objects from GUICE framework where we did't actually need a factory for factory purpose for NativeExecutionTask. But since we used the factory pattern it will be better to follow the pattern convention: XXXFactory shall create XXX. Here AAAFactory 1) creates 2 different types AAA and BBB, 2) is stateful and has control plan logic (stop()). It looks more like some mix of creation and control.
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.
We can have a separate NativeExecutionProcessFactory (it's okay to have 2 factories) that only creates a NativeExecutionProcess (for the purpose of accepting GUICE injections) and let operator do the rest (lifecycle management etc).
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.
One more question: Do we need to take care of the shutdown of injected resources? Shouldn't they be managed by the framework?
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.
Updated to bring back the NativeExecutionTaskFactory
|
Thanks @miaoever for working on this. Left some initial comments. |
531c785 to
b1011df
Compare
presto-spark-base/src/main/java/com/facebook/presto/spark/execution/NativeExecutionProcess.java
Outdated
Show resolved
Hide resolved
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.
In Presto the convention is.
static final
final
non final fields.
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.
This field can be marked as final.
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.
This is assigned but always overwritten, is this assignment required ?
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.
You're right, it's not required, just removed.
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.
others are calling shutdownNow, this calls shutdown
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.
good catch.
b1011df to
0f86d00
Compare
presto-spark-base/src/main/java/com/facebook/presto/spark/execution/NativeExecutionProcess.java
Outdated
Show resolved
Hide resolved
presto-spark-base/src/main/java/com/facebook/presto/spark/execution/NativeExecutionProcess.java
Outdated
Show resolved
Hide resolved
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.
Should we log the errors when it fails to start ?
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.
this method can be static.
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.
this method can be static.
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.
This method is bound to have race conditions, we are opening and closing the port, and other thread could re-open the same port. Not sure if my understanding is right.
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.
Once we close the socket with a given port, the TCP will be in the TIME_WAIT states for certain time (minutes IIRC), so during the TIME_WAIT period, that port won't be selected again by other processes/threads. Although this can not avoid the race condition totally, it can largely reduce the likelihood IMO. Without a central coordinated mechanism (say the coordinator assigns unique ports to different workers), this probably the only feasible way to choose an available port by each worker independently. Happy to hear your thought here.
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.
Do we want to log the failedReason as well here ?
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.
should this method be marked as @ Override
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.
nit: It is generally an anti pattern to overwrite the config property in the code.
Can there be multiple workers on the same box, if so there is a chance for all the workers to keep mutating this property.
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.
This code will be executed on the worker side, so the mutation is only visible to the current worker IIUC. The reason we have to pick and assign the port per worker is in our prod environment, there is no port isolation among all the containers running on the same host, so we have to pick unique port per worker to avoid port collision. This system config (NativeExecutionSystemConfig) will be passed down to the presto_cpp process eventually.
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.
Can you please add this as a comment on why we are doing this ?
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.
Ensure that this property can never be overriden and only be set by the system admins. Otherwise, bad actors might trick Presto in to running untrusted binaries.
It will also be good, if you ensure that the file exists with this executable and fail with a meaningful error when the file is not present.
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.
That's a good point. On Sapphire/Spark side (internally) we prevent the bad actor by not distributing untrusted libraries, so even user set this property, the library won't be distributed to the worker for execution.
We also already have the file not exist check inside the getProcessWorkingPath method.
0f86d00 to
799948a
Compare
arunthirupathi
left a comment
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.
Some more comments, looks good.
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.
This will NPE, if the object is created, but not started and then closed.
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.
Can you please add this as a comment on why we are doing this ?
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.
Since this will be use visible also wrap this in PrestoException.
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.
The method returns void, but this is retrieved and ignored. Can you please add comment around the requirement of the code.
799948a to
a851971
Compare
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.
I don't quite understand. Can you clarify?
a851971 to
5f53961
Compare
5f53961 to
e0756a1
Compare
| private final ScheduledExecutorService errorRetryScheduledExecutor; | ||
| private final RequestErrorTracker errorTracker; | ||
| private final HttpClient httpClient; | ||
| private final NativeExecutionTask nativeExecutionTask; |
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.
NativeExecutionTask is not used in the class other than a getter. Could we move NativeExecutionTask to be owned by the same entity that owns NativeExecutionProcess?
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.
I think since logically the NativeExecutionProcess need to outlast the NativeExecutionTask and IMO it's reasonable to let it controls the lifecycle of the NativeExecutionTask, WDYT?
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.
Since we are also referencing NativeExecutionTask in NativeExecutionOperator for calls and controls then operator would be a better place for both task and process. I believe putting task inside process does not actually do lifecycle control as JVM has its own garbage collection mechanism. Even if last reference to process is gone it will just be the process that is garbage collected but not the task within as long as there is something referencing that task.
Also if we think about higher level concepts, a class being a member is a belong-to relation. Here a task exists more like in parallel with process in a way that process is an abstract concept of cpp process, and it communicates with task through http. Together they get a job done.
If we compare it to the universal "car" example:
- native process is the car engine.
- native task is the car tire.
- native operator is the car.
having task inside process is like having tire inside engine. But car also references tire as it needs it to roll and run. Now it creates this coupling that is generally discouraged if it could be avoided.
I'm approving this change to unblock since it's been a while. It will be the best to make the change in this PR but I'm okay if we want to do it in a follow-up.
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.
IMO, practically NativeExecutionTask strictly depends on the lifecycle of NativeExecutionProcess, if the process has died or not started yet, the task shouldn't be called by the user, giving both the NativeExecutionTask and NativeExecutionProcess to the user (e.g NativeExecutionOperator) to make sure every time they want to call the task , the native process is alive/available is not ideal - Ideally, we should expose the NativeExecutionTask's APIs through the NativeExecutionProcess and check the process's status before actually calling into the NativeExecutionTask. I can do a it in the follow-up diff.
| } | ||
|
|
||
| @PreDestroy | ||
| public void stop() |
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.
I see Presto code base other places doing the same thing (shutting down resources in factory class, basing on the @PreDestroy annotation). It'll do the job but just super weird place. Let's keep it this way then.
| * An abstraction of HTTP client that communicates with the locally running Presto worker process. It exposes worker's server level endpoints to simple method calls. | ||
| */ | ||
| @ThreadSafe | ||
| public class PrestoSparkHttpServerClient |
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.
Maybe we just need one class for http calls. Can we merge this one and PrestoSparkHttpWorkerClient? Maybe not in this PR. We can put it as a todo.
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.
Added the TODO as suggested.
|
Thanks MJ, overall looks good. Just some nits |
tanjialiang
left a comment
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.
Approve to unblock. Please make sure to read the comments before proceeding.
TODOs as discussed:
|
NativeExecutionProcessclass to be responsible to launch the native process.