mirror of
https://github.com/GreptimeTeam/greptimedb.git
synced 2025-12-24 07:00:00 +00:00
Compare commits
61 Commits
poc/create
...
v0.11.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
17d75c767c | ||
|
|
a1ed450c0c | ||
|
|
ea4ce9d1e3 | ||
|
|
1f7d9666b7 | ||
|
|
9f1a0d78b2 | ||
|
|
ed8e418716 | ||
|
|
9e7121c1bb | ||
|
|
94a49ed4f0 | ||
|
|
f5e743379f | ||
|
|
6735e5867e | ||
|
|
925525726b | ||
|
|
6427682a9a | ||
|
|
55b0022676 | ||
|
|
2d84cc8d87 | ||
|
|
c030705b17 | ||
|
|
443c600bd0 | ||
|
|
39cadfe10b | ||
|
|
9b5e4e80f7 | ||
|
|
041a276b66 | ||
|
|
614a25ddc5 | ||
|
|
4337e20010 | ||
|
|
65c52cc698 | ||
|
|
50f31fd681 | ||
|
|
b5af5aaf8d | ||
|
|
27693c7f1e | ||
|
|
a59fef9ffb | ||
|
|
bcecd8ce52 | ||
|
|
ffdcb8c1ac | ||
|
|
554121ad79 | ||
|
|
43c12b4f2c | ||
|
|
7aa8c28fe4 | ||
|
|
34fbe7739e | ||
|
|
06d7bd99dd | ||
|
|
b71d842615 | ||
|
|
7f71693b8e | ||
|
|
615ea1a171 | ||
|
|
4e725d259d | ||
|
|
dc2252eb6d | ||
|
|
6d4cc2e070 | ||
|
|
6066ce2c4a | ||
|
|
b90d8f7dbd | ||
|
|
fdccf4ff84 | ||
|
|
8b1484c064 | ||
|
|
576e20ac78 | ||
|
|
10b3e3da0f | ||
|
|
4a3ef2d718 | ||
|
|
65eabb2a05 | ||
|
|
bc5a57f51f | ||
|
|
f24b9d8814 | ||
|
|
dd4d0a88ce | ||
|
|
3d2096fe9d | ||
|
|
35715bb710 | ||
|
|
08a3befa67 | ||
|
|
ca1758d4e7 | ||
|
|
42bf818167 | ||
|
|
2c9b117224 | ||
|
|
3edf2317e1 | ||
|
|
85d72a3cd0 | ||
|
|
928172bd82 | ||
|
|
e9f5bddeff | ||
|
|
486755d795 |
@@ -18,6 +18,8 @@ runs:
|
|||||||
--set controller.replicaCount=${{ inputs.controller-replicas }} \
|
--set controller.replicaCount=${{ inputs.controller-replicas }} \
|
||||||
--set controller.resources.requests.cpu=50m \
|
--set controller.resources.requests.cpu=50m \
|
||||||
--set controller.resources.requests.memory=128Mi \
|
--set controller.resources.requests.memory=128Mi \
|
||||||
|
--set controller.resources.limits.cpu=2000m \
|
||||||
|
--set controller.resources.limits.memory=2Gi \
|
||||||
--set listeners.controller.protocol=PLAINTEXT \
|
--set listeners.controller.protocol=PLAINTEXT \
|
||||||
--set listeners.client.protocol=PLAINTEXT \
|
--set listeners.client.protocol=PLAINTEXT \
|
||||||
--create-namespace \
|
--create-namespace \
|
||||||
|
|||||||
1
.github/cargo-blacklist.txt
vendored
1
.github/cargo-blacklist.txt
vendored
@@ -1,2 +1,3 @@
|
|||||||
native-tls
|
native-tls
|
||||||
openssl
|
openssl
|
||||||
|
aws-lc-sys
|
||||||
|
|||||||
10
.github/pull_request_template.md
vendored
10
.github/pull_request_template.md
vendored
@@ -4,7 +4,8 @@ I hereby agree to the terms of the [GreptimeDB CLA](https://github.com/GreptimeT
|
|||||||
|
|
||||||
## What's changed and what's your intention?
|
## What's changed and what's your intention?
|
||||||
|
|
||||||
__!!! DO NOT LEAVE THIS BLOCK EMPTY !!!__
|
<!--
|
||||||
|
__!!! DO NOT LEAVE THIS BLOCK EMPTY !!!__
|
||||||
|
|
||||||
Please explain IN DETAIL what the changes are in this PR and why they are needed:
|
Please explain IN DETAIL what the changes are in this PR and why they are needed:
|
||||||
|
|
||||||
@@ -12,9 +13,14 @@ Please explain IN DETAIL what the changes are in this PR and why they are needed
|
|||||||
- How does this PR work? Need a brief introduction for the changed logic (optional)
|
- How does this PR work? Need a brief introduction for the changed logic (optional)
|
||||||
- Describe clearly one logical change and avoid lazy messages (optional)
|
- Describe clearly one logical change and avoid lazy messages (optional)
|
||||||
- Describe any limitations of the current code (optional)
|
- Describe any limitations of the current code (optional)
|
||||||
|
- Describe if this PR will break **API or data compatibility** (optional)
|
||||||
|
-->
|
||||||
|
|
||||||
## Checklist
|
## PR Checklist
|
||||||
|
Please convert it to a draft if some of the following conditions are not met.
|
||||||
|
|
||||||
- [ ] I have written the necessary rustdoc comments.
|
- [ ] I have written the necessary rustdoc comments.
|
||||||
- [ ] I have added the necessary unit tests and integration tests.
|
- [ ] I have added the necessary unit tests and integration tests.
|
||||||
- [ ] This PR requires documentation updates.
|
- [ ] This PR requires documentation updates.
|
||||||
|
- [ ] API changes are backward compatible.
|
||||||
|
- [ ] Schema or data changes are backward compatible.
|
||||||
|
|||||||
2
.github/workflows/dev-build.yml
vendored
2
.github/workflows/dev-build.yml
vendored
@@ -29,7 +29,7 @@ on:
|
|||||||
linux_arm64_runner:
|
linux_arm64_runner:
|
||||||
type: choice
|
type: choice
|
||||||
description: The runner uses to build linux-arm64 artifacts
|
description: The runner uses to build linux-arm64 artifacts
|
||||||
default: ec2-c6g.4xlarge-arm64
|
default: ec2-c6g.8xlarge-arm64
|
||||||
options:
|
options:
|
||||||
- ec2-c6g.xlarge-arm64 # 4C8G
|
- ec2-c6g.xlarge-arm64 # 4C8G
|
||||||
- ec2-c6g.2xlarge-arm64 # 8C16G
|
- ec2-c6g.2xlarge-arm64 # 8C16G
|
||||||
|
|||||||
11
.github/workflows/develop.yml
vendored
11
.github/workflows/develop.yml
vendored
@@ -269,13 +269,6 @@ jobs:
|
|||||||
- name: Install cargo-gc-bin
|
- name: Install cargo-gc-bin
|
||||||
shell: bash
|
shell: bash
|
||||||
run: cargo install cargo-gc-bin
|
run: cargo install cargo-gc-bin
|
||||||
- name: Check aws-lc-sys will not build
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
if cargo tree -i aws-lc-sys -e features | grep -q aws-lc-sys; then
|
|
||||||
echo "Found aws-lc-sys, which has compilation problems on older gcc versions. Please replace it with ring until its building experience improves."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
- name: Build greptime bianry
|
- name: Build greptime bianry
|
||||||
shell: bash
|
shell: bash
|
||||||
# `cargo gc` will invoke `cargo build` with specified args
|
# `cargo gc` will invoke `cargo build` with specified args
|
||||||
@@ -330,8 +323,6 @@ jobs:
|
|||||||
uses: ./.github/actions/setup-kafka-cluster
|
uses: ./.github/actions/setup-kafka-cluster
|
||||||
- name: Setup Etcd cluser
|
- name: Setup Etcd cluser
|
||||||
uses: ./.github/actions/setup-etcd-cluster
|
uses: ./.github/actions/setup-etcd-cluster
|
||||||
- name: Setup Postgres cluser
|
|
||||||
uses: ./.github/actions/setup-postgres-cluster
|
|
||||||
# Prepares for fuzz tests
|
# Prepares for fuzz tests
|
||||||
- uses: arduino/setup-protoc@v3
|
- uses: arduino/setup-protoc@v3
|
||||||
with:
|
with:
|
||||||
@@ -481,8 +472,6 @@ jobs:
|
|||||||
uses: ./.github/actions/setup-kafka-cluster
|
uses: ./.github/actions/setup-kafka-cluster
|
||||||
- name: Setup Etcd cluser
|
- name: Setup Etcd cluser
|
||||||
uses: ./.github/actions/setup-etcd-cluster
|
uses: ./.github/actions/setup-etcd-cluster
|
||||||
- name: Setup Postgres cluser
|
|
||||||
uses: ./.github/actions/setup-postgres-cluster
|
|
||||||
# Prepares for fuzz tests
|
# Prepares for fuzz tests
|
||||||
- uses: arduino/setup-protoc@v3
|
- uses: arduino/setup-protoc@v3
|
||||||
with:
|
with:
|
||||||
|
|||||||
4
.github/workflows/nightly-build.yml
vendored
4
.github/workflows/nightly-build.yml
vendored
@@ -12,7 +12,7 @@ on:
|
|||||||
linux_amd64_runner:
|
linux_amd64_runner:
|
||||||
type: choice
|
type: choice
|
||||||
description: The runner uses to build linux-amd64 artifacts
|
description: The runner uses to build linux-amd64 artifacts
|
||||||
default: ec2-c6i.2xlarge-amd64
|
default: ec2-c6i.4xlarge-amd64
|
||||||
options:
|
options:
|
||||||
- ubuntu-20.04
|
- ubuntu-20.04
|
||||||
- ubuntu-20.04-8-cores
|
- ubuntu-20.04-8-cores
|
||||||
@@ -27,7 +27,7 @@ on:
|
|||||||
linux_arm64_runner:
|
linux_arm64_runner:
|
||||||
type: choice
|
type: choice
|
||||||
description: The runner uses to build linux-arm64 artifacts
|
description: The runner uses to build linux-arm64 artifacts
|
||||||
default: ec2-c6g.2xlarge-arm64
|
default: ec2-c6g.8xlarge-arm64
|
||||||
options:
|
options:
|
||||||
- ec2-c6g.xlarge-arm64 # 4C8G
|
- ec2-c6g.xlarge-arm64 # 4C8G
|
||||||
- ec2-c6g.2xlarge-arm64 # 8C16G
|
- ec2-c6g.2xlarge-arm64 # 8C16G
|
||||||
|
|||||||
11
.github/workflows/nightly-ci.yml
vendored
11
.github/workflows/nightly-ci.yml
vendored
@@ -114,6 +114,17 @@ jobs:
|
|||||||
GT_S3_REGION: ${{ vars.AWS_CI_TEST_BUCKET_REGION }}
|
GT_S3_REGION: ${{ vars.AWS_CI_TEST_BUCKET_REGION }}
|
||||||
UNITTEST_LOG_DIR: "__unittest_logs"
|
UNITTEST_LOG_DIR: "__unittest_logs"
|
||||||
|
|
||||||
|
cleanbuild-linux-nix:
|
||||||
|
runs-on: ubuntu-latest-8-cores
|
||||||
|
timeout-minutes: 60
|
||||||
|
needs: [coverage, fmt, clippy, check]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: cachix/install-nix-action@v27
|
||||||
|
with:
|
||||||
|
nix_path: nixpkgs=channel:nixos-unstable
|
||||||
|
- run: nix-shell --pure --run "cargo build"
|
||||||
|
|
||||||
check-status:
|
check-status:
|
||||||
name: Check status
|
name: Check status
|
||||||
needs: [sqlness-test, sqlness-windows, test-on-windows]
|
needs: [sqlness-test, sqlness-windows, test-on-windows]
|
||||||
|
|||||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -31,7 +31,7 @@ on:
|
|||||||
linux_arm64_runner:
|
linux_arm64_runner:
|
||||||
type: choice
|
type: choice
|
||||||
description: The runner uses to build linux-arm64 artifacts
|
description: The runner uses to build linux-arm64 artifacts
|
||||||
default: ec2-c6g.4xlarge-arm64
|
default: ec2-c6g.8xlarge-arm64
|
||||||
options:
|
options:
|
||||||
- ubuntu-2204-32-cores-arm
|
- ubuntu-2204-32-cores-arm
|
||||||
- ec2-c6g.xlarge-arm64 # 4C8G
|
- ec2-c6g.xlarge-arm64 # 4C8G
|
||||||
|
|||||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -47,6 +47,10 @@ benchmarks/data
|
|||||||
|
|
||||||
venv/
|
venv/
|
||||||
|
|
||||||
# Fuzz tests
|
# Fuzz tests
|
||||||
tests-fuzz/artifacts/
|
tests-fuzz/artifacts/
|
||||||
tests-fuzz/corpus/
|
tests-fuzz/corpus/
|
||||||
|
|
||||||
|
# Nix
|
||||||
|
.direnv
|
||||||
|
.envrc
|
||||||
|
|||||||
578
Cargo.lock
generated
578
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -68,7 +68,7 @@ members = [
|
|||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.11.0"
|
version = "0.11.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
|
|
||||||
@@ -180,6 +180,7 @@ sysinfo = "0.30"
|
|||||||
# on branch v0.44.x
|
# on branch v0.44.x
|
||||||
sqlparser = { git = "https://github.com/GreptimeTeam/sqlparser-rs.git", rev = "54a267ac89c09b11c0c88934690530807185d3e7", features = [
|
sqlparser = { git = "https://github.com/GreptimeTeam/sqlparser-rs.git", rev = "54a267ac89c09b11c0c88934690530807185d3e7", features = [
|
||||||
"visitor",
|
"visitor",
|
||||||
|
"serde",
|
||||||
] }
|
] }
|
||||||
strum = { version = "0.25", features = ["derive"] }
|
strum = { version = "0.25", features = ["derive"] }
|
||||||
tempfile = "3"
|
tempfile = "3"
|
||||||
|
|||||||
@@ -13,11 +13,11 @@
|
|||||||
| Key | Type | Default | Descriptions |
|
| Key | Type | Default | Descriptions |
|
||||||
| --- | -----| ------- | ----------- |
|
| --- | -----| ------- | ----------- |
|
||||||
| `mode` | String | `standalone` | The running mode of the datanode. It can be `standalone` or `distributed`. |
|
| `mode` | String | `standalone` | The running mode of the datanode. It can be `standalone` or `distributed`. |
|
||||||
| `enable_telemetry` | Bool | `true` | Enable telemetry to collect anonymous usage data. |
|
|
||||||
| `default_timezone` | String | Unset | The default timezone of the server. |
|
| `default_timezone` | String | Unset | The default timezone of the server. |
|
||||||
| `init_regions_in_background` | Bool | `false` | Initialize all regions in the background during the startup.<br/>By default, it provides services after all regions have been initialized. |
|
| `init_regions_in_background` | Bool | `false` | Initialize all regions in the background during the startup.<br/>By default, it provides services after all regions have been initialized. |
|
||||||
| `init_regions_parallelism` | Integer | `16` | Parallelism of initializing regions. |
|
| `init_regions_parallelism` | Integer | `16` | Parallelism of initializing regions. |
|
||||||
| `max_concurrent_queries` | Integer | `0` | The maximum current queries allowed to be executed. Zero means unlimited. |
|
| `max_concurrent_queries` | Integer | `0` | The maximum current queries allowed to be executed. Zero means unlimited. |
|
||||||
|
| `enable_telemetry` | Bool | `true` | Enable telemetry to collect anonymous usage data. Enabled by default. |
|
||||||
| `runtime` | -- | -- | The runtime options. |
|
| `runtime` | -- | -- | The runtime options. |
|
||||||
| `runtime.global_rt_size` | Integer | `8` | The number of threads to execute the runtime for global read operations. |
|
| `runtime.global_rt_size` | Integer | `8` | The number of threads to execute the runtime for global read operations. |
|
||||||
| `runtime.compact_rt_size` | Integer | `4` | The number of threads to execute the runtime for global write operations. |
|
| `runtime.compact_rt_size` | Integer | `4` | The number of threads to execute the runtime for global write operations. |
|
||||||
@@ -61,9 +61,9 @@
|
|||||||
| `wal` | -- | -- | The WAL options. |
|
| `wal` | -- | -- | The WAL options. |
|
||||||
| `wal.provider` | String | `raft_engine` | The provider of the WAL.<br/>- `raft_engine`: the wal is stored in the local file system by raft-engine.<br/>- `kafka`: it's remote wal that data is stored in Kafka. |
|
| `wal.provider` | String | `raft_engine` | The provider of the WAL.<br/>- `raft_engine`: the wal is stored in the local file system by raft-engine.<br/>- `kafka`: it's remote wal that data is stored in Kafka. |
|
||||||
| `wal.dir` | String | Unset | The directory to store the WAL files.<br/>**It's only used when the provider is `raft_engine`**. |
|
| `wal.dir` | String | Unset | The directory to store the WAL files.<br/>**It's only used when the provider is `raft_engine`**. |
|
||||||
| `wal.file_size` | String | `256MB` | The size of the WAL segment file.<br/>**It's only used when the provider is `raft_engine`**. |
|
| `wal.file_size` | String | `128MB` | The size of the WAL segment file.<br/>**It's only used when the provider is `raft_engine`**. |
|
||||||
| `wal.purge_threshold` | String | `4GB` | The threshold of the WAL size to trigger a flush.<br/>**It's only used when the provider is `raft_engine`**. |
|
| `wal.purge_threshold` | String | `1GB` | The threshold of the WAL size to trigger a flush.<br/>**It's only used when the provider is `raft_engine`**. |
|
||||||
| `wal.purge_interval` | String | `10m` | The interval to trigger a flush.<br/>**It's only used when the provider is `raft_engine`**. |
|
| `wal.purge_interval` | String | `1m` | The interval to trigger a flush.<br/>**It's only used when the provider is `raft_engine`**. |
|
||||||
| `wal.read_batch_size` | Integer | `128` | The read batch size.<br/>**It's only used when the provider is `raft_engine`**. |
|
| `wal.read_batch_size` | Integer | `128` | The read batch size.<br/>**It's only used when the provider is `raft_engine`**. |
|
||||||
| `wal.sync_write` | Bool | `false` | Whether to use sync write.<br/>**It's only used when the provider is `raft_engine`**. |
|
| `wal.sync_write` | Bool | `false` | Whether to use sync write.<br/>**It's only used when the provider is `raft_engine`**. |
|
||||||
| `wal.enable_log_recycle` | Bool | `true` | Whether to reuse logically truncated log files.<br/>**It's only used when the provider is `raft_engine`**. |
|
| `wal.enable_log_recycle` | Bool | `true` | Whether to reuse logically truncated log files.<br/>**It's only used when the provider is `raft_engine`**. |
|
||||||
@@ -150,6 +150,7 @@
|
|||||||
| `region_engine.mito.inverted_index.intermediate_path` | String | `""` | Deprecated, use `region_engine.mito.index.aux_path` instead. |
|
| `region_engine.mito.inverted_index.intermediate_path` | String | `""` | Deprecated, use `region_engine.mito.index.aux_path` instead. |
|
||||||
| `region_engine.mito.inverted_index.metadata_cache_size` | String | `64MiB` | Cache size for inverted index metadata. |
|
| `region_engine.mito.inverted_index.metadata_cache_size` | String | `64MiB` | Cache size for inverted index metadata. |
|
||||||
| `region_engine.mito.inverted_index.content_cache_size` | String | `128MiB` | Cache size for inverted index content. |
|
| `region_engine.mito.inverted_index.content_cache_size` | String | `128MiB` | Cache size for inverted index content. |
|
||||||
|
| `region_engine.mito.inverted_index.content_cache_page_size` | String | `8MiB` | Page size for inverted index content cache. |
|
||||||
| `region_engine.mito.fulltext_index` | -- | -- | The options for full-text index in Mito engine. |
|
| `region_engine.mito.fulltext_index` | -- | -- | The options for full-text index in Mito engine. |
|
||||||
| `region_engine.mito.fulltext_index.create_on_flush` | String | `auto` | Whether to create the index on flush.<br/>- `auto`: automatically (default)<br/>- `disable`: never |
|
| `region_engine.mito.fulltext_index.create_on_flush` | String | `auto` | Whether to create the index on flush.<br/>- `auto`: automatically (default)<br/>- `disable`: never |
|
||||||
| `region_engine.mito.fulltext_index.create_on_compaction` | String | `auto` | Whether to create the index on compaction.<br/>- `auto`: automatically (default)<br/>- `disable`: never |
|
| `region_engine.mito.fulltext_index.create_on_compaction` | String | `auto` | Whether to create the index on compaction.<br/>- `auto`: automatically (default)<br/>- `disable`: never |
|
||||||
@@ -286,12 +287,12 @@
|
|||||||
| `bind_addr` | String | `127.0.0.1:3002` | The bind address of metasrv. |
|
| `bind_addr` | String | `127.0.0.1:3002` | The bind address of metasrv. |
|
||||||
| `server_addr` | String | `127.0.0.1:3002` | The communication server address for frontend and datanode to connect to metasrv, "127.0.0.1:3002" by default for localhost. |
|
| `server_addr` | String | `127.0.0.1:3002` | The communication server address for frontend and datanode to connect to metasrv, "127.0.0.1:3002" by default for localhost. |
|
||||||
| `store_addrs` | Array | -- | Store server address default to etcd store. |
|
| `store_addrs` | Array | -- | Store server address default to etcd store. |
|
||||||
|
| `store_key_prefix` | String | `""` | If it's not empty, the metasrv will store all data with this key prefix. |
|
||||||
|
| `backend` | String | `EtcdStore` | The datastore for meta server. |
|
||||||
| `selector` | String | `round_robin` | Datanode selector type.<br/>- `round_robin` (default value)<br/>- `lease_based`<br/>- `load_based`<br/>For details, please see "https://docs.greptime.com/developer-guide/metasrv/selector". |
|
| `selector` | String | `round_robin` | Datanode selector type.<br/>- `round_robin` (default value)<br/>- `lease_based`<br/>- `load_based`<br/>For details, please see "https://docs.greptime.com/developer-guide/metasrv/selector". |
|
||||||
| `use_memory_store` | Bool | `false` | Store data in memory. |
|
| `use_memory_store` | Bool | `false` | Store data in memory. |
|
||||||
| `enable_telemetry` | Bool | `true` | Whether to enable greptimedb telemetry. |
|
|
||||||
| `store_key_prefix` | String | `""` | If it's not empty, the metasrv will store all data with this key prefix. |
|
|
||||||
| `enable_region_failover` | Bool | `false` | Whether to enable region failover.<br/>This feature is only available on GreptimeDB running on cluster mode and<br/>- Using Remote WAL<br/>- Using shared storage (e.g., s3). |
|
| `enable_region_failover` | Bool | `false` | Whether to enable region failover.<br/>This feature is only available on GreptimeDB running on cluster mode and<br/>- Using Remote WAL<br/>- Using shared storage (e.g., s3). |
|
||||||
| `backend` | String | `EtcdStore` | The datastore for meta server. |
|
| `enable_telemetry` | Bool | `true` | Whether to enable greptimedb telemetry. Enabled by default. |
|
||||||
| `runtime` | -- | -- | The runtime options. |
|
| `runtime` | -- | -- | The runtime options. |
|
||||||
| `runtime.global_rt_size` | Integer | `8` | The number of threads to execute the runtime for global read operations. |
|
| `runtime.global_rt_size` | Integer | `8` | The number of threads to execute the runtime for global read operations. |
|
||||||
| `runtime.compact_rt_size` | Integer | `4` | The number of threads to execute the runtime for global write operations. |
|
| `runtime.compact_rt_size` | Integer | `4` | The number of threads to execute the runtime for global write operations. |
|
||||||
@@ -356,7 +357,6 @@
|
|||||||
| `node_id` | Integer | Unset | The datanode identifier and should be unique in the cluster. |
|
| `node_id` | Integer | Unset | The datanode identifier and should be unique in the cluster. |
|
||||||
| `require_lease_before_startup` | Bool | `false` | Start services after regions have obtained leases.<br/>It will block the datanode start if it can't receive leases in the heartbeat from metasrv. |
|
| `require_lease_before_startup` | Bool | `false` | Start services after regions have obtained leases.<br/>It will block the datanode start if it can't receive leases in the heartbeat from metasrv. |
|
||||||
| `init_regions_in_background` | Bool | `false` | Initialize all regions in the background during the startup.<br/>By default, it provides services after all regions have been initialized. |
|
| `init_regions_in_background` | Bool | `false` | Initialize all regions in the background during the startup.<br/>By default, it provides services after all regions have been initialized. |
|
||||||
| `enable_telemetry` | Bool | `true` | Enable telemetry to collect anonymous usage data. |
|
|
||||||
| `init_regions_parallelism` | Integer | `16` | Parallelism of initializing regions. |
|
| `init_regions_parallelism` | Integer | `16` | Parallelism of initializing regions. |
|
||||||
| `max_concurrent_queries` | Integer | `0` | The maximum current queries allowed to be executed. Zero means unlimited. |
|
| `max_concurrent_queries` | Integer | `0` | The maximum current queries allowed to be executed. Zero means unlimited. |
|
||||||
| `rpc_addr` | String | Unset | Deprecated, use `grpc.addr` instead. |
|
| `rpc_addr` | String | Unset | Deprecated, use `grpc.addr` instead. |
|
||||||
@@ -364,6 +364,7 @@
|
|||||||
| `rpc_runtime_size` | Integer | Unset | Deprecated, use `grpc.runtime_size` instead. |
|
| `rpc_runtime_size` | Integer | Unset | Deprecated, use `grpc.runtime_size` instead. |
|
||||||
| `rpc_max_recv_message_size` | String | Unset | Deprecated, use `grpc.rpc_max_recv_message_size` instead. |
|
| `rpc_max_recv_message_size` | String | Unset | Deprecated, use `grpc.rpc_max_recv_message_size` instead. |
|
||||||
| `rpc_max_send_message_size` | String | Unset | Deprecated, use `grpc.rpc_max_send_message_size` instead. |
|
| `rpc_max_send_message_size` | String | Unset | Deprecated, use `grpc.rpc_max_send_message_size` instead. |
|
||||||
|
| `enable_telemetry` | Bool | `true` | Enable telemetry to collect anonymous usage data. Enabled by default. |
|
||||||
| `http` | -- | -- | The HTTP server options. |
|
| `http` | -- | -- | The HTTP server options. |
|
||||||
| `http.addr` | String | `127.0.0.1:4000` | The address to bind the HTTP server. |
|
| `http.addr` | String | `127.0.0.1:4000` | The address to bind the HTTP server. |
|
||||||
| `http.timeout` | String | `30s` | HTTP request timeout. Set to 0 to disable timeout. |
|
| `http.timeout` | String | `30s` | HTTP request timeout. Set to 0 to disable timeout. |
|
||||||
@@ -398,9 +399,9 @@
|
|||||||
| `wal` | -- | -- | The WAL options. |
|
| `wal` | -- | -- | The WAL options. |
|
||||||
| `wal.provider` | String | `raft_engine` | The provider of the WAL.<br/>- `raft_engine`: the wal is stored in the local file system by raft-engine.<br/>- `kafka`: it's remote wal that data is stored in Kafka. |
|
| `wal.provider` | String | `raft_engine` | The provider of the WAL.<br/>- `raft_engine`: the wal is stored in the local file system by raft-engine.<br/>- `kafka`: it's remote wal that data is stored in Kafka. |
|
||||||
| `wal.dir` | String | Unset | The directory to store the WAL files.<br/>**It's only used when the provider is `raft_engine`**. |
|
| `wal.dir` | String | Unset | The directory to store the WAL files.<br/>**It's only used when the provider is `raft_engine`**. |
|
||||||
| `wal.file_size` | String | `256MB` | The size of the WAL segment file.<br/>**It's only used when the provider is `raft_engine`**. |
|
| `wal.file_size` | String | `128MB` | The size of the WAL segment file.<br/>**It's only used when the provider is `raft_engine`**. |
|
||||||
| `wal.purge_threshold` | String | `4GB` | The threshold of the WAL size to trigger a flush.<br/>**It's only used when the provider is `raft_engine`**. |
|
| `wal.purge_threshold` | String | `1GB` | The threshold of the WAL size to trigger a flush.<br/>**It's only used when the provider is `raft_engine`**. |
|
||||||
| `wal.purge_interval` | String | `10m` | The interval to trigger a flush.<br/>**It's only used when the provider is `raft_engine`**. |
|
| `wal.purge_interval` | String | `1m` | The interval to trigger a flush.<br/>**It's only used when the provider is `raft_engine`**. |
|
||||||
| `wal.read_batch_size` | Integer | `128` | The read batch size.<br/>**It's only used when the provider is `raft_engine`**. |
|
| `wal.read_batch_size` | Integer | `128` | The read batch size.<br/>**It's only used when the provider is `raft_engine`**. |
|
||||||
| `wal.sync_write` | Bool | `false` | Whether to use sync write.<br/>**It's only used when the provider is `raft_engine`**. |
|
| `wal.sync_write` | Bool | `false` | Whether to use sync write.<br/>**It's only used when the provider is `raft_engine`**. |
|
||||||
| `wal.enable_log_recycle` | Bool | `true` | Whether to reuse logically truncated log files.<br/>**It's only used when the provider is `raft_engine`**. |
|
| `wal.enable_log_recycle` | Bool | `true` | Whether to reuse logically truncated log files.<br/>**It's only used when the provider is `raft_engine`**. |
|
||||||
@@ -475,6 +476,9 @@
|
|||||||
| `region_engine.mito.inverted_index.apply_on_query` | String | `auto` | Whether to apply the index on query<br/>- `auto`: automatically (default)<br/>- `disable`: never |
|
| `region_engine.mito.inverted_index.apply_on_query` | String | `auto` | Whether to apply the index on query<br/>- `auto`: automatically (default)<br/>- `disable`: never |
|
||||||
| `region_engine.mito.inverted_index.mem_threshold_on_create` | String | `auto` | Memory threshold for performing an external sort during index creation.<br/>- `auto`: automatically determine the threshold based on the system memory size (default)<br/>- `unlimited`: no memory limit<br/>- `[size]` e.g. `64MB`: fixed memory threshold |
|
| `region_engine.mito.inverted_index.mem_threshold_on_create` | String | `auto` | Memory threshold for performing an external sort during index creation.<br/>- `auto`: automatically determine the threshold based on the system memory size (default)<br/>- `unlimited`: no memory limit<br/>- `[size]` e.g. `64MB`: fixed memory threshold |
|
||||||
| `region_engine.mito.inverted_index.intermediate_path` | String | `""` | Deprecated, use `region_engine.mito.index.aux_path` instead. |
|
| `region_engine.mito.inverted_index.intermediate_path` | String | `""` | Deprecated, use `region_engine.mito.index.aux_path` instead. |
|
||||||
|
| `region_engine.mito.inverted_index.metadata_cache_size` | String | `64MiB` | Cache size for inverted index metadata. |
|
||||||
|
| `region_engine.mito.inverted_index.content_cache_size` | String | `128MiB` | Cache size for inverted index content. |
|
||||||
|
| `region_engine.mito.inverted_index.content_cache_page_size` | String | `8MiB` | Page size for inverted index content cache. |
|
||||||
| `region_engine.mito.fulltext_index` | -- | -- | The options for full-text index in Mito engine. |
|
| `region_engine.mito.fulltext_index` | -- | -- | The options for full-text index in Mito engine. |
|
||||||
| `region_engine.mito.fulltext_index.create_on_flush` | String | `auto` | Whether to create the index on flush.<br/>- `auto`: automatically (default)<br/>- `disable`: never |
|
| `region_engine.mito.fulltext_index.create_on_flush` | String | `auto` | Whether to create the index on flush.<br/>- `auto`: automatically (default)<br/>- `disable`: never |
|
||||||
| `region_engine.mito.fulltext_index.create_on_compaction` | String | `auto` | Whether to create the index on compaction.<br/>- `auto`: automatically (default)<br/>- `disable`: never |
|
| `region_engine.mito.fulltext_index.create_on_compaction` | String | `auto` | Whether to create the index on compaction.<br/>- `auto`: automatically (default)<br/>- `disable`: never |
|
||||||
|
|||||||
@@ -13,9 +13,6 @@ require_lease_before_startup = false
|
|||||||
## By default, it provides services after all regions have been initialized.
|
## By default, it provides services after all regions have been initialized.
|
||||||
init_regions_in_background = false
|
init_regions_in_background = false
|
||||||
|
|
||||||
## Enable telemetry to collect anonymous usage data.
|
|
||||||
enable_telemetry = true
|
|
||||||
|
|
||||||
## Parallelism of initializing regions.
|
## Parallelism of initializing regions.
|
||||||
init_regions_parallelism = 16
|
init_regions_parallelism = 16
|
||||||
|
|
||||||
@@ -42,6 +39,8 @@ rpc_max_recv_message_size = "512MB"
|
|||||||
## @toml2docs:none-default
|
## @toml2docs:none-default
|
||||||
rpc_max_send_message_size = "512MB"
|
rpc_max_send_message_size = "512MB"
|
||||||
|
|
||||||
|
## Enable telemetry to collect anonymous usage data. Enabled by default.
|
||||||
|
#+ enable_telemetry = true
|
||||||
|
|
||||||
## The HTTP server options.
|
## The HTTP server options.
|
||||||
[http]
|
[http]
|
||||||
@@ -143,15 +142,15 @@ dir = "/tmp/greptimedb/wal"
|
|||||||
|
|
||||||
## The size of the WAL segment file.
|
## The size of the WAL segment file.
|
||||||
## **It's only used when the provider is `raft_engine`**.
|
## **It's only used when the provider is `raft_engine`**.
|
||||||
file_size = "256MB"
|
file_size = "128MB"
|
||||||
|
|
||||||
## The threshold of the WAL size to trigger a flush.
|
## The threshold of the WAL size to trigger a flush.
|
||||||
## **It's only used when the provider is `raft_engine`**.
|
## **It's only used when the provider is `raft_engine`**.
|
||||||
purge_threshold = "4GB"
|
purge_threshold = "1GB"
|
||||||
|
|
||||||
## The interval to trigger a flush.
|
## The interval to trigger a flush.
|
||||||
## **It's only used when the provider is `raft_engine`**.
|
## **It's only used when the provider is `raft_engine`**.
|
||||||
purge_interval = "10m"
|
purge_interval = "1m"
|
||||||
|
|
||||||
## The read batch size.
|
## The read batch size.
|
||||||
## **It's only used when the provider is `raft_engine`**.
|
## **It's only used when the provider is `raft_engine`**.
|
||||||
@@ -544,6 +543,15 @@ mem_threshold_on_create = "auto"
|
|||||||
## Deprecated, use `region_engine.mito.index.aux_path` instead.
|
## Deprecated, use `region_engine.mito.index.aux_path` instead.
|
||||||
intermediate_path = ""
|
intermediate_path = ""
|
||||||
|
|
||||||
|
## Cache size for inverted index metadata.
|
||||||
|
metadata_cache_size = "64MiB"
|
||||||
|
|
||||||
|
## Cache size for inverted index content.
|
||||||
|
content_cache_size = "128MiB"
|
||||||
|
|
||||||
|
## Page size for inverted index content cache.
|
||||||
|
content_cache_page_size = "8MiB"
|
||||||
|
|
||||||
## The options for full-text index in Mito engine.
|
## The options for full-text index in Mito engine.
|
||||||
[region_engine.mito.fulltext_index]
|
[region_engine.mito.fulltext_index]
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,12 @@ server_addr = "127.0.0.1:3002"
|
|||||||
## Store server address default to etcd store.
|
## Store server address default to etcd store.
|
||||||
store_addrs = ["127.0.0.1:2379"]
|
store_addrs = ["127.0.0.1:2379"]
|
||||||
|
|
||||||
|
## If it's not empty, the metasrv will store all data with this key prefix.
|
||||||
|
store_key_prefix = ""
|
||||||
|
|
||||||
|
## The datastore for meta server.
|
||||||
|
backend = "EtcdStore"
|
||||||
|
|
||||||
## Datanode selector type.
|
## Datanode selector type.
|
||||||
## - `round_robin` (default value)
|
## - `round_robin` (default value)
|
||||||
## - `lease_based`
|
## - `lease_based`
|
||||||
@@ -20,20 +26,14 @@ selector = "round_robin"
|
|||||||
## Store data in memory.
|
## Store data in memory.
|
||||||
use_memory_store = false
|
use_memory_store = false
|
||||||
|
|
||||||
## Whether to enable greptimedb telemetry.
|
|
||||||
enable_telemetry = true
|
|
||||||
|
|
||||||
## If it's not empty, the metasrv will store all data with this key prefix.
|
|
||||||
store_key_prefix = ""
|
|
||||||
|
|
||||||
## Whether to enable region failover.
|
## Whether to enable region failover.
|
||||||
## This feature is only available on GreptimeDB running on cluster mode and
|
## This feature is only available on GreptimeDB running on cluster mode and
|
||||||
## - Using Remote WAL
|
## - Using Remote WAL
|
||||||
## - Using shared storage (e.g., s3).
|
## - Using shared storage (e.g., s3).
|
||||||
enable_region_failover = false
|
enable_region_failover = false
|
||||||
|
|
||||||
## The datastore for meta server.
|
## Whether to enable greptimedb telemetry. Enabled by default.
|
||||||
backend = "EtcdStore"
|
#+ enable_telemetry = true
|
||||||
|
|
||||||
## The runtime options.
|
## The runtime options.
|
||||||
#+ [runtime]
|
#+ [runtime]
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
## The running mode of the datanode. It can be `standalone` or `distributed`.
|
## The running mode of the datanode. It can be `standalone` or `distributed`.
|
||||||
mode = "standalone"
|
mode = "standalone"
|
||||||
|
|
||||||
## Enable telemetry to collect anonymous usage data.
|
|
||||||
enable_telemetry = true
|
|
||||||
|
|
||||||
## The default timezone of the server.
|
## The default timezone of the server.
|
||||||
## @toml2docs:none-default
|
## @toml2docs:none-default
|
||||||
default_timezone = "UTC"
|
default_timezone = "UTC"
|
||||||
@@ -18,6 +15,9 @@ init_regions_parallelism = 16
|
|||||||
## The maximum current queries allowed to be executed. Zero means unlimited.
|
## The maximum current queries allowed to be executed. Zero means unlimited.
|
||||||
max_concurrent_queries = 0
|
max_concurrent_queries = 0
|
||||||
|
|
||||||
|
## Enable telemetry to collect anonymous usage data. Enabled by default.
|
||||||
|
#+ enable_telemetry = true
|
||||||
|
|
||||||
## The runtime options.
|
## The runtime options.
|
||||||
#+ [runtime]
|
#+ [runtime]
|
||||||
## The number of threads to execute the runtime for global read operations.
|
## The number of threads to execute the runtime for global read operations.
|
||||||
@@ -147,15 +147,15 @@ dir = "/tmp/greptimedb/wal"
|
|||||||
|
|
||||||
## The size of the WAL segment file.
|
## The size of the WAL segment file.
|
||||||
## **It's only used when the provider is `raft_engine`**.
|
## **It's only used when the provider is `raft_engine`**.
|
||||||
file_size = "256MB"
|
file_size = "128MB"
|
||||||
|
|
||||||
## The threshold of the WAL size to trigger a flush.
|
## The threshold of the WAL size to trigger a flush.
|
||||||
## **It's only used when the provider is `raft_engine`**.
|
## **It's only used when the provider is `raft_engine`**.
|
||||||
purge_threshold = "4GB"
|
purge_threshold = "1GB"
|
||||||
|
|
||||||
## The interval to trigger a flush.
|
## The interval to trigger a flush.
|
||||||
## **It's only used when the provider is `raft_engine`**.
|
## **It's only used when the provider is `raft_engine`**.
|
||||||
purge_interval = "10m"
|
purge_interval = "1m"
|
||||||
|
|
||||||
## The read batch size.
|
## The read batch size.
|
||||||
## **It's only used when the provider is `raft_engine`**.
|
## **It's only used when the provider is `raft_engine`**.
|
||||||
@@ -588,6 +588,9 @@ metadata_cache_size = "64MiB"
|
|||||||
## Cache size for inverted index content.
|
## Cache size for inverted index content.
|
||||||
content_cache_size = "128MiB"
|
content_cache_size = "128MiB"
|
||||||
|
|
||||||
|
## Page size for inverted index content cache.
|
||||||
|
content_cache_page_size = "8MiB"
|
||||||
|
|
||||||
## The options for full-text index in Mito engine.
|
## The options for full-text index in Mito engine.
|
||||||
[region_engine.mito.fulltext_index]
|
[region_engine.mito.fulltext_index]
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ RUN apt-get update && \
|
|||||||
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \
|
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \
|
||||||
libssl-dev \
|
libssl-dev \
|
||||||
tzdata \
|
tzdata \
|
||||||
protobuf-compiler \
|
|
||||||
curl \
|
curl \
|
||||||
|
unzip \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
git \
|
git \
|
||||||
build-essential \
|
build-essential \
|
||||||
@@ -24,6 +24,20 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \
|
|||||||
python3.10 \
|
python3.10 \
|
||||||
python3.10-dev
|
python3.10-dev
|
||||||
|
|
||||||
|
ARG TARGETPLATFORM
|
||||||
|
RUN echo "target platform: $TARGETPLATFORM"
|
||||||
|
|
||||||
|
# Install protobuf, because the one in the apt is too old (v3.12).
|
||||||
|
RUN if [ "$TARGETPLATFORM" = "linux/arm64" ]; then \
|
||||||
|
curl -OL https://github.com/protocolbuffers/protobuf/releases/download/v29.1/protoc-29.1-linux-aarch_64.zip && \
|
||||||
|
unzip protoc-29.1-linux-aarch_64.zip -d protoc3; \
|
||||||
|
elif [ "$TARGETPLATFORM" = "linux/amd64" ]; then \
|
||||||
|
curl -OL https://github.com/protocolbuffers/protobuf/releases/download/v29.1/protoc-29.1-linux-x86_64.zip && \
|
||||||
|
unzip protoc-29.1-linux-x86_64.zip -d protoc3; \
|
||||||
|
fi
|
||||||
|
RUN mv protoc3/bin/* /usr/local/bin/
|
||||||
|
RUN mv protoc3/include/* /usr/local/include/
|
||||||
|
|
||||||
# https://github.com/GreptimeTeam/greptimedb/actions/runs/10935485852/job/30357457188#step:3:7106
|
# https://github.com/GreptimeTeam/greptimedb/actions/runs/10935485852/job/30357457188#step:3:7106
|
||||||
# `aws-lc-sys` require gcc >= 10.3.0 to work, hence alias to use gcc-10
|
# `aws-lc-sys` require gcc >= 10.3.0 to work, hence alias to use gcc-10
|
||||||
RUN apt-get remove -y gcc-9 g++-9 cpp-9 && \
|
RUN apt-get remove -y gcc-9 g++-9 cpp-9 && \
|
||||||
@@ -49,7 +63,7 @@ RUN apt-get -y purge python3.8 && \
|
|||||||
# wildcard here. However, that requires the git's config files and the submodules all owned by the very same user.
|
# wildcard here. However, that requires the git's config files and the submodules all owned by the very same user.
|
||||||
# It's troublesome to do this since the dev build runs in Docker, which is under user "root"; while outside the Docker,
|
# It's troublesome to do this since the dev build runs in Docker, which is under user "root"; while outside the Docker,
|
||||||
# it can be a different user that have prepared the submodules.
|
# it can be a different user that have prepared the submodules.
|
||||||
RUN git config --global --add safe.directory *
|
RUN git config --global --add safe.directory '*'
|
||||||
|
|
||||||
# Install Python dependencies.
|
# Install Python dependencies.
|
||||||
COPY $DOCKER_BUILD_ROOT/docker/python/requirements.txt /etc/greptime/requirements.txt
|
COPY $DOCKER_BUILD_ROOT/docker/python/requirements.txt /etc/greptime/requirements.txt
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,2 +1,3 @@
|
|||||||
[toolchain]
|
[toolchain]
|
||||||
channel = "nightly-2024-10-19"
|
channel = "nightly-2024-10-19"
|
||||||
|
components = ["rust-analyzer"]
|
||||||
|
|||||||
@@ -58,8 +58,10 @@ def main():
|
|||||||
if not check_snafu_in_files(branch_name, other_rust_files)
|
if not check_snafu_in_files(branch_name, other_rust_files)
|
||||||
]
|
]
|
||||||
|
|
||||||
for name in unused_snafu:
|
if unused_snafu:
|
||||||
print(name)
|
print("Unused error variants:")
|
||||||
|
for name in unused_snafu:
|
||||||
|
print(name)
|
||||||
|
|
||||||
if unused_snafu:
|
if unused_snafu:
|
||||||
raise SystemExit(1)
|
raise SystemExit(1)
|
||||||
|
|||||||
27
shell.nix
Normal file
27
shell.nix
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
let
|
||||||
|
nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-unstable";
|
||||||
|
fenix = import (fetchTarball "https://github.com/nix-community/fenix/archive/main.tar.gz") {};
|
||||||
|
pkgs = import nixpkgs { config = {}; overlays = []; };
|
||||||
|
in
|
||||||
|
|
||||||
|
pkgs.mkShell rec {
|
||||||
|
nativeBuildInputs = with pkgs; [
|
||||||
|
pkg-config
|
||||||
|
git
|
||||||
|
clang
|
||||||
|
gcc
|
||||||
|
protobuf
|
||||||
|
mold
|
||||||
|
(fenix.fromToolchainFile {
|
||||||
|
dir = ./.;
|
||||||
|
})
|
||||||
|
cargo-nextest
|
||||||
|
taplo
|
||||||
|
];
|
||||||
|
|
||||||
|
buildInputs = with pkgs; [
|
||||||
|
libgit2
|
||||||
|
];
|
||||||
|
|
||||||
|
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath buildInputs;
|
||||||
|
}
|
||||||
@@ -16,7 +16,7 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
use datatypes::schema::{
|
use datatypes::schema::{
|
||||||
ColumnDefaultConstraint, ColumnSchema, FulltextAnalyzer, FulltextOptions, COMMENT_KEY,
|
ColumnDefaultConstraint, ColumnSchema, FulltextAnalyzer, FulltextOptions, COMMENT_KEY,
|
||||||
FULLTEXT_KEY, INVERTED_INDEX_KEY,
|
FULLTEXT_KEY, INVERTED_INDEX_KEY, SKIPPING_INDEX_KEY,
|
||||||
};
|
};
|
||||||
use greptime_proto::v1::Analyzer;
|
use greptime_proto::v1::Analyzer;
|
||||||
use snafu::ResultExt;
|
use snafu::ResultExt;
|
||||||
@@ -29,6 +29,8 @@ use crate::v1::{ColumnDef, ColumnOptions, SemanticType};
|
|||||||
const FULLTEXT_GRPC_KEY: &str = "fulltext";
|
const FULLTEXT_GRPC_KEY: &str = "fulltext";
|
||||||
/// Key used to store inverted index options in gRPC column options.
|
/// Key used to store inverted index options in gRPC column options.
|
||||||
const INVERTED_INDEX_GRPC_KEY: &str = "inverted_index";
|
const INVERTED_INDEX_GRPC_KEY: &str = "inverted_index";
|
||||||
|
/// Key used to store skip index options in gRPC column options.
|
||||||
|
const SKIPPING_INDEX_GRPC_KEY: &str = "skipping_index";
|
||||||
|
|
||||||
/// Tries to construct a `ColumnSchema` from the given `ColumnDef`.
|
/// Tries to construct a `ColumnSchema` from the given `ColumnDef`.
|
||||||
pub fn try_as_column_schema(column_def: &ColumnDef) -> Result<ColumnSchema> {
|
pub fn try_as_column_schema(column_def: &ColumnDef) -> Result<ColumnSchema> {
|
||||||
@@ -60,6 +62,9 @@ pub fn try_as_column_schema(column_def: &ColumnDef) -> Result<ColumnSchema> {
|
|||||||
if let Some(inverted_index) = options.options.get(INVERTED_INDEX_GRPC_KEY) {
|
if let Some(inverted_index) = options.options.get(INVERTED_INDEX_GRPC_KEY) {
|
||||||
metadata.insert(INVERTED_INDEX_KEY.to_string(), inverted_index.clone());
|
metadata.insert(INVERTED_INDEX_KEY.to_string(), inverted_index.clone());
|
||||||
}
|
}
|
||||||
|
if let Some(skipping_index) = options.options.get(SKIPPING_INDEX_GRPC_KEY) {
|
||||||
|
metadata.insert(SKIPPING_INDEX_KEY.to_string(), skipping_index.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnSchema::new(&column_def.name, data_type.into(), column_def.is_nullable)
|
ColumnSchema::new(&column_def.name, data_type.into(), column_def.is_nullable)
|
||||||
@@ -84,6 +89,11 @@ pub fn options_from_column_schema(column_schema: &ColumnSchema) -> Option<Column
|
|||||||
.options
|
.options
|
||||||
.insert(INVERTED_INDEX_GRPC_KEY.to_string(), inverted_index.clone());
|
.insert(INVERTED_INDEX_GRPC_KEY.to_string(), inverted_index.clone());
|
||||||
}
|
}
|
||||||
|
if let Some(skipping_index) = column_schema.metadata().get(SKIPPING_INDEX_KEY) {
|
||||||
|
options
|
||||||
|
.options
|
||||||
|
.insert(SKIPPING_INDEX_GRPC_KEY.to_string(), skipping_index.clone());
|
||||||
|
}
|
||||||
|
|
||||||
(!options.options.is_empty()).then_some(options)
|
(!options.options.is_empty()).then_some(options)
|
||||||
}
|
}
|
||||||
|
|||||||
1
src/cache/Cargo.toml
vendored
1
src/cache/Cargo.toml
vendored
@@ -11,4 +11,3 @@ common-macro.workspace = true
|
|||||||
common-meta.workspace = true
|
common-meta.workspace = true
|
||||||
moka.workspace = true
|
moka.workspace = true
|
||||||
snafu.workspace = true
|
snafu.workspace = true
|
||||||
substrait.workspace = true
|
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ async-stream.workspace = true
|
|||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
bytes.workspace = true
|
bytes.workspace = true
|
||||||
common-catalog.workspace = true
|
common-catalog.workspace = true
|
||||||
common-config.workspace = true
|
|
||||||
common-error.workspace = true
|
common-error.workspace = true
|
||||||
common-macro.workspace = true
|
common-macro.workspace = true
|
||||||
common-meta.workspace = true
|
common-meta.workspace = true
|
||||||
@@ -58,7 +57,5 @@ catalog = { workspace = true, features = ["testing"] }
|
|||||||
chrono.workspace = true
|
chrono.workspace = true
|
||||||
common-meta = { workspace = true, features = ["testing"] }
|
common-meta = { workspace = true, features = ["testing"] }
|
||||||
common-query = { workspace = true, features = ["testing"] }
|
common-query = { workspace = true, features = ["testing"] }
|
||||||
common-test-util.workspace = true
|
|
||||||
log-store.workspace = true
|
|
||||||
object-store.workspace = true
|
object-store.workspace = true
|
||||||
tokio.workspace = true
|
tokio.workspace = true
|
||||||
|
|||||||
@@ -64,6 +64,13 @@ pub enum Error {
|
|||||||
source: BoxedError,
|
source: BoxedError,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Failed to list flow stats"))]
|
||||||
|
ListFlowStats {
|
||||||
|
#[snafu(implicit)]
|
||||||
|
location: Location,
|
||||||
|
source: BoxedError,
|
||||||
|
},
|
||||||
|
|
||||||
#[snafu(display("Failed to list flows in catalog {catalog}"))]
|
#[snafu(display("Failed to list flows in catalog {catalog}"))]
|
||||||
ListFlows {
|
ListFlows {
|
||||||
#[snafu(implicit)]
|
#[snafu(implicit)]
|
||||||
@@ -326,6 +333,7 @@ impl ErrorExt for Error {
|
|||||||
| Error::ListSchemas { source, .. }
|
| Error::ListSchemas { source, .. }
|
||||||
| Error::ListTables { source, .. }
|
| Error::ListTables { source, .. }
|
||||||
| Error::ListFlows { source, .. }
|
| Error::ListFlows { source, .. }
|
||||||
|
| Error::ListFlowStats { source, .. }
|
||||||
| Error::ListProcedures { source, .. }
|
| Error::ListProcedures { source, .. }
|
||||||
| Error::ListRegionStats { source, .. }
|
| Error::ListRegionStats { source, .. }
|
||||||
| Error::ConvertProtoData { source, .. } => source.status_code(),
|
| Error::ConvertProtoData { source, .. } => source.status_code(),
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ use common_error::ext::BoxedError;
|
|||||||
use common_meta::cluster::{ClusterInfo, NodeInfo};
|
use common_meta::cluster::{ClusterInfo, NodeInfo};
|
||||||
use common_meta::datanode::RegionStat;
|
use common_meta::datanode::RegionStat;
|
||||||
use common_meta::ddl::{ExecutorContext, ProcedureExecutor};
|
use common_meta::ddl::{ExecutorContext, ProcedureExecutor};
|
||||||
|
use common_meta::key::flow::flow_state::FlowStat;
|
||||||
use common_meta::rpc::procedure;
|
use common_meta::rpc::procedure;
|
||||||
use common_procedure::{ProcedureInfo, ProcedureState};
|
use common_procedure::{ProcedureInfo, ProcedureState};
|
||||||
use meta_client::MetaClientRef;
|
use meta_client::MetaClientRef;
|
||||||
@@ -89,4 +90,12 @@ impl InformationExtension for DistributedInformationExtension {
|
|||||||
.map_err(BoxedError::new)
|
.map_err(BoxedError::new)
|
||||||
.context(error::ListRegionStatsSnafu)
|
.context(error::ListRegionStatsSnafu)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn flow_stats(&self) -> std::result::Result<Option<FlowStat>, Self::Error> {
|
||||||
|
self.meta_client
|
||||||
|
.list_flow_stats()
|
||||||
|
.await
|
||||||
|
.map_err(BoxedError::new)
|
||||||
|
.context(crate::error::ListFlowStatsSnafu)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ use common_catalog::consts::{self, DEFAULT_CATALOG_NAME, INFORMATION_SCHEMA_NAME
|
|||||||
use common_error::ext::ErrorExt;
|
use common_error::ext::ErrorExt;
|
||||||
use common_meta::cluster::NodeInfo;
|
use common_meta::cluster::NodeInfo;
|
||||||
use common_meta::datanode::RegionStat;
|
use common_meta::datanode::RegionStat;
|
||||||
|
use common_meta::key::flow::flow_state::FlowStat;
|
||||||
use common_meta::key::flow::FlowMetadataManager;
|
use common_meta::key::flow::FlowMetadataManager;
|
||||||
use common_procedure::ProcedureInfo;
|
use common_procedure::ProcedureInfo;
|
||||||
use common_recordbatch::SendableRecordBatchStream;
|
use common_recordbatch::SendableRecordBatchStream;
|
||||||
@@ -192,6 +193,7 @@ impl SystemSchemaProviderInner for InformationSchemaProvider {
|
|||||||
)) as _),
|
)) as _),
|
||||||
FLOWS => Some(Arc::new(InformationSchemaFlows::new(
|
FLOWS => Some(Arc::new(InformationSchemaFlows::new(
|
||||||
self.catalog_name.clone(),
|
self.catalog_name.clone(),
|
||||||
|
self.catalog_manager.clone(),
|
||||||
self.flow_metadata_manager.clone(),
|
self.flow_metadata_manager.clone(),
|
||||||
)) as _),
|
)) as _),
|
||||||
PROCEDURE_INFO => Some(
|
PROCEDURE_INFO => Some(
|
||||||
@@ -338,6 +340,9 @@ pub trait InformationExtension {
|
|||||||
|
|
||||||
/// Gets the region statistics.
|
/// Gets the region statistics.
|
||||||
async fn region_stats(&self) -> std::result::Result<Vec<RegionStat>, Self::Error>;
|
async fn region_stats(&self) -> std::result::Result<Vec<RegionStat>, Self::Error>;
|
||||||
|
|
||||||
|
/// Get the flow statistics. If no flownode is available, return `None`.
|
||||||
|
async fn flow_stats(&self) -> std::result::Result<Option<FlowStat>, Self::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct NoopInformationExtension;
|
pub struct NoopInformationExtension;
|
||||||
@@ -357,4 +362,8 @@ impl InformationExtension for NoopInformationExtension {
|
|||||||
async fn region_stats(&self) -> std::result::Result<Vec<RegionStat>, Self::Error> {
|
async fn region_stats(&self) -> std::result::Result<Vec<RegionStat>, Self::Error> {
|
||||||
Ok(vec![])
|
Ok(vec![])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn flow_stats(&self) -> std::result::Result<Option<FlowStat>, Self::Error> {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,11 +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 std::sync::Arc;
|
use std::sync::{Arc, Weak};
|
||||||
|
|
||||||
use common_catalog::consts::INFORMATION_SCHEMA_FLOW_TABLE_ID;
|
use common_catalog::consts::INFORMATION_SCHEMA_FLOW_TABLE_ID;
|
||||||
use common_error::ext::BoxedError;
|
use common_error::ext::BoxedError;
|
||||||
use common_meta::key::flow::flow_info::FlowInfoValue;
|
use common_meta::key::flow::flow_info::FlowInfoValue;
|
||||||
|
use common_meta::key::flow::flow_state::FlowStat;
|
||||||
use common_meta::key::flow::FlowMetadataManager;
|
use common_meta::key::flow::FlowMetadataManager;
|
||||||
use common_meta::key::FlowId;
|
use common_meta::key::FlowId;
|
||||||
use common_recordbatch::adapter::RecordBatchStreamAdapter;
|
use common_recordbatch::adapter::RecordBatchStreamAdapter;
|
||||||
@@ -28,7 +29,9 @@ use datatypes::prelude::ConcreteDataType as CDT;
|
|||||||
use datatypes::scalars::ScalarVectorBuilder;
|
use datatypes::scalars::ScalarVectorBuilder;
|
||||||
use datatypes::schema::{ColumnSchema, Schema, SchemaRef};
|
use datatypes::schema::{ColumnSchema, Schema, SchemaRef};
|
||||||
use datatypes::value::Value;
|
use datatypes::value::Value;
|
||||||
use datatypes::vectors::{Int64VectorBuilder, StringVectorBuilder, UInt32VectorBuilder, VectorRef};
|
use datatypes::vectors::{
|
||||||
|
Int64VectorBuilder, StringVectorBuilder, UInt32VectorBuilder, UInt64VectorBuilder, VectorRef,
|
||||||
|
};
|
||||||
use futures::TryStreamExt;
|
use futures::TryStreamExt;
|
||||||
use snafu::{OptionExt, ResultExt};
|
use snafu::{OptionExt, ResultExt};
|
||||||
use store_api::storage::{ScanRequest, TableId};
|
use store_api::storage::{ScanRequest, TableId};
|
||||||
@@ -38,6 +41,8 @@ use crate::error::{
|
|||||||
};
|
};
|
||||||
use crate::information_schema::{Predicates, FLOWS};
|
use crate::information_schema::{Predicates, FLOWS};
|
||||||
use crate::system_schema::information_schema::InformationTable;
|
use crate::system_schema::information_schema::InformationTable;
|
||||||
|
use crate::system_schema::utils;
|
||||||
|
use crate::CatalogManager;
|
||||||
|
|
||||||
const INIT_CAPACITY: usize = 42;
|
const INIT_CAPACITY: usize = 42;
|
||||||
|
|
||||||
@@ -45,6 +50,7 @@ const INIT_CAPACITY: usize = 42;
|
|||||||
// pk is (flow_name, flow_id, table_catalog)
|
// pk is (flow_name, flow_id, table_catalog)
|
||||||
pub const FLOW_NAME: &str = "flow_name";
|
pub const FLOW_NAME: &str = "flow_name";
|
||||||
pub const FLOW_ID: &str = "flow_id";
|
pub const FLOW_ID: &str = "flow_id";
|
||||||
|
pub const STATE_SIZE: &str = "state_size";
|
||||||
pub const TABLE_CATALOG: &str = "table_catalog";
|
pub const TABLE_CATALOG: &str = "table_catalog";
|
||||||
pub const FLOW_DEFINITION: &str = "flow_definition";
|
pub const FLOW_DEFINITION: &str = "flow_definition";
|
||||||
pub const COMMENT: &str = "comment";
|
pub const COMMENT: &str = "comment";
|
||||||
@@ -55,20 +61,24 @@ pub const FLOWNODE_IDS: &str = "flownode_ids";
|
|||||||
pub const OPTIONS: &str = "options";
|
pub const OPTIONS: &str = "options";
|
||||||
|
|
||||||
/// The `information_schema.flows` to provides information about flows in databases.
|
/// The `information_schema.flows` to provides information about flows in databases.
|
||||||
|
///
|
||||||
pub(super) struct InformationSchemaFlows {
|
pub(super) struct InformationSchemaFlows {
|
||||||
schema: SchemaRef,
|
schema: SchemaRef,
|
||||||
catalog_name: String,
|
catalog_name: String,
|
||||||
|
catalog_manager: Weak<dyn CatalogManager>,
|
||||||
flow_metadata_manager: Arc<FlowMetadataManager>,
|
flow_metadata_manager: Arc<FlowMetadataManager>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InformationSchemaFlows {
|
impl InformationSchemaFlows {
|
||||||
pub(super) fn new(
|
pub(super) fn new(
|
||||||
catalog_name: String,
|
catalog_name: String,
|
||||||
|
catalog_manager: Weak<dyn CatalogManager>,
|
||||||
flow_metadata_manager: Arc<FlowMetadataManager>,
|
flow_metadata_manager: Arc<FlowMetadataManager>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
schema: Self::schema(),
|
schema: Self::schema(),
|
||||||
catalog_name,
|
catalog_name,
|
||||||
|
catalog_manager,
|
||||||
flow_metadata_manager,
|
flow_metadata_manager,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,6 +90,7 @@ impl InformationSchemaFlows {
|
|||||||
vec![
|
vec![
|
||||||
(FLOW_NAME, CDT::string_datatype(), false),
|
(FLOW_NAME, CDT::string_datatype(), false),
|
||||||
(FLOW_ID, CDT::uint32_datatype(), false),
|
(FLOW_ID, CDT::uint32_datatype(), false),
|
||||||
|
(STATE_SIZE, CDT::uint64_datatype(), true),
|
||||||
(TABLE_CATALOG, CDT::string_datatype(), false),
|
(TABLE_CATALOG, CDT::string_datatype(), false),
|
||||||
(FLOW_DEFINITION, CDT::string_datatype(), false),
|
(FLOW_DEFINITION, CDT::string_datatype(), false),
|
||||||
(COMMENT, CDT::string_datatype(), true),
|
(COMMENT, CDT::string_datatype(), true),
|
||||||
@@ -99,6 +110,7 @@ impl InformationSchemaFlows {
|
|||||||
InformationSchemaFlowsBuilder::new(
|
InformationSchemaFlowsBuilder::new(
|
||||||
self.schema.clone(),
|
self.schema.clone(),
|
||||||
self.catalog_name.clone(),
|
self.catalog_name.clone(),
|
||||||
|
self.catalog_manager.clone(),
|
||||||
&self.flow_metadata_manager,
|
&self.flow_metadata_manager,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -144,10 +156,12 @@ impl InformationTable for InformationSchemaFlows {
|
|||||||
struct InformationSchemaFlowsBuilder {
|
struct InformationSchemaFlowsBuilder {
|
||||||
schema: SchemaRef,
|
schema: SchemaRef,
|
||||||
catalog_name: String,
|
catalog_name: String,
|
||||||
|
catalog_manager: Weak<dyn CatalogManager>,
|
||||||
flow_metadata_manager: Arc<FlowMetadataManager>,
|
flow_metadata_manager: Arc<FlowMetadataManager>,
|
||||||
|
|
||||||
flow_names: StringVectorBuilder,
|
flow_names: StringVectorBuilder,
|
||||||
flow_ids: UInt32VectorBuilder,
|
flow_ids: UInt32VectorBuilder,
|
||||||
|
state_sizes: UInt64VectorBuilder,
|
||||||
table_catalogs: StringVectorBuilder,
|
table_catalogs: StringVectorBuilder,
|
||||||
raw_sqls: StringVectorBuilder,
|
raw_sqls: StringVectorBuilder,
|
||||||
comments: StringVectorBuilder,
|
comments: StringVectorBuilder,
|
||||||
@@ -162,15 +176,18 @@ impl InformationSchemaFlowsBuilder {
|
|||||||
fn new(
|
fn new(
|
||||||
schema: SchemaRef,
|
schema: SchemaRef,
|
||||||
catalog_name: String,
|
catalog_name: String,
|
||||||
|
catalog_manager: Weak<dyn CatalogManager>,
|
||||||
flow_metadata_manager: &Arc<FlowMetadataManager>,
|
flow_metadata_manager: &Arc<FlowMetadataManager>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
schema,
|
schema,
|
||||||
catalog_name,
|
catalog_name,
|
||||||
|
catalog_manager,
|
||||||
flow_metadata_manager: flow_metadata_manager.clone(),
|
flow_metadata_manager: flow_metadata_manager.clone(),
|
||||||
|
|
||||||
flow_names: StringVectorBuilder::with_capacity(INIT_CAPACITY),
|
flow_names: StringVectorBuilder::with_capacity(INIT_CAPACITY),
|
||||||
flow_ids: UInt32VectorBuilder::with_capacity(INIT_CAPACITY),
|
flow_ids: UInt32VectorBuilder::with_capacity(INIT_CAPACITY),
|
||||||
|
state_sizes: UInt64VectorBuilder::with_capacity(INIT_CAPACITY),
|
||||||
table_catalogs: StringVectorBuilder::with_capacity(INIT_CAPACITY),
|
table_catalogs: StringVectorBuilder::with_capacity(INIT_CAPACITY),
|
||||||
raw_sqls: StringVectorBuilder::with_capacity(INIT_CAPACITY),
|
raw_sqls: StringVectorBuilder::with_capacity(INIT_CAPACITY),
|
||||||
comments: StringVectorBuilder::with_capacity(INIT_CAPACITY),
|
comments: StringVectorBuilder::with_capacity(INIT_CAPACITY),
|
||||||
@@ -195,6 +212,11 @@ impl InformationSchemaFlowsBuilder {
|
|||||||
.flow_names(&catalog_name)
|
.flow_names(&catalog_name)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
let flow_stat = {
|
||||||
|
let information_extension = utils::information_extension(&self.catalog_manager)?;
|
||||||
|
information_extension.flow_stats().await?
|
||||||
|
};
|
||||||
|
|
||||||
while let Some((flow_name, flow_id)) = stream
|
while let Some((flow_name, flow_id)) = stream
|
||||||
.try_next()
|
.try_next()
|
||||||
.await
|
.await
|
||||||
@@ -213,7 +235,7 @@ impl InformationSchemaFlowsBuilder {
|
|||||||
catalog_name: catalog_name.to_string(),
|
catalog_name: catalog_name.to_string(),
|
||||||
flow_name: flow_name.to_string(),
|
flow_name: flow_name.to_string(),
|
||||||
})?;
|
})?;
|
||||||
self.add_flow(&predicates, flow_id.flow_id(), flow_info)?;
|
self.add_flow(&predicates, flow_id.flow_id(), flow_info, &flow_stat)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.finish()
|
self.finish()
|
||||||
@@ -224,6 +246,7 @@ impl InformationSchemaFlowsBuilder {
|
|||||||
predicates: &Predicates,
|
predicates: &Predicates,
|
||||||
flow_id: FlowId,
|
flow_id: FlowId,
|
||||||
flow_info: FlowInfoValue,
|
flow_info: FlowInfoValue,
|
||||||
|
flow_stat: &Option<FlowStat>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let row = [
|
let row = [
|
||||||
(FLOW_NAME, &Value::from(flow_info.flow_name().to_string())),
|
(FLOW_NAME, &Value::from(flow_info.flow_name().to_string())),
|
||||||
@@ -238,6 +261,11 @@ impl InformationSchemaFlowsBuilder {
|
|||||||
}
|
}
|
||||||
self.flow_names.push(Some(flow_info.flow_name()));
|
self.flow_names.push(Some(flow_info.flow_name()));
|
||||||
self.flow_ids.push(Some(flow_id));
|
self.flow_ids.push(Some(flow_id));
|
||||||
|
self.state_sizes.push(
|
||||||
|
flow_stat
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|state| state.state_size.get(&flow_id).map(|v| *v as u64)),
|
||||||
|
);
|
||||||
self.table_catalogs.push(Some(flow_info.catalog_name()));
|
self.table_catalogs.push(Some(flow_info.catalog_name()));
|
||||||
self.raw_sqls.push(Some(flow_info.raw_sql()));
|
self.raw_sqls.push(Some(flow_info.raw_sql()));
|
||||||
self.comments.push(Some(flow_info.comment()));
|
self.comments.push(Some(flow_info.comment()));
|
||||||
@@ -270,6 +298,7 @@ impl InformationSchemaFlowsBuilder {
|
|||||||
let columns: Vec<VectorRef> = vec![
|
let columns: Vec<VectorRef> = vec![
|
||||||
Arc::new(self.flow_names.finish()),
|
Arc::new(self.flow_names.finish()),
|
||||||
Arc::new(self.flow_ids.finish()),
|
Arc::new(self.flow_ids.finish()),
|
||||||
|
Arc::new(self.state_sizes.finish()),
|
||||||
Arc::new(self.table_catalogs.finish()),
|
Arc::new(self.table_catalogs.finish()),
|
||||||
Arc::new(self.raw_sqls.finish()),
|
Arc::new(self.raw_sqls.finish()),
|
||||||
Arc::new(self.comments.finish()),
|
Arc::new(self.comments.finish()),
|
||||||
|
|||||||
@@ -54,6 +54,10 @@ const INIT_CAPACITY: usize = 42;
|
|||||||
pub(crate) const PRI_CONSTRAINT_NAME: &str = "PRIMARY";
|
pub(crate) const PRI_CONSTRAINT_NAME: &str = "PRIMARY";
|
||||||
/// Time index constraint name
|
/// Time index constraint name
|
||||||
pub(crate) const TIME_INDEX_CONSTRAINT_NAME: &str = "TIME INDEX";
|
pub(crate) const TIME_INDEX_CONSTRAINT_NAME: &str = "TIME INDEX";
|
||||||
|
/// Inverted index constraint name
|
||||||
|
pub(crate) const INVERTED_INDEX_CONSTRAINT_NAME: &str = "INVERTED INDEX";
|
||||||
|
/// Fulltext index constraint name
|
||||||
|
pub(crate) const FULLTEXT_INDEX_CONSTRAINT_NAME: &str = "FULLTEXT INDEX";
|
||||||
|
|
||||||
/// The virtual table implementation for `information_schema.KEY_COLUMN_USAGE`.
|
/// The virtual table implementation for `information_schema.KEY_COLUMN_USAGE`.
|
||||||
pub(super) struct InformationSchemaKeyColumnUsage {
|
pub(super) struct InformationSchemaKeyColumnUsage {
|
||||||
@@ -216,14 +220,13 @@ impl InformationSchemaKeyColumnUsageBuilder {
|
|||||||
let mut stream = catalog_manager.tables(&catalog_name, &schema_name, None);
|
let mut stream = catalog_manager.tables(&catalog_name, &schema_name, None);
|
||||||
|
|
||||||
while let Some(table) = stream.try_next().await? {
|
while let Some(table) = stream.try_next().await? {
|
||||||
let mut primary_constraints = vec![];
|
|
||||||
|
|
||||||
let table_info = table.table_info();
|
let table_info = table.table_info();
|
||||||
let table_name = &table_info.name;
|
let table_name = &table_info.name;
|
||||||
let keys = &table_info.meta.primary_key_indices;
|
let keys = &table_info.meta.primary_key_indices;
|
||||||
let schema = table.schema();
|
let schema = table.schema();
|
||||||
|
|
||||||
for (idx, column) in schema.column_schemas().iter().enumerate() {
|
for (idx, column) in schema.column_schemas().iter().enumerate() {
|
||||||
|
let mut constraints = vec![];
|
||||||
if column.is_time_index() {
|
if column.is_time_index() {
|
||||||
self.add_key_column_usage(
|
self.add_key_column_usage(
|
||||||
&predicates,
|
&predicates,
|
||||||
@@ -236,30 +239,31 @@ impl InformationSchemaKeyColumnUsageBuilder {
|
|||||||
1, //always 1 for time index
|
1, //always 1 for time index
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if keys.contains(&idx) {
|
|
||||||
primary_constraints.push((
|
|
||||||
catalog_name.clone(),
|
|
||||||
schema_name.clone(),
|
|
||||||
table_name.to_string(),
|
|
||||||
column.name.clone(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
// TODO(dimbtp): foreign key constraint not supported yet
|
// TODO(dimbtp): foreign key constraint not supported yet
|
||||||
}
|
if keys.contains(&idx) {
|
||||||
|
constraints.push(PRI_CONSTRAINT_NAME);
|
||||||
|
}
|
||||||
|
if column.is_inverted_indexed() {
|
||||||
|
constraints.push(INVERTED_INDEX_CONSTRAINT_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
for (i, (catalog_name, schema_name, table_name, column_name)) in
|
if column.has_fulltext_index_key() {
|
||||||
primary_constraints.into_iter().enumerate()
|
constraints.push(FULLTEXT_INDEX_CONSTRAINT_NAME);
|
||||||
{
|
}
|
||||||
self.add_key_column_usage(
|
|
||||||
&predicates,
|
if !constraints.is_empty() {
|
||||||
&schema_name,
|
let aggregated_constraints = constraints.join(", ");
|
||||||
PRI_CONSTRAINT_NAME,
|
self.add_key_column_usage(
|
||||||
&catalog_name,
|
&predicates,
|
||||||
&schema_name,
|
&schema_name,
|
||||||
&table_name,
|
&aggregated_constraints,
|
||||||
&column_name,
|
&catalog_name,
|
||||||
i as u32 + 1,
|
&schema_name,
|
||||||
);
|
table_name,
|
||||||
|
&column.name,
|
||||||
|
idx as u32 + 1,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ common-error.workspace = true
|
|||||||
common-grpc.workspace = true
|
common-grpc.workspace = true
|
||||||
common-macro.workspace = true
|
common-macro.workspace = true
|
||||||
common-meta.workspace = true
|
common-meta.workspace = true
|
||||||
common-options.workspace = true
|
|
||||||
common-procedure.workspace = true
|
common-procedure.workspace = true
|
||||||
common-query.workspace = true
|
common-query.workspace = true
|
||||||
common-recordbatch.workspace = true
|
common-recordbatch.workspace = true
|
||||||
@@ -61,5 +60,4 @@ client = { workspace = true, features = ["testing"] }
|
|||||||
common-test-util.workspace = true
|
common-test-util.workspace = true
|
||||||
common-version.workspace = true
|
common-version.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
temp-env = "0.3"
|
|
||||||
tempfile.workspace = true
|
tempfile.workspace = true
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ use common_query::Output;
|
|||||||
use common_recordbatch::RecordBatches;
|
use common_recordbatch::RecordBatches;
|
||||||
use common_telemetry::debug;
|
use common_telemetry::debug;
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use meta_client::client::MetaClientBuilder;
|
use meta_client::client::{ClusterKvBackend, MetaClientBuilder};
|
||||||
use query::datafusion::DatafusionQueryEngine;
|
use query::datafusion::DatafusionQueryEngine;
|
||||||
use query::parser::QueryLanguageParser;
|
use query::parser::QueryLanguageParser;
|
||||||
use query::query_engine::{DefaultSerializer, QueryEngineState};
|
use query::query_engine::{DefaultSerializer, QueryEngineState};
|
||||||
|
|||||||
@@ -42,8 +42,6 @@ tonic.workspace = true
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
common-grpc-expr.workspace = true
|
common-grpc-expr.workspace = true
|
||||||
datanode.workspace = true
|
|
||||||
derive-new = "0.5"
|
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
|
|
||||||
[dev-dependencies.substrait_proto]
|
[dev-dependencies.substrait_proto]
|
||||||
|
|||||||
@@ -59,10 +59,6 @@ impl Instance {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn datanode_mut(&mut self) -> &mut Datanode {
|
|
||||||
&mut self.datanode
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn datanode(&self) -> &Datanode {
|
pub fn datanode(&self) -> &Datanode {
|
||||||
&self.datanode
|
&self.datanode
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,10 +63,6 @@ impl Instance {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn flownode_mut(&mut self) -> &mut FlownodeInstance {
|
|
||||||
&mut self.flownode
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn flownode(&self) -> &FlownodeInstance {
|
pub fn flownode(&self) -> &FlownodeInstance {
|
||||||
&self.flownode
|
&self.flownode
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ use common_meta::ddl::flow_meta::{FlowMetadataAllocator, FlowMetadataAllocatorRe
|
|||||||
use common_meta::ddl::table_meta::{TableMetadataAllocator, TableMetadataAllocatorRef};
|
use common_meta::ddl::table_meta::{TableMetadataAllocator, TableMetadataAllocatorRef};
|
||||||
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;
|
||||||
|
use common_meta::key::flow::flow_state::FlowStat;
|
||||||
use common_meta::key::flow::{FlowMetadataManager, FlowMetadataManagerRef};
|
use common_meta::key::flow::{FlowMetadataManager, FlowMetadataManagerRef};
|
||||||
use common_meta::key::{TableMetadataManager, TableMetadataManagerRef};
|
use common_meta::key::{TableMetadataManager, TableMetadataManagerRef};
|
||||||
use common_meta::kv_backend::KvBackendRef;
|
use common_meta::kv_backend::KvBackendRef;
|
||||||
@@ -70,7 +71,7 @@ use servers::http::HttpOptions;
|
|||||||
use servers::tls::{TlsMode, TlsOption};
|
use servers::tls::{TlsMode, TlsOption};
|
||||||
use servers::Mode;
|
use servers::Mode;
|
||||||
use snafu::ResultExt;
|
use snafu::ResultExt;
|
||||||
use tokio::sync::broadcast;
|
use tokio::sync::{broadcast, RwLock};
|
||||||
use tracing_appender::non_blocking::WorkerGuard;
|
use tracing_appender::non_blocking::WorkerGuard;
|
||||||
|
|
||||||
use crate::error::{
|
use crate::error::{
|
||||||
@@ -507,7 +508,7 @@ impl StartCommand {
|
|||||||
procedure_manager.clone(),
|
procedure_manager.clone(),
|
||||||
));
|
));
|
||||||
let catalog_manager = KvBackendCatalogManager::new(
|
let catalog_manager = KvBackendCatalogManager::new(
|
||||||
information_extension,
|
information_extension.clone(),
|
||||||
kv_backend.clone(),
|
kv_backend.clone(),
|
||||||
layered_cache_registry.clone(),
|
layered_cache_registry.clone(),
|
||||||
Some(procedure_manager.clone()),
|
Some(procedure_manager.clone()),
|
||||||
@@ -532,6 +533,14 @@ impl StartCommand {
|
|||||||
.context(OtherSnafu)?,
|
.context(OtherSnafu)?,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// set the ref to query for the local flow state
|
||||||
|
{
|
||||||
|
let flow_worker_manager = flownode.flow_worker_manager();
|
||||||
|
information_extension
|
||||||
|
.set_flow_worker_manager(flow_worker_manager.clone())
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
let node_manager = Arc::new(StandaloneDatanodeManager {
|
let node_manager = Arc::new(StandaloneDatanodeManager {
|
||||||
region_server: datanode.region_server(),
|
region_server: datanode.region_server(),
|
||||||
flow_server: flownode.flow_worker_manager(),
|
flow_server: flownode.flow_worker_manager(),
|
||||||
@@ -669,6 +678,7 @@ pub struct StandaloneInformationExtension {
|
|||||||
region_server: RegionServer,
|
region_server: RegionServer,
|
||||||
procedure_manager: ProcedureManagerRef,
|
procedure_manager: ProcedureManagerRef,
|
||||||
start_time_ms: u64,
|
start_time_ms: u64,
|
||||||
|
flow_worker_manager: RwLock<Option<Arc<FlowWorkerManager>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StandaloneInformationExtension {
|
impl StandaloneInformationExtension {
|
||||||
@@ -677,8 +687,15 @@ impl StandaloneInformationExtension {
|
|||||||
region_server,
|
region_server,
|
||||||
procedure_manager,
|
procedure_manager,
|
||||||
start_time_ms: common_time::util::current_time_millis() as u64,
|
start_time_ms: common_time::util::current_time_millis() as u64,
|
||||||
|
flow_worker_manager: RwLock::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set the flow worker manager for the standalone instance.
|
||||||
|
pub async fn set_flow_worker_manager(&self, flow_worker_manager: Arc<FlowWorkerManager>) {
|
||||||
|
let mut guard = self.flow_worker_manager.write().await;
|
||||||
|
*guard = Some(flow_worker_manager);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
@@ -750,6 +767,18 @@ impl InformationExtension for StandaloneInformationExtension {
|
|||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
Ok(stats)
|
Ok(stats)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn flow_stats(&self) -> std::result::Result<Option<FlowStat>, Self::Error> {
|
||||||
|
Ok(Some(
|
||||||
|
self.flow_worker_manager
|
||||||
|
.read()
|
||||||
|
.await
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.gen_state_report()
|
||||||
|
.await,
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ common-macro.workspace = true
|
|||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
paste = "1.0"
|
paste = "1.0"
|
||||||
pin-project.workspace = true
|
pin-project.workspace = true
|
||||||
|
rand.workspace = true
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
snafu.workspace = true
|
snafu.workspace = true
|
||||||
tokio.workspace = true
|
tokio.workspace = true
|
||||||
|
|||||||
@@ -36,6 +36,11 @@ pub struct Metadata {
|
|||||||
/// `RangeReader` reads a range of bytes from a source.
|
/// `RangeReader` reads a range of bytes from a source.
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait RangeReader: Send + Unpin {
|
pub trait RangeReader: Send + Unpin {
|
||||||
|
/// Sets the file size hint for the reader.
|
||||||
|
///
|
||||||
|
/// It's used to optimize the reading process by reducing the number of remote requests.
|
||||||
|
fn with_file_size_hint(&mut self, file_size_hint: u64);
|
||||||
|
|
||||||
/// Returns the metadata of the source.
|
/// Returns the metadata of the source.
|
||||||
async fn metadata(&mut self) -> io::Result<Metadata>;
|
async fn metadata(&mut self) -> io::Result<Metadata>;
|
||||||
|
|
||||||
@@ -70,6 +75,10 @@ pub trait RangeReader: Send + Unpin {
|
|||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl<R: ?Sized + RangeReader> RangeReader for &mut R {
|
impl<R: ?Sized + RangeReader> RangeReader for &mut R {
|
||||||
|
fn with_file_size_hint(&mut self, file_size_hint: u64) {
|
||||||
|
(*self).with_file_size_hint(file_size_hint)
|
||||||
|
}
|
||||||
|
|
||||||
async fn metadata(&mut self) -> io::Result<Metadata> {
|
async fn metadata(&mut self) -> io::Result<Metadata> {
|
||||||
(*self).metadata().await
|
(*self).metadata().await
|
||||||
}
|
}
|
||||||
@@ -186,15 +195,17 @@ impl<R: RangeReader + 'static> AsyncRead for AsyncReadAdapter<R> {
|
|||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl RangeReader for Vec<u8> {
|
impl RangeReader for Vec<u8> {
|
||||||
|
fn with_file_size_hint(&mut self, _file_size_hint: u64) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
async fn metadata(&mut self) -> io::Result<Metadata> {
|
async fn metadata(&mut self) -> io::Result<Metadata> {
|
||||||
Ok(Metadata {
|
Ok(Metadata {
|
||||||
content_length: self.len() as u64,
|
content_length: self.len() as u64,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn read(&mut self, mut range: Range<u64>) -> io::Result<Bytes> {
|
async fn read(&mut self, range: Range<u64>) -> io::Result<Bytes> {
|
||||||
range.end = range.end.min(self.len() as u64);
|
|
||||||
|
|
||||||
let bytes = Bytes::copy_from_slice(&self[range.start as usize..range.end as usize]);
|
let bytes = Bytes::copy_from_slice(&self[range.start as usize..range.end as usize]);
|
||||||
Ok(bytes)
|
Ok(bytes)
|
||||||
}
|
}
|
||||||
@@ -222,6 +233,10 @@ impl FileReader {
|
|||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl RangeReader for FileReader {
|
impl RangeReader for FileReader {
|
||||||
|
fn with_file_size_hint(&mut self, _file_size_hint: u64) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
async fn metadata(&mut self) -> io::Result<Metadata> {
|
async fn metadata(&mut self) -> io::Result<Metadata> {
|
||||||
Ok(Metadata {
|
Ok(Metadata {
|
||||||
content_length: self.content_length,
|
content_length: self.content_length,
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ pub const GIB: u64 = MIB * BINARY_DATA_MAGNITUDE;
|
|||||||
pub const TIB: u64 = GIB * BINARY_DATA_MAGNITUDE;
|
pub const TIB: u64 = GIB * BINARY_DATA_MAGNITUDE;
|
||||||
pub const PIB: u64 = TIB * BINARY_DATA_MAGNITUDE;
|
pub const PIB: u64 = TIB * BINARY_DATA_MAGNITUDE;
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd)]
|
#[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Default)]
|
||||||
pub struct ReadableSize(pub u64);
|
pub struct ReadableSize(pub u64);
|
||||||
|
|
||||||
impl ReadableSize {
|
impl ReadableSize {
|
||||||
|
|||||||
@@ -8,10 +8,5 @@ license.workspace = true
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
common-error.workspace = true
|
|
||||||
common-macro.workspace = true
|
|
||||||
snafu.workspace = true
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
chrono.workspace = true
|
|
||||||
tokio.workspace = true
|
|
||||||
|
|||||||
@@ -48,5 +48,4 @@ url = "2.3"
|
|||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
common-telemetry.workspace = true
|
common-telemetry.workspace = true
|
||||||
common-test-util.workspace = true
|
common-test-util.workspace = true
|
||||||
dotenv.workspace = true
|
|
||||||
uuid.workspace = true
|
uuid.workspace = true
|
||||||
|
|||||||
@@ -5,12 +5,7 @@ edition.workspace = true
|
|||||||
license.workspace = true
|
license.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
api.workspace = true
|
|
||||||
async-trait.workspace = true
|
async-trait.workspace = true
|
||||||
common-base.workspace = true
|
|
||||||
common-error.workspace = true
|
common-error.workspace = true
|
||||||
common-macro.workspace = true
|
common-macro.workspace = true
|
||||||
common-query.workspace = true
|
|
||||||
session.workspace = true
|
|
||||||
snafu.workspace = true
|
snafu.workspace = true
|
||||||
sql.workspace = true
|
|
||||||
|
|||||||
@@ -51,6 +51,5 @@ wkt = { version = "0.11", optional = true }
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
approx = "0.5"
|
approx = "0.5"
|
||||||
ron = "0.7"
|
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
tokio.workspace = true
|
tokio.workspace = true
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
mod convert;
|
mod convert;
|
||||||
mod distance;
|
mod distance;
|
||||||
pub(crate) mod impl_conv;
|
pub(crate) mod impl_conv;
|
||||||
|
mod scalar_add;
|
||||||
|
mod scalar_mul;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@@ -32,5 +34,9 @@ impl VectorFunction {
|
|||||||
registry.register(Arc::new(distance::CosDistanceFunction));
|
registry.register(Arc::new(distance::CosDistanceFunction));
|
||||||
registry.register(Arc::new(distance::DotProductFunction));
|
registry.register(Arc::new(distance::DotProductFunction));
|
||||||
registry.register(Arc::new(distance::L2SqDistanceFunction));
|
registry.register(Arc::new(distance::L2SqDistanceFunction));
|
||||||
|
|
||||||
|
// scalar calculation
|
||||||
|
registry.register(Arc::new(scalar_add::ScalarAddFunction));
|
||||||
|
registry.register(Arc::new(scalar_mul::ScalarMulFunction));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,7 +109,6 @@ pub fn parse_veclit_from_strlit(s: &str) -> Result<Vec<f32>> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
/// Convert a vector literal to a binary literal.
|
/// Convert a vector literal to a binary literal.
|
||||||
pub fn veclit_to_binlit(vec: &[f32]) -> Vec<u8> {
|
pub fn veclit_to_binlit(vec: &[f32]) -> Vec<u8> {
|
||||||
if cfg!(target_endian = "little") {
|
if cfg!(target_endian = "little") {
|
||||||
|
|||||||
173
src/common/function/src/scalars/vector/scalar_add.rs
Normal file
173
src/common/function/src/scalars/vector/scalar_add.rs
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
// 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::borrow::Cow;
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use common_query::error::{InvalidFuncArgsSnafu, Result};
|
||||||
|
use common_query::prelude::Signature;
|
||||||
|
use datatypes::prelude::ConcreteDataType;
|
||||||
|
use datatypes::scalars::ScalarVectorBuilder;
|
||||||
|
use datatypes::vectors::{BinaryVectorBuilder, MutableVector, VectorRef};
|
||||||
|
use nalgebra::DVectorView;
|
||||||
|
use snafu::ensure;
|
||||||
|
|
||||||
|
use crate::function::{Function, FunctionContext};
|
||||||
|
use crate::helper;
|
||||||
|
use crate::scalars::vector::impl_conv::{as_veclit, as_veclit_if_const, veclit_to_binlit};
|
||||||
|
|
||||||
|
const NAME: &str = "vec_scalar_add";
|
||||||
|
|
||||||
|
/// Adds a scalar to each element of a vector.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```sql
|
||||||
|
/// SELECT vec_to_string(vec_scalar_add(1, "[1, 2, 3]")) as result;
|
||||||
|
///
|
||||||
|
/// +---------+
|
||||||
|
/// | result |
|
||||||
|
/// +---------+
|
||||||
|
/// | [2,3,4] |
|
||||||
|
/// +---------+
|
||||||
|
///
|
||||||
|
/// -- Negative scalar to simulate subtraction
|
||||||
|
/// SELECT vec_to_string(vec_scalar_add(-1, "[1, 2, 3]")) as result;
|
||||||
|
///
|
||||||
|
/// +---------+
|
||||||
|
/// | result |
|
||||||
|
/// +---------+
|
||||||
|
/// | [0,1,2] |
|
||||||
|
/// +---------+
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct ScalarAddFunction;
|
||||||
|
|
||||||
|
impl Function for ScalarAddFunction {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
NAME
|
||||||
|
}
|
||||||
|
|
||||||
|
fn return_type(&self, _input_types: &[ConcreteDataType]) -> Result<ConcreteDataType> {
|
||||||
|
Ok(ConcreteDataType::binary_datatype())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
helper::one_of_sigs2(
|
||||||
|
vec![ConcreteDataType::float64_datatype()],
|
||||||
|
vec![
|
||||||
|
ConcreteDataType::string_datatype(),
|
||||||
|
ConcreteDataType::binary_datatype(),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval(&self, _func_ctx: FunctionContext, columns: &[VectorRef]) -> Result<VectorRef> {
|
||||||
|
ensure!(
|
||||||
|
columns.len() == 2,
|
||||||
|
InvalidFuncArgsSnafu {
|
||||||
|
err_msg: format!(
|
||||||
|
"The length of the args is not correct, expect exactly two, have: {}",
|
||||||
|
columns.len()
|
||||||
|
),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
let arg0 = &columns[0];
|
||||||
|
let arg1 = &columns[1];
|
||||||
|
|
||||||
|
let len = arg0.len();
|
||||||
|
let mut result = BinaryVectorBuilder::with_capacity(len);
|
||||||
|
if len == 0 {
|
||||||
|
return Ok(result.to_vector());
|
||||||
|
}
|
||||||
|
|
||||||
|
let arg1_const = as_veclit_if_const(arg1)?;
|
||||||
|
|
||||||
|
for i in 0..len {
|
||||||
|
let arg0 = arg0.get(i).as_f64_lossy();
|
||||||
|
let Some(arg0) = arg0 else {
|
||||||
|
result.push_null();
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let arg1 = match arg1_const.as_ref() {
|
||||||
|
Some(arg1) => Some(Cow::Borrowed(arg1.as_ref())),
|
||||||
|
None => as_veclit(arg1.get_ref(i))?,
|
||||||
|
};
|
||||||
|
let Some(arg1) = arg1 else {
|
||||||
|
result.push_null();
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let vec = DVectorView::from_slice(&arg1, arg1.len());
|
||||||
|
let vec_res = vec.add_scalar(arg0 as _);
|
||||||
|
|
||||||
|
let veclit = vec_res.as_slice();
|
||||||
|
let binlit = veclit_to_binlit(veclit);
|
||||||
|
result.push(Some(&binlit));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(result.to_vector())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for ScalarAddFunction {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", NAME.to_ascii_uppercase())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use datatypes::vectors::{Float32Vector, StringVector};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_scalar_add() {
|
||||||
|
let func = ScalarAddFunction;
|
||||||
|
|
||||||
|
let input0 = Arc::new(Float32Vector::from(vec![
|
||||||
|
Some(1.0),
|
||||||
|
Some(-1.0),
|
||||||
|
None,
|
||||||
|
Some(3.0),
|
||||||
|
]));
|
||||||
|
let input1 = Arc::new(StringVector::from(vec![
|
||||||
|
Some("[1.0,2.0,3.0]".to_string()),
|
||||||
|
Some("[4.0,5.0,6.0]".to_string()),
|
||||||
|
Some("[7.0,8.0,9.0]".to_string()),
|
||||||
|
None,
|
||||||
|
]));
|
||||||
|
|
||||||
|
let result = func
|
||||||
|
.eval(FunctionContext::default(), &[input0, input1])
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let result = result.as_ref();
|
||||||
|
assert_eq!(result.len(), 4);
|
||||||
|
assert_eq!(
|
||||||
|
result.get_ref(0).as_binary().unwrap(),
|
||||||
|
Some(veclit_to_binlit(&[2.0, 3.0, 4.0]).as_slice())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
result.get_ref(1).as_binary().unwrap(),
|
||||||
|
Some(veclit_to_binlit(&[3.0, 4.0, 5.0]).as_slice())
|
||||||
|
);
|
||||||
|
assert!(result.get_ref(2).is_null());
|
||||||
|
assert!(result.get_ref(3).is_null());
|
||||||
|
}
|
||||||
|
}
|
||||||
173
src/common/function/src/scalars/vector/scalar_mul.rs
Normal file
173
src/common/function/src/scalars/vector/scalar_mul.rs
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
// 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::borrow::Cow;
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use common_query::error::{InvalidFuncArgsSnafu, Result};
|
||||||
|
use common_query::prelude::Signature;
|
||||||
|
use datatypes::prelude::ConcreteDataType;
|
||||||
|
use datatypes::scalars::ScalarVectorBuilder;
|
||||||
|
use datatypes::vectors::{BinaryVectorBuilder, MutableVector, VectorRef};
|
||||||
|
use nalgebra::DVectorView;
|
||||||
|
use snafu::ensure;
|
||||||
|
|
||||||
|
use crate::function::{Function, FunctionContext};
|
||||||
|
use crate::helper;
|
||||||
|
use crate::scalars::vector::impl_conv::{as_veclit, as_veclit_if_const, veclit_to_binlit};
|
||||||
|
|
||||||
|
const NAME: &str = "vec_scalar_mul";
|
||||||
|
|
||||||
|
/// Multiples a scalar to each element of a vector.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```sql
|
||||||
|
/// SELECT vec_to_string(vec_scalar_mul(2, "[1, 2, 3]")) as result;
|
||||||
|
///
|
||||||
|
/// +---------+
|
||||||
|
/// | result |
|
||||||
|
/// +---------+
|
||||||
|
/// | [2,4,6] |
|
||||||
|
/// +---------+
|
||||||
|
///
|
||||||
|
/// -- 1/scalar to simulate division
|
||||||
|
/// SELECT vec_to_string(vec_scalar_mul(0.5, "[2, 4, 6]")) as result;
|
||||||
|
///
|
||||||
|
/// +---------+
|
||||||
|
/// | result |
|
||||||
|
/// +---------+
|
||||||
|
/// | [1,2,3] |
|
||||||
|
/// +---------+
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct ScalarMulFunction;
|
||||||
|
|
||||||
|
impl Function for ScalarMulFunction {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
NAME
|
||||||
|
}
|
||||||
|
|
||||||
|
fn return_type(&self, _input_types: &[ConcreteDataType]) -> Result<ConcreteDataType> {
|
||||||
|
Ok(ConcreteDataType::binary_datatype())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
helper::one_of_sigs2(
|
||||||
|
vec![ConcreteDataType::float64_datatype()],
|
||||||
|
vec![
|
||||||
|
ConcreteDataType::string_datatype(),
|
||||||
|
ConcreteDataType::binary_datatype(),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval(&self, _func_ctx: FunctionContext, columns: &[VectorRef]) -> Result<VectorRef> {
|
||||||
|
ensure!(
|
||||||
|
columns.len() == 2,
|
||||||
|
InvalidFuncArgsSnafu {
|
||||||
|
err_msg: format!(
|
||||||
|
"The length of the args is not correct, expect exactly two, have: {}",
|
||||||
|
columns.len()
|
||||||
|
),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
let arg0 = &columns[0];
|
||||||
|
let arg1 = &columns[1];
|
||||||
|
|
||||||
|
let len = arg0.len();
|
||||||
|
let mut result = BinaryVectorBuilder::with_capacity(len);
|
||||||
|
if len == 0 {
|
||||||
|
return Ok(result.to_vector());
|
||||||
|
}
|
||||||
|
|
||||||
|
let arg1_const = as_veclit_if_const(arg1)?;
|
||||||
|
|
||||||
|
for i in 0..len {
|
||||||
|
let arg0 = arg0.get(i).as_f64_lossy();
|
||||||
|
let Some(arg0) = arg0 else {
|
||||||
|
result.push_null();
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let arg1 = match arg1_const.as_ref() {
|
||||||
|
Some(arg1) => Some(Cow::Borrowed(arg1.as_ref())),
|
||||||
|
None => as_veclit(arg1.get_ref(i))?,
|
||||||
|
};
|
||||||
|
let Some(arg1) = arg1 else {
|
||||||
|
result.push_null();
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let vec = DVectorView::from_slice(&arg1, arg1.len());
|
||||||
|
let vec_res = vec.scale(arg0 as _);
|
||||||
|
|
||||||
|
let veclit = vec_res.as_slice();
|
||||||
|
let binlit = veclit_to_binlit(veclit);
|
||||||
|
result.push(Some(&binlit));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(result.to_vector())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for ScalarMulFunction {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", NAME.to_ascii_uppercase())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use datatypes::vectors::{Float32Vector, StringVector};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_scalar_mul() {
|
||||||
|
let func = ScalarMulFunction;
|
||||||
|
|
||||||
|
let input0 = Arc::new(Float32Vector::from(vec![
|
||||||
|
Some(2.0),
|
||||||
|
Some(-0.5),
|
||||||
|
None,
|
||||||
|
Some(3.0),
|
||||||
|
]));
|
||||||
|
let input1 = Arc::new(StringVector::from(vec![
|
||||||
|
Some("[1.0,2.0,3.0]".to_string()),
|
||||||
|
Some("[8.0,10.0,12.0]".to_string()),
|
||||||
|
Some("[7.0,8.0,9.0]".to_string()),
|
||||||
|
None,
|
||||||
|
]));
|
||||||
|
|
||||||
|
let result = func
|
||||||
|
.eval(FunctionContext::default(), &[input0, input1])
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let result = result.as_ref();
|
||||||
|
assert_eq!(result.len(), 4);
|
||||||
|
assert_eq!(
|
||||||
|
result.get_ref(0).as_binary().unwrap(),
|
||||||
|
Some(veclit_to_binlit(&[2.0, 4.0, 6.0]).as_slice())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
result.get_ref(1).as_binary().unwrap(),
|
||||||
|
Some(veclit_to_binlit(&[-4.0, -5.0, -6.0]).as_slice())
|
||||||
|
);
|
||||||
|
assert!(result.get_ref(2).is_null());
|
||||||
|
assert!(result.get_ref(3).is_null());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -49,14 +49,6 @@ impl TableRoute {
|
|||||||
TableRoute::Logical(_) => None,
|
TableRoute::Logical(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns [LogicalTableRouteValue] reference if it's [TableRoute::Logical]; Otherwise it returns [None].
|
|
||||||
pub fn as_logical_table_route_ref(&self) -> Option<&Arc<LogicalTableRouteValue>> {
|
|
||||||
match self {
|
|
||||||
TableRoute::Physical(_) => None,
|
|
||||||
TableRoute::Logical(table_route) => Some(table_route),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [TableRouteCache] caches the [TableId] to [TableRoute] mapping.
|
/// [TableRouteCache] caches the [TableId] to [TableRoute] mapping.
|
||||||
|
|||||||
@@ -137,6 +137,7 @@ use self::schema_name::{SchemaManager, SchemaNameKey, SchemaNameValue};
|
|||||||
use self::table_route::{TableRouteManager, TableRouteValue};
|
use self::table_route::{TableRouteManager, TableRouteValue};
|
||||||
use self::tombstone::TombstoneManager;
|
use self::tombstone::TombstoneManager;
|
||||||
use crate::error::{self, Result, SerdeJsonSnafu};
|
use crate::error::{self, Result, SerdeJsonSnafu};
|
||||||
|
use crate::key::flow::flow_state::FlowStateValue;
|
||||||
use crate::key::node_address::NodeAddressValue;
|
use crate::key::node_address::NodeAddressValue;
|
||||||
use crate::key::table_route::TableRouteKey;
|
use crate::key::table_route::TableRouteKey;
|
||||||
use crate::key::txn_helper::TxnOpGetResponseSet;
|
use crate::key::txn_helper::TxnOpGetResponseSet;
|
||||||
@@ -1262,7 +1263,8 @@ impl_metadata_value! {
|
|||||||
FlowRouteValue,
|
FlowRouteValue,
|
||||||
TableFlowValue,
|
TableFlowValue,
|
||||||
NodeAddressValue,
|
NodeAddressValue,
|
||||||
SchemaNameValue
|
SchemaNameValue,
|
||||||
|
FlowStateValue
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_optional_metadata_value! {
|
impl_optional_metadata_value! {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
pub mod flow_info;
|
pub mod flow_info;
|
||||||
pub(crate) mod flow_name;
|
pub(crate) mod flow_name;
|
||||||
pub(crate) mod flow_route;
|
pub(crate) mod flow_route;
|
||||||
|
pub mod flow_state;
|
||||||
pub(crate) mod flownode_flow;
|
pub(crate) mod flownode_flow;
|
||||||
pub(crate) mod table_flow;
|
pub(crate) mod table_flow;
|
||||||
|
|
||||||
@@ -35,6 +36,7 @@ use crate::ensure_values;
|
|||||||
use crate::error::{self, Result};
|
use crate::error::{self, Result};
|
||||||
use crate::key::flow::flow_info::FlowInfoManager;
|
use crate::key::flow::flow_info::FlowInfoManager;
|
||||||
use crate::key::flow::flow_name::FlowNameManager;
|
use crate::key::flow::flow_name::FlowNameManager;
|
||||||
|
use crate::key::flow::flow_state::FlowStateManager;
|
||||||
use crate::key::flow::flownode_flow::FlownodeFlowManager;
|
use crate::key::flow::flownode_flow::FlownodeFlowManager;
|
||||||
pub use crate::key::flow::table_flow::{TableFlowManager, TableFlowManagerRef};
|
pub use crate::key::flow::table_flow::{TableFlowManager, TableFlowManagerRef};
|
||||||
use crate::key::txn_helper::TxnOpGetResponseSet;
|
use crate::key::txn_helper::TxnOpGetResponseSet;
|
||||||
@@ -102,6 +104,8 @@ pub struct FlowMetadataManager {
|
|||||||
flownode_flow_manager: FlownodeFlowManager,
|
flownode_flow_manager: FlownodeFlowManager,
|
||||||
table_flow_manager: TableFlowManager,
|
table_flow_manager: TableFlowManager,
|
||||||
flow_name_manager: FlowNameManager,
|
flow_name_manager: FlowNameManager,
|
||||||
|
/// only metasrv have access to itself's memory backend, so for other case it should be None
|
||||||
|
flow_state_manager: Option<FlowStateManager>,
|
||||||
kv_backend: KvBackendRef,
|
kv_backend: KvBackendRef,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,6 +118,7 @@ impl FlowMetadataManager {
|
|||||||
flow_name_manager: FlowNameManager::new(kv_backend.clone()),
|
flow_name_manager: FlowNameManager::new(kv_backend.clone()),
|
||||||
flownode_flow_manager: FlownodeFlowManager::new(kv_backend.clone()),
|
flownode_flow_manager: FlownodeFlowManager::new(kv_backend.clone()),
|
||||||
table_flow_manager: TableFlowManager::new(kv_backend.clone()),
|
table_flow_manager: TableFlowManager::new(kv_backend.clone()),
|
||||||
|
flow_state_manager: None,
|
||||||
kv_backend,
|
kv_backend,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -123,6 +128,10 @@ impl FlowMetadataManager {
|
|||||||
&self.flow_name_manager
|
&self.flow_name_manager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn flow_state_manager(&self) -> Option<&FlowStateManager> {
|
||||||
|
self.flow_state_manager.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the [`FlowInfoManager`].
|
/// Returns the [`FlowInfoManager`].
|
||||||
pub fn flow_info_manager(&self) -> &FlowInfoManager {
|
pub fn flow_info_manager(&self) -> &FlowInfoManager {
|
||||||
&self.flow_info_manager
|
&self.flow_info_manager
|
||||||
|
|||||||
162
src/common/meta/src/key/flow/flow_state.rs
Normal file
162
src/common/meta/src/key/flow/flow_state.rs
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
// 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::BTreeMap;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::error::{self, Result};
|
||||||
|
use crate::key::flow::FlowScoped;
|
||||||
|
use crate::key::{FlowId, MetadataKey, MetadataValue};
|
||||||
|
use crate::kv_backend::KvBackendRef;
|
||||||
|
use crate::rpc::store::PutRequest;
|
||||||
|
|
||||||
|
/// The entire FlowId to Flow Size's Map is stored directly in the value part of the key.
|
||||||
|
const FLOW_STATE_KEY: &str = "state";
|
||||||
|
|
||||||
|
/// The key of flow state.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
struct FlowStateKeyInner;
|
||||||
|
|
||||||
|
impl FlowStateKeyInner {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> MetadataKey<'a, FlowStateKeyInner> for FlowStateKeyInner {
|
||||||
|
fn to_bytes(&self) -> Vec<u8> {
|
||||||
|
FLOW_STATE_KEY.as_bytes().to_vec()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_bytes(bytes: &'a [u8]) -> Result<FlowStateKeyInner> {
|
||||||
|
let key = std::str::from_utf8(bytes).map_err(|e| {
|
||||||
|
error::InvalidMetadataSnafu {
|
||||||
|
err_msg: format!(
|
||||||
|
"FlowInfoKeyInner '{}' is not a valid UTF8 string: {e}",
|
||||||
|
String::from_utf8_lossy(bytes)
|
||||||
|
),
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
})?;
|
||||||
|
if key != FLOW_STATE_KEY {
|
||||||
|
return Err(error::InvalidMetadataSnafu {
|
||||||
|
err_msg: format!("Invalid FlowStateKeyInner '{key}'"),
|
||||||
|
}
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
Ok(FlowStateKeyInner::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The key stores the state size of the flow.
|
||||||
|
///
|
||||||
|
/// The layout: `__flow/state`.
|
||||||
|
pub struct FlowStateKey(FlowScoped<FlowStateKeyInner>);
|
||||||
|
|
||||||
|
impl FlowStateKey {
|
||||||
|
/// Returns the [FlowStateKey].
|
||||||
|
pub fn new() -> FlowStateKey {
|
||||||
|
let inner = FlowStateKeyInner::new();
|
||||||
|
FlowStateKey(FlowScoped::new(inner))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for FlowStateKey {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> MetadataKey<'a, FlowStateKey> for FlowStateKey {
|
||||||
|
fn to_bytes(&self) -> Vec<u8> {
|
||||||
|
self.0.to_bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_bytes(bytes: &'a [u8]) -> Result<FlowStateKey> {
|
||||||
|
Ok(FlowStateKey(FlowScoped::<FlowStateKeyInner>::from_bytes(
|
||||||
|
bytes,
|
||||||
|
)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The value of flow state size
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub struct FlowStateValue {
|
||||||
|
/// For each key, the bytes of the state in memory
|
||||||
|
pub state_size: BTreeMap<FlowId, usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FlowStateValue {
|
||||||
|
pub fn new(state_size: BTreeMap<FlowId, usize>) -> Self {
|
||||||
|
Self { state_size }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type FlowStateManagerRef = Arc<FlowStateManager>;
|
||||||
|
|
||||||
|
/// The manager of [FlowStateKey]. Since state size changes frequently, we store it in memory.
|
||||||
|
///
|
||||||
|
/// This is only used in distributed mode. When meta-srv use heartbeat to update the flow stat report
|
||||||
|
/// and frontned use get to get the latest flow stat report.
|
||||||
|
pub struct FlowStateManager {
|
||||||
|
in_memory: KvBackendRef,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FlowStateManager {
|
||||||
|
pub fn new(in_memory: KvBackendRef) -> Self {
|
||||||
|
Self { in_memory }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get(&self) -> Result<Option<FlowStateValue>> {
|
||||||
|
let key = FlowStateKey::new().to_bytes();
|
||||||
|
self.in_memory
|
||||||
|
.get(&key)
|
||||||
|
.await?
|
||||||
|
.map(|x| FlowStateValue::try_from_raw_value(&x.value))
|
||||||
|
.transpose()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn put(&self, value: FlowStateValue) -> Result<()> {
|
||||||
|
let key = FlowStateKey::new().to_bytes();
|
||||||
|
let value = value.try_as_raw_value()?;
|
||||||
|
let req = PutRequest::new().with_key(key).with_value(value);
|
||||||
|
self.in_memory.put(req).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Flow's state report, send regularly through heartbeat message
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FlowStat {
|
||||||
|
/// For each key, the bytes of the state in memory
|
||||||
|
pub state_size: BTreeMap<u32, usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<FlowStateValue> for FlowStat {
|
||||||
|
fn from(value: FlowStateValue) -> Self {
|
||||||
|
Self {
|
||||||
|
state_size: value.state_size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<FlowStat> for FlowStateValue {
|
||||||
|
fn from(value: FlowStat) -> Self {
|
||||||
|
Self {
|
||||||
|
state_size: value.state_size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -290,28 +290,6 @@ impl TableRouteManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the [`PhysicalTableRouteValue`] in the first level,
|
|
||||||
/// It won't follow the [`LogicalTableRouteValue`] to find the next level [`PhysicalTableRouteValue`].
|
|
||||||
///
|
|
||||||
/// Returns an error if the first level value is not a [`PhysicalTableRouteValue`].
|
|
||||||
pub async fn try_get_physical_table_route(
|
|
||||||
&self,
|
|
||||||
table_id: TableId,
|
|
||||||
) -> Result<Option<PhysicalTableRouteValue>> {
|
|
||||||
match self.storage.get(table_id).await? {
|
|
||||||
Some(route) => {
|
|
||||||
ensure!(
|
|
||||||
route.is_physical(),
|
|
||||||
UnexpectedLogicalRouteTableSnafu {
|
|
||||||
err_msg: format!("{route:?} is a non-physical TableRouteValue.")
|
|
||||||
}
|
|
||||||
);
|
|
||||||
Ok(Some(route.into_physical_table_route()))
|
|
||||||
}
|
|
||||||
None => Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the [TableId] recursively.
|
/// Returns the [TableId] recursively.
|
||||||
///
|
///
|
||||||
/// Returns a [TableRouteNotFound](crate::error::Error::TableRouteNotFound) Error if:
|
/// Returns a [TableRouteNotFound](crate::error::Error::TableRouteNotFound) Error if:
|
||||||
@@ -569,37 +547,6 @@ impl TableRouteStorage {
|
|||||||
.transpose()
|
.transpose()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the physical `DeserializedValueWithBytes<TableRouteValue>` recursively.
|
|
||||||
///
|
|
||||||
/// Returns a [TableRouteNotFound](crate::error::Error::TableRouteNotFound) Error if:
|
|
||||||
/// - the physical table(`logical_or_physical_table_id`) does not exist
|
|
||||||
/// - the corresponding physical table of the logical table(`logical_or_physical_table_id`) does not exist.
|
|
||||||
pub async fn get_physical_table_route_with_raw_bytes(
|
|
||||||
&self,
|
|
||||||
logical_or_physical_table_id: TableId,
|
|
||||||
) -> Result<(TableId, DeserializedValueWithBytes<TableRouteValue>)> {
|
|
||||||
let table_route = self
|
|
||||||
.get_with_raw_bytes(logical_or_physical_table_id)
|
|
||||||
.await?
|
|
||||||
.context(TableRouteNotFoundSnafu {
|
|
||||||
table_id: logical_or_physical_table_id,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
match table_route.get_inner_ref() {
|
|
||||||
TableRouteValue::Physical(_) => Ok((logical_or_physical_table_id, table_route)),
|
|
||||||
TableRouteValue::Logical(x) => {
|
|
||||||
let physical_table_id = x.physical_table_id();
|
|
||||||
let physical_table_route = self
|
|
||||||
.get_with_raw_bytes(physical_table_id)
|
|
||||||
.await?
|
|
||||||
.context(TableRouteNotFoundSnafu {
|
|
||||||
table_id: physical_table_id,
|
|
||||||
})?;
|
|
||||||
Ok((physical_table_id, physical_table_route))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns batch of [`TableRouteValue`] that respects the order of `table_ids`.
|
/// Returns batch of [`TableRouteValue`] that respects the order of `table_ids`.
|
||||||
pub async fn batch_get(&self, table_ids: &[TableId]) -> Result<Vec<Option<TableRouteValue>>> {
|
pub async fn batch_get(&self, table_ids: &[TableId]) -> Result<Vec<Option<TableRouteValue>>> {
|
||||||
let mut table_routes = self.batch_get_inner(table_ids).await?;
|
let mut table_routes = self.batch_get_inner(table_ids).await?;
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ pub mod postgres;
|
|||||||
pub mod test;
|
pub mod test;
|
||||||
pub mod txn;
|
pub mod txn;
|
||||||
|
|
||||||
pub type KvBackendRef = Arc<dyn KvBackend<Error = Error> + Send + Sync>;
|
pub type KvBackendRef<E = Error> = Arc<dyn KvBackend<Error = E> + Send + Sync>;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait KvBackend: TxnService
|
pub trait KvBackend: TxnService
|
||||||
@@ -161,6 +161,9 @@ where
|
|||||||
Self::Error: ErrorExt,
|
Self::Error: ErrorExt,
|
||||||
{
|
{
|
||||||
fn reset(&self);
|
fn reset(&self);
|
||||||
|
|
||||||
|
/// Upcast as `KvBackendRef`. Since https://github.com/rust-lang/rust/issues/65991 is not yet stable.
|
||||||
|
fn as_kv_backend_ref(self: Arc<Self>) -> KvBackendRef<Self::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type ResettableKvBackendRef = Arc<dyn ResettableKvBackend<Error = Error> + Send + Sync>;
|
pub type ResettableKvBackendRef<E = Error> = Arc<dyn ResettableKvBackend<Error = E> + Send + Sync>;
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use common_telemetry::info;
|
||||||
use etcd_client::{
|
use etcd_client::{
|
||||||
Client, DeleteOptions, GetOptions, PutOptions, Txn, TxnOp, TxnOpResponse, TxnResponse,
|
Client, DeleteOptions, GetOptions, PutOptions, Txn, TxnOp, TxnOpResponse, TxnResponse,
|
||||||
};
|
};
|
||||||
@@ -55,6 +56,7 @@ impl EtcdStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_etcd_client(client: Client, max_txn_ops: usize) -> KvBackendRef {
|
pub fn with_etcd_client(client: Client, max_txn_ops: usize) -> KvBackendRef {
|
||||||
|
info!("Connected to etcd");
|
||||||
Arc::new(Self {
|
Arc::new(Self {
|
||||||
client,
|
client,
|
||||||
max_txn_ops,
|
max_txn_ops,
|
||||||
|
|||||||
@@ -16,13 +16,13 @@ use std::any::Any;
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::sync::RwLock;
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use common_error::ext::ErrorExt;
|
use common_error::ext::ErrorExt;
|
||||||
use serde::Serializer;
|
use serde::Serializer;
|
||||||
|
|
||||||
use super::ResettableKvBackend;
|
use super::{KvBackendRef, ResettableKvBackend};
|
||||||
use crate::kv_backend::txn::{Txn, TxnOp, TxnOpResponse, TxnRequest, TxnResponse};
|
use crate::kv_backend::txn::{Txn, TxnOp, TxnOpResponse, TxnRequest, TxnResponse};
|
||||||
use crate::kv_backend::{KvBackend, TxnService};
|
use crate::kv_backend::{KvBackend, TxnService};
|
||||||
use crate::metrics::METRIC_META_TXN_REQUEST;
|
use crate::metrics::METRIC_META_TXN_REQUEST;
|
||||||
@@ -311,6 +311,10 @@ impl<T: ErrorExt + Send + Sync + 'static> ResettableKvBackend for MemoryKvBacken
|
|||||||
fn reset(&self) {
|
fn reset(&self) {
|
||||||
self.clear();
|
self.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn as_kv_backend_ref(self: Arc<Self>) -> KvBackendRef<T> {
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -89,39 +89,6 @@ pub fn convert_to_region_leader_map(region_routes: &[RegionRoute]) -> HashMap<Re
|
|||||||
.collect::<HashMap<_, _>>()
|
.collect::<HashMap<_, _>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the HashMap<[RegionNumber], HashSet<DatanodeId>>
|
|
||||||
pub fn convert_to_region_peer_map(
|
|
||||||
region_routes: &[RegionRoute],
|
|
||||||
) -> HashMap<RegionNumber, HashSet<u64>> {
|
|
||||||
region_routes
|
|
||||||
.iter()
|
|
||||||
.map(|x| {
|
|
||||||
let set = x
|
|
||||||
.follower_peers
|
|
||||||
.iter()
|
|
||||||
.map(|p| p.id)
|
|
||||||
.chain(x.leader_peer.as_ref().map(|p| p.id))
|
|
||||||
.collect::<HashSet<_>>();
|
|
||||||
|
|
||||||
(x.region.id.region_number(), set)
|
|
||||||
})
|
|
||||||
.collect::<HashMap<_, _>>()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the HashMap<[RegionNumber], [LeaderState]>;
|
|
||||||
pub fn convert_to_region_leader_state_map(
|
|
||||||
region_routes: &[RegionRoute],
|
|
||||||
) -> HashMap<RegionNumber, LeaderState> {
|
|
||||||
region_routes
|
|
||||||
.iter()
|
|
||||||
.filter_map(|x| {
|
|
||||||
x.leader_state
|
|
||||||
.as_ref()
|
|
||||||
.map(|state| (x.region.id.region_number(), *state))
|
|
||||||
})
|
|
||||||
.collect::<HashMap<_, _>>()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn find_region_leader(
|
pub fn find_region_leader(
|
||||||
region_routes: &[RegionRoute],
|
region_routes: &[RegionRoute],
|
||||||
region_number: RegionNumber,
|
region_number: RegionNumber,
|
||||||
@@ -147,19 +114,6 @@ pub fn find_leader_regions(region_routes: &[RegionRoute], datanode: &Peer) -> Ve
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn extract_all_peers(region_routes: &[RegionRoute]) -> Vec<Peer> {
|
|
||||||
let mut peers = region_routes
|
|
||||||
.iter()
|
|
||||||
.flat_map(|x| x.leader_peer.iter().chain(x.follower_peers.iter()))
|
|
||||||
.collect::<HashSet<_>>()
|
|
||||||
.into_iter()
|
|
||||||
.cloned()
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
peers.sort_by_key(|x| x.id);
|
|
||||||
|
|
||||||
peers
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TableRoute {
|
impl TableRoute {
|
||||||
pub fn new(table: Table, region_routes: Vec<RegionRoute>) -> Self {
|
pub fn new(table: Table, region_routes: Vec<RegionRoute>) -> Self {
|
||||||
let region_leaders = region_routes
|
let region_leaders = region_routes
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use adapter::RecordBatchMetrics;
|
use adapter::RecordBatchMetrics;
|
||||||
use arc_swap::ArcSwapOption;
|
use arc_swap::ArcSwapOption;
|
||||||
use datafusion::physical_plan::memory::MemoryStream;
|
|
||||||
pub use datafusion::physical_plan::SendableRecordBatchStream as DfSendableRecordBatchStream;
|
pub use datafusion::physical_plan::SendableRecordBatchStream as DfSendableRecordBatchStream;
|
||||||
use datatypes::arrow::compute::SortOptions;
|
use datatypes::arrow::compute::SortOptions;
|
||||||
pub use datatypes::arrow::record_batch::RecordBatch as DfRecordBatch;
|
pub use datatypes::arrow::record_batch::RecordBatch as DfRecordBatch;
|
||||||
@@ -170,19 +169,6 @@ impl RecordBatches {
|
|||||||
index: 0,
|
index: 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_df_stream(self) -> DfSendableRecordBatchStream {
|
|
||||||
let df_record_batches = self
|
|
||||||
.batches
|
|
||||||
.into_iter()
|
|
||||||
.map(|batch| batch.into_df_record_batch())
|
|
||||||
.collect();
|
|
||||||
// unwrap safety: `MemoryStream::try_new` won't fail
|
|
||||||
Box::pin(
|
|
||||||
MemoryStream::try_new(df_record_batches, self.schema.arrow_schema().clone(), None)
|
|
||||||
.unwrap(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntoIterator for RecordBatches {
|
impl IntoIterator for RecordBatches {
|
||||||
|
|||||||
@@ -35,8 +35,6 @@ serde_json.workspace = true
|
|||||||
snafu.workspace = true
|
snafu.workspace = true
|
||||||
tempfile.workspace = true
|
tempfile.workspace = true
|
||||||
tokio.workspace = true
|
tokio.workspace = true
|
||||||
tokio-metrics = "0.3"
|
|
||||||
tokio-metrics-collector = { git = "https://github.com/MichaelScofield/tokio-metrics-collector.git", rev = "89d692d5753d28564a7aac73c6ac5aba22243ba0" }
|
|
||||||
tokio-util.workspace = true
|
tokio-util.workspace = true
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|||||||
@@ -29,10 +29,6 @@ pub fn format_utc_datetime(utc: &NaiveDateTime, pattern: &str) -> String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn system_datetime_to_utc(local: &NaiveDateTime) -> LocalResult<NaiveDateTime> {
|
|
||||||
datetime_to_utc(local, get_timezone(None))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Cast a [`NaiveDateTime`] with the given timezone.
|
/// Cast a [`NaiveDateTime`] with the given timezone.
|
||||||
pub fn datetime_to_utc(
|
pub fn datetime_to_utc(
|
||||||
datetime: &NaiveDateTime,
|
datetime: &NaiveDateTime,
|
||||||
|
|||||||
@@ -49,9 +49,9 @@ impl Default for RaftEngineConfig {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
dir: None,
|
dir: None,
|
||||||
file_size: ReadableSize::mb(256),
|
file_size: ReadableSize::mb(128),
|
||||||
purge_threshold: ReadableSize::gb(4),
|
purge_threshold: ReadableSize::gb(1),
|
||||||
purge_interval: Duration::from_secs(600),
|
purge_interval: Duration::from_secs(60),
|
||||||
read_batch_size: 128,
|
read_batch_size: 128,
|
||||||
sync_write: false,
|
sync_write: false,
|
||||||
enable_log_recycle: true,
|
enable_log_recycle: true,
|
||||||
|
|||||||
@@ -370,6 +370,51 @@ impl ConcreteDataType {
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the datatype name in postgres type system
|
||||||
|
pub fn postgres_datatype_name(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
&ConcreteDataType::Null(_) => "UNKNOWN",
|
||||||
|
&ConcreteDataType::Boolean(_) => "BOOL",
|
||||||
|
&ConcreteDataType::Int8(_) | &ConcreteDataType::UInt8(_) => "CHAR",
|
||||||
|
&ConcreteDataType::Int16(_) | &ConcreteDataType::UInt16(_) => "INT2",
|
||||||
|
&ConcreteDataType::Int32(_) | &ConcreteDataType::UInt32(_) => "INT4",
|
||||||
|
&ConcreteDataType::Int64(_) | &ConcreteDataType::UInt64(_) => "INT8",
|
||||||
|
&ConcreteDataType::Float32(_) => "FLOAT4",
|
||||||
|
&ConcreteDataType::Float64(_) => "FLOAT8",
|
||||||
|
&ConcreteDataType::Binary(_) | &ConcreteDataType::Vector(_) => "BYTEA",
|
||||||
|
&ConcreteDataType::String(_) => "VARCHAR",
|
||||||
|
&ConcreteDataType::Date(_) => "DATE",
|
||||||
|
&ConcreteDataType::DateTime(_) | &ConcreteDataType::Timestamp(_) => "TIMESTAMP",
|
||||||
|
&ConcreteDataType::Time(_) => "TIME",
|
||||||
|
&ConcreteDataType::Interval(_) => "INTERVAL",
|
||||||
|
&ConcreteDataType::Decimal128(_) => "NUMERIC",
|
||||||
|
&ConcreteDataType::Json(_) => "JSON",
|
||||||
|
ConcreteDataType::List(list) => match list.item_type() {
|
||||||
|
&ConcreteDataType::Null(_) => "UNKNOWN",
|
||||||
|
&ConcreteDataType::Boolean(_) => "_BOOL",
|
||||||
|
&ConcreteDataType::Int8(_) | &ConcreteDataType::UInt8(_) => "_CHAR",
|
||||||
|
&ConcreteDataType::Int16(_) | &ConcreteDataType::UInt16(_) => "_INT2",
|
||||||
|
&ConcreteDataType::Int32(_) | &ConcreteDataType::UInt32(_) => "_INT4",
|
||||||
|
&ConcreteDataType::Int64(_) | &ConcreteDataType::UInt64(_) => "_INT8",
|
||||||
|
&ConcreteDataType::Float32(_) => "_FLOAT4",
|
||||||
|
&ConcreteDataType::Float64(_) => "_FLOAT8",
|
||||||
|
&ConcreteDataType::Binary(_) => "_BYTEA",
|
||||||
|
&ConcreteDataType::String(_) => "_VARCHAR",
|
||||||
|
&ConcreteDataType::Date(_) => "_DATE",
|
||||||
|
&ConcreteDataType::DateTime(_) | &ConcreteDataType::Timestamp(_) => "_TIMESTAMP",
|
||||||
|
&ConcreteDataType::Time(_) => "_TIME",
|
||||||
|
&ConcreteDataType::Interval(_) => "_INTERVAL",
|
||||||
|
&ConcreteDataType::Decimal128(_) => "_NUMERIC",
|
||||||
|
&ConcreteDataType::Json(_) => "_JSON",
|
||||||
|
&ConcreteDataType::Duration(_)
|
||||||
|
| &ConcreteDataType::Dictionary(_)
|
||||||
|
| &ConcreteDataType::Vector(_)
|
||||||
|
| &ConcreteDataType::List(_) => "UNKNOWN",
|
||||||
|
},
|
||||||
|
&ConcreteDataType::Duration(_) | &ConcreteDataType::Dictionary(_) => "UNKNOWN",
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&ConcreteDataType> for ConcreteDataType {
|
impl From<&ConcreteDataType> for ConcreteDataType {
|
||||||
|
|||||||
@@ -232,6 +232,12 @@ pub enum Error {
|
|||||||
#[snafu(implicit)]
|
#[snafu(implicit)]
|
||||||
location: Location,
|
location: Location,
|
||||||
},
|
},
|
||||||
|
#[snafu(display("Invalid skipping index option: {}", msg))]
|
||||||
|
InvalidSkippingIndexOption {
|
||||||
|
msg: String,
|
||||||
|
#[snafu(implicit)]
|
||||||
|
location: Location,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ErrorExt for Error {
|
impl ErrorExt for Error {
|
||||||
@@ -252,7 +258,8 @@ impl ErrorExt for Error {
|
|||||||
| InvalidPrecisionOrScale { .. }
|
| InvalidPrecisionOrScale { .. }
|
||||||
| InvalidJson { .. }
|
| InvalidJson { .. }
|
||||||
| InvalidVector { .. }
|
| InvalidVector { .. }
|
||||||
| InvalidFulltextOption { .. } => StatusCode::InvalidArguments,
|
| InvalidFulltextOption { .. }
|
||||||
|
| InvalidSkippingIndexOption { .. } => StatusCode::InvalidArguments,
|
||||||
|
|
||||||
ValueExceedsPrecision { .. }
|
ValueExceedsPrecision { .. }
|
||||||
| CastType { .. }
|
| CastType { .. }
|
||||||
|
|||||||
@@ -28,10 +28,11 @@ use snafu::{ensure, ResultExt};
|
|||||||
use crate::error::{self, DuplicateColumnSnafu, Error, ProjectArrowSchemaSnafu, Result};
|
use crate::error::{self, DuplicateColumnSnafu, Error, ProjectArrowSchemaSnafu, Result};
|
||||||
use crate::prelude::ConcreteDataType;
|
use crate::prelude::ConcreteDataType;
|
||||||
pub use crate::schema::column_schema::{
|
pub use crate::schema::column_schema::{
|
||||||
ColumnSchema, FulltextAnalyzer, FulltextOptions, Metadata,
|
ColumnSchema, FulltextAnalyzer, FulltextOptions, Metadata, SkippingIndexOptions,
|
||||||
COLUMN_FULLTEXT_CHANGE_OPT_KEY_ENABLE, COLUMN_FULLTEXT_OPT_KEY_ANALYZER,
|
COLUMN_FULLTEXT_CHANGE_OPT_KEY_ENABLE, COLUMN_FULLTEXT_OPT_KEY_ANALYZER,
|
||||||
COLUMN_FULLTEXT_OPT_KEY_CASE_SENSITIVE, COMMENT_KEY, FULLTEXT_KEY, INVERTED_INDEX_KEY,
|
COLUMN_FULLTEXT_OPT_KEY_CASE_SENSITIVE, COLUMN_SKIPPING_INDEX_OPT_KEY_GRANULARITY,
|
||||||
TIME_INDEX_KEY,
|
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;
|
||||||
|
|||||||
@@ -39,12 +39,20 @@ const DEFAULT_CONSTRAINT_KEY: &str = "greptime:default_constraint";
|
|||||||
pub const FULLTEXT_KEY: &str = "greptime:fulltext";
|
pub const FULLTEXT_KEY: &str = "greptime:fulltext";
|
||||||
/// Key used to store whether the column has inverted index in arrow field's metadata.
|
/// Key used to store whether the column has inverted index in arrow field's metadata.
|
||||||
pub const INVERTED_INDEX_KEY: &str = "greptime:inverted_index";
|
pub const INVERTED_INDEX_KEY: &str = "greptime:inverted_index";
|
||||||
|
/// Key used to store skip options in arrow field's metadata.
|
||||||
|
pub const SKIPPING_INDEX_KEY: &str = "greptime:skipping_index";
|
||||||
|
|
||||||
/// Keys used in fulltext options
|
/// Keys used in fulltext options
|
||||||
pub const COLUMN_FULLTEXT_CHANGE_OPT_KEY_ENABLE: &str = "enable";
|
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";
|
||||||
|
|
||||||
|
/// Keys used in SKIPPING index options
|
||||||
|
pub const COLUMN_SKIPPING_INDEX_OPT_KEY_GRANULARITY: &str = "granularity";
|
||||||
|
pub const COLUMN_SKIPPING_INDEX_OPT_KEY_TYPE: &str = "type";
|
||||||
|
|
||||||
|
pub const DEFAULT_GRANULARITY: u32 = 10240;
|
||||||
|
|
||||||
/// 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 {
|
||||||
@@ -156,6 +164,10 @@ impl ColumnSchema {
|
|||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_fulltext_index_key(&self) -> bool {
|
||||||
|
self.metadata.contains_key(FULLTEXT_KEY)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn has_inverted_index_key(&self) -> bool {
|
pub fn has_inverted_index_key(&self) -> bool {
|
||||||
self.metadata.contains_key(INVERTED_INDEX_KEY)
|
self.metadata.contains_key(INVERTED_INDEX_KEY)
|
||||||
}
|
}
|
||||||
@@ -298,6 +310,34 @@ impl ColumnSchema {
|
|||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieves the skipping index options for the column.
|
||||||
|
pub fn skipping_index_options(&self) -> Result<Option<SkippingIndexOptions>> {
|
||||||
|
match self.metadata.get(SKIPPING_INDEX_KEY) {
|
||||||
|
None => Ok(None),
|
||||||
|
Some(json) => {
|
||||||
|
let options =
|
||||||
|
serde_json::from_str(json).context(error::DeserializeSnafu { json })?;
|
||||||
|
Ok(Some(options))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_skipping_options(mut self, options: SkippingIndexOptions) -> Result<Self> {
|
||||||
|
self.metadata.insert(
|
||||||
|
SKIPPING_INDEX_KEY.to_string(),
|
||||||
|
serde_json::to_string(&options).context(error::SerializeSnafu)?,
|
||||||
|
);
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_skipping_options(&mut self, options: &SkippingIndexOptions) -> Result<()> {
|
||||||
|
self.metadata.insert(
|
||||||
|
SKIPPING_INDEX_KEY.to_string(),
|
||||||
|
serde_json::to_string(options).context(error::SerializeSnafu)?,
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Column extended type set in column schema's metadata.
|
/// Column extended type set in column schema's metadata.
|
||||||
@@ -495,6 +535,76 @@ impl fmt::Display for FulltextAnalyzer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Skipping options for a column.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default, Visit, VisitMut)]
|
||||||
|
#[serde(rename_all = "kebab-case")]
|
||||||
|
pub struct SkippingIndexOptions {
|
||||||
|
/// The granularity of the skip index.
|
||||||
|
pub granularity: u32,
|
||||||
|
/// The type of the skip index.
|
||||||
|
#[serde(default)]
|
||||||
|
pub index_type: SkipIndexType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for SkippingIndexOptions {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "granularity={}", self.granularity)?;
|
||||||
|
write!(f, ", index_type={}", self.index_type)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Skip index types.
|
||||||
|
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, Visit, VisitMut)]
|
||||||
|
pub enum SkipIndexType {
|
||||||
|
#[default]
|
||||||
|
BloomFilter,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for SkipIndexType {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
SkipIndexType::BloomFilter => write!(f, "BLOOM"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<HashMap<String, String>> for SkippingIndexOptions {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(options: HashMap<String, String>) -> Result<Self> {
|
||||||
|
// Parse granularity with default value 1
|
||||||
|
let granularity = match options.get(COLUMN_SKIPPING_INDEX_OPT_KEY_GRANULARITY) {
|
||||||
|
Some(value) => value.parse::<u32>().map_err(|_| {
|
||||||
|
error::InvalidSkippingIndexOptionSnafu {
|
||||||
|
msg: format!("Invalid granularity: {value}, expected: positive integer"),
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
})?,
|
||||||
|
None => DEFAULT_GRANULARITY,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Parse index type with default value BloomFilter
|
||||||
|
let index_type = match options.get(COLUMN_SKIPPING_INDEX_OPT_KEY_TYPE) {
|
||||||
|
Some(typ) => match typ.to_ascii_uppercase().as_str() {
|
||||||
|
"BLOOM" => SkipIndexType::BloomFilter,
|
||||||
|
_ => {
|
||||||
|
return error::InvalidSkippingIndexOptionSnafu {
|
||||||
|
msg: format!("Invalid index type: {typ}, expected: 'BLOOM'"),
|
||||||
|
}
|
||||||
|
.fail();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => SkipIndexType::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(SkippingIndexOptions {
|
||||||
|
granularity,
|
||||||
|
index_type,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|||||||
@@ -38,5 +38,4 @@ tokio.workspace = true
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
api.workspace = true
|
api.workspace = true
|
||||||
common-procedure-test.workspace = true
|
|
||||||
common-test-util.workspace = true
|
common-test-util.workspace = true
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ datatypes.workspace = true
|
|||||||
enum-as-inner = "0.6.0"
|
enum-as-inner = "0.6.0"
|
||||||
enum_dispatch = "0.3"
|
enum_dispatch = "0.3"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
|
get-size-derive2 = "0.1.2"
|
||||||
|
get-size2 = "0.1.2"
|
||||||
greptime-proto.workspace = true
|
greptime-proto.workspace = true
|
||||||
# This fork of hydroflow is simply for keeping our dependency in our org, and pin the version
|
# This fork of hydroflow is simply for keeping our dependency in our org, and pin the version
|
||||||
# otherwise it is the same with upstream repo
|
# otherwise it is the same with upstream repo
|
||||||
@@ -47,7 +49,6 @@ hydroflow = { git = "https://github.com/GreptimeTeam/hydroflow.git", branch = "m
|
|||||||
itertools.workspace = true
|
itertools.workspace = true
|
||||||
lazy_static.workspace = true
|
lazy_static.workspace = true
|
||||||
meta-client.workspace = true
|
meta-client.workspace = true
|
||||||
minstant = "0.1.7"
|
|
||||||
nom = "7.1.3"
|
nom = "7.1.3"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
operator.workspace = true
|
operator.workspace = true
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ use crate::repr::{self, DiffRow, Row, BATCH_SIZE};
|
|||||||
|
|
||||||
mod flownode_impl;
|
mod flownode_impl;
|
||||||
mod parse_expr;
|
mod parse_expr;
|
||||||
|
mod stat;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
mod util;
|
mod util;
|
||||||
@@ -69,6 +70,7 @@ pub(crate) mod node_context;
|
|||||||
mod table_source;
|
mod table_source;
|
||||||
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
use crate::utils::StateReportHandler;
|
||||||
use crate::FrontendInvoker;
|
use crate::FrontendInvoker;
|
||||||
|
|
||||||
// `GREPTIME_TIMESTAMP` is not used to distinguish when table is created automatically by flow
|
// `GREPTIME_TIMESTAMP` is not used to distinguish when table is created automatically by flow
|
||||||
@@ -137,6 +139,8 @@ pub struct FlowWorkerManager {
|
|||||||
///
|
///
|
||||||
/// So that a series of event like `inserts -> flush` can be handled correctly
|
/// So that a series of event like `inserts -> flush` can be handled correctly
|
||||||
flush_lock: RwLock<()>,
|
flush_lock: RwLock<()>,
|
||||||
|
/// receive a oneshot sender to send state size report
|
||||||
|
state_report_handler: RwLock<Option<StateReportHandler>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Building FlownodeManager
|
/// Building FlownodeManager
|
||||||
@@ -170,9 +174,15 @@ impl FlowWorkerManager {
|
|||||||
tick_manager,
|
tick_manager,
|
||||||
node_id,
|
node_id,
|
||||||
flush_lock: RwLock::new(()),
|
flush_lock: RwLock::new(()),
|
||||||
|
state_report_handler: RwLock::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn with_state_report_handler(self, handler: StateReportHandler) -> Self {
|
||||||
|
*self.state_report_handler.write().await = Some(handler);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a flownode manager with one worker
|
/// Create a flownode manager with one worker
|
||||||
pub fn new_with_worker<'s>(
|
pub fn new_with_worker<'s>(
|
||||||
node_id: Option<u32>,
|
node_id: Option<u32>,
|
||||||
@@ -206,28 +216,6 @@ impl DiffRequest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// iterate through the diff row and form continuous diff row with same diff type
|
|
||||||
pub fn diff_row_to_request(rows: Vec<DiffRow>) -> Vec<DiffRequest> {
|
|
||||||
let mut reqs = Vec::new();
|
|
||||||
for (row, ts, diff) in rows {
|
|
||||||
let last = reqs.last_mut();
|
|
||||||
match (last, diff) {
|
|
||||||
(Some(DiffRequest::Insert(rows)), 1) => {
|
|
||||||
rows.push((row, ts));
|
|
||||||
}
|
|
||||||
(Some(DiffRequest::Insert(_)), -1) => reqs.push(DiffRequest::Delete(vec![(row, ts)])),
|
|
||||||
(Some(DiffRequest::Delete(rows)), -1) => {
|
|
||||||
rows.push((row, ts));
|
|
||||||
}
|
|
||||||
(Some(DiffRequest::Delete(_)), 1) => reqs.push(DiffRequest::Insert(vec![(row, ts)])),
|
|
||||||
(None, 1) => reqs.push(DiffRequest::Insert(vec![(row, ts)])),
|
|
||||||
(None, -1) => reqs.push(DiffRequest::Delete(vec![(row, ts)])),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
reqs
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn batches_to_rows_req(batches: Vec<Batch>) -> Result<Vec<DiffRequest>, Error> {
|
pub fn batches_to_rows_req(batches: Vec<Batch>) -> Result<Vec<DiffRequest>, Error> {
|
||||||
let mut reqs = Vec::new();
|
let mut reqs = Vec::new();
|
||||||
for batch in batches {
|
for batch in batches {
|
||||||
@@ -522,6 +510,27 @@ impl FlowWorkerManager {
|
|||||||
|
|
||||||
/// Flow Runtime related methods
|
/// Flow Runtime related methods
|
||||||
impl FlowWorkerManager {
|
impl FlowWorkerManager {
|
||||||
|
/// Start state report handler, which will receive a sender from HeartbeatTask to send state size report back
|
||||||
|
///
|
||||||
|
/// if heartbeat task is shutdown, this future will exit too
|
||||||
|
async fn start_state_report_handler(self: Arc<Self>) -> Option<JoinHandle<()>> {
|
||||||
|
let state_report_handler = self.state_report_handler.write().await.take();
|
||||||
|
if let Some(mut handler) = state_report_handler {
|
||||||
|
let zelf = self.clone();
|
||||||
|
let handler = common_runtime::spawn_global(async move {
|
||||||
|
while let Some(ret_handler) = handler.recv().await {
|
||||||
|
let state_report = zelf.gen_state_report().await;
|
||||||
|
ret_handler.send(state_report).unwrap_or_else(|err| {
|
||||||
|
common_telemetry::error!(err; "Send state size report error");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Some(handler)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// run in common_runtime background runtime
|
/// run in common_runtime background runtime
|
||||||
pub fn run_background(
|
pub fn run_background(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
@@ -529,6 +538,7 @@ impl FlowWorkerManager {
|
|||||||
) -> JoinHandle<()> {
|
) -> JoinHandle<()> {
|
||||||
info!("Starting flownode manager's background task");
|
info!("Starting flownode manager's background task");
|
||||||
common_runtime::spawn_global(async move {
|
common_runtime::spawn_global(async move {
|
||||||
|
let _state_report_handler = self.clone().start_state_report_handler().await;
|
||||||
self.run(shutdown).await;
|
self.run(shutdown).await;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -555,6 +565,8 @@ impl FlowWorkerManager {
|
|||||||
let default_interval = Duration::from_secs(1);
|
let default_interval = Duration::from_secs(1);
|
||||||
let mut avg_spd = 0; // rows/sec
|
let mut avg_spd = 0; // rows/sec
|
||||||
let mut since_last_run = tokio::time::Instant::now();
|
let mut since_last_run = tokio::time::Instant::now();
|
||||||
|
let run_per_trace = 10;
|
||||||
|
let mut run_cnt = 0;
|
||||||
loop {
|
loop {
|
||||||
// TODO(discord9): only run when new inputs arrive or scheduled to
|
// TODO(discord9): only run when new inputs arrive or scheduled to
|
||||||
let row_cnt = self.run_available(true).await.unwrap_or_else(|err| {
|
let row_cnt = self.run_available(true).await.unwrap_or_else(|err| {
|
||||||
@@ -597,10 +609,19 @@ impl FlowWorkerManager {
|
|||||||
} else {
|
} else {
|
||||||
(9 * avg_spd + cur_spd) / 10
|
(9 * avg_spd + cur_spd) / 10
|
||||||
};
|
};
|
||||||
trace!("avg_spd={} r/s, cur_spd={} r/s", avg_spd, cur_spd);
|
|
||||||
let new_wait = BATCH_SIZE * 1000 / avg_spd.max(1); //in ms
|
let new_wait = BATCH_SIZE * 1000 / avg_spd.max(1); //in ms
|
||||||
let new_wait = Duration::from_millis(new_wait as u64).min(default_interval);
|
let new_wait = Duration::from_millis(new_wait as u64).min(default_interval);
|
||||||
trace!("Wait for {} ms, row_cnt={}", new_wait.as_millis(), row_cnt);
|
|
||||||
|
// print trace every `run_per_trace` times so that we can see if there is something wrong
|
||||||
|
// but also not get flooded with trace
|
||||||
|
if run_cnt >= run_per_trace {
|
||||||
|
trace!("avg_spd={} r/s, cur_spd={} r/s", avg_spd, cur_spd);
|
||||||
|
trace!("Wait for {} ms, row_cnt={}", new_wait.as_millis(), row_cnt);
|
||||||
|
run_cnt = 0;
|
||||||
|
} else {
|
||||||
|
run_cnt += 1;
|
||||||
|
}
|
||||||
|
|
||||||
METRIC_FLOW_RUN_INTERVAL_MS.set(new_wait.as_millis() as i64);
|
METRIC_FLOW_RUN_INTERVAL_MS.set(new_wait.as_millis() as i64);
|
||||||
since_last_run = tokio::time::Instant::now();
|
since_last_run = tokio::time::Instant::now();
|
||||||
tokio::time::sleep(new_wait).await;
|
tokio::time::sleep(new_wait).await;
|
||||||
@@ -660,13 +681,18 @@ impl FlowWorkerManager {
|
|||||||
&self,
|
&self,
|
||||||
region_id: RegionId,
|
region_id: RegionId,
|
||||||
rows: Vec<DiffRow>,
|
rows: Vec<DiffRow>,
|
||||||
|
batch_datatypes: &[ConcreteDataType],
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let rows_len = rows.len();
|
let rows_len = rows.len();
|
||||||
let table_id = region_id.table_id();
|
let table_id = region_id.table_id();
|
||||||
let _timer = METRIC_FLOW_INSERT_ELAPSED
|
let _timer = METRIC_FLOW_INSERT_ELAPSED
|
||||||
.with_label_values(&[table_id.to_string().as_str()])
|
.with_label_values(&[table_id.to_string().as_str()])
|
||||||
.start_timer();
|
.start_timer();
|
||||||
self.node_context.read().await.send(table_id, rows).await?;
|
self.node_context
|
||||||
|
.read()
|
||||||
|
.await
|
||||||
|
.send(table_id, rows, batch_datatypes)
|
||||||
|
.await?;
|
||||||
trace!(
|
trace!(
|
||||||
"Handling write request for table_id={} with {} rows",
|
"Handling write request for table_id={} with {} rows",
|
||||||
table_id,
|
table_id,
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ use itertools::Itertools;
|
|||||||
use snafu::{OptionExt, ResultExt};
|
use snafu::{OptionExt, ResultExt};
|
||||||
use store_api::storage::RegionId;
|
use store_api::storage::RegionId;
|
||||||
|
|
||||||
|
use super::util::from_proto_to_data_type;
|
||||||
use crate::adapter::{CreateFlowArgs, FlowWorkerManager};
|
use crate::adapter::{CreateFlowArgs, FlowWorkerManager};
|
||||||
use crate::error::InternalSnafu;
|
use crate::error::InternalSnafu;
|
||||||
use crate::metrics::METRIC_FLOW_TASK_COUNT;
|
use crate::metrics::METRIC_FLOW_TASK_COUNT;
|
||||||
@@ -206,9 +207,17 @@ impl Flownode for FlowWorkerManager {
|
|||||||
})
|
})
|
||||||
.map(|r| (r, now, 1))
|
.map(|r| (r, now, 1))
|
||||||
.collect_vec();
|
.collect_vec();
|
||||||
self.handle_write_request(region_id.into(), rows)
|
let batch_datatypes = insert_schema
|
||||||
.await
|
.iter()
|
||||||
|
.map(from_proto_to_data_type)
|
||||||
|
.collect::<std::result::Result<Vec<_>, _>>()
|
||||||
.map_err(to_meta_err)?;
|
.map_err(to_meta_err)?;
|
||||||
|
self.handle_write_request(region_id.into(), rows, &batch_datatypes)
|
||||||
|
.await
|
||||||
|
.map_err(|err| {
|
||||||
|
common_telemetry::error!(err;"Failed to handle write request");
|
||||||
|
to_meta_err(err)
|
||||||
|
})?;
|
||||||
}
|
}
|
||||||
Ok(Default::default())
|
Ok(Default::default())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ use std::sync::atomic::{AtomicUsize, Ordering};
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use common_telemetry::trace;
|
use common_telemetry::trace;
|
||||||
|
use datatypes::prelude::ConcreteDataType;
|
||||||
use session::context::QueryContext;
|
use session::context::QueryContext;
|
||||||
use snafu::{OptionExt, ResultExt};
|
use snafu::{OptionExt, ResultExt};
|
||||||
use table::metadata::TableId;
|
use table::metadata::TableId;
|
||||||
@@ -131,7 +132,11 @@ impl SourceSender {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// return number of rows it actual send(including what's in the buffer)
|
/// return number of rows it actual send(including what's in the buffer)
|
||||||
pub async fn send_rows(&self, rows: Vec<DiffRow>) -> Result<usize, Error> {
|
pub async fn send_rows(
|
||||||
|
&self,
|
||||||
|
rows: Vec<DiffRow>,
|
||||||
|
batch_datatypes: &[ConcreteDataType],
|
||||||
|
) -> Result<usize, Error> {
|
||||||
METRIC_FLOW_INPUT_BUF_SIZE.add(rows.len() as _);
|
METRIC_FLOW_INPUT_BUF_SIZE.add(rows.len() as _);
|
||||||
while self.send_buf_row_cnt.load(Ordering::SeqCst) >= BATCH_SIZE * 4 {
|
while self.send_buf_row_cnt.load(Ordering::SeqCst) >= BATCH_SIZE * 4 {
|
||||||
tokio::task::yield_now().await;
|
tokio::task::yield_now().await;
|
||||||
@@ -139,8 +144,11 @@ impl SourceSender {
|
|||||||
// row count metrics is approx so relaxed order is ok
|
// row count metrics is approx so relaxed order is ok
|
||||||
self.send_buf_row_cnt
|
self.send_buf_row_cnt
|
||||||
.fetch_add(rows.len(), Ordering::SeqCst);
|
.fetch_add(rows.len(), Ordering::SeqCst);
|
||||||
let batch = Batch::try_from_rows(rows.into_iter().map(|(row, _, _)| row).collect())
|
let batch = Batch::try_from_rows_with_types(
|
||||||
.context(EvalSnafu)?;
|
rows.into_iter().map(|(row, _, _)| row).collect(),
|
||||||
|
batch_datatypes,
|
||||||
|
)
|
||||||
|
.context(EvalSnafu)?;
|
||||||
common_telemetry::trace!("Send one batch to worker with {} rows", batch.row_count());
|
common_telemetry::trace!("Send one batch to worker with {} rows", batch.row_count());
|
||||||
self.send_buf_tx.send(batch).await.map_err(|e| {
|
self.send_buf_tx.send(batch).await.map_err(|e| {
|
||||||
crate::error::InternalSnafu {
|
crate::error::InternalSnafu {
|
||||||
@@ -157,14 +165,19 @@ impl FlownodeContext {
|
|||||||
/// return number of rows it actual send(including what's in the buffer)
|
/// return number of rows it actual send(including what's in the buffer)
|
||||||
///
|
///
|
||||||
/// TODO(discord9): make this concurrent
|
/// TODO(discord9): make this concurrent
|
||||||
pub async fn send(&self, table_id: TableId, rows: Vec<DiffRow>) -> Result<usize, Error> {
|
pub async fn send(
|
||||||
|
&self,
|
||||||
|
table_id: TableId,
|
||||||
|
rows: Vec<DiffRow>,
|
||||||
|
batch_datatypes: &[ConcreteDataType],
|
||||||
|
) -> Result<usize, Error> {
|
||||||
let sender = self
|
let sender = self
|
||||||
.source_sender
|
.source_sender
|
||||||
.get(&table_id)
|
.get(&table_id)
|
||||||
.with_context(|| TableNotFoundSnafu {
|
.with_context(|| TableNotFoundSnafu {
|
||||||
name: table_id.to_string(),
|
name: table_id.to_string(),
|
||||||
})?;
|
})?;
|
||||||
sender.send_rows(rows).await
|
sender.send_rows(rows, batch_datatypes).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// flush all sender's buf
|
/// flush all sender's buf
|
||||||
|
|||||||
40
src/flow/src/adapter/stat.rs
Normal file
40
src/flow/src/adapter/stat.rs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
// 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::BTreeMap;
|
||||||
|
|
||||||
|
use common_meta::key::flow::flow_state::FlowStat;
|
||||||
|
|
||||||
|
use crate::FlowWorkerManager;
|
||||||
|
|
||||||
|
impl FlowWorkerManager {
|
||||||
|
pub async fn gen_state_report(&self) -> FlowStat {
|
||||||
|
let mut full_report = BTreeMap::new();
|
||||||
|
for worker in self.worker_handles.iter() {
|
||||||
|
let worker = worker.lock().await;
|
||||||
|
match worker.get_state_size().await {
|
||||||
|
Ok(state_size) => {
|
||||||
|
full_report.extend(state_size.into_iter().map(|(k, v)| (k as u32, v)))
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
common_telemetry::error!(err; "Get flow stat size error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FlowStat {
|
||||||
|
state_size: full_report,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,12 +16,27 @@ use api::helper::ColumnDataTypeWrapper;
|
|||||||
use api::v1::column_def::options_from_column_schema;
|
use api::v1::column_def::options_from_column_schema;
|
||||||
use api::v1::{ColumnDataType, ColumnDataTypeExtension, SemanticType};
|
use api::v1::{ColumnDataType, ColumnDataTypeExtension, SemanticType};
|
||||||
use common_error::ext::BoxedError;
|
use common_error::ext::BoxedError;
|
||||||
|
use datatypes::prelude::ConcreteDataType;
|
||||||
use datatypes::schema::ColumnSchema;
|
use datatypes::schema::ColumnSchema;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use snafu::ResultExt;
|
use snafu::ResultExt;
|
||||||
|
|
||||||
use crate::error::{Error, ExternalSnafu};
|
use crate::error::{Error, ExternalSnafu};
|
||||||
|
|
||||||
|
pub fn from_proto_to_data_type(
|
||||||
|
column_schema: &api::v1::ColumnSchema,
|
||||||
|
) -> Result<ConcreteDataType, Error> {
|
||||||
|
let wrapper = ColumnDataTypeWrapper::try_new(
|
||||||
|
column_schema.datatype,
|
||||||
|
column_schema.datatype_extension.clone(),
|
||||||
|
)
|
||||||
|
.map_err(BoxedError::new)
|
||||||
|
.context(ExternalSnafu)?;
|
||||||
|
let cdt = ConcreteDataType::from(wrapper);
|
||||||
|
|
||||||
|
Ok(cdt)
|
||||||
|
}
|
||||||
|
|
||||||
/// convert `ColumnSchema` lists to it's corresponding proto type
|
/// convert `ColumnSchema` lists to it's corresponding proto type
|
||||||
pub fn column_schemas_to_proto(
|
pub fn column_schemas_to_proto(
|
||||||
column_schemas: Vec<ColumnSchema>,
|
column_schemas: Vec<ColumnSchema>,
|
||||||
|
|||||||
@@ -197,6 +197,21 @@ impl WorkerHandle {
|
|||||||
.fail()
|
.fail()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_state_size(&self) -> Result<BTreeMap<FlowId, usize>, Error> {
|
||||||
|
let ret = self
|
||||||
|
.itc_client
|
||||||
|
.call_with_resp(Request::QueryStateSize)
|
||||||
|
.await?;
|
||||||
|
ret.into_query_state_size().map_err(|ret| {
|
||||||
|
InternalSnafu {
|
||||||
|
reason: format!(
|
||||||
|
"Flow Node/Worker itc failed, expect Response::QueryStateSize, found {ret:?}"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for WorkerHandle {
|
impl Drop for WorkerHandle {
|
||||||
@@ -361,6 +376,13 @@ impl<'s> Worker<'s> {
|
|||||||
Some(Response::ContainTask { result: ret })
|
Some(Response::ContainTask { result: ret })
|
||||||
}
|
}
|
||||||
Request::Shutdown => return Err(()),
|
Request::Shutdown => return Err(()),
|
||||||
|
Request::QueryStateSize => {
|
||||||
|
let mut ret = BTreeMap::new();
|
||||||
|
for (flow_id, task_state) in self.task_states.iter() {
|
||||||
|
ret.insert(*flow_id, task_state.state.get_state_size());
|
||||||
|
}
|
||||||
|
Some(Response::QueryStateSize { result: ret })
|
||||||
|
}
|
||||||
};
|
};
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
@@ -391,6 +413,7 @@ pub enum Request {
|
|||||||
flow_id: FlowId,
|
flow_id: FlowId,
|
||||||
},
|
},
|
||||||
Shutdown,
|
Shutdown,
|
||||||
|
QueryStateSize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, EnumAsInner)]
|
#[derive(Debug, EnumAsInner)]
|
||||||
@@ -406,6 +429,10 @@ enum Response {
|
|||||||
result: bool,
|
result: bool,
|
||||||
},
|
},
|
||||||
RunAvail,
|
RunAvail,
|
||||||
|
QueryStateSize {
|
||||||
|
/// each flow tasks' state size
|
||||||
|
result: BTreeMap<FlowId, usize>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_inter_thread_call() -> (InterThreadCallClient, InterThreadCallServer) {
|
fn create_inter_thread_call() -> (InterThreadCallClient, InterThreadCallServer) {
|
||||||
@@ -423,10 +450,12 @@ struct InterThreadCallClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl InterThreadCallClient {
|
impl InterThreadCallClient {
|
||||||
|
/// call without response
|
||||||
fn call_no_resp(&self, req: Request) -> Result<(), Error> {
|
fn call_no_resp(&self, req: Request) -> Result<(), Error> {
|
||||||
self.arg_sender.send((req, None)).map_err(from_send_error)
|
self.arg_sender.send((req, None)).map_err(from_send_error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// call with response
|
||||||
async fn call_with_resp(&self, req: Request) -> Result<Response, Error> {
|
async fn call_with_resp(&self, req: Request) -> Result<Response, Error> {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
self.arg_sender
|
self.arg_sender
|
||||||
@@ -527,6 +556,7 @@ mod test {
|
|||||||
);
|
);
|
||||||
tx.send(Batch::empty()).unwrap();
|
tx.send(Batch::empty()).unwrap();
|
||||||
handle.run_available(0, true).await.unwrap();
|
handle.run_available(0, true).await.unwrap();
|
||||||
|
assert_eq!(handle.get_state_size().await.unwrap().len(), 1);
|
||||||
assert_eq!(sink_rx.recv().await.unwrap(), Batch::empty());
|
assert_eq!(sink_rx.recv().await.unwrap(), Batch::empty());
|
||||||
drop(handle);
|
drop(handle);
|
||||||
worker_thread_handle.join().unwrap();
|
worker_thread_handle.join().unwrap();
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ use crate::compute::types::{Collection, CollectionBundle, ErrCollector, Toff};
|
|||||||
use crate::error::{Error, InvalidQuerySnafu, NotImplementedSnafu};
|
use crate::error::{Error, InvalidQuerySnafu, NotImplementedSnafu};
|
||||||
use crate::expr::{self, Batch, GlobalId, LocalId};
|
use crate::expr::{self, Batch, GlobalId, LocalId};
|
||||||
use crate::plan::{Plan, TypedPlan};
|
use crate::plan::{Plan, TypedPlan};
|
||||||
use crate::repr::{self, DiffRow};
|
use crate::repr::{self, DiffRow, RelationType};
|
||||||
|
|
||||||
mod map;
|
mod map;
|
||||||
mod reduce;
|
mod reduce;
|
||||||
@@ -124,10 +124,10 @@ impl Context<'_, '_> {
|
|||||||
/// Like `render_plan` but in Batch Mode
|
/// Like `render_plan` but in Batch Mode
|
||||||
pub fn render_plan_batch(&mut self, plan: TypedPlan) -> Result<CollectionBundle<Batch>, Error> {
|
pub fn render_plan_batch(&mut self, plan: TypedPlan) -> Result<CollectionBundle<Batch>, Error> {
|
||||||
match plan.plan {
|
match plan.plan {
|
||||||
Plan::Constant { rows } => Ok(self.render_constant_batch(rows)),
|
Plan::Constant { rows } => Ok(self.render_constant_batch(rows, &plan.schema.typ)),
|
||||||
Plan::Get { id } => self.get_batch_by_id(id),
|
Plan::Get { id } => self.get_batch_by_id(id),
|
||||||
Plan::Let { id, value, body } => self.eval_batch_let(id, value, body),
|
Plan::Let { id, value, body } => self.eval_batch_let(id, value, body),
|
||||||
Plan::Mfp { input, mfp } => self.render_mfp_batch(input, mfp),
|
Plan::Mfp { input, mfp } => self.render_mfp_batch(input, mfp, &plan.schema.typ),
|
||||||
Plan::Reduce {
|
Plan::Reduce {
|
||||||
input,
|
input,
|
||||||
key_val_plan,
|
key_val_plan,
|
||||||
@@ -172,7 +172,11 @@ impl Context<'_, '_> {
|
|||||||
/// render Constant, take all rows that have a timestamp not greater than the current time
|
/// render Constant, take all rows that have a timestamp not greater than the current time
|
||||||
/// This function is primarily used for testing
|
/// This function is primarily used for testing
|
||||||
/// Always assume input is sorted by timestamp
|
/// Always assume input is sorted by timestamp
|
||||||
pub fn render_constant_batch(&mut self, rows: Vec<DiffRow>) -> CollectionBundle<Batch> {
|
pub fn render_constant_batch(
|
||||||
|
&mut self,
|
||||||
|
rows: Vec<DiffRow>,
|
||||||
|
output_type: &RelationType,
|
||||||
|
) -> CollectionBundle<Batch> {
|
||||||
let (send_port, recv_port) = self.df.make_edge::<_, Toff<Batch>>("constant_batch");
|
let (send_port, recv_port) = self.df.make_edge::<_, Toff<Batch>>("constant_batch");
|
||||||
let mut per_time: BTreeMap<repr::Timestamp, Vec<DiffRow>> = Default::default();
|
let mut per_time: BTreeMap<repr::Timestamp, Vec<DiffRow>> = Default::default();
|
||||||
for (key, group) in &rows.into_iter().group_by(|(_row, ts, _diff)| *ts) {
|
for (key, group) in &rows.into_iter().group_by(|(_row, ts, _diff)| *ts) {
|
||||||
@@ -185,6 +189,8 @@ impl Context<'_, '_> {
|
|||||||
let scheduler_inner = scheduler.clone();
|
let scheduler_inner = scheduler.clone();
|
||||||
let err_collector = self.err_collector.clone();
|
let err_collector = self.err_collector.clone();
|
||||||
|
|
||||||
|
let output_type = output_type.clone();
|
||||||
|
|
||||||
let subgraph_id =
|
let subgraph_id =
|
||||||
self.df
|
self.df
|
||||||
.add_subgraph_source("ConstantBatch", send_port, move |_ctx, send_port| {
|
.add_subgraph_source("ConstantBatch", send_port, move |_ctx, send_port| {
|
||||||
@@ -199,7 +205,14 @@ impl Context<'_, '_> {
|
|||||||
not_great_than_now.into_iter().for_each(|(_ts, rows)| {
|
not_great_than_now.into_iter().for_each(|(_ts, rows)| {
|
||||||
err_collector.run(|| {
|
err_collector.run(|| {
|
||||||
let rows = rows.into_iter().map(|(row, _ts, _diff)| row).collect();
|
let rows = rows.into_iter().map(|(row, _ts, _diff)| row).collect();
|
||||||
let batch = Batch::try_from_rows(rows)?;
|
let batch = Batch::try_from_rows_with_types(
|
||||||
|
rows,
|
||||||
|
&output_type
|
||||||
|
.column_types
|
||||||
|
.iter()
|
||||||
|
.map(|ty| ty.scalar_type().clone())
|
||||||
|
.collect_vec(),
|
||||||
|
)?;
|
||||||
send_port.give(vec![batch]);
|
send_port.give(vec![batch]);
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ use crate::compute::types::{Arranged, Collection, CollectionBundle, ErrCollector
|
|||||||
use crate::error::{Error, PlanSnafu};
|
use crate::error::{Error, PlanSnafu};
|
||||||
use crate::expr::{Batch, EvalError, MapFilterProject, MfpPlan, ScalarExpr};
|
use crate::expr::{Batch, EvalError, MapFilterProject, MfpPlan, ScalarExpr};
|
||||||
use crate::plan::TypedPlan;
|
use crate::plan::TypedPlan;
|
||||||
use crate::repr::{self, DiffRow, KeyValDiffRow, Row};
|
use crate::repr::{self, DiffRow, KeyValDiffRow, RelationType, Row};
|
||||||
use crate::utils::ArrangeHandler;
|
use crate::utils::ArrangeHandler;
|
||||||
|
|
||||||
impl Context<'_, '_> {
|
impl Context<'_, '_> {
|
||||||
@@ -34,6 +34,7 @@ impl Context<'_, '_> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
input: Box<TypedPlan>,
|
input: Box<TypedPlan>,
|
||||||
mfp: MapFilterProject,
|
mfp: MapFilterProject,
|
||||||
|
_output_type: &RelationType,
|
||||||
) -> Result<CollectionBundle<Batch>, Error> {
|
) -> Result<CollectionBundle<Batch>, Error> {
|
||||||
let input = self.render_plan_batch(*input)?;
|
let input = self.render_plan_batch(*input)?;
|
||||||
|
|
||||||
|
|||||||
@@ -87,6 +87,8 @@ impl Context<'_, '_> {
|
|||||||
})?;
|
})?;
|
||||||
let key_val_plan = key_val_plan.clone();
|
let key_val_plan = key_val_plan.clone();
|
||||||
|
|
||||||
|
let output_type = output_type.clone();
|
||||||
|
|
||||||
let now = self.compute_state.current_time_ref();
|
let now = self.compute_state.current_time_ref();
|
||||||
|
|
||||||
let err_collector = self.err_collector.clone();
|
let err_collector = self.err_collector.clone();
|
||||||
@@ -118,6 +120,7 @@ impl Context<'_, '_> {
|
|||||||
src_data,
|
src_data,
|
||||||
&key_val_plan,
|
&key_val_plan,
|
||||||
&accum_plan,
|
&accum_plan,
|
||||||
|
&output_type,
|
||||||
SubgraphArg {
|
SubgraphArg {
|
||||||
now,
|
now,
|
||||||
err_collector: &err_collector,
|
err_collector: &err_collector,
|
||||||
@@ -354,6 +357,7 @@ fn reduce_batch_subgraph(
|
|||||||
src_data: impl IntoIterator<Item = Batch>,
|
src_data: impl IntoIterator<Item = Batch>,
|
||||||
key_val_plan: &KeyValPlan,
|
key_val_plan: &KeyValPlan,
|
||||||
accum_plan: &AccumulablePlan,
|
accum_plan: &AccumulablePlan,
|
||||||
|
output_type: &RelationType,
|
||||||
SubgraphArg {
|
SubgraphArg {
|
||||||
now,
|
now,
|
||||||
err_collector,
|
err_collector,
|
||||||
@@ -535,17 +539,13 @@ fn reduce_batch_subgraph(
|
|||||||
// this output part is not supposed to be resource intensive
|
// this output part is not supposed to be resource intensive
|
||||||
// (because for every batch there wouldn't usually be as many output row?),
|
// (because for every batch there wouldn't usually be as many output row?),
|
||||||
// so we can do some costly operation here
|
// so we can do some costly operation here
|
||||||
let output_types = all_output_dict.first_entry().map(|entry| {
|
let output_types = output_type
|
||||||
entry
|
.column_types
|
||||||
.key()
|
.iter()
|
||||||
.iter()
|
.map(|t| t.scalar_type.clone())
|
||||||
.chain(entry.get().iter())
|
.collect_vec();
|
||||||
.map(|v| v.data_type())
|
|
||||||
.collect::<Vec<ConcreteDataType>>()
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(output_types) = output_types {
|
err_collector.run(|| {
|
||||||
err_collector.run(|| {
|
|
||||||
let column_cnt = output_types.len();
|
let column_cnt = output_types.len();
|
||||||
let row_cnt = all_output_dict.len();
|
let row_cnt = all_output_dict.len();
|
||||||
|
|
||||||
@@ -585,7 +585,6 @@ fn reduce_batch_subgraph(
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// reduce subgraph, reduce the input data into a single row
|
/// reduce subgraph, reduce the input data into a single row
|
||||||
@@ -1516,7 +1515,9 @@ mod test {
|
|||||||
let mut ctx = harness_test_ctx(&mut df, &mut state);
|
let mut ctx = harness_test_ctx(&mut df, &mut state);
|
||||||
|
|
||||||
let rows = vec![
|
let rows = vec![
|
||||||
(Row::new(vec![1i64.into()]), 1, 1),
|
(Row::new(vec![Value::Null]), -1, 1),
|
||||||
|
(Row::new(vec![1i64.into()]), 0, 1),
|
||||||
|
(Row::new(vec![Value::Null]), 1, 1),
|
||||||
(Row::new(vec![2i64.into()]), 2, 1),
|
(Row::new(vec![2i64.into()]), 2, 1),
|
||||||
(Row::new(vec![3i64.into()]), 3, 1),
|
(Row::new(vec![3i64.into()]), 3, 1),
|
||||||
(Row::new(vec![1i64.into()]), 4, 1),
|
(Row::new(vec![1i64.into()]), 4, 1),
|
||||||
@@ -1558,13 +1559,15 @@ mod test {
|
|||||||
Box::new(input_plan.with_types(typ.into_unnamed())),
|
Box::new(input_plan.with_types(typ.into_unnamed())),
|
||||||
&key_val_plan,
|
&key_val_plan,
|
||||||
&reduce_plan,
|
&reduce_plan,
|
||||||
&RelationType::empty(),
|
&RelationType::new(vec![ColumnType::new(CDT::int64_datatype(), true)]),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
{
|
{
|
||||||
let now_inner = now.clone();
|
let now_inner = now.clone();
|
||||||
let expected = BTreeMap::<i64, Vec<i64>>::from([
|
let expected = BTreeMap::<i64, Vec<i64>>::from([
|
||||||
|
(-1, vec![]),
|
||||||
|
(0, vec![1i64]),
|
||||||
(1, vec![1i64]),
|
(1, vec![1i64]),
|
||||||
(2, vec![3i64]),
|
(2, vec![3i64]),
|
||||||
(3, vec![6i64]),
|
(3, vec![6i64]),
|
||||||
@@ -1581,7 +1584,11 @@ mod test {
|
|||||||
|
|
||||||
if let Some(expected) = expected.get(&now) {
|
if let Some(expected) = expected.get(&now) {
|
||||||
let batch = expected.iter().map(|v| Value::from(*v)).collect_vec();
|
let batch = expected.iter().map(|v| Value::from(*v)).collect_vec();
|
||||||
let batch = Batch::try_from_rows(vec![batch.into()]).unwrap();
|
let batch = Batch::try_from_rows_with_types(
|
||||||
|
vec![batch.into()],
|
||||||
|
&[CDT::int64_datatype()],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
assert_eq!(res.first(), Some(&batch));
|
assert_eq!(res.first(), Some(&batch));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
//! Source and Sink for the dataflow
|
//! Source and Sink for the dataflow
|
||||||
|
|
||||||
use std::collections::{BTreeMap, VecDeque};
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use common_telemetry::{debug, trace};
|
use common_telemetry::{debug, trace};
|
||||||
use hydroflow::scheduled::graph_ext::GraphExt;
|
use hydroflow::scheduled::graph_ext::GraphExt;
|
||||||
@@ -28,7 +28,7 @@ use crate::compute::types::{Arranged, Collection, CollectionBundle, Toff};
|
|||||||
use crate::error::{Error, PlanSnafu};
|
use crate::error::{Error, PlanSnafu};
|
||||||
use crate::expr::error::InternalSnafu;
|
use crate::expr::error::InternalSnafu;
|
||||||
use crate::expr::{Batch, EvalError};
|
use crate::expr::{Batch, EvalError};
|
||||||
use crate::repr::{DiffRow, Row, BROADCAST_CAP};
|
use crate::repr::{DiffRow, Row};
|
||||||
|
|
||||||
#[allow(clippy::mutable_key_type)]
|
#[allow(clippy::mutable_key_type)]
|
||||||
impl Context<'_, '_> {
|
impl Context<'_, '_> {
|
||||||
@@ -242,44 +242,4 @@ impl Context<'_, '_> {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Render a sink which send updates to broadcast channel, have internal buffer in case broadcast channel is full
|
|
||||||
pub fn render_sink(&mut self, bundle: CollectionBundle, sender: broadcast::Sender<DiffRow>) {
|
|
||||||
let CollectionBundle {
|
|
||||||
collection,
|
|
||||||
arranged: _,
|
|
||||||
} = bundle;
|
|
||||||
let mut buf = VecDeque::with_capacity(1000);
|
|
||||||
|
|
||||||
let schd = self.compute_state.get_scheduler();
|
|
||||||
let inner_schd = schd.clone();
|
|
||||||
let now = self.compute_state.current_time_ref();
|
|
||||||
|
|
||||||
let sink = self
|
|
||||||
.df
|
|
||||||
.add_subgraph_sink("Sink", collection.into_inner(), move |_ctx, recv| {
|
|
||||||
let data = recv.take_inner();
|
|
||||||
buf.extend(data.into_iter().flat_map(|i| i.into_iter()));
|
|
||||||
if sender.len() >= BROADCAST_CAP {
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
while let Some(row) = buf.pop_front() {
|
|
||||||
// if the sender is full, stop sending
|
|
||||||
if sender.len() >= BROADCAST_CAP {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// TODO(discord9): handling tokio broadcast error
|
|
||||||
let _ = sender.send(row);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if buffer is not empty, schedule the next run at next tick
|
|
||||||
// so the buffer can be drained as soon as possible
|
|
||||||
if !buf.is_empty() {
|
|
||||||
inner_schd.schedule_at(*now.borrow() + 1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
schd.set_cur_subgraph(sink);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ use std::cell::RefCell;
|
|||||||
use std::collections::{BTreeMap, VecDeque};
|
use std::collections::{BTreeMap, VecDeque};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use get_size2::GetSize;
|
||||||
use hydroflow::scheduled::graph::Hydroflow;
|
use hydroflow::scheduled::graph::Hydroflow;
|
||||||
use hydroflow::scheduled::SubgraphId;
|
use hydroflow::scheduled::SubgraphId;
|
||||||
|
|
||||||
@@ -109,6 +110,10 @@ impl DataflowState {
|
|||||||
pub fn expire_after(&self) -> Option<Timestamp> {
|
pub fn expire_after(&self) -> Option<Timestamp> {
|
||||||
self.expire_after
|
self.expire_after
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_state_size(&self) -> usize {
|
||||||
|
self.arrange_used.iter().map(|x| x.read().get_size()).sum()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|||||||
@@ -82,22 +82,6 @@ impl Arranged {
|
|||||||
writer: self.writer.clone(),
|
writer: self.writer.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Copy the full arrangement, including the future and the current updates.
|
|
||||||
///
|
|
||||||
/// Internally `Rc-ed` so it's cheap to copy
|
|
||||||
pub fn try_copy_full(&self) -> Option<Self> {
|
|
||||||
self.arrangement
|
|
||||||
.clone_full_arrange()
|
|
||||||
.map(|arrangement| Arranged {
|
|
||||||
arrangement,
|
|
||||||
readers: self.readers.clone(),
|
|
||||||
writer: self.writer.clone(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
pub fn add_reader(&self, id: SubgraphId) {
|
|
||||||
self.readers.borrow_mut().push(id)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A bundle of the various ways a collection can be represented.
|
/// A bundle of the various ways a collection can be represented.
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ mod scalar;
|
|||||||
mod signature;
|
mod signature;
|
||||||
|
|
||||||
use arrow::compute::FilterBuilder;
|
use arrow::compute::FilterBuilder;
|
||||||
use datatypes::prelude::DataType;
|
use datatypes::prelude::{ConcreteDataType, DataType};
|
||||||
use datatypes::value::Value;
|
use datatypes::value::Value;
|
||||||
use datatypes::vectors::{BooleanVector, Helper, VectorRef};
|
use datatypes::vectors::{BooleanVector, Helper, VectorRef};
|
||||||
pub(crate) use df_func::{DfScalarFunction, RawDfScalarFn};
|
pub(crate) use df_func::{DfScalarFunction, RawDfScalarFn};
|
||||||
@@ -85,16 +85,18 @@ impl Default for Batch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Batch {
|
impl Batch {
|
||||||
pub fn try_from_rows(rows: Vec<crate::repr::Row>) -> Result<Self, EvalError> {
|
/// Get batch from rows, will try best to determine data type
|
||||||
|
pub fn try_from_rows_with_types(
|
||||||
|
rows: Vec<crate::repr::Row>,
|
||||||
|
batch_datatypes: &[ConcreteDataType],
|
||||||
|
) -> Result<Self, EvalError> {
|
||||||
if rows.is_empty() {
|
if rows.is_empty() {
|
||||||
return Ok(Self::empty());
|
return Ok(Self::empty());
|
||||||
}
|
}
|
||||||
let len = rows.len();
|
let len = rows.len();
|
||||||
let mut builder = rows
|
let mut builder = batch_datatypes
|
||||||
.first()
|
|
||||||
.unwrap()
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|v| v.data_type().create_mutable_vector(len))
|
.map(|ty| ty.create_mutable_vector(len))
|
||||||
.collect_vec();
|
.collect_vec();
|
||||||
for row in rows {
|
for row in rows {
|
||||||
ensure!(
|
ensure!(
|
||||||
@@ -221,10 +223,25 @@ impl Batch {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let dts = if self.batch.is_empty() {
|
let dts = {
|
||||||
other.batch.iter().map(|v| v.data_type()).collect_vec()
|
let max_len = self.batch.len().max(other.batch.len());
|
||||||
} else {
|
let mut dts = Vec::with_capacity(max_len);
|
||||||
self.batch.iter().map(|v| v.data_type()).collect_vec()
|
for i in 0..max_len {
|
||||||
|
if let Some(v) = self.batch().get(i)
|
||||||
|
&& !v.data_type().is_null()
|
||||||
|
{
|
||||||
|
dts.push(v.data_type())
|
||||||
|
} else if let Some(v) = other.batch().get(i)
|
||||||
|
&& !v.data_type().is_null()
|
||||||
|
{
|
||||||
|
dts.push(v.data_type())
|
||||||
|
} else {
|
||||||
|
// both are null, so we will push null type
|
||||||
|
dts.push(datatypes::prelude::ConcreteDataType::null_datatype())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dts
|
||||||
};
|
};
|
||||||
|
|
||||||
let batch_builders = dts
|
let batch_builders = dts
|
||||||
|
|||||||
@@ -21,11 +21,6 @@ use datafusion_common::DataFusionError;
|
|||||||
use datatypes::data_type::ConcreteDataType;
|
use datatypes::data_type::ConcreteDataType;
|
||||||
use snafu::{Location, Snafu};
|
use snafu::{Location, Snafu};
|
||||||
|
|
||||||
fn is_send_sync() {
|
|
||||||
fn check<T: Send + Sync>() {}
|
|
||||||
check::<EvalError>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// EvalError is about errors happen on columnar evaluation
|
/// EvalError is about errors happen on columnar evaluation
|
||||||
///
|
///
|
||||||
/// TODO(discord9): add detailed location of column/operator(instead of code) to errors tp help identify related column
|
/// TODO(discord9): add detailed location of column/operator(instead of code) to errors tp help identify related column
|
||||||
|
|||||||
@@ -359,14 +359,6 @@ impl MapFilterProject {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert the `MapFilterProject` into a staged evaluation plan.
|
|
||||||
///
|
|
||||||
/// The main behavior is extract temporal predicates, which cannot be evaluated
|
|
||||||
/// using the standard machinery.
|
|
||||||
pub fn into_plan(self) -> Result<MfpPlan, Error> {
|
|
||||||
MfpPlan::create_from(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Lists input columns whose values are used in outputs.
|
/// Lists input columns whose values are used in outputs.
|
||||||
///
|
///
|
||||||
/// It is entirely appropriate to determine the demand of an instance
|
/// It is entirely appropriate to determine the demand of an instance
|
||||||
@@ -602,26 +594,6 @@ impl SafeMfpPlan {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A version of `evaluate` which produces an iterator over `Datum`
|
|
||||||
/// as output.
|
|
||||||
///
|
|
||||||
/// This version can be useful when one wants to capture the resulting
|
|
||||||
/// datums without packing and then unpacking a row.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn evaluate_iter<'a>(
|
|
||||||
&'a self,
|
|
||||||
datums: &'a mut Vec<Value>,
|
|
||||||
) -> Result<Option<impl Iterator<Item = Value> + 'a>, EvalError> {
|
|
||||||
let passed_predicates = self.evaluate_inner(datums)?;
|
|
||||||
if !passed_predicates {
|
|
||||||
Ok(None)
|
|
||||||
} else {
|
|
||||||
Ok(Some(
|
|
||||||
self.mfp.projection.iter().map(move |i| datums[*i].clone()),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Populates `values` with `self.expressions` and tests `self.predicates`.
|
/// Populates `values` with `self.expressions` and tests `self.predicates`.
|
||||||
///
|
///
|
||||||
/// This does not apply `self.projection`, which is up to the calling method.
|
/// This does not apply `self.projection`, which is up to the calling method.
|
||||||
@@ -936,20 +908,33 @@ mod test {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(ret, Row::pack(vec![Value::from(false), Value::from(true)]));
|
assert_eq!(ret, Row::pack(vec![Value::from(false), Value::from(true)]));
|
||||||
|
let ty = [
|
||||||
|
ConcreteDataType::int32_datatype(),
|
||||||
|
ConcreteDataType::int32_datatype(),
|
||||||
|
ConcreteDataType::int32_datatype(),
|
||||||
|
];
|
||||||
// batch mode
|
// batch mode
|
||||||
let mut batch = Batch::try_from_rows(vec![Row::from(vec![
|
let mut batch = Batch::try_from_rows_with_types(
|
||||||
Value::from(4),
|
vec![Row::from(vec![
|
||||||
Value::from(2),
|
Value::from(4),
|
||||||
Value::from(3),
|
Value::from(2),
|
||||||
])])
|
Value::from(3),
|
||||||
|
])],
|
||||||
|
&ty,
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let ret = safe_mfp.eval_batch_into(&mut batch).unwrap();
|
let ret = safe_mfp.eval_batch_into(&mut batch).unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ret,
|
ret,
|
||||||
Batch::try_from_rows(vec![Row::from(vec![Value::from(false), Value::from(true)])])
|
Batch::try_from_rows_with_types(
|
||||||
.unwrap()
|
vec![Row::from(vec![Value::from(false), Value::from(true)])],
|
||||||
|
&[
|
||||||
|
ConcreteDataType::boolean_datatype(),
|
||||||
|
ConcreteDataType::boolean_datatype(),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -984,7 +969,15 @@ mod test {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(ret, None);
|
assert_eq!(ret, None);
|
||||||
|
|
||||||
let mut input1_batch = Batch::try_from_rows(vec![Row::new(input1)]).unwrap();
|
let input_type = [
|
||||||
|
ConcreteDataType::int32_datatype(),
|
||||||
|
ConcreteDataType::int32_datatype(),
|
||||||
|
ConcreteDataType::int32_datatype(),
|
||||||
|
ConcreteDataType::string_datatype(),
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut input1_batch =
|
||||||
|
Batch::try_from_rows_with_types(vec![Row::new(input1)], &input_type).unwrap();
|
||||||
let ret_batch = safe_mfp.eval_batch_into(&mut input1_batch).unwrap();
|
let ret_batch = safe_mfp.eval_batch_into(&mut input1_batch).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ret_batch,
|
ret_batch,
|
||||||
@@ -1002,7 +995,8 @@ mod test {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(ret, Some(Row::pack(vec![Value::from(11)])));
|
assert_eq!(ret, Some(Row::pack(vec![Value::from(11)])));
|
||||||
|
|
||||||
let mut input2_batch = Batch::try_from_rows(vec![Row::new(input2)]).unwrap();
|
let mut input2_batch =
|
||||||
|
Batch::try_from_rows_with_types(vec![Row::new(input2)], &input_type).unwrap();
|
||||||
let ret_batch = safe_mfp.eval_batch_into(&mut input2_batch).unwrap();
|
let ret_batch = safe_mfp.eval_batch_into(&mut input2_batch).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ret_batch,
|
ret_batch,
|
||||||
@@ -1055,7 +1049,14 @@ mod test {
|
|||||||
let ret = safe_mfp.evaluate_into(&mut input1.clone(), &mut Row::empty());
|
let ret = safe_mfp.evaluate_into(&mut input1.clone(), &mut Row::empty());
|
||||||
assert!(matches!(ret, Err(EvalError::InvalidArgument { .. })));
|
assert!(matches!(ret, Err(EvalError::InvalidArgument { .. })));
|
||||||
|
|
||||||
let mut input1_batch = Batch::try_from_rows(vec![Row::new(input1)]).unwrap();
|
let input_type = [
|
||||||
|
ConcreteDataType::int64_datatype(),
|
||||||
|
ConcreteDataType::int32_datatype(),
|
||||||
|
ConcreteDataType::int32_datatype(),
|
||||||
|
ConcreteDataType::int32_datatype(),
|
||||||
|
];
|
||||||
|
let mut input1_batch =
|
||||||
|
Batch::try_from_rows_with_types(vec![Row::new(input1)], &input_type).unwrap();
|
||||||
let ret_batch = safe_mfp.eval_batch_into(&mut input1_batch);
|
let ret_batch = safe_mfp.eval_batch_into(&mut input1_batch);
|
||||||
assert!(matches!(ret_batch, Err(EvalError::InvalidArgument { .. })));
|
assert!(matches!(ret_batch, Err(EvalError::InvalidArgument { .. })));
|
||||||
|
|
||||||
@@ -1065,7 +1066,13 @@ mod test {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(ret, Some(Row::new(input2.clone())));
|
assert_eq!(ret, Some(Row::new(input2.clone())));
|
||||||
|
|
||||||
let input2_batch = Batch::try_from_rows(vec![Row::new(input2)]).unwrap();
|
let input_type = [
|
||||||
|
ConcreteDataType::int64_datatype(),
|
||||||
|
ConcreteDataType::int32_datatype(),
|
||||||
|
ConcreteDataType::int32_datatype(),
|
||||||
|
];
|
||||||
|
let input2_batch =
|
||||||
|
Batch::try_from_rows_with_types(vec![Row::new(input2)], &input_type).unwrap();
|
||||||
let ret_batch = safe_mfp.eval_batch_into(&mut input2_batch.clone()).unwrap();
|
let ret_batch = safe_mfp.eval_batch_into(&mut input2_batch.clone()).unwrap();
|
||||||
assert_eq!(ret_batch, input2_batch);
|
assert_eq!(ret_batch, input2_batch);
|
||||||
|
|
||||||
@@ -1075,7 +1082,8 @@ mod test {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(ret, None);
|
assert_eq!(ret, None);
|
||||||
|
|
||||||
let input3_batch = Batch::try_from_rows(vec![Row::new(input3)]).unwrap();
|
let input3_batch =
|
||||||
|
Batch::try_from_rows_with_types(vec![Row::new(input3)], &input_type).unwrap();
|
||||||
let ret_batch = safe_mfp.eval_batch_into(&mut input3_batch.clone()).unwrap();
|
let ret_batch = safe_mfp.eval_batch_into(&mut input3_batch.clone()).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ret_batch,
|
ret_batch,
|
||||||
@@ -1111,7 +1119,13 @@ mod test {
|
|||||||
let ret = safe_mfp.evaluate_into(&mut input1.clone(), &mut Row::empty());
|
let ret = safe_mfp.evaluate_into(&mut input1.clone(), &mut Row::empty());
|
||||||
assert_eq!(ret.unwrap(), Some(Row::new(vec![Value::from(false)])));
|
assert_eq!(ret.unwrap(), Some(Row::new(vec![Value::from(false)])));
|
||||||
|
|
||||||
let mut input1_batch = Batch::try_from_rows(vec![Row::new(input1)]).unwrap();
|
let input_type = [
|
||||||
|
ConcreteDataType::int32_datatype(),
|
||||||
|
ConcreteDataType::int32_datatype(),
|
||||||
|
ConcreteDataType::int32_datatype(),
|
||||||
|
];
|
||||||
|
let mut input1_batch =
|
||||||
|
Batch::try_from_rows_with_types(vec![Row::new(input1)], &input_type).unwrap();
|
||||||
let ret_batch = safe_mfp.eval_batch_into(&mut input1_batch).unwrap();
|
let ret_batch = safe_mfp.eval_batch_into(&mut input1_batch).unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ use common_meta::heartbeat::handler::{
|
|||||||
};
|
};
|
||||||
use common_meta::heartbeat::mailbox::{HeartbeatMailbox, MailboxRef, OutgoingMessage};
|
use common_meta::heartbeat::mailbox::{HeartbeatMailbox, MailboxRef, OutgoingMessage};
|
||||||
use common_meta::heartbeat::utils::outgoing_message_to_mailbox_message;
|
use common_meta::heartbeat::utils::outgoing_message_to_mailbox_message;
|
||||||
|
use common_meta::key::flow::flow_state::FlowStat;
|
||||||
use common_telemetry::{debug, error, info, warn};
|
use common_telemetry::{debug, error, info, warn};
|
||||||
use greptime_proto::v1::meta::NodeInfo;
|
use greptime_proto::v1::meta::NodeInfo;
|
||||||
use meta_client::client::{HeartbeatSender, HeartbeatStream, MetaClient};
|
use meta_client::client::{HeartbeatSender, HeartbeatStream, MetaClient};
|
||||||
@@ -34,8 +35,27 @@ use tokio::sync::mpsc;
|
|||||||
use tokio::time::Duration;
|
use tokio::time::Duration;
|
||||||
|
|
||||||
use crate::error::ExternalSnafu;
|
use crate::error::ExternalSnafu;
|
||||||
|
use crate::utils::SizeReportSender;
|
||||||
use crate::{Error, FlownodeOptions};
|
use crate::{Error, FlownodeOptions};
|
||||||
|
|
||||||
|
async fn query_flow_state(
|
||||||
|
query_stat_size: &Option<SizeReportSender>,
|
||||||
|
timeout: Duration,
|
||||||
|
) -> Option<FlowStat> {
|
||||||
|
if let Some(report_requester) = query_stat_size.as_ref() {
|
||||||
|
let ret = report_requester.query(timeout).await;
|
||||||
|
match ret {
|
||||||
|
Ok(latest) => Some(latest),
|
||||||
|
Err(err) => {
|
||||||
|
error!(err; "Failed to get query stat size");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The flownode heartbeat task which sending `[HeartbeatRequest]` to Metasrv periodically in background.
|
/// The flownode heartbeat task which sending `[HeartbeatRequest]` to Metasrv periodically in background.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct HeartbeatTask {
|
pub struct HeartbeatTask {
|
||||||
@@ -47,9 +67,14 @@ pub struct HeartbeatTask {
|
|||||||
resp_handler_executor: HeartbeatResponseHandlerExecutorRef,
|
resp_handler_executor: HeartbeatResponseHandlerExecutorRef,
|
||||||
start_time_ms: u64,
|
start_time_ms: u64,
|
||||||
running: Arc<AtomicBool>,
|
running: Arc<AtomicBool>,
|
||||||
|
query_stat_size: Option<SizeReportSender>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HeartbeatTask {
|
impl HeartbeatTask {
|
||||||
|
pub fn with_query_stat_size(mut self, query_stat_size: SizeReportSender) -> Self {
|
||||||
|
self.query_stat_size = Some(query_stat_size);
|
||||||
|
self
|
||||||
|
}
|
||||||
pub fn new(
|
pub fn new(
|
||||||
opts: &FlownodeOptions,
|
opts: &FlownodeOptions,
|
||||||
meta_client: Arc<MetaClient>,
|
meta_client: Arc<MetaClient>,
|
||||||
@@ -65,6 +90,7 @@ impl HeartbeatTask {
|
|||||||
resp_handler_executor,
|
resp_handler_executor,
|
||||||
start_time_ms: common_time::util::current_time_millis() as u64,
|
start_time_ms: common_time::util::current_time_millis() as u64,
|
||||||
running: Arc::new(AtomicBool::new(false)),
|
running: Arc::new(AtomicBool::new(false)),
|
||||||
|
query_stat_size: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,6 +138,7 @@ impl HeartbeatTask {
|
|||||||
message: Option<OutgoingMessage>,
|
message: Option<OutgoingMessage>,
|
||||||
peer: Option<Peer>,
|
peer: Option<Peer>,
|
||||||
start_time_ms: u64,
|
start_time_ms: u64,
|
||||||
|
latest_report: &Option<FlowStat>,
|
||||||
) -> Option<HeartbeatRequest> {
|
) -> Option<HeartbeatRequest> {
|
||||||
let mailbox_message = match message.map(outgoing_message_to_mailbox_message) {
|
let mailbox_message = match message.map(outgoing_message_to_mailbox_message) {
|
||||||
Some(Ok(message)) => Some(message),
|
Some(Ok(message)) => Some(message),
|
||||||
@@ -121,11 +148,22 @@ impl HeartbeatTask {
|
|||||||
}
|
}
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
|
let flow_stat = latest_report
|
||||||
|
.as_ref()
|
||||||
|
.map(|report| {
|
||||||
|
report
|
||||||
|
.state_size
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| (*k, *v as u64))
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.map(|f| api::v1::meta::FlowStat { flow_stat_size: f });
|
||||||
|
|
||||||
Some(HeartbeatRequest {
|
Some(HeartbeatRequest {
|
||||||
mailbox_message,
|
mailbox_message,
|
||||||
peer,
|
peer,
|
||||||
info: Self::build_node_info(start_time_ms),
|
info: Self::build_node_info(start_time_ms),
|
||||||
|
flow_stat,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -151,24 +189,27 @@ impl HeartbeatTask {
|
|||||||
addr: self.peer_addr.clone(),
|
addr: self.peer_addr.clone(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let query_stat_size = self.query_stat_size.clone();
|
||||||
|
|
||||||
common_runtime::spawn_hb(async move {
|
common_runtime::spawn_hb(async move {
|
||||||
// note that using interval will cause it to first immediately send
|
// note that using interval will cause it to first immediately send
|
||||||
// a heartbeat
|
// a heartbeat
|
||||||
let mut interval = tokio::time::interval(report_interval);
|
let mut interval = tokio::time::interval(report_interval);
|
||||||
interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
|
interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
|
||||||
|
let mut latest_report = None;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let req = tokio::select! {
|
let req = tokio::select! {
|
||||||
message = outgoing_rx.recv() => {
|
message = outgoing_rx.recv() => {
|
||||||
if let Some(message) = message {
|
if let Some(message) = message {
|
||||||
Self::create_heartbeat_request(Some(message), self_peer.clone(), start_time_ms)
|
Self::create_heartbeat_request(Some(message), self_peer.clone(), start_time_ms, &latest_report)
|
||||||
} else {
|
} else {
|
||||||
// Receives None that means Sender was dropped, we need to break the current loop
|
// Receives None that means Sender was dropped, we need to break the current loop
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ = interval.tick() => {
|
_ = interval.tick() => {
|
||||||
Self::create_heartbeat_request(None, self_peer.clone(), start_time_ms)
|
Self::create_heartbeat_request(None, self_peer.clone(), start_time_ms, &latest_report)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -180,6 +221,10 @@ impl HeartbeatTask {
|
|||||||
debug!("Send a heartbeat request to metasrv, content: {:?}", req);
|
debug!("Send a heartbeat request to metasrv, content: {:?}", req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// after sending heartbeat, try to get the latest report
|
||||||
|
// TODO(discord9): consider a better place to update the size report
|
||||||
|
// set the timeout to half of the report interval so that it wouldn't delay heartbeat if something went horribly wrong
|
||||||
|
latest_report = query_flow_state(&query_stat_size, report_interval / 2).await;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,10 +18,8 @@
|
|||||||
mod join;
|
mod join;
|
||||||
mod reduce;
|
mod reduce;
|
||||||
|
|
||||||
use std::collections::BTreeSet;
|
|
||||||
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::expr::{GlobalId, Id, LocalId, MapFilterProject, SafeMfpPlan, TypedExpr};
|
use crate::expr::{Id, LocalId, MapFilterProject, SafeMfpPlan, TypedExpr};
|
||||||
use crate::plan::join::JoinPlan;
|
use crate::plan::join::JoinPlan;
|
||||||
pub(crate) use crate::plan::reduce::{AccumulablePlan, AggrWithIndex, KeyValPlan, ReducePlan};
|
pub(crate) use crate::plan::reduce::{AccumulablePlan, AggrWithIndex, KeyValPlan, ReducePlan};
|
||||||
use crate::repr::{DiffRow, RelationDesc};
|
use crate::repr::{DiffRow, RelationDesc};
|
||||||
@@ -186,48 +184,6 @@ pub enum Plan {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Plan {
|
|
||||||
/// Find all the used collection in the plan
|
|
||||||
pub fn find_used_collection(&self) -> BTreeSet<GlobalId> {
|
|
||||||
fn recur_find_use(plan: &Plan, used: &mut BTreeSet<GlobalId>) {
|
|
||||||
match plan {
|
|
||||||
Plan::Get { id } => {
|
|
||||||
match id {
|
|
||||||
Id::Local(_) => (),
|
|
||||||
Id::Global(g) => {
|
|
||||||
used.insert(*g);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Plan::Let { value, body, .. } => {
|
|
||||||
recur_find_use(&value.plan, used);
|
|
||||||
recur_find_use(&body.plan, used);
|
|
||||||
}
|
|
||||||
Plan::Mfp { input, .. } => {
|
|
||||||
recur_find_use(&input.plan, used);
|
|
||||||
}
|
|
||||||
Plan::Reduce { input, .. } => {
|
|
||||||
recur_find_use(&input.plan, used);
|
|
||||||
}
|
|
||||||
Plan::Join { inputs, .. } => {
|
|
||||||
for input in inputs {
|
|
||||||
recur_find_use(&input.plan, used);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Plan::Union { inputs, .. } => {
|
|
||||||
for input in inputs {
|
|
||||||
recur_find_use(&input.plan, used);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut ret = Default::default();
|
|
||||||
recur_find_use(self, &mut ret);
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Plan {
|
impl Plan {
|
||||||
pub fn with_types(self, schema: RelationDesc) -> TypedPlan {
|
pub fn with_types(self, schema: RelationDesc) -> TypedPlan {
|
||||||
TypedPlan { schema, plan: self }
|
TypedPlan { schema, plan: self }
|
||||||
|
|||||||
@@ -22,12 +22,14 @@ use api::v1::Row as ProtoRow;
|
|||||||
use datatypes::data_type::ConcreteDataType;
|
use datatypes::data_type::ConcreteDataType;
|
||||||
use datatypes::types::cast;
|
use datatypes::types::cast;
|
||||||
use datatypes::value::Value;
|
use datatypes::value::Value;
|
||||||
|
use get_size2::GetSize;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
pub(crate) use relation::{ColumnType, Key, RelationDesc, RelationType};
|
pub(crate) use relation::{ColumnType, Key, RelationDesc, RelationType};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use snafu::ResultExt;
|
use snafu::ResultExt;
|
||||||
|
|
||||||
use crate::expr::error::{CastValueSnafu, EvalError, InvalidArgumentSnafu};
|
use crate::expr::error::{CastValueSnafu, EvalError, InvalidArgumentSnafu};
|
||||||
|
use crate::utils::get_value_heap_size;
|
||||||
|
|
||||||
/// System-wide Record count difference type. Useful for capture data change
|
/// System-wide Record count difference type. Useful for capture data change
|
||||||
///
|
///
|
||||||
@@ -105,6 +107,12 @@ pub struct Row {
|
|||||||
pub inner: Vec<Value>,
|
pub inner: Vec<Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl GetSize for Row {
|
||||||
|
fn get_heap_size(&self) -> usize {
|
||||||
|
self.inner.iter().map(get_value_heap_size).sum()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Row {
|
impl Row {
|
||||||
/// Create an empty row
|
/// Create an empty row
|
||||||
pub fn empty() -> Self {
|
pub fn empty() -> Self {
|
||||||
|
|||||||
@@ -46,14 +46,6 @@ impl Key {
|
|||||||
self.column_indices.push(col);
|
self.column_indices.push(col);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add columns to Key
|
|
||||||
pub fn add_cols<I>(&mut self, cols: I)
|
|
||||||
where
|
|
||||||
I: IntoIterator<Item = usize>,
|
|
||||||
{
|
|
||||||
self.column_indices.extend(cols);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove a column from Key
|
/// Remove a column from Key
|
||||||
pub fn remove_col(&mut self, col: usize) {
|
pub fn remove_col(&mut self, col: usize) {
|
||||||
self.column_indices.retain(|&r| r != col);
|
self.column_indices.retain(|&r| r != col);
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ use crate::error::{
|
|||||||
};
|
};
|
||||||
use crate::heartbeat::HeartbeatTask;
|
use crate::heartbeat::HeartbeatTask;
|
||||||
use crate::transform::register_function_to_query_engine;
|
use crate::transform::register_function_to_query_engine;
|
||||||
|
use crate::utils::{SizeReportSender, StateReportHandler};
|
||||||
use crate::{Error, FlowWorkerManager, FlownodeOptions};
|
use crate::{Error, FlowWorkerManager, FlownodeOptions};
|
||||||
|
|
||||||
pub const FLOW_NODE_SERVER_NAME: &str = "FLOW_NODE_SERVER";
|
pub const FLOW_NODE_SERVER_NAME: &str = "FLOW_NODE_SERVER";
|
||||||
@@ -236,6 +237,8 @@ pub struct FlownodeBuilder {
|
|||||||
catalog_manager: CatalogManagerRef,
|
catalog_manager: CatalogManagerRef,
|
||||||
flow_metadata_manager: FlowMetadataManagerRef,
|
flow_metadata_manager: FlowMetadataManagerRef,
|
||||||
heartbeat_task: Option<HeartbeatTask>,
|
heartbeat_task: Option<HeartbeatTask>,
|
||||||
|
/// receive a oneshot sender to send state size report
|
||||||
|
state_report_handler: Option<StateReportHandler>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FlownodeBuilder {
|
impl FlownodeBuilder {
|
||||||
@@ -254,17 +257,20 @@ impl FlownodeBuilder {
|
|||||||
catalog_manager,
|
catalog_manager,
|
||||||
flow_metadata_manager,
|
flow_metadata_manager,
|
||||||
heartbeat_task: None,
|
heartbeat_task: None,
|
||||||
|
state_report_handler: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_heartbeat_task(self, heartbeat_task: HeartbeatTask) -> Self {
|
pub fn with_heartbeat_task(self, heartbeat_task: HeartbeatTask) -> Self {
|
||||||
|
let (sender, receiver) = SizeReportSender::new();
|
||||||
Self {
|
Self {
|
||||||
heartbeat_task: Some(heartbeat_task),
|
heartbeat_task: Some(heartbeat_task.with_query_stat_size(sender)),
|
||||||
|
state_report_handler: Some(receiver),
|
||||||
..self
|
..self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn build(self) -> Result<FlownodeInstance, Error> {
|
pub async fn build(mut self) -> Result<FlownodeInstance, Error> {
|
||||||
// TODO(discord9): does this query engine need those?
|
// TODO(discord9): does this query engine need those?
|
||||||
let query_engine_factory = QueryEngineFactory::new_with_plugins(
|
let query_engine_factory = QueryEngineFactory::new_with_plugins(
|
||||||
// query engine in flownode is only used for translate plan with resolved table source.
|
// query engine in flownode is only used for translate plan with resolved table source.
|
||||||
@@ -383,7 +389,7 @@ impl FlownodeBuilder {
|
|||||||
/// build [`FlowWorkerManager`], note this doesn't take ownership of `self`,
|
/// build [`FlowWorkerManager`], note this doesn't take ownership of `self`,
|
||||||
/// nor does it actually start running the worker.
|
/// nor does it actually start running the worker.
|
||||||
async fn build_manager(
|
async fn build_manager(
|
||||||
&self,
|
&mut self,
|
||||||
query_engine: Arc<dyn QueryEngine>,
|
query_engine: Arc<dyn QueryEngine>,
|
||||||
) -> Result<FlowWorkerManager, Error> {
|
) -> Result<FlowWorkerManager, Error> {
|
||||||
let table_meta = self.table_meta.clone();
|
let table_meta = self.table_meta.clone();
|
||||||
@@ -402,12 +408,15 @@ impl FlownodeBuilder {
|
|||||||
info!("Flow Worker started in new thread");
|
info!("Flow Worker started in new thread");
|
||||||
worker.run();
|
worker.run();
|
||||||
});
|
});
|
||||||
let man = rx.await.map_err(|_e| {
|
let mut man = rx.await.map_err(|_e| {
|
||||||
UnexpectedSnafu {
|
UnexpectedSnafu {
|
||||||
reason: "sender is dropped, failed to create flow node manager",
|
reason: "sender is dropped, failed to create flow node manager",
|
||||||
}
|
}
|
||||||
.build()
|
.build()
|
||||||
})?;
|
})?;
|
||||||
|
if let Some(handler) = self.state_report_handler.take() {
|
||||||
|
man = man.with_state_report_handler(handler).await;
|
||||||
|
}
|
||||||
info!("Flow Node Manager started");
|
info!("Flow Node Manager started");
|
||||||
Ok(man)
|
Ok(man)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,16 +18,73 @@ use std::collections::{BTreeMap, BTreeSet};
|
|||||||
use std::ops::Bound;
|
use std::ops::Bound;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use common_meta::key::flow::flow_state::FlowStat;
|
||||||
use common_telemetry::trace;
|
use common_telemetry::trace;
|
||||||
|
use datatypes::value::Value;
|
||||||
|
use get_size2::GetSize;
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::{mpsc, oneshot, RwLock};
|
||||||
|
use tokio::time::Instant;
|
||||||
|
|
||||||
|
use crate::error::InternalSnafu;
|
||||||
use crate::expr::{EvalError, ScalarExpr};
|
use crate::expr::{EvalError, ScalarExpr};
|
||||||
use crate::repr::{value_to_internal_ts, DiffRow, Duration, KeyValDiffRow, Row, Timestamp};
|
use crate::repr::{value_to_internal_ts, DiffRow, Duration, KeyValDiffRow, Row, Timestamp};
|
||||||
|
|
||||||
/// A batch of updates, arranged by key
|
/// A batch of updates, arranged by key
|
||||||
pub type Batch = BTreeMap<Row, SmallVec<[DiffRow; 2]>>;
|
pub type Batch = BTreeMap<Row, SmallVec<[DiffRow; 2]>>;
|
||||||
|
|
||||||
|
/// Get a estimate of heap size of a value
|
||||||
|
pub fn get_value_heap_size(v: &Value) -> usize {
|
||||||
|
match v {
|
||||||
|
Value::Binary(bin) => bin.len(),
|
||||||
|
Value::String(s) => s.len(),
|
||||||
|
Value::List(list) => list.items().iter().map(get_value_heap_size).sum(),
|
||||||
|
_ => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SizeReportSender {
|
||||||
|
inner: mpsc::Sender<oneshot::Sender<FlowStat>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SizeReportSender {
|
||||||
|
pub fn new() -> (Self, StateReportHandler) {
|
||||||
|
let (tx, rx) = mpsc::channel(1);
|
||||||
|
let zelf = Self { inner: tx };
|
||||||
|
(zelf, rx)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Query the size report, will timeout after one second if no response
|
||||||
|
pub async fn query(&self, timeout: std::time::Duration) -> crate::Result<FlowStat> {
|
||||||
|
let (tx, rx) = oneshot::channel();
|
||||||
|
self.inner.send(tx).await.map_err(|_| {
|
||||||
|
InternalSnafu {
|
||||||
|
reason: "failed to send size report request due to receiver dropped",
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
})?;
|
||||||
|
let timeout = tokio::time::timeout(timeout, rx);
|
||||||
|
timeout
|
||||||
|
.await
|
||||||
|
.map_err(|_elapsed| {
|
||||||
|
InternalSnafu {
|
||||||
|
reason: "failed to receive size report after one second timeout",
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
})?
|
||||||
|
.map_err(|_| {
|
||||||
|
InternalSnafu {
|
||||||
|
reason: "failed to receive size report due to sender dropped",
|
||||||
|
}
|
||||||
|
.build()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle the size report request, and send the report back
|
||||||
|
pub type StateReportHandler = mpsc::Receiver<oneshot::Sender<FlowStat>>;
|
||||||
|
|
||||||
/// A spine of batches, arranged by timestamp
|
/// A spine of batches, arranged by timestamp
|
||||||
/// TODO(discord9): consider internally index by key, value, and timestamp for faster lookup
|
/// TODO(discord9): consider internally index by key, value, and timestamp for faster lookup
|
||||||
pub type Spine = BTreeMap<Timestamp, Batch>;
|
pub type Spine = BTreeMap<Timestamp, Batch>;
|
||||||
@@ -49,6 +106,24 @@ pub struct KeyExpiryManager {
|
|||||||
event_timestamp_from_row: Option<ScalarExpr>,
|
event_timestamp_from_row: Option<ScalarExpr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl GetSize for KeyExpiryManager {
|
||||||
|
fn get_heap_size(&self) -> usize {
|
||||||
|
let row_size = if let Some(row_size) = &self
|
||||||
|
.event_ts_to_key
|
||||||
|
.first_key_value()
|
||||||
|
.map(|(_, v)| v.first().get_heap_size())
|
||||||
|
{
|
||||||
|
*row_size
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
self.event_ts_to_key
|
||||||
|
.values()
|
||||||
|
.map(|v| v.len() * row_size + std::mem::size_of::<i64>())
|
||||||
|
.sum::<usize>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl KeyExpiryManager {
|
impl KeyExpiryManager {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
key_expiration_duration: Option<Duration>,
|
key_expiration_duration: Option<Duration>,
|
||||||
@@ -154,7 +229,7 @@ impl KeyExpiryManager {
|
|||||||
///
|
///
|
||||||
/// Note the two way arrow between reduce operator and arrange, it's because reduce operator need to query existing state
|
/// Note the two way arrow between reduce operator and arrange, it's because reduce operator need to query existing state
|
||||||
/// and also need to update existing state.
|
/// and also need to update existing state.
|
||||||
#[derive(Debug, Clone, Default, Eq, PartialEq, Ord, PartialOrd)]
|
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
pub struct Arrangement {
|
pub struct Arrangement {
|
||||||
/// A name or identifier for the arrangement which can be used for debugging or logging purposes.
|
/// A name or identifier for the arrangement which can be used for debugging or logging purposes.
|
||||||
/// This field is not critical to the functionality but aids in monitoring and management of arrangements.
|
/// This field is not critical to the functionality but aids in monitoring and management of arrangements.
|
||||||
@@ -196,6 +271,61 @@ pub struct Arrangement {
|
|||||||
|
|
||||||
/// The time that the last compaction happened, also known as the current time.
|
/// The time that the last compaction happened, also known as the current time.
|
||||||
last_compaction_time: Option<Timestamp>,
|
last_compaction_time: Option<Timestamp>,
|
||||||
|
|
||||||
|
/// Estimated size of the arrangement in heap size.
|
||||||
|
estimated_size: usize,
|
||||||
|
last_size_update: Instant,
|
||||||
|
size_update_interval: tokio::time::Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Arrangement {
|
||||||
|
fn compute_size(&self) -> usize {
|
||||||
|
self.spine
|
||||||
|
.values()
|
||||||
|
.map(|v| {
|
||||||
|
let per_entry_size = v
|
||||||
|
.first_key_value()
|
||||||
|
.map(|(k, v)| {
|
||||||
|
k.get_heap_size()
|
||||||
|
+ v.len() * v.first().map(|r| r.get_heap_size()).unwrap_or(0)
|
||||||
|
})
|
||||||
|
.unwrap_or(0);
|
||||||
|
std::mem::size_of::<i64>() + v.len() * per_entry_size
|
||||||
|
})
|
||||||
|
.sum::<usize>()
|
||||||
|
+ self.expire_state.get_heap_size()
|
||||||
|
+ self.name.get_heap_size()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_and_fetch_size(&mut self) -> usize {
|
||||||
|
if self.last_size_update.elapsed() > self.size_update_interval {
|
||||||
|
self.estimated_size = self.compute_size();
|
||||||
|
self.last_size_update = Instant::now();
|
||||||
|
}
|
||||||
|
self.estimated_size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GetSize for Arrangement {
|
||||||
|
fn get_heap_size(&self) -> usize {
|
||||||
|
self.estimated_size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Arrangement {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
spine: Default::default(),
|
||||||
|
full_arrangement: false,
|
||||||
|
is_written: false,
|
||||||
|
expire_state: None,
|
||||||
|
last_compaction_time: None,
|
||||||
|
name: Vec::new(),
|
||||||
|
estimated_size: 0,
|
||||||
|
last_size_update: Instant::now(),
|
||||||
|
size_update_interval: tokio::time::Duration::from_secs(3),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Arrangement {
|
impl Arrangement {
|
||||||
@@ -207,6 +337,9 @@ impl Arrangement {
|
|||||||
expire_state: None,
|
expire_state: None,
|
||||||
last_compaction_time: None,
|
last_compaction_time: None,
|
||||||
name,
|
name,
|
||||||
|
estimated_size: 0,
|
||||||
|
last_size_update: Instant::now(),
|
||||||
|
size_update_interval: tokio::time::Duration::from_secs(3),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,6 +402,7 @@ impl Arrangement {
|
|||||||
// without changing the order of updates within same tick
|
// without changing the order of updates within same tick
|
||||||
key_updates.sort_by_key(|(_val, ts, _diff)| *ts);
|
key_updates.sort_by_key(|(_val, ts, _diff)| *ts);
|
||||||
}
|
}
|
||||||
|
self.update_and_fetch_size();
|
||||||
Ok(max_expired_by)
|
Ok(max_expired_by)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -390,6 +524,7 @@ impl Arrangement {
|
|||||||
|
|
||||||
// insert the compacted batch into spine with key being `now`
|
// insert the compacted batch into spine with key being `now`
|
||||||
self.spine.insert(now, compacting_batch);
|
self.spine.insert(now, compacting_batch);
|
||||||
|
self.update_and_fetch_size();
|
||||||
Ok(max_expired_by)
|
Ok(max_expired_by)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ common-catalog.workspace = true
|
|||||||
common-config.workspace = true
|
common-config.workspace = true
|
||||||
common-datasource.workspace = true
|
common-datasource.workspace = true
|
||||||
common-error.workspace = true
|
common-error.workspace = true
|
||||||
common-frontend.workspace = true
|
|
||||||
common-function.workspace = true
|
common-function.workspace = true
|
||||||
common-grpc.workspace = true
|
common-grpc.workspace = true
|
||||||
common-macro.workspace = true
|
common-macro.workspace = true
|
||||||
@@ -71,7 +70,6 @@ common-test-util.workspace = true
|
|||||||
datanode.workspace = true
|
datanode.workspace = true
|
||||||
datatypes.workspace = true
|
datatypes.workspace = true
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
meta-srv = { workspace = true, features = ["mock"] }
|
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
strfmt = "0.2"
|
strfmt = "0.2"
|
||||||
tower.workspace = true
|
tower.workspace = true
|
||||||
|
|||||||
@@ -19,14 +19,16 @@ use async_trait::async_trait;
|
|||||||
use auth::{PermissionChecker, PermissionCheckerRef, PermissionReq};
|
use auth::{PermissionChecker, PermissionCheckerRef, PermissionReq};
|
||||||
use client::Output;
|
use client::Output;
|
||||||
use common_error::ext::BoxedError;
|
use common_error::ext::BoxedError;
|
||||||
|
use pipeline::pipeline_operator::PipelineOperator;
|
||||||
use pipeline::{GreptimeTransformer, Pipeline, PipelineInfo, PipelineVersion};
|
use pipeline::{GreptimeTransformer, Pipeline, PipelineInfo, PipelineVersion};
|
||||||
use servers::error::{
|
use servers::error::{
|
||||||
AuthSnafu, Error as ServerError, ExecuteGrpcRequestSnafu, PipelineSnafu, Result as ServerResult,
|
AuthSnafu, Error as ServerError, ExecuteGrpcRequestSnafu, PipelineSnafu, Result as ServerResult,
|
||||||
};
|
};
|
||||||
use servers::interceptor::{LogIngestInterceptor, LogIngestInterceptorRef};
|
use servers::interceptor::{LogIngestInterceptor, LogIngestInterceptorRef};
|
||||||
use servers::query_handler::PipelineHandler;
|
use servers::query_handler::PipelineHandler;
|
||||||
use session::context::QueryContextRef;
|
use session::context::{QueryContext, QueryContextRef};
|
||||||
use snafu::ResultExt;
|
use snafu::ResultExt;
|
||||||
|
use table::Table;
|
||||||
|
|
||||||
use crate::instance::Instance;
|
use crate::instance::Instance;
|
||||||
|
|
||||||
@@ -84,6 +86,22 @@ impl PipelineHandler for Instance {
|
|||||||
.await
|
.await
|
||||||
.context(PipelineSnafu)
|
.context(PipelineSnafu)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_table(
|
||||||
|
&self,
|
||||||
|
table: &str,
|
||||||
|
query_ctx: &QueryContext,
|
||||||
|
) -> std::result::Result<Option<Arc<Table>>, catalog::error::Error> {
|
||||||
|
let catalog = query_ctx.current_catalog();
|
||||||
|
let schema = query_ctx.current_schema();
|
||||||
|
self.catalog_manager
|
||||||
|
.table(catalog, &schema, table, None)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_pipeline(&self, pipeline: &str) -> ServerResult<Pipeline<GreptimeTransformer>> {
|
||||||
|
PipelineOperator::build_pipeline(pipeline).context(PipelineSnafu)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Instance {
|
impl Instance {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ common-error.workspace = true
|
|||||||
common-macro.workspace = true
|
common-macro.workspace = true
|
||||||
common-runtime.workspace = true
|
common-runtime.workspace = true
|
||||||
common-telemetry.workspace = true
|
common-telemetry.workspace = true
|
||||||
|
fastbloom = "0.8"
|
||||||
fst.workspace = true
|
fst.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
greptime-proto.workspace = true
|
greptime-proto.workspace = true
|
||||||
@@ -26,6 +27,7 @@ prost.workspace = true
|
|||||||
regex.workspace = true
|
regex.workspace = true
|
||||||
regex-automata.workspace = true
|
regex-automata.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
|
serde_json.workspace = true
|
||||||
snafu.workspace = true
|
snafu.workspace = true
|
||||||
tantivy = { version = "0.22", features = ["zstd-compression"] }
|
tantivy = { version = "0.22", features = ["zstd-compression"] }
|
||||||
tantivy-jieba = "0.11.0"
|
tantivy-jieba = "0.11.0"
|
||||||
|
|||||||
53
src/index/src/bloom_filter.rs
Normal file
53
src/index/src/bloom_filter.rs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
// 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 serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
pub mod creator;
|
||||||
|
mod error;
|
||||||
|
|
||||||
|
pub type Bytes = Vec<u8>;
|
||||||
|
pub type BytesRef<'a> = &'a [u8];
|
||||||
|
|
||||||
|
/// The Meta information of the bloom filter stored in the file.
|
||||||
|
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||||
|
pub struct BloomFilterMeta {
|
||||||
|
/// The number of rows per segment.
|
||||||
|
pub rows_per_segment: usize,
|
||||||
|
|
||||||
|
/// The number of segments.
|
||||||
|
pub seg_count: usize,
|
||||||
|
|
||||||
|
/// The number of total rows.
|
||||||
|
pub row_count: usize,
|
||||||
|
|
||||||
|
/// The size of the bloom filter excluding the meta information.
|
||||||
|
pub bloom_filter_segments_size: usize,
|
||||||
|
|
||||||
|
/// Offset and size of bloom filters in the file.
|
||||||
|
pub bloom_filter_segments: Vec<BloomFilterSegmentLocation>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The location of the bloom filter segment in the file.
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct BloomFilterSegmentLocation {
|
||||||
|
/// The offset of the bloom filter segment in the file.
|
||||||
|
pub offset: u64,
|
||||||
|
|
||||||
|
/// The size of the bloom filter segment in the file.
|
||||||
|
pub size: u64,
|
||||||
|
|
||||||
|
/// The number of elements in the bloom filter segment.
|
||||||
|
pub elem_count: usize,
|
||||||
|
}
|
||||||
294
src/index/src/bloom_filter/creator.rs
Normal file
294
src/index/src/bloom_filter/creator.rs
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
// 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::HashSet;
|
||||||
|
|
||||||
|
use fastbloom::BloomFilter;
|
||||||
|
use futures::{AsyncWrite, AsyncWriteExt};
|
||||||
|
use snafu::ResultExt;
|
||||||
|
|
||||||
|
use super::error::{IoSnafu, SerdeJsonSnafu};
|
||||||
|
use crate::bloom_filter::error::Result;
|
||||||
|
use crate::bloom_filter::{BloomFilterMeta, BloomFilterSegmentLocation, Bytes};
|
||||||
|
|
||||||
|
/// The seed used for the Bloom filter.
|
||||||
|
const SEED: u128 = 42;
|
||||||
|
|
||||||
|
/// The false positive rate of the Bloom filter.
|
||||||
|
const FALSE_POSITIVE_RATE: f64 = 0.01;
|
||||||
|
|
||||||
|
/// `BloomFilterCreator` is responsible for creating and managing bloom filters
|
||||||
|
/// for a set of elements. It divides the rows into segments and creates
|
||||||
|
/// bloom filters for each segment.
|
||||||
|
///
|
||||||
|
/// # Format
|
||||||
|
///
|
||||||
|
/// The bloom filter creator writes the following format to the writer:
|
||||||
|
///
|
||||||
|
/// ```text
|
||||||
|
/// +--------------------+--------------------+-----+----------------------+----------------------+
|
||||||
|
/// | Bloom filter 0 | Bloom filter 1 | ... | BloomFilterMeta | Meta size |
|
||||||
|
/// +--------------------+--------------------+-----+----------------------+----------------------+
|
||||||
|
/// |<- bytes (size 0) ->|<- bytes (size 1) ->| ... |<- json (meta size) ->|<- u32 LE (4 bytes) ->|
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
pub struct BloomFilterCreator {
|
||||||
|
/// The number of rows per segment set by the user.
|
||||||
|
rows_per_segment: usize,
|
||||||
|
|
||||||
|
/// Row count that added to the bloom filter so far.
|
||||||
|
accumulated_row_count: usize,
|
||||||
|
|
||||||
|
/// A set of distinct elements in the current segment.
|
||||||
|
cur_seg_distinct_elems: HashSet<Bytes>,
|
||||||
|
|
||||||
|
/// The memory usage of the current segment's distinct elements.
|
||||||
|
cur_seg_distinct_elems_mem_usage: usize,
|
||||||
|
|
||||||
|
/// Storage for finalized Bloom filters.
|
||||||
|
finalized_bloom_filters: FinalizedBloomFilterStorage,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BloomFilterCreator {
|
||||||
|
/// Creates a new `BloomFilterCreator` with the specified number of rows per segment.
|
||||||
|
///
|
||||||
|
/// # PANICS
|
||||||
|
///
|
||||||
|
/// `rows_per_segment` <= 0
|
||||||
|
pub fn new(rows_per_segment: usize) -> Self {
|
||||||
|
assert!(
|
||||||
|
rows_per_segment > 0,
|
||||||
|
"rows_per_segment must be greater than 0"
|
||||||
|
);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
rows_per_segment,
|
||||||
|
accumulated_row_count: 0,
|
||||||
|
cur_seg_distinct_elems: HashSet::default(),
|
||||||
|
cur_seg_distinct_elems_mem_usage: 0,
|
||||||
|
finalized_bloom_filters: FinalizedBloomFilterStorage::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a row of elements to the bloom filter. If the number of accumulated rows
|
||||||
|
/// reaches `rows_per_segment`, it finalizes the current segment.
|
||||||
|
pub fn push_row_elems(&mut self, elems: impl IntoIterator<Item = Bytes>) {
|
||||||
|
self.accumulated_row_count += 1;
|
||||||
|
for elem in elems.into_iter() {
|
||||||
|
let len = elem.len();
|
||||||
|
let is_new = self.cur_seg_distinct_elems.insert(elem);
|
||||||
|
if is_new {
|
||||||
|
self.cur_seg_distinct_elems_mem_usage += len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.accumulated_row_count % self.rows_per_segment == 0 {
|
||||||
|
self.finalize_segment();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finalizes any remaining segments and writes the bloom filters and metadata to the provided writer.
|
||||||
|
pub async fn finish(&mut self, mut writer: impl AsyncWrite + Unpin) -> Result<()> {
|
||||||
|
if !self.cur_seg_distinct_elems.is_empty() {
|
||||||
|
self.finalize_segment();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut meta = BloomFilterMeta {
|
||||||
|
rows_per_segment: self.rows_per_segment,
|
||||||
|
seg_count: self.finalized_bloom_filters.len(),
|
||||||
|
row_count: self.accumulated_row_count,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
for segment in self.finalized_bloom_filters.drain() {
|
||||||
|
let slice = segment.bloom_filter.as_slice();
|
||||||
|
buf.clear();
|
||||||
|
write_u64_slice(&mut buf, slice);
|
||||||
|
writer.write_all(&buf).await.context(IoSnafu)?;
|
||||||
|
|
||||||
|
let size = buf.len();
|
||||||
|
meta.bloom_filter_segments.push(BloomFilterSegmentLocation {
|
||||||
|
offset: meta.bloom_filter_segments_size as _,
|
||||||
|
size: size as _,
|
||||||
|
elem_count: segment.element_count,
|
||||||
|
});
|
||||||
|
meta.bloom_filter_segments_size += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
let meta_bytes = serde_json::to_vec(&meta).context(SerdeJsonSnafu)?;
|
||||||
|
writer.write_all(&meta_bytes).await.context(IoSnafu)?;
|
||||||
|
|
||||||
|
let meta_size = meta_bytes.len() as u32;
|
||||||
|
writer
|
||||||
|
.write_all(&meta_size.to_le_bytes())
|
||||||
|
.await
|
||||||
|
.context(IoSnafu)?;
|
||||||
|
writer.flush().await.unwrap();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the memory usage of the creating bloom filter.
|
||||||
|
pub fn memory_usage(&self) -> usize {
|
||||||
|
self.cur_seg_distinct_elems_mem_usage + self.finalized_bloom_filters.memory_usage()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finalize_segment(&mut self) {
|
||||||
|
let elem_count = self.cur_seg_distinct_elems.len();
|
||||||
|
self.finalized_bloom_filters
|
||||||
|
.add(self.cur_seg_distinct_elems.drain(), elem_count);
|
||||||
|
self.cur_seg_distinct_elems_mem_usage = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Storage for finalized Bloom filters.
|
||||||
|
///
|
||||||
|
/// TODO(zhongzc): Add support for storing intermediate bloom filters on disk to control memory usage.
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct FinalizedBloomFilterStorage {
|
||||||
|
/// Bloom filters that are stored in memory.
|
||||||
|
in_memory: Vec<FinalizedBloomFilterSegment>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FinalizedBloomFilterStorage {
|
||||||
|
fn memory_usage(&self) -> usize {
|
||||||
|
self.in_memory.iter().map(|s| s.size).sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a new finalized Bloom filter to the storage.
|
||||||
|
///
|
||||||
|
/// TODO(zhongzc): Add support for flushing to disk.
|
||||||
|
fn add(&mut self, elems: impl IntoIterator<Item = Bytes>, elem_count: usize) {
|
||||||
|
let mut bf = BloomFilter::with_false_pos(FALSE_POSITIVE_RATE)
|
||||||
|
.seed(&SEED)
|
||||||
|
.expected_items(elem_count);
|
||||||
|
for elem in elems.into_iter() {
|
||||||
|
bf.insert(&elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
let cbf = FinalizedBloomFilterSegment::new(bf, elem_count);
|
||||||
|
self.in_memory.push(cbf);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.in_memory.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drain(&mut self) -> impl Iterator<Item = FinalizedBloomFilterSegment> + '_ {
|
||||||
|
self.in_memory.drain(..)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A finalized Bloom filter segment.
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct FinalizedBloomFilterSegment {
|
||||||
|
/// The underlying Bloom filter.
|
||||||
|
bloom_filter: BloomFilter,
|
||||||
|
|
||||||
|
/// The number of elements in the Bloom filter.
|
||||||
|
element_count: usize,
|
||||||
|
|
||||||
|
/// The occupied memory size of the Bloom filter.
|
||||||
|
size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FinalizedBloomFilterSegment {
|
||||||
|
fn new(bloom_filter: BloomFilter, elem_count: usize) -> Self {
|
||||||
|
let memory_usage = std::mem::size_of_val(bloom_filter.as_slice());
|
||||||
|
Self {
|
||||||
|
bloom_filter,
|
||||||
|
element_count: elem_count,
|
||||||
|
size: memory_usage,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes a slice of `u64` to the buffer in little-endian order.
|
||||||
|
fn write_u64_slice(buf: &mut Vec<u8>, slice: &[u64]) {
|
||||||
|
buf.reserve(std::mem::size_of_val(slice));
|
||||||
|
for &x in slice {
|
||||||
|
buf.extend_from_slice(&x.to_le_bytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use futures::io::Cursor;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn u64_vec_from_bytes(bytes: &[u8]) -> Vec<u64> {
|
||||||
|
bytes
|
||||||
|
.chunks_exact(std::mem::size_of::<u64>())
|
||||||
|
.map(|chunk| u64::from_le_bytes(chunk.try_into().unwrap()))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_bloom_filter_creator() {
|
||||||
|
let mut writer = Cursor::new(Vec::new());
|
||||||
|
let mut creator = BloomFilterCreator::new(2);
|
||||||
|
|
||||||
|
creator.push_row_elems(vec![b"a".to_vec(), b"b".to_vec()]);
|
||||||
|
assert!(creator.cur_seg_distinct_elems_mem_usage > 0);
|
||||||
|
assert!(creator.memory_usage() > 0);
|
||||||
|
|
||||||
|
creator.push_row_elems(vec![b"c".to_vec(), b"d".to_vec()]);
|
||||||
|
// Finalize the first segment
|
||||||
|
assert!(creator.cur_seg_distinct_elems_mem_usage == 0);
|
||||||
|
assert!(creator.memory_usage() > 0);
|
||||||
|
|
||||||
|
creator.push_row_elems(vec![b"e".to_vec(), b"f".to_vec()]);
|
||||||
|
assert!(creator.cur_seg_distinct_elems_mem_usage > 0);
|
||||||
|
assert!(creator.memory_usage() > 0);
|
||||||
|
|
||||||
|
creator.finish(&mut writer).await.unwrap();
|
||||||
|
|
||||||
|
let bytes = writer.into_inner();
|
||||||
|
let total_size = bytes.len();
|
||||||
|
let meta_size_offset = total_size - 4;
|
||||||
|
let meta_size = u32::from_le_bytes((&bytes[meta_size_offset..]).try_into().unwrap());
|
||||||
|
|
||||||
|
let meta_bytes = &bytes[total_size - meta_size as usize - 4..total_size - 4];
|
||||||
|
let meta: BloomFilterMeta = serde_json::from_slice(meta_bytes).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(meta.rows_per_segment, 2);
|
||||||
|
assert_eq!(meta.seg_count, 2);
|
||||||
|
assert_eq!(meta.row_count, 3);
|
||||||
|
assert_eq!(
|
||||||
|
meta.bloom_filter_segments_size + meta_bytes.len() + 4,
|
||||||
|
total_size
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut bfs = Vec::new();
|
||||||
|
for segment in meta.bloom_filter_segments {
|
||||||
|
let bloom_filter_bytes =
|
||||||
|
&bytes[segment.offset as usize..(segment.offset + segment.size) as usize];
|
||||||
|
let v = u64_vec_from_bytes(bloom_filter_bytes);
|
||||||
|
let bloom_filter = BloomFilter::from_vec(v)
|
||||||
|
.seed(&SEED)
|
||||||
|
.expected_items(segment.elem_count);
|
||||||
|
bfs.push(bloom_filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(bfs.len(), 2);
|
||||||
|
assert!(bfs[0].contains(&b"a"));
|
||||||
|
assert!(bfs[0].contains(&b"b"));
|
||||||
|
assert!(bfs[0].contains(&b"c"));
|
||||||
|
assert!(bfs[0].contains(&b"d"));
|
||||||
|
assert!(bfs[1].contains(&b"e"));
|
||||||
|
assert!(bfs[1].contains(&b"f"));
|
||||||
|
}
|
||||||
|
}
|
||||||
66
src/index/src/bloom_filter/error.rs
Normal file
66
src/index/src/bloom_filter/error.rs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
// 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::any::Any;
|
||||||
|
|
||||||
|
use common_error::ext::{BoxedError, ErrorExt};
|
||||||
|
use common_error::status_code::StatusCode;
|
||||||
|
use common_macro::stack_trace_debug;
|
||||||
|
use snafu::{Location, Snafu};
|
||||||
|
|
||||||
|
#[derive(Snafu)]
|
||||||
|
#[snafu(visibility(pub))]
|
||||||
|
#[stack_trace_debug]
|
||||||
|
pub enum Error {
|
||||||
|
#[snafu(display("IO error"))]
|
||||||
|
Io {
|
||||||
|
#[snafu(source)]
|
||||||
|
error: std::io::Error,
|
||||||
|
#[snafu(implicit)]
|
||||||
|
location: Location,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Failed to serde json"))]
|
||||||
|
SerdeJson {
|
||||||
|
#[snafu(source)]
|
||||||
|
error: serde_json::error::Error,
|
||||||
|
#[snafu(implicit)]
|
||||||
|
location: Location,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("External error"))]
|
||||||
|
External {
|
||||||
|
source: BoxedError,
|
||||||
|
#[snafu(implicit)]
|
||||||
|
location: Location,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ErrorExt for Error {
|
||||||
|
fn status_code(&self) -> StatusCode {
|
||||||
|
use Error::*;
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Io { .. } | Self::SerdeJson { .. } => StatusCode::Unexpected,
|
||||||
|
|
||||||
|
External { source, .. } => source.status_code(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
@@ -26,14 +26,6 @@ use crate::inverted_index::search::predicate::Predicate;
|
|||||||
#[snafu(visibility(pub))]
|
#[snafu(visibility(pub))]
|
||||||
#[stack_trace_debug]
|
#[stack_trace_debug]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
#[snafu(display("Failed to seek"))]
|
|
||||||
Seek {
|
|
||||||
#[snafu(source)]
|
|
||||||
error: IoError,
|
|
||||||
#[snafu(implicit)]
|
|
||||||
location: Location,
|
|
||||||
},
|
|
||||||
|
|
||||||
#[snafu(display("Failed to read"))]
|
#[snafu(display("Failed to read"))]
|
||||||
Read {
|
Read {
|
||||||
#[snafu(source)]
|
#[snafu(source)]
|
||||||
@@ -76,6 +68,18 @@ pub enum Error {
|
|||||||
location: Location,
|
location: Location,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Blob size too small"))]
|
||||||
|
BlobSizeTooSmall {
|
||||||
|
#[snafu(implicit)]
|
||||||
|
location: Location,
|
||||||
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Invalid footer payload size"))]
|
||||||
|
InvalidFooterPayloadSize {
|
||||||
|
#[snafu(implicit)]
|
||||||
|
location: Location,
|
||||||
|
},
|
||||||
|
|
||||||
#[snafu(display("Unexpected inverted index footer payload size, max: {max_payload_size}, actual: {actual_payload_size}"))]
|
#[snafu(display("Unexpected inverted index footer payload size, max: {max_payload_size}, actual: {actual_payload_size}"))]
|
||||||
UnexpectedFooterPayloadSize {
|
UnexpectedFooterPayloadSize {
|
||||||
max_payload_size: u64,
|
max_payload_size: u64,
|
||||||
@@ -215,8 +219,7 @@ impl ErrorExt for Error {
|
|||||||
fn status_code(&self) -> StatusCode {
|
fn status_code(&self) -> StatusCode {
|
||||||
use Error::*;
|
use Error::*;
|
||||||
match self {
|
match self {
|
||||||
Seek { .. }
|
Read { .. }
|
||||||
| Read { .. }
|
|
||||||
| Write { .. }
|
| Write { .. }
|
||||||
| Flush { .. }
|
| Flush { .. }
|
||||||
| Close { .. }
|
| Close { .. }
|
||||||
@@ -229,7 +232,9 @@ impl ErrorExt for Error {
|
|||||||
| KeysApplierUnexpectedPredicates { .. }
|
| KeysApplierUnexpectedPredicates { .. }
|
||||||
| CommonIo { .. }
|
| CommonIo { .. }
|
||||||
| UnknownIntermediateCodecMagic { .. }
|
| UnknownIntermediateCodecMagic { .. }
|
||||||
| FstCompile { .. } => StatusCode::Unexpected,
|
| FstCompile { .. }
|
||||||
|
| InvalidFooterPayloadSize { .. }
|
||||||
|
| BlobSizeTooSmall { .. } => StatusCode::Unexpected,
|
||||||
|
|
||||||
ParseRegex { .. }
|
ParseRegex { .. }
|
||||||
| ParseDFA { .. }
|
| ParseDFA { .. }
|
||||||
|
|||||||
@@ -12,9 +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 std::ops::Range;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use bytes::Bytes;
|
||||||
use common_base::BitVec;
|
use common_base::BitVec;
|
||||||
use greptime_proto::v1::index::InvertedIndexMetas;
|
use greptime_proto::v1::index::InvertedIndexMetas;
|
||||||
use snafu::ResultExt;
|
use snafu::ResultExt;
|
||||||
@@ -30,23 +32,23 @@ mod footer;
|
|||||||
#[mockall::automock]
|
#[mockall::automock]
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait InvertedIndexReader: Send {
|
pub trait InvertedIndexReader: Send {
|
||||||
/// Reads all data to dest.
|
|
||||||
async fn read_all(&mut self, dest: &mut Vec<u8>) -> Result<usize>;
|
|
||||||
|
|
||||||
/// Seeks to given offset and reads data with exact size as provided.
|
/// Seeks to given offset and reads data with exact size as provided.
|
||||||
async fn seek_read(&mut self, offset: u64, size: u32) -> Result<Vec<u8>>;
|
async fn range_read(&mut self, offset: u64, size: u32) -> Result<Vec<u8>>;
|
||||||
|
|
||||||
|
/// Reads the bytes in the given ranges.
|
||||||
|
async fn read_vec(&mut self, ranges: &[Range<u64>]) -> Result<Vec<Bytes>>;
|
||||||
|
|
||||||
/// Retrieves metadata of all inverted indices stored within the blob.
|
/// Retrieves metadata of all inverted indices stored within the blob.
|
||||||
async fn metadata(&mut self) -> Result<Arc<InvertedIndexMetas>>;
|
async fn metadata(&mut self) -> Result<Arc<InvertedIndexMetas>>;
|
||||||
|
|
||||||
/// Retrieves the finite state transducer (FST) map from the given offset and size.
|
/// Retrieves the finite state transducer (FST) map from the given offset and size.
|
||||||
async fn fst(&mut self, offset: u64, size: u32) -> Result<FstMap> {
|
async fn fst(&mut self, offset: u64, size: u32) -> Result<FstMap> {
|
||||||
let fst_data = self.seek_read(offset, size).await?;
|
let fst_data = self.range_read(offset, size).await?;
|
||||||
FstMap::new(fst_data).context(DecodeFstSnafu)
|
FstMap::new(fst_data).context(DecodeFstSnafu)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves the bitmap from the given offset and size.
|
/// Retrieves the bitmap from the given offset and size.
|
||||||
async fn bitmap(&mut self, offset: u64, size: u32) -> Result<BitVec> {
|
async fn bitmap(&mut self, offset: u64, size: u32) -> Result<BitVec> {
|
||||||
self.seek_read(offset, size).await.map(BitVec::from_vec)
|
self.range_read(offset, size).await.map(BitVec::from_vec)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,15 +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::ops::Range;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use bytes::Bytes;
|
||||||
use common_base::range_read::RangeReader;
|
use common_base::range_read::RangeReader;
|
||||||
use greptime_proto::v1::index::InvertedIndexMetas;
|
use greptime_proto::v1::index::InvertedIndexMetas;
|
||||||
use snafu::{ensure, ResultExt};
|
use snafu::{ensure, ResultExt};
|
||||||
|
|
||||||
|
use super::footer::DEFAULT_PREFETCH_SIZE;
|
||||||
use crate::inverted_index::error::{CommonIoSnafu, Result, UnexpectedBlobSizeSnafu};
|
use crate::inverted_index::error::{CommonIoSnafu, Result, UnexpectedBlobSizeSnafu};
|
||||||
use crate::inverted_index::format::reader::footer::InvertedIndeFooterReader;
|
use crate::inverted_index::format::reader::footer::InvertedIndexFooterReader;
|
||||||
use crate::inverted_index::format::reader::InvertedIndexReader;
|
use crate::inverted_index::format::reader::InvertedIndexReader;
|
||||||
use crate::inverted_index::format::MIN_BLOB_SIZE;
|
use crate::inverted_index::format::MIN_BLOB_SIZE;
|
||||||
|
|
||||||
@@ -49,16 +52,7 @@ impl<R> InvertedIndexBlobReader<R> {
|
|||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl<R: RangeReader> InvertedIndexReader for InvertedIndexBlobReader<R> {
|
impl<R: RangeReader> InvertedIndexReader for InvertedIndexBlobReader<R> {
|
||||||
async fn read_all(&mut self, dest: &mut Vec<u8>) -> Result<usize> {
|
async fn range_read(&mut self, offset: u64, size: u32) -> Result<Vec<u8>> {
|
||||||
let metadata = self.source.metadata().await.context(CommonIoSnafu)?;
|
|
||||||
self.source
|
|
||||||
.read_into(0..metadata.content_length, dest)
|
|
||||||
.await
|
|
||||||
.context(CommonIoSnafu)?;
|
|
||||||
Ok(metadata.content_length as usize)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn seek_read(&mut self, offset: u64, size: u32) -> Result<Vec<u8>> {
|
|
||||||
let buf = self
|
let buf = self
|
||||||
.source
|
.source
|
||||||
.read(offset..offset + size as u64)
|
.read(offset..offset + size as u64)
|
||||||
@@ -67,12 +61,17 @@ impl<R: RangeReader> InvertedIndexReader for InvertedIndexBlobReader<R> {
|
|||||||
Ok(buf.into())
|
Ok(buf.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn read_vec(&mut self, ranges: &[Range<u64>]) -> Result<Vec<Bytes>> {
|
||||||
|
self.source.read_vec(ranges).await.context(CommonIoSnafu)
|
||||||
|
}
|
||||||
|
|
||||||
async fn metadata(&mut self) -> Result<Arc<InvertedIndexMetas>> {
|
async fn metadata(&mut self) -> Result<Arc<InvertedIndexMetas>> {
|
||||||
let metadata = self.source.metadata().await.context(CommonIoSnafu)?;
|
let metadata = self.source.metadata().await.context(CommonIoSnafu)?;
|
||||||
let blob_size = metadata.content_length;
|
let blob_size = metadata.content_length;
|
||||||
Self::validate_blob_size(blob_size)?;
|
Self::validate_blob_size(blob_size)?;
|
||||||
|
|
||||||
let mut footer_reader = InvertedIndeFooterReader::new(&mut self.source, blob_size);
|
let mut footer_reader = InvertedIndexFooterReader::new(&mut self.source, blob_size)
|
||||||
|
.with_prefetch_size(DEFAULT_PREFETCH_SIZE);
|
||||||
footer_reader.metadata().await.map(Arc::new)
|
footer_reader.metadata().await.map(Arc::new)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,53 +18,88 @@ use prost::Message;
|
|||||||
use snafu::{ensure, ResultExt};
|
use snafu::{ensure, ResultExt};
|
||||||
|
|
||||||
use crate::inverted_index::error::{
|
use crate::inverted_index::error::{
|
||||||
CommonIoSnafu, DecodeProtoSnafu, Result, UnexpectedFooterPayloadSizeSnafu,
|
BlobSizeTooSmallSnafu, CommonIoSnafu, DecodeProtoSnafu, InvalidFooterPayloadSizeSnafu, Result,
|
||||||
UnexpectedOffsetSizeSnafu, UnexpectedZeroSegmentRowCountSnafu,
|
UnexpectedFooterPayloadSizeSnafu, UnexpectedOffsetSizeSnafu,
|
||||||
|
UnexpectedZeroSegmentRowCountSnafu,
|
||||||
};
|
};
|
||||||
use crate::inverted_index::format::FOOTER_PAYLOAD_SIZE_SIZE;
|
use crate::inverted_index::format::FOOTER_PAYLOAD_SIZE_SIZE;
|
||||||
|
|
||||||
/// InvertedIndeFooterReader is for reading the footer section of the blob.
|
pub const DEFAULT_PREFETCH_SIZE: u64 = 1024; // 1KiB
|
||||||
pub struct InvertedIndeFooterReader<R> {
|
|
||||||
|
/// InvertedIndexFooterReader is for reading the footer section of the blob.
|
||||||
|
pub struct InvertedIndexFooterReader<R> {
|
||||||
source: R,
|
source: R,
|
||||||
blob_size: u64,
|
blob_size: u64,
|
||||||
|
prefetch_size: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R> InvertedIndeFooterReader<R> {
|
impl<R> InvertedIndexFooterReader<R> {
|
||||||
pub fn new(source: R, blob_size: u64) -> Self {
|
pub fn new(source: R, blob_size: u64) -> Self {
|
||||||
Self { source, blob_size }
|
Self {
|
||||||
|
source,
|
||||||
|
blob_size,
|
||||||
|
prefetch_size: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the prefetch size for the footer reader.
|
||||||
|
pub fn with_prefetch_size(mut self, prefetch_size: u64) -> Self {
|
||||||
|
self.prefetch_size = Some(prefetch_size.max(FOOTER_PAYLOAD_SIZE_SIZE));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prefetch_size(&self) -> u64 {
|
||||||
|
self.prefetch_size.unwrap_or(FOOTER_PAYLOAD_SIZE_SIZE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: RangeReader> InvertedIndeFooterReader<R> {
|
impl<R: RangeReader> InvertedIndexFooterReader<R> {
|
||||||
pub async fn metadata(&mut self) -> Result<InvertedIndexMetas> {
|
pub async fn metadata(&mut self) -> Result<InvertedIndexMetas> {
|
||||||
let payload_size = self.read_payload_size().await?;
|
ensure!(
|
||||||
let metas = self.read_payload(payload_size).await?;
|
self.blob_size >= FOOTER_PAYLOAD_SIZE_SIZE,
|
||||||
Ok(metas)
|
BlobSizeTooSmallSnafu
|
||||||
}
|
);
|
||||||
|
|
||||||
async fn read_payload_size(&mut self) -> Result<u64> {
|
let footer_start = self.blob_size.saturating_sub(self.prefetch_size());
|
||||||
let mut size_buf = [0u8; FOOTER_PAYLOAD_SIZE_SIZE as usize];
|
let suffix = self
|
||||||
let end = self.blob_size;
|
.source
|
||||||
let start = end - FOOTER_PAYLOAD_SIZE_SIZE;
|
.read(footer_start..self.blob_size)
|
||||||
self.source
|
|
||||||
.read_into(start..end, &mut &mut size_buf[..])
|
|
||||||
.await
|
.await
|
||||||
.context(CommonIoSnafu)?;
|
.context(CommonIoSnafu)?;
|
||||||
|
let suffix_len = suffix.len();
|
||||||
|
let length = u32::from_le_bytes(Self::read_tailing_four_bytes(&suffix)?) as u64;
|
||||||
|
self.validate_payload_size(length)?;
|
||||||
|
|
||||||
let payload_size = u32::from_le_bytes(size_buf) as u64;
|
let footer_size = FOOTER_PAYLOAD_SIZE_SIZE;
|
||||||
self.validate_payload_size(payload_size)?;
|
|
||||||
|
|
||||||
Ok(payload_size)
|
// Did not fetch the entire file metadata in the initial read, need to make a second request.
|
||||||
|
if length > suffix_len as u64 - footer_size {
|
||||||
|
let metadata_start = self.blob_size - length - footer_size;
|
||||||
|
let meta = self
|
||||||
|
.source
|
||||||
|
.read(metadata_start..self.blob_size - footer_size)
|
||||||
|
.await
|
||||||
|
.context(CommonIoSnafu)?;
|
||||||
|
self.parse_payload(&meta, length)
|
||||||
|
} else {
|
||||||
|
let metadata_start = self.blob_size - length - footer_size - footer_start;
|
||||||
|
let meta = &suffix[metadata_start as usize..suffix_len - footer_size as usize];
|
||||||
|
self.parse_payload(meta, length)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn read_payload(&mut self, payload_size: u64) -> Result<InvertedIndexMetas> {
|
fn read_tailing_four_bytes(suffix: &[u8]) -> Result<[u8; 4]> {
|
||||||
let end = self.blob_size - FOOTER_PAYLOAD_SIZE_SIZE;
|
let suffix_len = suffix.len();
|
||||||
let start = end - payload_size;
|
ensure!(suffix_len >= 4, InvalidFooterPayloadSizeSnafu);
|
||||||
let bytes = self.source.read(start..end).await.context(CommonIoSnafu)?;
|
let mut bytes = [0; 4];
|
||||||
|
bytes.copy_from_slice(&suffix[suffix_len - 4..suffix_len]);
|
||||||
|
|
||||||
let metas = InvertedIndexMetas::decode(&*bytes).context(DecodeProtoSnafu)?;
|
Ok(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_payload(&mut self, bytes: &[u8], payload_size: u64) -> Result<InvertedIndexMetas> {
|
||||||
|
let metas = InvertedIndexMetas::decode(bytes).context(DecodeProtoSnafu)?;
|
||||||
self.validate_metas(&metas, payload_size)?;
|
self.validate_metas(&metas, payload_size)?;
|
||||||
|
|
||||||
Ok(metas)
|
Ok(metas)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,9 +148,12 @@ impl<R: RangeReader> InvertedIndeFooterReader<R> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use std::assert_matches::assert_matches;
|
||||||
|
|
||||||
use prost::Message;
|
use prost::Message;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::inverted_index::error::Error;
|
||||||
|
|
||||||
fn create_test_payload(meta: InvertedIndexMeta) -> Vec<u8> {
|
fn create_test_payload(meta: InvertedIndexMeta) -> Vec<u8> {
|
||||||
let mut metas = InvertedIndexMetas {
|
let mut metas = InvertedIndexMetas {
|
||||||
@@ -141,14 +179,18 @@ mod tests {
|
|||||||
|
|
||||||
let mut payload_buf = create_test_payload(meta);
|
let mut payload_buf = create_test_payload(meta);
|
||||||
let blob_size = payload_buf.len() as u64;
|
let blob_size = payload_buf.len() as u64;
|
||||||
let mut reader = InvertedIndeFooterReader::new(&mut payload_buf, blob_size);
|
|
||||||
|
|
||||||
let payload_size = reader.read_payload_size().await.unwrap();
|
for prefetch in [0, blob_size / 2, blob_size, blob_size + 10] {
|
||||||
let metas = reader.read_payload(payload_size).await.unwrap();
|
let mut reader = InvertedIndexFooterReader::new(&mut payload_buf, blob_size);
|
||||||
|
if prefetch > 0 {
|
||||||
|
reader = reader.with_prefetch_size(prefetch);
|
||||||
|
}
|
||||||
|
|
||||||
assert_eq!(metas.metas.len(), 1);
|
let metas = reader.metadata().await.unwrap();
|
||||||
let index_meta = &metas.metas.get("test").unwrap();
|
assert_eq!(metas.metas.len(), 1);
|
||||||
assert_eq!(index_meta.name, "test");
|
let index_meta = &metas.metas.get("test").unwrap();
|
||||||
|
assert_eq!(index_meta.name, "test");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
@@ -157,14 +199,20 @@ mod tests {
|
|||||||
name: "test".to_string(),
|
name: "test".to_string(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut payload_buf = create_test_payload(meta);
|
let mut payload_buf = create_test_payload(meta);
|
||||||
payload_buf.push(0xff); // Add an extra byte to corrupt the footer
|
payload_buf.push(0xff); // Add an extra byte to corrupt the footer
|
||||||
let blob_size = payload_buf.len() as u64;
|
let blob_size = payload_buf.len() as u64;
|
||||||
let mut reader = InvertedIndeFooterReader::new(&mut payload_buf, blob_size);
|
|
||||||
|
|
||||||
let payload_size_result = reader.read_payload_size().await;
|
for prefetch in [0, blob_size / 2, blob_size, blob_size + 10] {
|
||||||
assert!(payload_size_result.is_err());
|
let blob_size = payload_buf.len() as u64;
|
||||||
|
let mut reader = InvertedIndexFooterReader::new(&mut payload_buf, blob_size);
|
||||||
|
if prefetch > 0 {
|
||||||
|
reader = reader.with_prefetch_size(prefetch);
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = reader.metadata().await;
|
||||||
|
assert_matches!(result, Err(Error::UnexpectedFooterPayloadSize { .. }));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
@@ -178,10 +226,15 @@ mod tests {
|
|||||||
|
|
||||||
let mut payload_buf = create_test_payload(meta);
|
let mut payload_buf = create_test_payload(meta);
|
||||||
let blob_size = payload_buf.len() as u64;
|
let blob_size = payload_buf.len() as u64;
|
||||||
let mut reader = InvertedIndeFooterReader::new(&mut payload_buf, blob_size);
|
|
||||||
|
|
||||||
let payload_size = reader.read_payload_size().await.unwrap();
|
for prefetch in [0, blob_size / 2, blob_size, blob_size + 10] {
|
||||||
let payload_result = reader.read_payload(payload_size).await;
|
let mut reader = InvertedIndexFooterReader::new(&mut payload_buf, blob_size);
|
||||||
assert!(payload_result.is_err());
|
if prefetch > 0 {
|
||||||
|
reader = reader.with_prefetch_size(prefetch);
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = reader.metadata().await;
|
||||||
|
assert_matches!(result, Err(Error::UnexpectedOffsetSize { .. }));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
#![feature(iter_partition_in_place)]
|
#![feature(iter_partition_in_place)]
|
||||||
|
#![feature(assert_matches)]
|
||||||
|
|
||||||
|
pub mod bloom_filter;
|
||||||
pub mod fulltext_index;
|
pub mod fulltext_index;
|
||||||
pub mod inverted_index;
|
pub mod inverted_index;
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use api::v1::meta::{ProcedureDetailResponse, Role};
|
use api::v1::meta::{ProcedureDetailResponse, Role};
|
||||||
use cluster::Client as ClusterClient;
|
use cluster::Client as ClusterClient;
|
||||||
|
pub use cluster::ClusterKvBackend;
|
||||||
use common_error::ext::BoxedError;
|
use common_error::ext::BoxedError;
|
||||||
use common_grpc::channel_manager::{ChannelConfig, ChannelManager};
|
use common_grpc::channel_manager::{ChannelConfig, ChannelManager};
|
||||||
use common_meta::cluster::{
|
use common_meta::cluster::{
|
||||||
@@ -33,6 +34,8 @@ use common_meta::cluster::{
|
|||||||
use common_meta::datanode::{DatanodeStatKey, DatanodeStatValue, RegionStat};
|
use common_meta::datanode::{DatanodeStatKey, DatanodeStatValue, RegionStat};
|
||||||
use common_meta::ddl::{ExecutorContext, ProcedureExecutor};
|
use common_meta::ddl::{ExecutorContext, ProcedureExecutor};
|
||||||
use common_meta::error::{self as meta_error, ExternalSnafu, Result as MetaResult};
|
use common_meta::error::{self as meta_error, ExternalSnafu, Result as MetaResult};
|
||||||
|
use common_meta::key::flow::flow_state::{FlowStat, FlowStateManager};
|
||||||
|
use common_meta::kv_backend::KvBackendRef;
|
||||||
use common_meta::range_stream::PaginationStream;
|
use common_meta::range_stream::PaginationStream;
|
||||||
use common_meta::rpc::ddl::{SubmitDdlTaskRequest, SubmitDdlTaskResponse};
|
use common_meta::rpc::ddl::{SubmitDdlTaskRequest, SubmitDdlTaskResponse};
|
||||||
use common_meta::rpc::procedure::{
|
use common_meta::rpc::procedure::{
|
||||||
@@ -54,7 +57,8 @@ use store::Client as StoreClient;
|
|||||||
|
|
||||||
pub use self::heartbeat::{HeartbeatSender, HeartbeatStream};
|
pub use self::heartbeat::{HeartbeatSender, HeartbeatStream};
|
||||||
use crate::error::{
|
use crate::error::{
|
||||||
ConvertMetaRequestSnafu, ConvertMetaResponseSnafu, Error, NotStartedSnafu, Result,
|
ConvertMetaRequestSnafu, ConvertMetaResponseSnafu, Error, GetFlowStatSnafu, NotStartedSnafu,
|
||||||
|
Result,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type Id = (u64, u64);
|
pub type Id = (u64, u64);
|
||||||
@@ -347,6 +351,15 @@ fn decode_stats(kv: KeyValue) -> MetaResult<DatanodeStatValue> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MetaClient {
|
impl MetaClient {
|
||||||
|
pub async fn list_flow_stats(&self) -> Result<Option<FlowStat>> {
|
||||||
|
let cluster_backend = ClusterKvBackend::new(Arc::new(self.cluster_client()?));
|
||||||
|
let cluster_backend = Arc::new(cluster_backend) as KvBackendRef;
|
||||||
|
let flow_state_manager = FlowStateManager::new(cluster_backend);
|
||||||
|
let res = flow_state_manager.get().await.context(GetFlowStatSnafu)?;
|
||||||
|
|
||||||
|
Ok(res.map(|r| r.into()))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new(id: Id) -> Self {
|
pub fn new(id: Id) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ use tonic::Status;
|
|||||||
use crate::client::ask_leader::AskLeader;
|
use crate::client::ask_leader::AskLeader;
|
||||||
use crate::client::{util, Id};
|
use crate::client::{util, Id};
|
||||||
use crate::error::{
|
use crate::error::{
|
||||||
ConvertMetaResponseSnafu, CreateChannelSnafu, Error, IllegalGrpcClientStateSnafu, Result,
|
ConvertMetaResponseSnafu, CreateChannelSnafu, Error, IllegalGrpcClientStateSnafu,
|
||||||
RetryTimesExceededSnafu,
|
ReadOnlyKvBackendSnafu, Result, RetryTimesExceededSnafu,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@@ -308,3 +308,75 @@ impl Inner {
|
|||||||
.map(|res| (res.leader, res.followers))
|
.map(|res| (res.leader, res.followers))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A client for the cluster info. Read only and corresponding to
|
||||||
|
/// `in_memory` kvbackend in the meta-srv.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct ClusterKvBackend {
|
||||||
|
inner: Arc<Client>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClusterKvBackend {
|
||||||
|
pub fn new(client: Arc<Client>) -> Self {
|
||||||
|
Self { inner: client }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unimpl(&self) -> common_meta::error::Error {
|
||||||
|
let ret: common_meta::error::Result<()> = ReadOnlyKvBackendSnafu {
|
||||||
|
name: self.name().to_string(),
|
||||||
|
}
|
||||||
|
.fail()
|
||||||
|
.map_err(BoxedError::new)
|
||||||
|
.context(common_meta::error::ExternalSnafu);
|
||||||
|
ret.unwrap_err()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TxnService for ClusterKvBackend {
|
||||||
|
type Error = common_meta::error::Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl KvBackend for ClusterKvBackend {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"ClusterKvBackend"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn range(&self, req: RangeRequest) -> common_meta::error::Result<RangeResponse> {
|
||||||
|
self.inner
|
||||||
|
.range(req)
|
||||||
|
.await
|
||||||
|
.map_err(BoxedError::new)
|
||||||
|
.context(common_meta::error::ExternalSnafu)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn batch_get(&self, _: BatchGetRequest) -> common_meta::error::Result<BatchGetResponse> {
|
||||||
|
Err(self.unimpl())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn put(&self, _: PutRequest) -> common_meta::error::Result<PutResponse> {
|
||||||
|
Err(self.unimpl())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn batch_put(&self, _: BatchPutRequest) -> common_meta::error::Result<BatchPutResponse> {
|
||||||
|
Err(self.unimpl())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn delete_range(
|
||||||
|
&self,
|
||||||
|
_: DeleteRangeRequest,
|
||||||
|
) -> common_meta::error::Result<DeleteRangeResponse> {
|
||||||
|
Err(self.unimpl())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn batch_delete(
|
||||||
|
&self,
|
||||||
|
_: BatchDeleteRequest,
|
||||||
|
) -> common_meta::error::Result<BatchDeleteResponse> {
|
||||||
|
Err(self.unimpl())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -99,8 +99,22 @@ pub enum Error {
|
|||||||
source: common_meta::error::Error,
|
source: common_meta::error::Error,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
#[snafu(display("Failed to get flow stat"))]
|
||||||
|
GetFlowStat {
|
||||||
|
#[snafu(implicit)]
|
||||||
|
location: Location,
|
||||||
|
source: common_meta::error::Error,
|
||||||
|
},
|
||||||
|
|
||||||
#[snafu(display("Retry exceeded max times({}), message: {}", times, msg))]
|
#[snafu(display("Retry exceeded max times({}), message: {}", times, msg))]
|
||||||
RetryTimesExceeded { times: usize, msg: String },
|
RetryTimesExceeded { times: usize, msg: String },
|
||||||
|
|
||||||
|
#[snafu(display("Trying to write to a read-only kv backend: {}", name))]
|
||||||
|
ReadOnlyKvBackend {
|
||||||
|
name: String,
|
||||||
|
#[snafu(implicit)]
|
||||||
|
location: Location,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@@ -120,13 +134,15 @@ impl ErrorExt for Error {
|
|||||||
| Error::SendHeartbeat { .. }
|
| Error::SendHeartbeat { .. }
|
||||||
| Error::CreateHeartbeatStream { .. }
|
| Error::CreateHeartbeatStream { .. }
|
||||||
| Error::CreateChannel { .. }
|
| Error::CreateChannel { .. }
|
||||||
| Error::RetryTimesExceeded { .. } => StatusCode::Internal,
|
| Error::RetryTimesExceeded { .. }
|
||||||
|
| Error::ReadOnlyKvBackend { .. } => StatusCode::Internal,
|
||||||
|
|
||||||
Error::MetaServer { code, .. } => *code,
|
Error::MetaServer { code, .. } => *code,
|
||||||
|
|
||||||
Error::InvalidResponseHeader { source, .. }
|
Error::InvalidResponseHeader { source, .. }
|
||||||
| Error::ConvertMetaRequest { source, .. }
|
| Error::ConvertMetaRequest { source, .. }
|
||||||
| Error::ConvertMetaResponse { source, .. } => source.status_code(),
|
| Error::ConvertMetaResponse { source, .. }
|
||||||
|
| Error::GetFlowStat { source, .. } => source.status_code(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -206,43 +206,41 @@ pub async fn metasrv_builder(
|
|||||||
plugins: Plugins,
|
plugins: Plugins,
|
||||||
kv_backend: Option<KvBackendRef>,
|
kv_backend: Option<KvBackendRef>,
|
||||||
) -> Result<MetasrvBuilder> {
|
) -> Result<MetasrvBuilder> {
|
||||||
let (kv_backend, election) = match (kv_backend, &opts.backend) {
|
let (mut kv_backend, election) = match (kv_backend, &opts.backend) {
|
||||||
(Some(kv_backend), _) => (kv_backend, None),
|
(Some(kv_backend), _) => (kv_backend, None),
|
||||||
(None, BackendImpl::MemoryStore) => (Arc::new(MemoryKvBackend::new()) as _, None),
|
(None, BackendImpl::MemoryStore) => (Arc::new(MemoryKvBackend::new()) as _, None),
|
||||||
(None, BackendImpl::EtcdStore) => {
|
(None, BackendImpl::EtcdStore) => {
|
||||||
let etcd_client = create_etcd_client(opts).await?;
|
let etcd_client = create_etcd_client(opts).await?;
|
||||||
let kv_backend = {
|
let kv_backend = EtcdStore::with_etcd_client(etcd_client.clone(), opts.max_txn_ops);
|
||||||
let etcd_backend =
|
let election = EtcdElection::with_etcd_client(
|
||||||
EtcdStore::with_etcd_client(etcd_client.clone(), opts.max_txn_ops);
|
&opts.server_addr,
|
||||||
if !opts.store_key_prefix.is_empty() {
|
etcd_client,
|
||||||
Arc::new(ChrootKvBackend::new(
|
opts.store_key_prefix.clone(),
|
||||||
opts.store_key_prefix.clone().into_bytes(),
|
|
||||||
etcd_backend,
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
etcd_backend
|
|
||||||
}
|
|
||||||
};
|
|
||||||
(
|
|
||||||
kv_backend,
|
|
||||||
Some(
|
|
||||||
EtcdElection::with_etcd_client(
|
|
||||||
&opts.server_addr,
|
|
||||||
etcd_client.clone(),
|
|
||||||
opts.store_key_prefix.clone(),
|
|
||||||
)
|
|
||||||
.await?,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
(kv_backend, Some(election))
|
||||||
}
|
}
|
||||||
#[cfg(feature = "pg_kvbackend")]
|
#[cfg(feature = "pg_kvbackend")]
|
||||||
(None, BackendImpl::PostgresStore) => {
|
(None, BackendImpl::PostgresStore) => {
|
||||||
let pg_client = create_postgres_client(opts).await?;
|
let pg_client = create_postgres_client(opts).await?;
|
||||||
let kv_backend = PgStore::with_pg_client(pg_client).await.unwrap();
|
let kv_backend = PgStore::with_pg_client(pg_client).await.unwrap();
|
||||||
|
// TODO(jeremy, weny): implement election for postgres
|
||||||
(kv_backend, None)
|
(kv_backend, None)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if !opts.store_key_prefix.is_empty() {
|
||||||
|
info!(
|
||||||
|
"using chroot kv backend with prefix: {prefix}",
|
||||||
|
prefix = opts.store_key_prefix
|
||||||
|
);
|
||||||
|
kv_backend = Arc::new(ChrootKvBackend::new(
|
||||||
|
opts.store_key_prefix.clone().into_bytes(),
|
||||||
|
kv_backend,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
let in_memory = Arc::new(MemoryKvBackend::new()) as ResettableKvBackendRef;
|
let in_memory = Arc::new(MemoryKvBackend::new()) as ResettableKvBackendRef;
|
||||||
|
|
||||||
let selector = match opts.selector {
|
let selector = match opts.selector {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user