diff --git a/.github/workflows/nucliadb_node.yml b/.github/workflows/nucliadb_node.yml index 632ee4df17..976bdf6221 100644 --- a/.github/workflows/nucliadb_node.yml +++ b/.github/workflows/nucliadb_node.yml @@ -58,6 +58,15 @@ env: IMAGE_NAME_NODE_SIDECAR: node_sidecar jobs: + pre-checks-rust: + runs-on: ubuntu-latest + steps: + - name: Checkout the repository + uses: actions/checkout@v3 + + - name: Check system + run: make -C nucliadb_node/ check-system + pre-checks-python: runs-on: ubuntu-latest strategy: @@ -112,6 +121,7 @@ jobs: clippy-rust: name: Clippy lint runs-on: ubuntu-latest + needs: pre-check-rust steps: - uses: actions/checkout@v3 @@ -131,6 +141,7 @@ jobs: prebuild-tests-rust: name: Prebuild rust tests runs-on: ubuntu-latest + needs: pre-check-rust steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/nucliadb_node_release.yml b/.github/workflows/nucliadb_node_release.yml index 83b0ed5cbe..bfa72d21ee 100644 --- a/.github/workflows/nucliadb_node_release.yml +++ b/.github/workflows/nucliadb_node_release.yml @@ -81,6 +81,10 @@ jobs: - run: pip install -U twine 'black>=22.3.0,<23' typing_extensions + - name: Check system deps + if: ${{ matrix.os != 'windows' }} + run: make -C nucliadb check-system + - name: build sdist if: ${{ matrix.os == 'ubuntu' && matrix.target == 'x86_64' && matrix.manylinux == 'auto' }} uses: messense/maturin-action@v1 @@ -91,7 +95,10 @@ jobs: - name: build wheels uses: messense/maturin-action@v1 + env: + RUSTFLAGS: --cfg tokio_unstable with: + before-script-linux: scripts/install-system-deps.sh target: ${{ matrix.target }} manylinux: ${{ matrix.manylinux || 'auto' }} container: ${{ matrix.container }} diff --git a/Cargo.lock b/Cargo.lock index a3e213c8b2..5dc3e6a38f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,6 +62,12 @@ dependencies = [ "libc", ] +[[package]] +name = "antidote" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34fde25430d87a9388dadbe6e34d7f72a462c8b43ac8d309b42b0a8505d7e2a5" + [[package]] name = "anyhow" version = "1.0.75" @@ -888,6 +894,27 @@ dependencies = [ "shared_child", ] +[[package]] +name = "dw" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef0ed82b765c2ab79fb48e4bf2c95bd583202f4078a702bc714cc6e6f3ca80c3" +dependencies = [ + "dw-sys", + "foreign-types 0.5.0", + "libc", +] + +[[package]] +name = "dw-sys" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14eb35c87ff6626cd1021bb32bc7d9a5372ea72547e1eaf0343a841d9d55a973" +dependencies = [ + "libc", + "pkg-config", +] + [[package]] name = "either" version = "1.9.0" @@ -1009,7 +1036,28 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ - "foreign-types-shared", + "foreign-types-shared 0.1.1", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared 0.3.1", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.29", ] [[package]] @@ -1018,6 +1066,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + [[package]] name = "form_urlencoded" version = "1.2.0" @@ -1888,6 +1942,7 @@ dependencies = [ "rand", "regex", "reqwest", + "rstack-self", "sentry", "sentry-tracing", "serde", @@ -2078,7 +2133,7 @@ checksum = "729b745ad4a5575dd06a3e1af1414bd330ee561c01b3899eb584baeaa8def17e" dependencies = [ "bitflags 1.3.2", "cfg-if", - "foreign-types", + "foreign-types 0.3.2", "libc", "once_cell", "openssl-macros", @@ -2739,6 +2794,34 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "rstack" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7df9d3ebd4f17b52e6134efe2fa20021c80688cbe823d481a729a993b730493" +dependencies = [ + "cfg-if", + "dw", + "lazy_static", + "libc", + "log", +] + +[[package]] +name = "rstack-self" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd5030da3aba0ec731502f74ec38e63798eea6bc8b8ba5972129afe3eababd2" +dependencies = [ + "antidote", + "backtrace", + "bincode", + "lazy_static", + "libc", + "rstack", + "serde", +] + [[package]] name = "rust-stemmers" version = "1.2.0" diff --git a/Dockerfile.node b/Dockerfile.node index 71f964aced..3067359940 100644 --- a/Dockerfile.node +++ b/Dockerfile.node @@ -29,6 +29,10 @@ ARG CARGO_FEATURES=release-feature-set ARG CARGO_PROFILE=release ENV RUSTFLAGS="--cfg prometheus_metrics --cfg tokio_unstable" +# Install system dependencies +COPY scripts/install-system-deps.sh /tmp/ +RUN /tmp/install-system-deps.sh && rm /tmp/install-system-deps.sh && rm -rf /var/lib/apt/lists/* + COPY --from=planner /nucliadb/recipe.json recipe.json # Build dependencies (this is the cached docker layer) @@ -54,7 +58,6 @@ COPY nucliadb_texts /nucliadb/nucliadb_texts COPY nucliadb_vectors /nucliadb/nucliadb_vectors COPY vectors_benchmark /nucliadb/vectors_benchmark -# Build application (only run when sources are modified) RUN echo "Building workspace with feature(s) '$CARGO_FEATURES' and profile '$CARGO_PROFILE'" \ && cargo build \ --locked \ @@ -78,6 +81,7 @@ RUN apt-get -y update \ && apt-get -y install ca-certificates curl \ libssl1.1 \ lmdb-utils \ + libdw-dev \ && rm -rf /var/lib/apt/lists/* COPY --from=eu.gcr.io/stashify-218417/basenode:latest /bin/grpc_health_probe /bin/grpc_health_probe diff --git a/Dockerfile.node_local b/Dockerfile.node_local index d1e62eb823..fa0652d000 100644 --- a/Dockerfile.node_local +++ b/Dockerfile.node_local @@ -5,6 +5,10 @@ LABEL org.opencontainers.image.vendor="Nuclia Inc." WORKDIR /nucliadb +# Install system dependencies +COPY scripts/install-system-deps.sh /tmp/ +RUN /tmp/install-system-deps.sh && rm /tmp/install-system-deps.sh + RUN apt-get -y update \ && apt-get -y install ca-certificates \ cmake \ diff --git a/Dockerfile.rust b/Dockerfile.rust index cbe124a9d1..5950b0208d 100644 --- a/Dockerfile.rust +++ b/Dockerfile.rust @@ -25,6 +25,10 @@ RUN set -eux; \ cargo --version; \ rustc --version; +# Install system dependencies +COPY scripts/install-system-deps.sh /tmp/ +RUN /tmp/install-system-deps.sh && rm /tmp/install-system-deps.sh + RUN apt-get update -y && apt-get install --yes --no-install-recommends patchelf cmake && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* RUN pip install maturin diff --git a/Dockerfile.withbinding b/Dockerfile.withbinding index f923c70fc0..7214dfc441 100644 --- a/Dockerfile.withbinding +++ b/Dockerfile.withbinding @@ -26,6 +26,10 @@ RUN set -eux; \ cargo --version; \ rustc --version; +# Install system dependencies +COPY scripts/install-system-deps.sh /tmp/ +RUN /tmp/install-system-deps.sh && rm /tmp/install-system-deps.sh + RUN apt-get update -y && apt-get install --yes --no-install-recommends patchelf cmake && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* RUN pip install maturin diff --git a/nucliadb/Makefile b/nucliadb/Makefile index 58e2f749bc..d3733acd8b 100644 --- a/nucliadb/Makefile +++ b/nucliadb/Makefile @@ -1,5 +1,5 @@ .PHONY: install-dev -install-dev: +install-dev: check-system pip install --upgrade pip wheel cd .. && pip install \ -r test-requirements.txt \ @@ -9,6 +9,10 @@ install-dev: -r nucliadb/requirements-test.txt pip install -e . +.PHONY: check-system +check-system: + ../scripts/install-system-deps.sh + freeze-requirements: # create venv only for freezing requirements python -m venv .freeze-venv diff --git a/nucliadb_node/Cargo.toml b/nucliadb_node/Cargo.toml index 2743daf858..148d475ef0 100644 --- a/nucliadb_node/Cargo.toml +++ b/nucliadb_node/Cargo.toml @@ -68,10 +68,10 @@ rand = "0.8.4" # nucliadb dependencies nucliadb_telemetry = { path = "../nucliadb_telemetry" } nucliadb_core = { path = "../nucliadb_core" } -nucliadb_texts= { path = "../nucliadb_texts" } -nucliadb_paragraphs= { path = "../nucliadb_paragraphs" } -nucliadb_vectors= { path = "../nucliadb_vectors" } -nucliadb_relations= { path = "../nucliadb_relations" } +nucliadb_texts = { path = "../nucliadb_texts" } +nucliadb_paragraphs = { path = "../nucliadb_paragraphs" } +nucliadb_vectors = { path = "../nucliadb_vectors" } +nucliadb_relations = { path = "../nucliadb_relations" } # sentry sdk sentry = "0.26.0" @@ -86,6 +86,9 @@ tracing-log = { version = "0.1.3", features = ["env_logger"] } opentelemetry-zipkin = "0.15.0" sentry-tracing = "0.27.0" +[target.'cfg(all(target_arch = "x86_64", target_os = "linux", not(target_env = "musl")))'.dependencies] +rstack-self = { version = "0.3.0", features = ["dw"], default-features = false } + # indra # indradb-lib = { version = "1", features = ["rocksdb-datastore"] } diff --git a/nucliadb_node/Makefile b/nucliadb_node/Makefile index e68bbce9e1..1aa7ca69ba 100644 --- a/nucliadb_node/Makefile +++ b/nucliadb_node/Makefile @@ -1,5 +1,5 @@ .PHONY: install-dev -install-dev: +install-dev: check-system pip install --upgrade pip wheel cd .. && pip install \ -r test-requirements.txt \ @@ -8,6 +8,11 @@ install-dev: -r nucliadb_node/requirements.txt pip install -e . +.PHONY: check-system +check-system: + ../scripts/install-system-deps.sh + + .PHONY: format format: cd .. && isort --profile black nucliadb_node diff --git a/nucliadb_node/src/bin/reader.rs b/nucliadb_node/src/bin/reader.rs index 0c947d4967..e52d2f731a 100644 --- a/nucliadb_node/src/bin/reader.rs +++ b/nucliadb_node/src/bin/reader.rs @@ -28,7 +28,7 @@ use nucliadb_node::grpc::middleware::{ GrpcDebugLogsLayer, GrpcInstrumentorLayer, GrpcTasksMetricsLayer, }; use nucliadb_node::grpc::reader::NodeReaderGRPCDriver; -use nucliadb_node::http_server::{run_http_metrics_server, MetricsServerOptions}; +use nucliadb_node::http_server::{run_http_server, ServerOptions}; use nucliadb_node::lifecycle; use nucliadb_node::settings::providers::env::EnvSettingsProvider; use nucliadb_node::settings::providers::SettingsProvider; @@ -64,7 +64,7 @@ async fn main() -> NodeResult<()> { grpc_driver, settings.reader_listen_address(), )); - let metrics_task = tokio::spawn(run_http_metrics_server(MetricsServerOptions { + let metrics_task = tokio::spawn(run_http_server(ServerOptions { default_http_port: 3031, })); diff --git a/nucliadb_node/src/bin/writer.rs b/nucliadb_node/src/bin/writer.rs index ae0fd0ed8d..80163fda31 100644 --- a/nucliadb_node/src/bin/writer.rs +++ b/nucliadb_node/src/bin/writer.rs @@ -29,7 +29,7 @@ use nucliadb_node::grpc::middleware::{ GrpcDebugLogsLayer, GrpcInstrumentorLayer, GrpcTasksMetricsLayer, }; use nucliadb_node::grpc::writer::{NodeWriterEvent, NodeWriterGRPCDriver}; -use nucliadb_node::http_server::{run_http_metrics_server, MetricsServerOptions}; +use nucliadb_node::http_server::{run_http_server, ServerOptions}; use nucliadb_node::node_metadata::NodeMetadata; use nucliadb_node::settings::providers::env::EnvSettingsProvider; use nucliadb_node::settings::providers::SettingsProvider; @@ -90,7 +90,7 @@ async fn main() -> NodeResult<()> { None => tokio::spawn(task), }; } - let metrics_task = tokio::spawn(run_http_metrics_server(MetricsServerOptions { + let metrics_task = tokio::spawn(run_http_server(ServerOptions { default_http_port: 3032, })); diff --git a/nucliadb_node/src/http_server/mod.rs b/nucliadb_node/src/http_server/mod.rs index 155b2b00ae..90b9292799 100644 --- a/nucliadb_node/src/http_server/mod.rs +++ b/nucliadb_node/src/http_server/mod.rs @@ -21,6 +21,7 @@ //! HTTP serving utilities mod metrics_service; +mod traces_service; use std::net::SocketAddr; @@ -29,17 +30,17 @@ use axum::Router; use crate::env::metrics_http_port; -pub struct MetricsServerOptions { +pub struct ServerOptions { pub default_http_port: u16, } -pub async fn run_http_metrics_server(options: MetricsServerOptions) { +pub async fn run_http_server(options: ServerOptions) { // Add routes to services let addr = SocketAddr::from(([0, 0, 0, 0], metrics_http_port(options.default_http_port))); - let metrics = Router::new().route("/metrics", get(metrics_service::metrics_service)); + let router = Router::new().route("/metrics", get(metrics_service::metrics_service)); + let router = router.route("/__dump", get(traces_service::thread_dump_service)); axum_server::bind(addr) - // Services will be added here - .serve(metrics.into_make_service()) + .serve(router.into_make_service()) .await .expect("Error starting the HTTP server"); } diff --git a/nucliadb_node/src/http_server/traces_service.rs b/nucliadb_node/src/http_server/traces_service.rs new file mode 100644 index 0000000000..8ccfec5574 --- /dev/null +++ b/nucliadb_node/src/http_server/traces_service.rs @@ -0,0 +1,63 @@ +// Copyright (C) 2021 Bosutech XXI S.L. +// +// nucliadb is offered under the AGPL v3.0 and as commercial software. +// For commercial licensing, contact us at info@nuclia.com. +// +// AGPL: +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// +use axum::response::{Json, IntoResponse, Response}; +use serde::Serialize; + +#[derive(Serialize)] +struct Traces { + trace: String, +} + +#[cfg(all(target_arch = "x86_64", target_os = "linux", not(target_env = "musl")))] +use std::env; + +#[cfg(all(target_arch = "x86_64", target_os = "linux", not(target_env = "musl")))] +use std::process::Command; + +#[cfg(all(target_arch = "x86_64", target_os = "linux", not(target_env = "musl")))] +use rstack_self; + +#[cfg(all(target_arch = "x86_64", target_os = "linux", not(target_env = "musl")))] +pub async fn thread_dump_service() -> Response { + let exe = env::current_exe().unwrap(); + let trace = rstack_self::trace(Command::new(exe).arg("child")); + + match trace { + Ok(res) => Json( + Traces { + trace: format!("{:#?}", res) + } + ).into_response(), + Err(err) => Json( + Traces { + trace: format!("Could not retrieve thread dump {:?}", err) + } + ).into_response(), + } +} + +#[cfg(not(all(target_arch = "x86_64", target_os = "linux", not(target_env = "musl"))))] +pub async fn thread_dump_service() -> Response { + Json( + Traces { + trace: String::from("Not supported on this platform"), + } + ).into_response() +} diff --git a/nucliadb_node_binding/Makefile b/nucliadb_node_binding/Makefile index a2586eec9c..ca7b5a1ce8 100644 --- a/nucliadb_node_binding/Makefile +++ b/nucliadb_node_binding/Makefile @@ -1,7 +1,7 @@ SHELL := /bin/bash .PHONY: install-dev -install-dev: +install-dev: check-system pip install --upgrade pip wheel cd .. && pip install \ -r test-requirements.txt \ @@ -11,6 +11,11 @@ install-dev: ./nucliadb_node/ pip install -e . +.PHONY: check-system +check-system: + ../scripts/install-system-deps.sh + + .PHONY: format format: isort --profile black tests diff --git a/scripts/install-system-deps.sh b/scripts/install-system-deps.sh new file mode 100755 index 0000000000..141bd67686 --- /dev/null +++ b/scripts/install-system-deps.sh @@ -0,0 +1,35 @@ +#!/bin/bash +set -ex + +UNAME="$(uname -s)" +case "${UNAME}" in +Linux*) machine=linux ;; +Darwin*) machine=macos ;; +CYGWIN*) machine=cygwin ;; +MINGW*) machine=mingw ;; +*) machine="UNKNOWN:${UNAME}" ;; +esac + +if [ "$machine" == "linux" ]; then + SUDO='' + if (($EUID != 0)); then + SUDO='sudo' + fi + + source /etc/os-release + case $ID in + debian | ubuntu | mint) + $SUDO apt-get -y update + $SUDO apt-get install -y libdw-dev pkg-config + ;; + + fedora | rhel | centos) + $SUDO yum update + $SUDO yum -y install elfutils-devel pkgconfig + ;; + + *) + echo -n "unsupported linux distro" + ;; + esac +fi