Skip to content

Conversation

@miaoever
Copy link
Contributor

  • Add the NativeExecutionProcess class to be responsible to launch the native process.
  • Add the health check of the process launching by calling the native process's /v1/info endpoint with backoff retry.

@miaoever miaoever force-pushed the extend_native_execution_operator branch 6 times, most recently from db5f9da to 69204b3 Compare November 15, 2022 07:14
@miaoever miaoever marked this pull request as ready for review November 15, 2022 16:39
@miaoever miaoever requested a review from a team as a code owner November 15, 2022 16:39
@miaoever miaoever requested a review from presto-oss November 15, 2022 16:39
@v-jizhang
Copy link
Contributor

@bot kick off tests

Copy link
Contributor

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

Copy link
Contributor Author

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.

Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor

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

Copy link
Contributor

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.

Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor

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?

Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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?

Copy link
Contributor

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.

Copy link
Contributor Author

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()

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.

Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

@tanjialiang tanjialiang Nov 19, 2022

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.

Copy link
Contributor

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).

Copy link
Contributor

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?

Copy link
Contributor Author

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

@tanjialiang
Copy link
Contributor

tanjialiang commented Nov 17, 2022

Thanks @miaoever for working on this. Left some initial comments.
It would also be nice to break it down to multiple smaller commits so that it is easier to review.

@miaoever miaoever force-pushed the extend_native_execution_operator branch 2 times, most recently from 531c785 to b1011df Compare November 18, 2022 01:54
@miaoever miaoever requested a review from tanjialiang November 18, 2022 01:55

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.

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.

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 ?

Copy link
Contributor Author

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.

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch.

@miaoever miaoever force-pushed the extend_native_execution_operator branch from b1011df to 0f86d00 Compare November 18, 2022 17:58

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 ?

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.

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.

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.

Copy link
Contributor Author

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.

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 ?

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

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.

Copy link
Contributor Author

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.

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 ?

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.

Copy link
Contributor Author

@miaoever miaoever Nov 18, 2022

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.

@miaoever miaoever force-pushed the extend_native_execution_operator branch from 0f86d00 to 799948a Compare November 18, 2022 19:01
@highker highker removed their request for review November 18, 2022 19:36
Copy link

@arunthirupathi arunthirupathi left a 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.

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.

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 ?

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.

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.

@miaoever miaoever force-pushed the extend_native_execution_operator branch from 799948a to a851971 Compare November 18, 2022 22:22
Copy link
Contributor

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?

@miaoever miaoever force-pushed the extend_native_execution_operator branch from a851971 to 5f53961 Compare November 21, 2022 05:40
@miaoever miaoever requested a review from tanjialiang November 21, 2022 05:42
@miaoever miaoever force-pushed the extend_native_execution_operator branch from 5f53961 to e0756a1 Compare November 21, 2022 07:59
private final ScheduledExecutorService errorRetryScheduledExecutor;
private final RequestErrorTracker errorTracker;
private final HttpClient httpClient;
private final NativeExecutionTask nativeExecutionTask;
Copy link
Contributor

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?

Copy link
Contributor Author

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?

Copy link
Contributor

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.

Copy link
Contributor Author

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()
Copy link
Contributor

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
Copy link
Contributor

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.

Copy link
Contributor Author

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.

@tanjialiang
Copy link
Contributor

Thanks MJ, overall looks good. Just some nits

Copy link
Contributor

@tanjialiang tanjialiang left a 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.

@tanjialiang
Copy link
Contributor

tanjialiang commented Nov 25, 2022

Approve to unblock. Please make sure to read the comments before proceeding.

TODOs as discussed:

  1. move NativeExecutionTask out from NativeExecutionProcess (if not done in this PR).
  2. merge task/server clients to a single one.
  3. Change info fetcher and result fetcher to use RequestErrorTracker to be consistent.

@tanjialiang tanjialiang merged commit eeb5a7d into prestodb:master Nov 28, 2022
@wanglinsong wanglinsong mentioned this pull request Jan 12, 2023
30 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants