Skip to content

Commit 4158912

Browse files
authored
chore(ibis): enable OpenTelemetry and multiple workers for the ibis-server image (#1187)
1 parent 0494727 commit 4158912

File tree

12 files changed

+1743
-1648
lines changed

12 files changed

+1743
-1648
lines changed

ibis-server/Dockerfile

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,13 @@ COPY --from=builder ${VIRTUAL_ENV} ${VIRTUAL_ENV}
6464
COPY app app
6565
COPY resources resources
6666

67+
# Install Opentelemetry zero-instrumentation python
68+
RUN pip install opentelemetry-distro opentelemetry-exporter-otlp \
69+
&& opentelemetry-bootstrap -a install
70+
71+
COPY entrypoint.sh ./
72+
RUN chmod +x ./entrypoint.sh
73+
6774
EXPOSE 8000
6875

69-
CMD ["fastapi", "run"]
76+
CMD ["./entrypoint.sh"]

ibis-server/README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ This module is the API server of Wren Engine. It's built on top of [FastAPI](htt
77
You can follow the steps below to run the Java engine and ibis.
88
> Wren Engine is migrating to [wren-core](../wren-core/). However, we still recommend starting [the Java engine](../wren-core-legacy/) to enable the query fallback mechanism.
99
10-
Create `compose.yaml` file and add the following content, edit environment variables if needed (see [Environment Variables](docs/development#environment-variables))
10+
Create `compose.yaml` file and add the following content, edit environment variables if needed (see [Environment Variables](docs/development#environment-variables)).
1111
```yaml
1212
services:
1313
ibis:
@@ -38,6 +38,9 @@ Run the docker compose
3838
docker compose up
3939
```
4040

41+
Set up [OpenTelemetry Envrionment Variables](docs/development#environment-variable) to enable tracing log.
42+
See [Tracing with Jaeger](#tracing-with-jaeger) to start up a Jaeger Server.
43+
4144
### Running on Local
4245
Requirements:
4346
- Python 3.11
@@ -92,6 +95,11 @@ docker run --rm --name jaeger \
9295
-p 9411:9411 \
9396
jaegertracing/jaeger:2.5.0
9497
```
98+
- Install [OpenTelemetry Python zero-code instrumentation](https://opentelemetry.io/docs/zero-code/python/#setup)
99+
```
100+
pip install opentelemetry-distro opentelemetry-exporter-otlp
101+
opentelemetry-bootstrap -a install
102+
```
95103
- Use the following `just` command to start the `ibis-server` and export tracing logs to Jaeger:
96104
```
97105
just run-trace-otlp

ibis-server/app/dependencies.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ def _filter_headers(header_string: str) -> bool:
2929
return True
3030
elif header_string.startswith("x-user-"):
3131
return True
32+
elif header_string.startswith("x-correlation-id"):
33+
return True
3234
elif header_string == "traceparent":
3335
return True
3436
elif header_string == "tracestate":

ibis-server/app/routers/v2/connector.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,13 @@
2222
from app.model.metadata.factory import MetadataFactory
2323
from app.model.validator import Validator
2424
from app.query_cache import QueryCacheManager
25-
from app.util import build_context, get_fallback_message, pushdown_limit, to_json
25+
from app.util import (
26+
build_context,
27+
get_fallback_message,
28+
pushdown_limit,
29+
set_attribute,
30+
to_json,
31+
)
2632

2733
router = APIRouter(prefix="/connector", tags=["connector"])
2834
tracer = trace.get_tracer(__name__)
@@ -70,6 +76,7 @@ async def query(
7076
with tracer.start_as_current_span(
7177
name=span_name, kind=trace.SpanKind.SERVER, context=build_context(headers)
7278
) as span:
79+
set_attribute(headers, span)
7380
try:
7481
sql = pushdown_limit(dto.sql, limit)
7582
except Exception as e:
@@ -176,7 +183,8 @@ async def validate(
176183
span_name = f"v2_validate_{data_source}"
177184
with tracer.start_as_current_span(
178185
name=span_name, kind=trace.SpanKind.SERVER, context=build_context(headers)
179-
):
186+
) as span:
187+
set_attribute(headers, span)
180188
validator = Validator(
181189
Connector(data_source, dto.connection_info),
182190
Rewriter(
@@ -208,7 +216,8 @@ def get_table_list(
208216
span_name = f"v2_metadata_tables_{data_source}"
209217
with tracer.start_as_current_span(
210218
name=span_name, kind=trace.SpanKind.SERVER, context=build_context(headers)
211-
):
219+
) as span:
220+
set_attribute(headers, span)
212221
return MetadataFactory.get_metadata(
213222
data_source, dto.connection_info
214223
).get_table_list()
@@ -228,7 +237,8 @@ def get_constraints(
228237
span_name = f"v2_metadata_constraints_{data_source}"
229238
with tracer.start_as_current_span(
230239
name=span_name, kind=trace.SpanKind.SERVER, context=build_context(headers)
231-
):
240+
) as span:
241+
set_attribute(headers, span)
232242
return MetadataFactory.get_metadata(
233243
data_source, dto.connection_info
234244
).get_constraints()
@@ -252,7 +262,8 @@ async def dry_plan(
252262
) -> str:
253263
with tracer.start_as_current_span(
254264
name="dry_plan", kind=trace.SpanKind.SERVER, context=build_context(headers)
255-
):
265+
) as span:
266+
set_attribute(headers, span)
256267
sql = await Rewriter(
257268
dto.manifest_str, java_engine_connector=java_engine_connector
258269
).rewrite(dto.sql)
@@ -278,7 +289,8 @@ async def dry_plan_for_data_source(
278289
span_name = f"v2_dry_plan_{data_source}"
279290
with tracer.start_as_current_span(
280291
name=span_name, kind=trace.SpanKind.SERVER, context=build_context(headers)
281-
):
292+
) as span:
293+
set_attribute(headers, span)
282294
sql = await Rewriter(
283295
dto.manifest_str,
284296
data_source=data_source,
@@ -306,7 +318,8 @@ async def model_substitute(
306318
span_name = f"v2_model_substitute_{data_source}"
307319
with tracer.start_as_current_span(
308320
name=span_name, kind=trace.SpanKind.SERVER, context=build_context(headers)
309-
):
321+
) as span:
322+
set_attribute(headers, span)
310323
sql = ModelSubstitute(data_source, dto.manifest_str, headers).substitute(
311324
dto.sql, write="trino"
312325
)

ibis-server/app/routers/v3/connector.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
build_context,
3535
pushdown_limit,
3636
safe_strtobool,
37+
set_attribute,
3738
to_json,
3839
)
3940

@@ -73,6 +74,7 @@ async def query(
7374
with tracer.start_as_current_span(
7475
name=span_name, kind=trace.SpanKind.SERVER, context=build_context(headers)
7576
) as span:
77+
set_attribute(headers, span)
7678
try:
7779
if dry_run:
7880
sql = pushdown_limit(dto.sql, limit)
@@ -188,6 +190,7 @@ async def dry_plan(
188190
with tracer.start_as_current_span(
189191
name="dry_plan", kind=trace.SpanKind.SERVER, context=build_context(headers)
190192
) as span:
193+
set_attribute(headers, span)
191194
try:
192195
return await Rewriter(
193196
dto.manifest_str, experiment=True, properties=dict(headers)
@@ -228,6 +231,7 @@ async def dry_plan_for_data_source(
228231
with tracer.start_as_current_span(
229232
name=span_name, kind=trace.SpanKind.SERVER, context=build_context(headers)
230233
) as span:
234+
set_attribute(headers, span)
231235
try:
232236
return await Rewriter(
233237
dto.manifest_str,
@@ -273,6 +277,7 @@ async def validate(
273277
with tracer.start_as_current_span(
274278
name=span_name, kind=trace.SpanKind.SERVER, context=build_context(headers)
275279
) as span:
280+
set_attribute(headers, span)
276281
try:
277282
validator = Validator(
278283
Connector(data_source, dto.connection_info),
@@ -342,6 +347,7 @@ async def model_substitute(
342347
with tracer.start_as_current_span(
343348
name=span_name, kind=trace.SpanKind.SERVER, context=build_context(headers)
344349
) as span:
350+
set_attribute(headers, span)
345351
try:
346352
sql = ModelSubstitute(data_source, dto.manifest_str, headers).substitute(
347353
dto.sql

ibis-server/app/util.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,16 @@ def build_context(headers: Header) -> Context:
114114
return extract(headers)
115115

116116

117+
def set_attribute(
118+
header: Header,
119+
span: trace.Span,
120+
) -> None:
121+
if header is None:
122+
return
123+
if "X-Correlation-ID" in header:
124+
span.set_attribute("correlation_id", header["X-Correlation-ID"])
125+
126+
117127
def append_fallback_context(headers: Header, span: trace.Span) -> Headers:
118128
if headers is None:
119129
headers = {}

ibis-server/app/worker.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from uvicorn.workers import UvicornWorker
2+
3+
4+
class WrenUvicornWorker(UvicornWorker):
5+
CONFIG_KWARGS = {"loop": "uvloop", "http": "httptools"}

ibis-server/docs/development.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,20 @@ To start the server:
6363
### Environment Variables
6464

6565
- `WREN_ENGINE_ENDPOINT`: The endpoint of the Wren Java engine
66+
- `WREN_NUM_WORKERS`: The number of gunicoron workers
67+
68+
### OpenTelemetry Envrionment Variables
69+
- `OTLP_ENABLED`: Enable the tracing for Ibis Server.
70+
- See more [OpenTelemetry environment variables](https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/).
71+
- The minimize setting:
72+
```
73+
OTLP_ENABLED=true
74+
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://host.docker.internal:4317
75+
OTEL_SERVICE_NAME=wren-engine
76+
OTEL_TRACES_EXPORTER=otlp
77+
OTEL_METRICS_EXPORTER=none
78+
OTEL_LOGS_EXPORTER=none
79+
```
6680

6781
## How to add new data source
6882

ibis-server/entrypoint.sh

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/bin/bash
2+
3+
if [[ -z "${WREN_NUM_WORKERS}" ]]; then
4+
echo "WREN_NUM_WORKERS is not set. Using default value of 2."
5+
WREN_NUM_WORKERS=2
6+
else
7+
WREN_NUM_WORKERS=${WREN_NUM_WORKERS}
8+
fi
9+
10+
echo "Number of workers: ${WREN_NUM_WORKERS}"
11+
12+
# Determine the command prefix based on OTLP_ENABLED
13+
if [[ "${OTLP_ENABLED}" == "true" ]]; then
14+
CMD_PREFIX="opentelemetry-instrument"
15+
else
16+
CMD_PREFIX=""
17+
fi
18+
19+
# Start the WrenUvicornWorker with the specified configuration
20+
${CMD_PREFIX} gunicorn app.main:app --bind 0.0.0.0:8000 \
21+
-k app.worker.WrenUvicornWorker \
22+
--workers ${WREN_NUM_WORKERS} \
23+
--max-requests 1000 \
24+
--max-requests-jitter 100 \
25+
--timeout 300 \
26+
--graceful-timeout 60

ibis-server/justfile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,17 @@ port := "8000"
2323
run:
2424
poetry run fastapi run --port {{ port }}
2525

26+
workers := "2"
27+
28+
run-gunicorn:
29+
gunicorn app.main:app --bind 0.0.0.0:{{ port }} \
30+
-k app.worker.WrenUvicornWorker \
31+
--workers {{ workers }} \
32+
--max-requests 1000 \
33+
--max-requests-jitter 100 \
34+
--timeout 300 \
35+
--graceful-timeout 60
36+
2637
run-trace-console:
2738
opentelemetry-instrument --traces_exporter console --metrics_exporter none fastapi run --port {{ port }}
2839

0 commit comments

Comments
 (0)