Skip to content
This repository was archived by the owner on Nov 7, 2025. It is now read-only.

Commit 33bae75

Browse files
pdelewskinablaone
andauthored
v2 frontend connector serveHTTP unification (#1115)
This PR is a continuation of v2 production code ServeHTTP and POC ServeHTTP unification. One thing that left is fallback handling --------- Co-authored-by: Rafal Strzalinski <[email protected]>
1 parent 0e2d4b8 commit 33bae75

File tree

5 files changed

+149
-77
lines changed

5 files changed

+149
-77
lines changed

quesma/frontend_connectors/basic_http_frontend_connector.go

Lines changed: 93 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,14 @@ import (
99
"fmt"
1010
"io"
1111
"net/http"
12+
"quesma/clickhouse"
1213
"quesma/logger"
14+
"quesma/quesma/config"
1315
"quesma/quesma/recovery"
16+
"quesma/quesma/types"
17+
"quesma/schema"
1418
quesma_api "quesma_v2/core"
19+
"quesma_v2/core/diag"
1520
"strings"
1621
"sync"
1722
)
@@ -22,11 +27,32 @@ type BasicHTTPFrontendConnector struct {
2227
mutex sync.Mutex
2328
responseMutator func(w http.ResponseWriter) http.ResponseWriter
2429
endpoint string
30+
routerInstance *RouterV2
31+
logManager *clickhouse.LogManager
32+
registry schema.Registry
33+
config *config.QuesmaConfiguration
34+
35+
diagnostic diag.Diagnostic
36+
}
37+
38+
func (h *BasicHTTPFrontendConnector) InjectDiagnostic(diagnostic diag.Diagnostic) {
39+
40+
h.diagnostic = diagnostic
41+
42+
// TODO this is a hack
43+
if h.routerInstance != nil {
44+
h.routerInstance.InjectDiagnostic(diagnostic)
45+
}
2546
}
2647

27-
func NewBasicHTTPFrontendConnector(endpoint string) *BasicHTTPFrontendConnector {
48+
func NewBasicHTTPFrontendConnector(endpoint string, config *config.QuesmaConfiguration) *BasicHTTPFrontendConnector {
49+
2850
return &BasicHTTPFrontendConnector{
29-
endpoint: endpoint,
51+
endpoint: endpoint,
52+
config: config,
53+
routerInstance: NewRouterV2(config),
54+
logManager: nil,
55+
registry: nil,
3056
responseMutator: func(w http.ResponseWriter) http.ResponseWriter {
3157
return w
3258
},
@@ -43,25 +69,40 @@ func (h *BasicHTTPFrontendConnector) GetRouter() quesma_api.Router {
4369

4470
func (h *BasicHTTPFrontendConnector) ServeHTTP(w http.ResponseWriter, req *http.Request) {
4571
defer recovery.LogPanic()
72+
73+
ctx := req.Context()
74+
requestPreprocessors := quesma_api.ProcessorChain{}
75+
requestPreprocessors = append(requestPreprocessors, quesma_api.NewTraceIdPreprocessor())
76+
4677
reqBody, err := PeekBodyV2(req)
4778
if err != nil {
4879
http.Error(w, "Error reading request body", http.StatusInternalServerError)
4980
return
5081
}
51-
ctx := req.Context()
52-
requestPreprocessors := quesma_api.ProcessorChain{}
53-
requestPreprocessors = append(requestPreprocessors, quesma_api.NewTraceIdPreprocessor())
82+
83+
ua := req.Header.Get("User-Agent")
84+
if h.diagnostic.PhoneHomeAgent() != nil {
85+
h.diagnostic.PhoneHomeAgent().UserAgentCounters().Add(ua, 1)
86+
}
5487

5588
quesmaRequest, ctx, err := preprocessRequest(ctx, &quesma_api.Request{
56-
Method: req.Method,
57-
Path: strings.TrimSuffix(req.URL.Path, "/"),
58-
Params: map[string]string{},
59-
Headers: req.Header,
60-
QueryParams: req.URL.Query(),
61-
Body: string(reqBody),
89+
Method: req.Method,
90+
Path: strings.TrimSuffix(req.URL.Path, "/"),
91+
Params: map[string]string{},
92+
Headers: req.Header,
93+
QueryParams: req.URL.Query(),
94+
Body: string(reqBody),
95+
OriginalRequest: req,
6296
}, requestPreprocessors)
6397

98+
if err != nil {
99+
logger.ErrorWithCtx(ctx).Msgf("Error preprocessing request: %v", err)
100+
}
101+
102+
quesmaRequest.ParsedBody = types.ParseRequestBody(quesmaRequest.Body)
103+
64104
handlersPipe, decision := h.router.Matches(quesmaRequest)
105+
65106
if decision != nil {
66107
w.Header().Set(QuesmaTableResolverHeader, decision.String())
67108
} else {
@@ -70,53 +111,53 @@ func (h *BasicHTTPFrontendConnector) ServeHTTP(w http.ResponseWriter, req *http.
70111
dispatcher := &quesma_api.Dispatcher{}
71112
w = h.responseMutator(w)
72113

73-
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
74-
if handlersPipe != nil {
75-
result, _ := handlersPipe.Handler(context.Background(), &quesma_api.Request{OriginalRequest: req})
76-
var quesmaResponse *quesma_api.Result
77-
78-
if result != nil {
79-
metadata, message := dispatcher.Dispatch(handlersPipe.Processors, result.Meta, result.GenericResult)
80-
result = &quesma_api.Result{
81-
Body: result.Body,
82-
Meta: metadata,
83-
StatusCode: result.StatusCode,
84-
GenericResult: message,
85-
}
86-
quesmaResponse = result
114+
if handlersPipe != nil {
115+
quesmaResponse, err := recordRequestToClickhouseV2(req.URL.Path, h.diagnostic.DebugInfoCollector(), func() (*quesma_api.Result, error) {
116+
var result *quesma_api.Result
117+
result, err = handlersPipe.Handler(ctx, quesmaRequest)
118+
119+
if result == nil {
120+
return result, err
87121
}
88-
zip := strings.Contains(req.Header.Get("Accept-Encoding"), "gzip")
89-
_ = zip
90-
if err == nil {
91-
logger.Debug().Ctx(ctx).Msg("responding from quesma")
92-
unzipped := []byte{}
93-
if quesmaResponse != nil {
94-
unzipped = quesmaResponse.GenericResult.([]byte)
95-
}
96-
if len(unzipped) == 0 {
97-
logger.WarnWithCtx(ctx).Msgf("empty response from Clickhouse, method=%s", req.Method)
98-
}
99-
AddProductAndContentHeaders(req.Header, w.Header())
100-
_, err := w.Write(unzipped)
101-
if err != nil {
102-
fmt.Printf("Error writing response: %s\n", err)
103-
}
104-
105-
} else {
122+
metadata, message := dispatcher.Dispatch(handlersPipe.Processors, result.Meta, result.GenericResult)
106123

124+
result = &quesma_api.Result{
125+
Body: result.Body,
126+
Meta: metadata,
127+
StatusCode: result.StatusCode,
128+
GenericResult: message,
107129
}
130+
return result, err
131+
})
132+
133+
zip := strings.Contains(req.Header.Get("Accept-Encoding"), "gzip")
134+
if err == nil {
135+
logger.Debug().Ctx(ctx).Msg("responding from quesma")
136+
unzipped := []byte{}
137+
if quesmaResponse != nil {
138+
unzipped = quesmaResponse.GenericResult.([]byte)
139+
}
140+
if len(unzipped) == 0 {
141+
logger.WarnWithCtx(ctx).Msgf("empty response from Clickhouse, method=%s", req.Method)
142+
}
143+
AddProductAndContentHeaders(req.Header, w.Header())
144+
145+
responseFromQuesmaV2(ctx, unzipped, w, quesmaResponse, zip)
146+
108147
} else {
109-
if h.router.GetFallbackHandler() != nil {
110-
fmt.Printf("No handler found for path: %s\n", req.URL.Path)
111-
handler := h.router.GetFallbackHandler()
112-
result, _ := handler(context.Background(), &quesma_api.Request{OriginalRequest: req})
113-
_, err := w.Write(result.GenericResult.([]byte))
114-
if err != nil {
115-
fmt.Printf("Error writing response: %s\n", err)
116-
}
148+
h.routerInstance.errorResponseV2(ctx, err, w)
149+
}
150+
} else {
151+
if h.router.GetFallbackHandler() != nil {
152+
fmt.Printf("No handler found for path: %s\n", req.URL.Path)
153+
handler := h.router.GetFallbackHandler()
154+
result, _ := handler(context.Background(), &quesma_api.Request{OriginalRequest: req})
155+
_, err := w.Write(result.GenericResult.([]byte))
156+
if err != nil {
157+
fmt.Printf("Error writing response: %s\n", err)
117158
}
118159
}
119-
}).ServeHTTP(w, req)
160+
}
120161
}
121162

122163
func (h *BasicHTTPFrontendConnector) Listen() error {

quesma/frontend_connectors/router_v2.go

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,9 @@ func responseFromQuesmaV2(ctx context.Context, unzipped []byte, w http.ResponseW
5353
logger.Debug().Str(logger.RID, id).Msg("responding from Quesma")
5454

5555
for key, value := range quesmaResponse.Meta {
56-
w.Header().Set(key, value.(string))
56+
if headerStringValue, ok := value.(string); ok {
57+
w.Header().Set(key, headerStringValue)
58+
}
5759
}
5860
if zip {
5961
w.Header().Set("Content-Encoding", "gzip")
@@ -164,7 +166,7 @@ func (*RouterV2) closedIndexResponse(ctx context.Context, w http.ResponseWriter,
164166

165167
}
166168

167-
func (r *RouterV2) elasticFallback(decision *quesma_api.Decision,
169+
func (r *RouterV2) ElasticFallback(decision *quesma_api.Decision,
168170
ctx context.Context, w http.ResponseWriter,
169171
req *http.Request, reqBody []byte, logManager *clickhouse.LogManager, schemaRegistry schema.Registry) {
170172

@@ -238,12 +240,13 @@ func (r *RouterV2) Reroute(ctx context.Context, w http.ResponseWriter, req *http
238240
})
239241

240242
quesmaRequest, ctx, err := preprocessRequest(ctx, &quesma_api.Request{
241-
Method: req.Method,
242-
Path: strings.TrimSuffix(req.URL.Path, "/"),
243-
Params: map[string]string{},
244-
Headers: req.Header,
245-
QueryParams: req.URL.Query(),
246-
Body: string(reqBody),
243+
Method: req.Method,
244+
Path: strings.TrimSuffix(req.URL.Path, "/"),
245+
Params: map[string]string{},
246+
Headers: req.Header,
247+
QueryParams: req.URL.Query(),
248+
Body: string(reqBody),
249+
OriginalRequest: req,
247250
}, r.RequestPreprocessors)
248251

249252
if err != nil {
@@ -298,7 +301,7 @@ func (r *RouterV2) Reroute(ctx context.Context, w http.ResponseWriter, req *http
298301
r.errorResponseV2(ctx, err, w)
299302
}
300303
} else {
301-
r.elasticFallback(decision, ctx, w, req, reqBody, logManager, schemaRegistry)
304+
r.ElasticFallback(decision, ctx, w, req, reqBody, logManager, schemaRegistry)
302305
}
303306
}
304307

@@ -385,7 +388,9 @@ func recordRequestToClickhouseV2(path string, qmc diag.DebugInfoCollector, reque
385388
}
386389
now := time.Now()
387390
response, err := requestFunc()
388-
qmc.RecordRequest(statName, time.Since(now), err != nil)
391+
if qmc != nil {
392+
qmc.RecordRequest(statName, time.Since(now), err != nil)
393+
}
389394
return response, err
390395
}
391396

@@ -396,7 +401,9 @@ func recordRequestToElasticV2(path string, qmc diag.DebugInfoCollector, requestF
396401
}
397402
now := time.Now()
398403
response := requestFunc()
399-
qmc.RecordRequest(statName, time.Since(now), !isResponseOkV2(response.response))
404+
if qmc != nil {
405+
qmc.RecordRequest(statName, time.Since(now), !isResponseOkV2(response.response))
406+
}
400407
return response
401408
}
402409

quesma/main_test.go

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"quesma/backend_connectors"
1212
"quesma/frontend_connectors"
1313
"quesma/processors"
14+
"quesma/quesma/config"
1415
quesma_api "quesma_v2/core"
1516
"sync/atomic"
1617
"syscall"
@@ -65,7 +66,16 @@ func ab_testing_scenario() quesma_api.QuesmaBuilder {
6566
var quesmaBuilder quesma_api.QuesmaBuilder = quesma_api.NewQuesma()
6667
quesmaBuilder.SetDependencies(quesma_api.EmptyDependencies())
6768

68-
ingestFrontendConnector := frontend_connectors.NewBasicHTTPFrontendConnector(":8888")
69+
cfg := &config.QuesmaConfiguration{
70+
DisableAuth: true,
71+
Elasticsearch: config.ElasticsearchConfiguration{
72+
Url: &config.Url{Host: "localhost:9200", Scheme: "http"},
73+
User: "",
74+
Password: "",
75+
},
76+
}
77+
78+
ingestFrontendConnector := frontend_connectors.NewBasicHTTPFrontendConnector(":8888", cfg)
6979
ingestHTTPRouter := quesma_api.NewPathRouter()
7080
ingestHTTPRouter.AddRoute("/_bulk", bulk)
7181
ingestHTTPRouter.AddRoute("/_doc", doc)
@@ -83,7 +93,7 @@ func ab_testing_scenario() quesma_api.QuesmaBuilder {
8393
ingestPipeline.AddProcessor(ingestProcessor)
8494
ingestPipeline.AddProcessor(abIngestTestProcessor)
8595

86-
queryFrontendConnector := frontend_connectors.NewBasicHTTPFrontendConnector(":8888")
96+
queryFrontendConnector := frontend_connectors.NewBasicHTTPFrontendConnector(":8888", cfg)
8797
queryHTTPRouter := quesma_api.NewPathRouter()
8898
queryHTTPRouter.AddRoute("/_search", search)
8999
queryFrontendConnector.AddRouter(queryHTTPRouter)
@@ -108,8 +118,16 @@ func ab_testing_scenario() quesma_api.QuesmaBuilder {
108118
func fallbackScenario() quesma_api.QuesmaBuilder {
109119
var quesmaBuilder quesma_api.QuesmaBuilder = quesma_api.NewQuesma()
110120
quesmaBuilder.SetDependencies(quesma_api.EmptyDependencies())
121+
cfg := &config.QuesmaConfiguration{
122+
DisableAuth: true,
123+
Elasticsearch: config.ElasticsearchConfiguration{
124+
Url: &config.Url{Host: "localhost:9200", Scheme: "http"},
125+
User: "",
126+
Password: "",
127+
},
128+
}
129+
ingestFrontendConnector := frontend_connectors.NewBasicHTTPFrontendConnector(":8888", cfg)
111130

112-
ingestFrontendConnector := frontend_connectors.NewBasicHTTPFrontendConnector(":8888")
113131
ingestHTTPRouter := quesma_api.NewPathRouter()
114132
var fallback quesma_api.HTTPFrontendHandler = fallback
115133
ingestHTTPRouter.AddFallbackHandler(fallback)

quesma/quesma/dual_write_proxy_v2.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,12 @@ func newDualWriteProxyV2(dependencies *quesma_api.Dependencies, schemaLoader cli
9191
searchRouter := ConfigureSearchRouterV2(config, dependencies, registry, logManager, queryProcessor, resolver)
9292

9393
elasticHttpIngestFrontendConnector := NewElasticHttpIngestFrontendConnector(":"+strconv.Itoa(int(config.PublicTcpPort)),
94-
95-
routerInstance, logManager, registry)
94+
logManager, registry, config)
9695
elasticHttpIngestFrontendConnector.AddRouter(ingestRouter)
9796

9897
elasticHttpQueryFrontendConnector := NewElasticHttpQueryFrontendConnector(":"+strconv.Itoa(int(config.PublicTcpPort)),
99-
routerInstance, logManager, registry)
98+
logManager, registry, config)
99+
100100
elasticHttpQueryFrontendConnector.AddRouter(searchRouter)
101101

102102
quesmaBuilder := quesma_api.NewQuesma()

quesma/quesma/elastic_http_frontend_connector.go

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"net/http"
88
"quesma/clickhouse"
99
"quesma/frontend_connectors"
10+
"quesma/quesma/config"
1011
"quesma/quesma/recovery"
1112
"quesma/schema"
1213
quesma_api "quesma_v2/core"
@@ -18,24 +19,29 @@ type ElasticHttpIngestFrontendConnector struct {
1819
routerInstance *frontend_connectors.RouterV2
1920
logManager *clickhouse.LogManager
2021
registry schema.Registry
22+
Config *config.QuesmaConfiguration
2123
diagnostic diag.Diagnostic
2224
}
2325

2426
func NewElasticHttpIngestFrontendConnector(endpoint string,
25-
routerInstance *frontend_connectors.RouterV2,
2627
logManager *clickhouse.LogManager,
27-
registry schema.Registry) *ElasticHttpIngestFrontendConnector {
28+
registry schema.Registry,
29+
config *config.QuesmaConfiguration) *ElasticHttpIngestFrontendConnector {
2830

2931
return &ElasticHttpIngestFrontendConnector{
30-
BasicHTTPFrontendConnector: frontend_connectors.NewBasicHTTPFrontendConnector(endpoint),
31-
routerInstance: routerInstance,
32+
BasicHTTPFrontendConnector: frontend_connectors.NewBasicHTTPFrontendConnector(endpoint, config),
33+
routerInstance: frontend_connectors.NewRouterV2(config),
3234
logManager: logManager,
3335
registry: registry,
3436
}
3537
}
3638

3739
func (h *ElasticHttpIngestFrontendConnector) InjectDiagnostic(diagnostic diag.Diagnostic) {
3840
h.diagnostic = diagnostic
41+
42+
// TODO this is a hack
43+
h.BasicHTTPFrontendConnector.InjectDiagnostic(diagnostic)
44+
h.routerInstance.InjectDiagnostic(diagnostic)
3945
}
4046

4147
func serveHTTPHelper(w http.ResponseWriter, req *http.Request,
@@ -70,13 +76,13 @@ type ElasticHttpQueryFrontendConnector struct {
7076
}
7177

7278
func NewElasticHttpQueryFrontendConnector(endpoint string,
73-
routerInstance *frontend_connectors.RouterV2,
7479
logManager *clickhouse.LogManager,
75-
registry schema.Registry) *ElasticHttpIngestFrontendConnector {
80+
registry schema.Registry,
81+
config *config.QuesmaConfiguration) *ElasticHttpIngestFrontendConnector {
7682

7783
return &ElasticHttpIngestFrontendConnector{
78-
BasicHTTPFrontendConnector: frontend_connectors.NewBasicHTTPFrontendConnector(endpoint),
79-
routerInstance: routerInstance,
84+
BasicHTTPFrontendConnector: frontend_connectors.NewBasicHTTPFrontendConnector(endpoint, config),
85+
routerInstance: frontend_connectors.NewRouterV2(config),
8086
logManager: logManager,
8187
registry: registry,
8288
}

0 commit comments

Comments
 (0)