Skip to content

Commit c74f2f0

Browse files
Add turing_req_id in requests made to Turing Router components (#230)
* Add turing request id to requests sent to router components * Replace deprecated ioutil.ReadAll function with io.ReadAll * Fix http handler test * Add mention of turing_req_id to docs * Add sample of how turing_req_id is accessed in the pyfunc ensembler * Rename turing_req_id in docs to Turing-Req-Id
1 parent 82c7979 commit c74f2f0

File tree

15 files changed

+60
-25
lines changed

15 files changed

+60
-25
lines changed

docs/how-to/create-a-router/configure-enricher.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,12 @@ The Turing enricher has the ability to perform arbitrary transformations on the
1010
Original request will be sent to configured routes without enrichment.
1111

1212
### Docker
13-
Turing will deploy specified Docker image as a pre-processor and will send original request to it for enrichment. To configure a Docker enricher, there are 3 sections to fill.
13+
Turing will deploy specified Docker image as a pre-processor and will send original request to it for enrichment.
14+
15+
The request to the enricher is constructed from the request headers to the Router, and an identifier `Turing-Req-Id`
16+
that is uniquely assigned to each request received by the Router.
17+
18+
To configure a Docker enricher, there are 3 sections to fill.
1419

1520
Configure the Docker Container. There are 4 required inputs.
1621

docs/how-to/create-a-router/configure-ensembler.md

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,14 @@ It is not possible to select as the fallback response a route that has traffic r
2626
{% endhint %}
2727

2828
## Docker
29-
Turing will deploy the specified image as a post-processor and will send in the request payload the following, for ensembling - the original request, responses from all routes, and the treatment configuration (if an Experiment Engine is selected, in Configure Experiment Engine). The ensembler's request headers will contain the original request headers sent to Turing, merged with the enricher's response headers (if there are duplicates, the value in the enricher's response headers will take precedence). To configure a Docker ensembler, there are 3 sections to be filled.
29+
30+
Turing will deploy the specified image as a post-processor and will send in the request payload the following, for
31+
ensembling - the original request, responses from all routes, and the treatment configuration (if an Experiment
32+
Engine is selected, in the Configure Experiment Engine step). The ensembler's request headers will contain the original
33+
request headers sent to Turing, merged with the enricher's response headers (if there are duplicates, the value in
34+
the enricher's response headers will take precedence), and an identifier `Turing-Req-Id` that is uniquely assigned to each request received by the Router.
35+
36+
To configure a Docker ensembler, there are 3 sections to be filled.
3037

3138
Configure the Docker Container. There are 4 required inputs.
3239

@@ -57,20 +64,26 @@ Configure the resources required for the ensembler. There are 3 required inputs,
5764
**Min/Max Replicas**: Min/max number of replicas for your ensembler. Scaling of the ensembler based on traffic volume will be automatically done for you.
5865

5966
## Pyfunc Ensembler
60-
Turing will deploy a previously registered pyfunc ensembler (refer to
67+
Turing will deploy a previously registered Pyfunc ensembler (refer to
6168
[the samples](https://github.com/caraml-dev/turing/tree/main/sdk/samples) in the SDK section for more information on how to
6269
deploy one) as a containerised web service.
6370

6471
This allows you to simply define the logic required for the ensembling
6572
step by implementing a Python `mlflow`-based interface, and rely on Turing API to containerise and package your
6673
implementation as an entire web service automatically.
6774

75+
Similar to requests sent to a Docker Ensembler, the request payload sent to a Pyfunc ensembler will contain the
76+
original request, responses from all routes, and the treatment configuration (if an Experiment
77+
Engine is selected, in the Configure Experiment Engine step). The ensembler's request headers will contain the original
78+
request headers sent to Turing, merged with the enricher's response headers (if there are duplicates, the value in
79+
the enricher's response headers will take precedence), and an identifier `Turing-Req-Id` that is uniquely assigned to each request received by the Router.
80+
6881
To configure your router with a Pyfunc ensembler, simply select from the drop down list your desired ensembler,
6982
registered in your current project. You'll also need to indicate your desired timeout value and resource request values:
7083

7184
![](../../.gitbook/assets/pyfunc_ensembler_config.png)
7285

73-
**Pyfunc Ensembler**: The name of the pyfunc ensembler that has been deployed in your *current* project
86+
**Pyfunc Ensembler**: The name of the Pyfunc ensembler that has been deployed in your *current* project
7487

7588
**Timeout**: Request timeout, which when exceeded, the request to the ensembler will be terminated
7689

@@ -87,7 +100,8 @@ The router will send responses from all routes, together with treatment configur
87100

88101

89102
## Ensembler Request Payload Format
90-
When the ensembler type is Docker/External, the ensembler will receive the following information in the request payload and the behaviour of the ensembler is up to the implementer.
103+
When the ensembler type is Docker/Pyfunc/External, the ensembler will receive the following information in the request
104+
payload and the behaviour of the ensembler is up to the implementer.
91105

92106
```json
93107
{

docs/how-to/create-a-router/configure-experiment-engine.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
This step is **optional** and the default behaviour will be to send the request to all of the configured routes, but the response from Turing will depend on the Ensembler configuration.
55
{% endhint %}
66

7+
The request sent to the experiment engine is constructed from the request headers to the Router (including an
8+
identifier `Turing-Req-Id` that is uniquely assigned to each request received by the Router), and either the request
9+
body to the Router (if there is no Enricher) or the response body from the Enricher (if enabled).
10+
711
If you intend to determine how to select/combine responses from the individual routes (typically in the Ensembler) to send back as the Turing response, configure the Experiment Engine as shown in Configure Experiment Engine.
812

913
![](../../.gitbook/assets/configure_expriement_engine.png)

docs/how-to/create-a-router/configure-routes.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
Routes are an essential part of your Turing Router setup. Each route is defined by its ID and the endpoint. A route can be your deployed ML model, exposed via HTTP interface, or an arbitrary non-ML web-service. Each router should be configured with at least one route and each route's ID should be unique among other routes of this router.
44

5+
The request sent to the each route is constructed from the request headers to the Router (including an
6+
identifier `Turing-Req-Id` that is uniquely assigned to each request received by the Router), and either the request
7+
body to the Router (if there is no Enricher) or the response body from the Enricher (if enabled).
8+
59
![](../../.gitbook/assets/routes_panel.png)
610

711
{% hint style="info" %}

engines/router/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ go 1.18
55
require (
66
bou.ke/monkey v1.0.2
77
cloud.google.com/go/bigquery v1.14.0
8+
github.com/caraml-dev/turing/engines/experiment v0.0.0
89
github.com/fluent/fluent-logger-golang v1.5.0
910
github.com/go-playground/validator/v10 v10.3.0
1011
github.com/gojek/fiber v0.0.0-20201008181849-4f0f8284dc84
1112
github.com/gojek/mlp v1.4.7
12-
github.com/caraml-dev/turing/engines/experiment v0.0.0
1313
github.com/google/go-cmp v0.5.5
1414
github.com/google/uuid v1.1.2
1515
github.com/heptiolabs/healthcheck v0.0.0-20180807145615-6ff867650f40

engines/router/missionctl/handlers/batch_http_handler.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package handlers
22

33
import (
44
"encoding/json"
5-
"io/ioutil"
5+
"io"
66
"net/http"
77
"strconv"
88
"sync"
@@ -62,7 +62,7 @@ func (h *batchHTTPHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request)
6262
}
6363

6464
// Read the request body
65-
requestBody, err := ioutil.ReadAll(req.Body)
65+
requestBody, err := io.ReadAll(req.Body)
6666
if err != nil {
6767
h.error(ctx, rw, errors.NewHTTPError(err))
6868
return

engines/router/missionctl/handlers/batch_http_handler_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package handlers
22

33
import (
44
"bytes"
5-
"io/ioutil"
5+
"io"
66
"net"
77
"net/http"
88
"net/http/httptest"
@@ -25,7 +25,7 @@ func TestNewBatchHTTPHandler(t *testing.T) {
2525

2626
//Create test routes endpoint. Route will write request body as response
2727
testRouterHandler := http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
28-
requestBody, err := ioutil.ReadAll(request.Body)
28+
requestBody, err := io.ReadAll(request.Body)
2929
assert.NoError(t, err)
3030
_, err = writer.Write(requestBody)
3131
assert.NoError(t, err)
@@ -101,7 +101,7 @@ func TestNewBatchHTTPHandler(t *testing.T) {
101101
batchHTTPHandler.ServeHTTP(w, req)
102102
res := w.Result()
103103
defer res.Body.Close()
104-
result, _ := ioutil.ReadAll(res.Body)
104+
result, _ := io.ReadAll(res.Body)
105105

106106
assert.Equal(t, test.expectedStatusCode, res.StatusCode)
107107
assert.Equal(t, test.expectedContentType, res.Header.Get("Content-Type"))

engines/router/missionctl/handlers/http_handler.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package handlers
33
import (
44
"context"
55
"fmt"
6-
"io/ioutil"
6+
"io"
77
"net/http"
88
"time"
99

@@ -161,6 +161,10 @@ func (h *httpHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
161161
}
162162
ctxLogger.Debugf("Received request for %v", turingReqID)
163163

164+
// Sets the turing request id in the header of the original request so that it gets sent to the enricher,
165+
// the experiment engine, the model routes, and the ensembler
166+
req.Header.Set(turingReqIDHeaderKey, turingReqID)
167+
164168
if tracing.Glob().IsEnabled() {
165169
var sp opentracing.Span
166170
ctx, sp = h.enableTracingSpan(ctx, req, httpHandlerID)
@@ -170,7 +174,7 @@ func (h *httpHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
170174
}
171175

172176
// Read the request body
173-
requestBody, err := ioutil.ReadAll(req.Body)
177+
requestBody, err := io.ReadAll(req.Body)
174178
if err != nil {
175179
h.error(ctx, rw, errors.NewHTTPError(err))
176180
return

engines/router/missionctl/handlers/http_handler_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,8 @@ func TestHTTPService(t *testing.T) {
192192

193193
// Check that ensembler was called with the expected headers
194194
mc.AssertCalled(t, "Ensemble",
195-
http.Header{"Context-Type": []string{"application/json"}, "Enricher": []string{"value"}})
195+
http.Header{"Context-Type": []string{"application/json"}, "Enricher": []string{"value"},
196+
"Turing-Req-Id": []string{rr.Header().Get("Turing-Req-Id")}})
196197
}
197198

198199
// TestHTTPServiceBadRequest tests for a HTTP InternalServerError on bad

engines/router/missionctl/http/response.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package http
22

33
import (
4-
"io/ioutil"
4+
"io"
55
"net/http"
66
)
77

@@ -32,7 +32,7 @@ func (r *CachedResponse) Body() []byte {
3232
// a HTTP response as input
3333
func NewCachedResponseFromHTTP(r *http.Response) (*CachedResponse, error) {
3434
// Consume response body
35-
data, err := ioutil.ReadAll(r.Body)
35+
data, err := io.ReadAll(r.Body)
3636
if err != nil {
3737
return nil, err
3838
}

0 commit comments

Comments
 (0)