mirror of
https://github.com/GreptimeTeam/greptimedb.git
synced 2026-01-06 05:12:54 +00:00
Compare commits
97 Commits
feat/query
...
v0.15.4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5fc0c5706c | ||
|
|
4d768b2c31 | ||
|
|
b62f219810 | ||
|
|
5d330fad17 | ||
|
|
dfdfae1a7b | ||
|
|
822f0caf4b | ||
|
|
09f3d72d2d | ||
|
|
ca0c1282ed | ||
|
|
b719c020ba | ||
|
|
717c1d1807 | ||
|
|
291f3c89fe | ||
|
|
602cc38056 | ||
|
|
46b3593021 | ||
|
|
ff402fd6f6 | ||
|
|
b83e6e2b18 | ||
|
|
cb74337dbe | ||
|
|
32bffbb668 | ||
|
|
941906dc74 | ||
|
|
cbf251d0f0 | ||
|
|
1519379262 | ||
|
|
4bfe02ec7f | ||
|
|
ecacf1333e | ||
|
|
92fa33c250 | ||
|
|
8b2d1a3753 | ||
|
|
13401c94e0 | ||
|
|
fd637dae47 | ||
|
|
69fac19770 | ||
|
|
6435b97314 | ||
|
|
726e3909fe | ||
|
|
00d759e828 | ||
|
|
0042ea6462 | ||
|
|
d06450715f | ||
|
|
8612bb066f | ||
|
|
467593d329 | ||
|
|
9e4ae070b2 | ||
|
|
d8261dda51 | ||
|
|
7ab9b335a1 | ||
|
|
60835afb47 | ||
|
|
aba5bf7431 | ||
|
|
7897fe8dbe | ||
|
|
cc8ec706a1 | ||
|
|
7c688718db | ||
|
|
8a0e554e5a | ||
|
|
80fae1c559 | ||
|
|
c37c4df20d | ||
|
|
f712c1b356 | ||
|
|
7cd6be41ce | ||
|
|
15616d0c43 | ||
|
|
b43e315c67 | ||
|
|
36ab1ceef7 | ||
|
|
3fb1b726c6 | ||
|
|
c423bb31fe | ||
|
|
e026f766d2 | ||
|
|
9d08f2532a | ||
|
|
e072726ea8 | ||
|
|
e78c3e1eaa | ||
|
|
89e3c8edab | ||
|
|
d4826b998d | ||
|
|
d9faa5c801 | ||
|
|
12c3a3205b | ||
|
|
5231505021 | ||
|
|
6ece560f8c | ||
|
|
2ab08a8f93 | ||
|
|
086ae9cdcd | ||
|
|
6da8e00243 | ||
|
|
4b04c402b6 | ||
|
|
a59b6c36d2 | ||
|
|
f6ce6fe385 | ||
|
|
4d4bfb7d8b | ||
|
|
6e1e8f19e6 | ||
|
|
49cb4da6d2 | ||
|
|
0d0236ddab | ||
|
|
f8edb53b30 | ||
|
|
438791b3e4 | ||
|
|
50e4c916e7 | ||
|
|
16e7f7b64b | ||
|
|
53c4fd478e | ||
|
|
ecbbd2fbdb | ||
|
|
3e3a12385c | ||
|
|
079daf5db9 | ||
|
|
56b9ab5279 | ||
|
|
be4e0d589e | ||
|
|
2a3445c72c | ||
|
|
9d997d593c | ||
|
|
10bf9b11f6 | ||
|
|
f4f8d65a39 | ||
|
|
b31990e881 | ||
|
|
6da633e70d | ||
|
|
9633e794c7 | ||
|
|
eaf1e1198f | ||
|
|
505bf25505 | ||
|
|
f1b29ece3c | ||
|
|
74df12e8c0 | ||
|
|
be6a5d2da8 | ||
|
|
7468a8ab2a | ||
|
|
5bb0466ff2 | ||
|
|
f6db419afd |
@@ -12,3 +12,6 @@ fetch = true
|
|||||||
checkout = true
|
checkout = true
|
||||||
list_files = true
|
list_files = true
|
||||||
internal_use_git2 = false
|
internal_use_git2 = false
|
||||||
|
|
||||||
|
[env]
|
||||||
|
CARGO_WORKSPACE_DIR = { value = "", relative = true }
|
||||||
|
|||||||
15
.github/labeler.yaml
vendored
Normal file
15
.github/labeler.yaml
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
ci:
|
||||||
|
- changed-files:
|
||||||
|
- any-glob-to-any-file: .github/**
|
||||||
|
|
||||||
|
docker:
|
||||||
|
- changed-files:
|
||||||
|
- any-glob-to-any-file: docker/**
|
||||||
|
|
||||||
|
documentation:
|
||||||
|
- changed-files:
|
||||||
|
- any-glob-to-any-file: docs/**
|
||||||
|
|
||||||
|
dashboard:
|
||||||
|
- changed-files:
|
||||||
|
- any-glob-to-any-file: grafana/**
|
||||||
42
.github/workflows/pr-labeling.yaml
vendored
Normal file
42
.github/workflows/pr-labeling.yaml
vendored
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
name: 'PR Labeling'
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
- synchronize
|
||||||
|
- reopened
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
issues: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
labeler:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout sources
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: actions/labeler@v5
|
||||||
|
with:
|
||||||
|
configuration-path: ".github/labeler.yaml"
|
||||||
|
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
|
|
||||||
|
size-label:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: pascalgn/size-label-action@v0.5.5
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
|
with:
|
||||||
|
sizes: >
|
||||||
|
{
|
||||||
|
"0": "XS",
|
||||||
|
"100": "S",
|
||||||
|
"300": "M",
|
||||||
|
"1000": "L",
|
||||||
|
"1500": "XL",
|
||||||
|
"2000": "XXL"
|
||||||
|
}
|
||||||
222
Cargo.lock
generated
222
Cargo.lock
generated
@@ -211,7 +211,7 @@ checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "api"
|
name = "api"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"common-base",
|
"common-base",
|
||||||
"common-decimal",
|
"common-decimal",
|
||||||
@@ -944,7 +944,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "auth"
|
name = "auth"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"api",
|
"api",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -1586,7 +1586,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cache"
|
name = "cache"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"catalog",
|
"catalog",
|
||||||
"common-error",
|
"common-error",
|
||||||
@@ -1602,6 +1602,17 @@ version = "1.0.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "acbc26382d871df4b7442e3df10a9402bf3cf5e55cbd66f12be38861425f0564"
|
checksum = "acbc26382d871df4b7442e3df10a9402bf3cf5e55cbd66f12be38861425f0564"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cargo-manifest"
|
||||||
|
version = "0.19.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1d8af896b707212cd0e99c112a78c9497dd32994192a463ed2f7419d29bd8c6"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"thiserror 2.0.12",
|
||||||
|
"toml 0.8.19",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cast"
|
name = "cast"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
@@ -1610,7 +1621,7 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "catalog"
|
name = "catalog"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"api",
|
"api",
|
||||||
"arrow 54.2.1",
|
"arrow 54.2.1",
|
||||||
@@ -1621,6 +1632,7 @@ dependencies = [
|
|||||||
"cache",
|
"cache",
|
||||||
"catalog",
|
"catalog",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
"common-base",
|
||||||
"common-catalog",
|
"common-catalog",
|
||||||
"common-error",
|
"common-error",
|
||||||
"common-frontend",
|
"common-frontend",
|
||||||
@@ -1669,9 +1681,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.1.24"
|
version = "1.2.27"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "812acba72f0a070b003d3697490d2b55b837230ae7c6c6497f05cc2ddbb8d938"
|
checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jobserver",
|
"jobserver",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -1947,8 +1959,9 @@ checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cli"
|
name = "cli"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"async-stream",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"auth",
|
"auth",
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
@@ -1981,6 +1994,7 @@ dependencies = [
|
|||||||
"meta-srv",
|
"meta-srv",
|
||||||
"nu-ansi-term",
|
"nu-ansi-term",
|
||||||
"object-store",
|
"object-store",
|
||||||
|
"operator",
|
||||||
"query",
|
"query",
|
||||||
"rand 0.9.0",
|
"rand 0.9.0",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
@@ -1990,7 +2004,7 @@ dependencies = [
|
|||||||
"session",
|
"session",
|
||||||
"snafu 0.8.5",
|
"snafu 0.8.5",
|
||||||
"store-api",
|
"store-api",
|
||||||
"substrait 0.15.0",
|
"substrait 0.15.4",
|
||||||
"table",
|
"table",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"tokio",
|
"tokio",
|
||||||
@@ -1999,7 +2013,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "client"
|
name = "client"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"api",
|
"api",
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
@@ -2029,7 +2043,7 @@ dependencies = [
|
|||||||
"rand 0.9.0",
|
"rand 0.9.0",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"snafu 0.8.5",
|
"snafu 0.8.5",
|
||||||
"substrait 0.15.0",
|
"substrait 0.15.4",
|
||||||
"substrait 0.37.3",
|
"substrait 0.37.3",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
@@ -2070,7 +2084,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cmd"
|
name = "cmd"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"auth",
|
"auth",
|
||||||
@@ -2131,7 +2145,7 @@ dependencies = [
|
|||||||
"snafu 0.8.5",
|
"snafu 0.8.5",
|
||||||
"stat",
|
"stat",
|
||||||
"store-api",
|
"store-api",
|
||||||
"substrait 0.15.0",
|
"substrait 0.15.4",
|
||||||
"table",
|
"table",
|
||||||
"temp-env",
|
"temp-env",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
@@ -2178,7 +2192,7 @@ checksum = "55b672471b4e9f9e95499ea597ff64941a309b2cdbffcc46f2cc5e2d971fd335"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "common-base"
|
name = "common-base"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anymap2",
|
"anymap2",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -2200,11 +2214,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "common-catalog"
|
name = "common-catalog"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "common-config"
|
name = "common-config"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"common-base",
|
"common-base",
|
||||||
"common-error",
|
"common-error",
|
||||||
@@ -2229,7 +2243,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "common-datasource"
|
name = "common-datasource"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrow 54.2.1",
|
"arrow 54.2.1",
|
||||||
"arrow-schema 54.3.1",
|
"arrow-schema 54.3.1",
|
||||||
@@ -2266,7 +2280,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "common-decimal"
|
name = "common-decimal"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bigdecimal 0.4.8",
|
"bigdecimal 0.4.8",
|
||||||
"common-error",
|
"common-error",
|
||||||
@@ -2279,7 +2293,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "common-error"
|
name = "common-error"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"common-macro",
|
"common-macro",
|
||||||
"http 1.1.0",
|
"http 1.1.0",
|
||||||
@@ -2290,7 +2304,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "common-frontend"
|
name = "common-frontend"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"common-error",
|
"common-error",
|
||||||
@@ -2306,7 +2320,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "common-function"
|
name = "common-function"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash 0.8.11",
|
"ahash 0.8.11",
|
||||||
"api",
|
"api",
|
||||||
@@ -2359,7 +2373,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "common-greptimedb-telemetry"
|
name = "common-greptimedb-telemetry"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"common-runtime",
|
"common-runtime",
|
||||||
@@ -2376,7 +2390,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "common-grpc"
|
name = "common-grpc"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"api",
|
"api",
|
||||||
"arrow-flight",
|
"arrow-flight",
|
||||||
@@ -2408,7 +2422,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "common-grpc-expr"
|
name = "common-grpc-expr"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"api",
|
"api",
|
||||||
"common-base",
|
"common-base",
|
||||||
@@ -2427,7 +2441,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "common-macro"
|
name = "common-macro"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
"common-query",
|
"common-query",
|
||||||
@@ -2441,7 +2455,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "common-mem-prof"
|
name = "common-mem-prof"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"common-error",
|
"common-error",
|
||||||
@@ -2457,7 +2471,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "common-meta"
|
name = "common-meta"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anymap2",
|
"anymap2",
|
||||||
"api",
|
"api",
|
||||||
@@ -2522,7 +2536,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "common-options"
|
name = "common-options"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"common-grpc",
|
"common-grpc",
|
||||||
"humantime-serde",
|
"humantime-serde",
|
||||||
@@ -2531,11 +2545,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "common-plugins"
|
name = "common-plugins"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "common-pprof"
|
name = "common-pprof"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"common-error",
|
"common-error",
|
||||||
"common-macro",
|
"common-macro",
|
||||||
@@ -2547,7 +2561,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "common-procedure"
|
name = "common-procedure"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-stream",
|
"async-stream",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -2574,7 +2588,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "common-procedure-test"
|
name = "common-procedure-test"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"common-procedure",
|
"common-procedure",
|
||||||
@@ -2583,7 +2597,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "common-query"
|
name = "common-query"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"api",
|
"api",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -2609,7 +2623,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "common-recordbatch"
|
name = "common-recordbatch"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
"common-error",
|
"common-error",
|
||||||
@@ -2629,7 +2643,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "common-runtime"
|
name = "common-runtime"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"clap 4.5.19",
|
"clap 4.5.19",
|
||||||
@@ -2659,18 +2673,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "common-session"
|
name = "common-session"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"strum 0.27.1",
|
"strum 0.27.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "common-telemetry"
|
name = "common-telemetry"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atty",
|
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"common-error",
|
"common-error",
|
||||||
|
"common-version",
|
||||||
"console-subscriber",
|
"console-subscriber",
|
||||||
"greptime-proto",
|
"greptime-proto",
|
||||||
"humantime-serde",
|
"humantime-serde",
|
||||||
@@ -2694,7 +2708,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "common-test-util"
|
name = "common-test-util"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"client",
|
"client",
|
||||||
"common-grpc",
|
"common-grpc",
|
||||||
@@ -2707,7 +2721,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "common-time"
|
name = "common-time"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrow 54.2.1",
|
"arrow 54.2.1",
|
||||||
"chrono",
|
"chrono",
|
||||||
@@ -2725,9 +2739,10 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "common-version"
|
name = "common-version"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"build-data",
|
"build-data",
|
||||||
|
"cargo-manifest",
|
||||||
"const_format",
|
"const_format",
|
||||||
"serde",
|
"serde",
|
||||||
"shadow-rs",
|
"shadow-rs",
|
||||||
@@ -2735,7 +2750,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "common-wal"
|
name = "common-wal"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"common-base",
|
"common-base",
|
||||||
"common-error",
|
"common-error",
|
||||||
@@ -2758,7 +2773,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "common-workload"
|
name = "common-workload"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"api",
|
"api",
|
||||||
"common-telemetry",
|
"common-telemetry",
|
||||||
@@ -3069,9 +3084,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-channel"
|
name = "crossbeam-channel"
|
||||||
version = "0.5.13"
|
version = "0.5.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2"
|
checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
@@ -3714,7 +3729,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "datanode"
|
name = "datanode"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"api",
|
"api",
|
||||||
"arrow-flight",
|
"arrow-flight",
|
||||||
@@ -3767,7 +3782,7 @@ dependencies = [
|
|||||||
"session",
|
"session",
|
||||||
"snafu 0.8.5",
|
"snafu 0.8.5",
|
||||||
"store-api",
|
"store-api",
|
||||||
"substrait 0.15.0",
|
"substrait 0.15.4",
|
||||||
"table",
|
"table",
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml 0.8.19",
|
"toml 0.8.19",
|
||||||
@@ -3776,7 +3791,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "datatypes"
|
name = "datatypes"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrow 54.2.1",
|
"arrow 54.2.1",
|
||||||
"arrow-array 54.2.1",
|
"arrow-array 54.2.1",
|
||||||
@@ -4436,7 +4451,7 @@ checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "file-engine"
|
name = "file-engine"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"api",
|
"api",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -4573,7 +4588,7 @@ checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flow"
|
name = "flow"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"api",
|
"api",
|
||||||
"arrow 54.2.1",
|
"arrow 54.2.1",
|
||||||
@@ -4638,7 +4653,7 @@ dependencies = [
|
|||||||
"sql",
|
"sql",
|
||||||
"store-api",
|
"store-api",
|
||||||
"strum 0.27.1",
|
"strum 0.27.1",
|
||||||
"substrait 0.15.0",
|
"substrait 0.15.4",
|
||||||
"table",
|
"table",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tonic 0.12.3",
|
"tonic 0.12.3",
|
||||||
@@ -4693,10 +4708,11 @@ checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "frontend"
|
name = "frontend"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"api",
|
"api",
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
|
"async-stream",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"auth",
|
"auth",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -4752,9 +4768,10 @@ dependencies = [
|
|||||||
"sqlparser 0.54.0 (git+https://github.com/GreptimeTeam/sqlparser-rs.git?rev=0cf6c04490d59435ee965edd2078e8855bd8471e)",
|
"sqlparser 0.54.0 (git+https://github.com/GreptimeTeam/sqlparser-rs.git?rev=0cf6c04490d59435ee965edd2078e8855bd8471e)",
|
||||||
"store-api",
|
"store-api",
|
||||||
"strfmt",
|
"strfmt",
|
||||||
"substrait 0.15.0",
|
"substrait 0.15.4",
|
||||||
"table",
|
"table",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
"toml 0.8.19",
|
"toml 0.8.19",
|
||||||
"tonic 0.12.3",
|
"tonic 0.12.3",
|
||||||
"tower 0.5.2",
|
"tower 0.5.2",
|
||||||
@@ -5141,7 +5158,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "greptime-proto"
|
name = "greptime-proto"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/GreptimeTeam/greptime-proto.git?rev=5f6119ac7952878d39dcde0343c4bf828d18ffc8#5f6119ac7952878d39dcde0343c4bf828d18ffc8"
|
source = "git+https://github.com/GreptimeTeam/greptime-proto.git?rev=a5d256ba4abb7393e0859ffbf7fca1e38f3433dc#a5d256ba4abb7393e0859ffbf7fca1e38f3433dc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"prost 0.13.5",
|
"prost 0.13.5",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -5912,7 +5929,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "index"
|
name = "index"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"asynchronous-codec",
|
"asynchronous-codec",
|
||||||
@@ -6692,7 +6709,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
|
checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"windows-targets 0.52.6",
|
"windows-targets 0.48.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -6797,7 +6814,7 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log-query"
|
name = "log-query"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"common-error",
|
"common-error",
|
||||||
@@ -6809,7 +6826,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log-store"
|
name = "log-store"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-stream",
|
"async-stream",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -7107,7 +7124,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "meta-client"
|
name = "meta-client"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"api",
|
"api",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -7135,7 +7152,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "meta-srv"
|
name = "meta-srv"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"api",
|
"api",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -7226,7 +7243,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "metric-engine"
|
name = "metric-engine"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"api",
|
"api",
|
||||||
"aquamarine",
|
"aquamarine",
|
||||||
@@ -7248,6 +7265,7 @@ dependencies = [
|
|||||||
"humantime-serde",
|
"humantime-serde",
|
||||||
"itertools 0.14.0",
|
"itertools 0.14.0",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
"mito-codec",
|
||||||
"mito2",
|
"mito2",
|
||||||
"mur3",
|
"mur3",
|
||||||
"object-store",
|
"object-store",
|
||||||
@@ -7313,9 +7331,32 @@ dependencies = [
|
|||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mito-codec"
|
||||||
|
version = "0.15.4"
|
||||||
|
dependencies = [
|
||||||
|
"api",
|
||||||
|
"bytes",
|
||||||
|
"common-base",
|
||||||
|
"common-decimal",
|
||||||
|
"common-error",
|
||||||
|
"common-macro",
|
||||||
|
"common-recordbatch",
|
||||||
|
"common-telemetry",
|
||||||
|
"common-time",
|
||||||
|
"datafusion-common",
|
||||||
|
"datafusion-expr",
|
||||||
|
"datatypes",
|
||||||
|
"memcomparable",
|
||||||
|
"paste",
|
||||||
|
"serde",
|
||||||
|
"snafu 0.8.5",
|
||||||
|
"store-api",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mito2"
|
name = "mito2"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"api",
|
"api",
|
||||||
"aquamarine",
|
"aquamarine",
|
||||||
@@ -7355,6 +7396,7 @@ dependencies = [
|
|||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log-store",
|
"log-store",
|
||||||
"memcomparable",
|
"memcomparable",
|
||||||
|
"mito-codec",
|
||||||
"moka",
|
"moka",
|
||||||
"object-store",
|
"object-store",
|
||||||
"parquet",
|
"parquet",
|
||||||
@@ -8064,7 +8106,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "object-store"
|
name = "object-store"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -8378,7 +8420,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "operator"
|
name = "operator"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash 0.8.11",
|
"ahash 0.8.11",
|
||||||
"api",
|
"api",
|
||||||
@@ -8394,6 +8436,7 @@ dependencies = [
|
|||||||
"common-catalog",
|
"common-catalog",
|
||||||
"common-datasource",
|
"common-datasource",
|
||||||
"common-error",
|
"common-error",
|
||||||
|
"common-frontend",
|
||||||
"common-function",
|
"common-function",
|
||||||
"common-grpc",
|
"common-grpc",
|
||||||
"common-grpc-expr",
|
"common-grpc-expr",
|
||||||
@@ -8432,7 +8475,7 @@ dependencies = [
|
|||||||
"sql",
|
"sql",
|
||||||
"sqlparser 0.54.0 (git+https://github.com/GreptimeTeam/sqlparser-rs.git?rev=0cf6c04490d59435ee965edd2078e8855bd8471e)",
|
"sqlparser 0.54.0 (git+https://github.com/GreptimeTeam/sqlparser-rs.git?rev=0cf6c04490d59435ee965edd2078e8855bd8471e)",
|
||||||
"store-api",
|
"store-api",
|
||||||
"substrait 0.15.0",
|
"substrait 0.15.4",
|
||||||
"table",
|
"table",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
@@ -8699,7 +8742,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "partition"
|
name = "partition"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"api",
|
"api",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -8894,8 +8937,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "pgwire"
|
name = "pgwire"
|
||||||
version = "0.30.2"
|
version = "0.30.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/sunng87/pgwire?rev=127573d997228cfb70c7699881c568eae8131270#127573d997228cfb70c7699881c568eae8131270"
|
||||||
checksum = "4ca6c26b25be998208a13ff2f0c55b567363f34675410e6d6f1c513a150583fd"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -8988,7 +9030,7 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pipeline"
|
name = "pipeline"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash 0.8.11",
|
"ahash 0.8.11",
|
||||||
"api",
|
"api",
|
||||||
@@ -9131,7 +9173,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "plugins"
|
name = "plugins"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"auth",
|
"auth",
|
||||||
"clap 4.5.19",
|
"clap 4.5.19",
|
||||||
@@ -9444,7 +9486,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "promql"
|
name = "promql"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash 0.8.11",
|
"ahash 0.8.11",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -9540,7 +9582,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf"
|
checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck 0.5.0",
|
"heck 0.5.0",
|
||||||
"itertools 0.14.0",
|
"itertools 0.11.0",
|
||||||
"log",
|
"log",
|
||||||
"multimap",
|
"multimap",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
@@ -9586,7 +9628,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d"
|
checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"itertools 0.14.0",
|
"itertools 0.11.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.100",
|
"syn 2.0.100",
|
||||||
@@ -9726,7 +9768,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "puffin"
|
name = "puffin"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-compression 0.4.13",
|
"async-compression 0.4.13",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -9768,7 +9810,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "query"
|
name = "query"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash 0.8.11",
|
"ahash 0.8.11",
|
||||||
"api",
|
"api",
|
||||||
@@ -9834,7 +9876,7 @@ dependencies = [
|
|||||||
"sqlparser 0.54.0 (git+https://github.com/GreptimeTeam/sqlparser-rs.git?rev=0cf6c04490d59435ee965edd2078e8855bd8471e)",
|
"sqlparser 0.54.0 (git+https://github.com/GreptimeTeam/sqlparser-rs.git?rev=0cf6c04490d59435ee965edd2078e8855bd8471e)",
|
||||||
"statrs",
|
"statrs",
|
||||||
"store-api",
|
"store-api",
|
||||||
"substrait 0.15.0",
|
"substrait 0.15.4",
|
||||||
"table",
|
"table",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
@@ -10361,15 +10403,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ring"
|
name = "ring"
|
||||||
version = "0.17.8"
|
version = "0.17.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
|
checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"getrandom 0.2.15",
|
"getrandom 0.2.15",
|
||||||
"libc",
|
"libc",
|
||||||
"spin",
|
|
||||||
"untrusted",
|
"untrusted",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
@@ -11121,7 +11162,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "servers"
|
name = "servers"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash 0.8.11",
|
"ahash 0.8.11",
|
||||||
"api",
|
"api",
|
||||||
@@ -11242,7 +11283,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "session"
|
name = "session"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"api",
|
"api",
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
@@ -11581,7 +11622,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sql"
|
name = "sql"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"api",
|
"api",
|
||||||
"chrono",
|
"chrono",
|
||||||
@@ -11636,7 +11677,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlness-runner"
|
name = "sqlness-runner"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"clap 4.5.19",
|
"clap 4.5.19",
|
||||||
@@ -11936,7 +11977,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stat"
|
name = "stat"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"nix 0.30.1",
|
"nix 0.30.1",
|
||||||
]
|
]
|
||||||
@@ -11962,7 +12003,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "store-api"
|
name = "store-api"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"api",
|
"api",
|
||||||
"aquamarine",
|
"aquamarine",
|
||||||
@@ -12123,7 +12164,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "substrait"
|
name = "substrait"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -12303,7 +12344,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "table"
|
name = "table"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"api",
|
"api",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -12564,7 +12605,7 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tests-fuzz"
|
name = "tests-fuzz"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arbitrary",
|
"arbitrary",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@@ -12608,7 +12649,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tests-integration"
|
name = "tests-integration"
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"api",
|
"api",
|
||||||
"arrow-flight",
|
"arrow-flight",
|
||||||
@@ -12675,7 +12716,7 @@ dependencies = [
|
|||||||
"sql",
|
"sql",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"store-api",
|
"store-api",
|
||||||
"substrait 0.15.0",
|
"substrait 0.15.4",
|
||||||
"table",
|
"table",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"time",
|
"time",
|
||||||
@@ -13045,6 +13086,7 @@ version = "0.8.19"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
|
checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"indexmap 2.9.0",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_spanned",
|
"serde_spanned",
|
||||||
"toml_datetime",
|
"toml_datetime",
|
||||||
@@ -14156,7 +14198,7 @@ version = "0.1.9"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ members = [
|
|||||||
"src/meta-client",
|
"src/meta-client",
|
||||||
"src/meta-srv",
|
"src/meta-srv",
|
||||||
"src/metric-engine",
|
"src/metric-engine",
|
||||||
|
"src/mito-codec",
|
||||||
"src/mito2",
|
"src/mito2",
|
||||||
"src/object-store",
|
"src/object-store",
|
||||||
"src/operator",
|
"src/operator",
|
||||||
@@ -70,7 +71,7 @@ members = [
|
|||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.15.0"
|
version = "0.15.4"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
|
|
||||||
@@ -133,7 +134,7 @@ etcd-client = "0.14"
|
|||||||
fst = "0.4.7"
|
fst = "0.4.7"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
futures-util = "0.3"
|
futures-util = "0.3"
|
||||||
greptime-proto = { git = "https://github.com/GreptimeTeam/greptime-proto.git", rev = "5f6119ac7952878d39dcde0343c4bf828d18ffc8" }
|
greptime-proto = { git = "https://github.com/GreptimeTeam/greptime-proto.git", rev = "a5d256ba4abb7393e0859ffbf7fca1e38f3433dc" }
|
||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
http = "1"
|
http = "1"
|
||||||
humantime = "2.1"
|
humantime = "2.1"
|
||||||
@@ -274,6 +275,7 @@ log-store = { path = "src/log-store" }
|
|||||||
meta-client = { path = "src/meta-client" }
|
meta-client = { path = "src/meta-client" }
|
||||||
meta-srv = { path = "src/meta-srv" }
|
meta-srv = { path = "src/meta-srv" }
|
||||||
metric-engine = { path = "src/metric-engine" }
|
metric-engine = { path = "src/metric-engine" }
|
||||||
|
mito-codec = { path = "src/mito-codec" }
|
||||||
mito2 = { path = "src/mito2" }
|
mito2 = { path = "src/mito2" }
|
||||||
object-store = { path = "src/object-store" }
|
object-store = { path = "src/object-store" }
|
||||||
operator = { path = "src/operator" }
|
operator = { path = "src/operator" }
|
||||||
|
|||||||
@@ -189,7 +189,8 @@ We invite you to engage and contribute!
|
|||||||
- [Official Website](https://greptime.com/)
|
- [Official Website](https://greptime.com/)
|
||||||
- [Blog](https://greptime.com/blogs/)
|
- [Blog](https://greptime.com/blogs/)
|
||||||
- [LinkedIn](https://www.linkedin.com/company/greptime/)
|
- [LinkedIn](https://www.linkedin.com/company/greptime/)
|
||||||
- [Twitter](https://twitter.com/greptime)
|
- [X (Twitter)](https://X.com/greptime)
|
||||||
|
- [YouTube](https://www.youtube.com/@greptime)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
@@ -123,6 +123,7 @@
|
|||||||
| `storage.http_client.connect_timeout` | String | `30s` | The timeout for only the connect phase of a http client. |
|
| `storage.http_client.connect_timeout` | String | `30s` | The timeout for only the connect phase of a http client. |
|
||||||
| `storage.http_client.timeout` | String | `30s` | The total request timeout, applied from when the request starts connecting until the response body has finished.<br/>Also considered a total deadline. |
|
| `storage.http_client.timeout` | String | `30s` | The total request timeout, applied from when the request starts connecting until the response body has finished.<br/>Also considered a total deadline. |
|
||||||
| `storage.http_client.pool_idle_timeout` | String | `90s` | The timeout for idle sockets being kept-alive. |
|
| `storage.http_client.pool_idle_timeout` | String | `90s` | The timeout for idle sockets being kept-alive. |
|
||||||
|
| `storage.http_client.skip_ssl_validation` | Bool | `false` | To skip the ssl verification<br/>**Security Notice**: Setting `skip_ssl_validation = true` disables certificate verification, making connections vulnerable to man-in-the-middle attacks. Only use this in development or trusted private networks. |
|
||||||
| `[[region_engine]]` | -- | -- | The region engine options. You can configure multiple region engines. |
|
| `[[region_engine]]` | -- | -- | The region engine options. You can configure multiple region engines. |
|
||||||
| `region_engine.mito` | -- | -- | The Mito engine options. |
|
| `region_engine.mito` | -- | -- | The Mito engine options. |
|
||||||
| `region_engine.mito.num_workers` | Integer | `8` | Number of region workers. |
|
| `region_engine.mito.num_workers` | Integer | `8` | Number of region workers. |
|
||||||
@@ -146,6 +147,7 @@
|
|||||||
| `region_engine.mito.write_cache_ttl` | String | Unset | TTL for write cache. |
|
| `region_engine.mito.write_cache_ttl` | String | Unset | TTL for write cache. |
|
||||||
| `region_engine.mito.sst_write_buffer_size` | String | `8MB` | Buffer size for SST writing. |
|
| `region_engine.mito.sst_write_buffer_size` | String | `8MB` | Buffer size for SST writing. |
|
||||||
| `region_engine.mito.parallel_scan_channel_size` | Integer | `32` | Capacity of the channel to send data from parallel scan tasks to the main task. |
|
| `region_engine.mito.parallel_scan_channel_size` | Integer | `32` | Capacity of the channel to send data from parallel scan tasks to the main task. |
|
||||||
|
| `region_engine.mito.max_concurrent_scan_files` | Integer | `128` | Maximum number of SST files to scan concurrently. |
|
||||||
| `region_engine.mito.allow_stale_entries` | Bool | `false` | Whether to allow stale WAL entries read during replay. |
|
| `region_engine.mito.allow_stale_entries` | Bool | `false` | Whether to allow stale WAL entries read during replay. |
|
||||||
| `region_engine.mito.min_compaction_interval` | String | `0m` | Minimum time interval between two compactions.<br/>To align with the old behavior, the default value is 0 (no restrictions). |
|
| `region_engine.mito.min_compaction_interval` | String | `0m` | Minimum time interval between two compactions.<br/>To align with the old behavior, the default value is 0 (no restrictions). |
|
||||||
| `region_engine.mito.index` | -- | -- | The options for index in Mito engine. |
|
| `region_engine.mito.index` | -- | -- | The options for index in Mito engine. |
|
||||||
@@ -471,6 +473,7 @@
|
|||||||
| `storage.http_client.connect_timeout` | String | `30s` | The timeout for only the connect phase of a http client. |
|
| `storage.http_client.connect_timeout` | String | `30s` | The timeout for only the connect phase of a http client. |
|
||||||
| `storage.http_client.timeout` | String | `30s` | The total request timeout, applied from when the request starts connecting until the response body has finished.<br/>Also considered a total deadline. |
|
| `storage.http_client.timeout` | String | `30s` | The total request timeout, applied from when the request starts connecting until the response body has finished.<br/>Also considered a total deadline. |
|
||||||
| `storage.http_client.pool_idle_timeout` | String | `90s` | The timeout for idle sockets being kept-alive. |
|
| `storage.http_client.pool_idle_timeout` | String | `90s` | The timeout for idle sockets being kept-alive. |
|
||||||
|
| `storage.http_client.skip_ssl_validation` | Bool | `false` | To skip the ssl verification<br/>**Security Notice**: Setting `skip_ssl_validation = true` disables certificate verification, making connections vulnerable to man-in-the-middle attacks. Only use this in development or trusted private networks. |
|
||||||
| `[[region_engine]]` | -- | -- | The region engine options. You can configure multiple region engines. |
|
| `[[region_engine]]` | -- | -- | The region engine options. You can configure multiple region engines. |
|
||||||
| `region_engine.mito` | -- | -- | The Mito engine options. |
|
| `region_engine.mito` | -- | -- | The Mito engine options. |
|
||||||
| `region_engine.mito.num_workers` | Integer | `8` | Number of region workers. |
|
| `region_engine.mito.num_workers` | Integer | `8` | Number of region workers. |
|
||||||
@@ -494,6 +497,7 @@
|
|||||||
| `region_engine.mito.write_cache_ttl` | String | Unset | TTL for write cache. |
|
| `region_engine.mito.write_cache_ttl` | String | Unset | TTL for write cache. |
|
||||||
| `region_engine.mito.sst_write_buffer_size` | String | `8MB` | Buffer size for SST writing. |
|
| `region_engine.mito.sst_write_buffer_size` | String | `8MB` | Buffer size for SST writing. |
|
||||||
| `region_engine.mito.parallel_scan_channel_size` | Integer | `32` | Capacity of the channel to send data from parallel scan tasks to the main task. |
|
| `region_engine.mito.parallel_scan_channel_size` | Integer | `32` | Capacity of the channel to send data from parallel scan tasks to the main task. |
|
||||||
|
| `region_engine.mito.max_concurrent_scan_files` | Integer | `128` | Maximum number of SST files to scan concurrently. |
|
||||||
| `region_engine.mito.allow_stale_entries` | Bool | `false` | Whether to allow stale WAL entries read during replay. |
|
| `region_engine.mito.allow_stale_entries` | Bool | `false` | Whether to allow stale WAL entries read during replay. |
|
||||||
| `region_engine.mito.min_compaction_interval` | String | `0m` | Minimum time interval between two compactions.<br/>To align with the old behavior, the default value is 0 (no restrictions). |
|
| `region_engine.mito.min_compaction_interval` | String | `0m` | Minimum time interval between two compactions.<br/>To align with the old behavior, the default value is 0 (no restrictions). |
|
||||||
| `region_engine.mito.index` | -- | -- | The options for index in Mito engine. |
|
| `region_engine.mito.index` | -- | -- | The options for index in Mito engine. |
|
||||||
|
|||||||
@@ -367,6 +367,10 @@ timeout = "30s"
|
|||||||
## The timeout for idle sockets being kept-alive.
|
## The timeout for idle sockets being kept-alive.
|
||||||
pool_idle_timeout = "90s"
|
pool_idle_timeout = "90s"
|
||||||
|
|
||||||
|
## To skip the ssl verification
|
||||||
|
## **Security Notice**: Setting `skip_ssl_validation = true` disables certificate verification, making connections vulnerable to man-in-the-middle attacks. Only use this in development or trusted private networks.
|
||||||
|
skip_ssl_validation = false
|
||||||
|
|
||||||
# Custom storage options
|
# Custom storage options
|
||||||
# [[storage.providers]]
|
# [[storage.providers]]
|
||||||
# name = "S3"
|
# name = "S3"
|
||||||
@@ -470,6 +474,9 @@ sst_write_buffer_size = "8MB"
|
|||||||
## Capacity of the channel to send data from parallel scan tasks to the main task.
|
## Capacity of the channel to send data from parallel scan tasks to the main task.
|
||||||
parallel_scan_channel_size = 32
|
parallel_scan_channel_size = 32
|
||||||
|
|
||||||
|
## Maximum number of SST files to scan concurrently.
|
||||||
|
max_concurrent_scan_files = 128
|
||||||
|
|
||||||
## Whether to allow stale WAL entries read during replay.
|
## Whether to allow stale WAL entries read during replay.
|
||||||
allow_stale_entries = false
|
allow_stale_entries = false
|
||||||
|
|
||||||
|
|||||||
@@ -458,6 +458,10 @@ timeout = "30s"
|
|||||||
## The timeout for idle sockets being kept-alive.
|
## The timeout for idle sockets being kept-alive.
|
||||||
pool_idle_timeout = "90s"
|
pool_idle_timeout = "90s"
|
||||||
|
|
||||||
|
## To skip the ssl verification
|
||||||
|
## **Security Notice**: Setting `skip_ssl_validation = true` disables certificate verification, making connections vulnerable to man-in-the-middle attacks. Only use this in development or trusted private networks.
|
||||||
|
skip_ssl_validation = false
|
||||||
|
|
||||||
# Custom storage options
|
# Custom storage options
|
||||||
# [[storage.providers]]
|
# [[storage.providers]]
|
||||||
# name = "S3"
|
# name = "S3"
|
||||||
@@ -561,6 +565,9 @@ sst_write_buffer_size = "8MB"
|
|||||||
## Capacity of the channel to send data from parallel scan tasks to the main task.
|
## Capacity of the channel to send data from parallel scan tasks to the main task.
|
||||||
parallel_scan_channel_size = 32
|
parallel_scan_channel_size = 32
|
||||||
|
|
||||||
|
## Maximum number of SST files to scan concurrently.
|
||||||
|
max_concurrent_scan_files = 128
|
||||||
|
|
||||||
## Whether to allow stale WAL entries read during replay.
|
## Whether to allow stale WAL entries read during replay.
|
||||||
allow_stale_entries = false
|
allow_stale_entries = false
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -70,6 +70,7 @@
|
|||||||
| Inflight Flush | `greptime_mito_inflight_flush_count` | `timeseries` | Ongoing flush task count | `prometheus` | `none` | `[{{instance}}]-[{{pod}}]` |
|
| Inflight Flush | `greptime_mito_inflight_flush_count` | `timeseries` | Ongoing flush task count | `prometheus` | `none` | `[{{instance}}]-[{{pod}}]` |
|
||||||
| Compaction Input/Output Bytes | `sum by(instance, pod) (greptime_mito_compaction_input_bytes)`<br/>`sum by(instance, pod) (greptime_mito_compaction_output_bytes)` | `timeseries` | Compaction oinput output bytes | `prometheus` | `bytes` | `[{{instance}}]-[{{pod}}]-input` |
|
| Compaction Input/Output Bytes | `sum by(instance, pod) (greptime_mito_compaction_input_bytes)`<br/>`sum by(instance, pod) (greptime_mito_compaction_output_bytes)` | `timeseries` | Compaction oinput output bytes | `prometheus` | `bytes` | `[{{instance}}]-[{{pod}}]-input` |
|
||||||
| Region Worker Handle Bulk Insert Requests | `histogram_quantile(0.95, sum by(le,instance, stage, pod) (rate(greptime_region_worker_handle_write_bucket[$__rate_interval])))`<br/>`sum by(instance, stage, pod) (rate(greptime_region_worker_handle_write_sum[$__rate_interval]))/sum by(instance, stage, pod) (rate(greptime_region_worker_handle_write_count[$__rate_interval]))` | `timeseries` | Per-stage elapsed time for region worker to handle bulk insert region requests. | `prometheus` | `s` | `[{{instance}}]-[{{pod}}]-[{{stage}}]-P95` |
|
| Region Worker Handle Bulk Insert Requests | `histogram_quantile(0.95, sum by(le,instance, stage, pod) (rate(greptime_region_worker_handle_write_bucket[$__rate_interval])))`<br/>`sum by(instance, stage, pod) (rate(greptime_region_worker_handle_write_sum[$__rate_interval]))/sum by(instance, stage, pod) (rate(greptime_region_worker_handle_write_count[$__rate_interval]))` | `timeseries` | Per-stage elapsed time for region worker to handle bulk insert region requests. | `prometheus` | `s` | `[{{instance}}]-[{{pod}}]-[{{stage}}]-P95` |
|
||||||
|
| Active Series and Field Builders Count | `sum by(instance, pod) (greptime_mito_memtable_active_series_count)`<br/>`sum by(instance, pod) (greptime_mito_memtable_field_builder_count)` | `timeseries` | Compaction oinput output bytes | `prometheus` | `none` | `[{{instance}}]-[{{pod}}]-series` |
|
||||||
| Region Worker Convert Requests | `histogram_quantile(0.95, sum by(le, instance, stage, pod) (rate(greptime_datanode_convert_region_request_bucket[$__rate_interval])))`<br/>`sum by(le,instance, stage, pod) (rate(greptime_datanode_convert_region_request_sum[$__rate_interval]))/sum by(le,instance, stage, pod) (rate(greptime_datanode_convert_region_request_count[$__rate_interval]))` | `timeseries` | Per-stage elapsed time for region worker to decode requests. | `prometheus` | `s` | `[{{instance}}]-[{{pod}}]-[{{stage}}]-P95` |
|
| Region Worker Convert Requests | `histogram_quantile(0.95, sum by(le, instance, stage, pod) (rate(greptime_datanode_convert_region_request_bucket[$__rate_interval])))`<br/>`sum by(le,instance, stage, pod) (rate(greptime_datanode_convert_region_request_sum[$__rate_interval]))/sum by(le,instance, stage, pod) (rate(greptime_datanode_convert_region_request_count[$__rate_interval]))` | `timeseries` | Per-stage elapsed time for region worker to decode requests. | `prometheus` | `s` | `[{{instance}}]-[{{pod}}]-[{{stage}}]-P95` |
|
||||||
# OpenDAL
|
# OpenDAL
|
||||||
| Title | Query | Type | Description | Datasource | Unit | Legend Format |
|
| Title | Query | Type | Description | Datasource | Unit | Legend Format |
|
||||||
|
|||||||
@@ -612,6 +612,21 @@ groups:
|
|||||||
type: prometheus
|
type: prometheus
|
||||||
uid: ${metrics}
|
uid: ${metrics}
|
||||||
legendFormat: '[{{instance}}]-[{{pod}}]-[{{stage}}]-AVG'
|
legendFormat: '[{{instance}}]-[{{pod}}]-[{{stage}}]-AVG'
|
||||||
|
- title: Active Series and Field Builders Count
|
||||||
|
type: timeseries
|
||||||
|
description: Compaction oinput output bytes
|
||||||
|
unit: none
|
||||||
|
queries:
|
||||||
|
- expr: sum by(instance, pod) (greptime_mito_memtable_active_series_count)
|
||||||
|
datasource:
|
||||||
|
type: prometheus
|
||||||
|
uid: ${metrics}
|
||||||
|
legendFormat: '[{{instance}}]-[{{pod}}]-series'
|
||||||
|
- expr: sum by(instance, pod) (greptime_mito_memtable_field_builder_count)
|
||||||
|
datasource:
|
||||||
|
type: prometheus
|
||||||
|
uid: ${metrics}
|
||||||
|
legendFormat: '[{{instance}}]-[{{pod}}]-field_builders'
|
||||||
- title: Region Worker Convert Requests
|
- title: Region Worker Convert Requests
|
||||||
type: timeseries
|
type: timeseries
|
||||||
description: Per-stage elapsed time for region worker to decode requests.
|
description: Per-stage elapsed time for region worker to decode requests.
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -70,6 +70,7 @@
|
|||||||
| Inflight Flush | `greptime_mito_inflight_flush_count` | `timeseries` | Ongoing flush task count | `prometheus` | `none` | `[{{instance}}]-[{{pod}}]` |
|
| Inflight Flush | `greptime_mito_inflight_flush_count` | `timeseries` | Ongoing flush task count | `prometheus` | `none` | `[{{instance}}]-[{{pod}}]` |
|
||||||
| Compaction Input/Output Bytes | `sum by(instance, pod) (greptime_mito_compaction_input_bytes)`<br/>`sum by(instance, pod) (greptime_mito_compaction_output_bytes)` | `timeseries` | Compaction oinput output bytes | `prometheus` | `bytes` | `[{{instance}}]-[{{pod}}]-input` |
|
| Compaction Input/Output Bytes | `sum by(instance, pod) (greptime_mito_compaction_input_bytes)`<br/>`sum by(instance, pod) (greptime_mito_compaction_output_bytes)` | `timeseries` | Compaction oinput output bytes | `prometheus` | `bytes` | `[{{instance}}]-[{{pod}}]-input` |
|
||||||
| Region Worker Handle Bulk Insert Requests | `histogram_quantile(0.95, sum by(le,instance, stage, pod) (rate(greptime_region_worker_handle_write_bucket[$__rate_interval])))`<br/>`sum by(instance, stage, pod) (rate(greptime_region_worker_handle_write_sum[$__rate_interval]))/sum by(instance, stage, pod) (rate(greptime_region_worker_handle_write_count[$__rate_interval]))` | `timeseries` | Per-stage elapsed time for region worker to handle bulk insert region requests. | `prometheus` | `s` | `[{{instance}}]-[{{pod}}]-[{{stage}}]-P95` |
|
| Region Worker Handle Bulk Insert Requests | `histogram_quantile(0.95, sum by(le,instance, stage, pod) (rate(greptime_region_worker_handle_write_bucket[$__rate_interval])))`<br/>`sum by(instance, stage, pod) (rate(greptime_region_worker_handle_write_sum[$__rate_interval]))/sum by(instance, stage, pod) (rate(greptime_region_worker_handle_write_count[$__rate_interval]))` | `timeseries` | Per-stage elapsed time for region worker to handle bulk insert region requests. | `prometheus` | `s` | `[{{instance}}]-[{{pod}}]-[{{stage}}]-P95` |
|
||||||
|
| Active Series and Field Builders Count | `sum by(instance, pod) (greptime_mito_memtable_active_series_count)`<br/>`sum by(instance, pod) (greptime_mito_memtable_field_builder_count)` | `timeseries` | Compaction oinput output bytes | `prometheus` | `none` | `[{{instance}}]-[{{pod}}]-series` |
|
||||||
| Region Worker Convert Requests | `histogram_quantile(0.95, sum by(le, instance, stage, pod) (rate(greptime_datanode_convert_region_request_bucket[$__rate_interval])))`<br/>`sum by(le,instance, stage, pod) (rate(greptime_datanode_convert_region_request_sum[$__rate_interval]))/sum by(le,instance, stage, pod) (rate(greptime_datanode_convert_region_request_count[$__rate_interval]))` | `timeseries` | Per-stage elapsed time for region worker to decode requests. | `prometheus` | `s` | `[{{instance}}]-[{{pod}}]-[{{stage}}]-P95` |
|
| Region Worker Convert Requests | `histogram_quantile(0.95, sum by(le, instance, stage, pod) (rate(greptime_datanode_convert_region_request_bucket[$__rate_interval])))`<br/>`sum by(le,instance, stage, pod) (rate(greptime_datanode_convert_region_request_sum[$__rate_interval]))/sum by(le,instance, stage, pod) (rate(greptime_datanode_convert_region_request_count[$__rate_interval]))` | `timeseries` | Per-stage elapsed time for region worker to decode requests. | `prometheus` | `s` | `[{{instance}}]-[{{pod}}]-[{{stage}}]-P95` |
|
||||||
# OpenDAL
|
# OpenDAL
|
||||||
| Title | Query | Type | Description | Datasource | Unit | Legend Format |
|
| Title | Query | Type | Description | Datasource | Unit | Legend Format |
|
||||||
|
|||||||
@@ -612,6 +612,21 @@ groups:
|
|||||||
type: prometheus
|
type: prometheus
|
||||||
uid: ${metrics}
|
uid: ${metrics}
|
||||||
legendFormat: '[{{instance}}]-[{{pod}}]-[{{stage}}]-AVG'
|
legendFormat: '[{{instance}}]-[{{pod}}]-[{{stage}}]-AVG'
|
||||||
|
- title: Active Series and Field Builders Count
|
||||||
|
type: timeseries
|
||||||
|
description: Compaction oinput output bytes
|
||||||
|
unit: none
|
||||||
|
queries:
|
||||||
|
- expr: sum by(instance, pod) (greptime_mito_memtable_active_series_count)
|
||||||
|
datasource:
|
||||||
|
type: prometheus
|
||||||
|
uid: ${metrics}
|
||||||
|
legendFormat: '[{{instance}}]-[{{pod}}]-series'
|
||||||
|
- expr: sum by(instance, pod) (greptime_mito_memtable_field_builder_count)
|
||||||
|
datasource:
|
||||||
|
type: prometheus
|
||||||
|
uid: ${metrics}
|
||||||
|
legendFormat: '[{{instance}}]-[{{pod}}]-field_builders'
|
||||||
- title: Region Worker Convert Requests
|
- title: Region Worker Convert Requests
|
||||||
type: timeseries
|
type: timeseries
|
||||||
description: Per-stage elapsed time for region worker to decode requests.
|
description: Per-stage elapsed time for region worker to decode requests.
|
||||||
|
|||||||
@@ -226,18 +226,20 @@ mod tests {
|
|||||||
assert!(options.is_none());
|
assert!(options.is_none());
|
||||||
|
|
||||||
let mut schema = ColumnSchema::new("test", ConcreteDataType::string_datatype(), true)
|
let mut schema = ColumnSchema::new("test", ConcreteDataType::string_datatype(), true)
|
||||||
.with_fulltext_options(FulltextOptions {
|
.with_fulltext_options(FulltextOptions::new_unchecked(
|
||||||
enable: true,
|
true,
|
||||||
analyzer: FulltextAnalyzer::English,
|
FulltextAnalyzer::English,
|
||||||
case_sensitive: false,
|
false,
|
||||||
backend: FulltextBackend::Bloom,
|
FulltextBackend::Bloom,
|
||||||
})
|
10240,
|
||||||
|
0.01,
|
||||||
|
))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
schema.set_inverted_index(true);
|
schema.set_inverted_index(true);
|
||||||
let options = options_from_column_schema(&schema).unwrap();
|
let options = options_from_column_schema(&schema).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
options.options.get(FULLTEXT_GRPC_KEY).unwrap(),
|
options.options.get(FULLTEXT_GRPC_KEY).unwrap(),
|
||||||
"{\"enable\":true,\"analyzer\":\"English\",\"case-sensitive\":false,\"backend\":\"bloom\"}"
|
"{\"enable\":true,\"analyzer\":\"English\",\"case-sensitive\":false,\"backend\":\"bloom\",\"granularity\":10240,\"false-positive-rate-in-10000\":100}"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
options.options.get(INVERTED_INDEX_GRPC_KEY).unwrap(),
|
options.options.get(INVERTED_INDEX_GRPC_KEY).unwrap(),
|
||||||
@@ -247,16 +249,18 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_options_with_fulltext() {
|
fn test_options_with_fulltext() {
|
||||||
let fulltext = FulltextOptions {
|
let fulltext = FulltextOptions::new_unchecked(
|
||||||
enable: true,
|
true,
|
||||||
analyzer: FulltextAnalyzer::English,
|
FulltextAnalyzer::English,
|
||||||
case_sensitive: false,
|
false,
|
||||||
backend: FulltextBackend::Bloom,
|
FulltextBackend::Bloom,
|
||||||
};
|
10240,
|
||||||
|
0.01,
|
||||||
|
);
|
||||||
let options = options_from_fulltext(&fulltext).unwrap().unwrap();
|
let options = options_from_fulltext(&fulltext).unwrap().unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
options.options.get(FULLTEXT_GRPC_KEY).unwrap(),
|
options.options.get(FULLTEXT_GRPC_KEY).unwrap(),
|
||||||
"{\"enable\":true,\"analyzer\":\"English\",\"case-sensitive\":false,\"backend\":\"bloom\"}"
|
"{\"enable\":true,\"analyzer\":\"English\",\"case-sensitive\":false,\"backend\":\"bloom\",\"granularity\":10240,\"false-positive-rate-in-10000\":100}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ arrow-schema.workspace = true
|
|||||||
async-stream.workspace = true
|
async-stream.workspace = true
|
||||||
async-trait.workspace = true
|
async-trait.workspace = true
|
||||||
bytes.workspace = true
|
bytes.workspace = true
|
||||||
|
common-base.workspace = true
|
||||||
common-catalog.workspace = true
|
common-catalog.workspace = true
|
||||||
common-error.workspace = true
|
common-error.workspace = true
|
||||||
common-frontend.workspace = true
|
common-frontend.workspace = true
|
||||||
|
|||||||
@@ -278,12 +278,25 @@ pub enum Error {
|
|||||||
location: Location,
|
location: Location,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[snafu(display("Failed to list frontend nodes"))]
|
#[snafu(display("Failed to invoke frontend services"))]
|
||||||
ListProcess {
|
InvokeFrontend {
|
||||||
source: common_frontend::error::Error,
|
source: common_frontend::error::Error,
|
||||||
#[snafu(implicit)]
|
#[snafu(implicit)]
|
||||||
location: Location,
|
location: Location,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Meta client is not provided"))]
|
||||||
|
MetaClientMissing {
|
||||||
|
#[snafu(implicit)]
|
||||||
|
location: Location,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Failed to find frontend node: {}", addr))]
|
||||||
|
FrontendNotFound {
|
||||||
|
addr: String,
|
||||||
|
#[snafu(implicit)]
|
||||||
|
location: Location,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
@@ -352,7 +365,10 @@ impl ErrorExt for Error {
|
|||||||
Error::GetViewCache { source, .. } | Error::GetTableCache { source, .. } => {
|
Error::GetViewCache { source, .. } | Error::GetTableCache { source, .. } => {
|
||||||
source.status_code()
|
source.status_code()
|
||||||
}
|
}
|
||||||
Error::ListProcess { source, .. } => source.status_code(),
|
Error::InvokeFrontend { source, .. } => source.status_code(),
|
||||||
|
Error::FrontendNotFound { .. } | Error::MetaClientMissing { .. } => {
|
||||||
|
StatusCode::Unexpected
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,11 +22,13 @@ use common_catalog::consts::{
|
|||||||
PG_CATALOG_NAME,
|
PG_CATALOG_NAME,
|
||||||
};
|
};
|
||||||
use common_error::ext::BoxedError;
|
use common_error::ext::BoxedError;
|
||||||
use common_meta::cache::{LayeredCacheRegistryRef, ViewInfoCacheRef};
|
use common_meta::cache::{
|
||||||
|
LayeredCacheRegistryRef, TableRoute, TableRouteCacheRef, ViewInfoCacheRef,
|
||||||
|
};
|
||||||
use common_meta::key::catalog_name::CatalogNameKey;
|
use common_meta::key::catalog_name::CatalogNameKey;
|
||||||
use common_meta::key::flow::FlowMetadataManager;
|
use common_meta::key::flow::FlowMetadataManager;
|
||||||
use common_meta::key::schema_name::SchemaNameKey;
|
use common_meta::key::schema_name::SchemaNameKey;
|
||||||
use common_meta::key::table_info::TableInfoValue;
|
use common_meta::key::table_info::{TableInfoManager, TableInfoValue};
|
||||||
use common_meta::key::table_name::TableNameKey;
|
use common_meta::key::table_name::TableNameKey;
|
||||||
use common_meta::key::{TableMetadataManager, TableMetadataManagerRef};
|
use common_meta::key::{TableMetadataManager, TableMetadataManagerRef};
|
||||||
use common_meta::kv_backend::KvBackendRef;
|
use common_meta::kv_backend::KvBackendRef;
|
||||||
@@ -37,6 +39,7 @@ use moka::sync::Cache;
|
|||||||
use partition::manager::{PartitionRuleManager, PartitionRuleManagerRef};
|
use partition::manager::{PartitionRuleManager, PartitionRuleManagerRef};
|
||||||
use session::context::{Channel, QueryContext};
|
use session::context::{Channel, QueryContext};
|
||||||
use snafu::prelude::*;
|
use snafu::prelude::*;
|
||||||
|
use store_api::metric_engine_consts::METRIC_ENGINE_NAME;
|
||||||
use table::dist_table::DistTable;
|
use table::dist_table::DistTable;
|
||||||
use table::metadata::TableId;
|
use table::metadata::TableId;
|
||||||
use table::table::numbers::{NumbersTable, NUMBERS_TABLE_NAME};
|
use table::table::numbers::{NumbersTable, NUMBERS_TABLE_NAME};
|
||||||
@@ -140,6 +143,61 @@ impl KvBackendCatalogManager {
|
|||||||
pub fn procedure_manager(&self) -> Option<ProcedureManagerRef> {
|
pub fn procedure_manager(&self) -> Option<ProcedureManagerRef> {
|
||||||
self.procedure_manager.clone()
|
self.procedure_manager.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Override logical table's partition key indices with physical table's.
|
||||||
|
async fn override_logical_table_partition_key_indices(
|
||||||
|
table_route_cache: &TableRouteCacheRef,
|
||||||
|
table_info_manager: &TableInfoManager,
|
||||||
|
table: TableRef,
|
||||||
|
) -> Result<TableRef> {
|
||||||
|
// If the table is not a metric table, return the table directly.
|
||||||
|
if table.table_info().meta.engine != METRIC_ENGINE_NAME {
|
||||||
|
return Ok(table);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(table_route_value) = table_route_cache
|
||||||
|
.get(table.table_info().table_id())
|
||||||
|
.await
|
||||||
|
.context(TableMetadataManagerSnafu)?
|
||||||
|
&& let TableRoute::Logical(logical_route) = &*table_route_value
|
||||||
|
&& let Some(physical_table_info_value) = table_info_manager
|
||||||
|
.get(logical_route.physical_table_id())
|
||||||
|
.await
|
||||||
|
.context(TableMetadataManagerSnafu)?
|
||||||
|
{
|
||||||
|
let mut new_table_info = (*table.table_info()).clone();
|
||||||
|
|
||||||
|
// Remap partition key indices from physical table to logical table
|
||||||
|
new_table_info.meta.partition_key_indices = physical_table_info_value
|
||||||
|
.table_info
|
||||||
|
.meta
|
||||||
|
.partition_key_indices
|
||||||
|
.iter()
|
||||||
|
.filter_map(|&physical_index| {
|
||||||
|
// Get the column name from the physical table using the physical index
|
||||||
|
physical_table_info_value
|
||||||
|
.table_info
|
||||||
|
.meta
|
||||||
|
.schema
|
||||||
|
.column_schemas
|
||||||
|
.get(physical_index)
|
||||||
|
.and_then(|physical_column| {
|
||||||
|
// Find the corresponding index in the logical table schema
|
||||||
|
new_table_info
|
||||||
|
.meta
|
||||||
|
.schema
|
||||||
|
.column_index_by_name(physical_column.name.as_str())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let new_table = DistTable::table(Arc::new(new_table_info));
|
||||||
|
|
||||||
|
return Ok(new_table);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(table)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
@@ -266,16 +324,28 @@ impl CatalogManager for KvBackendCatalogManager {
|
|||||||
let table_cache: TableCacheRef = self.cache_registry.get().context(CacheNotFoundSnafu {
|
let table_cache: TableCacheRef = self.cache_registry.get().context(CacheNotFoundSnafu {
|
||||||
name: "table_cache",
|
name: "table_cache",
|
||||||
})?;
|
})?;
|
||||||
if let Some(table) = table_cache
|
|
||||||
|
let table = table_cache
|
||||||
.get_by_ref(&TableName {
|
.get_by_ref(&TableName {
|
||||||
catalog_name: catalog_name.to_string(),
|
catalog_name: catalog_name.to_string(),
|
||||||
schema_name: schema_name.to_string(),
|
schema_name: schema_name.to_string(),
|
||||||
table_name: table_name.to_string(),
|
table_name: table_name.to_string(),
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.context(GetTableCacheSnafu)?
|
.context(GetTableCacheSnafu)?;
|
||||||
{
|
|
||||||
return Ok(Some(table));
|
if let Some(table) = table {
|
||||||
|
let table_route_cache: TableRouteCacheRef =
|
||||||
|
self.cache_registry.get().context(CacheNotFoundSnafu {
|
||||||
|
name: "table_route_cache",
|
||||||
|
})?;
|
||||||
|
return Self::override_logical_table_partition_key_indices(
|
||||||
|
&table_route_cache,
|
||||||
|
self.table_metadata_manager.table_info_manager(),
|
||||||
|
table,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map(Some);
|
||||||
}
|
}
|
||||||
|
|
||||||
if channel == Channel::Postgres {
|
if channel == Channel::Postgres {
|
||||||
@@ -288,7 +358,7 @@ impl CatalogManager for KvBackendCatalogManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(None);
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn tables_by_ids(
|
async fn tables_by_ids(
|
||||||
@@ -340,8 +410,20 @@ impl CatalogManager for KvBackendCatalogManager {
|
|||||||
let catalog = catalog.to_string();
|
let catalog = catalog.to_string();
|
||||||
let schema = schema.to_string();
|
let schema = schema.to_string();
|
||||||
let semaphore = Arc::new(Semaphore::new(CONCURRENCY));
|
let semaphore = Arc::new(Semaphore::new(CONCURRENCY));
|
||||||
|
let table_route_cache: Result<TableRouteCacheRef> =
|
||||||
|
self.cache_registry.get().context(CacheNotFoundSnafu {
|
||||||
|
name: "table_route_cache",
|
||||||
|
});
|
||||||
|
|
||||||
common_runtime::spawn_global(async move {
|
common_runtime::spawn_global(async move {
|
||||||
|
let table_route_cache = match table_route_cache {
|
||||||
|
Ok(table_route_cache) => table_route_cache,
|
||||||
|
Err(e) => {
|
||||||
|
let _ = tx.send(Err(e)).await;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let table_id_stream = metadata_manager
|
let table_id_stream = metadata_manager
|
||||||
.table_name_manager()
|
.table_name_manager()
|
||||||
.tables(&catalog, &schema)
|
.tables(&catalog, &schema)
|
||||||
@@ -368,6 +450,7 @@ impl CatalogManager for KvBackendCatalogManager {
|
|||||||
let metadata_manager = metadata_manager.clone();
|
let metadata_manager = metadata_manager.clone();
|
||||||
let tx = tx.clone();
|
let tx = tx.clone();
|
||||||
let semaphore = semaphore.clone();
|
let semaphore = semaphore.clone();
|
||||||
|
let table_route_cache = table_route_cache.clone();
|
||||||
common_runtime::spawn_global(async move {
|
common_runtime::spawn_global(async move {
|
||||||
// we don't explicitly close the semaphore so just ignore the potential error.
|
// we don't explicitly close the semaphore so just ignore the potential error.
|
||||||
let _ = semaphore.acquire().await;
|
let _ = semaphore.acquire().await;
|
||||||
@@ -385,6 +468,16 @@ impl CatalogManager for KvBackendCatalogManager {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for table in table_info_values.into_values().map(build_table) {
|
for table in table_info_values.into_values().map(build_table) {
|
||||||
|
let table = if let Ok(table) = table {
|
||||||
|
Self::override_logical_table_partition_key_indices(
|
||||||
|
&table_route_cache,
|
||||||
|
metadata_manager.table_info_manager(),
|
||||||
|
table,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
} else {
|
||||||
|
table
|
||||||
|
};
|
||||||
if tx.send(table).await.is_err() {
|
if tx.send(table).await.is_err() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
#![feature(assert_matches)]
|
#![feature(assert_matches)]
|
||||||
#![feature(try_blocks)]
|
#![feature(try_blocks)]
|
||||||
|
#![feature(let_chains)]
|
||||||
|
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::fmt::{Debug, Formatter};
|
use std::fmt::{Debug, Formatter};
|
||||||
|
|||||||
@@ -34,4 +34,20 @@ lazy_static! {
|
|||||||
register_histogram!("greptime_catalog_kv_get", "catalog kv get").unwrap();
|
register_histogram!("greptime_catalog_kv_get", "catalog kv get").unwrap();
|
||||||
pub static ref METRIC_CATALOG_KV_BATCH_GET: Histogram =
|
pub static ref METRIC_CATALOG_KV_BATCH_GET: Histogram =
|
||||||
register_histogram!("greptime_catalog_kv_batch_get", "catalog kv batch get").unwrap();
|
register_histogram!("greptime_catalog_kv_batch_get", "catalog kv batch get").unwrap();
|
||||||
|
|
||||||
|
/// Count of running process in each catalog.
|
||||||
|
pub static ref PROCESS_LIST_COUNT: IntGaugeVec = register_int_gauge_vec!(
|
||||||
|
"greptime_process_list_count",
|
||||||
|
"Running process count per catalog",
|
||||||
|
&["catalog"]
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
/// Count of killed process in each catalog.
|
||||||
|
pub static ref PROCESS_KILL_COUNT: IntCounterVec = register_int_counter_vec!(
|
||||||
|
"greptime_process_kill_count",
|
||||||
|
"Completed kill process requests count",
|
||||||
|
&["catalog"]
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,24 +14,33 @@
|
|||||||
|
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::atomic::{AtomicU64, Ordering};
|
use std::fmt::{Debug, Formatter};
|
||||||
|
use std::sync::atomic::{AtomicU32, Ordering};
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use api::v1::frontend::{ListProcessRequest, ProcessInfo};
|
use api::v1::frontend::{KillProcessRequest, ListProcessRequest, ProcessInfo};
|
||||||
|
use common_base::cancellation::CancellationHandle;
|
||||||
use common_frontend::selector::{FrontendSelector, MetaClientSelector};
|
use common_frontend::selector::{FrontendSelector, MetaClientSelector};
|
||||||
use common_telemetry::{debug, info};
|
use common_telemetry::{debug, info, warn};
|
||||||
use common_time::util::current_time_millis;
|
use common_time::util::current_time_millis;
|
||||||
use meta_client::MetaClientRef;
|
use meta_client::MetaClientRef;
|
||||||
use snafu::ResultExt;
|
use snafu::{ensure, OptionExt, ResultExt};
|
||||||
|
|
||||||
use crate::error;
|
use crate::error;
|
||||||
|
use crate::metrics::{PROCESS_KILL_COUNT, PROCESS_LIST_COUNT};
|
||||||
|
|
||||||
|
pub type ProcessId = u32;
|
||||||
pub type ProcessManagerRef = Arc<ProcessManager>;
|
pub type ProcessManagerRef = Arc<ProcessManager>;
|
||||||
|
|
||||||
|
/// Query process manager.
|
||||||
pub struct ProcessManager {
|
pub struct ProcessManager {
|
||||||
|
/// Local frontend server address,
|
||||||
server_addr: String,
|
server_addr: String,
|
||||||
next_id: AtomicU64,
|
/// Next process id for local queries.
|
||||||
catalogs: RwLock<HashMap<String, HashMap<u64, ProcessInfo>>>,
|
next_id: AtomicU32,
|
||||||
|
/// Running process per catalog.
|
||||||
|
catalogs: RwLock<HashMap<String, HashMap<ProcessId, CancellableProcess>>>,
|
||||||
|
/// Frontend selector to locate frontend nodes.
|
||||||
frontend_selector: Option<MetaClientSelector>,
|
frontend_selector: Option<MetaClientSelector>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,15 +59,16 @@ impl ProcessManager {
|
|||||||
|
|
||||||
impl ProcessManager {
|
impl ProcessManager {
|
||||||
/// Registers a submitted query. Use the provided id if present.
|
/// Registers a submitted query. Use the provided id if present.
|
||||||
|
#[must_use]
|
||||||
pub fn register_query(
|
pub fn register_query(
|
||||||
self: &Arc<Self>,
|
self: &Arc<Self>,
|
||||||
catalog: String,
|
catalog: String,
|
||||||
schemas: Vec<String>,
|
schemas: Vec<String>,
|
||||||
query: String,
|
query: String,
|
||||||
client: String,
|
client: String,
|
||||||
id: Option<u64>,
|
query_id: Option<ProcessId>,
|
||||||
) -> Ticket {
|
) -> Ticket {
|
||||||
let id = id.unwrap_or_else(|| self.next_id.fetch_add(1, Ordering::Relaxed));
|
let id = query_id.unwrap_or_else(|| self.next_id.fetch_add(1, Ordering::Relaxed));
|
||||||
let process = ProcessInfo {
|
let process = ProcessInfo {
|
||||||
id,
|
id,
|
||||||
catalog: catalog.clone(),
|
catalog: catalog.clone(),
|
||||||
@@ -68,53 +78,53 @@ impl ProcessManager {
|
|||||||
client,
|
client,
|
||||||
frontend: self.server_addr.clone(),
|
frontend: self.server_addr.clone(),
|
||||||
};
|
};
|
||||||
|
let cancellation_handle = Arc::new(CancellationHandle::default());
|
||||||
|
let cancellable_process = CancellableProcess::new(cancellation_handle.clone(), process);
|
||||||
|
|
||||||
self.catalogs
|
self.catalogs
|
||||||
.write()
|
.write()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.entry(catalog.clone())
|
.entry(catalog.clone())
|
||||||
.or_default()
|
.or_default()
|
||||||
.insert(id, process);
|
.insert(id, cancellable_process);
|
||||||
|
|
||||||
Ticket {
|
Ticket {
|
||||||
catalog,
|
catalog,
|
||||||
manager: self.clone(),
|
manager: self.clone(),
|
||||||
id,
|
id,
|
||||||
|
cancellation_handle,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates the next process id.
|
/// Generates the next process id.
|
||||||
pub fn next_id(&self) -> u64 {
|
pub fn next_id(&self) -> u32 {
|
||||||
self.next_id.fetch_add(1, Ordering::Relaxed)
|
self.next_id.fetch_add(1, Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// De-register a query from process list.
|
/// De-register a query from process list.
|
||||||
pub fn deregister_query(&self, catalog: String, id: u64) {
|
pub fn deregister_query(&self, catalog: String, id: ProcessId) {
|
||||||
if let Entry::Occupied(mut o) = self.catalogs.write().unwrap().entry(catalog) {
|
if let Entry::Occupied(mut o) = self.catalogs.write().unwrap().entry(catalog) {
|
||||||
let process = o.get_mut().remove(&id);
|
let process = o.get_mut().remove(&id);
|
||||||
debug!("Deregister process: {:?}", process);
|
debug!("Deregister process: {:?}", process);
|
||||||
if o.get_mut().is_empty() {
|
if o.get().is_empty() {
|
||||||
o.remove();
|
o.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deregister_all_queries(&self) {
|
|
||||||
self.catalogs.write().unwrap().clear();
|
|
||||||
info!("All queries on {} has been deregistered", self.server_addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// List local running processes in given catalog.
|
/// List local running processes in given catalog.
|
||||||
pub fn local_processes(&self, catalog: Option<&str>) -> error::Result<Vec<ProcessInfo>> {
|
pub fn local_processes(&self, catalog: Option<&str>) -> error::Result<Vec<ProcessInfo>> {
|
||||||
let catalogs = self.catalogs.read().unwrap();
|
let catalogs = self.catalogs.read().unwrap();
|
||||||
let result = if let Some(catalog) = catalog {
|
let result = if let Some(catalog) = catalog {
|
||||||
if let Some(catalogs) = catalogs.get(catalog) {
|
if let Some(catalogs) = catalogs.get(catalog) {
|
||||||
catalogs.values().cloned().collect()
|
catalogs.values().map(|p| p.process.clone()).collect()
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
catalogs
|
catalogs
|
||||||
.values()
|
.values()
|
||||||
.flat_map(|v| v.values().cloned())
|
.flat_map(|v| v.values().map(|p| p.process.clone()))
|
||||||
.collect()
|
.collect()
|
||||||
};
|
};
|
||||||
Ok(result)
|
Ok(result)
|
||||||
@@ -129,27 +139,90 @@ impl ProcessManager {
|
|||||||
let frontends = remote_frontend_selector
|
let frontends = remote_frontend_selector
|
||||||
.select(|node| node.peer.addr != self.server_addr)
|
.select(|node| node.peer.addr != self.server_addr)
|
||||||
.await
|
.await
|
||||||
.context(error::ListProcessSnafu)?;
|
.context(error::InvokeFrontendSnafu)?;
|
||||||
for mut f in frontends {
|
for mut f in frontends {
|
||||||
processes.extend(
|
let result = f
|
||||||
f.list_process(ListProcessRequest {
|
.list_process(ListProcessRequest {
|
||||||
catalog: catalog.unwrap_or_default().to_string(),
|
catalog: catalog.unwrap_or_default().to_string(),
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.context(error::ListProcessSnafu)?
|
.context(error::InvokeFrontendSnafu);
|
||||||
.processes,
|
match result {
|
||||||
);
|
Ok(resp) => {
|
||||||
|
processes.extend(resp.processes);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
warn!(e; "Skipping failing node: {:?}", f)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
processes.extend(self.local_processes(catalog)?);
|
processes.extend(self.local_processes(catalog)?);
|
||||||
Ok(processes)
|
Ok(processes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Kills query with provided catalog and id.
|
||||||
|
pub async fn kill_process(
|
||||||
|
&self,
|
||||||
|
server_addr: String,
|
||||||
|
catalog: String,
|
||||||
|
id: ProcessId,
|
||||||
|
) -> error::Result<bool> {
|
||||||
|
if server_addr == self.server_addr {
|
||||||
|
self.kill_local_process(catalog, id).await
|
||||||
|
} else {
|
||||||
|
let mut nodes = self
|
||||||
|
.frontend_selector
|
||||||
|
.as_ref()
|
||||||
|
.context(error::MetaClientMissingSnafu)?
|
||||||
|
.select(|node| node.peer.addr == server_addr)
|
||||||
|
.await
|
||||||
|
.context(error::InvokeFrontendSnafu)?;
|
||||||
|
ensure!(
|
||||||
|
!nodes.is_empty(),
|
||||||
|
error::FrontendNotFoundSnafu { addr: server_addr }
|
||||||
|
);
|
||||||
|
|
||||||
|
let request = KillProcessRequest {
|
||||||
|
server_addr,
|
||||||
|
catalog,
|
||||||
|
process_id: id,
|
||||||
|
};
|
||||||
|
nodes[0]
|
||||||
|
.kill_process(request)
|
||||||
|
.await
|
||||||
|
.context(error::InvokeFrontendSnafu)?;
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Kills local query with provided catalog and id.
|
||||||
|
pub async fn kill_local_process(&self, catalog: String, id: ProcessId) -> error::Result<bool> {
|
||||||
|
if let Some(catalogs) = self.catalogs.write().unwrap().get_mut(&catalog) {
|
||||||
|
if let Some(process) = catalogs.remove(&id) {
|
||||||
|
process.handle.cancel();
|
||||||
|
info!(
|
||||||
|
"Killed process, catalog: {}, id: {:?}",
|
||||||
|
process.process.catalog, process.process.id
|
||||||
|
);
|
||||||
|
PROCESS_KILL_COUNT.with_label_values(&[&catalog]).inc();
|
||||||
|
Ok(true)
|
||||||
|
} else {
|
||||||
|
debug!("Failed to kill process, id not found: {}", id);
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
debug!("Failed to kill process, catalog not found: {}", catalog);
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Ticket {
|
pub struct Ticket {
|
||||||
pub(crate) catalog: String,
|
pub(crate) catalog: String,
|
||||||
pub(crate) manager: ProcessManagerRef,
|
pub(crate) manager: ProcessManagerRef,
|
||||||
pub(crate) id: u64,
|
pub(crate) id: ProcessId,
|
||||||
|
pub cancellation_handle: Arc<CancellationHandle>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Ticket {
|
impl Drop for Ticket {
|
||||||
@@ -159,6 +232,37 @@ impl Drop for Ticket {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct CancellableProcess {
|
||||||
|
handle: Arc<CancellationHandle>,
|
||||||
|
process: ProcessInfo,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for CancellableProcess {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
PROCESS_LIST_COUNT
|
||||||
|
.with_label_values(&[&self.process.catalog])
|
||||||
|
.dec();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CancellableProcess {
|
||||||
|
fn new(handle: Arc<CancellationHandle>, process: ProcessInfo) -> Self {
|
||||||
|
PROCESS_LIST_COUNT
|
||||||
|
.with_label_values(&[&process.catalog])
|
||||||
|
.inc();
|
||||||
|
Self { handle, process }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for CancellableProcess {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("CancellableProcess")
|
||||||
|
.field("cancelled", &self.handle.is_cancelled())
|
||||||
|
.field("process", &self.process)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -185,4 +289,212 @@ mod tests {
|
|||||||
drop(ticket);
|
drop(ticket);
|
||||||
assert_eq!(process_manager.local_processes(None).unwrap().len(), 0);
|
assert_eq!(process_manager.local_processes(None).unwrap().len(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_register_query_with_custom_id() {
|
||||||
|
let process_manager = Arc::new(ProcessManager::new("127.0.0.1:8000".to_string(), None));
|
||||||
|
let custom_id = 12345;
|
||||||
|
|
||||||
|
let ticket = process_manager.clone().register_query(
|
||||||
|
"public".to_string(),
|
||||||
|
vec!["test".to_string()],
|
||||||
|
"SELECT * FROM table".to_string(),
|
||||||
|
"client1".to_string(),
|
||||||
|
Some(custom_id),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(ticket.id, custom_id);
|
||||||
|
|
||||||
|
let running_processes = process_manager.local_processes(None).unwrap();
|
||||||
|
assert_eq!(running_processes.len(), 1);
|
||||||
|
assert_eq!(running_processes[0].id, custom_id);
|
||||||
|
assert_eq!(&running_processes[0].client, "client1");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_multiple_queries_same_catalog() {
|
||||||
|
let process_manager = Arc::new(ProcessManager::new("127.0.0.1:8000".to_string(), None));
|
||||||
|
|
||||||
|
let ticket1 = process_manager.clone().register_query(
|
||||||
|
"public".to_string(),
|
||||||
|
vec!["schema1".to_string()],
|
||||||
|
"SELECT * FROM table1".to_string(),
|
||||||
|
"client1".to_string(),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
let ticket2 = process_manager.clone().register_query(
|
||||||
|
"public".to_string(),
|
||||||
|
vec!["schema2".to_string()],
|
||||||
|
"SELECT * FROM table2".to_string(),
|
||||||
|
"client2".to_string(),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
let running_processes = process_manager.local_processes(Some("public")).unwrap();
|
||||||
|
assert_eq!(running_processes.len(), 2);
|
||||||
|
|
||||||
|
// Verify both processes are present
|
||||||
|
let ids: Vec<u32> = running_processes.iter().map(|p| p.id).collect();
|
||||||
|
assert!(ids.contains(&ticket1.id));
|
||||||
|
assert!(ids.contains(&ticket2.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_multiple_catalogs() {
|
||||||
|
let process_manager = Arc::new(ProcessManager::new("127.0.0.1:8000".to_string(), None));
|
||||||
|
|
||||||
|
let _ticket1 = process_manager.clone().register_query(
|
||||||
|
"catalog1".to_string(),
|
||||||
|
vec!["schema1".to_string()],
|
||||||
|
"SELECT * FROM table1".to_string(),
|
||||||
|
"client1".to_string(),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
let _ticket2 = process_manager.clone().register_query(
|
||||||
|
"catalog2".to_string(),
|
||||||
|
vec!["schema2".to_string()],
|
||||||
|
"SELECT * FROM table2".to_string(),
|
||||||
|
"client2".to_string(),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test listing processes for specific catalog
|
||||||
|
let catalog1_processes = process_manager.local_processes(Some("catalog1")).unwrap();
|
||||||
|
assert_eq!(catalog1_processes.len(), 1);
|
||||||
|
assert_eq!(&catalog1_processes[0].catalog, "catalog1");
|
||||||
|
|
||||||
|
let catalog2_processes = process_manager.local_processes(Some("catalog2")).unwrap();
|
||||||
|
assert_eq!(catalog2_processes.len(), 1);
|
||||||
|
assert_eq!(&catalog2_processes[0].catalog, "catalog2");
|
||||||
|
|
||||||
|
// Test listing all processes
|
||||||
|
let all_processes = process_manager.local_processes(None).unwrap();
|
||||||
|
assert_eq!(all_processes.len(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_deregister_query() {
|
||||||
|
let process_manager = Arc::new(ProcessManager::new("127.0.0.1:8000".to_string(), None));
|
||||||
|
|
||||||
|
let ticket = process_manager.clone().register_query(
|
||||||
|
"public".to_string(),
|
||||||
|
vec!["test".to_string()],
|
||||||
|
"SELECT * FROM table".to_string(),
|
||||||
|
"client1".to_string(),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
assert_eq!(process_manager.local_processes(None).unwrap().len(), 1);
|
||||||
|
process_manager.deregister_query("public".to_string(), ticket.id);
|
||||||
|
assert_eq!(process_manager.local_processes(None).unwrap().len(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_cancellation_handle() {
|
||||||
|
let process_manager = Arc::new(ProcessManager::new("127.0.0.1:8000".to_string(), None));
|
||||||
|
|
||||||
|
let ticket = process_manager.clone().register_query(
|
||||||
|
"public".to_string(),
|
||||||
|
vec!["test".to_string()],
|
||||||
|
"SELECT * FROM table".to_string(),
|
||||||
|
"client1".to_string(),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(!ticket.cancellation_handle.is_cancelled());
|
||||||
|
ticket.cancellation_handle.cancel();
|
||||||
|
assert!(ticket.cancellation_handle.is_cancelled());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_kill_local_process() {
|
||||||
|
let process_manager = Arc::new(ProcessManager::new("127.0.0.1:8000".to_string(), None));
|
||||||
|
|
||||||
|
let ticket = process_manager.clone().register_query(
|
||||||
|
"public".to_string(),
|
||||||
|
vec!["test".to_string()],
|
||||||
|
"SELECT * FROM table".to_string(),
|
||||||
|
"client1".to_string(),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
assert!(!ticket.cancellation_handle.is_cancelled());
|
||||||
|
let killed = process_manager
|
||||||
|
.kill_process(
|
||||||
|
"127.0.0.1:8000".to_string(),
|
||||||
|
"public".to_string(),
|
||||||
|
ticket.id,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert!(killed);
|
||||||
|
assert_eq!(process_manager.local_processes(None).unwrap().len(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_kill_nonexistent_process() {
|
||||||
|
let process_manager = Arc::new(ProcessManager::new("127.0.0.1:8000".to_string(), None));
|
||||||
|
let killed = process_manager
|
||||||
|
.kill_process("127.0.0.1:8000".to_string(), "public".to_string(), 999)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert!(!killed);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_kill_process_nonexistent_catalog() {
|
||||||
|
let process_manager = Arc::new(ProcessManager::new("127.0.0.1:8000".to_string(), None));
|
||||||
|
let killed = process_manager
|
||||||
|
.kill_process("127.0.0.1:8000".to_string(), "nonexistent".to_string(), 1)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert!(!killed);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_process_info_fields() {
|
||||||
|
let process_manager = Arc::new(ProcessManager::new("127.0.0.1:8000".to_string(), None));
|
||||||
|
|
||||||
|
let _ticket = process_manager.clone().register_query(
|
||||||
|
"test_catalog".to_string(),
|
||||||
|
vec!["schema1".to_string(), "schema2".to_string()],
|
||||||
|
"SELECT COUNT(*) FROM users WHERE age > 18".to_string(),
|
||||||
|
"test_client".to_string(),
|
||||||
|
Some(42),
|
||||||
|
);
|
||||||
|
|
||||||
|
let processes = process_manager.local_processes(None).unwrap();
|
||||||
|
assert_eq!(processes.len(), 1);
|
||||||
|
|
||||||
|
let process = &processes[0];
|
||||||
|
assert_eq!(process.id, 42);
|
||||||
|
assert_eq!(&process.catalog, "test_catalog");
|
||||||
|
assert_eq!(process.schemas, vec!["schema1", "schema2"]);
|
||||||
|
assert_eq!(&process.query, "SELECT COUNT(*) FROM users WHERE age > 18");
|
||||||
|
assert_eq!(&process.client, "test_client");
|
||||||
|
assert_eq!(&process.frontend, "127.0.0.1:8000");
|
||||||
|
assert!(process.start_timestamp > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_ticket_drop_deregisters_process() {
|
||||||
|
let process_manager = Arc::new(ProcessManager::new("127.0.0.1:8000".to_string(), None));
|
||||||
|
|
||||||
|
{
|
||||||
|
let _ticket = process_manager.clone().register_query(
|
||||||
|
"public".to_string(),
|
||||||
|
vec!["test".to_string()],
|
||||||
|
"SELECT * FROM table".to_string(),
|
||||||
|
"client1".to_string(),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Process should be registered
|
||||||
|
assert_eq!(process_manager.local_processes(None).unwrap().len(), 1);
|
||||||
|
} // ticket goes out of scope here
|
||||||
|
|
||||||
|
// Process should be automatically deregistered
|
||||||
|
assert_eq!(process_manager.local_processes(None).unwrap().len(), 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ mysql_kvbackend = ["common-meta/mysql_kvbackend", "meta-srv/mysql_kvbackend"]
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
async-stream.workspace = true
|
||||||
async-trait.workspace = true
|
async-trait.workspace = true
|
||||||
auth.workspace = true
|
auth.workspace = true
|
||||||
base64.workspace = true
|
base64.workspace = true
|
||||||
@@ -50,6 +51,7 @@ meta-client.workspace = true
|
|||||||
meta-srv.workspace = true
|
meta-srv.workspace = true
|
||||||
nu-ansi-term = "0.46"
|
nu-ansi-term = "0.46"
|
||||||
object-store.workspace = true
|
object-store.workspace = true
|
||||||
|
operator.workspace = true
|
||||||
query.workspace = true
|
query.workspace = true
|
||||||
rand.workspace = true
|
rand.workspace = true
|
||||||
reqwest.workspace = true
|
reqwest.workspace = true
|
||||||
@@ -65,6 +67,7 @@ tokio.workspace = true
|
|||||||
tracing-appender.workspace = true
|
tracing-appender.workspace = true
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
common-meta = { workspace = true, features = ["testing"] }
|
||||||
common-version.workspace = true
|
common-version.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
tempfile.workspace = true
|
tempfile.workspace = true
|
||||||
|
|||||||
@@ -17,8 +17,10 @@ use std::any::Any;
|
|||||||
use common_error::ext::{BoxedError, ErrorExt};
|
use common_error::ext::{BoxedError, ErrorExt};
|
||||||
use common_error::status_code::StatusCode;
|
use common_error::status_code::StatusCode;
|
||||||
use common_macro::stack_trace_debug;
|
use common_macro::stack_trace_debug;
|
||||||
|
use common_meta::peer::Peer;
|
||||||
use object_store::Error as ObjectStoreError;
|
use object_store::Error as ObjectStoreError;
|
||||||
use snafu::{Location, Snafu};
|
use snafu::{Location, Snafu};
|
||||||
|
use store_api::storage::TableId;
|
||||||
|
|
||||||
#[derive(Snafu)]
|
#[derive(Snafu)]
|
||||||
#[snafu(visibility(pub))]
|
#[snafu(visibility(pub))]
|
||||||
@@ -73,6 +75,20 @@ pub enum Error {
|
|||||||
source: common_meta::error::Error,
|
source: common_meta::error::Error,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Failed to get table metadata"))]
|
||||||
|
TableMetadata {
|
||||||
|
#[snafu(implicit)]
|
||||||
|
location: Location,
|
||||||
|
source: common_meta::error::Error,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Unexpected error: {}", msg))]
|
||||||
|
Unexpected {
|
||||||
|
msg: String,
|
||||||
|
#[snafu(implicit)]
|
||||||
|
location: Location,
|
||||||
|
},
|
||||||
|
|
||||||
#[snafu(display("Missing config, msg: {}", msg))]
|
#[snafu(display("Missing config, msg: {}", msg))]
|
||||||
MissingConfig {
|
MissingConfig {
|
||||||
msg: String,
|
msg: String,
|
||||||
@@ -222,6 +238,13 @@ pub enum Error {
|
|||||||
location: Location,
|
location: Location,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Table not found: {table_id}"))]
|
||||||
|
TableNotFound {
|
||||||
|
table_id: TableId,
|
||||||
|
#[snafu(implicit)]
|
||||||
|
location: Location,
|
||||||
|
},
|
||||||
|
|
||||||
#[snafu(display("OpenDAL operator failed"))]
|
#[snafu(display("OpenDAL operator failed"))]
|
||||||
OpenDal {
|
OpenDal {
|
||||||
#[snafu(implicit)]
|
#[snafu(implicit)]
|
||||||
@@ -267,6 +290,29 @@ pub enum Error {
|
|||||||
#[snafu(implicit)]
|
#[snafu(implicit)]
|
||||||
location: Location,
|
location: Location,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Failed to init backend"))]
|
||||||
|
InitBackend {
|
||||||
|
#[snafu(implicit)]
|
||||||
|
location: Location,
|
||||||
|
#[snafu(source)]
|
||||||
|
error: ObjectStoreError,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Covert column schemas to defs failed"))]
|
||||||
|
CovertColumnSchemasToDefs {
|
||||||
|
#[snafu(implicit)]
|
||||||
|
location: Location,
|
||||||
|
source: operator::error::Error,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Failed to send request to datanode: {}", peer))]
|
||||||
|
SendRequestToDatanode {
|
||||||
|
peer: Peer,
|
||||||
|
#[snafu(implicit)]
|
||||||
|
location: Location,
|
||||||
|
source: common_meta::error::Error,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
@@ -274,9 +320,9 @@ pub type Result<T> = std::result::Result<T, Error>;
|
|||||||
impl ErrorExt for Error {
|
impl ErrorExt for Error {
|
||||||
fn status_code(&self) -> StatusCode {
|
fn status_code(&self) -> StatusCode {
|
||||||
match self {
|
match self {
|
||||||
Error::InitMetadata { source, .. } | Error::InitDdlManager { source, .. } => {
|
Error::InitMetadata { source, .. }
|
||||||
source.status_code()
|
| Error::InitDdlManager { source, .. }
|
||||||
}
|
| Error::TableMetadata { source, .. } => source.status_code(),
|
||||||
|
|
||||||
Error::MissingConfig { .. }
|
Error::MissingConfig { .. }
|
||||||
| Error::LoadLayeredConfig { .. }
|
| Error::LoadLayeredConfig { .. }
|
||||||
@@ -290,6 +336,9 @@ impl ErrorExt for Error {
|
|||||||
| Error::InvalidArguments { .. }
|
| Error::InvalidArguments { .. }
|
||||||
| Error::ParseProxyOpts { .. } => StatusCode::InvalidArguments,
|
| Error::ParseProxyOpts { .. } => StatusCode::InvalidArguments,
|
||||||
|
|
||||||
|
Error::CovertColumnSchemasToDefs { source, .. } => source.status_code(),
|
||||||
|
Error::SendRequestToDatanode { source, .. } => source.status_code(),
|
||||||
|
|
||||||
Error::StartProcedureManager { source, .. }
|
Error::StartProcedureManager { source, .. }
|
||||||
| Error::StopProcedureManager { source, .. } => source.status_code(),
|
| Error::StopProcedureManager { source, .. } => source.status_code(),
|
||||||
Error::StartWalOptionsAllocator { source, .. } => source.status_code(),
|
Error::StartWalOptionsAllocator { source, .. } => source.status_code(),
|
||||||
@@ -297,6 +346,7 @@ impl ErrorExt for Error {
|
|||||||
Error::ParseSql { source, .. } | Error::PlanStatement { source, .. } => {
|
Error::ParseSql { source, .. } | Error::PlanStatement { source, .. } => {
|
||||||
source.status_code()
|
source.status_code()
|
||||||
}
|
}
|
||||||
|
Error::Unexpected { .. } => StatusCode::Unexpected,
|
||||||
|
|
||||||
Error::SerdeJson { .. }
|
Error::SerdeJson { .. }
|
||||||
| Error::FileIo { .. }
|
| Error::FileIo { .. }
|
||||||
@@ -305,7 +355,7 @@ impl ErrorExt for Error {
|
|||||||
| Error::BuildClient { .. } => StatusCode::Unexpected,
|
| Error::BuildClient { .. } => StatusCode::Unexpected,
|
||||||
|
|
||||||
Error::Other { source, .. } => source.status_code(),
|
Error::Other { source, .. } => source.status_code(),
|
||||||
Error::OpenDal { .. } => StatusCode::Internal,
|
Error::OpenDal { .. } | Error::InitBackend { .. } => StatusCode::Internal,
|
||||||
Error::S3ConfigNotSet { .. }
|
Error::S3ConfigNotSet { .. }
|
||||||
| Error::OutputDirNotSet { .. }
|
| Error::OutputDirNotSet { .. }
|
||||||
| Error::EmptyStoreAddrs { .. } => StatusCode::InvalidArguments,
|
| Error::EmptyStoreAddrs { .. } => StatusCode::InvalidArguments,
|
||||||
@@ -314,6 +364,7 @@ impl ErrorExt for Error {
|
|||||||
|
|
||||||
Error::CacheRequired { .. } | Error::BuildCacheRegistry { .. } => StatusCode::Internal,
|
Error::CacheRequired { .. } | Error::BuildCacheRegistry { .. } => StatusCode::Internal,
|
||||||
Error::MetaClientInit { source, .. } => source.status_code(),
|
Error::MetaClientInit { source, .. } => source.status_code(),
|
||||||
|
Error::TableNotFound { .. } => StatusCode::TableNotFound,
|
||||||
Error::SchemaNotFound { .. } => StatusCode::DatabaseNotFound,
|
Error::SchemaNotFound { .. } => StatusCode::DatabaseNotFound,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,29 +14,39 @@
|
|||||||
|
|
||||||
mod common;
|
mod common;
|
||||||
mod control;
|
mod control;
|
||||||
|
mod repair;
|
||||||
mod snapshot;
|
mod snapshot;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
use clap::Subcommand;
|
use clap::Subcommand;
|
||||||
use common_error::ext::BoxedError;
|
use common_error::ext::BoxedError;
|
||||||
|
|
||||||
use crate::metadata::control::ControlCommand;
|
use crate::metadata::control::{DelCommand, GetCommand};
|
||||||
|
use crate::metadata::repair::RepairLogicalTablesCommand;
|
||||||
use crate::metadata::snapshot::SnapshotCommand;
|
use crate::metadata::snapshot::SnapshotCommand;
|
||||||
use crate::Tool;
|
use crate::Tool;
|
||||||
|
|
||||||
/// Command for managing metadata operations, including saving metadata snapshots and restoring metadata from snapshots.
|
/// Command for managing metadata operations,
|
||||||
|
/// including saving and restoring metadata snapshots,
|
||||||
|
/// controlling metadata operations, and diagnosing and repairing metadata.
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
pub enum MetadataCommand {
|
pub enum MetadataCommand {
|
||||||
#[clap(subcommand)]
|
#[clap(subcommand)]
|
||||||
Snapshot(SnapshotCommand),
|
Snapshot(SnapshotCommand),
|
||||||
#[clap(subcommand)]
|
#[clap(subcommand)]
|
||||||
Control(ControlCommand),
|
Get(GetCommand),
|
||||||
|
#[clap(subcommand)]
|
||||||
|
Del(DelCommand),
|
||||||
|
RepairLogicalTables(RepairLogicalTablesCommand),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MetadataCommand {
|
impl MetadataCommand {
|
||||||
pub async fn build(&self) -> Result<Box<dyn Tool>, BoxedError> {
|
pub async fn build(&self) -> Result<Box<dyn Tool>, BoxedError> {
|
||||||
match self {
|
match self {
|
||||||
MetadataCommand::Snapshot(cmd) => cmd.build().await,
|
MetadataCommand::Snapshot(cmd) => cmd.build().await,
|
||||||
MetadataCommand::Control(cmd) => cmd.build().await,
|
MetadataCommand::RepairLogicalTables(cmd) => cmd.build().await,
|
||||||
|
MetadataCommand::Get(cmd) => cmd.build().await,
|
||||||
|
MetadataCommand::Del(cmd) => cmd.build().await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,27 +12,11 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
mod del;
|
||||||
mod get;
|
mod get;
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test_utils;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
use clap::Subcommand;
|
pub(crate) use del::DelCommand;
|
||||||
use common_error::ext::BoxedError;
|
pub(crate) use get::GetCommand;
|
||||||
use get::GetCommand;
|
|
||||||
|
|
||||||
use crate::Tool;
|
|
||||||
|
|
||||||
/// Subcommand for metadata control.
|
|
||||||
#[derive(Subcommand)]
|
|
||||||
pub enum ControlCommand {
|
|
||||||
/// Get the metadata from the metasrv.
|
|
||||||
#[clap(subcommand)]
|
|
||||||
Get(GetCommand),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ControlCommand {
|
|
||||||
pub async fn build(&self) -> Result<Box<dyn Tool>, BoxedError> {
|
|
||||||
match self {
|
|
||||||
ControlCommand::Get(cmd) => cmd.build().await,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
42
src/cli/src/metadata/control/del.rs
Normal file
42
src/cli/src/metadata/control/del.rs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
// Copyright 2023 Greptime Team
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
mod key;
|
||||||
|
mod table;
|
||||||
|
|
||||||
|
use clap::Subcommand;
|
||||||
|
use common_error::ext::BoxedError;
|
||||||
|
|
||||||
|
use crate::metadata::control::del::key::DelKeyCommand;
|
||||||
|
use crate::metadata::control::del::table::DelTableCommand;
|
||||||
|
use crate::Tool;
|
||||||
|
|
||||||
|
/// The prefix of the tombstone keys.
|
||||||
|
pub(crate) const CLI_TOMBSTONE_PREFIX: &str = "__cli_tombstone/";
|
||||||
|
|
||||||
|
/// Subcommand for deleting metadata from the metadata store.
|
||||||
|
#[derive(Subcommand)]
|
||||||
|
pub enum DelCommand {
|
||||||
|
Key(DelKeyCommand),
|
||||||
|
Table(DelTableCommand),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DelCommand {
|
||||||
|
pub async fn build(&self) -> Result<Box<dyn Tool>, BoxedError> {
|
||||||
|
match self {
|
||||||
|
DelCommand::Key(cmd) => cmd.build().await,
|
||||||
|
DelCommand::Table(cmd) => cmd.build().await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
132
src/cli/src/metadata/control/del/key.rs
Normal file
132
src/cli/src/metadata/control/del/key.rs
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
// Copyright 2023 Greptime Team
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use clap::Parser;
|
||||||
|
use common_error::ext::BoxedError;
|
||||||
|
use common_meta::key::tombstone::TombstoneManager;
|
||||||
|
use common_meta::kv_backend::KvBackendRef;
|
||||||
|
use common_meta::rpc::store::RangeRequest;
|
||||||
|
|
||||||
|
use crate::metadata::common::StoreConfig;
|
||||||
|
use crate::metadata::control::del::CLI_TOMBSTONE_PREFIX;
|
||||||
|
use crate::Tool;
|
||||||
|
|
||||||
|
/// Delete key-value pairs logically from the metadata store.
|
||||||
|
#[derive(Debug, Default, Parser)]
|
||||||
|
pub struct DelKeyCommand {
|
||||||
|
/// The key to delete from the metadata store.
|
||||||
|
key: String,
|
||||||
|
|
||||||
|
/// Delete key-value pairs with the given prefix.
|
||||||
|
#[clap(long)]
|
||||||
|
prefix: bool,
|
||||||
|
|
||||||
|
#[clap(flatten)]
|
||||||
|
store: StoreConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DelKeyCommand {
|
||||||
|
pub async fn build(&self) -> Result<Box<dyn Tool>, BoxedError> {
|
||||||
|
let kv_backend = self.store.build().await?;
|
||||||
|
Ok(Box::new(DelKeyTool {
|
||||||
|
key: self.key.to_string(),
|
||||||
|
prefix: self.prefix,
|
||||||
|
key_deleter: KeyDeleter::new(kv_backend),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct KeyDeleter {
|
||||||
|
kv_backend: KvBackendRef,
|
||||||
|
tombstone_manager: TombstoneManager,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KeyDeleter {
|
||||||
|
fn new(kv_backend: KvBackendRef) -> Self {
|
||||||
|
Self {
|
||||||
|
kv_backend: kv_backend.clone(),
|
||||||
|
tombstone_manager: TombstoneManager::new_with_prefix(kv_backend, CLI_TOMBSTONE_PREFIX),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete(&self, key: &str, prefix: bool) -> Result<usize, BoxedError> {
|
||||||
|
let mut req = RangeRequest::default().with_keys_only();
|
||||||
|
if prefix {
|
||||||
|
req = req.with_prefix(key.as_bytes());
|
||||||
|
} else {
|
||||||
|
req = req.with_key(key.as_bytes());
|
||||||
|
}
|
||||||
|
let resp = self.kv_backend.range(req).await.map_err(BoxedError::new)?;
|
||||||
|
let keys = resp.kvs.iter().map(|kv| kv.key.clone()).collect::<Vec<_>>();
|
||||||
|
self.tombstone_manager
|
||||||
|
.create(keys)
|
||||||
|
.await
|
||||||
|
.map_err(BoxedError::new)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DelKeyTool {
|
||||||
|
key: String,
|
||||||
|
prefix: bool,
|
||||||
|
key_deleter: KeyDeleter,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Tool for DelKeyTool {
|
||||||
|
async fn do_work(&self) -> Result<(), BoxedError> {
|
||||||
|
let deleted = self.key_deleter.delete(&self.key, self.prefix).await?;
|
||||||
|
// Print the number of deleted keys.
|
||||||
|
println!("{}", deleted);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use common_meta::kv_backend::chroot::ChrootKvBackend;
|
||||||
|
use common_meta::kv_backend::memory::MemoryKvBackend;
|
||||||
|
use common_meta::kv_backend::{KvBackend, KvBackendRef};
|
||||||
|
use common_meta::rpc::store::RangeRequest;
|
||||||
|
|
||||||
|
use crate::metadata::control::del::key::KeyDeleter;
|
||||||
|
use crate::metadata::control::del::CLI_TOMBSTONE_PREFIX;
|
||||||
|
use crate::metadata::control::test_utils::put_key;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_delete_keys() {
|
||||||
|
let kv_backend = Arc::new(MemoryKvBackend::new()) as KvBackendRef;
|
||||||
|
let key_deleter = KeyDeleter::new(kv_backend.clone());
|
||||||
|
put_key(&kv_backend, "foo", "bar").await;
|
||||||
|
put_key(&kv_backend, "foo/bar", "baz").await;
|
||||||
|
put_key(&kv_backend, "foo/baz", "qux").await;
|
||||||
|
let deleted = key_deleter.delete("foo", true).await.unwrap();
|
||||||
|
assert_eq!(deleted, 3);
|
||||||
|
let deleted = key_deleter.delete("foo/bar", false).await.unwrap();
|
||||||
|
assert_eq!(deleted, 0);
|
||||||
|
|
||||||
|
let chroot = ChrootKvBackend::new(CLI_TOMBSTONE_PREFIX.as_bytes().to_vec(), kv_backend);
|
||||||
|
let req = RangeRequest::default().with_prefix(b"foo");
|
||||||
|
let resp = chroot.range(req).await.unwrap();
|
||||||
|
assert_eq!(resp.kvs.len(), 3);
|
||||||
|
assert_eq!(resp.kvs[0].key, b"foo");
|
||||||
|
assert_eq!(resp.kvs[0].value, b"bar");
|
||||||
|
assert_eq!(resp.kvs[1].key, b"foo/bar");
|
||||||
|
assert_eq!(resp.kvs[1].value, b"baz");
|
||||||
|
assert_eq!(resp.kvs[2].key, b"foo/baz");
|
||||||
|
assert_eq!(resp.kvs[2].value, b"qux");
|
||||||
|
}
|
||||||
|
}
|
||||||
235
src/cli/src/metadata/control/del/table.rs
Normal file
235
src/cli/src/metadata/control/del/table.rs
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
// Copyright 2023 Greptime Team
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use clap::Parser;
|
||||||
|
use client::{DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME};
|
||||||
|
use common_catalog::format_full_table_name;
|
||||||
|
use common_error::ext::BoxedError;
|
||||||
|
use common_meta::ddl::utils::get_region_wal_options;
|
||||||
|
use common_meta::key::table_name::TableNameManager;
|
||||||
|
use common_meta::key::TableMetadataManager;
|
||||||
|
use common_meta::kv_backend::KvBackendRef;
|
||||||
|
use store_api::storage::TableId;
|
||||||
|
|
||||||
|
use crate::error::{InvalidArgumentsSnafu, TableNotFoundSnafu};
|
||||||
|
use crate::metadata::common::StoreConfig;
|
||||||
|
use crate::metadata::control::del::CLI_TOMBSTONE_PREFIX;
|
||||||
|
use crate::metadata::control::utils::get_table_id_by_name;
|
||||||
|
use crate::Tool;
|
||||||
|
|
||||||
|
/// Delete table metadata logically from the metadata store.
|
||||||
|
#[derive(Debug, Default, Parser)]
|
||||||
|
pub struct DelTableCommand {
|
||||||
|
/// The table id to delete from the metadata store.
|
||||||
|
#[clap(long)]
|
||||||
|
table_id: Option<u32>,
|
||||||
|
|
||||||
|
/// The table name to delete from the metadata store.
|
||||||
|
#[clap(long)]
|
||||||
|
table_name: Option<String>,
|
||||||
|
|
||||||
|
/// The schema name of the table.
|
||||||
|
#[clap(long, default_value = DEFAULT_SCHEMA_NAME)]
|
||||||
|
schema_name: String,
|
||||||
|
|
||||||
|
/// The catalog name of the table.
|
||||||
|
#[clap(long, default_value = DEFAULT_CATALOG_NAME)]
|
||||||
|
catalog_name: String,
|
||||||
|
|
||||||
|
#[clap(flatten)]
|
||||||
|
store: StoreConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DelTableCommand {
|
||||||
|
fn validate(&self) -> Result<(), BoxedError> {
|
||||||
|
if matches!(
|
||||||
|
(&self.table_id, &self.table_name),
|
||||||
|
(Some(_), Some(_)) | (None, None)
|
||||||
|
) {
|
||||||
|
return Err(BoxedError::new(
|
||||||
|
InvalidArgumentsSnafu {
|
||||||
|
msg: "You must specify either --table-id or --table-name.",
|
||||||
|
}
|
||||||
|
.build(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DelTableCommand {
|
||||||
|
pub async fn build(&self) -> Result<Box<dyn Tool>, BoxedError> {
|
||||||
|
self.validate()?;
|
||||||
|
let kv_backend = self.store.build().await?;
|
||||||
|
Ok(Box::new(DelTableTool {
|
||||||
|
table_id: self.table_id,
|
||||||
|
table_name: self.table_name.clone(),
|
||||||
|
schema_name: self.schema_name.clone(),
|
||||||
|
catalog_name: self.catalog_name.clone(),
|
||||||
|
table_name_manager: TableNameManager::new(kv_backend.clone()),
|
||||||
|
table_metadata_deleter: TableMetadataDeleter::new(kv_backend),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DelTableTool {
|
||||||
|
table_id: Option<u32>,
|
||||||
|
table_name: Option<String>,
|
||||||
|
schema_name: String,
|
||||||
|
catalog_name: String,
|
||||||
|
table_name_manager: TableNameManager,
|
||||||
|
table_metadata_deleter: TableMetadataDeleter,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Tool for DelTableTool {
|
||||||
|
async fn do_work(&self) -> Result<(), BoxedError> {
|
||||||
|
let table_id = if let Some(table_name) = &self.table_name {
|
||||||
|
let catalog_name = &self.catalog_name;
|
||||||
|
let schema_name = &self.schema_name;
|
||||||
|
|
||||||
|
let Some(table_id) = get_table_id_by_name(
|
||||||
|
&self.table_name_manager,
|
||||||
|
catalog_name,
|
||||||
|
schema_name,
|
||||||
|
table_name,
|
||||||
|
)
|
||||||
|
.await?
|
||||||
|
else {
|
||||||
|
println!(
|
||||||
|
"Table({}) not found",
|
||||||
|
format_full_table_name(catalog_name, schema_name, table_name)
|
||||||
|
);
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
table_id
|
||||||
|
} else {
|
||||||
|
// Safety: we have validated that table_id or table_name is not None
|
||||||
|
self.table_id.unwrap()
|
||||||
|
};
|
||||||
|
self.table_metadata_deleter.delete(table_id).await?;
|
||||||
|
println!("Table({}) deleted", table_id);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TableMetadataDeleter {
|
||||||
|
table_metadata_manager: TableMetadataManager,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TableMetadataDeleter {
|
||||||
|
fn new(kv_backend: KvBackendRef) -> Self {
|
||||||
|
Self {
|
||||||
|
table_metadata_manager: TableMetadataManager::new_with_custom_tombstone_prefix(
|
||||||
|
kv_backend,
|
||||||
|
CLI_TOMBSTONE_PREFIX,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete(&self, table_id: TableId) -> Result<(), BoxedError> {
|
||||||
|
let (table_info, table_route) = self
|
||||||
|
.table_metadata_manager
|
||||||
|
.get_full_table_info(table_id)
|
||||||
|
.await
|
||||||
|
.map_err(BoxedError::new)?;
|
||||||
|
let Some(table_info) = table_info else {
|
||||||
|
return Err(BoxedError::new(TableNotFoundSnafu { table_id }.build()));
|
||||||
|
};
|
||||||
|
let Some(table_route) = table_route else {
|
||||||
|
return Err(BoxedError::new(TableNotFoundSnafu { table_id }.build()));
|
||||||
|
};
|
||||||
|
let physical_table_id = self
|
||||||
|
.table_metadata_manager
|
||||||
|
.table_route_manager()
|
||||||
|
.get_physical_table_id(table_id)
|
||||||
|
.await
|
||||||
|
.map_err(BoxedError::new)?;
|
||||||
|
|
||||||
|
let table_name = table_info.table_name();
|
||||||
|
let region_wal_options = get_region_wal_options(
|
||||||
|
&self.table_metadata_manager,
|
||||||
|
&table_route,
|
||||||
|
physical_table_id,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(BoxedError::new)?;
|
||||||
|
|
||||||
|
self.table_metadata_manager
|
||||||
|
.delete_table_metadata(table_id, &table_name, &table_route, ®ion_wal_options)
|
||||||
|
.await
|
||||||
|
.map_err(BoxedError::new)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use common_error::ext::ErrorExt;
|
||||||
|
use common_error::status_code::StatusCode;
|
||||||
|
use common_meta::key::table_route::TableRouteValue;
|
||||||
|
use common_meta::key::TableMetadataManager;
|
||||||
|
use common_meta::kv_backend::chroot::ChrootKvBackend;
|
||||||
|
use common_meta::kv_backend::memory::MemoryKvBackend;
|
||||||
|
use common_meta::kv_backend::{KvBackend, KvBackendRef};
|
||||||
|
use common_meta::rpc::store::RangeRequest;
|
||||||
|
|
||||||
|
use crate::metadata::control::del::table::TableMetadataDeleter;
|
||||||
|
use crate::metadata::control::del::CLI_TOMBSTONE_PREFIX;
|
||||||
|
use crate::metadata::control::test_utils::prepare_physical_table_metadata;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_delete_table_not_found() {
|
||||||
|
let kv_backend = Arc::new(MemoryKvBackend::new()) as KvBackendRef;
|
||||||
|
|
||||||
|
let table_metadata_deleter = TableMetadataDeleter::new(kv_backend);
|
||||||
|
let table_id = 1;
|
||||||
|
let err = table_metadata_deleter.delete(table_id).await.unwrap_err();
|
||||||
|
assert_eq!(err.status_code(), StatusCode::TableNotFound);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_delete_table_metadata() {
|
||||||
|
let kv_backend = Arc::new(MemoryKvBackend::new());
|
||||||
|
let table_metadata_manager = TableMetadataManager::new(kv_backend.clone());
|
||||||
|
let table_id = 1024;
|
||||||
|
let (table_info, table_route) = prepare_physical_table_metadata("my_table", table_id).await;
|
||||||
|
table_metadata_manager
|
||||||
|
.create_table_metadata(
|
||||||
|
table_info,
|
||||||
|
TableRouteValue::Physical(table_route),
|
||||||
|
HashMap::new(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let total_keys = kv_backend.len();
|
||||||
|
assert!(total_keys > 0);
|
||||||
|
|
||||||
|
let table_metadata_deleter = TableMetadataDeleter::new(kv_backend.clone());
|
||||||
|
table_metadata_deleter.delete(table_id).await.unwrap();
|
||||||
|
|
||||||
|
// Check the tombstone keys are deleted
|
||||||
|
let chroot =
|
||||||
|
ChrootKvBackend::new(CLI_TOMBSTONE_PREFIX.as_bytes().to_vec(), kv_backend.clone());
|
||||||
|
let req = RangeRequest::default().with_range(vec![0], vec![0]);
|
||||||
|
let resp = chroot.range(req).await.unwrap();
|
||||||
|
assert_eq!(resp.kvs.len(), total_keys);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,7 +20,6 @@ use client::{DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME};
|
|||||||
use common_catalog::format_full_table_name;
|
use common_catalog::format_full_table_name;
|
||||||
use common_error::ext::BoxedError;
|
use common_error::ext::BoxedError;
|
||||||
use common_meta::key::table_info::TableInfoKey;
|
use common_meta::key::table_info::TableInfoKey;
|
||||||
use common_meta::key::table_name::TableNameKey;
|
|
||||||
use common_meta::key::table_route::TableRouteKey;
|
use common_meta::key::table_route::TableRouteKey;
|
||||||
use common_meta::key::TableMetadataManager;
|
use common_meta::key::TableMetadataManager;
|
||||||
use common_meta::kv_backend::KvBackendRef;
|
use common_meta::kv_backend::KvBackendRef;
|
||||||
@@ -30,10 +29,10 @@ use futures::TryStreamExt;
|
|||||||
|
|
||||||
use crate::error::InvalidArgumentsSnafu;
|
use crate::error::InvalidArgumentsSnafu;
|
||||||
use crate::metadata::common::StoreConfig;
|
use crate::metadata::common::StoreConfig;
|
||||||
use crate::metadata::control::utils::{decode_key_value, json_fromatter};
|
use crate::metadata::control::utils::{decode_key_value, get_table_id_by_name, json_fromatter};
|
||||||
use crate::Tool;
|
use crate::Tool;
|
||||||
|
|
||||||
/// Subcommand for get command.
|
/// Getting metadata from metadata store.
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
pub enum GetCommand {
|
pub enum GetCommand {
|
||||||
Key(GetKeyCommand),
|
Key(GetKeyCommand),
|
||||||
@@ -52,7 +51,7 @@ impl GetCommand {
|
|||||||
/// Get key-value pairs from the metadata store.
|
/// Get key-value pairs from the metadata store.
|
||||||
#[derive(Debug, Default, Parser)]
|
#[derive(Debug, Default, Parser)]
|
||||||
pub struct GetKeyCommand {
|
pub struct GetKeyCommand {
|
||||||
/// The key to get from the metadata store. If empty, returns all key-value pairs.
|
/// The key to get from the metadata store.
|
||||||
#[clap(default_value = "")]
|
#[clap(default_value = "")]
|
||||||
key: String,
|
key: String,
|
||||||
|
|
||||||
@@ -130,8 +129,12 @@ pub struct GetTableCommand {
|
|||||||
table_name: Option<String>,
|
table_name: Option<String>,
|
||||||
|
|
||||||
/// The schema name of the table.
|
/// The schema name of the table.
|
||||||
#[clap(long)]
|
#[clap(long, default_value = DEFAULT_SCHEMA_NAME)]
|
||||||
schema_name: Option<String>,
|
schema_name: String,
|
||||||
|
|
||||||
|
/// The catalog name of the table.
|
||||||
|
#[clap(long, default_value = DEFAULT_CATALOG_NAME)]
|
||||||
|
catalog_name: String,
|
||||||
|
|
||||||
/// Pretty print the output.
|
/// Pretty print the output.
|
||||||
#[clap(long, default_value = "false")]
|
#[clap(long, default_value = "false")]
|
||||||
@@ -143,7 +146,10 @@ pub struct GetTableCommand {
|
|||||||
|
|
||||||
impl GetTableCommand {
|
impl GetTableCommand {
|
||||||
pub fn validate(&self) -> Result<(), BoxedError> {
|
pub fn validate(&self) -> Result<(), BoxedError> {
|
||||||
if self.table_id.is_none() && self.table_name.is_none() {
|
if matches!(
|
||||||
|
(&self.table_id, &self.table_name),
|
||||||
|
(Some(_), Some(_)) | (None, None)
|
||||||
|
) {
|
||||||
return Err(BoxedError::new(
|
return Err(BoxedError::new(
|
||||||
InvalidArgumentsSnafu {
|
InvalidArgumentsSnafu {
|
||||||
msg: "You must specify either --table-id or --table-name.",
|
msg: "You must specify either --table-id or --table-name.",
|
||||||
@@ -159,7 +165,8 @@ struct GetTableTool {
|
|||||||
kvbackend: KvBackendRef,
|
kvbackend: KvBackendRef,
|
||||||
table_id: Option<u32>,
|
table_id: Option<u32>,
|
||||||
table_name: Option<String>,
|
table_name: Option<String>,
|
||||||
schema_name: Option<String>,
|
schema_name: String,
|
||||||
|
catalog_name: String,
|
||||||
pretty: bool,
|
pretty: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,23 +179,20 @@ impl Tool for GetTableTool {
|
|||||||
let table_route_manager = table_metadata_manager.table_route_manager();
|
let table_route_manager = table_metadata_manager.table_route_manager();
|
||||||
|
|
||||||
let table_id = if let Some(table_name) = &self.table_name {
|
let table_id = if let Some(table_name) = &self.table_name {
|
||||||
let catalog = DEFAULT_CATALOG_NAME.to_string();
|
let catalog_name = &self.catalog_name;
|
||||||
let schema_name = self
|
let schema_name = &self.schema_name;
|
||||||
.schema_name
|
|
||||||
.clone()
|
|
||||||
.unwrap_or_else(|| DEFAULT_SCHEMA_NAME.to_string());
|
|
||||||
let key = TableNameKey::new(&catalog, &schema_name, table_name);
|
|
||||||
|
|
||||||
let Some(table_name) = table_name_manager.get(key).await.map_err(BoxedError::new)?
|
let Some(table_id) =
|
||||||
|
get_table_id_by_name(table_name_manager, catalog_name, schema_name, table_name)
|
||||||
|
.await?
|
||||||
else {
|
else {
|
||||||
println!(
|
println!(
|
||||||
"Table({}) not found",
|
"Table({}) not found",
|
||||||
format_full_table_name(&catalog, &schema_name, table_name)
|
format_full_table_name(catalog_name, schema_name, table_name)
|
||||||
);
|
);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
table_id
|
||||||
table_name.table_id()
|
|
||||||
} else {
|
} else {
|
||||||
// Safety: we have validated that table_id or table_name is not None
|
// Safety: we have validated that table_id or table_name is not None
|
||||||
self.table_id.unwrap()
|
self.table_id.unwrap()
|
||||||
@@ -236,6 +240,7 @@ impl GetTableCommand {
|
|||||||
table_id: self.table_id,
|
table_id: self.table_id,
|
||||||
table_name: self.table_name.clone(),
|
table_name: self.table_name.clone(),
|
||||||
schema_name: self.schema_name.clone(),
|
schema_name: self.schema_name.clone(),
|
||||||
|
catalog_name: self.catalog_name.clone(),
|
||||||
pretty: self.pretty,
|
pretty: self.pretty,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|||||||
51
src/cli/src/metadata/control/test_utils.rs
Normal file
51
src/cli/src/metadata/control/test_utils.rs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
// Copyright 2023 Greptime Team
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
use common_meta::ddl::test_util::test_create_physical_table_task;
|
||||||
|
use common_meta::key::table_route::PhysicalTableRouteValue;
|
||||||
|
use common_meta::kv_backend::KvBackendRef;
|
||||||
|
use common_meta::peer::Peer;
|
||||||
|
use common_meta::rpc::router::{Region, RegionRoute};
|
||||||
|
use common_meta::rpc::store::PutRequest;
|
||||||
|
use store_api::storage::{RegionId, TableId};
|
||||||
|
use table::metadata::RawTableInfo;
|
||||||
|
|
||||||
|
/// Puts a key-value pair into the kv backend.
|
||||||
|
pub async fn put_key(kv_backend: &KvBackendRef, key: &str, value: &str) {
|
||||||
|
let put_req = PutRequest::new()
|
||||||
|
.with_key(key.as_bytes())
|
||||||
|
.with_value(value.as_bytes());
|
||||||
|
kv_backend.put(put_req).await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prepares the physical table metadata for testing.
|
||||||
|
///
|
||||||
|
/// Returns the table info and the table route.
|
||||||
|
pub async fn prepare_physical_table_metadata(
|
||||||
|
table_name: &str,
|
||||||
|
table_id: TableId,
|
||||||
|
) -> (RawTableInfo, PhysicalTableRouteValue) {
|
||||||
|
let mut create_physical_table_task = test_create_physical_table_task(table_name);
|
||||||
|
let table_route = PhysicalTableRouteValue::new(vec![RegionRoute {
|
||||||
|
region: Region {
|
||||||
|
id: RegionId::new(table_id, 1),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
leader_peer: Some(Peer::empty(1)),
|
||||||
|
..Default::default()
|
||||||
|
}]);
|
||||||
|
create_physical_table_task.set_table_id(table_id);
|
||||||
|
|
||||||
|
(create_physical_table_task.table_info, table_route)
|
||||||
|
}
|
||||||
@@ -12,9 +12,12 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
use common_error::ext::BoxedError;
|
||||||
use common_meta::error::Result as CommonMetaResult;
|
use common_meta::error::Result as CommonMetaResult;
|
||||||
|
use common_meta::key::table_name::{TableNameKey, TableNameManager};
|
||||||
use common_meta::rpc::KeyValue;
|
use common_meta::rpc::KeyValue;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
use store_api::storage::TableId;
|
||||||
|
|
||||||
/// Decodes a key-value pair into a string.
|
/// Decodes a key-value pair into a string.
|
||||||
pub fn decode_key_value(kv: KeyValue) -> CommonMetaResult<(String, String)> {
|
pub fn decode_key_value(kv: KeyValue) -> CommonMetaResult<(String, String)> {
|
||||||
@@ -34,3 +37,21 @@ where
|
|||||||
serde_json::to_string(value).unwrap()
|
serde_json::to_string(value).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the table id by table name.
|
||||||
|
pub async fn get_table_id_by_name(
|
||||||
|
table_name_manager: &TableNameManager,
|
||||||
|
catalog_name: &str,
|
||||||
|
schema_name: &str,
|
||||||
|
table_name: &str,
|
||||||
|
) -> Result<Option<TableId>, BoxedError> {
|
||||||
|
let table_name_key = TableNameKey::new(catalog_name, schema_name, table_name);
|
||||||
|
let Some(table_name_value) = table_name_manager
|
||||||
|
.get(table_name_key)
|
||||||
|
.await
|
||||||
|
.map_err(BoxedError::new)?
|
||||||
|
else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
Ok(Some(table_name_value.table_id()))
|
||||||
|
}
|
||||||
|
|||||||
369
src/cli/src/metadata/repair.rs
Normal file
369
src/cli/src/metadata/repair.rs
Normal file
@@ -0,0 +1,369 @@
|
|||||||
|
// Copyright 2023 Greptime Team
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
mod alter_table;
|
||||||
|
mod create_table;
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use clap::Parser;
|
||||||
|
use client::api::v1::CreateTableExpr;
|
||||||
|
use client::client_manager::NodeClients;
|
||||||
|
use client::{DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME};
|
||||||
|
use common_error::ext::{BoxedError, ErrorExt};
|
||||||
|
use common_error::status_code::StatusCode;
|
||||||
|
use common_grpc::channel_manager::ChannelConfig;
|
||||||
|
use common_meta::error::Error as CommonMetaError;
|
||||||
|
use common_meta::key::TableMetadataManager;
|
||||||
|
use common_meta::kv_backend::KvBackendRef;
|
||||||
|
use common_meta::node_manager::NodeManagerRef;
|
||||||
|
use common_meta::peer::Peer;
|
||||||
|
use common_meta::rpc::router::{find_leaders, RegionRoute};
|
||||||
|
use common_telemetry::{error, info, warn};
|
||||||
|
use futures::TryStreamExt;
|
||||||
|
use snafu::{ensure, ResultExt};
|
||||||
|
use store_api::storage::TableId;
|
||||||
|
|
||||||
|
use crate::error::{
|
||||||
|
InvalidArgumentsSnafu, Result, SendRequestToDatanodeSnafu, TableMetadataSnafu, UnexpectedSnafu,
|
||||||
|
};
|
||||||
|
use crate::metadata::common::StoreConfig;
|
||||||
|
use crate::metadata::utils::{FullTableMetadata, IteratorInput, TableMetadataIterator};
|
||||||
|
use crate::Tool;
|
||||||
|
|
||||||
|
/// Repair metadata of logical tables.
|
||||||
|
#[derive(Debug, Default, Parser)]
|
||||||
|
pub struct RepairLogicalTablesCommand {
|
||||||
|
/// The names of the tables to repair.
|
||||||
|
#[clap(long, value_delimiter = ',', alias = "table-name")]
|
||||||
|
table_names: Vec<String>,
|
||||||
|
|
||||||
|
/// The id of the table to repair.
|
||||||
|
#[clap(long, value_delimiter = ',', alias = "table-id")]
|
||||||
|
table_ids: Vec<TableId>,
|
||||||
|
|
||||||
|
/// The schema of the tables to repair.
|
||||||
|
#[clap(long, default_value = DEFAULT_SCHEMA_NAME)]
|
||||||
|
schema_name: String,
|
||||||
|
|
||||||
|
/// The catalog of the tables to repair.
|
||||||
|
#[clap(long, default_value = DEFAULT_CATALOG_NAME)]
|
||||||
|
catalog_name: String,
|
||||||
|
|
||||||
|
/// Whether to fail fast if any repair operation fails.
|
||||||
|
#[clap(long)]
|
||||||
|
fail_fast: bool,
|
||||||
|
|
||||||
|
#[clap(flatten)]
|
||||||
|
store: StoreConfig,
|
||||||
|
|
||||||
|
/// The timeout for the client to operate the datanode.
|
||||||
|
#[clap(long, default_value_t = 30)]
|
||||||
|
client_timeout_secs: u64,
|
||||||
|
|
||||||
|
/// The timeout for the client to connect to the datanode.
|
||||||
|
#[clap(long, default_value_t = 3)]
|
||||||
|
client_connect_timeout_secs: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RepairLogicalTablesCommand {
|
||||||
|
fn validate(&self) -> Result<()> {
|
||||||
|
ensure!(
|
||||||
|
!self.table_names.is_empty() || !self.table_ids.is_empty(),
|
||||||
|
InvalidArgumentsSnafu {
|
||||||
|
msg: "You must specify --table-names or --table-ids.",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RepairLogicalTablesCommand {
|
||||||
|
pub async fn build(&self) -> std::result::Result<Box<dyn Tool>, BoxedError> {
|
||||||
|
self.validate().map_err(BoxedError::new)?;
|
||||||
|
let kv_backend = self.store.build().await?;
|
||||||
|
let node_client_channel_config = ChannelConfig::new()
|
||||||
|
.timeout(Duration::from_secs(self.client_timeout_secs))
|
||||||
|
.connect_timeout(Duration::from_secs(self.client_connect_timeout_secs));
|
||||||
|
let node_manager = Arc::new(NodeClients::new(node_client_channel_config));
|
||||||
|
|
||||||
|
Ok(Box::new(RepairTool {
|
||||||
|
table_names: self.table_names.clone(),
|
||||||
|
table_ids: self.table_ids.clone(),
|
||||||
|
schema_name: self.schema_name.clone(),
|
||||||
|
catalog_name: self.catalog_name.clone(),
|
||||||
|
fail_fast: self.fail_fast,
|
||||||
|
kv_backend,
|
||||||
|
node_manager,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RepairTool {
|
||||||
|
table_names: Vec<String>,
|
||||||
|
table_ids: Vec<TableId>,
|
||||||
|
schema_name: String,
|
||||||
|
catalog_name: String,
|
||||||
|
fail_fast: bool,
|
||||||
|
kv_backend: KvBackendRef,
|
||||||
|
node_manager: NodeManagerRef,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Tool for RepairTool {
|
||||||
|
async fn do_work(&self) -> std::result::Result<(), BoxedError> {
|
||||||
|
self.repair_tables().await.map_err(BoxedError::new)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RepairTool {
|
||||||
|
fn generate_iterator_input(&self) -> Result<IteratorInput> {
|
||||||
|
if !self.table_names.is_empty() {
|
||||||
|
let table_names = &self.table_names;
|
||||||
|
let catalog = &self.catalog_name;
|
||||||
|
let schema_name = &self.schema_name;
|
||||||
|
|
||||||
|
let table_names = table_names
|
||||||
|
.iter()
|
||||||
|
.map(|table_name| {
|
||||||
|
(
|
||||||
|
catalog.to_string(),
|
||||||
|
schema_name.to_string(),
|
||||||
|
table_name.to_string(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
return Ok(IteratorInput::new_table_names(table_names));
|
||||||
|
} else if !self.table_ids.is_empty() {
|
||||||
|
return Ok(IteratorInput::new_table_ids(self.table_ids.clone()));
|
||||||
|
};
|
||||||
|
|
||||||
|
InvalidArgumentsSnafu {
|
||||||
|
msg: "You must specify --table-names or --table-id.",
|
||||||
|
}
|
||||||
|
.fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn repair_tables(&self) -> Result<()> {
|
||||||
|
let input = self.generate_iterator_input()?;
|
||||||
|
let mut table_metadata_iterator =
|
||||||
|
Box::pin(TableMetadataIterator::new(self.kv_backend.clone(), input).into_stream());
|
||||||
|
let table_metadata_manager = TableMetadataManager::new(self.kv_backend.clone());
|
||||||
|
|
||||||
|
let mut skipped_table = 0;
|
||||||
|
let mut success_table = 0;
|
||||||
|
while let Some(full_table_metadata) = table_metadata_iterator.try_next().await? {
|
||||||
|
let full_table_name = full_table_metadata.full_table_name();
|
||||||
|
if !full_table_metadata.is_metric_engine() {
|
||||||
|
warn!(
|
||||||
|
"Skipping repair for non-metric engine table: {}",
|
||||||
|
full_table_name
|
||||||
|
);
|
||||||
|
skipped_table += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if full_table_metadata.is_physical_table() {
|
||||||
|
warn!("Skipping repair for physical table: {}", full_table_name);
|
||||||
|
skipped_table += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (physical_table_id, physical_table_route) = table_metadata_manager
|
||||||
|
.table_route_manager()
|
||||||
|
.get_physical_table_route(full_table_metadata.table_id)
|
||||||
|
.await
|
||||||
|
.context(TableMetadataSnafu)?;
|
||||||
|
|
||||||
|
if let Err(err) = self
|
||||||
|
.repair_table(
|
||||||
|
&full_table_metadata,
|
||||||
|
physical_table_id,
|
||||||
|
&physical_table_route.region_routes,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
error!(
|
||||||
|
err;
|
||||||
|
"Failed to repair table: {}, skipped table: {}",
|
||||||
|
full_table_name,
|
||||||
|
skipped_table,
|
||||||
|
);
|
||||||
|
|
||||||
|
if self.fail_fast {
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
success_table += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"Repair logical tables result: {} tables repaired, {} tables skipped",
|
||||||
|
success_table, skipped_table
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn alter_table_on_datanodes(
|
||||||
|
&self,
|
||||||
|
full_table_metadata: &FullTableMetadata,
|
||||||
|
physical_region_routes: &[RegionRoute],
|
||||||
|
) -> Result<Vec<(Peer, CommonMetaError)>> {
|
||||||
|
let logical_table_id = full_table_metadata.table_id;
|
||||||
|
let alter_table_expr = alter_table::generate_alter_table_expr_for_all_columns(
|
||||||
|
&full_table_metadata.table_info,
|
||||||
|
)?;
|
||||||
|
let node_manager = self.node_manager.clone();
|
||||||
|
|
||||||
|
let mut failed_peers = Vec::new();
|
||||||
|
info!(
|
||||||
|
"Sending alter table requests to all datanodes for table: {}, number of regions:{}.",
|
||||||
|
full_table_metadata.full_table_name(),
|
||||||
|
physical_region_routes.len()
|
||||||
|
);
|
||||||
|
let leaders = find_leaders(physical_region_routes);
|
||||||
|
for peer in &leaders {
|
||||||
|
let alter_table_request = alter_table::make_alter_region_request_for_peer(
|
||||||
|
logical_table_id,
|
||||||
|
&alter_table_expr,
|
||||||
|
full_table_metadata.table_info.ident.version,
|
||||||
|
peer,
|
||||||
|
physical_region_routes,
|
||||||
|
)?;
|
||||||
|
let datanode = node_manager.datanode(peer).await;
|
||||||
|
if let Err(err) = datanode.handle(alter_table_request).await {
|
||||||
|
failed_peers.push((peer.clone(), err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(failed_peers)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_table_on_datanode(
|
||||||
|
&self,
|
||||||
|
create_table_expr: &CreateTableExpr,
|
||||||
|
logical_table_id: TableId,
|
||||||
|
physical_table_id: TableId,
|
||||||
|
peer: &Peer,
|
||||||
|
physical_region_routes: &[RegionRoute],
|
||||||
|
) -> Result<()> {
|
||||||
|
let node_manager = self.node_manager.clone();
|
||||||
|
let datanode = node_manager.datanode(peer).await;
|
||||||
|
let create_table_request = create_table::make_create_region_request_for_peer(
|
||||||
|
logical_table_id,
|
||||||
|
physical_table_id,
|
||||||
|
create_table_expr,
|
||||||
|
peer,
|
||||||
|
physical_region_routes,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
datanode
|
||||||
|
.handle(create_table_request)
|
||||||
|
.await
|
||||||
|
.with_context(|_| SendRequestToDatanodeSnafu { peer: peer.clone() })?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn repair_table(
|
||||||
|
&self,
|
||||||
|
full_table_metadata: &FullTableMetadata,
|
||||||
|
physical_table_id: TableId,
|
||||||
|
physical_region_routes: &[RegionRoute],
|
||||||
|
) -> Result<()> {
|
||||||
|
let full_table_name = full_table_metadata.full_table_name();
|
||||||
|
// First we sends alter table requests to all datanodes with all columns.
|
||||||
|
let failed_peers = self
|
||||||
|
.alter_table_on_datanodes(full_table_metadata, physical_region_routes)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if failed_peers.is_empty() {
|
||||||
|
info!(
|
||||||
|
"All alter table requests sent successfully for table: {}",
|
||||||
|
full_table_name
|
||||||
|
);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
warn!(
|
||||||
|
"Sending alter table requests to datanodes for table: {} failed for the datanodes: {:?}",
|
||||||
|
full_table_name,
|
||||||
|
failed_peers.iter().map(|(peer, _)| peer.id).collect::<Vec<_>>()
|
||||||
|
);
|
||||||
|
|
||||||
|
let create_table_expr =
|
||||||
|
create_table::generate_create_table_expr(&full_table_metadata.table_info)?;
|
||||||
|
|
||||||
|
let mut errors = Vec::new();
|
||||||
|
for (peer, err) in failed_peers {
|
||||||
|
if err.status_code() != StatusCode::RegionNotFound {
|
||||||
|
error!(
|
||||||
|
err;
|
||||||
|
"Sending alter table requests to datanode: {} for table: {} failed",
|
||||||
|
peer.id,
|
||||||
|
full_table_name,
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
info!(
|
||||||
|
"Region not found for table: {}, datanode: {}, trying to create the logical table on that datanode",
|
||||||
|
full_table_name,
|
||||||
|
peer.id
|
||||||
|
);
|
||||||
|
|
||||||
|
// If the alter table request fails for any datanode, we attempt to create the table on that datanode
|
||||||
|
// as a fallback mechanism to ensure table consistency across the cluster.
|
||||||
|
if let Err(err) = self
|
||||||
|
.create_table_on_datanode(
|
||||||
|
&create_table_expr,
|
||||||
|
full_table_metadata.table_id,
|
||||||
|
physical_table_id,
|
||||||
|
&peer,
|
||||||
|
physical_region_routes,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
error!(
|
||||||
|
err;
|
||||||
|
"Failed to create table on datanode: {} for table: {}",
|
||||||
|
peer.id, full_table_name
|
||||||
|
);
|
||||||
|
errors.push(err);
|
||||||
|
if self.fail_fast {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
info!(
|
||||||
|
"Created table on datanode: {} for table: {}",
|
||||||
|
peer.id, full_table_name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !errors.is_empty() {
|
||||||
|
return UnexpectedSnafu {
|
||||||
|
msg: format!(
|
||||||
|
"Failed to create table on datanodes for table: {}",
|
||||||
|
full_table_name,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
.fail();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
85
src/cli/src/metadata/repair/alter_table.rs
Normal file
85
src/cli/src/metadata/repair/alter_table.rs
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
// Copyright 2023 Greptime Team
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
use client::api::v1::alter_table_expr::Kind;
|
||||||
|
use client::api::v1::region::{region_request, AlterRequests, RegionRequest, RegionRequestHeader};
|
||||||
|
use client::api::v1::{AddColumn, AddColumns, AlterTableExpr};
|
||||||
|
use common_meta::ddl::alter_logical_tables::make_alter_region_request;
|
||||||
|
use common_meta::peer::Peer;
|
||||||
|
use common_meta::rpc::router::{find_leader_regions, RegionRoute};
|
||||||
|
use operator::expr_helper::column_schemas_to_defs;
|
||||||
|
use snafu::ResultExt;
|
||||||
|
use store_api::storage::{RegionId, TableId};
|
||||||
|
use table::metadata::RawTableInfo;
|
||||||
|
|
||||||
|
use crate::error::{CovertColumnSchemasToDefsSnafu, Result};
|
||||||
|
|
||||||
|
/// Generates alter table expression for all columns.
|
||||||
|
pub fn generate_alter_table_expr_for_all_columns(
|
||||||
|
table_info: &RawTableInfo,
|
||||||
|
) -> Result<AlterTableExpr> {
|
||||||
|
let schema = &table_info.meta.schema;
|
||||||
|
|
||||||
|
let mut alter_table_expr = AlterTableExpr {
|
||||||
|
catalog_name: table_info.catalog_name.to_string(),
|
||||||
|
schema_name: table_info.schema_name.to_string(),
|
||||||
|
table_name: table_info.name.to_string(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let primary_keys = table_info
|
||||||
|
.meta
|
||||||
|
.primary_key_indices
|
||||||
|
.iter()
|
||||||
|
.map(|i| schema.column_schemas[*i].name.clone())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let add_columns = column_schemas_to_defs(schema.column_schemas.clone(), &primary_keys)
|
||||||
|
.context(CovertColumnSchemasToDefsSnafu)?;
|
||||||
|
|
||||||
|
alter_table_expr.kind = Some(Kind::AddColumns(AddColumns {
|
||||||
|
add_columns: add_columns
|
||||||
|
.into_iter()
|
||||||
|
.map(|col| AddColumn {
|
||||||
|
column_def: Some(col),
|
||||||
|
location: None,
|
||||||
|
add_if_not_exists: true,
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
Ok(alter_table_expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Makes an alter region request for a peer.
|
||||||
|
pub fn make_alter_region_request_for_peer(
|
||||||
|
logical_table_id: TableId,
|
||||||
|
alter_table_expr: &AlterTableExpr,
|
||||||
|
schema_version: u64,
|
||||||
|
peer: &Peer,
|
||||||
|
region_routes: &[RegionRoute],
|
||||||
|
) -> Result<RegionRequest> {
|
||||||
|
let regions_on_this_peer = find_leader_regions(region_routes, peer);
|
||||||
|
let mut requests = Vec::with_capacity(regions_on_this_peer.len());
|
||||||
|
for region_number in ®ions_on_this_peer {
|
||||||
|
let region_id = RegionId::new(logical_table_id, *region_number);
|
||||||
|
let request = make_alter_region_request(region_id, alter_table_expr, schema_version);
|
||||||
|
requests.push(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(RegionRequest {
|
||||||
|
header: Some(RegionRequestHeader::default()),
|
||||||
|
body: Some(region_request::Body::Alters(AlterRequests { requests })),
|
||||||
|
})
|
||||||
|
}
|
||||||
89
src/cli/src/metadata/repair/create_table.rs
Normal file
89
src/cli/src/metadata/repair/create_table.rs
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
// Copyright 2023 Greptime Team
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use client::api::v1::region::{region_request, CreateRequests, RegionRequest, RegionRequestHeader};
|
||||||
|
use client::api::v1::CreateTableExpr;
|
||||||
|
use common_meta::ddl::create_logical_tables::create_region_request_builder;
|
||||||
|
use common_meta::ddl::utils::region_storage_path;
|
||||||
|
use common_meta::peer::Peer;
|
||||||
|
use common_meta::rpc::router::{find_leader_regions, RegionRoute};
|
||||||
|
use operator::expr_helper::column_schemas_to_defs;
|
||||||
|
use snafu::ResultExt;
|
||||||
|
use store_api::storage::{RegionId, TableId};
|
||||||
|
use table::metadata::RawTableInfo;
|
||||||
|
|
||||||
|
use crate::error::{CovertColumnSchemasToDefsSnafu, Result};
|
||||||
|
|
||||||
|
/// Generates a `CreateTableExpr` from a `RawTableInfo`.
|
||||||
|
pub fn generate_create_table_expr(table_info: &RawTableInfo) -> Result<CreateTableExpr> {
|
||||||
|
let schema = &table_info.meta.schema;
|
||||||
|
let primary_keys = table_info
|
||||||
|
.meta
|
||||||
|
.primary_key_indices
|
||||||
|
.iter()
|
||||||
|
.map(|i| schema.column_schemas[*i].name.clone())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let timestamp_index = schema.timestamp_index.as_ref().unwrap();
|
||||||
|
let time_index = schema.column_schemas[*timestamp_index].name.clone();
|
||||||
|
let column_defs = column_schemas_to_defs(schema.column_schemas.clone(), &primary_keys)
|
||||||
|
.context(CovertColumnSchemasToDefsSnafu)?;
|
||||||
|
let table_options = HashMap::from(&table_info.meta.options);
|
||||||
|
|
||||||
|
Ok(CreateTableExpr {
|
||||||
|
catalog_name: table_info.catalog_name.to_string(),
|
||||||
|
schema_name: table_info.schema_name.to_string(),
|
||||||
|
table_name: table_info.name.to_string(),
|
||||||
|
desc: String::default(),
|
||||||
|
column_defs,
|
||||||
|
time_index,
|
||||||
|
primary_keys,
|
||||||
|
create_if_not_exists: true,
|
||||||
|
table_options,
|
||||||
|
table_id: None,
|
||||||
|
engine: table_info.meta.engine.to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Makes a create region request for a peer.
|
||||||
|
pub fn make_create_region_request_for_peer(
|
||||||
|
logical_table_id: TableId,
|
||||||
|
physical_table_id: TableId,
|
||||||
|
create_table_expr: &CreateTableExpr,
|
||||||
|
peer: &Peer,
|
||||||
|
region_routes: &[RegionRoute],
|
||||||
|
) -> Result<RegionRequest> {
|
||||||
|
let regions_on_this_peer = find_leader_regions(region_routes, peer);
|
||||||
|
let mut requests = Vec::with_capacity(regions_on_this_peer.len());
|
||||||
|
let request_builder =
|
||||||
|
create_region_request_builder(create_table_expr, physical_table_id).unwrap();
|
||||||
|
|
||||||
|
let catalog = &create_table_expr.catalog_name;
|
||||||
|
let schema = &create_table_expr.schema_name;
|
||||||
|
let storage_path = region_storage_path(catalog, schema);
|
||||||
|
|
||||||
|
for region_number in ®ions_on_this_peer {
|
||||||
|
let region_id = RegionId::new(logical_table_id, *region_number);
|
||||||
|
let region_request =
|
||||||
|
request_builder.build_one(region_id, storage_path.clone(), &HashMap::new());
|
||||||
|
requests.push(region_request);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(RegionRequest {
|
||||||
|
header: Some(RegionRequestHeader::default()),
|
||||||
|
body: Some(region_request::Body::Creates(CreateRequests { requests })),
|
||||||
|
})
|
||||||
|
}
|
||||||
178
src/cli/src/metadata/utils.rs
Normal file
178
src/cli/src/metadata/utils.rs
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
// Copyright 2023 Greptime Team
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
use async_stream::try_stream;
|
||||||
|
use common_catalog::consts::METRIC_ENGINE;
|
||||||
|
use common_catalog::format_full_table_name;
|
||||||
|
use common_meta::key::table_name::TableNameKey;
|
||||||
|
use common_meta::key::table_route::TableRouteValue;
|
||||||
|
use common_meta::key::TableMetadataManager;
|
||||||
|
use common_meta::kv_backend::KvBackendRef;
|
||||||
|
use futures::Stream;
|
||||||
|
use snafu::{OptionExt, ResultExt};
|
||||||
|
use store_api::storage::TableId;
|
||||||
|
use table::metadata::RawTableInfo;
|
||||||
|
|
||||||
|
use crate::error::{Result, TableMetadataSnafu, UnexpectedSnafu};
|
||||||
|
|
||||||
|
/// The input for the iterator.
|
||||||
|
pub enum IteratorInput {
|
||||||
|
TableIds(VecDeque<TableId>),
|
||||||
|
TableNames(VecDeque<(String, String, String)>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IteratorInput {
|
||||||
|
/// Creates a new iterator input from a list of table ids.
|
||||||
|
pub fn new_table_ids(table_ids: Vec<TableId>) -> Self {
|
||||||
|
Self::TableIds(table_ids.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new iterator input from a list of table names.
|
||||||
|
pub fn new_table_names(table_names: Vec<(String, String, String)>) -> Self {
|
||||||
|
Self::TableNames(table_names.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An iterator for retrieving table metadata from the metadata store.
|
||||||
|
///
|
||||||
|
/// This struct provides functionality to iterate over table metadata based on
|
||||||
|
/// either [`TableId`] and their associated regions or fully qualified table names.
|
||||||
|
pub struct TableMetadataIterator {
|
||||||
|
input: IteratorInput,
|
||||||
|
table_metadata_manager: TableMetadataManager,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The full table metadata.
|
||||||
|
pub struct FullTableMetadata {
|
||||||
|
pub table_id: TableId,
|
||||||
|
pub table_info: RawTableInfo,
|
||||||
|
pub table_route: TableRouteValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FullTableMetadata {
|
||||||
|
/// Returns true if it's [TableRouteValue::Physical].
|
||||||
|
pub fn is_physical_table(&self) -> bool {
|
||||||
|
self.table_route.is_physical()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if it's a metric engine table.
|
||||||
|
pub fn is_metric_engine(&self) -> bool {
|
||||||
|
self.table_info.meta.engine == METRIC_ENGINE
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the full table name.
|
||||||
|
pub fn full_table_name(&self) -> String {
|
||||||
|
format_full_table_name(
|
||||||
|
&self.table_info.catalog_name,
|
||||||
|
&self.table_info.schema_name,
|
||||||
|
&self.table_info.name,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TableMetadataIterator {
|
||||||
|
pub fn new(kvbackend: KvBackendRef, input: IteratorInput) -> Self {
|
||||||
|
let table_metadata_manager = TableMetadataManager::new(kvbackend);
|
||||||
|
Self {
|
||||||
|
input,
|
||||||
|
table_metadata_manager,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the next table metadata.
|
||||||
|
///
|
||||||
|
/// This method handles two types of inputs:
|
||||||
|
/// - TableIds: Returns metadata for a specific [`TableId`].
|
||||||
|
/// - TableNames: Returns metadata for a table identified by its full name (catalog.schema.table).
|
||||||
|
///
|
||||||
|
/// Returns `None` when there are no more tables to process.
|
||||||
|
pub async fn next(&mut self) -> Result<Option<FullTableMetadata>> {
|
||||||
|
match &mut self.input {
|
||||||
|
IteratorInput::TableIds(table_ids) => {
|
||||||
|
if let Some(table_id) = table_ids.pop_front() {
|
||||||
|
let full_table_metadata = self.get_table_metadata(table_id).await?;
|
||||||
|
return Ok(Some(full_table_metadata));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IteratorInput::TableNames(table_names) => {
|
||||||
|
if let Some(full_table_name) = table_names.pop_front() {
|
||||||
|
let table_id = self.get_table_id_by_name(full_table_name).await?;
|
||||||
|
let full_table_metadata = self.get_table_metadata(table_id).await?;
|
||||||
|
return Ok(Some(full_table_metadata));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts the iterator into a stream of table metadata.
|
||||||
|
pub fn into_stream(mut self) -> impl Stream<Item = Result<FullTableMetadata>> {
|
||||||
|
try_stream!({
|
||||||
|
while let Some(full_table_metadata) = self.next().await? {
|
||||||
|
yield full_table_metadata;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_table_id_by_name(
|
||||||
|
&mut self,
|
||||||
|
(catalog_name, schema_name, table_name): (String, String, String),
|
||||||
|
) -> Result<TableId> {
|
||||||
|
let key = TableNameKey::new(&catalog_name, &schema_name, &table_name);
|
||||||
|
let table_id = self
|
||||||
|
.table_metadata_manager
|
||||||
|
.table_name_manager()
|
||||||
|
.get(key)
|
||||||
|
.await
|
||||||
|
.context(TableMetadataSnafu)?
|
||||||
|
.with_context(|| UnexpectedSnafu {
|
||||||
|
msg: format!(
|
||||||
|
"Table not found: {}",
|
||||||
|
format_full_table_name(&catalog_name, &schema_name, &table_name)
|
||||||
|
),
|
||||||
|
})?
|
||||||
|
.table_id();
|
||||||
|
Ok(table_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_table_metadata(&mut self, table_id: TableId) -> Result<FullTableMetadata> {
|
||||||
|
let (table_info, table_route) = self
|
||||||
|
.table_metadata_manager
|
||||||
|
.get_full_table_info(table_id)
|
||||||
|
.await
|
||||||
|
.context(TableMetadataSnafu)?;
|
||||||
|
|
||||||
|
let table_info = table_info
|
||||||
|
.with_context(|| UnexpectedSnafu {
|
||||||
|
msg: format!("Table info not found for table id: {table_id}"),
|
||||||
|
})?
|
||||||
|
.into_inner()
|
||||||
|
.table_info;
|
||||||
|
let table_route = table_route
|
||||||
|
.with_context(|| UnexpectedSnafu {
|
||||||
|
msg: format!("Table route not found for table id: {table_id}"),
|
||||||
|
})?
|
||||||
|
.into_inner();
|
||||||
|
|
||||||
|
Ok(FullTableMetadata {
|
||||||
|
table_id,
|
||||||
|
table_info,
|
||||||
|
table_route,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -211,12 +211,18 @@ impl Database {
|
|||||||
retries += 1;
|
retries += 1;
|
||||||
warn!("Retrying {} times with error = {:?}", retries, err);
|
warn!("Retrying {} times with error = {:?}", retries, err);
|
||||||
continue;
|
continue;
|
||||||
|
} else {
|
||||||
|
error!(
|
||||||
|
err; "Failed to send request to grpc handle, retries = {}, not retryable error, aborting",
|
||||||
|
retries
|
||||||
|
);
|
||||||
|
return Err(err.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Err(err), false) => {
|
(Err(err), false) => {
|
||||||
error!(
|
error!(
|
||||||
"Failed to send request to grpc handle after {} retries, error = {:?}",
|
err; "Failed to send request to grpc handle after {} retries",
|
||||||
retries, err
|
retries,
|
||||||
);
|
);
|
||||||
return Err(err.into());
|
return Err(err.into());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use api::v1::flow::{FlowRequest, FlowResponse};
|
use api::v1::flow::{DirtyWindowRequest, DirtyWindowRequests, FlowRequest, FlowResponse};
|
||||||
use api::v1::region::InsertRequests;
|
use api::v1::region::InsertRequests;
|
||||||
use common_error::ext::BoxedError;
|
use common_error::ext::BoxedError;
|
||||||
use common_meta::node_manager::Flownode;
|
use common_meta::node_manager::Flownode;
|
||||||
@@ -44,6 +44,16 @@ impl Flownode for FlowRequester {
|
|||||||
.map_err(BoxedError::new)
|
.map_err(BoxedError::new)
|
||||||
.context(common_meta::error::ExternalSnafu)
|
.context(common_meta::error::ExternalSnafu)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn handle_mark_window_dirty(
|
||||||
|
&self,
|
||||||
|
req: DirtyWindowRequest,
|
||||||
|
) -> common_meta::error::Result<FlowResponse> {
|
||||||
|
self.handle_mark_window_dirty(req)
|
||||||
|
.await
|
||||||
|
.map_err(BoxedError::new)
|
||||||
|
.context(common_meta::error::ExternalSnafu)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FlowRequester {
|
impl FlowRequester {
|
||||||
@@ -91,4 +101,20 @@ impl FlowRequester {
|
|||||||
.into_inner();
|
.into_inner();
|
||||||
Ok(response)
|
Ok(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn handle_mark_window_dirty(&self, req: DirtyWindowRequest) -> Result<FlowResponse> {
|
||||||
|
let (addr, mut client) = self.client.raw_flow_client()?;
|
||||||
|
let response = client
|
||||||
|
.handle_mark_dirty_time_window(DirtyWindowRequests {
|
||||||
|
requests: vec![req],
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.or_else(|e| {
|
||||||
|
let code = e.code();
|
||||||
|
let err: crate::error::Error = e.into();
|
||||||
|
Err(BoxedError::new(err)).context(FlowServerSnafu { addr, code })
|
||||||
|
})?
|
||||||
|
.into_inner();
|
||||||
|
Ok(response)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -163,19 +163,70 @@ impl RegionRequester {
|
|||||||
let _span = tracing_context.attach(common_telemetry::tracing::info_span!(
|
let _span = tracing_context.attach(common_telemetry::tracing::info_span!(
|
||||||
"poll_flight_data_stream"
|
"poll_flight_data_stream"
|
||||||
));
|
));
|
||||||
while let Some(flight_message) = flight_message_stream.next().await {
|
|
||||||
let flight_message = flight_message
|
let mut buffered_message: Option<FlightMessage> = None;
|
||||||
.map_err(BoxedError::new)
|
let mut stream_ended = false;
|
||||||
.context(ExternalSnafu)?;
|
|
||||||
|
while !stream_ended {
|
||||||
|
// get the next message from the buffered message or read from the flight message stream
|
||||||
|
let flight_message_item = if let Some(msg) = buffered_message.take() {
|
||||||
|
Some(Ok(msg))
|
||||||
|
} else {
|
||||||
|
flight_message_stream.next().await
|
||||||
|
};
|
||||||
|
|
||||||
|
let flight_message = match flight_message_item {
|
||||||
|
Some(Ok(message)) => message,
|
||||||
|
Some(Err(e)) => {
|
||||||
|
yield Err(BoxedError::new(e)).context(ExternalSnafu);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
None => break,
|
||||||
|
};
|
||||||
|
|
||||||
match flight_message {
|
match flight_message {
|
||||||
FlightMessage::RecordBatch(record_batch) => {
|
FlightMessage::RecordBatch(record_batch) => {
|
||||||
yield RecordBatch::try_from_df_record_batch(
|
let result_to_yield = RecordBatch::try_from_df_record_batch(
|
||||||
schema_cloned.clone(),
|
schema_cloned.clone(),
|
||||||
record_batch,
|
record_batch,
|
||||||
)
|
);
|
||||||
|
|
||||||
|
// get the next message from the stream. normally it should be a metrics message.
|
||||||
|
if let Some(next_flight_message_result) = flight_message_stream.next().await
|
||||||
|
{
|
||||||
|
match next_flight_message_result {
|
||||||
|
Ok(FlightMessage::Metrics(s)) => {
|
||||||
|
let m = serde_json::from_str(&s).ok().map(Arc::new);
|
||||||
|
metrics_ref.swap(m);
|
||||||
|
}
|
||||||
|
Ok(FlightMessage::RecordBatch(rb)) => {
|
||||||
|
// for some reason it's not a metrics message, so we need to buffer this record batch
|
||||||
|
// and yield it in the next iteration.
|
||||||
|
buffered_message = Some(FlightMessage::RecordBatch(rb));
|
||||||
|
}
|
||||||
|
Ok(_) => {
|
||||||
|
yield IllegalFlightMessagesSnafu {
|
||||||
|
reason: "A RecordBatch message can only be succeeded by a Metrics message or another RecordBatch message"
|
||||||
|
}
|
||||||
|
.fail()
|
||||||
|
.map_err(BoxedError::new)
|
||||||
|
.context(ExternalSnafu);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
yield Err(BoxedError::new(e)).context(ExternalSnafu);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// the stream has ended
|
||||||
|
stream_ended = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
yield result_to_yield;
|
||||||
}
|
}
|
||||||
FlightMessage::Metrics(s) => {
|
FlightMessage::Metrics(s) => {
|
||||||
|
// just a branch in case of some metrics message comes after other things.
|
||||||
let m = serde_json::from_str(&s).ok().map(Arc::new);
|
let m = serde_json::from_str(&s).ok().map(Arc::new);
|
||||||
metrics_ref.swap(m);
|
metrics_ref.swap(m);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -20,11 +20,11 @@ use cmd::error::{InitTlsProviderSnafu, Result};
|
|||||||
use cmd::options::GlobalOptions;
|
use cmd::options::GlobalOptions;
|
||||||
use cmd::{cli, datanode, flownode, frontend, metasrv, standalone, App};
|
use cmd::{cli, datanode, flownode, frontend, metasrv, standalone, App};
|
||||||
use common_base::Plugins;
|
use common_base::Plugins;
|
||||||
use common_version::version;
|
use common_version::{verbose_version, version};
|
||||||
use servers::install_ring_crypto_provider;
|
use servers::install_ring_crypto_provider;
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[command(name = "greptime", author, version, long_version = version(), about)]
|
#[command(name = "greptime", author, version, long_version = verbose_version(), about)]
|
||||||
#[command(propagate_version = true)]
|
#[command(propagate_version = true)]
|
||||||
pub(crate) struct Command {
|
pub(crate) struct Command {
|
||||||
#[clap(subcommand)]
|
#[clap(subcommand)]
|
||||||
@@ -143,10 +143,8 @@ async fn start(cli: Command) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn setup_human_panic() {
|
fn setup_human_panic() {
|
||||||
human_panic::setup_panic!(
|
human_panic::setup_panic!(human_panic::Metadata::new("GreptimeDB", version())
|
||||||
human_panic::Metadata::new("GreptimeDB", env!("CARGO_PKG_VERSION"))
|
.homepage("https://github.com/GreptimeTeam/greptimedb/discussions"));
|
||||||
.homepage("https://github.com/GreptimeTeam/greptimedb/discussions")
|
|
||||||
);
|
|
||||||
|
|
||||||
common_telemetry::set_panic_hook();
|
common_telemetry::set_panic_hook();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ use catalog::kvbackend::MetaKvBackend;
|
|||||||
use common_base::Plugins;
|
use common_base::Plugins;
|
||||||
use common_meta::cache::LayeredCacheRegistryBuilder;
|
use common_meta::cache::LayeredCacheRegistryBuilder;
|
||||||
use common_telemetry::info;
|
use common_telemetry::info;
|
||||||
use common_version::{short_version, version};
|
use common_version::{short_version, verbose_version};
|
||||||
use datanode::datanode::DatanodeBuilder;
|
use datanode::datanode::DatanodeBuilder;
|
||||||
use datanode::service::DatanodeServiceBuilder;
|
use datanode::service::DatanodeServiceBuilder;
|
||||||
use meta_client::MetaClientType;
|
use meta_client::MetaClientType;
|
||||||
@@ -67,7 +67,7 @@ impl InstanceBuilder {
|
|||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
log_versions(version(), short_version(), APP_NAME);
|
log_versions(verbose_version(), short_version(), APP_NAME);
|
||||||
create_resource_limit_metrics(APP_NAME);
|
create_resource_limit_metrics(APP_NAME);
|
||||||
|
|
||||||
plugins::setup_datanode_plugins(plugins, &opts.plugins, dn_opts)
|
plugins::setup_datanode_plugins(plugins, &opts.plugins, dn_opts)
|
||||||
@@ -93,6 +93,7 @@ impl InstanceBuilder {
|
|||||||
MetaClientType::Datanode { member_id },
|
MetaClientType::Datanode { member_id },
|
||||||
meta_client_options,
|
meta_client_options,
|
||||||
Some(&plugins),
|
Some(&plugins),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.context(MetaClientInitSnafu)?;
|
.context(MetaClientInitSnafu)?;
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ use common_meta::key::flow::FlowMetadataManager;
|
|||||||
use common_meta::key::TableMetadataManager;
|
use common_meta::key::TableMetadataManager;
|
||||||
use common_telemetry::info;
|
use common_telemetry::info;
|
||||||
use common_telemetry::logging::{TracingOptions, DEFAULT_LOGGING_DIR};
|
use common_telemetry::logging::{TracingOptions, DEFAULT_LOGGING_DIR};
|
||||||
use common_version::{short_version, version};
|
use common_version::{short_version, verbose_version};
|
||||||
use flow::{
|
use flow::{
|
||||||
get_flow_auth_options, FlownodeBuilder, FlownodeInstance, FlownodeServiceBuilder,
|
get_flow_auth_options, FlownodeBuilder, FlownodeInstance, FlownodeServiceBuilder,
|
||||||
FrontendClient, FrontendInvoker,
|
FrontendClient, FrontendInvoker,
|
||||||
@@ -55,14 +55,32 @@ type FlownodeOptions = GreptimeOptions<flow::FlownodeOptions>;
|
|||||||
pub struct Instance {
|
pub struct Instance {
|
||||||
flownode: FlownodeInstance,
|
flownode: FlownodeInstance,
|
||||||
|
|
||||||
|
// The components of flownode, which make it easier to expand based
|
||||||
|
// on the components.
|
||||||
|
#[cfg(feature = "enterprise")]
|
||||||
|
components: Components,
|
||||||
|
|
||||||
// Keep the logging guard to prevent the worker from being dropped.
|
// Keep the logging guard to prevent the worker from being dropped.
|
||||||
_guard: Vec<WorkerGuard>,
|
_guard: Vec<WorkerGuard>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "enterprise")]
|
||||||
|
pub struct Components {
|
||||||
|
pub catalog_manager: catalog::CatalogManagerRef,
|
||||||
|
pub fe_client: Arc<FrontendClient>,
|
||||||
|
pub kv_backend: common_meta::kv_backend::KvBackendRef,
|
||||||
|
}
|
||||||
|
|
||||||
impl Instance {
|
impl Instance {
|
||||||
pub fn new(flownode: FlownodeInstance, guard: Vec<WorkerGuard>) -> Self {
|
pub fn new(
|
||||||
|
flownode: FlownodeInstance,
|
||||||
|
#[cfg(feature = "enterprise")] components: Components,
|
||||||
|
guard: Vec<WorkerGuard>,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
flownode,
|
flownode,
|
||||||
|
#[cfg(feature = "enterprise")]
|
||||||
|
components,
|
||||||
_guard: guard,
|
_guard: guard,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -75,6 +93,11 @@ impl Instance {
|
|||||||
pub fn flownode_mut(&mut self) -> &mut FlownodeInstance {
|
pub fn flownode_mut(&mut self) -> &mut FlownodeInstance {
|
||||||
&mut self.flownode
|
&mut self.flownode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "enterprise")]
|
||||||
|
pub fn components(&self) -> &Components {
|
||||||
|
&self.components
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
@@ -256,7 +279,7 @@ impl StartCommand {
|
|||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
log_versions(version(), short_version(), APP_NAME);
|
log_versions(verbose_version(), short_version(), APP_NAME);
|
||||||
create_resource_limit_metrics(APP_NAME);
|
create_resource_limit_metrics(APP_NAME);
|
||||||
|
|
||||||
info!("Flownode start command: {:#?}", self);
|
info!("Flownode start command: {:#?}", self);
|
||||||
@@ -283,6 +306,7 @@ impl StartCommand {
|
|||||||
MetaClientType::Flownode { member_id },
|
MetaClientType::Flownode { member_id },
|
||||||
meta_config,
|
meta_config,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.context(MetaClientInitSnafu)?;
|
.context(MetaClientInitSnafu)?;
|
||||||
@@ -349,19 +373,20 @@ impl StartCommand {
|
|||||||
let flow_auth_header = get_flow_auth_options(&opts).context(StartFlownodeSnafu)?;
|
let flow_auth_header = get_flow_auth_options(&opts).context(StartFlownodeSnafu)?;
|
||||||
let frontend_client =
|
let frontend_client =
|
||||||
FrontendClient::from_meta_client(meta_client.clone(), flow_auth_header);
|
FrontendClient::from_meta_client(meta_client.clone(), flow_auth_header);
|
||||||
|
let frontend_client = Arc::new(frontend_client);
|
||||||
let flownode_builder = FlownodeBuilder::new(
|
let flownode_builder = FlownodeBuilder::new(
|
||||||
opts.clone(),
|
opts.clone(),
|
||||||
plugins,
|
plugins,
|
||||||
table_metadata_manager,
|
table_metadata_manager,
|
||||||
catalog_manager.clone(),
|
catalog_manager.clone(),
|
||||||
flow_metadata_manager,
|
flow_metadata_manager,
|
||||||
Arc::new(frontend_client),
|
frontend_client.clone(),
|
||||||
)
|
)
|
||||||
.with_heartbeat_task(heartbeat_task);
|
.with_heartbeat_task(heartbeat_task);
|
||||||
|
|
||||||
let mut flownode = flownode_builder.build().await.context(StartFlownodeSnafu)?;
|
let mut flownode = flownode_builder.build().await.context(StartFlownodeSnafu)?;
|
||||||
let services = FlownodeServiceBuilder::new(&opts)
|
let services = FlownodeServiceBuilder::new(&opts)
|
||||||
.with_grpc_server(flownode.flownode_server().clone())
|
.with_default_grpc_server(flownode.flownode_server())
|
||||||
.enable_http_service()
|
.enable_http_service()
|
||||||
.build()
|
.build()
|
||||||
.context(StartFlownodeSnafu)?;
|
.context(StartFlownodeSnafu)?;
|
||||||
@@ -393,6 +418,16 @@ impl StartCommand {
|
|||||||
.set_frontend_invoker(invoker)
|
.set_frontend_invoker(invoker)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
Ok(Instance::new(flownode, guard))
|
#[cfg(feature = "enterprise")]
|
||||||
|
let components = Components {
|
||||||
|
catalog_manager: catalog_manager.clone(),
|
||||||
|
fe_client: frontend_client,
|
||||||
|
kv_backend: cached_meta_backend,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "enterprise"))]
|
||||||
|
return Ok(Instance::new(flownode, guard));
|
||||||
|
#[cfg(feature = "enterprise")]
|
||||||
|
Ok(Instance::new(flownode, components, guard))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ use common_meta::heartbeat::handler::HandlerGroupExecutor;
|
|||||||
use common_telemetry::info;
|
use common_telemetry::info;
|
||||||
use common_telemetry::logging::{TracingOptions, DEFAULT_LOGGING_DIR};
|
use common_telemetry::logging::{TracingOptions, DEFAULT_LOGGING_DIR};
|
||||||
use common_time::timezone::set_default_timezone;
|
use common_time::timezone::set_default_timezone;
|
||||||
use common_version::{short_version, version};
|
use common_version::{short_version, verbose_version};
|
||||||
use frontend::frontend::Frontend;
|
use frontend::frontend::Frontend;
|
||||||
use frontend::heartbeat::HeartbeatTask;
|
use frontend::heartbeat::HeartbeatTask;
|
||||||
use frontend::instance::builder::FrontendBuilder;
|
use frontend::instance::builder::FrontendBuilder;
|
||||||
@@ -282,7 +282,7 @@ impl StartCommand {
|
|||||||
opts.component.slow_query.as_ref(),
|
opts.component.slow_query.as_ref(),
|
||||||
);
|
);
|
||||||
|
|
||||||
log_versions(version(), short_version(), APP_NAME);
|
log_versions(verbose_version(), short_version(), APP_NAME);
|
||||||
create_resource_limit_metrics(APP_NAME);
|
create_resource_limit_metrics(APP_NAME);
|
||||||
|
|
||||||
info!("Frontend start command: {:#?}", self);
|
info!("Frontend start command: {:#?}", self);
|
||||||
@@ -313,6 +313,7 @@ impl StartCommand {
|
|||||||
MetaClientType::Frontend,
|
MetaClientType::Frontend,
|
||||||
meta_client_options,
|
meta_client_options,
|
||||||
Some(&plugins),
|
Some(&plugins),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.context(error::MetaClientInitSnafu)?;
|
.context(error::MetaClientInitSnafu)?;
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ pub trait App: Send {
|
|||||||
pub fn log_versions(version: &str, short_version: &str, app: &str) {
|
pub fn log_versions(version: &str, short_version: &str, app: &str) {
|
||||||
// Report app version as gauge.
|
// Report app version as gauge.
|
||||||
APP_VERSION
|
APP_VERSION
|
||||||
.with_label_values(&[env!("CARGO_PKG_VERSION"), short_version, app])
|
.with_label_values(&[common_version::version(), short_version, app])
|
||||||
.inc();
|
.inc();
|
||||||
|
|
||||||
// Log version and argument flags.
|
// Log version and argument flags.
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ use common_base::Plugins;
|
|||||||
use common_config::Configurable;
|
use common_config::Configurable;
|
||||||
use common_telemetry::info;
|
use common_telemetry::info;
|
||||||
use common_telemetry::logging::{TracingOptions, DEFAULT_LOGGING_DIR};
|
use common_telemetry::logging::{TracingOptions, DEFAULT_LOGGING_DIR};
|
||||||
use common_version::{short_version, version};
|
use common_version::{short_version, verbose_version};
|
||||||
use meta_srv::bootstrap::MetasrvInstance;
|
use meta_srv::bootstrap::MetasrvInstance;
|
||||||
use meta_srv::metasrv::BackendImpl;
|
use meta_srv::metasrv::BackendImpl;
|
||||||
use snafu::ResultExt;
|
use snafu::ResultExt;
|
||||||
@@ -320,7 +320,7 @@ impl StartCommand {
|
|||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
log_versions(version(), short_version(), APP_NAME);
|
log_versions(verbose_version(), short_version(), APP_NAME);
|
||||||
create_resource_limit_metrics(APP_NAME);
|
create_resource_limit_metrics(APP_NAME);
|
||||||
|
|
||||||
info!("Metasrv start command: {:#?}", self);
|
info!("Metasrv start command: {:#?}", self);
|
||||||
|
|||||||
@@ -30,20 +30,16 @@ use common_catalog::consts::{MIN_USER_FLOW_ID, MIN_USER_TABLE_ID};
|
|||||||
use common_config::{metadata_store_dir, Configurable, KvBackendConfig};
|
use common_config::{metadata_store_dir, Configurable, KvBackendConfig};
|
||||||
use common_error::ext::BoxedError;
|
use common_error::ext::BoxedError;
|
||||||
use common_meta::cache::LayeredCacheRegistryBuilder;
|
use common_meta::cache::LayeredCacheRegistryBuilder;
|
||||||
use common_meta::cache_invalidator::CacheInvalidatorRef;
|
|
||||||
use common_meta::cluster::{NodeInfo, NodeStatus};
|
use common_meta::cluster::{NodeInfo, NodeStatus};
|
||||||
use common_meta::datanode::RegionStat;
|
use common_meta::datanode::RegionStat;
|
||||||
use common_meta::ddl::flow_meta::{FlowMetadataAllocator, FlowMetadataAllocatorRef};
|
use common_meta::ddl::flow_meta::FlowMetadataAllocator;
|
||||||
use common_meta::ddl::table_meta::{TableMetadataAllocator, TableMetadataAllocatorRef};
|
use common_meta::ddl::table_meta::TableMetadataAllocator;
|
||||||
use common_meta::ddl::{DdlContext, NoopRegionFailureDetectorControl, ProcedureExecutorRef};
|
use common_meta::ddl::{DdlContext, NoopRegionFailureDetectorControl, ProcedureExecutorRef};
|
||||||
use common_meta::ddl_manager::DdlManager;
|
use common_meta::ddl_manager::DdlManager;
|
||||||
#[cfg(feature = "enterprise")]
|
|
||||||
use common_meta::ddl_manager::TriggerDdlManagerRef;
|
|
||||||
use common_meta::key::flow::flow_state::FlowStat;
|
use common_meta::key::flow::flow_state::FlowStat;
|
||||||
use common_meta::key::flow::{FlowMetadataManager, FlowMetadataManagerRef};
|
use common_meta::key::flow::FlowMetadataManager;
|
||||||
use common_meta::key::{TableMetadataManager, TableMetadataManagerRef};
|
use common_meta::key::{TableMetadataManager, TableMetadataManagerRef};
|
||||||
use common_meta::kv_backend::KvBackendRef;
|
use common_meta::kv_backend::KvBackendRef;
|
||||||
use common_meta::node_manager::NodeManagerRef;
|
|
||||||
use common_meta::peer::Peer;
|
use common_meta::peer::Peer;
|
||||||
use common_meta::region_keeper::MemoryRegionKeeper;
|
use common_meta::region_keeper::MemoryRegionKeeper;
|
||||||
use common_meta::region_registry::LeaderRegionRegistry;
|
use common_meta::region_registry::LeaderRegionRegistry;
|
||||||
@@ -55,7 +51,7 @@ use common_telemetry::logging::{
|
|||||||
LoggingOptions, SlowQueryOptions, TracingOptions, DEFAULT_LOGGING_DIR,
|
LoggingOptions, SlowQueryOptions, TracingOptions, DEFAULT_LOGGING_DIR,
|
||||||
};
|
};
|
||||||
use common_time::timezone::set_default_timezone;
|
use common_time::timezone::set_default_timezone;
|
||||||
use common_version::{short_version, version};
|
use common_version::{short_version, verbose_version};
|
||||||
use common_wal::config::DatanodeWalConfig;
|
use common_wal::config::DatanodeWalConfig;
|
||||||
use datanode::config::{DatanodeOptions, ProcedureConfig, RegionEngineConfig, StorageConfig};
|
use datanode::config::{DatanodeOptions, ProcedureConfig, RegionEngineConfig, StorageConfig};
|
||||||
use datanode::datanode::{Datanode, DatanodeBuilder};
|
use datanode::datanode::{Datanode, DatanodeBuilder};
|
||||||
@@ -470,7 +466,7 @@ impl StartCommand {
|
|||||||
opts.component.slow_query.as_ref(),
|
opts.component.slow_query.as_ref(),
|
||||||
);
|
);
|
||||||
|
|
||||||
log_versions(version(), short_version(), APP_NAME);
|
log_versions(verbose_version(), short_version(), APP_NAME);
|
||||||
create_resource_limit_metrics(APP_NAME);
|
create_resource_limit_metrics(APP_NAME);
|
||||||
|
|
||||||
info!("Standalone start command: {:#?}", self);
|
info!("Standalone start command: {:#?}", self);
|
||||||
@@ -594,28 +590,36 @@ impl StartCommand {
|
|||||||
.await
|
.await
|
||||||
.context(error::BuildWalOptionsAllocatorSnafu)?;
|
.context(error::BuildWalOptionsAllocatorSnafu)?;
|
||||||
let wal_options_allocator = Arc::new(wal_options_allocator);
|
let wal_options_allocator = Arc::new(wal_options_allocator);
|
||||||
let table_meta_allocator = Arc::new(TableMetadataAllocator::new(
|
let table_metadata_allocator = Arc::new(TableMetadataAllocator::new(
|
||||||
table_id_sequence,
|
table_id_sequence,
|
||||||
wal_options_allocator.clone(),
|
wal_options_allocator.clone(),
|
||||||
));
|
));
|
||||||
let flow_meta_allocator = Arc::new(FlowMetadataAllocator::with_noop_peer_allocator(
|
let flow_metadata_allocator = Arc::new(FlowMetadataAllocator::with_noop_peer_allocator(
|
||||||
flow_id_sequence,
|
flow_id_sequence,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
let ddl_context = DdlContext {
|
||||||
|
node_manager: node_manager.clone(),
|
||||||
|
cache_invalidator: layered_cache_registry.clone(),
|
||||||
|
memory_region_keeper: Arc::new(MemoryRegionKeeper::default()),
|
||||||
|
leader_region_registry: Arc::new(LeaderRegionRegistry::default()),
|
||||||
|
table_metadata_manager: table_metadata_manager.clone(),
|
||||||
|
table_metadata_allocator: table_metadata_allocator.clone(),
|
||||||
|
flow_metadata_manager: flow_metadata_manager.clone(),
|
||||||
|
flow_metadata_allocator: flow_metadata_allocator.clone(),
|
||||||
|
region_failure_detector_controller: Arc::new(NoopRegionFailureDetectorControl),
|
||||||
|
};
|
||||||
|
let procedure_manager_c = procedure_manager.clone();
|
||||||
|
|
||||||
|
let ddl_manager = DdlManager::try_new(ddl_context, procedure_manager_c, true)
|
||||||
|
.context(error::InitDdlManagerSnafu)?;
|
||||||
#[cfg(feature = "enterprise")]
|
#[cfg(feature = "enterprise")]
|
||||||
let trigger_ddl_manager: Option<TriggerDdlManagerRef> = plugins.get();
|
let ddl_manager = {
|
||||||
let ddl_task_executor = Self::create_ddl_task_executor(
|
let trigger_ddl_manager: Option<common_meta::ddl_manager::TriggerDdlManagerRef> =
|
||||||
procedure_manager.clone(),
|
plugins.get();
|
||||||
node_manager.clone(),
|
ddl_manager.with_trigger_ddl_manager(trigger_ddl_manager)
|
||||||
layered_cache_registry.clone(),
|
};
|
||||||
table_metadata_manager,
|
let ddl_task_executor: ProcedureExecutorRef = Arc::new(ddl_manager);
|
||||||
table_meta_allocator,
|
|
||||||
flow_metadata_manager,
|
|
||||||
flow_meta_allocator,
|
|
||||||
#[cfg(feature = "enterprise")]
|
|
||||||
trigger_ddl_manager,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let fe_instance = FrontendBuilder::new(
|
let fe_instance = FrontendBuilder::new(
|
||||||
fe_opts.clone(),
|
fe_opts.clone(),
|
||||||
@@ -679,41 +683,6 @@ impl StartCommand {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub async fn create_ddl_task_executor(
|
|
||||||
procedure_manager: ProcedureManagerRef,
|
|
||||||
node_manager: NodeManagerRef,
|
|
||||||
cache_invalidator: CacheInvalidatorRef,
|
|
||||||
table_metadata_manager: TableMetadataManagerRef,
|
|
||||||
table_metadata_allocator: TableMetadataAllocatorRef,
|
|
||||||
flow_metadata_manager: FlowMetadataManagerRef,
|
|
||||||
flow_metadata_allocator: FlowMetadataAllocatorRef,
|
|
||||||
#[cfg(feature = "enterprise")] trigger_ddl_manager: Option<TriggerDdlManagerRef>,
|
|
||||||
) -> Result<ProcedureExecutorRef> {
|
|
||||||
let procedure_executor: ProcedureExecutorRef = Arc::new(
|
|
||||||
DdlManager::try_new(
|
|
||||||
DdlContext {
|
|
||||||
node_manager,
|
|
||||||
cache_invalidator,
|
|
||||||
memory_region_keeper: Arc::new(MemoryRegionKeeper::default()),
|
|
||||||
leader_region_registry: Arc::new(LeaderRegionRegistry::default()),
|
|
||||||
table_metadata_manager,
|
|
||||||
table_metadata_allocator,
|
|
||||||
flow_metadata_manager,
|
|
||||||
flow_metadata_allocator,
|
|
||||||
region_failure_detector_controller: Arc::new(NoopRegionFailureDetectorControl),
|
|
||||||
},
|
|
||||||
procedure_manager,
|
|
||||||
true,
|
|
||||||
#[cfg(feature = "enterprise")]
|
|
||||||
trigger_ddl_manager,
|
|
||||||
)
|
|
||||||
.context(error::InitDdlManagerSnafu)?,
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(procedure_executor)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn create_table_metadata_manager(
|
pub async fn create_table_metadata_manager(
|
||||||
kv_backend: KvBackendRef,
|
kv_backend: KvBackendRef,
|
||||||
) -> Result<TableMetadataManagerRef> {
|
) -> Result<TableMetadataManagerRef> {
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use std::path::Path;
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use cmd::options::GreptimeOptions;
|
use cmd::options::GreptimeOptions;
|
||||||
@@ -58,12 +57,7 @@ fn test_load_datanode_example_config() {
|
|||||||
metadata_cache_tti: Duration::from_secs(300),
|
metadata_cache_tti: Duration::from_secs(300),
|
||||||
}),
|
}),
|
||||||
wal: DatanodeWalConfig::RaftEngine(RaftEngineConfig {
|
wal: DatanodeWalConfig::RaftEngine(RaftEngineConfig {
|
||||||
dir: Some(
|
dir: Some(format!("{}/{}", DEFAULT_DATA_HOME, WAL_DIR)),
|
||||||
Path::new(DEFAULT_DATA_HOME)
|
|
||||||
.join(WAL_DIR)
|
|
||||||
.to_string_lossy()
|
|
||||||
.to_string(),
|
|
||||||
),
|
|
||||||
sync_period: Some(Duration::from_secs(10)),
|
sync_period: Some(Duration::from_secs(10)),
|
||||||
recovery_parallelism: 2,
|
recovery_parallelism: 2,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@@ -86,10 +80,7 @@ fn test_load_datanode_example_config() {
|
|||||||
],
|
],
|
||||||
logging: LoggingOptions {
|
logging: LoggingOptions {
|
||||||
level: Some("info".to_string()),
|
level: Some("info".to_string()),
|
||||||
dir: Path::new(DEFAULT_DATA_HOME)
|
dir: format!("{}/{}", DEFAULT_DATA_HOME, DEFAULT_LOGGING_DIR),
|
||||||
.join(DEFAULT_LOGGING_DIR)
|
|
||||||
.to_string_lossy()
|
|
||||||
.to_string(),
|
|
||||||
otlp_endpoint: Some(DEFAULT_OTLP_ENDPOINT.to_string()),
|
otlp_endpoint: Some(DEFAULT_OTLP_ENDPOINT.to_string()),
|
||||||
tracing_sample_ratio: Some(Default::default()),
|
tracing_sample_ratio: Some(Default::default()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@@ -132,10 +123,7 @@ fn test_load_frontend_example_config() {
|
|||||||
}),
|
}),
|
||||||
logging: LoggingOptions {
|
logging: LoggingOptions {
|
||||||
level: Some("info".to_string()),
|
level: Some("info".to_string()),
|
||||||
dir: Path::new(DEFAULT_DATA_HOME)
|
dir: format!("{}/{}", DEFAULT_DATA_HOME, DEFAULT_LOGGING_DIR),
|
||||||
.join(DEFAULT_LOGGING_DIR)
|
|
||||||
.to_string_lossy()
|
|
||||||
.to_string(),
|
|
||||||
otlp_endpoint: Some(DEFAULT_OTLP_ENDPOINT.to_string()),
|
otlp_endpoint: Some(DEFAULT_OTLP_ENDPOINT.to_string()),
|
||||||
tracing_sample_ratio: Some(Default::default()),
|
tracing_sample_ratio: Some(Default::default()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@@ -182,10 +170,7 @@ fn test_load_metasrv_example_config() {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
logging: LoggingOptions {
|
logging: LoggingOptions {
|
||||||
dir: Path::new(DEFAULT_DATA_HOME)
|
dir: format!("{}/{}", DEFAULT_DATA_HOME, DEFAULT_LOGGING_DIR),
|
||||||
.join(DEFAULT_LOGGING_DIR)
|
|
||||||
.to_string_lossy()
|
|
||||||
.to_string(),
|
|
||||||
level: Some("info".to_string()),
|
level: Some("info".to_string()),
|
||||||
otlp_endpoint: Some(DEFAULT_OTLP_ENDPOINT.to_string()),
|
otlp_endpoint: Some(DEFAULT_OTLP_ENDPOINT.to_string()),
|
||||||
tracing_sample_ratio: Some(Default::default()),
|
tracing_sample_ratio: Some(Default::default()),
|
||||||
@@ -220,12 +205,7 @@ fn test_load_standalone_example_config() {
|
|||||||
component: StandaloneOptions {
|
component: StandaloneOptions {
|
||||||
default_timezone: Some("UTC".to_string()),
|
default_timezone: Some("UTC".to_string()),
|
||||||
wal: DatanodeWalConfig::RaftEngine(RaftEngineConfig {
|
wal: DatanodeWalConfig::RaftEngine(RaftEngineConfig {
|
||||||
dir: Some(
|
dir: Some(format!("{}/{}", DEFAULT_DATA_HOME, WAL_DIR)),
|
||||||
Path::new(DEFAULT_DATA_HOME)
|
|
||||||
.join(WAL_DIR)
|
|
||||||
.to_string_lossy()
|
|
||||||
.to_string(),
|
|
||||||
),
|
|
||||||
sync_period: Some(Duration::from_secs(10)),
|
sync_period: Some(Duration::from_secs(10)),
|
||||||
recovery_parallelism: 2,
|
recovery_parallelism: 2,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@@ -248,10 +228,7 @@ fn test_load_standalone_example_config() {
|
|||||||
},
|
},
|
||||||
logging: LoggingOptions {
|
logging: LoggingOptions {
|
||||||
level: Some("info".to_string()),
|
level: Some("info".to_string()),
|
||||||
dir: Path::new(DEFAULT_DATA_HOME)
|
dir: format!("{}/{}", DEFAULT_DATA_HOME, DEFAULT_LOGGING_DIR),
|
||||||
.join(DEFAULT_LOGGING_DIR)
|
|
||||||
.to_string_lossy()
|
|
||||||
.to_string(),
|
|
||||||
otlp_endpoint: Some(DEFAULT_OTLP_ENDPOINT.to_string()),
|
otlp_endpoint: Some(DEFAULT_OTLP_ENDPOINT.to_string()),
|
||||||
tracing_sample_ratio: Some(Default::default()),
|
tracing_sample_ratio: Some(Default::default()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|||||||
240
src/common/base/src/cancellation.rs
Normal file
240
src/common/base/src/cancellation.rs
Normal file
@@ -0,0 +1,240 @@
|
|||||||
|
// Copyright 2023 Greptime Team
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
//! [CancellationHandle] is used to compose with manual implementation of [futures::future::Future]
|
||||||
|
//! or [futures::stream::Stream] to facilitate cancellation.
|
||||||
|
//! See example in [frontend::stream_wrapper::CancellableStreamWrapper] and [CancellableFuture].
|
||||||
|
|
||||||
|
use std::fmt::{Debug, Display, Formatter};
|
||||||
|
use std::future::Future;
|
||||||
|
use std::pin::Pin;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::task::{Context, Poll};
|
||||||
|
|
||||||
|
use futures::task::AtomicWaker;
|
||||||
|
use pin_project::pin_project;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct CancellationHandle {
|
||||||
|
waker: AtomicWaker,
|
||||||
|
cancelled: AtomicBool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for CancellationHandle {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("CancellationHandle")
|
||||||
|
.field("cancelled", &self.is_cancelled())
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CancellationHandle {
|
||||||
|
pub fn waker(&self) -> &AtomicWaker {
|
||||||
|
&self.waker
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cancels a future or stream.
|
||||||
|
pub fn cancel(&self) {
|
||||||
|
if self
|
||||||
|
.cancelled
|
||||||
|
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
self.waker.wake();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Is this handle cancelled.
|
||||||
|
pub fn is_cancelled(&self) -> bool {
|
||||||
|
self.cancelled.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pin_project]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct CancellableFuture<T> {
|
||||||
|
#[pin]
|
||||||
|
fut: T,
|
||||||
|
handle: Arc<CancellationHandle>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> CancellableFuture<T> {
|
||||||
|
pub fn new(fut: T, handle: Arc<CancellationHandle>) -> Self {
|
||||||
|
Self { fut, handle }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Future for CancellableFuture<T>
|
||||||
|
where
|
||||||
|
T: Future,
|
||||||
|
{
|
||||||
|
type Output = Result<T::Output, Cancelled>;
|
||||||
|
|
||||||
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let this = self.as_mut().project();
|
||||||
|
// Check if the task has been aborted
|
||||||
|
if this.handle.is_cancelled() {
|
||||||
|
return Poll::Ready(Err(Cancelled));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Poll::Ready(x) = this.fut.poll(cx) {
|
||||||
|
return Poll::Ready(Ok(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.handle.waker().register(cx.waker());
|
||||||
|
if this.handle.is_cancelled() {
|
||||||
|
return Poll::Ready(Err(Cancelled));
|
||||||
|
}
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct Cancelled;
|
||||||
|
|
||||||
|
impl Display for Cancelled {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "Future has been cancelled")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use tokio::time::{sleep, timeout};
|
||||||
|
|
||||||
|
use crate::cancellation::{CancellableFuture, CancellationHandle, Cancelled};
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_cancellable_future_completes_normally() {
|
||||||
|
let handle = Arc::new(CancellationHandle::default());
|
||||||
|
let future = async { 42 };
|
||||||
|
let cancellable = CancellableFuture::new(future, handle);
|
||||||
|
|
||||||
|
let result = cancellable.await;
|
||||||
|
assert!(result.is_ok());
|
||||||
|
assert_eq!(result.unwrap(), 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_cancellable_future_cancelled_before_start() {
|
||||||
|
let handle = Arc::new(CancellationHandle::default());
|
||||||
|
handle.cancel();
|
||||||
|
|
||||||
|
let future = async { 42 };
|
||||||
|
let cancellable = CancellableFuture::new(future, handle);
|
||||||
|
|
||||||
|
let result = cancellable.await;
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert!(matches!(result.unwrap_err(), Cancelled));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_cancellable_future_cancelled_during_execution() {
|
||||||
|
let handle = Arc::new(CancellationHandle::default());
|
||||||
|
let handle_clone = handle.clone();
|
||||||
|
|
||||||
|
// Create a future that sleeps for a long time
|
||||||
|
let future = async {
|
||||||
|
sleep(Duration::from_secs(10)).await;
|
||||||
|
42
|
||||||
|
};
|
||||||
|
let cancellable = CancellableFuture::new(future, handle);
|
||||||
|
|
||||||
|
// Cancel the future after a short delay
|
||||||
|
tokio::spawn(async move {
|
||||||
|
sleep(Duration::from_millis(50)).await;
|
||||||
|
handle_clone.cancel();
|
||||||
|
});
|
||||||
|
|
||||||
|
let result = cancellable.await;
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert!(matches!(result.unwrap_err(), Cancelled));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_cancellable_future_completes_before_cancellation() {
|
||||||
|
let handle = Arc::new(CancellationHandle::default());
|
||||||
|
let handle_clone = handle.clone();
|
||||||
|
|
||||||
|
// Create a future that completes quickly
|
||||||
|
let future = async {
|
||||||
|
sleep(Duration::from_millis(10)).await;
|
||||||
|
42
|
||||||
|
};
|
||||||
|
let cancellable = CancellableFuture::new(future, handle);
|
||||||
|
|
||||||
|
// Try to cancel after the future should have completed
|
||||||
|
tokio::spawn(async move {
|
||||||
|
sleep(Duration::from_millis(100)).await;
|
||||||
|
handle_clone.cancel();
|
||||||
|
});
|
||||||
|
|
||||||
|
let result = cancellable.await;
|
||||||
|
assert!(result.is_ok());
|
||||||
|
assert_eq!(result.unwrap(), 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_cancellation_handle_is_cancelled() {
|
||||||
|
let handle = CancellationHandle::default();
|
||||||
|
assert!(!handle.is_cancelled());
|
||||||
|
|
||||||
|
handle.cancel();
|
||||||
|
assert!(handle.is_cancelled());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_multiple_cancellable_futures_with_same_handle() {
|
||||||
|
let handle = Arc::new(CancellationHandle::default());
|
||||||
|
|
||||||
|
let future1 = CancellableFuture::new(async { 1 }, handle.clone());
|
||||||
|
let future2 = CancellableFuture::new(async { 2 }, handle.clone());
|
||||||
|
|
||||||
|
// Cancel before starting
|
||||||
|
handle.cancel();
|
||||||
|
|
||||||
|
let (result1, result2) = tokio::join!(future1, future2);
|
||||||
|
|
||||||
|
assert!(result1.is_err());
|
||||||
|
assert!(result2.is_err());
|
||||||
|
assert!(matches!(result1.unwrap_err(), Cancelled));
|
||||||
|
assert!(matches!(result2.unwrap_err(), Cancelled));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_cancellable_future_with_timeout() {
|
||||||
|
let handle = Arc::new(CancellationHandle::default());
|
||||||
|
let future = async {
|
||||||
|
sleep(Duration::from_secs(1)).await;
|
||||||
|
42
|
||||||
|
};
|
||||||
|
let cancellable = CancellableFuture::new(future, handle.clone());
|
||||||
|
|
||||||
|
// Use timeout to ensure the test doesn't hang
|
||||||
|
let result = timeout(Duration::from_millis(100), cancellable).await;
|
||||||
|
|
||||||
|
// Should timeout because the future takes 1 second but we timeout after 100ms
|
||||||
|
assert!(result.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_cancelled_display() {
|
||||||
|
let cancelled = Cancelled;
|
||||||
|
assert_eq!(format!("{}", cancelled), "Future has been cancelled");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
pub mod bit_vec;
|
pub mod bit_vec;
|
||||||
pub mod bytes;
|
pub mod bytes;
|
||||||
|
pub mod cancellation;
|
||||||
pub mod plugins;
|
pub mod plugins;
|
||||||
pub mod range_read;
|
pub mod range_read;
|
||||||
#[allow(clippy::all)]
|
#[allow(clippy::all)]
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ pub enum Error {
|
|||||||
location: Location,
|
location: Location,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[snafu(display("Failed to invoke list process service"))]
|
#[snafu(display("Failed to invoke frontend service"))]
|
||||||
ListProcess {
|
InvokeFrontend {
|
||||||
#[snafu(source)]
|
#[snafu(source)]
|
||||||
error: tonic::Status,
|
error: tonic::Status,
|
||||||
#[snafu(implicit)]
|
#[snafu(implicit)]
|
||||||
@@ -67,7 +67,7 @@ impl ErrorExt for Error {
|
|||||||
External { source, .. } => source.status_code(),
|
External { source, .. } => source.status_code(),
|
||||||
Meta { source, .. } => source.status_code(),
|
Meta { source, .. } => source.status_code(),
|
||||||
ParseProcessId { .. } => StatusCode::InvalidArguments,
|
ParseProcessId { .. } => StatusCode::InvalidArguments,
|
||||||
ListProcess { .. } => StatusCode::External,
|
InvokeFrontend { .. } => StatusCode::Unexpected,
|
||||||
CreateChannel { source, .. } => source.status_code(),
|
CreateChannel { source, .. } => source.status_code(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ pub mod selector;
|
|||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub struct DisplayProcessId {
|
pub struct DisplayProcessId {
|
||||||
pub server_addr: String,
|
pub server_addr: String,
|
||||||
pub id: u64,
|
pub id: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for DisplayProcessId {
|
impl Display for DisplayProcessId {
|
||||||
@@ -44,7 +44,7 @@ impl TryFrom<&str> for DisplayProcessId {
|
|||||||
let id = split
|
let id = split
|
||||||
.next()
|
.next()
|
||||||
.context(error::ParseProcessIdSnafu { s: value })?;
|
.context(error::ParseProcessIdSnafu { s: value })?;
|
||||||
let id = u64::from_str(id)
|
let id = u32::from_str(id)
|
||||||
.ok()
|
.ok()
|
||||||
.context(error::ParseProcessIdSnafu { s: value })?;
|
.context(error::ParseProcessIdSnafu { s: value })?;
|
||||||
Ok(DisplayProcessId { server_addr, id })
|
Ok(DisplayProcessId { server_addr, id })
|
||||||
|
|||||||
@@ -12,13 +12,18 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
use std::fmt::Debug;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use common_grpc::channel_manager::{ChannelConfig, ChannelManager};
|
use common_grpc::channel_manager::{ChannelConfig, ChannelManager};
|
||||||
use common_meta::cluster::{ClusterInfo, NodeInfo, Role};
|
use common_meta::cluster::{ClusterInfo, NodeInfo, Role};
|
||||||
use greptime_proto::v1::frontend::{frontend_client, ListProcessRequest, ListProcessResponse};
|
use greptime_proto::v1::frontend::{
|
||||||
|
frontend_client, KillProcessRequest, KillProcessResponse, ListProcessRequest,
|
||||||
|
ListProcessResponse,
|
||||||
|
};
|
||||||
use meta_client::MetaClientRef;
|
use meta_client::MetaClientRef;
|
||||||
use snafu::ResultExt;
|
use snafu::ResultExt;
|
||||||
|
use tonic::Response;
|
||||||
|
|
||||||
use crate::error;
|
use crate::error;
|
||||||
use crate::error::{MetaSnafu, Result};
|
use crate::error::{MetaSnafu, Result};
|
||||||
@@ -26,20 +31,30 @@ use crate::error::{MetaSnafu, Result};
|
|||||||
pub type FrontendClientPtr = Box<dyn FrontendClient>;
|
pub type FrontendClientPtr = Box<dyn FrontendClient>;
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
pub trait FrontendClient: Send {
|
pub trait FrontendClient: Send + Debug {
|
||||||
async fn list_process(&mut self, req: ListProcessRequest) -> Result<ListProcessResponse>;
|
async fn list_process(&mut self, req: ListProcessRequest) -> Result<ListProcessResponse>;
|
||||||
|
|
||||||
|
async fn kill_process(&mut self, req: KillProcessRequest) -> Result<KillProcessResponse>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl FrontendClient for frontend_client::FrontendClient<tonic::transport::channel::Channel> {
|
impl FrontendClient for frontend_client::FrontendClient<tonic::transport::channel::Channel> {
|
||||||
async fn list_process(&mut self, req: ListProcessRequest) -> Result<ListProcessResponse> {
|
async fn list_process(&mut self, req: ListProcessRequest) -> Result<ListProcessResponse> {
|
||||||
let response: ListProcessResponse = frontend_client::FrontendClient::<
|
frontend_client::FrontendClient::<tonic::transport::channel::Channel>::list_process(
|
||||||
tonic::transport::channel::Channel,
|
self, req,
|
||||||
>::list_process(self, req)
|
)
|
||||||
.await
|
.await
|
||||||
.context(error::ListProcessSnafu)?
|
.context(error::InvokeFrontendSnafu)
|
||||||
.into_inner();
|
.map(Response::into_inner)
|
||||||
Ok(response)
|
}
|
||||||
|
|
||||||
|
async fn kill_process(&mut self, req: KillProcessRequest) -> Result<KillProcessResponse> {
|
||||||
|
frontend_client::FrontendClient::<tonic::transport::channel::Channel>::kill_process(
|
||||||
|
self, req,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.context(error::InvokeFrontendSnafu)
|
||||||
|
.map(Response::into_inner)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,8 +14,8 @@
|
|||||||
|
|
||||||
use crate::function_registry::FunctionRegistry;
|
use crate::function_registry::FunctionRegistry;
|
||||||
|
|
||||||
pub(crate) mod hll;
|
pub mod hll;
|
||||||
mod uddsketch;
|
pub mod uddsketch;
|
||||||
|
|
||||||
pub(crate) struct ApproximateFunction;
|
pub(crate) struct ApproximateFunction;
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use build::BuildFunction;
|
use build::BuildFunction;
|
||||||
use database::{
|
use database::{
|
||||||
CurrentSchemaFunction, DatabaseFunction, ReadPreferenceFunction, SessionUserFunction,
|
ConnectionIdFunction, CurrentSchemaFunction, DatabaseFunction, PgBackendPidFunction,
|
||||||
|
ReadPreferenceFunction, SessionUserFunction,
|
||||||
};
|
};
|
||||||
use pg_catalog::PGCatalogFunction;
|
use pg_catalog::PGCatalogFunction;
|
||||||
use procedure_state::ProcedureStateFunction;
|
use procedure_state::ProcedureStateFunction;
|
||||||
@@ -42,6 +43,8 @@ impl SystemFunction {
|
|||||||
registry.register_scalar(DatabaseFunction);
|
registry.register_scalar(DatabaseFunction);
|
||||||
registry.register_scalar(SessionUserFunction);
|
registry.register_scalar(SessionUserFunction);
|
||||||
registry.register_scalar(ReadPreferenceFunction);
|
registry.register_scalar(ReadPreferenceFunction);
|
||||||
|
registry.register_scalar(PgBackendPidFunction);
|
||||||
|
registry.register_scalar(ConnectionIdFunction);
|
||||||
registry.register_scalar(TimezoneFunction);
|
registry.register_scalar(TimezoneFunction);
|
||||||
registry.register_async(Arc::new(ProcedureStateFunction));
|
registry.register_async(Arc::new(ProcedureStateFunction));
|
||||||
PGCatalogFunction::register(registry);
|
PGCatalogFunction::register(registry);
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ use std::sync::Arc;
|
|||||||
use common_query::error::Result;
|
use common_query::error::Result;
|
||||||
use common_query::prelude::{Signature, Volatility};
|
use common_query::prelude::{Signature, Volatility};
|
||||||
use datatypes::prelude::{ConcreteDataType, ScalarVector};
|
use datatypes::prelude::{ConcreteDataType, ScalarVector};
|
||||||
use datatypes::vectors::{StringVector, VectorRef};
|
use datatypes::vectors::{StringVector, UInt32Vector, VectorRef};
|
||||||
|
use derive_more::Display;
|
||||||
|
|
||||||
use crate::function::{Function, FunctionContext};
|
use crate::function::{Function, FunctionContext};
|
||||||
|
|
||||||
@@ -32,10 +33,20 @@ pub struct SessionUserFunction;
|
|||||||
|
|
||||||
pub struct ReadPreferenceFunction;
|
pub struct ReadPreferenceFunction;
|
||||||
|
|
||||||
|
#[derive(Display)]
|
||||||
|
#[display("{}", self.name())]
|
||||||
|
pub struct PgBackendPidFunction;
|
||||||
|
|
||||||
|
#[derive(Display)]
|
||||||
|
#[display("{}", self.name())]
|
||||||
|
pub struct ConnectionIdFunction;
|
||||||
|
|
||||||
const DATABASE_FUNCTION_NAME: &str = "database";
|
const DATABASE_FUNCTION_NAME: &str = "database";
|
||||||
const CURRENT_SCHEMA_FUNCTION_NAME: &str = "current_schema";
|
const CURRENT_SCHEMA_FUNCTION_NAME: &str = "current_schema";
|
||||||
const SESSION_USER_FUNCTION_NAME: &str = "session_user";
|
const SESSION_USER_FUNCTION_NAME: &str = "session_user";
|
||||||
const READ_PREFERENCE_FUNCTION_NAME: &str = "read_preference";
|
const READ_PREFERENCE_FUNCTION_NAME: &str = "read_preference";
|
||||||
|
const PG_BACKEND_PID: &str = "pg_backend_pid";
|
||||||
|
const CONNECTION_ID: &str = "connection_id";
|
||||||
|
|
||||||
impl Function for DatabaseFunction {
|
impl Function for DatabaseFunction {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
@@ -117,6 +128,46 @@ impl Function for ReadPreferenceFunction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Function for PgBackendPidFunction {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
PG_BACKEND_PID
|
||||||
|
}
|
||||||
|
|
||||||
|
fn return_type(&self, _input_types: &[ConcreteDataType]) -> Result<ConcreteDataType> {
|
||||||
|
Ok(ConcreteDataType::uint64_datatype())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::nullary(Volatility::Immutable)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval(&self, func_ctx: &FunctionContext, _columns: &[VectorRef]) -> Result<VectorRef> {
|
||||||
|
let pid = func_ctx.query_ctx.process_id();
|
||||||
|
|
||||||
|
Ok(Arc::new(UInt32Vector::from_slice([pid])) as _)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Function for ConnectionIdFunction {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
CONNECTION_ID
|
||||||
|
}
|
||||||
|
|
||||||
|
fn return_type(&self, _input_types: &[ConcreteDataType]) -> Result<ConcreteDataType> {
|
||||||
|
Ok(ConcreteDataType::uint64_datatype())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::nullary(Volatility::Immutable)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval(&self, func_ctx: &FunctionContext, _columns: &[VectorRef]) -> Result<VectorRef> {
|
||||||
|
let pid = func_ctx.query_ctx.process_id();
|
||||||
|
|
||||||
|
Ok(Arc::new(UInt32Vector::from_slice([pid])) as _)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for DatabaseFunction {
|
impl fmt::Display for DatabaseFunction {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "DATABASE")
|
write!(f, "DATABASE")
|
||||||
|
|||||||
@@ -12,8 +12,8 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{env, fmt};
|
|
||||||
|
|
||||||
use common_query::error::Result;
|
use common_query::error::Result;
|
||||||
use common_query::prelude::{Signature, Volatility};
|
use common_query::prelude::{Signature, Volatility};
|
||||||
@@ -47,7 +47,7 @@ impl Function for PGVersionFunction {
|
|||||||
fn eval(&self, _func_ctx: &FunctionContext, _columns: &[VectorRef]) -> Result<VectorRef> {
|
fn eval(&self, _func_ctx: &FunctionContext, _columns: &[VectorRef]) -> Result<VectorRef> {
|
||||||
let result = StringVector::from(vec![format!(
|
let result = StringVector::from(vec![format!(
|
||||||
"PostgreSQL 16.3 GreptimeDB {}",
|
"PostgreSQL 16.3 GreptimeDB {}",
|
||||||
env!("CARGO_PKG_VERSION")
|
common_version::version()
|
||||||
)]);
|
)]);
|
||||||
Ok(Arc::new(result))
|
Ok(Arc::new(result))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,8 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{env, fmt};
|
|
||||||
|
|
||||||
use common_query::error::Result;
|
use common_query::error::Result;
|
||||||
use common_query::prelude::{Signature, Volatility};
|
use common_query::prelude::{Signature, Volatility};
|
||||||
@@ -52,13 +52,13 @@ impl Function for VersionFunction {
|
|||||||
"{}-greptimedb-{}",
|
"{}-greptimedb-{}",
|
||||||
std::env::var("GREPTIMEDB_MYSQL_SERVER_VERSION")
|
std::env::var("GREPTIMEDB_MYSQL_SERVER_VERSION")
|
||||||
.unwrap_or_else(|_| "8.4.2".to_string()),
|
.unwrap_or_else(|_| "8.4.2".to_string()),
|
||||||
env!("CARGO_PKG_VERSION")
|
common_version::version()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Channel::Postgres => {
|
Channel::Postgres => {
|
||||||
format!("16.3-greptimedb-{}", env!("CARGO_PKG_VERSION"))
|
format!("16.3-greptimedb-{}", common_version::version())
|
||||||
}
|
}
|
||||||
_ => env!("CARGO_PKG_VERSION").to_string(),
|
_ => common_version::version().to_string(),
|
||||||
};
|
};
|
||||||
let result = StringVector::from(vec![version]);
|
let result = StringVector::from(vec![version]);
|
||||||
Ok(Arc::new(result))
|
Ok(Arc::new(result))
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ use table::requests::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::error::{
|
use crate::error::{
|
||||||
InvalidColumnDefSnafu, InvalidSetFulltextOptionRequestSnafu,
|
InvalidColumnDefSnafu, InvalidIndexOptionSnafu, InvalidSetFulltextOptionRequestSnafu,
|
||||||
InvalidSetSkippingIndexOptionRequestSnafu, InvalidSetTableOptionRequestSnafu,
|
InvalidSetSkippingIndexOptionRequestSnafu, InvalidSetTableOptionRequestSnafu,
|
||||||
InvalidUnsetTableOptionRequestSnafu, MissingAlterIndexOptionSnafu, MissingFieldSnafu,
|
InvalidUnsetTableOptionRequestSnafu, MissingAlterIndexOptionSnafu, MissingFieldSnafu,
|
||||||
MissingTimestampColumnSnafu, Result, UnknownLocationTypeSnafu,
|
MissingTimestampColumnSnafu, Result, UnknownLocationTypeSnafu,
|
||||||
@@ -126,18 +126,21 @@ pub fn alter_expr_to_request(table_id: TableId, expr: AlterTableExpr) -> Result<
|
|||||||
api::v1::set_index::Options::Fulltext(f) => AlterKind::SetIndex {
|
api::v1::set_index::Options::Fulltext(f) => AlterKind::SetIndex {
|
||||||
options: SetIndexOptions::Fulltext {
|
options: SetIndexOptions::Fulltext {
|
||||||
column_name: f.column_name.clone(),
|
column_name: f.column_name.clone(),
|
||||||
options: FulltextOptions {
|
options: FulltextOptions::new(
|
||||||
enable: f.enable,
|
f.enable,
|
||||||
analyzer: as_fulltext_option_analyzer(
|
as_fulltext_option_analyzer(
|
||||||
Analyzer::try_from(f.analyzer)
|
Analyzer::try_from(f.analyzer)
|
||||||
.context(InvalidSetFulltextOptionRequestSnafu)?,
|
.context(InvalidSetFulltextOptionRequestSnafu)?,
|
||||||
),
|
),
|
||||||
case_sensitive: f.case_sensitive,
|
f.case_sensitive,
|
||||||
backend: as_fulltext_option_backend(
|
as_fulltext_option_backend(
|
||||||
PbFulltextBackend::try_from(f.backend)
|
PbFulltextBackend::try_from(f.backend)
|
||||||
.context(InvalidSetFulltextOptionRequestSnafu)?,
|
.context(InvalidSetFulltextOptionRequestSnafu)?,
|
||||||
),
|
),
|
||||||
},
|
f.granularity as u32,
|
||||||
|
f.false_positive_rate,
|
||||||
|
)
|
||||||
|
.context(InvalidIndexOptionSnafu)?,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
api::v1::set_index::Options::Inverted(i) => AlterKind::SetIndex {
|
api::v1::set_index::Options::Inverted(i) => AlterKind::SetIndex {
|
||||||
@@ -148,13 +151,15 @@ pub fn alter_expr_to_request(table_id: TableId, expr: AlterTableExpr) -> Result<
|
|||||||
api::v1::set_index::Options::Skipping(s) => AlterKind::SetIndex {
|
api::v1::set_index::Options::Skipping(s) => AlterKind::SetIndex {
|
||||||
options: SetIndexOptions::Skipping {
|
options: SetIndexOptions::Skipping {
|
||||||
column_name: s.column_name,
|
column_name: s.column_name,
|
||||||
options: SkippingIndexOptions {
|
options: SkippingIndexOptions::new(
|
||||||
granularity: s.granularity as u32,
|
s.granularity as u32,
|
||||||
index_type: as_skipping_index_type(
|
s.false_positive_rate,
|
||||||
|
as_skipping_index_type(
|
||||||
PbSkippingIndexType::try_from(s.skipping_index_type)
|
PbSkippingIndexType::try_from(s.skipping_index_type)
|
||||||
.context(InvalidSetSkippingIndexOptionRequestSnafu)?,
|
.context(InvalidSetSkippingIndexOptionRequestSnafu)?,
|
||||||
),
|
),
|
||||||
},
|
)
|
||||||
|
.context(InvalidIndexOptionSnafu)?,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -180,6 +185,22 @@ pub fn alter_expr_to_request(table_id: TableId, expr: AlterTableExpr) -> Result<
|
|||||||
},
|
},
|
||||||
None => return MissingAlterIndexOptionSnafu.fail(),
|
None => return MissingAlterIndexOptionSnafu.fail(),
|
||||||
},
|
},
|
||||||
|
Kind::DropDefaults(o) => {
|
||||||
|
let names = o
|
||||||
|
.drop_defaults
|
||||||
|
.into_iter()
|
||||||
|
.map(|col| {
|
||||||
|
ensure!(
|
||||||
|
!col.column_name.is_empty(),
|
||||||
|
MissingFieldSnafu {
|
||||||
|
field: "column_name"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
Ok(col.column_name)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
AlterKind::DropDefaults { names }
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let request = AlterTableRequest {
|
let request = AlterTableRequest {
|
||||||
|
|||||||
@@ -153,6 +153,14 @@ pub enum Error {
|
|||||||
#[snafu(implicit)]
|
#[snafu(implicit)]
|
||||||
location: Location,
|
location: Location,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Invalid index option"))]
|
||||||
|
InvalidIndexOption {
|
||||||
|
#[snafu(implicit)]
|
||||||
|
location: Location,
|
||||||
|
#[snafu(source)]
|
||||||
|
error: datatypes::error::Error,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
@@ -180,7 +188,8 @@ impl ErrorExt for Error {
|
|||||||
| Error::InvalidUnsetTableOptionRequest { .. }
|
| Error::InvalidUnsetTableOptionRequest { .. }
|
||||||
| Error::InvalidSetFulltextOptionRequest { .. }
|
| Error::InvalidSetFulltextOptionRequest { .. }
|
||||||
| Error::InvalidSetSkippingIndexOptionRequest { .. }
|
| Error::InvalidSetSkippingIndexOptionRequest { .. }
|
||||||
| Error::MissingAlterIndexOption { .. } => StatusCode::InvalidArguments,
|
| Error::MissingAlterIndexOption { .. }
|
||||||
|
| Error::InvalidIndexOption { .. } => StatusCode::InvalidArguments,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -201,8 +201,8 @@ impl ChannelManager {
|
|||||||
"http"
|
"http"
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut endpoint =
|
let mut endpoint = Endpoint::new(format!("{http_prefix}://{addr}"))
|
||||||
Endpoint::new(format!("{http_prefix}://{addr}")).context(CreateChannelSnafu)?;
|
.context(CreateChannelSnafu { addr })?;
|
||||||
|
|
||||||
if let Some(dur) = self.config().timeout {
|
if let Some(dur) = self.config().timeout {
|
||||||
endpoint = endpoint.timeout(dur);
|
endpoint = endpoint.timeout(dur);
|
||||||
@@ -237,7 +237,7 @@ impl ChannelManager {
|
|||||||
if let Some(tls_config) = &self.inner.client_tls_config {
|
if let Some(tls_config) = &self.inner.client_tls_config {
|
||||||
endpoint = endpoint
|
endpoint = endpoint
|
||||||
.tls_config(tls_config.clone())
|
.tls_config(tls_config.clone())
|
||||||
.context(CreateChannelSnafu)?;
|
.context(CreateChannelSnafu { addr })?;
|
||||||
}
|
}
|
||||||
|
|
||||||
endpoint = endpoint
|
endpoint = endpoint
|
||||||
|
|||||||
@@ -52,8 +52,9 @@ pub enum Error {
|
|||||||
location: Location,
|
location: Location,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[snafu(display("Failed to create gRPC channel"))]
|
#[snafu(display("Failed to create gRPC channel from '{addr}'"))]
|
||||||
CreateChannel {
|
CreateChannel {
|
||||||
|
addr: String,
|
||||||
#[snafu(source)]
|
#[snafu(source)]
|
||||||
error: tonic::transport::Error,
|
error: tonic::transport::Error,
|
||||||
#[snafu(implicit)]
|
#[snafu(implicit)]
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ workspace = true
|
|||||||
anymap2 = "0.13.0"
|
anymap2 = "0.13.0"
|
||||||
api.workspace = true
|
api.workspace = true
|
||||||
async-recursion = "1.0"
|
async-recursion = "1.0"
|
||||||
async-stream = "0.3"
|
async-stream.workspace = true
|
||||||
async-trait.workspace = true
|
async-trait.workspace = true
|
||||||
backon = { workspace = true, optional = true }
|
backon = { workspace = true, optional = true }
|
||||||
base64.workspace = true
|
base64.workspace = true
|
||||||
|
|||||||
19
src/common/meta/src/cache/flow/table_flownode.rs
vendored
19
src/common/meta/src/cache/flow/table_flownode.rs
vendored
@@ -15,6 +15,7 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use common_telemetry::info;
|
||||||
use futures::future::BoxFuture;
|
use futures::future::BoxFuture;
|
||||||
use moka::future::Cache;
|
use moka::future::Cache;
|
||||||
use moka::ops::compute::Op;
|
use moka::ops::compute::Op;
|
||||||
@@ -89,6 +90,12 @@ fn init_factory(table_flow_manager: TableFlowManagerRef) -> Initializer<TableId,
|
|||||||
// we have a corresponding cache invalidation mechanism to invalidate `(Key, EmptyHashSet)`.
|
// we have a corresponding cache invalidation mechanism to invalidate `(Key, EmptyHashSet)`.
|
||||||
.map(Arc::new)
|
.map(Arc::new)
|
||||||
.map(Some)
|
.map(Some)
|
||||||
|
.inspect(|set| {
|
||||||
|
info!(
|
||||||
|
"Initialized table_flownode cache for table_id: {}, set: {:?}",
|
||||||
|
table_id, set
|
||||||
|
);
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -167,6 +174,13 @@ fn invalidator<'a>(
|
|||||||
match ident {
|
match ident {
|
||||||
CacheIdent::CreateFlow(create_flow) => handle_create_flow(cache, create_flow).await,
|
CacheIdent::CreateFlow(create_flow) => handle_create_flow(cache, create_flow).await,
|
||||||
CacheIdent::DropFlow(drop_flow) => handle_drop_flow(cache, drop_flow).await,
|
CacheIdent::DropFlow(drop_flow) => handle_drop_flow(cache, drop_flow).await,
|
||||||
|
CacheIdent::FlowNodeAddressChange(node_id) => {
|
||||||
|
info!(
|
||||||
|
"Invalidate flow node cache for node_id in table_flownode: {}",
|
||||||
|
node_id
|
||||||
|
);
|
||||||
|
cache.invalidate_all();
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -174,7 +188,10 @@ fn invalidator<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn filter(ident: &CacheIdent) -> bool {
|
fn filter(ident: &CacheIdent) -> bool {
|
||||||
matches!(ident, CacheIdent::CreateFlow(_) | CacheIdent::DropFlow(_))
|
matches!(
|
||||||
|
ident,
|
||||||
|
CacheIdent::CreateFlow(_) | CacheIdent::DropFlow(_) | CacheIdent::FlowNodeAddressChange(_)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ use crate::key::flow::flow_name::FlowNameKey;
|
|||||||
use crate::key::flow::flow_route::FlowRouteKey;
|
use crate::key::flow::flow_route::FlowRouteKey;
|
||||||
use crate::key::flow::flownode_flow::FlownodeFlowKey;
|
use crate::key::flow::flownode_flow::FlownodeFlowKey;
|
||||||
use crate::key::flow::table_flow::TableFlowKey;
|
use crate::key::flow::table_flow::TableFlowKey;
|
||||||
|
use crate::key::node_address::NodeAddressKey;
|
||||||
use crate::key::schema_name::SchemaNameKey;
|
use crate::key::schema_name::SchemaNameKey;
|
||||||
use crate::key::table_info::TableInfoKey;
|
use crate::key::table_info::TableInfoKey;
|
||||||
use crate::key::table_name::TableNameKey;
|
use crate::key::table_name::TableNameKey;
|
||||||
@@ -53,6 +54,10 @@ pub struct Context {
|
|||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
pub trait CacheInvalidator: Send + Sync {
|
pub trait CacheInvalidator: Send + Sync {
|
||||||
async fn invalidate(&self, ctx: &Context, caches: &[CacheIdent]) -> Result<()>;
|
async fn invalidate(&self, ctx: &Context, caches: &[CacheIdent]) -> Result<()>;
|
||||||
|
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
std::any::type_name::<Self>()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type CacheInvalidatorRef = Arc<dyn CacheInvalidator>;
|
pub type CacheInvalidatorRef = Arc<dyn CacheInvalidator>;
|
||||||
@@ -137,6 +142,13 @@ where
|
|||||||
let key = FlowInfoKey::new(*flow_id);
|
let key = FlowInfoKey::new(*flow_id);
|
||||||
self.invalidate_key(&key.to_bytes()).await;
|
self.invalidate_key(&key.to_bytes()).await;
|
||||||
}
|
}
|
||||||
|
CacheIdent::FlowNodeAddressChange(node_id) => {
|
||||||
|
// other caches doesn't need to be invalidated
|
||||||
|
// since this is only for flownode address change not id change
|
||||||
|
common_telemetry::info!("Invalidate flow node cache for node_id: {}", node_id);
|
||||||
|
let key = NodeAddressKey::with_flownode(*node_id);
|
||||||
|
self.invalidate_key(&key.to_bytes()).await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ use common_procedure::error::{FromJsonSnafu, Result as ProcedureResult, ToJsonSn
|
|||||||
use common_procedure::{Context, LockKey, Procedure, Status};
|
use common_procedure::{Context, LockKey, Procedure, Status};
|
||||||
use common_telemetry::{error, info, warn};
|
use common_telemetry::{error, info, warn};
|
||||||
use futures_util::future;
|
use futures_util::future;
|
||||||
|
pub use region_request::make_alter_region_request;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use snafu::{ensure, ResultExt};
|
use snafu::{ensure, ResultExt};
|
||||||
use store_api::metadata::ColumnMetadata;
|
use store_api::metadata::ColumnMetadata;
|
||||||
|
|||||||
@@ -12,20 +12,18 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use api::v1;
|
|
||||||
use api::v1::alter_table_expr::Kind;
|
use api::v1::alter_table_expr::Kind;
|
||||||
use api::v1::region::{
|
use api::v1::region::{
|
||||||
alter_request, region_request, AddColumn, AddColumns, AlterRequest, AlterRequests,
|
alter_request, region_request, AddColumn, AddColumns, AlterRequest, AlterRequests,
|
||||||
RegionColumnDef, RegionRequest, RegionRequestHeader,
|
RegionColumnDef, RegionRequest, RegionRequestHeader,
|
||||||
};
|
};
|
||||||
|
use api::v1::{self, AlterTableExpr};
|
||||||
use common_telemetry::tracing_context::TracingContext;
|
use common_telemetry::tracing_context::TracingContext;
|
||||||
use store_api::storage::RegionId;
|
use store_api::storage::RegionId;
|
||||||
|
|
||||||
use crate::ddl::alter_logical_tables::AlterLogicalTablesProcedure;
|
use crate::ddl::alter_logical_tables::AlterLogicalTablesProcedure;
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use crate::key::table_info::TableInfoValue;
|
|
||||||
use crate::peer::Peer;
|
use crate::peer::Peer;
|
||||||
use crate::rpc::ddl::AlterTableTask;
|
|
||||||
use crate::rpc::router::{find_leader_regions, RegionRoute};
|
use crate::rpc::router::{find_leader_regions, RegionRoute};
|
||||||
|
|
||||||
impl AlterLogicalTablesProcedure {
|
impl AlterLogicalTablesProcedure {
|
||||||
@@ -62,34 +60,37 @@ impl AlterLogicalTablesProcedure {
|
|||||||
{
|
{
|
||||||
for region_number in ®ions_on_this_peer {
|
for region_number in ®ions_on_this_peer {
|
||||||
let region_id = RegionId::new(table.table_info.ident.table_id, *region_number);
|
let region_id = RegionId::new(table.table_info.ident.table_id, *region_number);
|
||||||
let request = self.make_alter_region_request(region_id, task, table)?;
|
let request = make_alter_region_request(
|
||||||
|
region_id,
|
||||||
|
&task.alter_table,
|
||||||
|
table.table_info.ident.version,
|
||||||
|
);
|
||||||
requests.push(request);
|
requests.push(request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(AlterRequests { requests })
|
Ok(AlterRequests { requests })
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn make_alter_region_request(
|
/// Makes an alter region request.
|
||||||
&self,
|
pub fn make_alter_region_request(
|
||||||
region_id: RegionId,
|
region_id: RegionId,
|
||||||
task: &AlterTableTask,
|
alter_table_expr: &AlterTableExpr,
|
||||||
table: &TableInfoValue,
|
schema_version: u64,
|
||||||
) -> Result<AlterRequest> {
|
) -> AlterRequest {
|
||||||
let region_id = region_id.as_u64();
|
let region_id = region_id.as_u64();
|
||||||
let schema_version = table.table_info.ident.version;
|
let kind = match &alter_table_expr.kind {
|
||||||
let kind = match &task.alter_table.kind {
|
Some(Kind::AddColumns(add_columns)) => Some(alter_request::Kind::AddColumns(
|
||||||
Some(Kind::AddColumns(add_columns)) => Some(alter_request::Kind::AddColumns(
|
to_region_add_columns(add_columns),
|
||||||
to_region_add_columns(add_columns),
|
)),
|
||||||
)),
|
_ => unreachable!(), // Safety: we have checked the kind in check_input_tasks
|
||||||
_ => unreachable!(), // Safety: we have checked the kind in check_input_tasks
|
};
|
||||||
};
|
|
||||||
|
|
||||||
Ok(AlterRequest {
|
AlterRequest {
|
||||||
region_id,
|
region_id,
|
||||||
schema_version,
|
schema_version,
|
||||||
kind,
|
kind,
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -135,6 +135,7 @@ fn create_proto_alter_kind(
|
|||||||
Kind::UnsetTableOptions(v) => Ok(Some(alter_request::Kind::UnsetTableOptions(v.clone()))),
|
Kind::UnsetTableOptions(v) => Ok(Some(alter_request::Kind::UnsetTableOptions(v.clone()))),
|
||||||
Kind::SetIndex(v) => Ok(Some(alter_request::Kind::SetIndex(v.clone()))),
|
Kind::SetIndex(v) => Ok(Some(alter_request::Kind::SetIndex(v.clone()))),
|
||||||
Kind::UnsetIndex(v) => Ok(Some(alter_request::Kind::UnsetIndex(v.clone()))),
|
Kind::UnsetIndex(v) => Ok(Some(alter_request::Kind::UnsetIndex(v.clone()))),
|
||||||
|
Kind::DropDefaults(v) => Ok(Some(alter_request::Kind::DropDefaults(v.clone()))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,8 @@ impl AlterTableProcedure {
|
|||||||
| AlterKind::SetTableOptions { .. }
|
| AlterKind::SetTableOptions { .. }
|
||||||
| AlterKind::UnsetTableOptions { .. }
|
| AlterKind::UnsetTableOptions { .. }
|
||||||
| AlterKind::SetIndex { .. }
|
| AlterKind::SetIndex { .. }
|
||||||
| AlterKind::UnsetIndex { .. } => {}
|
| AlterKind::UnsetIndex { .. }
|
||||||
|
| AlterKind::DropDefaults { .. } => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(new_info)
|
Ok(new_info)
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ use common_procedure::error::{FromJsonSnafu, Result as ProcedureResult, ToJsonSn
|
|||||||
use common_procedure::{Context as ProcedureContext, LockKey, Procedure, Status};
|
use common_procedure::{Context as ProcedureContext, LockKey, Procedure, Status};
|
||||||
use common_telemetry::{debug, error, warn};
|
use common_telemetry::{debug, error, warn};
|
||||||
use futures::future;
|
use futures::future;
|
||||||
|
pub use region_request::create_region_request_builder;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use snafu::{ensure, ResultExt};
|
use snafu::{ensure, ResultExt};
|
||||||
use store_api::metadata::ColumnMetadata;
|
use store_api::metadata::ColumnMetadata;
|
||||||
|
|||||||
@@ -15,16 +15,16 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use api::v1::region::{region_request, CreateRequests, RegionRequest, RegionRequestHeader};
|
use api::v1::region::{region_request, CreateRequests, RegionRequest, RegionRequestHeader};
|
||||||
|
use api::v1::CreateTableExpr;
|
||||||
use common_telemetry::debug;
|
use common_telemetry::debug;
|
||||||
use common_telemetry::tracing_context::TracingContext;
|
use common_telemetry::tracing_context::TracingContext;
|
||||||
use store_api::storage::RegionId;
|
use store_api::storage::{RegionId, TableId};
|
||||||
|
|
||||||
use crate::ddl::create_logical_tables::CreateLogicalTablesProcedure;
|
use crate::ddl::create_logical_tables::CreateLogicalTablesProcedure;
|
||||||
use crate::ddl::create_table_template::{build_template, CreateRequestBuilder};
|
use crate::ddl::create_table_template::{build_template, CreateRequestBuilder};
|
||||||
use crate::ddl::utils::region_storage_path;
|
use crate::ddl::utils::region_storage_path;
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use crate::peer::Peer;
|
use crate::peer::Peer;
|
||||||
use crate::rpc::ddl::CreateTableTask;
|
|
||||||
use crate::rpc::router::{find_leader_regions, RegionRoute};
|
use crate::rpc::router::{find_leader_regions, RegionRoute};
|
||||||
|
|
||||||
impl CreateLogicalTablesProcedure {
|
impl CreateLogicalTablesProcedure {
|
||||||
@@ -45,13 +45,15 @@ impl CreateLogicalTablesProcedure {
|
|||||||
let catalog = &create_table_expr.catalog_name;
|
let catalog = &create_table_expr.catalog_name;
|
||||||
let schema = &create_table_expr.schema_name;
|
let schema = &create_table_expr.schema_name;
|
||||||
let logical_table_id = task.table_info.ident.table_id;
|
let logical_table_id = task.table_info.ident.table_id;
|
||||||
|
let physical_table_id = self.data.physical_table_id;
|
||||||
let storage_path = region_storage_path(catalog, schema);
|
let storage_path = region_storage_path(catalog, schema);
|
||||||
let request_builder = self.create_region_request_builder(task)?;
|
let request_builder =
|
||||||
|
create_region_request_builder(&task.create_table, physical_table_id)?;
|
||||||
|
|
||||||
for region_number in ®ions_on_this_peer {
|
for region_number in ®ions_on_this_peer {
|
||||||
let region_id = RegionId::new(logical_table_id, *region_number);
|
let region_id = RegionId::new(logical_table_id, *region_number);
|
||||||
let one_region_request =
|
let one_region_request =
|
||||||
request_builder.build_one(region_id, storage_path.clone(), &HashMap::new())?;
|
request_builder.build_one(region_id, storage_path.clone(), &HashMap::new());
|
||||||
requests.push(one_region_request);
|
requests.push(one_region_request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -69,16 +71,13 @@ impl CreateLogicalTablesProcedure {
|
|||||||
body: Some(region_request::Body::Creates(CreateRequests { requests })),
|
body: Some(region_request::Body::Creates(CreateRequests { requests })),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
fn create_region_request_builder(
|
|
||||||
&self,
|
/// Creates a region request builder.
|
||||||
task: &CreateTableTask,
|
pub fn create_region_request_builder(
|
||||||
) -> Result<CreateRequestBuilder> {
|
create_table_expr: &CreateTableExpr,
|
||||||
let create_expr = &task.create_table;
|
physical_table_id: TableId,
|
||||||
let template = build_template(create_expr)?;
|
) -> Result<CreateRequestBuilder> {
|
||||||
Ok(CreateRequestBuilder::new(
|
let template = build_template(create_table_expr)?;
|
||||||
template,
|
Ok(CreateRequestBuilder::new(template, Some(physical_table_id)))
|
||||||
Some(self.data.physical_table_id),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -218,11 +218,8 @@ impl CreateTableProcedure {
|
|||||||
let mut requests = Vec::with_capacity(regions.len());
|
let mut requests = Vec::with_capacity(regions.len());
|
||||||
for region_number in regions {
|
for region_number in regions {
|
||||||
let region_id = RegionId::new(self.table_id(), region_number);
|
let region_id = RegionId::new(self.table_id(), region_number);
|
||||||
let create_region_request = request_builder.build_one(
|
let create_region_request =
|
||||||
region_id,
|
request_builder.build_one(region_id, storage_path.clone(), region_wal_options);
|
||||||
storage_path.clone(),
|
|
||||||
region_wal_options,
|
|
||||||
)?;
|
|
||||||
requests.push(PbRegionRequest::Create(create_region_request));
|
requests.push(PbRegionRequest::Create(create_region_request));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -105,12 +105,12 @@ impl CreateRequestBuilder {
|
|||||||
&self.template
|
&self.template
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn build_one(
|
pub fn build_one(
|
||||||
&self,
|
&self,
|
||||||
region_id: RegionId,
|
region_id: RegionId,
|
||||||
storage_path: String,
|
storage_path: String,
|
||||||
region_wal_options: &HashMap<RegionNumber, String>,
|
region_wal_options: &HashMap<RegionNumber, String>,
|
||||||
) -> Result<CreateRequest> {
|
) -> CreateRequest {
|
||||||
let mut request = self.template.clone();
|
let mut request = self.template.clone();
|
||||||
|
|
||||||
request.region_id = region_id.as_u64();
|
request.region_id = region_id.as_u64();
|
||||||
@@ -130,6 +130,6 @@ impl CreateRequestBuilder {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(request)
|
request
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use common_procedure::Status;
|
use common_procedure::Status;
|
||||||
use common_telemetry::info;
|
use common_telemetry::info;
|
||||||
@@ -25,7 +24,7 @@ use table::table_name::TableName;
|
|||||||
use crate::ddl::drop_database::cursor::DropDatabaseCursor;
|
use crate::ddl::drop_database::cursor::DropDatabaseCursor;
|
||||||
use crate::ddl::drop_database::{DropDatabaseContext, DropTableTarget, State};
|
use crate::ddl::drop_database::{DropDatabaseContext, DropTableTarget, State};
|
||||||
use crate::ddl::drop_table::executor::DropTableExecutor;
|
use crate::ddl::drop_table::executor::DropTableExecutor;
|
||||||
use crate::ddl::utils::extract_region_wal_options;
|
use crate::ddl::utils::get_region_wal_options;
|
||||||
use crate::ddl::DdlContext;
|
use crate::ddl::DdlContext;
|
||||||
use crate::error::{self, Result};
|
use crate::error::{self, Result};
|
||||||
use crate::key::table_route::TableRouteValue;
|
use crate::key::table_route::TableRouteValue;
|
||||||
@@ -109,17 +108,12 @@ impl State for DropDatabaseExecutor {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Deletes topic-region mapping if dropping physical table
|
// Deletes topic-region mapping if dropping physical table
|
||||||
let region_wal_options =
|
let region_wal_options = get_region_wal_options(
|
||||||
if let TableRouteValue::Physical(table_route_value) = &table_route_value {
|
&ddl_ctx.table_metadata_manager,
|
||||||
let datanode_table_values = ddl_ctx
|
&table_route_value,
|
||||||
.table_metadata_manager
|
self.physical_table_id,
|
||||||
.datanode_table_manager()
|
)
|
||||||
.regions(self.physical_table_id, table_route_value)
|
.await?;
|
||||||
.await?;
|
|
||||||
extract_region_wal_options(&datanode_table_values)?
|
|
||||||
} else {
|
|
||||||
HashMap::new()
|
|
||||||
};
|
|
||||||
|
|
||||||
executor
|
executor
|
||||||
.on_destroy_metadata(ddl_ctx, &table_route_value, ®ion_wal_options)
|
.on_destroy_metadata(ddl_ctx, &table_route_value, ®ion_wal_options)
|
||||||
|
|||||||
@@ -42,7 +42,8 @@ use crate::error::{
|
|||||||
};
|
};
|
||||||
use crate::key::datanode_table::DatanodeTableValue;
|
use crate::key::datanode_table::DatanodeTableValue;
|
||||||
use crate::key::table_name::TableNameKey;
|
use crate::key::table_name::TableNameKey;
|
||||||
use crate::key::TableMetadataManagerRef;
|
use crate::key::table_route::TableRouteValue;
|
||||||
|
use crate::key::{TableMetadataManager, TableMetadataManagerRef};
|
||||||
use crate::peer::Peer;
|
use crate::peer::Peer;
|
||||||
use crate::rpc::ddl::CreateTableTask;
|
use crate::rpc::ddl::CreateTableTask;
|
||||||
use crate::rpc::router::{find_follower_regions, find_followers, RegionRoute};
|
use crate::rpc::router::{find_follower_regions, find_followers, RegionRoute};
|
||||||
@@ -187,6 +188,25 @@ pub fn parse_region_wal_options(
|
|||||||
Ok(region_wal_options)
|
Ok(region_wal_options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the wal options for a table.
|
||||||
|
pub async fn get_region_wal_options(
|
||||||
|
table_metadata_manager: &TableMetadataManager,
|
||||||
|
table_route_value: &TableRouteValue,
|
||||||
|
physical_table_id: TableId,
|
||||||
|
) -> Result<HashMap<RegionNumber, WalOptions>> {
|
||||||
|
let region_wal_options =
|
||||||
|
if let TableRouteValue::Physical(table_route_value) = &table_route_value {
|
||||||
|
let datanode_table_values = table_metadata_manager
|
||||||
|
.datanode_table_manager()
|
||||||
|
.regions(physical_table_id, table_route_value)
|
||||||
|
.await?;
|
||||||
|
extract_region_wal_options(&datanode_table_values)?
|
||||||
|
} else {
|
||||||
|
HashMap::new()
|
||||||
|
};
|
||||||
|
Ok(region_wal_options)
|
||||||
|
}
|
||||||
|
|
||||||
/// Extracts region wal options from [DatanodeTableValue]s.
|
/// Extracts region wal options from [DatanodeTableValue]s.
|
||||||
pub fn extract_region_wal_options(
|
pub fn extract_region_wal_options(
|
||||||
datanode_table_values: &Vec<DatanodeTableValue>,
|
datanode_table_values: &Vec<DatanodeTableValue>,
|
||||||
|
|||||||
@@ -125,13 +125,12 @@ impl DdlManager {
|
|||||||
ddl_context: DdlContext,
|
ddl_context: DdlContext,
|
||||||
procedure_manager: ProcedureManagerRef,
|
procedure_manager: ProcedureManagerRef,
|
||||||
register_loaders: bool,
|
register_loaders: bool,
|
||||||
#[cfg(feature = "enterprise")] trigger_ddl_manager: Option<TriggerDdlManagerRef>,
|
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let manager = Self {
|
let manager = Self {
|
||||||
ddl_context,
|
ddl_context,
|
||||||
procedure_manager,
|
procedure_manager,
|
||||||
#[cfg(feature = "enterprise")]
|
#[cfg(feature = "enterprise")]
|
||||||
trigger_ddl_manager,
|
trigger_ddl_manager: None,
|
||||||
};
|
};
|
||||||
if register_loaders {
|
if register_loaders {
|
||||||
manager.register_loaders()?;
|
manager.register_loaders()?;
|
||||||
@@ -139,6 +138,15 @@ impl DdlManager {
|
|||||||
Ok(manager)
|
Ok(manager)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "enterprise")]
|
||||||
|
pub fn with_trigger_ddl_manager(
|
||||||
|
mut self,
|
||||||
|
trigger_ddl_manager: Option<TriggerDdlManagerRef>,
|
||||||
|
) -> Self {
|
||||||
|
self.trigger_ddl_manager = trigger_ddl_manager;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the [TableMetadataManagerRef].
|
/// Returns the [TableMetadataManagerRef].
|
||||||
pub fn table_metadata_manager(&self) -> &TableMetadataManagerRef {
|
pub fn table_metadata_manager(&self) -> &TableMetadataManagerRef {
|
||||||
&self.ddl_context.table_metadata_manager
|
&self.ddl_context.table_metadata_manager
|
||||||
@@ -964,8 +972,6 @@ mod tests {
|
|||||||
},
|
},
|
||||||
procedure_manager.clone(),
|
procedure_manager.clone(),
|
||||||
true,
|
true,
|
||||||
#[cfg(feature = "enterprise")]
|
|
||||||
None,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let expected_loaders = vec![
|
let expected_loaders = vec![
|
||||||
|
|||||||
@@ -174,6 +174,8 @@ pub struct UpgradeRegion {
|
|||||||
/// The identifier of cache.
|
/// The identifier of cache.
|
||||||
pub enum CacheIdent {
|
pub enum CacheIdent {
|
||||||
FlowId(FlowId),
|
FlowId(FlowId),
|
||||||
|
/// Indicate change of address of flownode.
|
||||||
|
FlowNodeAddressChange(u64),
|
||||||
FlowName(FlowName),
|
FlowName(FlowName),
|
||||||
TableId(TableId),
|
TableId(TableId),
|
||||||
TableName(TableName),
|
TableName(TableName),
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ pub mod table_name;
|
|||||||
pub mod table_route;
|
pub mod table_route;
|
||||||
#[cfg(any(test, feature = "testing"))]
|
#[cfg(any(test, feature = "testing"))]
|
||||||
pub mod test_utils;
|
pub mod test_utils;
|
||||||
mod tombstone;
|
pub mod tombstone;
|
||||||
pub mod topic_name;
|
pub mod topic_name;
|
||||||
pub mod topic_region;
|
pub mod topic_region;
|
||||||
pub mod txn_helper;
|
pub mod txn_helper;
|
||||||
@@ -535,6 +535,29 @@ impl TableMetadataManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a new `TableMetadataManager` with a custom tombstone prefix.
|
||||||
|
pub fn new_with_custom_tombstone_prefix(
|
||||||
|
kv_backend: KvBackendRef,
|
||||||
|
tombstone_prefix: &str,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
table_name_manager: TableNameManager::new(kv_backend.clone()),
|
||||||
|
table_info_manager: TableInfoManager::new(kv_backend.clone()),
|
||||||
|
view_info_manager: ViewInfoManager::new(kv_backend.clone()),
|
||||||
|
datanode_table_manager: DatanodeTableManager::new(kv_backend.clone()),
|
||||||
|
catalog_manager: CatalogManager::new(kv_backend.clone()),
|
||||||
|
schema_manager: SchemaManager::new(kv_backend.clone()),
|
||||||
|
table_route_manager: TableRouteManager::new(kv_backend.clone()),
|
||||||
|
tombstone_manager: TombstoneManager::new_with_prefix(
|
||||||
|
kv_backend.clone(),
|
||||||
|
tombstone_prefix,
|
||||||
|
),
|
||||||
|
topic_name_manager: TopicNameManager::new(kv_backend.clone()),
|
||||||
|
topic_region_manager: TopicRegionManager::new(kv_backend.clone()),
|
||||||
|
kv_backend,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn init(&self) -> Result<()> {
|
pub async fn init(&self) -> Result<()> {
|
||||||
let catalog_name = CatalogNameKey::new(DEFAULT_CATALOG_NAME);
|
let catalog_name = CatalogNameKey::new(DEFAULT_CATALOG_NAME);
|
||||||
|
|
||||||
@@ -925,7 +948,7 @@ impl TableMetadataManager {
|
|||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let keys =
|
let keys =
|
||||||
self.table_metadata_keys(table_id, table_name, table_route_value, region_wal_options)?;
|
self.table_metadata_keys(table_id, table_name, table_route_value, region_wal_options)?;
|
||||||
self.tombstone_manager.create(keys).await
|
self.tombstone_manager.create(keys).await.map(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deletes metadata tombstone for table **permanently**.
|
/// Deletes metadata tombstone for table **permanently**.
|
||||||
@@ -939,7 +962,10 @@ impl TableMetadataManager {
|
|||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let table_metadata_keys =
|
let table_metadata_keys =
|
||||||
self.table_metadata_keys(table_id, table_name, table_route_value, region_wal_options)?;
|
self.table_metadata_keys(table_id, table_name, table_route_value, region_wal_options)?;
|
||||||
self.tombstone_manager.delete(table_metadata_keys).await
|
self.tombstone_manager
|
||||||
|
.delete(table_metadata_keys)
|
||||||
|
.await
|
||||||
|
.map(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Restores metadata for table.
|
/// Restores metadata for table.
|
||||||
@@ -953,7 +979,7 @@ impl TableMetadataManager {
|
|||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let keys =
|
let keys =
|
||||||
self.table_metadata_keys(table_id, table_name, table_route_value, region_wal_options)?;
|
self.table_metadata_keys(table_id, table_name, table_route_value, region_wal_options)?;
|
||||||
self.tombstone_manager.restore(keys).await
|
self.tombstone_manager.restore(keys).await.map(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deletes metadata for table **permanently**.
|
/// Deletes metadata for table **permanently**.
|
||||||
|
|||||||
@@ -14,31 +14,51 @@
|
|||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use common_telemetry::debug;
|
||||||
use snafu::ensure;
|
use snafu::ensure;
|
||||||
|
|
||||||
use crate::error::{self, Result};
|
use crate::error::{self, Result};
|
||||||
use crate::key::txn_helper::TxnOpGetResponseSet;
|
use crate::key::txn_helper::TxnOpGetResponseSet;
|
||||||
use crate::kv_backend::txn::{Compare, CompareOp, Txn, TxnOp};
|
use crate::kv_backend::txn::{Compare, CompareOp, Txn, TxnOp};
|
||||||
use crate::kv_backend::KvBackendRef;
|
use crate::kv_backend::KvBackendRef;
|
||||||
use crate::rpc::store::BatchGetRequest;
|
use crate::rpc::store::{BatchDeleteRequest, BatchGetRequest};
|
||||||
|
|
||||||
/// [TombstoneManager] provides the ability to:
|
/// [TombstoneManager] provides the ability to:
|
||||||
/// - logically delete values
|
/// - logically delete values
|
||||||
/// - restore the deleted values
|
/// - restore the deleted values
|
||||||
pub(crate) struct TombstoneManager {
|
pub struct TombstoneManager {
|
||||||
kv_backend: KvBackendRef,
|
kv_backend: KvBackendRef,
|
||||||
|
tombstone_prefix: String,
|
||||||
|
// Only used for testing.
|
||||||
|
#[cfg(test)]
|
||||||
|
max_txn_ops: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const TOMBSTONE_PREFIX: &str = "__tombstone/";
|
const TOMBSTONE_PREFIX: &str = "__tombstone/";
|
||||||
|
|
||||||
fn to_tombstone(key: &[u8]) -> Vec<u8> {
|
|
||||||
[TOMBSTONE_PREFIX.as_bytes(), key].concat()
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TombstoneManager {
|
impl TombstoneManager {
|
||||||
/// Returns [TombstoneManager].
|
/// Returns [TombstoneManager].
|
||||||
pub fn new(kv_backend: KvBackendRef) -> Self {
|
pub fn new(kv_backend: KvBackendRef) -> Self {
|
||||||
Self { kv_backend }
|
Self::new_with_prefix(kv_backend, TOMBSTONE_PREFIX)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns [TombstoneManager] with a custom tombstone prefix.
|
||||||
|
pub fn new_with_prefix(kv_backend: KvBackendRef, prefix: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
kv_backend,
|
||||||
|
tombstone_prefix: prefix.to_string(),
|
||||||
|
#[cfg(test)]
|
||||||
|
max_txn_ops: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_tombstone(&self, key: &[u8]) -> Vec<u8> {
|
||||||
|
[self.tombstone_prefix.as_bytes(), key].concat()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn set_max_txn_ops(&mut self, max_txn_ops: usize) {
|
||||||
|
self.max_txn_ops = Some(max_txn_ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Moves value to `dest_key`.
|
/// Moves value to `dest_key`.
|
||||||
@@ -67,11 +87,15 @@ impl TombstoneManager {
|
|||||||
(txn, TxnOpGetResponseSet::filter(src_key))
|
(txn, TxnOpGetResponseSet::filter(src_key))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn move_values_inner(&self, keys: &[Vec<u8>], dest_keys: &[Vec<u8>]) -> Result<()> {
|
async fn move_values_inner(&self, keys: &[Vec<u8>], dest_keys: &[Vec<u8>]) -> Result<usize> {
|
||||||
ensure!(
|
ensure!(
|
||||||
keys.len() == dest_keys.len(),
|
keys.len() == dest_keys.len(),
|
||||||
error::UnexpectedSnafu {
|
error::UnexpectedSnafu {
|
||||||
err_msg: "The length of keys does not match the length of dest_keys."
|
err_msg: format!(
|
||||||
|
"The length of keys({}) does not match the length of dest_keys({}).",
|
||||||
|
keys.len(),
|
||||||
|
dest_keys.len()
|
||||||
|
),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
// The key -> dest key mapping.
|
// The key -> dest key mapping.
|
||||||
@@ -102,7 +126,7 @@ impl TombstoneManager {
|
|||||||
.unzip();
|
.unzip();
|
||||||
let mut resp = self.kv_backend.txn(Txn::merge_all(txns)).await?;
|
let mut resp = self.kv_backend.txn(Txn::merge_all(txns)).await?;
|
||||||
if resp.succeeded {
|
if resp.succeeded {
|
||||||
return Ok(());
|
return Ok(keys.len());
|
||||||
}
|
}
|
||||||
let mut set = TxnOpGetResponseSet::from(&mut resp.responses);
|
let mut set = TxnOpGetResponseSet::from(&mut resp.responses);
|
||||||
// Updates results.
|
// Updates results.
|
||||||
@@ -124,17 +148,45 @@ impl TombstoneManager {
|
|||||||
.fail()
|
.fail()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Moves values to `dest_key`.
|
fn max_txn_ops(&self) -> usize {
|
||||||
async fn move_values(&self, keys: Vec<Vec<u8>>, dest_keys: Vec<Vec<u8>>) -> Result<()> {
|
#[cfg(test)]
|
||||||
let chunk_size = self.kv_backend.max_txn_ops() / 2;
|
if let Some(max_txn_ops) = self.max_txn_ops {
|
||||||
if keys.len() > chunk_size {
|
return max_txn_ops;
|
||||||
let keys_chunks = keys.chunks(chunk_size).collect::<Vec<_>>();
|
}
|
||||||
let dest_keys_chunks = keys.chunks(chunk_size).collect::<Vec<_>>();
|
self.kv_backend.max_txn_ops()
|
||||||
for (keys, dest_keys) in keys_chunks.into_iter().zip(dest_keys_chunks) {
|
}
|
||||||
self.move_values_inner(keys, dest_keys).await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
/// Moves values to `dest_key`.
|
||||||
|
///
|
||||||
|
/// Returns the number of keys that were moved.
|
||||||
|
async fn move_values(&self, keys: Vec<Vec<u8>>, dest_keys: Vec<Vec<u8>>) -> Result<usize> {
|
||||||
|
ensure!(
|
||||||
|
keys.len() == dest_keys.len(),
|
||||||
|
error::UnexpectedSnafu {
|
||||||
|
err_msg: format!(
|
||||||
|
"The length of keys({}) does not match the length of dest_keys({}).",
|
||||||
|
keys.len(),
|
||||||
|
dest_keys.len()
|
||||||
|
),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if keys.is_empty() {
|
||||||
|
return Ok(0);
|
||||||
|
}
|
||||||
|
let chunk_size = self.max_txn_ops() / 2;
|
||||||
|
if keys.len() > chunk_size {
|
||||||
|
debug!(
|
||||||
|
"Moving values with multiple chunks, keys len: {}, chunk_size: {}",
|
||||||
|
keys.len(),
|
||||||
|
chunk_size
|
||||||
|
);
|
||||||
|
let mut moved_keys = 0;
|
||||||
|
let keys_chunks = keys.chunks(chunk_size).collect::<Vec<_>>();
|
||||||
|
let dest_keys_chunks = dest_keys.chunks(chunk_size).collect::<Vec<_>>();
|
||||||
|
for (keys, dest_keys) in keys_chunks.into_iter().zip(dest_keys_chunks) {
|
||||||
|
moved_keys += self.move_values_inner(keys, dest_keys).await?;
|
||||||
|
}
|
||||||
|
Ok(moved_keys)
|
||||||
} else {
|
} else {
|
||||||
self.move_values_inner(&keys, &dest_keys).await
|
self.move_values_inner(&keys, &dest_keys).await
|
||||||
}
|
}
|
||||||
@@ -145,11 +197,13 @@ impl TombstoneManager {
|
|||||||
/// Preforms to:
|
/// Preforms to:
|
||||||
/// - deletes origin values.
|
/// - deletes origin values.
|
||||||
/// - stores tombstone values.
|
/// - stores tombstone values.
|
||||||
pub(crate) async fn create(&self, keys: Vec<Vec<u8>>) -> Result<()> {
|
///
|
||||||
|
/// Returns the number of keys that were moved.
|
||||||
|
pub async fn create(&self, keys: Vec<Vec<u8>>) -> Result<usize> {
|
||||||
let (keys, dest_keys): (Vec<_>, Vec<_>) = keys
|
let (keys, dest_keys): (Vec<_>, Vec<_>) = keys
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|key| {
|
.map(|key| {
|
||||||
let tombstone_key = to_tombstone(&key);
|
let tombstone_key = self.to_tombstone(&key);
|
||||||
(key, tombstone_key)
|
(key, tombstone_key)
|
||||||
})
|
})
|
||||||
.unzip();
|
.unzip();
|
||||||
@@ -162,11 +216,13 @@ impl TombstoneManager {
|
|||||||
/// Preforms to:
|
/// Preforms to:
|
||||||
/// - restore origin value.
|
/// - restore origin value.
|
||||||
/// - deletes tombstone values.
|
/// - deletes tombstone values.
|
||||||
pub(crate) async fn restore(&self, keys: Vec<Vec<u8>>) -> Result<()> {
|
///
|
||||||
|
/// Returns the number of keys that were restored.
|
||||||
|
pub async fn restore(&self, keys: Vec<Vec<u8>>) -> Result<usize> {
|
||||||
let (keys, dest_keys): (Vec<_>, Vec<_>) = keys
|
let (keys, dest_keys): (Vec<_>, Vec<_>) = keys
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|key| {
|
.map(|key| {
|
||||||
let tombstone_key = to_tombstone(&key);
|
let tombstone_key = self.to_tombstone(&key);
|
||||||
(tombstone_key, key)
|
(tombstone_key, key)
|
||||||
})
|
})
|
||||||
.unzip();
|
.unzip();
|
||||||
@@ -175,16 +231,21 @@ impl TombstoneManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Deletes tombstones values for the specified `keys`.
|
/// Deletes tombstones values for the specified `keys`.
|
||||||
pub(crate) async fn delete(&self, keys: Vec<Vec<u8>>) -> Result<()> {
|
///
|
||||||
let operations = keys
|
/// Returns the number of keys that were deleted.
|
||||||
|
pub async fn delete(&self, keys: Vec<Vec<u8>>) -> Result<usize> {
|
||||||
|
let keys = keys
|
||||||
.iter()
|
.iter()
|
||||||
.map(|key| TxnOp::Delete(to_tombstone(key)))
|
.map(|key| self.to_tombstone(key))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let txn = Txn::new().and_then(operations);
|
let num_keys = keys.len();
|
||||||
// Always success.
|
let _ = self
|
||||||
let _ = self.kv_backend.txn(txn).await?;
|
.kv_backend
|
||||||
Ok(())
|
.batch_delete(BatchDeleteRequest::new().with_keys(keys))
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(num_keys)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,7 +255,6 @@ mod tests {
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use super::to_tombstone;
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::key::tombstone::TombstoneManager;
|
use crate::key::tombstone::TombstoneManager;
|
||||||
use crate::kv_backend::memory::MemoryKvBackend;
|
use crate::kv_backend::memory::MemoryKvBackend;
|
||||||
@@ -246,7 +306,7 @@ mod tests {
|
|||||||
assert!(!kv_backend.exists(b"foo").await.unwrap());
|
assert!(!kv_backend.exists(b"foo").await.unwrap());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
kv_backend
|
kv_backend
|
||||||
.get(&to_tombstone(b"bar"))
|
.get(&tombstone_manager.to_tombstone(b"bar"))
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@@ -255,7 +315,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
kv_backend
|
kv_backend
|
||||||
.get(&to_tombstone(b"foo"))
|
.get(&tombstone_manager.to_tombstone(b"foo"))
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@@ -287,7 +347,7 @@ mod tests {
|
|||||||
kv_backend.clone(),
|
kv_backend.clone(),
|
||||||
&[MoveValue {
|
&[MoveValue {
|
||||||
key: b"bar".to_vec(),
|
key: b"bar".to_vec(),
|
||||||
dest_key: to_tombstone(b"bar"),
|
dest_key: tombstone_manager.to_tombstone(b"bar"),
|
||||||
value: b"baz".to_vec(),
|
value: b"baz".to_vec(),
|
||||||
}],
|
}],
|
||||||
)
|
)
|
||||||
@@ -364,7 +424,7 @@ mod tests {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|(key, value)| MoveValue {
|
.map(|(key, value)| MoveValue {
|
||||||
key: key.clone(),
|
key: key.clone(),
|
||||||
dest_key: to_tombstone(key),
|
dest_key: tombstone_manager.to_tombstone(key),
|
||||||
value: value.clone(),
|
value: value.clone(),
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
@@ -373,16 +433,73 @@ mod tests {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|kv| (kv.key, kv.dest_key))
|
.map(|kv| (kv.key, kv.dest_key))
|
||||||
.unzip();
|
.unzip();
|
||||||
tombstone_manager
|
let moved_keys = tombstone_manager
|
||||||
.move_values(keys.clone(), dest_keys.clone())
|
.move_values(keys.clone(), dest_keys.clone())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
assert_eq!(kvs.len(), moved_keys);
|
||||||
check_moved_values(kv_backend.clone(), &move_values).await;
|
check_moved_values(kv_backend.clone(), &move_values).await;
|
||||||
// Moves again
|
// Moves again
|
||||||
tombstone_manager
|
let moved_keys = tombstone_manager
|
||||||
.move_values(keys.clone(), dest_keys.clone())
|
.move_values(keys.clone(), dest_keys.clone())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
assert_eq!(0, moved_keys);
|
||||||
|
check_moved_values(kv_backend.clone(), &move_values).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_move_values_with_max_txn_ops() {
|
||||||
|
common_telemetry::init_default_ut_logging();
|
||||||
|
let kv_backend = Arc::new(MemoryKvBackend::default());
|
||||||
|
let mut tombstone_manager = TombstoneManager::new(kv_backend.clone());
|
||||||
|
tombstone_manager.set_max_txn_ops(4);
|
||||||
|
let kvs = HashMap::from([
|
||||||
|
(b"bar".to_vec(), b"baz".to_vec()),
|
||||||
|
(b"foo".to_vec(), b"hi".to_vec()),
|
||||||
|
(b"baz".to_vec(), b"hello".to_vec()),
|
||||||
|
(b"qux".to_vec(), b"world".to_vec()),
|
||||||
|
(b"quux".to_vec(), b"world".to_vec()),
|
||||||
|
(b"quuux".to_vec(), b"world".to_vec()),
|
||||||
|
(b"quuuux".to_vec(), b"world".to_vec()),
|
||||||
|
(b"quuuuux".to_vec(), b"world".to_vec()),
|
||||||
|
(b"quuuuuux".to_vec(), b"world".to_vec()),
|
||||||
|
]);
|
||||||
|
for (key, value) in &kvs {
|
||||||
|
kv_backend
|
||||||
|
.put(
|
||||||
|
PutRequest::new()
|
||||||
|
.with_key(key.clone())
|
||||||
|
.with_value(value.clone()),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
let move_values = kvs
|
||||||
|
.iter()
|
||||||
|
.map(|(key, value)| MoveValue {
|
||||||
|
key: key.clone(),
|
||||||
|
dest_key: tombstone_manager.to_tombstone(key),
|
||||||
|
value: value.clone(),
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let (keys, dest_keys): (Vec<_>, Vec<_>) = move_values
|
||||||
|
.clone()
|
||||||
|
.into_iter()
|
||||||
|
.map(|kv| (kv.key, kv.dest_key))
|
||||||
|
.unzip();
|
||||||
|
let moved_keys = tombstone_manager
|
||||||
|
.move_values(keys.clone(), dest_keys.clone())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(kvs.len(), moved_keys);
|
||||||
|
check_moved_values(kv_backend.clone(), &move_values).await;
|
||||||
|
// Moves again
|
||||||
|
let moved_keys = tombstone_manager
|
||||||
|
.move_values(keys.clone(), dest_keys.clone())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(0, moved_keys);
|
||||||
check_moved_values(kv_backend.clone(), &move_values).await;
|
check_moved_values(kv_backend.clone(), &move_values).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -409,7 +526,7 @@ mod tests {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|(key, value)| MoveValue {
|
.map(|(key, value)| MoveValue {
|
||||||
key: key.clone(),
|
key: key.clone(),
|
||||||
dest_key: to_tombstone(key),
|
dest_key: tombstone_manager.to_tombstone(key),
|
||||||
value: value.clone(),
|
value: value.clone(),
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
@@ -420,17 +537,19 @@ mod tests {
|
|||||||
.unzip();
|
.unzip();
|
||||||
keys.push(b"non-exists".to_vec());
|
keys.push(b"non-exists".to_vec());
|
||||||
dest_keys.push(b"hi/non-exists".to_vec());
|
dest_keys.push(b"hi/non-exists".to_vec());
|
||||||
tombstone_manager
|
let moved_keys = tombstone_manager
|
||||||
.move_values(keys.clone(), dest_keys.clone())
|
.move_values(keys.clone(), dest_keys.clone())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
check_moved_values(kv_backend.clone(), &move_values).await;
|
check_moved_values(kv_backend.clone(), &move_values).await;
|
||||||
|
assert_eq!(3, moved_keys);
|
||||||
// Moves again
|
// Moves again
|
||||||
tombstone_manager
|
let moved_keys = tombstone_manager
|
||||||
.move_values(keys.clone(), dest_keys.clone())
|
.move_values(keys.clone(), dest_keys.clone())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
check_moved_values(kv_backend.clone(), &move_values).await;
|
check_moved_values(kv_backend.clone(), &move_values).await;
|
||||||
|
assert_eq!(0, moved_keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
@@ -462,7 +581,7 @@ mod tests {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|(key, value)| MoveValue {
|
.map(|(key, value)| MoveValue {
|
||||||
key: key.clone(),
|
key: key.clone(),
|
||||||
dest_key: to_tombstone(key),
|
dest_key: tombstone_manager.to_tombstone(key),
|
||||||
value: value.clone(),
|
value: value.clone(),
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
@@ -471,10 +590,11 @@ mod tests {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|kv| (kv.key, kv.dest_key))
|
.map(|kv| (kv.key, kv.dest_key))
|
||||||
.unzip();
|
.unzip();
|
||||||
tombstone_manager
|
let moved_keys = tombstone_manager
|
||||||
.move_values(keys, dest_keys)
|
.move_values(keys, dest_keys)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
assert_eq!(kvs.len(), moved_keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
@@ -502,7 +622,7 @@ mod tests {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|(key, value)| MoveValue {
|
.map(|(key, value)| MoveValue {
|
||||||
key: key.clone(),
|
key: key.clone(),
|
||||||
dest_key: to_tombstone(key),
|
dest_key: tombstone_manager.to_tombstone(key),
|
||||||
value: value.clone(),
|
value: value.clone(),
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
@@ -537,7 +657,7 @@ mod tests {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|(key, value)| MoveValue {
|
.map(|(key, value)| MoveValue {
|
||||||
key: key.clone(),
|
key: key.clone(),
|
||||||
dest_key: to_tombstone(key),
|
dest_key: tombstone_manager.to_tombstone(key),
|
||||||
value: value.clone(),
|
value: value.clone(),
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
@@ -552,4 +672,24 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
check_moved_values(kv_backend.clone(), &move_values).await;
|
check_moved_values(kv_backend.clone(), &move_values).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_move_values_with_different_lengths() {
|
||||||
|
let kv_backend = Arc::new(MemoryKvBackend::default());
|
||||||
|
let tombstone_manager = TombstoneManager::new(kv_backend.clone());
|
||||||
|
|
||||||
|
let keys = vec![b"bar".to_vec(), b"foo".to_vec()];
|
||||||
|
let dest_keys = vec![b"bar".to_vec(), b"foo".to_vec(), b"baz".to_vec()];
|
||||||
|
|
||||||
|
let err = tombstone_manager
|
||||||
|
.move_values(keys, dest_keys)
|
||||||
|
.await
|
||||||
|
.unwrap_err();
|
||||||
|
assert!(err
|
||||||
|
.to_string()
|
||||||
|
.contains("The length of keys(2) does not match the length of dest_keys(3)."),);
|
||||||
|
|
||||||
|
let moved_keys = tombstone_manager.move_values(vec![], vec![]).await.unwrap();
|
||||||
|
assert_eq!(0, moved_keys);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,20 +54,20 @@ impl<T> MemoryKvBackend<T> {
|
|||||||
kvs.clear();
|
kvs.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(any(test, feature = "testing"))]
|
||||||
/// Returns true if the `kvs` is empty.
|
/// Returns true if the `kvs` is empty.
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.kvs.read().unwrap().is_empty()
|
self.kvs.read().unwrap().is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(any(test, feature = "testing"))]
|
||||||
/// Returns the `kvs`.
|
/// Returns the `kvs`.
|
||||||
pub fn dump(&self) -> BTreeMap<Vec<u8>, Vec<u8>> {
|
pub fn dump(&self) -> BTreeMap<Vec<u8>, Vec<u8>> {
|
||||||
let kvs = self.kvs.read().unwrap();
|
let kvs = self.kvs.read().unwrap();
|
||||||
kvs.clone()
|
kvs.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(any(test, feature = "testing"))]
|
||||||
/// Returns the length of `kvs`
|
/// Returns the length of `kvs`
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.kvs.read().unwrap().len()
|
self.kvs.read().unwrap().len()
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use api::region::RegionResponse;
|
use api::region::RegionResponse;
|
||||||
use api::v1::flow::{FlowRequest, FlowResponse};
|
use api::v1::flow::{DirtyWindowRequest, FlowRequest, FlowResponse};
|
||||||
use api::v1::region::{InsertRequests, RegionRequest};
|
use api::v1::region::{InsertRequests, RegionRequest};
|
||||||
pub use common_base::AffectedRows;
|
pub use common_base::AffectedRows;
|
||||||
use common_query::request::QueryRequest;
|
use common_query::request::QueryRequest;
|
||||||
@@ -42,6 +42,9 @@ pub trait Flownode: Send + Sync {
|
|||||||
async fn handle(&self, request: FlowRequest) -> Result<FlowResponse>;
|
async fn handle(&self, request: FlowRequest) -> Result<FlowResponse>;
|
||||||
|
|
||||||
async fn handle_inserts(&self, request: InsertRequests) -> Result<FlowResponse>;
|
async fn handle_inserts(&self, request: InsertRequests) -> Result<FlowResponse>;
|
||||||
|
|
||||||
|
/// Handles requests to mark time window as dirty.
|
||||||
|
async fn handle_mark_window_dirty(&self, req: DirtyWindowRequest) -> Result<FlowResponse>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type FlownodeRef = Arc<dyn Flownode>;
|
pub type FlownodeRef = Arc<dyn Flownode>;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use api::region::RegionResponse;
|
use api::region::RegionResponse;
|
||||||
use api::v1::flow::{FlowRequest, FlowResponse};
|
use api::v1::flow::{DirtyWindowRequest, FlowRequest, FlowResponse};
|
||||||
use api::v1::region::{InsertRequests, RegionRequest};
|
use api::v1::region::{InsertRequests, RegionRequest};
|
||||||
pub use common_base::AffectedRows;
|
pub use common_base::AffectedRows;
|
||||||
use common_query::request::QueryRequest;
|
use common_query::request::QueryRequest;
|
||||||
@@ -67,6 +67,14 @@ pub trait MockFlownodeHandler: Sync + Send + Clone {
|
|||||||
) -> Result<FlowResponse> {
|
) -> Result<FlowResponse> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn handle_mark_window_dirty(
|
||||||
|
&self,
|
||||||
|
_peer: &Peer,
|
||||||
|
_req: DirtyWindowRequest,
|
||||||
|
) -> Result<FlowResponse> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A mock struct implements [NodeManager] only implement the `datanode` method.
|
/// A mock struct implements [NodeManager] only implement the `datanode` method.
|
||||||
@@ -134,6 +142,10 @@ impl<T: MockFlownodeHandler> Flownode for MockNode<T> {
|
|||||||
async fn handle_inserts(&self, requests: InsertRequests) -> Result<FlowResponse> {
|
async fn handle_inserts(&self, requests: InsertRequests) -> Result<FlowResponse> {
|
||||||
self.handler.handle_inserts(&self.peer, requests).await
|
self.handler.handle_inserts(&self.peer, requests).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn handle_mark_window_dirty(&self, req: DirtyWindowRequest) -> Result<FlowResponse> {
|
||||||
|
self.handler.handle_mark_window_dirty(&self.peer, req).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
|
|||||||
@@ -222,6 +222,7 @@ pub struct RecordBatchStreamAdapter {
|
|||||||
enum Metrics {
|
enum Metrics {
|
||||||
Unavailable,
|
Unavailable,
|
||||||
Unresolved(Arc<dyn ExecutionPlan>),
|
Unresolved(Arc<dyn ExecutionPlan>),
|
||||||
|
PartialResolved(Arc<dyn ExecutionPlan>, RecordBatchMetrics),
|
||||||
Resolved(RecordBatchMetrics),
|
Resolved(RecordBatchMetrics),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,7 +276,9 @@ impl RecordBatchStream for RecordBatchStreamAdapter {
|
|||||||
|
|
||||||
fn metrics(&self) -> Option<RecordBatchMetrics> {
|
fn metrics(&self) -> Option<RecordBatchMetrics> {
|
||||||
match &self.metrics_2 {
|
match &self.metrics_2 {
|
||||||
Metrics::Resolved(metrics) => Some(metrics.clone()),
|
Metrics::Resolved(metrics) | Metrics::PartialResolved(_, metrics) => {
|
||||||
|
Some(metrics.clone())
|
||||||
|
}
|
||||||
Metrics::Unavailable | Metrics::Unresolved(_) => None,
|
Metrics::Unavailable | Metrics::Unresolved(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -299,13 +302,25 @@ impl Stream for RecordBatchStreamAdapter {
|
|||||||
Poll::Pending => Poll::Pending,
|
Poll::Pending => Poll::Pending,
|
||||||
Poll::Ready(Some(df_record_batch)) => {
|
Poll::Ready(Some(df_record_batch)) => {
|
||||||
let df_record_batch = df_record_batch?;
|
let df_record_batch = df_record_batch?;
|
||||||
|
if let Metrics::Unresolved(df_plan) | Metrics::PartialResolved(df_plan, _) =
|
||||||
|
&self.metrics_2
|
||||||
|
{
|
||||||
|
let mut metric_collector = MetricCollector::new(self.explain_verbose);
|
||||||
|
accept(df_plan.as_ref(), &mut metric_collector).unwrap();
|
||||||
|
self.metrics_2 = Metrics::PartialResolved(
|
||||||
|
df_plan.clone(),
|
||||||
|
metric_collector.record_batch_metrics,
|
||||||
|
);
|
||||||
|
}
|
||||||
Poll::Ready(Some(RecordBatch::try_from_df_record_batch(
|
Poll::Ready(Some(RecordBatch::try_from_df_record_batch(
|
||||||
self.schema(),
|
self.schema(),
|
||||||
df_record_batch,
|
df_record_batch,
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
Poll::Ready(None) => {
|
Poll::Ready(None) => {
|
||||||
if let Metrics::Unresolved(df_plan) = &self.metrics_2 {
|
if let Metrics::Unresolved(df_plan) | Metrics::PartialResolved(df_plan, _) =
|
||||||
|
&self.metrics_2
|
||||||
|
{
|
||||||
let mut metric_collector = MetricCollector::new(self.explain_verbose);
|
let mut metric_collector = MetricCollector::new(self.explain_verbose);
|
||||||
accept(df_plan.as_ref(), &mut metric_collector).unwrap();
|
accept(df_plan.as_ref(), &mut metric_collector).unwrap();
|
||||||
self.metrics_2 = Metrics::Resolved(metric_collector.record_batch_metrics);
|
self.metrics_2 = Metrics::Resolved(metric_collector.record_batch_metrics);
|
||||||
|
|||||||
@@ -173,13 +173,13 @@ pub enum Error {
|
|||||||
#[snafu(implicit)]
|
#[snafu(implicit)]
|
||||||
location: Location,
|
location: Location,
|
||||||
},
|
},
|
||||||
|
|
||||||
#[snafu(display("Stream timeout"))]
|
#[snafu(display("Stream timeout"))]
|
||||||
StreamTimeout {
|
StreamTimeout {
|
||||||
#[snafu(implicit)]
|
#[snafu(implicit)]
|
||||||
location: Location,
|
location: Location,
|
||||||
#[snafu(source)]
|
|
||||||
error: tokio::time::error::Elapsed,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
#[snafu(display("RecordBatch slice index overflow: {visit_index} > {size}"))]
|
#[snafu(display("RecordBatch slice index overflow: {visit_index} > {size}"))]
|
||||||
RecordBatchSliceIndexOverflow {
|
RecordBatchSliceIndexOverflow {
|
||||||
#[snafu(implicit)]
|
#[snafu(implicit)]
|
||||||
@@ -187,6 +187,12 @@ pub enum Error {
|
|||||||
size: usize,
|
size: usize,
|
||||||
visit_index: usize,
|
visit_index: usize,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Stream has been cancelled"))]
|
||||||
|
StreamCancelled {
|
||||||
|
#[snafu(implicit)]
|
||||||
|
location: Location,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ErrorExt for Error {
|
impl ErrorExt for Error {
|
||||||
@@ -221,6 +227,8 @@ impl ErrorExt for Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Error::StreamTimeout { .. } => StatusCode::Cancelled,
|
Error::StreamTimeout { .. } => StatusCode::Cancelled,
|
||||||
|
|
||||||
|
Error::StreamCancelled { .. } => StatusCode::Cancelled,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ use datafusion::execution::registry::SerializerRegistry;
|
|||||||
use datafusion_common::DataFusionError;
|
use datafusion_common::DataFusionError;
|
||||||
use datafusion_expr::UserDefinedLogicalNode;
|
use datafusion_expr::UserDefinedLogicalNode;
|
||||||
use promql::extension_plan::{
|
use promql::extension_plan::{
|
||||||
EmptyMetric, InstantManipulate, RangeManipulate, ScalarCalculate, SeriesDivide, SeriesNormalize,
|
Absent, EmptyMetric, InstantManipulate, RangeManipulate, ScalarCalculate, SeriesDivide,
|
||||||
|
SeriesNormalize,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -65,6 +66,13 @@ impl SerializerRegistry for ExtensionSerializer {
|
|||||||
.expect("Failed to downcast to SeriesDivide");
|
.expect("Failed to downcast to SeriesDivide");
|
||||||
Ok(series_divide.serialize())
|
Ok(series_divide.serialize())
|
||||||
}
|
}
|
||||||
|
name if name == Absent::name() => {
|
||||||
|
let absent = node
|
||||||
|
.as_any()
|
||||||
|
.downcast_ref::<Absent>()
|
||||||
|
.expect("Failed to downcast to Absent");
|
||||||
|
Ok(absent.serialize())
|
||||||
|
}
|
||||||
name if name == EmptyMetric::name() => Err(DataFusionError::Substrait(
|
name if name == EmptyMetric::name() => Err(DataFusionError::Substrait(
|
||||||
"EmptyMetric should not be serialized".to_string(),
|
"EmptyMetric should not be serialized".to_string(),
|
||||||
)),
|
)),
|
||||||
@@ -103,6 +111,10 @@ impl SerializerRegistry for ExtensionSerializer {
|
|||||||
let scalar_calculate = ScalarCalculate::deserialize(bytes)?;
|
let scalar_calculate = ScalarCalculate::deserialize(bytes)?;
|
||||||
Ok(Arc::new(scalar_calculate))
|
Ok(Arc::new(scalar_calculate))
|
||||||
}
|
}
|
||||||
|
name if name == Absent::name() => {
|
||||||
|
let absent = Absent::deserialize(bytes)?;
|
||||||
|
Ok(Arc::new(absent))
|
||||||
|
}
|
||||||
name if name == EmptyMetric::name() => Err(DataFusionError::Substrait(
|
name if name == EmptyMetric::name() => Err(DataFusionError::Substrait(
|
||||||
"EmptyMetric should not be deserialized".to_string(),
|
"EmptyMetric should not be deserialized".to_string(),
|
||||||
)),
|
)),
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ deadlock_detection = ["parking_lot/deadlock_detection"]
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
atty = "0.2"
|
|
||||||
backtrace = "0.3"
|
backtrace = "0.3"
|
||||||
common-error.workspace = true
|
common-error.workspace = true
|
||||||
|
common-version.workspace = true
|
||||||
console-subscriber = { version = "0.1", optional = true }
|
console-subscriber = { version = "0.1", optional = true }
|
||||||
greptime-proto.workspace = true
|
greptime-proto.workspace = true
|
||||||
humantime-serde.workspace = true
|
humantime-serde.workspace = true
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
//! logging stuffs, inspired by databend
|
//! logging stuffs, inspired by databend
|
||||||
use std::env;
|
use std::env;
|
||||||
|
use std::io::IsTerminal;
|
||||||
use std::sync::{Arc, Mutex, Once};
|
use std::sync::{Arc, Mutex, Once};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
@@ -221,14 +222,14 @@ pub fn init_global_logging(
|
|||||||
Layer::new()
|
Layer::new()
|
||||||
.json()
|
.json()
|
||||||
.with_writer(writer)
|
.with_writer(writer)
|
||||||
.with_ansi(atty::is(atty::Stream::Stdout))
|
.with_ansi(std::io::stdout().is_terminal())
|
||||||
.boxed(),
|
.boxed(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Some(
|
Some(
|
||||||
Layer::new()
|
Layer::new()
|
||||||
.with_writer(writer)
|
.with_writer(writer)
|
||||||
.with_ansi(atty::is(atty::Stream::Stdout))
|
.with_ansi(std::io::stdout().is_terminal())
|
||||||
.boxed(),
|
.boxed(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -383,7 +384,7 @@ pub fn init_global_logging(
|
|||||||
resource::SERVICE_INSTANCE_ID,
|
resource::SERVICE_INSTANCE_ID,
|
||||||
node_id.unwrap_or("none".to_string()),
|
node_id.unwrap_or("none".to_string()),
|
||||||
),
|
),
|
||||||
KeyValue::new(resource::SERVICE_VERSION, env!("CARGO_PKG_VERSION")),
|
KeyValue::new(resource::SERVICE_VERSION, common_version::version()),
|
||||||
KeyValue::new(resource::PROCESS_PID, std::process::id().to_string()),
|
KeyValue::new(resource::PROCESS_PID, std::process::id().to_string()),
|
||||||
]));
|
]));
|
||||||
|
|
||||||
|
|||||||
@@ -17,4 +17,5 @@ shadow-rs.workspace = true
|
|||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
build-data = "0.2"
|
build-data = "0.2"
|
||||||
|
cargo-manifest = "0.19"
|
||||||
shadow-rs.workspace = true
|
shadow-rs.workspace = true
|
||||||
|
|||||||
@@ -14,8 +14,10 @@
|
|||||||
|
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use build_data::{format_timestamp, get_source_time};
|
use build_data::{format_timestamp, get_source_time};
|
||||||
|
use cargo_manifest::Manifest;
|
||||||
use shadow_rs::{BuildPattern, ShadowBuilder, CARGO_METADATA, CARGO_TREE};
|
use shadow_rs::{BuildPattern, ShadowBuilder, CARGO_METADATA, CARGO_TREE};
|
||||||
|
|
||||||
fn main() -> shadow_rs::SdResult<()> {
|
fn main() -> shadow_rs::SdResult<()> {
|
||||||
@@ -33,6 +35,24 @@ fn main() -> shadow_rs::SdResult<()> {
|
|||||||
// solve the problem where the "CARGO_MANIFEST_DIR" is not what we want when this repo is
|
// solve the problem where the "CARGO_MANIFEST_DIR" is not what we want when this repo is
|
||||||
// made as a submodule in another repo.
|
// made as a submodule in another repo.
|
||||||
let src_path = env::var("CARGO_WORKSPACE_DIR").or_else(|_| env::var("CARGO_MANIFEST_DIR"))?;
|
let src_path = env::var("CARGO_WORKSPACE_DIR").or_else(|_| env::var("CARGO_MANIFEST_DIR"))?;
|
||||||
|
|
||||||
|
let manifest = Manifest::from_path(PathBuf::from(&src_path).join("Cargo.toml"))
|
||||||
|
.expect("Failed to parse Cargo.toml");
|
||||||
|
if let Some(product_version) = manifest.workspace.as_ref().and_then(|w| {
|
||||||
|
w.metadata.as_ref().and_then(|m| {
|
||||||
|
m.get("greptime")
|
||||||
|
.and_then(|g| g.get("product_version").and_then(|v| v.as_str()))
|
||||||
|
})
|
||||||
|
}) {
|
||||||
|
println!(
|
||||||
|
"cargo:rustc-env=GREPTIME_PRODUCT_VERSION={}",
|
||||||
|
product_version
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let version = env::var("CARGO_PKG_VERSION").unwrap();
|
||||||
|
println!("cargo:rustc-env=GREPTIME_PRODUCT_VERSION={}", version,);
|
||||||
|
}
|
||||||
|
|
||||||
let out_path = env::var("OUT_DIR")?;
|
let out_path = env::var("OUT_DIR")?;
|
||||||
|
|
||||||
let _ = ShadowBuilder::builder()
|
let _ = ShadowBuilder::builder()
|
||||||
|
|||||||
@@ -105,13 +105,17 @@ pub const fn build_info() -> BuildInfo {
|
|||||||
build_time: env!("BUILD_TIMESTAMP"),
|
build_time: env!("BUILD_TIMESTAMP"),
|
||||||
rustc: build::RUST_VERSION,
|
rustc: build::RUST_VERSION,
|
||||||
target: build::BUILD_TARGET,
|
target: build::BUILD_TARGET,
|
||||||
version: build::PKG_VERSION,
|
version: env!("GREPTIME_PRODUCT_VERSION"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const BUILD_INFO: BuildInfo = build_info();
|
const BUILD_INFO: BuildInfo = build_info();
|
||||||
|
|
||||||
pub const fn version() -> &'static str {
|
pub const fn version() -> &'static str {
|
||||||
|
BUILD_INFO.version
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn verbose_version() -> &'static str {
|
||||||
const_format::formatcp!(
|
const_format::formatcp!(
|
||||||
"\nbranch: {}\ncommit: {}\nclean: {}\nversion: {}",
|
"\nbranch: {}\ncommit: {}\nclean: {}\nversion: {}",
|
||||||
BUILD_INFO.branch,
|
BUILD_INFO.branch,
|
||||||
|
|||||||
@@ -475,7 +475,7 @@ mod test {
|
|||||||
async fn region_alive_keeper() {
|
async fn region_alive_keeper() {
|
||||||
common_telemetry::init_default_ut_logging();
|
common_telemetry::init_default_ut_logging();
|
||||||
let mut region_server = mock_region_server();
|
let mut region_server = mock_region_server();
|
||||||
let mut engine_env = TestEnv::with_prefix("region-alive-keeper");
|
let mut engine_env = TestEnv::with_prefix("region-alive-keeper").await;
|
||||||
let engine = engine_env.create_engine(MitoConfig::default()).await;
|
let engine = engine_env.create_engine(MitoConfig::default()).await;
|
||||||
let engine = Arc::new(engine);
|
let engine = Arc::new(engine);
|
||||||
region_server.register_engine(engine.clone());
|
region_server.register_engine(engine.clone());
|
||||||
|
|||||||
@@ -144,6 +144,9 @@ pub struct HttpClientConfig {
|
|||||||
/// The timeout for idle sockets being kept-alive.
|
/// The timeout for idle sockets being kept-alive.
|
||||||
#[serde(with = "humantime_serde")]
|
#[serde(with = "humantime_serde")]
|
||||||
pub(crate) pool_idle_timeout: Duration,
|
pub(crate) pool_idle_timeout: Duration,
|
||||||
|
|
||||||
|
/// Skip SSL certificate validation (insecure)
|
||||||
|
pub skip_ssl_validation: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for HttpClientConfig {
|
impl Default for HttpClientConfig {
|
||||||
@@ -153,6 +156,7 @@ impl Default for HttpClientConfig {
|
|||||||
connect_timeout: Duration::from_secs(30),
|
connect_timeout: Duration::from_secs(30),
|
||||||
timeout: Duration::from_secs(30),
|
timeout: Duration::from_secs(30),
|
||||||
pool_idle_timeout: Duration::from_secs(90),
|
pool_idle_timeout: Duration::from_secs(90),
|
||||||
|
skip_ssl_validation: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -514,4 +518,48 @@ mod tests {
|
|||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_skip_ssl_validation_config() {
|
||||||
|
// Test with skip_ssl_validation = true
|
||||||
|
let toml_str_true = r#"
|
||||||
|
[storage]
|
||||||
|
type = "S3"
|
||||||
|
[storage.http_client]
|
||||||
|
skip_ssl_validation = true
|
||||||
|
"#;
|
||||||
|
let opts: DatanodeOptions = toml::from_str(toml_str_true).unwrap();
|
||||||
|
match &opts.storage.store {
|
||||||
|
ObjectStoreConfig::S3(cfg) => {
|
||||||
|
assert!(cfg.http_client.skip_ssl_validation);
|
||||||
|
}
|
||||||
|
_ => panic!("Expected S3 config"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test with skip_ssl_validation = false
|
||||||
|
let toml_str_false = r#"
|
||||||
|
[storage]
|
||||||
|
type = "S3"
|
||||||
|
[storage.http_client]
|
||||||
|
skip_ssl_validation = false
|
||||||
|
"#;
|
||||||
|
let opts: DatanodeOptions = toml::from_str(toml_str_false).unwrap();
|
||||||
|
match &opts.storage.store {
|
||||||
|
ObjectStoreConfig::S3(cfg) => {
|
||||||
|
assert!(!cfg.http_client.skip_ssl_validation);
|
||||||
|
}
|
||||||
|
_ => panic!("Expected S3 config"),
|
||||||
|
}
|
||||||
|
// Test default value (should be false)
|
||||||
|
let toml_str_default = r#"
|
||||||
|
[storage]
|
||||||
|
type = "S3"
|
||||||
|
"#;
|
||||||
|
let opts: DatanodeOptions = toml::from_str(toml_str_default).unwrap();
|
||||||
|
match &opts.storage.store {
|
||||||
|
ObjectStoreConfig::S3(cfg) => {
|
||||||
|
assert!(!cfg.http_client.skip_ssl_validation);
|
||||||
|
}
|
||||||
|
_ => panic!("Expected S3 config"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -278,7 +278,7 @@ mod tests {
|
|||||||
let mut region_server = mock_region_server();
|
let mut region_server = mock_region_server();
|
||||||
let heartbeat_handler = RegionHeartbeatResponseHandler::new(region_server.clone());
|
let heartbeat_handler = RegionHeartbeatResponseHandler::new(region_server.clone());
|
||||||
|
|
||||||
let mut engine_env = TestEnv::with_prefix("close-region");
|
let mut engine_env = TestEnv::with_prefix("close-region").await;
|
||||||
let engine = engine_env.create_engine(MitoConfig::default()).await;
|
let engine = engine_env.create_engine(MitoConfig::default()).await;
|
||||||
region_server.register_engine(Arc::new(engine));
|
region_server.register_engine(Arc::new(engine));
|
||||||
let region_id = RegionId::new(1024, 1);
|
let region_id = RegionId::new(1024, 1);
|
||||||
@@ -326,7 +326,7 @@ mod tests {
|
|||||||
let mut region_server = mock_region_server();
|
let mut region_server = mock_region_server();
|
||||||
let heartbeat_handler = RegionHeartbeatResponseHandler::new(region_server.clone());
|
let heartbeat_handler = RegionHeartbeatResponseHandler::new(region_server.clone());
|
||||||
|
|
||||||
let mut engine_env = TestEnv::with_prefix("open-region");
|
let mut engine_env = TestEnv::with_prefix("open-region").await;
|
||||||
let engine = engine_env.create_engine(MitoConfig::default()).await;
|
let engine = engine_env.create_engine(MitoConfig::default()).await;
|
||||||
region_server.register_engine(Arc::new(engine));
|
region_server.register_engine(Arc::new(engine));
|
||||||
let region_id = RegionId::new(1024, 1);
|
let region_id = RegionId::new(1024, 1);
|
||||||
@@ -374,7 +374,7 @@ mod tests {
|
|||||||
let mut region_server = mock_region_server();
|
let mut region_server = mock_region_server();
|
||||||
let heartbeat_handler = RegionHeartbeatResponseHandler::new(region_server.clone());
|
let heartbeat_handler = RegionHeartbeatResponseHandler::new(region_server.clone());
|
||||||
|
|
||||||
let mut engine_env = TestEnv::with_prefix("open-not-exists-region");
|
let mut engine_env = TestEnv::with_prefix("open-not-exists-region").await;
|
||||||
let engine = engine_env.create_engine(MitoConfig::default()).await;
|
let engine = engine_env.create_engine(MitoConfig::default()).await;
|
||||||
region_server.register_engine(Arc::new(engine));
|
region_server.register_engine(Arc::new(engine));
|
||||||
let region_id = RegionId::new(1024, 1);
|
let region_id = RegionId::new(1024, 1);
|
||||||
@@ -406,7 +406,7 @@ mod tests {
|
|||||||
let mut region_server = mock_region_server();
|
let mut region_server = mock_region_server();
|
||||||
let heartbeat_handler = RegionHeartbeatResponseHandler::new(region_server.clone());
|
let heartbeat_handler = RegionHeartbeatResponseHandler::new(region_server.clone());
|
||||||
|
|
||||||
let mut engine_env = TestEnv::with_prefix("downgrade-region");
|
let mut engine_env = TestEnv::with_prefix("downgrade-region").await;
|
||||||
let engine = engine_env.create_engine(MitoConfig::default()).await;
|
let engine = engine_env.create_engine(MitoConfig::default()).await;
|
||||||
region_server.register_engine(Arc::new(engine));
|
region_server.register_engine(Arc::new(engine));
|
||||||
let region_id = RegionId::new(1024, 1);
|
let region_id = RegionId::new(1024, 1);
|
||||||
|
|||||||
@@ -27,14 +27,14 @@ lazy_static! {
|
|||||||
pub static ref HANDLE_REGION_REQUEST_ELAPSED: HistogramVec = register_histogram_vec!(
|
pub static ref HANDLE_REGION_REQUEST_ELAPSED: HistogramVec = register_histogram_vec!(
|
||||||
"greptime_datanode_handle_region_request_elapsed",
|
"greptime_datanode_handle_region_request_elapsed",
|
||||||
"datanode handle region request elapsed",
|
"datanode handle region request elapsed",
|
||||||
&[REGION_ID, REGION_REQUEST_TYPE]
|
&[REGION_REQUEST_TYPE]
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
/// The number of rows in region request received by region server, labeled with request type.
|
/// The number of rows in region request received by region server, labeled with request type.
|
||||||
pub static ref REGION_CHANGED_ROW_COUNT: IntCounterVec = register_int_counter_vec!(
|
pub static ref REGION_CHANGED_ROW_COUNT: IntCounterVec = register_int_counter_vec!(
|
||||||
"greptime_datanode_region_changed_row_count",
|
"greptime_datanode_region_changed_row_count",
|
||||||
"datanode region changed row count",
|
"datanode region changed row count",
|
||||||
&[REGION_ID, REGION_REQUEST_TYPE]
|
&[REGION_REQUEST_TYPE]
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
/// The elapsed time since the last received heartbeat.
|
/// The elapsed time since the last received heartbeat.
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ use servers::error::{self as servers_error, ExecuteGrpcRequestSnafu, Result as S
|
|||||||
use servers::grpc::flight::{FlightCraft, FlightRecordBatchStream, TonicStream};
|
use servers::grpc::flight::{FlightCraft, FlightRecordBatchStream, TonicStream};
|
||||||
use servers::grpc::region_server::RegionServerHandler;
|
use servers::grpc::region_server::RegionServerHandler;
|
||||||
use servers::grpc::FlightCompression;
|
use servers::grpc::FlightCompression;
|
||||||
use session::context::{QueryContextBuilder, QueryContextRef};
|
use session::context::{QueryContext, QueryContextBuilder, QueryContextRef};
|
||||||
use snafu::{ensure, OptionExt, ResultExt};
|
use snafu::{ensure, OptionExt, ResultExt};
|
||||||
use store_api::metric_engine_consts::{
|
use store_api::metric_engine_consts::{
|
||||||
FILE_ENGINE_NAME, LOGICAL_TABLE_METADATA_KEY, METRIC_ENGINE_NAME,
|
FILE_ENGINE_NAME, LOGICAL_TABLE_METADATA_KEY, METRIC_ENGINE_NAME,
|
||||||
@@ -194,6 +194,7 @@ impl RegionServer {
|
|||||||
pub async fn handle_remote_read(
|
pub async fn handle_remote_read(
|
||||||
&self,
|
&self,
|
||||||
request: api::v1::region::QueryRequest,
|
request: api::v1::region::QueryRequest,
|
||||||
|
query_ctx: QueryContextRef,
|
||||||
) -> Result<SendableRecordBatchStream> {
|
) -> Result<SendableRecordBatchStream> {
|
||||||
let _permit = if let Some(p) = &self.inner.parallelism {
|
let _permit = if let Some(p) = &self.inner.parallelism {
|
||||||
Some(p.acquire().await?)
|
Some(p.acquire().await?)
|
||||||
@@ -201,12 +202,6 @@ impl RegionServer {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let query_ctx: QueryContextRef = request
|
|
||||||
.header
|
|
||||||
.as_ref()
|
|
||||||
.map(|h| Arc::new(h.into()))
|
|
||||||
.unwrap_or_else(|| Arc::new(QueryContextBuilder::default().build()));
|
|
||||||
|
|
||||||
let region_id = RegionId::from_u64(request.region_id);
|
let region_id = RegionId::from_u64(request.region_id);
|
||||||
let provider = self.table_provider(region_id, Some(&query_ctx)).await?;
|
let provider = self.table_provider(region_id, Some(&query_ctx)).await?;
|
||||||
let catalog_list = Arc::new(DummyCatalogList::with_table_provider(provider));
|
let catalog_list = Arc::new(DummyCatalogList::with_table_provider(provider));
|
||||||
@@ -214,7 +209,7 @@ impl RegionServer {
|
|||||||
let decoder = self
|
let decoder = self
|
||||||
.inner
|
.inner
|
||||||
.query_engine
|
.query_engine
|
||||||
.engine_context(query_ctx)
|
.engine_context(query_ctx.clone())
|
||||||
.new_plan_decoder()
|
.new_plan_decoder()
|
||||||
.context(NewPlanDecoderSnafu)?;
|
.context(NewPlanDecoderSnafu)?;
|
||||||
|
|
||||||
@@ -224,11 +219,14 @@ impl RegionServer {
|
|||||||
.context(DecodeLogicalPlanSnafu)?;
|
.context(DecodeLogicalPlanSnafu)?;
|
||||||
|
|
||||||
self.inner
|
self.inner
|
||||||
.handle_read(QueryRequest {
|
.handle_read(
|
||||||
header: request.header,
|
QueryRequest {
|
||||||
region_id,
|
header: request.header,
|
||||||
plan,
|
region_id,
|
||||||
})
|
plan,
|
||||||
|
},
|
||||||
|
query_ctx,
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,6 +241,7 @@ impl RegionServer {
|
|||||||
let ctx: Option<session::context::QueryContext> = request.header.as_ref().map(|h| h.into());
|
let ctx: Option<session::context::QueryContext> = request.header.as_ref().map(|h| h.into());
|
||||||
|
|
||||||
let provider = self.table_provider(request.region_id, ctx.as_ref()).await?;
|
let provider = self.table_provider(request.region_id, ctx.as_ref()).await?;
|
||||||
|
let query_ctx = Arc::new(ctx.unwrap_or_else(|| QueryContextBuilder::default().build()));
|
||||||
|
|
||||||
struct RegionDataSourceInjector {
|
struct RegionDataSourceInjector {
|
||||||
source: Arc<dyn TableSource>,
|
source: Arc<dyn TableSource>,
|
||||||
@@ -271,7 +270,7 @@ impl RegionServer {
|
|||||||
.data;
|
.data;
|
||||||
|
|
||||||
self.inner
|
self.inner
|
||||||
.handle_read(QueryRequest { plan, ..request })
|
.handle_read(QueryRequest { plan, ..request }, query_ctx)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -536,9 +535,14 @@ impl FlightCraft for RegionServer {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|h| TracingContext::from_w3c(&h.tracing_context))
|
.map(|h| TracingContext::from_w3c(&h.tracing_context))
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
let query_ctx = request
|
||||||
|
.header
|
||||||
|
.as_ref()
|
||||||
|
.map(|h| Arc::new(QueryContext::from(h)))
|
||||||
|
.unwrap_or(QueryContext::arc());
|
||||||
|
|
||||||
let result = self
|
let result = self
|
||||||
.handle_remote_read(request)
|
.handle_remote_read(request, query_ctx.clone())
|
||||||
.trace(tracing_context.attach(info_span!("RegionServer::handle_read")))
|
.trace(tracing_context.attach(info_span!("RegionServer::handle_read")))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@@ -546,6 +550,7 @@ impl FlightCraft for RegionServer {
|
|||||||
result,
|
result,
|
||||||
tracing_context,
|
tracing_context,
|
||||||
self.flight_compression,
|
self.flight_compression,
|
||||||
|
query_ctx,
|
||||||
));
|
));
|
||||||
Ok(Response::new(stream))
|
Ok(Response::new(stream))
|
||||||
}
|
}
|
||||||
@@ -915,9 +920,8 @@ impl RegionServerInner {
|
|||||||
request: RegionRequest,
|
request: RegionRequest,
|
||||||
) -> Result<RegionResponse> {
|
) -> Result<RegionResponse> {
|
||||||
let request_type = request.request_type();
|
let request_type = request.request_type();
|
||||||
let region_id_str = region_id.to_string();
|
|
||||||
let _timer = crate::metrics::HANDLE_REGION_REQUEST_ELAPSED
|
let _timer = crate::metrics::HANDLE_REGION_REQUEST_ELAPSED
|
||||||
.with_label_values(&[®ion_id_str, request_type])
|
.with_label_values(&[request_type])
|
||||||
.start_timer();
|
.start_timer();
|
||||||
|
|
||||||
let region_change = match &request {
|
let region_change = match &request {
|
||||||
@@ -957,7 +961,7 @@ impl RegionServerInner {
|
|||||||
// Update metrics
|
// Update metrics
|
||||||
if matches!(region_change, RegionChange::Ingest) {
|
if matches!(region_change, RegionChange::Ingest) {
|
||||||
crate::metrics::REGION_CHANGED_ROW_COUNT
|
crate::metrics::REGION_CHANGED_ROW_COUNT
|
||||||
.with_label_values(&[®ion_id_str, request_type])
|
.with_label_values(&[request_type])
|
||||||
.inc_by(result.affected_rows as u64);
|
.inc_by(result.affected_rows as u64);
|
||||||
}
|
}
|
||||||
// Sets corresponding region status to ready.
|
// Sets corresponding region status to ready.
|
||||||
@@ -1124,16 +1128,13 @@ impl RegionServerInner {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn handle_read(&self, request: QueryRequest) -> Result<SendableRecordBatchStream> {
|
pub async fn handle_read(
|
||||||
|
&self,
|
||||||
|
request: QueryRequest,
|
||||||
|
query_ctx: QueryContextRef,
|
||||||
|
) -> Result<SendableRecordBatchStream> {
|
||||||
// TODO(ruihang): add metrics and set trace id
|
// TODO(ruihang): add metrics and set trace id
|
||||||
|
|
||||||
// Build query context from gRPC header
|
|
||||||
let query_ctx: QueryContextRef = request
|
|
||||||
.header
|
|
||||||
.as_ref()
|
|
||||||
.map(|h| Arc::new(h.into()))
|
|
||||||
.unwrap_or_else(|| QueryContextBuilder::default().build().into());
|
|
||||||
|
|
||||||
let result = self
|
let result = self
|
||||||
.query_engine
|
.query_engine
|
||||||
.execute(request.plan, query_ctx)
|
.execute(request.plan, query_ctx)
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use common_config::Configurable;
|
use common_config::Configurable;
|
||||||
use servers::grpc::builder::GrpcServerBuilder;
|
use servers::grpc::builder::GrpcServerBuilder;
|
||||||
use servers::grpc::{GrpcServer, GrpcServerConfig};
|
use servers::grpc::GrpcServer;
|
||||||
use servers::http::HttpServerBuilder;
|
use servers::http::HttpServerBuilder;
|
||||||
use servers::metrics_handler::MetricsHandler;
|
use servers::metrics_handler::MetricsHandler;
|
||||||
use servers::server::{ServerHandler, ServerHandlers};
|
use servers::server::{ServerHandler, ServerHandlers};
|
||||||
@@ -92,13 +92,7 @@ impl<'a> DatanodeServiceBuilder<'a> {
|
|||||||
opts: &DatanodeOptions,
|
opts: &DatanodeOptions,
|
||||||
region_server: &RegionServer,
|
region_server: &RegionServer,
|
||||||
) -> GrpcServerBuilder {
|
) -> GrpcServerBuilder {
|
||||||
let config = GrpcServerConfig {
|
GrpcServerBuilder::new(opts.grpc.as_config(), region_server.runtime())
|
||||||
max_recv_message_size: opts.grpc.max_recv_message_size.as_bytes() as usize,
|
|
||||||
max_send_message_size: opts.grpc.max_send_message_size.as_bytes() as usize,
|
|
||||||
tls: opts.grpc.tls.clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
GrpcServerBuilder::new(config, region_server.runtime())
|
|
||||||
.flight_handler(Arc::new(region_server.clone()))
|
.flight_handler(Arc::new(region_server.clone()))
|
||||||
.region_server_handler(Arc::new(region_server.clone()))
|
.region_server_handler(Arc::new(region_server.clone()))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -207,11 +207,16 @@ pub(crate) fn clean_temp_dir(dir: &str) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn build_http_client(config: &HttpClientConfig) -> Result<HttpClient> {
|
pub(crate) fn build_http_client(config: &HttpClientConfig) -> Result<HttpClient> {
|
||||||
|
if config.skip_ssl_validation {
|
||||||
|
common_telemetry::warn!("Skipping SSL validation for object storage HTTP client. Please ensure the environment is trusted.");
|
||||||
|
}
|
||||||
|
|
||||||
let client = reqwest::ClientBuilder::new()
|
let client = reqwest::ClientBuilder::new()
|
||||||
.pool_max_idle_per_host(config.pool_max_idle_per_host as usize)
|
.pool_max_idle_per_host(config.pool_max_idle_per_host as usize)
|
||||||
.connect_timeout(config.connect_timeout)
|
.connect_timeout(config.connect_timeout)
|
||||||
.pool_idle_timeout(config.pool_idle_timeout)
|
.pool_idle_timeout(config.pool_idle_timeout)
|
||||||
.timeout(config.timeout)
|
.timeout(config.timeout)
|
||||||
|
.danger_accept_invalid_certs(config.skip_ssl_validation)
|
||||||
.build()
|
.build()
|
||||||
.context(BuildHttpClientSnafu)?;
|
.context(BuildHttpClientSnafu)?;
|
||||||
Ok(HttpClient::with(client))
|
Ok(HttpClient::with(client))
|
||||||
|
|||||||
@@ -31,9 +31,10 @@ pub use crate::schema::column_schema::{
|
|||||||
ColumnSchema, FulltextAnalyzer, FulltextBackend, FulltextOptions, Metadata,
|
ColumnSchema, FulltextAnalyzer, FulltextBackend, FulltextOptions, Metadata,
|
||||||
SkippingIndexOptions, SkippingIndexType, COLUMN_FULLTEXT_CHANGE_OPT_KEY_ENABLE,
|
SkippingIndexOptions, SkippingIndexType, COLUMN_FULLTEXT_CHANGE_OPT_KEY_ENABLE,
|
||||||
COLUMN_FULLTEXT_OPT_KEY_ANALYZER, COLUMN_FULLTEXT_OPT_KEY_BACKEND,
|
COLUMN_FULLTEXT_OPT_KEY_ANALYZER, COLUMN_FULLTEXT_OPT_KEY_BACKEND,
|
||||||
COLUMN_FULLTEXT_OPT_KEY_CASE_SENSITIVE, COLUMN_SKIPPING_INDEX_OPT_KEY_GRANULARITY,
|
COLUMN_FULLTEXT_OPT_KEY_CASE_SENSITIVE, COLUMN_FULLTEXT_OPT_KEY_FALSE_POSITIVE_RATE,
|
||||||
COLUMN_SKIPPING_INDEX_OPT_KEY_TYPE, COMMENT_KEY, FULLTEXT_KEY, INVERTED_INDEX_KEY,
|
COLUMN_FULLTEXT_OPT_KEY_GRANULARITY, COLUMN_SKIPPING_INDEX_OPT_KEY_FALSE_POSITIVE_RATE,
|
||||||
SKIPPING_INDEX_KEY, TIME_INDEX_KEY,
|
COLUMN_SKIPPING_INDEX_OPT_KEY_GRANULARITY, COLUMN_SKIPPING_INDEX_OPT_KEY_TYPE, COMMENT_KEY,
|
||||||
|
FULLTEXT_KEY, INVERTED_INDEX_KEY, SKIPPING_INDEX_KEY, TIME_INDEX_KEY,
|
||||||
};
|
};
|
||||||
pub use crate::schema::constraint::ColumnDefaultConstraint;
|
pub use crate::schema::constraint::ColumnDefaultConstraint;
|
||||||
pub use crate::schema::raw::RawSchema;
|
pub use crate::schema::raw::RawSchema;
|
||||||
|
|||||||
@@ -47,13 +47,18 @@ pub const COLUMN_FULLTEXT_CHANGE_OPT_KEY_ENABLE: &str = "enable";
|
|||||||
pub const COLUMN_FULLTEXT_OPT_KEY_ANALYZER: &str = "analyzer";
|
pub const COLUMN_FULLTEXT_OPT_KEY_ANALYZER: &str = "analyzer";
|
||||||
pub const COLUMN_FULLTEXT_OPT_KEY_CASE_SENSITIVE: &str = "case_sensitive";
|
pub const COLUMN_FULLTEXT_OPT_KEY_CASE_SENSITIVE: &str = "case_sensitive";
|
||||||
pub const COLUMN_FULLTEXT_OPT_KEY_BACKEND: &str = "backend";
|
pub const COLUMN_FULLTEXT_OPT_KEY_BACKEND: &str = "backend";
|
||||||
|
pub const COLUMN_FULLTEXT_OPT_KEY_GRANULARITY: &str = "granularity";
|
||||||
|
pub const COLUMN_FULLTEXT_OPT_KEY_FALSE_POSITIVE_RATE: &str = "false_positive_rate";
|
||||||
|
|
||||||
/// Keys used in SKIPPING index options
|
/// Keys used in SKIPPING index options
|
||||||
pub const COLUMN_SKIPPING_INDEX_OPT_KEY_GRANULARITY: &str = "granularity";
|
pub const COLUMN_SKIPPING_INDEX_OPT_KEY_GRANULARITY: &str = "granularity";
|
||||||
|
pub const COLUMN_SKIPPING_INDEX_OPT_KEY_FALSE_POSITIVE_RATE: &str = "false_positive_rate";
|
||||||
pub const COLUMN_SKIPPING_INDEX_OPT_KEY_TYPE: &str = "type";
|
pub const COLUMN_SKIPPING_INDEX_OPT_KEY_TYPE: &str = "type";
|
||||||
|
|
||||||
pub const DEFAULT_GRANULARITY: u32 = 10240;
|
pub const DEFAULT_GRANULARITY: u32 = 10240;
|
||||||
|
|
||||||
|
pub const DEFAULT_FALSE_POSITIVE_RATE: f64 = 0.01;
|
||||||
|
|
||||||
/// Schema of a column, used as an immutable struct.
|
/// Schema of a column, used as an immutable struct.
|
||||||
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct ColumnSchema {
|
pub struct ColumnSchema {
|
||||||
@@ -504,7 +509,7 @@ impl TryFrom<&ColumnSchema> for Field {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Fulltext options for a column.
|
/// Fulltext options for a column.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default, Visit, VisitMut)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Visit, VisitMut)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub struct FulltextOptions {
|
pub struct FulltextOptions {
|
||||||
/// Whether the fulltext index is enabled.
|
/// Whether the fulltext index is enabled.
|
||||||
@@ -518,6 +523,92 @@ pub struct FulltextOptions {
|
|||||||
/// The fulltext backend to use.
|
/// The fulltext backend to use.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub backend: FulltextBackend,
|
pub backend: FulltextBackend,
|
||||||
|
/// The granularity of the fulltext index (for bloom backend only)
|
||||||
|
#[serde(default = "fulltext_options_default_granularity")]
|
||||||
|
pub granularity: u32,
|
||||||
|
/// The false positive rate of the fulltext index (for bloom backend only)
|
||||||
|
#[serde(default = "index_options_default_false_positive_rate_in_10000")]
|
||||||
|
pub false_positive_rate_in_10000: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fulltext_options_default_granularity() -> u32 {
|
||||||
|
DEFAULT_GRANULARITY
|
||||||
|
}
|
||||||
|
|
||||||
|
fn index_options_default_false_positive_rate_in_10000() -> u32 {
|
||||||
|
(DEFAULT_FALSE_POSITIVE_RATE * 10000.0) as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FulltextOptions {
|
||||||
|
/// Creates a new fulltext options.
|
||||||
|
pub fn new(
|
||||||
|
enable: bool,
|
||||||
|
analyzer: FulltextAnalyzer,
|
||||||
|
case_sensitive: bool,
|
||||||
|
backend: FulltextBackend,
|
||||||
|
granularity: u32,
|
||||||
|
false_positive_rate: f64,
|
||||||
|
) -> Result<Self> {
|
||||||
|
ensure!(
|
||||||
|
0.0 < false_positive_rate && false_positive_rate <= 1.0,
|
||||||
|
error::InvalidFulltextOptionSnafu {
|
||||||
|
msg: format!(
|
||||||
|
"Invalid false positive rate: {false_positive_rate}, expected: 0.0 < rate <= 1.0"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
ensure!(
|
||||||
|
granularity > 0,
|
||||||
|
error::InvalidFulltextOptionSnafu {
|
||||||
|
msg: format!("Invalid granularity: {granularity}, expected: positive integer"),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
Ok(Self::new_unchecked(
|
||||||
|
enable,
|
||||||
|
analyzer,
|
||||||
|
case_sensitive,
|
||||||
|
backend,
|
||||||
|
granularity,
|
||||||
|
false_positive_rate,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new fulltext options without checking `false_positive_rate` and `granularity`.
|
||||||
|
pub fn new_unchecked(
|
||||||
|
enable: bool,
|
||||||
|
analyzer: FulltextAnalyzer,
|
||||||
|
case_sensitive: bool,
|
||||||
|
backend: FulltextBackend,
|
||||||
|
granularity: u32,
|
||||||
|
false_positive_rate: f64,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
enable,
|
||||||
|
analyzer,
|
||||||
|
case_sensitive,
|
||||||
|
backend,
|
||||||
|
granularity,
|
||||||
|
false_positive_rate_in_10000: (false_positive_rate * 10000.0) as u32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the false positive rate.
|
||||||
|
pub fn false_positive_rate(&self) -> f64 {
|
||||||
|
self.false_positive_rate_in_10000 as f64 / 10000.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for FulltextOptions {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new_unchecked(
|
||||||
|
false,
|
||||||
|
FulltextAnalyzer::default(),
|
||||||
|
false,
|
||||||
|
FulltextBackend::default(),
|
||||||
|
DEFAULT_GRANULARITY,
|
||||||
|
DEFAULT_FALSE_POSITIVE_RATE,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for FulltextOptions {
|
impl fmt::Display for FulltextOptions {
|
||||||
@@ -527,6 +618,10 @@ impl fmt::Display for FulltextOptions {
|
|||||||
write!(f, ", analyzer={}", self.analyzer)?;
|
write!(f, ", analyzer={}", self.analyzer)?;
|
||||||
write!(f, ", case_sensitive={}", self.case_sensitive)?;
|
write!(f, ", case_sensitive={}", self.case_sensitive)?;
|
||||||
write!(f, ", backend={}", self.backend)?;
|
write!(f, ", backend={}", self.backend)?;
|
||||||
|
if self.backend == FulltextBackend::Bloom {
|
||||||
|
write!(f, ", granularity={}", self.granularity)?;
|
||||||
|
write!(f, ", false_positive_rate={}", self.false_positive_rate())?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -611,6 +706,45 @@ impl TryFrom<HashMap<String, String>> for FulltextOptions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if fulltext_options.backend == FulltextBackend::Bloom {
|
||||||
|
// Parse granularity with default value 10240
|
||||||
|
let granularity = match options.get(COLUMN_FULLTEXT_OPT_KEY_GRANULARITY) {
|
||||||
|
Some(value) => value
|
||||||
|
.parse::<u32>()
|
||||||
|
.ok()
|
||||||
|
.filter(|&v| v > 0)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
error::InvalidFulltextOptionSnafu {
|
||||||
|
msg: format!(
|
||||||
|
"Invalid granularity: {value}, expected: positive integer"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
})?,
|
||||||
|
None => DEFAULT_GRANULARITY,
|
||||||
|
};
|
||||||
|
fulltext_options.granularity = granularity;
|
||||||
|
|
||||||
|
// Parse false positive rate with default value 0.01
|
||||||
|
let false_positive_rate = match options.get(COLUMN_FULLTEXT_OPT_KEY_FALSE_POSITIVE_RATE)
|
||||||
|
{
|
||||||
|
Some(value) => value
|
||||||
|
.parse::<f64>()
|
||||||
|
.ok()
|
||||||
|
.filter(|&v| v > 0.0 && v <= 1.0)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
error::InvalidFulltextOptionSnafu {
|
||||||
|
msg: format!(
|
||||||
|
"Invalid false positive rate: {value}, expected: 0.0 < rate <= 1.0"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
})?,
|
||||||
|
None => DEFAULT_FALSE_POSITIVE_RATE,
|
||||||
|
};
|
||||||
|
fulltext_options.false_positive_rate_in_10000 = (false_positive_rate * 10000.0) as u32;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(fulltext_options)
|
Ok(fulltext_options)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -638,23 +772,73 @@ impl fmt::Display for FulltextAnalyzer {
|
|||||||
pub struct SkippingIndexOptions {
|
pub struct SkippingIndexOptions {
|
||||||
/// The granularity of the skip index.
|
/// The granularity of the skip index.
|
||||||
pub granularity: u32,
|
pub granularity: u32,
|
||||||
|
/// The false positive rate of the skip index (in ten-thousandths, e.g., 100 = 1%).
|
||||||
|
#[serde(default = "index_options_default_false_positive_rate_in_10000")]
|
||||||
|
pub false_positive_rate_in_10000: u32,
|
||||||
/// The type of the skip index.
|
/// The type of the skip index.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub index_type: SkippingIndexType,
|
pub index_type: SkippingIndexType,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SkippingIndexOptions {
|
||||||
|
/// Creates a new skipping index options without checking `false_positive_rate` and `granularity`.
|
||||||
|
pub fn new_unchecked(
|
||||||
|
granularity: u32,
|
||||||
|
false_positive_rate: f64,
|
||||||
|
index_type: SkippingIndexType,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
granularity,
|
||||||
|
false_positive_rate_in_10000: (false_positive_rate * 10000.0) as u32,
|
||||||
|
index_type,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new skipping index options.
|
||||||
|
pub fn new(
|
||||||
|
granularity: u32,
|
||||||
|
false_positive_rate: f64,
|
||||||
|
index_type: SkippingIndexType,
|
||||||
|
) -> Result<Self> {
|
||||||
|
ensure!(
|
||||||
|
0.0 < false_positive_rate && false_positive_rate <= 1.0,
|
||||||
|
error::InvalidSkippingIndexOptionSnafu {
|
||||||
|
msg: format!("Invalid false positive rate: {false_positive_rate}, expected: 0.0 < rate <= 1.0"),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
ensure!(
|
||||||
|
granularity > 0,
|
||||||
|
error::InvalidSkippingIndexOptionSnafu {
|
||||||
|
msg: format!("Invalid granularity: {granularity}, expected: positive integer"),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
Ok(Self::new_unchecked(
|
||||||
|
granularity,
|
||||||
|
false_positive_rate,
|
||||||
|
index_type,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the false positive rate.
|
||||||
|
pub fn false_positive_rate(&self) -> f64 {
|
||||||
|
self.false_positive_rate_in_10000 as f64 / 10000.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for SkippingIndexOptions {
|
impl Default for SkippingIndexOptions {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self::new_unchecked(
|
||||||
granularity: DEFAULT_GRANULARITY,
|
DEFAULT_GRANULARITY,
|
||||||
index_type: SkippingIndexType::default(),
|
DEFAULT_FALSE_POSITIVE_RATE,
|
||||||
}
|
SkippingIndexType::default(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for SkippingIndexOptions {
|
impl fmt::Display for SkippingIndexOptions {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "granularity={}", self.granularity)?;
|
write!(f, "granularity={}", self.granularity)?;
|
||||||
|
write!(f, ", false_positive_rate={}", self.false_positive_rate())?;
|
||||||
write!(f, ", index_type={}", self.index_type)?;
|
write!(f, ", index_type={}", self.index_type)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -681,15 +865,37 @@ impl TryFrom<HashMap<String, String>> for SkippingIndexOptions {
|
|||||||
fn try_from(options: HashMap<String, String>) -> Result<Self> {
|
fn try_from(options: HashMap<String, String>) -> Result<Self> {
|
||||||
// Parse granularity with default value 1
|
// Parse granularity with default value 1
|
||||||
let granularity = match options.get(COLUMN_SKIPPING_INDEX_OPT_KEY_GRANULARITY) {
|
let granularity = match options.get(COLUMN_SKIPPING_INDEX_OPT_KEY_GRANULARITY) {
|
||||||
Some(value) => value.parse::<u32>().map_err(|_| {
|
Some(value) => value
|
||||||
error::InvalidSkippingIndexOptionSnafu {
|
.parse::<u32>()
|
||||||
msg: format!("Invalid granularity: {value}, expected: positive integer"),
|
.ok()
|
||||||
}
|
.filter(|&v| v > 0)
|
||||||
.build()
|
.ok_or_else(|| {
|
||||||
})?,
|
error::InvalidSkippingIndexOptionSnafu {
|
||||||
|
msg: format!("Invalid granularity: {value}, expected: positive integer"),
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
})?,
|
||||||
None => DEFAULT_GRANULARITY,
|
None => DEFAULT_GRANULARITY,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Parse false positive rate with default value 100
|
||||||
|
let false_positive_rate =
|
||||||
|
match options.get(COLUMN_SKIPPING_INDEX_OPT_KEY_FALSE_POSITIVE_RATE) {
|
||||||
|
Some(value) => value
|
||||||
|
.parse::<f64>()
|
||||||
|
.ok()
|
||||||
|
.filter(|&v| v > 0.0 && v <= 1.0)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
error::InvalidSkippingIndexOptionSnafu {
|
||||||
|
msg: format!(
|
||||||
|
"Invalid false positive rate: {value}, expected: 0.0 < rate <= 1.0"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
})?,
|
||||||
|
None => DEFAULT_FALSE_POSITIVE_RATE,
|
||||||
|
};
|
||||||
|
|
||||||
// Parse index type with default value BloomFilter
|
// Parse index type with default value BloomFilter
|
||||||
let index_type = match options.get(COLUMN_SKIPPING_INDEX_OPT_KEY_TYPE) {
|
let index_type = match options.get(COLUMN_SKIPPING_INDEX_OPT_KEY_TYPE) {
|
||||||
Some(typ) => match typ.to_ascii_uppercase().as_str() {
|
Some(typ) => match typ.to_ascii_uppercase().as_str() {
|
||||||
@@ -704,10 +910,11 @@ impl TryFrom<HashMap<String, String>> for SkippingIndexOptions {
|
|||||||
None => SkippingIndexType::default(),
|
None => SkippingIndexType::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(SkippingIndexOptions {
|
Ok(SkippingIndexOptions::new_unchecked(
|
||||||
granularity,
|
granularity,
|
||||||
|
false_positive_rate,
|
||||||
index_type,
|
index_type,
|
||||||
})
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -973,4 +1180,59 @@ mod tests {
|
|||||||
assert!(column_schema.default_constraint.is_none());
|
assert!(column_schema.default_constraint.is_none());
|
||||||
assert!(column_schema.metadata.is_empty());
|
assert!(column_schema.metadata.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_skipping_index_options_deserialization() {
|
||||||
|
let original_options = "{\"granularity\":1024,\"false-positive-rate-in-10000\":10,\"index-type\":\"BloomFilter\"}";
|
||||||
|
let options = serde_json::from_str::<SkippingIndexOptions>(original_options).unwrap();
|
||||||
|
assert_eq!(1024, options.granularity);
|
||||||
|
assert_eq!(SkippingIndexType::BloomFilter, options.index_type);
|
||||||
|
assert_eq!(0.001, options.false_positive_rate());
|
||||||
|
|
||||||
|
let options_str = serde_json::to_string(&options).unwrap();
|
||||||
|
assert_eq!(options_str, original_options);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_skipping_index_options_deserialization_v0_14_to_v0_15() {
|
||||||
|
let options = "{\"granularity\":10240,\"index-type\":\"BloomFilter\"}";
|
||||||
|
let options = serde_json::from_str::<SkippingIndexOptions>(options).unwrap();
|
||||||
|
assert_eq!(10240, options.granularity);
|
||||||
|
assert_eq!(SkippingIndexType::BloomFilter, options.index_type);
|
||||||
|
assert_eq!(DEFAULT_FALSE_POSITIVE_RATE, options.false_positive_rate());
|
||||||
|
|
||||||
|
let options_str = serde_json::to_string(&options).unwrap();
|
||||||
|
assert_eq!(options_str, "{\"granularity\":10240,\"false-positive-rate-in-10000\":100,\"index-type\":\"BloomFilter\"}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fulltext_options_deserialization() {
|
||||||
|
let original_options = "{\"enable\":true,\"analyzer\":\"English\",\"case-sensitive\":false,\"backend\":\"bloom\",\"granularity\":1024,\"false-positive-rate-in-10000\":10}";
|
||||||
|
let options = serde_json::from_str::<FulltextOptions>(original_options).unwrap();
|
||||||
|
assert!(!options.case_sensitive);
|
||||||
|
assert!(options.enable);
|
||||||
|
assert_eq!(FulltextBackend::Bloom, options.backend);
|
||||||
|
assert_eq!(FulltextAnalyzer::default(), options.analyzer);
|
||||||
|
assert_eq!(1024, options.granularity);
|
||||||
|
assert_eq!(0.001, options.false_positive_rate());
|
||||||
|
|
||||||
|
let options_str = serde_json::to_string(&options).unwrap();
|
||||||
|
assert_eq!(options_str, original_options);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fulltext_options_deserialization_v0_14_to_v0_15() {
|
||||||
|
// 0.14 to 0.15
|
||||||
|
let options = "{\"enable\":true,\"analyzer\":\"English\",\"case-sensitive\":false,\"backend\":\"bloom\"}";
|
||||||
|
let options = serde_json::from_str::<FulltextOptions>(options).unwrap();
|
||||||
|
assert!(!options.case_sensitive);
|
||||||
|
assert!(options.enable);
|
||||||
|
assert_eq!(FulltextBackend::Bloom, options.backend);
|
||||||
|
assert_eq!(FulltextAnalyzer::default(), options.analyzer);
|
||||||
|
assert_eq!(DEFAULT_GRANULARITY, options.granularity);
|
||||||
|
assert_eq!(DEFAULT_FALSE_POSITIVE_RATE, options.false_positive_rate());
|
||||||
|
|
||||||
|
let options_str = serde_json::to_string(&options).unwrap();
|
||||||
|
assert_eq!(options_str, "{\"enable\":true,\"analyzer\":\"English\",\"case-sensitive\":false,\"backend\":\"bloom\",\"granularity\":10240,\"false-positive-rate-in-10000\":100}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,11 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
use arrow_array::{
|
||||||
|
ArrayRef, PrimitiveArray, TimestampMicrosecondArray, TimestampMillisecondArray,
|
||||||
|
TimestampNanosecondArray, TimestampSecondArray,
|
||||||
|
};
|
||||||
|
use arrow_schema::DataType;
|
||||||
use common_time::timestamp::TimeUnit;
|
use common_time::timestamp::TimeUnit;
|
||||||
use common_time::Timestamp;
|
use common_time::Timestamp;
|
||||||
use paste::paste;
|
use paste::paste;
|
||||||
@@ -138,6 +143,41 @@ define_timestamp_with_unit!(Millisecond);
|
|||||||
define_timestamp_with_unit!(Microsecond);
|
define_timestamp_with_unit!(Microsecond);
|
||||||
define_timestamp_with_unit!(Nanosecond);
|
define_timestamp_with_unit!(Nanosecond);
|
||||||
|
|
||||||
|
pub fn timestamp_array_to_primitive(
|
||||||
|
ts_array: &ArrayRef,
|
||||||
|
) -> Option<(
|
||||||
|
PrimitiveArray<arrow_array::types::Int64Type>,
|
||||||
|
arrow::datatypes::TimeUnit,
|
||||||
|
)> {
|
||||||
|
let DataType::Timestamp(unit, _) = ts_array.data_type() else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
let ts_primitive = match unit {
|
||||||
|
arrow_schema::TimeUnit::Second => ts_array
|
||||||
|
.as_any()
|
||||||
|
.downcast_ref::<TimestampSecondArray>()
|
||||||
|
.unwrap()
|
||||||
|
.reinterpret_cast::<arrow_array::types::Int64Type>(),
|
||||||
|
arrow_schema::TimeUnit::Millisecond => ts_array
|
||||||
|
.as_any()
|
||||||
|
.downcast_ref::<TimestampMillisecondArray>()
|
||||||
|
.unwrap()
|
||||||
|
.reinterpret_cast::<arrow_array::types::Int64Type>(),
|
||||||
|
arrow_schema::TimeUnit::Microsecond => ts_array
|
||||||
|
.as_any()
|
||||||
|
.downcast_ref::<TimestampMicrosecondArray>()
|
||||||
|
.unwrap()
|
||||||
|
.reinterpret_cast::<arrow_array::types::Int64Type>(),
|
||||||
|
arrow_schema::TimeUnit::Nanosecond => ts_array
|
||||||
|
.as_any()
|
||||||
|
.downcast_ref::<TimestampNanosecondArray>()
|
||||||
|
.unwrap()
|
||||||
|
.reinterpret_cast::<arrow_array::types::Int64Type>(),
|
||||||
|
};
|
||||||
|
Some((ts_primitive, *unit))
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use common_time::timezone::set_default_timezone;
|
use common_time::timezone::set_default_timezone;
|
||||||
|
|||||||
@@ -316,7 +316,7 @@ impl StreamingEngine {
|
|||||||
);
|
);
|
||||||
|
|
||||||
METRIC_FLOW_ROWS
|
METRIC_FLOW_ROWS
|
||||||
.with_label_values(&["out"])
|
.with_label_values(&["out-streaming"])
|
||||||
.inc_by(total_rows as u64);
|
.inc_by(total_rows as u64);
|
||||||
|
|
||||||
let now = self.tick_manager.tick();
|
let now = self.tick_manager.tick();
|
||||||
@@ -899,7 +899,7 @@ impl StreamingEngine {
|
|||||||
let rows_send = self.run_available(true).await?;
|
let rows_send = self.run_available(true).await?;
|
||||||
let row = self.send_writeback_requests().await?;
|
let row = self.send_writeback_requests().await?;
|
||||||
debug!(
|
debug!(
|
||||||
"Done to flush flow_id={:?} with {} input rows flushed, {} rows sended and {} output rows flushed",
|
"Done to flush flow_id={:?} with {} input rows flushed, {} rows sent and {} output rows flushed",
|
||||||
flow_id, flushed_input_rows, rows_send, row
|
flow_id, flushed_input_rows, rows_send, row
|
||||||
);
|
);
|
||||||
Ok(row)
|
Ok(row)
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user