diff --git a/Cargo.lock b/Cargo.lock index 044e502d9eef5..1b8246066c707 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5370,6 +5370,7 @@ dependencies = [ "clap 3.2.23", "log", "madsim-tokio", + "risingwave_common", "risingwave_compaction_test", "risingwave_compactor", "risingwave_compute", @@ -8359,8 +8360,6 @@ dependencies = [ "stable_deref_trait", "strum", "syn", - "tikv-jemalloc-sys", - "tikv-jemallocator", "time 0.3.15", "tokio", "tokio-stream", diff --git a/docs/memory-profiling.md b/docs/memory-profiling.md index 954db12f64cbc..e65a1185c52ca 100644 --- a/docs/memory-profiling.md +++ b/docs/memory-profiling.md @@ -1,12 +1,14 @@ # Memory (Heap) Profiling Guide +Note that the content below is Linux-exclusive. + ## What is Heap Profile? A heap profiler records the **stack trace of the allocation** of each **live** object, so it’s possible that function A allocates something and then hand over it to struct B, in this case, the allocation will still be counted on A. ## Internals -RisingWave uses [tikv-jemallocator](https://crates.io/crates/tikv-jemallocator), which is a Rust wrapper of [jemalloc](https://github.com/jemalloc/jemalloc), as its memory allocator. +RisingWave uses [tikv-jemallocator](https://crates.io/crates/tikv-jemallocator) on Linux, which is a Rust wrapper of [jemalloc](https://github.com/jemalloc/jemalloc), as its memory allocator. On other platforms, RisingWave uses the default allocator. Luckily, jemalloc provides built-in profiling support ([official wiki](https://github.com/jemalloc/jemalloc/wiki/Use-Case%3A-Heap-Profiling)). jemallocator exposes the feature via a cargo feature ‘profiling’. [Here](https://gist.github.com/ordian/928dc2bd45022cddd547528f64db9174) is a simple guide to profiling with jemallocator. diff --git a/src/batch/Cargo.toml b/src/batch/Cargo.toml index 6ac856bac8045..7d6683db41f67 100644 --- a/src/batch/Cargo.toml +++ b/src/batch/Cargo.toml @@ -71,6 +71,8 @@ workspace-hack = { path = "../workspace-hack" } criterion = { version = "0.4", features = ["async_tokio", "async"] } rand = "0.8" tempfile = "3" + +[target.'cfg(target_os = "linux")'.dev-dependencies] tikv-jemallocator = "0.5" [[bench]] diff --git a/src/batch/benches/expand.rs b/src/batch/benches/expand.rs index 52cb069f48e07..af0a5abea8fda 100644 --- a/src/batch/benches/expand.rs +++ b/src/batch/benches/expand.rs @@ -15,13 +15,12 @@ pub mod utils; use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion}; use risingwave_batch::executor::{BoxedExecutor, ExpandExecutor}; +use risingwave_common::enable_jemalloc_on_linux; use risingwave_common::types::DataType; -use tikv_jemallocator::Jemalloc; use tokio::runtime::Runtime; use utils::{create_input, execute_executor}; -#[global_allocator] -static GLOBAL: Jemalloc = Jemalloc; +enable_jemalloc_on_linux!(); fn create_expand_executor( column_subsets: Vec>, diff --git a/src/batch/benches/filter.rs b/src/batch/benches/filter.rs index 0fd516db7dab4..67ea2032f3922 100644 --- a/src/batch/benches/filter.rs +++ b/src/batch/benches/filter.rs @@ -16,6 +16,7 @@ pub mod utils; use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion}; use risingwave_batch::executor::{BoxedExecutor, FilterExecutor}; +use risingwave_common::enable_jemalloc_on_linux; use risingwave_common::types::{DataType, ScalarImpl}; use risingwave_common::util::value_encoding::serialize_datum; use risingwave_expr::expr::build_from_prost; @@ -26,12 +27,10 @@ use risingwave_pb::expr::expr_node::Type::{ ConstantValue as TConstValue, Equal, InputRef, Modulus, }; use risingwave_pb::expr::{ExprNode, FunctionCall, InputRefExpr}; -use tikv_jemallocator::Jemalloc; use tokio::runtime::Runtime; use utils::{create_input, execute_executor}; -#[global_allocator] -static GLOBAL: Jemalloc = Jemalloc; +enable_jemalloc_on_linux!(); fn create_filter_executor(chunk_size: usize, chunk_num: usize) -> BoxedExecutor { const CHUNK_SIZE: usize = 1024; diff --git a/src/batch/benches/hash_agg.rs b/src/batch/benches/hash_agg.rs index 483e27bec9613..8d307ba1e4cac 100644 --- a/src/batch/benches/hash_agg.rs +++ b/src/batch/benches/hash_agg.rs @@ -17,18 +17,16 @@ use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criteri use itertools::Itertools; use risingwave_batch::executor::{BoxedExecutor, HashAggExecutor}; use risingwave_common::catalog::{Field, Schema}; -use risingwave_common::hash; use risingwave_common::types::DataType; +use risingwave_common::{enable_jemalloc_on_linux, hash}; use risingwave_expr::expr::AggKind; use risingwave_expr::vector_op::agg::AggStateFactory; use risingwave_pb::expr::agg_call::Arg; use risingwave_pb::expr::{AggCall, InputRefExpr}; -use tikv_jemallocator::Jemalloc; use tokio::runtime::Runtime; use utils::{create_input, execute_executor}; -#[global_allocator] -static GLOBAL: Jemalloc = Jemalloc; +enable_jemalloc_on_linux!(); fn create_agg_call( input_schema: &Schema, diff --git a/src/batch/benches/hash_join.rs b/src/batch/benches/hash_join.rs index c5dc62cc42604..a03641130fd40 100644 --- a/src/batch/benches/hash_join.rs +++ b/src/batch/benches/hash_join.rs @@ -19,9 +19,9 @@ use risingwave_batch::executor::hash_join::HashJoinExecutor; use risingwave_batch::executor::test_utils::{gen_projected_data, MockExecutor}; use risingwave_batch::executor::{BoxedExecutor, JoinType}; use risingwave_common::catalog::schema_test_utils::field_n; -use risingwave_common::hash; use risingwave_common::types::{DataType, ScalarImpl}; use risingwave_common::util::value_encoding::serialize_datum; +use risingwave_common::{enable_jemalloc_on_linux, hash}; use risingwave_expr::expr::build_from_prost; use risingwave_pb::data::data_type::TypeName; use risingwave_pb::data::Datum as ProstDatum; @@ -30,11 +30,9 @@ use risingwave_pb::expr::expr_node::Type::{ ConstantValue as TConstValue, GreaterThan, InputRef, Modulus, }; use risingwave_pb::expr::{ExprNode, FunctionCall, InputRefExpr}; -use tikv_jemallocator::Jemalloc; use utils::bench_join; -#[global_allocator] -static GLOBAL: Jemalloc = Jemalloc; +enable_jemalloc_on_linux!(); fn create_hash_join_executor( join_type: JoinType, diff --git a/src/batch/benches/limit.rs b/src/batch/benches/limit.rs index ed94ef0d489f9..c3a16add5a2d4 100644 --- a/src/batch/benches/limit.rs +++ b/src/batch/benches/limit.rs @@ -16,13 +16,12 @@ pub mod utils; use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion}; use risingwave_batch::executor::{BoxedExecutor, LimitExecutor}; +use risingwave_common::enable_jemalloc_on_linux; use risingwave_common::types::DataType; -use tikv_jemallocator::Jemalloc; use tokio::runtime::Runtime; use utils::{create_input, execute_executor}; -#[global_allocator] -static GLOBAL: Jemalloc = Jemalloc; +enable_jemalloc_on_linux!(); fn create_limit_executor( chunk_size: usize, diff --git a/src/batch/benches/nested_loop_join.rs b/src/batch/benches/nested_loop_join.rs index 7000bcf468d24..9bf833c1d65db 100644 --- a/src/batch/benches/nested_loop_join.rs +++ b/src/batch/benches/nested_loop_join.rs @@ -15,6 +15,7 @@ pub mod utils; use criterion::{criterion_group, criterion_main, Criterion}; use risingwave_batch::executor::{BoxedExecutor, JoinType, NestedLoopJoinExecutor}; +use risingwave_common::enable_jemalloc_on_linux; use risingwave_common::types::{DataType, ScalarImpl}; use risingwave_common::util::value_encoding::serialize_datum; use risingwave_expr::expr::build_from_prost; @@ -25,11 +26,9 @@ use risingwave_pb::expr::expr_node::Type::{ ConstantValue as TConstValue, Equal, InputRef, Modulus, }; use risingwave_pb::expr::{ExprNode, FunctionCall, InputRefExpr}; -use tikv_jemallocator::Jemalloc; use utils::{bench_join, create_input}; -#[global_allocator] -static GLOBAL: Jemalloc = Jemalloc; +enable_jemalloc_on_linux!(); fn create_nested_loop_join_executor( join_type: JoinType, diff --git a/src/batch/benches/sort.rs b/src/batch/benches/sort.rs index a00326a80dee4..7665473e1c7dd 100644 --- a/src/batch/benches/sort.rs +++ b/src/batch/benches/sort.rs @@ -16,14 +16,13 @@ pub mod utils; use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion}; use risingwave_batch::executor::{BoxedExecutor, SortExecutor}; +use risingwave_common::enable_jemalloc_on_linux; use risingwave_common::types::DataType; use risingwave_common::util::sort_util::{OrderPair, OrderType}; -use tikv_jemallocator::Jemalloc; use tokio::runtime::Runtime; use utils::{create_input, execute_executor}; -#[global_allocator] -static GLOBAL: Jemalloc = Jemalloc; +enable_jemalloc_on_linux!(); fn create_order_by_executor( chunk_size: usize, diff --git a/src/batch/benches/sort_merge_join.rs b/src/batch/benches/sort_merge_join.rs index 50d771f37a034..94b2c0b0bc26c 100644 --- a/src/batch/benches/sort_merge_join.rs +++ b/src/batch/benches/sort_merge_join.rs @@ -18,13 +18,12 @@ use criterion::{criterion_group, criterion_main, Criterion}; use risingwave_batch::executor::test_utils::{gen_sorted_data, MockExecutor}; use risingwave_batch::executor::{BoxedExecutor, JoinType, SortMergeJoinExecutor}; use risingwave_common::catalog::schema_test_utils::field_n; +use risingwave_common::enable_jemalloc_on_linux; use risingwave_common::types::DataType; use risingwave_common::util::sort_util::OrderType; -use tikv_jemallocator::Jemalloc; use utils::bench_join; -#[global_allocator] -static GLOBAL: Jemalloc = Jemalloc; +enable_jemalloc_on_linux!(); fn create_sort_merge_join_executor( join_type: JoinType, diff --git a/src/batch/benches/top_n.rs b/src/batch/benches/top_n.rs index b2f842979f4e5..977b8b3788f3b 100644 --- a/src/batch/benches/top_n.rs +++ b/src/batch/benches/top_n.rs @@ -16,14 +16,13 @@ pub mod utils; use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion}; use risingwave_batch::executor::{BoxedExecutor, TopNExecutor}; +use risingwave_common::enable_jemalloc_on_linux; use risingwave_common::types::DataType; use risingwave_common::util::sort_util::{OrderPair, OrderType}; -use tikv_jemallocator::Jemalloc; use tokio::runtime::Runtime; use utils::{create_input, execute_executor}; -#[global_allocator] -static GLOBAL: Jemalloc = Jemalloc; +enable_jemalloc_on_linux!(); fn create_top_n_executor( chunk_size: usize, diff --git a/src/cmd/Cargo.toml b/src/cmd/Cargo.toml index 6444171ad14aa..e7eff00e37130 100644 --- a/src/cmd/Cargo.toml +++ b/src/cmd/Cargo.toml @@ -15,6 +15,7 @@ static-log-level = ["workspace-config/enable-static-log-level"] anyhow = "1" clap = { version = "3", features = ["derive"] } log = { version = "0.4" } +risingwave_common = { path = "../common" } risingwave_compaction_test = { path = "../tests/compaction_test" } risingwave_compactor = { path = "../storage/compactor" } risingwave_compute = { path = "../compute" } @@ -23,7 +24,6 @@ risingwave_frontend = { path = "../frontend" } risingwave_meta = { path = "../meta" } risingwave_rt = { path = "../utils/runtime" } task_stats_alloc = { path = "../utils/task_stats_alloc" } -tikv-jemallocator = { version = "0.5", features = ["profiling", "stats"] } tokio = { version = "0.2", package = "madsim-tokio", features = [ "rt", "rt-multi-thread", @@ -39,6 +39,9 @@ tracing = { version = "0.1" } workspace-config = { path = "../utils/workspace-config", optional = true } workspace-hack = { path = "../workspace-hack" } +[target.'cfg(target_os = "linux")'.dependencies] +tikv-jemallocator = { version = "0.5", features = ["profiling", "stats"] } + [[bin]] name = "frontend" path = "src/bin/frontend_node.rs" diff --git a/src/cmd/src/bin/compactor.rs b/src/cmd/src/bin/compactor.rs index 197026b086dbe..0d4eb83749042 100644 --- a/src/cmd/src/bin/compactor.rs +++ b/src/cmd/src/bin/compactor.rs @@ -14,10 +14,9 @@ #![cfg_attr(coverage, feature(no_coverage))] -use tikv_jemallocator::Jemalloc; +use risingwave_common::enable_jemalloc_on_linux; -#[global_allocator] -static GLOBAL: Jemalloc = Jemalloc; +enable_jemalloc_on_linux!(); #[cfg_attr(coverage, no_coverage)] fn main() { diff --git a/src/cmd/src/bin/compute_node.rs b/src/cmd/src/bin/compute_node.rs index 883fc6b947c07..49309d563e21d 100644 --- a/src/cmd/src/bin/compute_node.rs +++ b/src/cmd/src/bin/compute_node.rs @@ -14,11 +14,9 @@ #![cfg_attr(coverage, feature(no_coverage))] -use task_stats_alloc::TaskLocalAlloc; -use tikv_jemallocator::Jemalloc; +use risingwave_common::enable_task_local_jemalloc_on_linux; -#[global_allocator] -static GLOBAL: TaskLocalAlloc = TaskLocalAlloc(Jemalloc); +enable_task_local_jemalloc_on_linux!(); #[cfg_attr(coverage, no_coverage)] fn main() { diff --git a/src/cmd/src/bin/ctl.rs b/src/cmd/src/bin/ctl.rs index 1b8dcd3af837f..2ed47be73bca0 100644 --- a/src/cmd/src/bin/ctl.rs +++ b/src/cmd/src/bin/ctl.rs @@ -15,10 +15,9 @@ #![cfg_attr(coverage, feature(no_coverage))] use anyhow::Result; -use tikv_jemallocator::Jemalloc; +use risingwave_common::enable_jemalloc_on_linux; -#[global_allocator] -static GLOBAL: Jemalloc = Jemalloc; +enable_jemalloc_on_linux!(); #[cfg_attr(coverage, no_coverage)] fn main() -> Result<()> { diff --git a/src/cmd/src/bin/frontend_node.rs b/src/cmd/src/bin/frontend_node.rs index 000ba7380674d..d130c0a7f4b57 100644 --- a/src/cmd/src/bin/frontend_node.rs +++ b/src/cmd/src/bin/frontend_node.rs @@ -14,10 +14,9 @@ #![cfg_attr(coverage, feature(no_coverage))] -use tikv_jemallocator::Jemalloc; +use risingwave_common::enable_jemalloc_on_linux; -#[global_allocator] -static GLOBAL: Jemalloc = Jemalloc; +enable_jemalloc_on_linux!(); #[cfg_attr(coverage, no_coverage)] fn main() { diff --git a/src/cmd/src/bin/meta_node.rs b/src/cmd/src/bin/meta_node.rs index 85c9a8a43e588..8c71639bfe31e 100644 --- a/src/cmd/src/bin/meta_node.rs +++ b/src/cmd/src/bin/meta_node.rs @@ -14,10 +14,9 @@ #![cfg_attr(coverage, feature(no_coverage))] -use tikv_jemallocator::Jemalloc; +use risingwave_common::enable_jemalloc_on_linux; -#[global_allocator] -static GLOBAL: Jemalloc = Jemalloc; +enable_jemalloc_on_linux!(); #[cfg_attr(coverage, no_coverage)] fn main() { diff --git a/src/cmd_all/src/bin/risingwave.rs b/src/cmd_all/src/bin/risingwave.rs index 390d5730fdaf8..6fd7627abd89d 100644 --- a/src/cmd_all/src/bin/risingwave.rs +++ b/src/cmd_all/src/bin/risingwave.rs @@ -15,18 +15,15 @@ #![cfg_attr(coverage, feature(no_coverage))] #![feature(let_chains)] -use task_stats_alloc::TaskLocalAlloc; -use tikv_jemallocator::Jemalloc; - -#[global_allocator] -static GLOBAL: TaskLocalAlloc = TaskLocalAlloc(Jemalloc); - use std::collections::HashMap; use std::env; use anyhow::{bail, Result}; use clap::StructOpt; use risingwave_cmd_all::playground; +use risingwave_common::enable_task_local_jemalloc_on_linux; + +enable_task_local_jemalloc_on_linux!(); type RwFns = HashMap<&'static str, Box) -> Result<()>>>; diff --git a/src/common/src/jemalloc.rs b/src/common/src/jemalloc.rs new file mode 100644 index 0000000000000..22be75c3d82cc --- /dev/null +++ b/src/common/src/jemalloc.rs @@ -0,0 +1,33 @@ +// Copyright 2023 Singularity Data +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// If is resolved, we may inline this +#[macro_export] +macro_rules! enable_jemalloc_on_linux { + () => { + #[cfg(target_os = "linux")] + #[global_allocator] + static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; + }; +} + +#[macro_export] +macro_rules! enable_task_local_jemalloc_on_linux { + () => { + #[cfg(target_os = "linux")] + #[global_allocator] + static GLOBAL: task_stats_alloc::TaskLocalAlloc = + task_stats_alloc::TaskLocalAlloc(tikv_jemallocator::Jemalloc); + }; +} diff --git a/src/common/src/lib.rs b/src/common/src/lib.rs index 80ce2ed4bc127..25d4334daac02 100644 --- a/src/common/src/lib.rs +++ b/src/common/src/lib.rs @@ -34,6 +34,8 @@ #![feature(array_chunks)] #![allow(incomplete_features)] +#[macro_use] +pub mod jemalloc; #[macro_use] pub mod error; #[macro_use] diff --git a/src/compute/Cargo.toml b/src/compute/Cargo.toml index aec0aa8833987..9731fb96ab248 100644 --- a/src/compute/Cargo.toml +++ b/src/compute/Cargo.toml @@ -53,7 +53,6 @@ smallvec = "1" static_assertions = "1" sysinfo = "0.26" thiserror = "1" -tikv-jemalloc-ctl = "0.5" tokio = { version = "0.2", package = "madsim-tokio", features = [ "rt", "rt-multi-thread", @@ -71,6 +70,9 @@ tracing = "0.1" twox-hash = "1" url = "2" +[target.'cfg(target_os = "linux")'.dependencies] +tikv-jemalloc-ctl = "0.5" + [target.'cfg(not(madsim))'.dependencies] workspace-hack = { path = "../workspace-hack" } diff --git a/src/compute/src/memory_management/memory_manager.rs b/src/compute/src/memory_management/memory_manager.rs index 63fb51605cc37..e13f9eff89095 100644 --- a/src/compute/src/memory_management/memory_manager.rs +++ b/src/compute/src/memory_management/memory_manager.rs @@ -20,6 +20,7 @@ use risingwave_batch::task::BatchManager; use risingwave_common::util::epoch::Epoch; use risingwave_stream::executor::monitor::StreamingMetrics; use risingwave_stream::task::LocalStreamManager; +#[cfg(target_os = "linux")] use tikv_jemalloc_ctl::{epoch as jemalloc_epoch, stats as jemalloc_stats}; use tracing; @@ -68,9 +69,16 @@ impl GlobalMemoryManager { watermark_epoch.store(epoch, Ordering::Relaxed); } + /// Jemalloc is not supported on non-Linux OSs, because tikv-jemalloc is not available. + /// See the comments for the macro enable_jemalloc_on_linux!(); + // FIXME: remove such limitation after #7180 + #[cfg(not(target_os = "linux"))] + pub async fn run(self: Arc, _: Arc, _: Arc) {} + /// Memory manager will get memory usage from batch and streaming, and do some actions. /// 1. if batch exceeds, kill running query. /// 2. if streaming exceeds, evict cache by watermark. + #[cfg(target_os = "linux")] pub async fn run( self: Arc, _batch_mgr: Arc, diff --git a/src/stream/Cargo.toml b/src/stream/Cargo.toml index 15940d4c69c28..4bd76915b713e 100644 --- a/src/stream/Cargo.toml +++ b/src/stream/Cargo.toml @@ -59,7 +59,6 @@ smallvec = "1" static_assertions = "1" task_stats_alloc = { path = "../utils/task_stats_alloc" } thiserror = "1" -tikv-jemalloc-ctl = "0.5" tokio = { version = "0.2", package = "madsim-tokio", features = [ "rt", "rt-multi-thread", @@ -78,6 +77,9 @@ tracing-futures = "0.2" twox-hash = "1" url = "2" +[target.'cfg(target_os = "linux")'.dependencies] +tikv-jemalloc-ctl = "0.5" + [target.'cfg(not(madsim))'.dependencies] workspace-hack = { path = "../workspace-hack" } diff --git a/src/workspace-hack/Cargo.toml b/src/workspace-hack/Cargo.toml index 1cfc9f801fd94..6a90a9e5357fa 100644 --- a/src/workspace-hack/Cargo.toml +++ b/src/workspace-hack/Cargo.toml @@ -84,8 +84,6 @@ smallvec = { version = "1", default-features = false, features = ["serde", "unio socket2 = { version = "0.4", default-features = false, features = ["all"] } stable_deref_trait = { version = "1", features = ["alloc", "std"] } strum = { version = "0.24", features = ["derive", "std", "strum_macros"] } -tikv-jemalloc-sys = { version = "0.5", features = ["background_threads_runtime_support", "profiling", "stats"] } -tikv-jemallocator = { version = "0.5", features = ["background_threads_runtime_support", "profiling", "stats"] } time = { version = "0.3", features = ["alloc", "formatting", "itoa", "local-offset", "macros", "parsing", "std", "time-macros"] } tokio = { version = "1", features = ["bytes", "fs", "io-std", "io-util", "libc", "macros", "memchr", "mio", "net", "num_cpus", "parking_lot", "process", "rt", "rt-multi-thread", "signal", "signal-hook-registry", "socket2", "stats", "sync", "time", "tokio-macros", "tracing"] } tokio-stream = { version = "0.1", features = ["net", "time"] } @@ -170,8 +168,6 @@ socket2 = { version = "0.4", default-features = false, features = ["all"] } stable_deref_trait = { version = "1", features = ["alloc", "std"] } strum = { version = "0.24", features = ["derive", "std", "strum_macros"] } syn = { version = "1", features = ["clone-impls", "derive", "extra-traits", "full", "parsing", "printing", "proc-macro", "quote", "visit", "visit-mut"] } -tikv-jemalloc-sys = { version = "0.5", features = ["background_threads_runtime_support", "profiling", "stats"] } -tikv-jemallocator = { version = "0.5", features = ["background_threads_runtime_support", "profiling", "stats"] } time = { version = "0.3", features = ["alloc", "formatting", "itoa", "local-offset", "macros", "parsing", "std", "time-macros"] } tokio = { version = "1", features = ["bytes", "fs", "io-std", "io-util", "libc", "macros", "memchr", "mio", "net", "num_cpus", "parking_lot", "process", "rt", "rt-multi-thread", "signal", "signal-hook-registry", "socket2", "stats", "sync", "time", "tokio-macros", "tracing"] } tokio-stream = { version = "0.1", features = ["net", "time"] }