Skip to content
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

adding multiple grpc connections for python server and executor client #3356

Merged
merged 10 commits into from
Jul 15, 2021

Conversation

mwm5945
Copy link
Contributor

@mwm5945 mwm5945 commented Jun 30, 2021

What this PR does / why we need it: Support multiple GRPC connections on the Python Server to allow for pre-fork multi-processing, and multiplex requests over multiple GRPC Client connections in the executor.

Which issue(s) this PR fixes:

Fixes #3334

Special notes for your reviewer:

Does this PR introduce a user-facing change?:

add configurable workers/ threads to GRPC server in Python wrapper
introduce multiple GRPC connections to each node from the executor

@seldondev
Copy link
Collaborator

Hi @mwm5945. Thanks for your PR.

I'm waiting for a SeldonIO or todo member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the jenkins-x/lighthouse repository.

Comment on lines 62 to 99
s.RLock()
randNum := rand.Intn(numConns)
k := fmt.Sprintf("%s:%d", host, port)
if conn, ok := s.conns[k]; ok {
return conn, nil
if nodeConns, ok := s.conns[k]; ok {
if c := nodeConns[randNum]; c != nil {
defer s.RUnlock()
return c, nil
}
s.RUnlock()

c, err := s.createNewConn(modelName, host, port)
if err != nil {
return nil, err
}

s.Lock()
s.conns[k][randNum] = c
s.Unlock()

return s.conns[k][randNum], nil
} else {
opts := []grpc.DialOption{
grpc.WithInsecure(),
}
opts = append(opts, grpc2.AddClientInterceptors(s.Predictor, s.DeploymentName, modelName, s.annotations, s.Log))
conn, err := grpc.Dial(fmt.Sprintf("%s:%d", host, port), opts...)
if err != nil {
return nil, err
}
s.conns[k] = conn
return conn, nil
s.RUnlock()
connList := make([]*grpc.ClientConn, numConns)

c, err := s.createNewConn(modelName, host, port)
if err != nil {
return nil, err
}

connList[randNum] = c

s.Lock()
s.conns[k] = connList
s.Unlock()

return s.conns[k][randNum], nil
}
}
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've done this complicated stuff because it appears to better spread out the connections amongst the available processes on the server side. There's still no guarantee that each client will connect to a unique process though.

@mwm5945
Copy link
Contributor Author

mwm5945 commented Jul 1, 2021

i think it'd also be a good idea to add a new arg to the python server to match the new GRPC_THREADS, i.e GRPC_WORKERS

@mwm5945 mwm5945 marked this pull request as ready for review July 1, 2021 16:06
@ukclivecox
Copy link
Contributor

/ok-to-test

@ukclivecox
Copy link
Contributor

@mwm5945 Can you run "make fmt" from python folder to solve lint issue?

@review-notebook-app
Copy link

Check out this pull request on  ReviewNB

See visual diffs & provide feedback on Jupyter Notebooks.


Powered by ReviewNB

@seldondev seldondev added size/XL and removed size/L labels Jul 2, 2021
@axsaucedo
Copy link
Contributor

Looking great @mwm5945 ! It seems the Python tests are failing for GRPC (on the CI) - conscious that this woudl run the grpc service locally it may require some tweaks to work with the new implementation

@axsaucedo
Copy link
Contributor

(The docs test failing is due to an issue which is now fixed in master so rebasing should address it)

@mwm5945
Copy link
Contributor Author

mwm5945 commented Jul 2, 2021

@axsaucedo do you know what version of python the python-builder is using? The error appears to be

   File "/__w/seldon-core/seldon-core/python/.tox/py3/lib/python3.7/site-packages/seldon_core/microservice.py", line 492, in _reserve_grpc_port
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
AttributeError: module 'socket' has no attribute 'SO_REUSEPORT'

Appears this might be an issue with Conda or even an issue with a specific version of Python 3

@mwm5945
Copy link
Contributor Author

mwm5945 commented Jul 2, 2021

(also the classic "works on my machine" lol)

python/seldon_core/wrapper.py Outdated Show resolved Hide resolved
@ukclivecox
Copy link
Contributor

@mwm5945 Can you fix lint with make fmt in python folder

@mwm5945 mwm5945 force-pushed the multi-conn branch 2 times, most recently from 756bf39 to d625585 Compare July 7, 2021 17:32
@mwm5945
Copy link
Contributor Author

mwm5945 commented Jul 7, 2021

@cliveseldon running make fmt in python directory doesn't change anything. looks like something else:

make: *** [Makefile:34: linkcheck] Error 1
examples/argo_workflows_batch.nblink:36: [broken] https://github.com/argoproj/argo/issues/2376#issuecomment-595593237: 404 Client Error: Not Found for url: https://github.com/argoproj/argocon21/issues/2376
examples/argo_workflows_hdfs_batch.nblink:37: [broken] https://github.com/argoproj/argo/issues/2376#issuecomment-595593237: 404 Client Error: Not Found for url: https://github.com/argoproj/argocon21/issues/2376
examples/knative_eventing_streaming.nblink:27: [broken] https://knative.dev/docs/install/any-kubernetes-cluster/: 404 Client Error: Not Found for url: https://knative.dev/docs/install/any-kubernetes-cluster/
examples/statsmodels.nblink:26: [broken] https://rb.gy/jgybo9: 403 Client Error: Forbidden for url: https://computingforgeeks.com/install-source-to-image-toolkit-on-linux/#:~:text=Install%20Source-To-Image%20(,correct%20distribution%20for%20your%20machine.
examples/vegeta_bench_argo_workflows.nblink:35: [broken] https://github.com/argoproj/argo/issues/2376#issuecomment-595593237: 404 Client Error: Not Found for url: https://github.com/argoproj/argocon21/issues/2376
reference/images.md:171: [broken] https://mvnrepository.com/artifact/io.seldon.wrapper/seldon-core-wrapper: 403 Client Error: Forbidden for url: https://mvnrepository.com/artifact/io.seldon.wrapper/seldon-core-wrapper
reference/images.md:174: [broken] https://mvnrepository.com/artifact/io.seldon.wrapper/seldon-core-jpmml: 403 Client Error: Forbidden for url: https://mvnrepository.com/artifact/io.seldon.wrapper/seldon-core-jpmml
make: Leaving directory '/__w/seldon-core/seldon-core/doc'

@axsaucedo
Copy link
Contributor

/test notebooks

@seldondev
Copy link
Collaborator

Benchmark results - Testing Workers Performance

Results table

mean 50th 90th 95th 99th throughputAchieved success errors iteration_name replicas serverWorkers serverThreads modelUri image server apiType requestsCpu requestsMemory limitsCpu limitsMemory benchmarkCpu concurrency duration rate disableOrchestrator
0 24.2608 20.5097 42.7636 49.5222 61.3458 411.208 12331 10 seldon-benchmark-sdep-0 1 1 1 seldonio/seldontest_predict:1.10.0-dev grpc 4000Mi 500Mi 4000Mi 500Mi 1 10 30s 0 false
4 185.899 193.455 209.361 243.485 296.011 536.583 16011 100 seldon-benchmark-sdep-1 1 1 1 seldonio/seldontest_predict:1.10.0-dev grpc 4000Mi 500Mi 4000Mi 500Mi 1 100 30s 0 false
10 1888.2 1959.76 2145.61 2391.51 5967.1 525.896 14892 958 seldon-benchmark-sdep-2 1 1 1 seldonio/seldontest_predict:1.10.0-dev grpc 4000Mi 500Mi 4000Mi 500Mi 1 1000 30s 0 false
2 142 118.202 203.706 210.909 293.252 699.264 20877 100 seldon-benchmark-sdep-13 1 10 1 seldonio/seldontest_predict:1.10.0-dev grpc 4000Mi 500Mi 4000Mi 500Mi 1 100 30s 0 false
3 1285.38 1004.45 2896.8 3221.89 5299.59 772.094 22234 991 seldon-benchmark-sdep-14 1 10 1 seldonio/seldontest_predict:1.10.0-dev grpc 4000Mi 500Mi 4000Mi 500Mi 1 1000 30s 0 false
17 22.8918 13.8471 58.5188 64.4981 74.1364 435.536 13063 10 seldon-benchmark-sdep-12 1 10 1 seldonio/seldontest_predict:1.10.0-dev grpc 4000Mi 500Mi 4000Mi 500Mi 1 10 30s 0 false
11 22.784 13.6012 59.2125 65.5706 75.1334 437.627 13125 10 seldon-benchmark-sdep-6 1 5 1 seldonio/seldontest_predict:1.10.0-dev grpc 4000Mi 500Mi 4000Mi 500Mi 1 10 30s 0 false
13 1427.98 1001.6 2423.75 5097.39 8926.23 695.29 20097 834 seldon-benchmark-sdep-8 1 5 1 seldonio/seldontest_predict:1.10.0-dev grpc 4000Mi 500Mi 4000Mi 500Mi 1 1000 30s 0 false
14 141.457 112.994 202.845 208.356 287.051 703.246 20999 100 seldon-benchmark-sdep-7 1 5 1 seldonio/seldontest_predict:1.10.0-dev grpc 4000Mi 500Mi 4000Mi 500Mi 1 100 30s 0 false
5 2625.33 2439.3 3771.87 4451.3 5396.75 367.99 11805 0 seldon-benchmark-sdep-5 1 1 1 seldonio/seldontest_predict:1.10.0-dev rest 4000Mi 500Mi 4000Mi 500Mi 1 1000 30s 0 false
15 38.5104 25.0979 73.9829 78.6367 86.8813 258.954 7774 0 seldon-benchmark-sdep-3 1 1 1 seldonio/seldontest_predict:1.10.0-dev rest 4000Mi 500Mi 4000Mi 500Mi 1 10 30s 0 false
16 270.036 293.456 310.817 396.607 493.878 367.044 11089 0 seldon-benchmark-sdep-4 1 1 1 seldonio/seldontest_predict:1.10.0-dev rest 4000Mi 500Mi 4000Mi 500Mi 1 100 30s 0 false
6 36.9401 16.8542 78.8649 81.8658 87.5473 270.092 8118 0 seldon-benchmark-sdep-15 1 10 1 seldonio/seldontest_predict:1.10.0-dev rest 4000Mi 500Mi 4000Mi 500Mi 1 10 30s 0 false
9 256.319 286.373 384.265 399.833 491.269 385.945 11663 0 seldon-benchmark-sdep-16 1 10 1 seldonio/seldontest_predict:1.10.0-dev rest 4000Mi 500Mi 4000Mi 500Mi 1 100 30s 0 false
12 2593.89 2397.71 3884.94 4944.02 6029.06 377.365 11728 0 seldon-benchmark-sdep-17 1 10 1 seldonio/seldontest_predict:1.10.0-dev rest 4000Mi 500Mi 4000Mi 500Mi 1 1000 30s 0 false
1 37.0189 16.6341 79.3571 82.4084 87.6142 269.506 8094 0 seldon-benchmark-sdep-9 1 5 1 seldonio/seldontest_predict:1.10.0-dev rest 4000Mi 500Mi 4000Mi 500Mi 1 10 30s 0 false
7 2662.02 2535.41 3902.7 4407.92 5921.37 366.961 11615 0 seldon-benchmark-sdep-11 1 5 1 seldonio/seldontest_predict:1.10.0-dev rest 4000Mi 500Mi 4000Mi 500Mi 1 1000 30s 0 false
8 261.917 289.146 388.399 400.194 489.467 378.103 11407 0 seldon-benchmark-sdep-10 1 5 1 seldonio/seldontest_predict:1.10.0-dev rest 4000Mi 500Mi 4000Mi 500Mi 1 100 30s 0 false

@seldondev
Copy link
Collaborator

Benchmark Results - Python Wrapper V1 vs V2

  • V1 base mean performance latency under 10ms: True
  • V1 base 99th performance latenc under 10ms: True
  • V1 base throughput above 180rps: True
  • V1 base throughput above 250rps: True
  • V2 mean performance latency under 5ms: True
  • V2 99th performance latenc under 10ms: True
  • V2 REST throughput above 250rps: True
  • V2 throughput above 300rps: True

Python V1 Wrapper Results table

mean 50th 90th 95th 99th throughputAchieved success errors iteration_name replicas serverWorkers serverThreads modelUri image server apiType requestsCpu requestsMemory limitsCpu limitsMemory benchmarkCpu concurrency duration rate disableOrchestrator
3 6.2184 5.03284 9.17607 13.3643 25.0441 158.952 4769 1 seldon-benchmark-sdep-3 1 5 1 gs://seldon-models/sklearn/iris SKLEARN_SERVER grpc 2000Mi 500Mi 2000Mi 500Mi 1 1 30s 0 false
2 6.98751 6.3428 9.85795 11.4814 15.3397 143.023 4293 0 seldon-benchmark-sdep-0 1 5 1 gs://seldon-models/sklearn/iris SKLEARN_SERVER rest 2000Mi 500Mi 2000Mi 500Mi 1 1 30s 0 false
5 506.688 496.119 568.383 605.451 733.702 295.355 8732 149 seldon-benchmark-sdep-5 1 5 1 gs://seldon-models/sklearn/iris SKLEARN_SERVER grpc 2000Mi 500Mi 2000Mi 500Mi 1 150 30s 0 false
1 379.011 390.536 526.133 599.789 716.463 391.924 11862 0 seldon-benchmark-sdep-2 1 5 1 gs://seldon-models/sklearn/iris SKLEARN_SERVER rest 2000Mi 500Mi 2000Mi 500Mi 1 150 30s 0 false
4 165.851 162.807 186.1 195.667 211.005 300.995 8988 49 seldon-benchmark-sdep-4 1 5 1 gs://seldon-models/sklearn/iris SKLEARN_SERVER grpc 2000Mi 500Mi 2000Mi 500Mi 1 50 30s 0 false
0 140.511 110.274 199.23 202.891 214.234 349.975 10531 0 seldon-benchmark-sdep-1 1 5 1 gs://seldon-models/sklearn/iris SKLEARN_SERVER rest 2000Mi 500Mi 2000Mi 500Mi 1 50 30s 0 false

Python V2 MLServer Results table

mean 50th 90th 95th 99th throughputAchieved success errors iteration_name replicas serverWorkers serverThreads modelUri image server apiType requestsCpu requestsMemory limitsCpu limitsMemory benchmarkCpu concurrency duration rate disableOrchestrator
2 3.64919 3.07471 4.82902 6.72292 14.454 269.713 8094 1 seldon-benchmark-sdep-3 1 5 1 gs://seldon-models/sklearn/iris-0.23.2/lr_model SKLEARN_SERVER grpc 2000Mi 500Mi 2000Mi 500Mi 1 1 30s 0 false
0 4.50848 3.86117 5.97066 8.93354 15.1812 221.677 6653 0 seldon-benchmark-sdep-0 1 5 1 gs://seldon-models/sklearn/iris-0.23.2/lr_model SKLEARN_SERVER rest 2000Mi 500Mi 2000Mi 500Mi 1 1 30s 0 false
5 189.577 193.87 229.434 280.851 331.534 785.02 23547 63 seldon-benchmark-sdep-5 1 5 1 gs://seldon-models/sklearn/iris-0.23.2/lr_model SKLEARN_SERVER grpc 2000Mi 500Mi 2000Mi 500Mi 1 150 30s 0 false
3 351.982 306.306 504.061 599.311 708.6 421.912 12783 0 seldon-benchmark-sdep-2 1 5 1 gs://seldon-models/sklearn/iris-0.23.2/lr_model SKLEARN_SERVER rest 2000Mi 500Mi 2000Mi 500Mi 1 150 30s 0 false
4 79.96 86.8954 115.535 124.668 183.272 620.549 18590 50 seldon-benchmark-sdep-4 1 5 1 gs://seldon-models/sklearn/iris-0.23.2/lr_model SKLEARN_SERVER grpc 2000Mi 500Mi 2000Mi 500Mi 1 50 30s 0 false
1 133.052 104.705 198.788 202.478 218.743 372.1 11181 0 seldon-benchmark-sdep-1 1 5 1 gs://seldon-models/sklearn/iris-0.23.2/lr_model SKLEARN_SERVER rest 2000Mi 500Mi 2000Mi 500Mi 1 50 30s 0 false

@seldondev
Copy link
Collaborator

Benchmark results - Testing Seldon V1 Data Types

Results for NDArray

mean 50th 90th 95th 99th throughputAchieved success errors iteration_name replicas serverWorkers serverThreads modelUri image server apiType requestsCpu requestsMemory limitsCpu limitsMemory benchmarkCpu concurrency duration rate disableOrchestrator
2 72.2499 68.4145 95.153 100.289 122.989 13.8209 414 1 seldon-benchmark-sdep-3 1 5 1 seldonio/seldontest_predict:1.10.0-dev grpc 2000Mi 500Mi 2000Mi 500Mi 1 1 30s 0 false
0 11.4367 9.73482 17.3051 20.6628 27.3786 87.4106 2625 0 seldon-benchmark-sdep-0 1 5 1 seldonio/seldontest_predict:1.10.0-dev rest 2000Mi 500Mi 2000Mi 500Mi 1 1 30s 0 false
5 5311.52 3055.22 13450.5 15067.5 16775 28.182 697 150 seldon-benchmark-sdep-5 1 5 1 seldonio/seldontest_predict:1.10.0-dev grpc 2000Mi 500Mi 2000Mi 500Mi 1 150 30s 0 false
3 531.805 500.217 800.327 896.725 1113.66 277.159 8404 0 seldon-benchmark-sdep-2 1 5 1 seldonio/seldontest_predict:1.10.0-dev rest 2000Mi 500Mi 2000Mi 500Mi 1 150 30s 0 false
4 1888.19 1807.14 3334.14 3904.93 4748.54 26.4031 743 50 seldon-benchmark-sdep-4 1 5 1 seldonio/seldontest_predict:1.10.0-dev grpc 2000Mi 500Mi 2000Mi 500Mi 1 50 30s 0 false
1 193.095 194.585 289.645 298.39 314.638 253.457 7651 0 seldon-benchmark-sdep-1 1 5 1 seldonio/seldontest_predict:1.10.0-dev rest 2000Mi 500Mi 2000Mi 500Mi 1 50 30s 0 false

Results for Tensor

mean 50th 90th 95th 99th throughputAchieved success errors iteration_name replicas serverWorkers serverThreads modelUri image server apiType requestsCpu requestsMemory limitsCpu limitsMemory benchmarkCpu concurrency duration rate disableOrchestrator
3 7.2633 5.55938 12.0603 16.9254 27.6171 136.289 4090 1 seldon-benchmark-sdep-3 1 5 1 seldonio/seldontest_predict:1.10.0-dev grpc 2000Mi 500Mi 2000Mi 500Mi 1 1 30s 0 false
0 11.81 10.3709 17.9178 21.8536 30.1171 84.6472 2541 0 seldon-benchmark-sdep-0 1 5 1 seldonio/seldontest_predict:1.10.0-dev rest 2000Mi 500Mi 2000Mi 500Mi 1 1 30s 0 false
2 463.074 425.641 815.059 907.971 1120.62 322.109 9533 150 seldon-benchmark-sdep-5 1 5 1 seldonio/seldontest_predict:1.10.0-dev grpc 2000Mi 500Mi 2000Mi 500Mi 1 150 30s 0 false
1 529.795 500.248 792.73 889.875 1092.3 279.013 8455 0 seldon-benchmark-sdep-2 1 5 1 seldonio/seldontest_predict:1.10.0-dev rest 2000Mi 500Mi 2000Mi 500Mi 1 150 30s 0 false
5 181.581 188.49 282.179 297.24 331.499 273.43 8165 50 seldon-benchmark-sdep-4 1 5 1 seldonio/seldontest_predict:1.10.0-dev grpc 2000Mi 500Mi 2000Mi 500Mi 1 50 30s 0 false
4 193.748 193.407 288.508 297.616 335.895 250.807 7562 0 seldon-benchmark-sdep-1 1 5 1 seldonio/seldontest_predict:1.10.0-dev rest 2000Mi 500Mi 2000Mi 500Mi 1 50 30s 0 false

Results for TFTensor

mean 50th 90th 95th 99th throughputAchieved success errors iteration_name replicas serverWorkers serverThreads modelUri image server apiType requestsCpu requestsMemory limitsCpu limitsMemory benchmarkCpu concurrency duration rate disableOrchestrator
0 6.53137 5.03924 10.8549 14.4056 26.2085 151.682 4551 1 seldon-benchmark-sdep-3 1 5 1 seldonio/seldontest_predict:1.10.0-dev grpc 2000Mi 500Mi 2000Mi 500Mi 1 1 30s 0 false
2 9.71706 8.60703 13.9114 15.944 21.8913 102.875 3087 0 seldon-benchmark-sdep-0 1 5 1 seldonio/seldontest_predict:1.10.0-dev rest 2000Mi 500Mi 2000Mi 500Mi 1 1 30s 0 false
5 356.9 306.762 600.2 690.43 914.403 417.781 12422 134 seldon-benchmark-sdep-5 1 5 1 seldonio/seldontest_predict:1.10.0-dev grpc 2000Mi 500Mi 2000Mi 500Mi 1 150 30s 0 false
1 555.003 516.095 810.986 929.128 1197.81 266.585 8119 0 seldon-benchmark-sdep-2 1 5 1 seldonio/seldontest_predict:1.10.0-dev rest 2000Mi 500Mi 2000Mi 500Mi 1 150 30s 0 false
4 145.02 128.482 210.199 231.784 301.324 342.415 10251 39 seldon-benchmark-sdep-4 1 5 1 seldonio/seldontest_predict:1.10.0-dev grpc 2000Mi 500Mi 2000Mi 500Mi 1 50 30s 0 false
3 198.747 195.004 291.187 301.946 361.192 244.31 7359 0 seldon-benchmark-sdep-1 1 5 1 seldonio/seldontest_predict:1.10.0-dev rest 2000Mi 500Mi 2000Mi 500Mi 1 50 30s 0 false

@axsaucedo
Copy link
Contributor

/test integration

@axsaucedo
Copy link
Contributor

/test notebooks

@axsaucedo
Copy link
Contributor

/test notebooks

1 similar comment
@axsaucedo
Copy link
Contributor

/test notebooks

Copy link
Contributor

@axsaucedo axsaucedo left a comment

Choose a reason for hiding this comment

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

Added a comment as seems custom metrics test is failing

python/seldon_core/microservice.py Show resolved Hide resolved
@@ -60,7 +63,7 @@ def start_servers(
"""
p2 = None
if target2:
p2 = mp.Process(target=target2, daemon=True)
Copy link
Contributor

Choose a reason for hiding this comment

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

Why this change?

Copy link
Contributor

Choose a reason for hiding this comment

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

@axsaucedo
Copy link
Contributor

It seems the custom metrics test is failing, it's not yet clear what may be causing this issue - trying to see whether it could be that the changes are somehow blocking the metrics port and the prometheus metrics are not being scraped, however I can't spot explicitly a reason why this could be the case. Not sure if you have any thouhts @mwm5945 ? I will try to run locally to validate

@axsaucedo
Copy link
Contributor

/test notebooks

@axsaucedo
Copy link
Contributor

/test integration

@axsaucedo
Copy link
Contributor

All tests passing - merging
/approve
image

@seldondev
Copy link
Collaborator

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: axsaucedo

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add functionality to support multiprocessing for Python wrapper GRPC
5 participants