From e78b8071dd5c1cf187f1683e23b21e97e00bec41 Mon Sep 17 00:00:00 2001 From: Vdaas CI Date: Wed, 16 Sep 2026 04:41:25 +0000 Subject: [PATCH 1/2] :pencil: Update contents by GitHub Actions --- VERSIONS/GO_VERSION | 2 +- VERSIONS/VALD_LATEST_VERSION | 2 +- content/docs/api/build_proto.md | 9 +- content/docs/contributing/coding-style.md | 5 +- .../docs/contributing/contributing-guide.md | 36 +- content/docs/overview/about-vald.md | 14 +- content/docs/overview/architecture.md | 7 +- content/docs/overview/component/_index.md | 4 +- content/docs/overview/component/agent.md | 4 +- .../docs/overview/component/mirror-gateway.md | 6 +- .../docs/performance/continuous-benchmark.md | 8 +- content/docs/performance/loadtest.md | 16 +- .../performance/tuning-search-performance.md | 20 +- content/docs/release/changelog.md | 397 +- .../tutorial/get-started-with-faiss-agent.md | 9 +- content/docs/tutorial/get-started.md | 10 +- .../vald-agent-standalone-on-docker.md | 3 +- .../tutorial/vald-agent-standalone-on-k8s.md | 11 +- content/docs/usecase/usage-example.md | 4 +- content/docs/user-guides/client-api-config.md | 5 +- content/docs/user-guides/configuration.md | 10 +- content/docs/user-guides/deployment.md | 5 +- content/docs/user-guides/operations.md | 8 +- .../user-guides/read-replica-and-rotator.md | 22 +- content/docs/user-guides/upgrade-cluster.md | 8 +- .../docs/user-guides/vald-operator/_index.md | 384 + .../vald-operator/controller-spec.md | 346 + .../docs/user-guides/vald-operator/design.md | 204 + .../vald-operator/resource-and-topology.md | 110 + .../vald-operator/test-strategy.md | 60 + content/docs/v1.8/_index.md | 6 + content/docs/v1.8/api/_index.md | 37 + content/docs/v1.8/api/build_proto.md | 383 + content/docs/v1.8/api/filter-gateway.md | 1643 ++++ content/docs/v1.8/api/flush.md | 78 + content/docs/v1.8/api/insert.md | 377 + content/docs/v1.8/api/mirror-gateway.md | 96 + content/docs/v1.8/api/object.md | 270 + content/docs/v1.8/api/remove.md | 493 ++ content/docs/v1.8/api/search.md | 1639 ++++ content/docs/v1.8/api/status.md | 151 + content/docs/v1.8/api/update.md | 386 + content/docs/v1.8/api/upsert.md | 384 + content/docs/v1.8/contributing/_index.md | 7 + .../docs/v1.8/contributing/coding-style.md | 1418 ++++ .../v1.8/contributing/contributing-guide.md | 206 + content/docs/v1.8/contributing/development.md | 81 + .../v1.8/contributing/reviewer-guideline.md | 92 + .../v1.8/contributing/unit-test-guideline.md | 217 + content/docs/v1.8/overview/_index.md | 7 + content/docs/v1.8/overview/about-vald.md | 94 + content/docs/v1.8/overview/architecture.md | 115 + .../docs/v1.8/overview/component/_index.md | 112 + content/docs/v1.8/overview/component/agent.md | 158 + .../v1.8/overview/component/discoverer.md | 81 + .../v1.8/overview/component/filter-gateway.md | 164 + .../v1.8/overview/component/index-manager.md | 55 + .../v1.8/overview/component/lb-gateway.md | 85 + .../v1.8/overview/component/mirror-gateway.md | 88 + content/docs/v1.8/overview/data-flow.md | 113 + content/docs/v1.8/performance/_index.md | 7 + content/docs/v1.8/performance/benchmark.md | 40 + .../v1.8/performance/continuous-benchmark.md | 513 ++ content/docs/v1.8/performance/loadtest.md | 117 + .../performance/tuning-search-performance.md | 75 + content/docs/v1.8/release/_index.md | 7 + content/docs/v1.8/release/changelog.md | 6780 +++++++++++++++++ content/docs/v1.8/support/FAQ.md | 96 + content/docs/v1.8/support/_index.md | 7 + content/docs/v1.8/support/contacts.md | 26 + content/docs/v1.8/troubleshooting/_index.md | 23 + .../docs/v1.8/troubleshooting/client-side.md | 69 + .../v1.8/troubleshooting/mirror-gateway.md | 86 + .../docs/v1.8/troubleshooting/provisioning.md | 26 + content/docs/v1.8/tutorial/_index.md | 7 + .../tutorial/get-started-with-faiss-agent.md | 398 + content/docs/v1.8/tutorial/get-started.md | 475 ++ .../vald-agent-standalone-on-docker.md | 222 + .../tutorial/vald-agent-standalone-on-k8s.md | 405 + .../v1.8/tutorial/vald-multicluster-on-k8s.md | 314 + content/docs/v1.8/usecase/_index.md | 7 + content/docs/v1.8/usecase/usage-example.md | 98 + content/docs/v1.8/user-guides/_index.md | 7 + .../v1.8/user-guides/backup-configuration.md | 322 + .../v1.8/user-guides/capacity-planning.md | 200 + .../v1.8/user-guides/client-api-config.md | 708 ++ .../v1.8/user-guides/cluster-role-binding.md | 261 + .../docs/v1.8/user-guides/configuration.md | 422 + content/docs/v1.8/user-guides/deployment.md | 325 + .../user-guides/filtering-configuration.md | 203 + .../docs/v1.8/user-guides/index-correction.md | 50 + .../user-guides/mirroring-configuration.md | 100 + .../docs/v1.8/user-guides/network-policy.md | 66 + .../observability-configuration.md | 290 + content/docs/v1.8/user-guides/operations.md | 161 + .../user-guides/read-replica-and-rotator.md | 116 + content/docs/v1.8/user-guides/sdks.md | 41 + .../docs/v1.8/user-guides/upgrade-cluster.md | 160 + .../v1.8/user-guides/vald-operator/_index.md | 384 + .../vald-operator/controller-spec.md | 346 + .../v1.8/user-guides/vald-operator/design.md | 204 + .../vald-operator/resource-and-topology.md | 110 + .../vald-operator/test-strategy.md | 60 + prod | 2 +- .../architecture.png | Bin 0 -> 44073 bytes .../guides/operations/grafana-example.png | Bin 0 -> 165201 bytes .../read-replica-and-rotator/architecture.png | Bin 0 -> 224800 bytes .../v1.8/overview/component/agent/ngt.png | Bin 0 -> 62861 bytes .../component/agent/sidecar_backup.png | Bin 0 -> 57263 bytes .../component/agent/sidecar_restore.png | Bin 0 -> 64460 bytes .../filter-gateway/egress_filtering.svg | 4 + .../filter-gateway/ingress_filtering_blob.svg | 4 + .../ingress_filtering_vector.svg | 4 + .../mirror-gateway/full-mesh-connection.png | Bin 0 -> 26793 bytes .../mirror-gateway/mirror-gateway.png | Bin 0 -> 46215 bytes .../mirror-gateway/request-forwarding.png | Bin 0 -> 55962 bytes static/images/v1.8/overview/insert_flow.svg | 4 + static/images/v1.8/overview/remove_flow.svg | 4 + static/images/v1.8/overview/search_flow.svg | 4 + static/images/v1.8/overview/update_flow.svg | 4 + static/images/v1.8/overview/upsert_flow.svg | 4 + .../v1.8/overview/vald_basic_architecture.svg | 4 + .../v1.8/performance/benchmark-grafana.png | Bin 0 -> 265154 bytes .../provisioning_flow_chart.svg | 3 + static/images/v1.8/tutorial/getstarted.svg | 4 + .../tutorial/vald-agent-standalone-on-k8s.svg | 4 + .../tutorial/vald-multicluster-on-k8s.png | Bin 0 -> 25971 bytes static/images/v1.8/usecase/usecase_audio.png | Bin 0 -> 5091 bytes static/images/v1.8/usecase/usecase_data.png | Bin 0 -> 5495 bytes static/images/v1.8/usecase/usecase_image.png | Bin 0 -> 10061 bytes static/images/v1.8/usecase/usecase_text.png | Bin 0 -> 3635 bytes 131 files changed, 24923 insertions(+), 225 deletions(-) create mode 100644 content/docs/user-guides/vald-operator/_index.md create mode 100644 content/docs/user-guides/vald-operator/controller-spec.md create mode 100644 content/docs/user-guides/vald-operator/design.md create mode 100644 content/docs/user-guides/vald-operator/resource-and-topology.md create mode 100644 content/docs/user-guides/vald-operator/test-strategy.md create mode 100644 content/docs/v1.8/_index.md create mode 100644 content/docs/v1.8/api/_index.md create mode 100644 content/docs/v1.8/api/build_proto.md create mode 100644 content/docs/v1.8/api/filter-gateway.md create mode 100644 content/docs/v1.8/api/flush.md create mode 100644 content/docs/v1.8/api/insert.md create mode 100644 content/docs/v1.8/api/mirror-gateway.md create mode 100644 content/docs/v1.8/api/object.md create mode 100644 content/docs/v1.8/api/remove.md create mode 100644 content/docs/v1.8/api/search.md create mode 100644 content/docs/v1.8/api/status.md create mode 100644 content/docs/v1.8/api/update.md create mode 100644 content/docs/v1.8/api/upsert.md create mode 100644 content/docs/v1.8/contributing/_index.md create mode 100644 content/docs/v1.8/contributing/coding-style.md create mode 100644 content/docs/v1.8/contributing/contributing-guide.md create mode 100644 content/docs/v1.8/contributing/development.md create mode 100644 content/docs/v1.8/contributing/reviewer-guideline.md create mode 100644 content/docs/v1.8/contributing/unit-test-guideline.md create mode 100644 content/docs/v1.8/overview/_index.md create mode 100644 content/docs/v1.8/overview/about-vald.md create mode 100644 content/docs/v1.8/overview/architecture.md create mode 100644 content/docs/v1.8/overview/component/_index.md create mode 100644 content/docs/v1.8/overview/component/agent.md create mode 100644 content/docs/v1.8/overview/component/discoverer.md create mode 100644 content/docs/v1.8/overview/component/filter-gateway.md create mode 100644 content/docs/v1.8/overview/component/index-manager.md create mode 100644 content/docs/v1.8/overview/component/lb-gateway.md create mode 100644 content/docs/v1.8/overview/component/mirror-gateway.md create mode 100644 content/docs/v1.8/overview/data-flow.md create mode 100644 content/docs/v1.8/performance/_index.md create mode 100644 content/docs/v1.8/performance/benchmark.md create mode 100644 content/docs/v1.8/performance/continuous-benchmark.md create mode 100644 content/docs/v1.8/performance/loadtest.md create mode 100644 content/docs/v1.8/performance/tuning-search-performance.md create mode 100644 content/docs/v1.8/release/_index.md create mode 100644 content/docs/v1.8/release/changelog.md create mode 100644 content/docs/v1.8/support/FAQ.md create mode 100644 content/docs/v1.8/support/_index.md create mode 100644 content/docs/v1.8/support/contacts.md create mode 100644 content/docs/v1.8/troubleshooting/_index.md create mode 100644 content/docs/v1.8/troubleshooting/client-side.md create mode 100644 content/docs/v1.8/troubleshooting/mirror-gateway.md create mode 100644 content/docs/v1.8/troubleshooting/provisioning.md create mode 100644 content/docs/v1.8/tutorial/_index.md create mode 100644 content/docs/v1.8/tutorial/get-started-with-faiss-agent.md create mode 100644 content/docs/v1.8/tutorial/get-started.md create mode 100644 content/docs/v1.8/tutorial/vald-agent-standalone-on-docker.md create mode 100644 content/docs/v1.8/tutorial/vald-agent-standalone-on-k8s.md create mode 100644 content/docs/v1.8/tutorial/vald-multicluster-on-k8s.md create mode 100644 content/docs/v1.8/usecase/_index.md create mode 100644 content/docs/v1.8/usecase/usage-example.md create mode 100644 content/docs/v1.8/user-guides/_index.md create mode 100644 content/docs/v1.8/user-guides/backup-configuration.md create mode 100644 content/docs/v1.8/user-guides/capacity-planning.md create mode 100644 content/docs/v1.8/user-guides/client-api-config.md create mode 100644 content/docs/v1.8/user-guides/cluster-role-binding.md create mode 100644 content/docs/v1.8/user-guides/configuration.md create mode 100644 content/docs/v1.8/user-guides/deployment.md create mode 100644 content/docs/v1.8/user-guides/filtering-configuration.md create mode 100644 content/docs/v1.8/user-guides/index-correction.md create mode 100644 content/docs/v1.8/user-guides/mirroring-configuration.md create mode 100644 content/docs/v1.8/user-guides/network-policy.md create mode 100644 content/docs/v1.8/user-guides/observability-configuration.md create mode 100644 content/docs/v1.8/user-guides/operations.md create mode 100644 content/docs/v1.8/user-guides/read-replica-and-rotator.md create mode 100644 content/docs/v1.8/user-guides/sdks.md create mode 100644 content/docs/v1.8/user-guides/upgrade-cluster.md create mode 100644 content/docs/v1.8/user-guides/vald-operator/_index.md create mode 100644 content/docs/v1.8/user-guides/vald-operator/controller-spec.md create mode 100644 content/docs/v1.8/user-guides/vald-operator/design.md create mode 100644 content/docs/v1.8/user-guides/vald-operator/resource-and-topology.md create mode 100644 content/docs/v1.8/user-guides/vald-operator/test-strategy.md create mode 100644 static/images/v1.8/guides/observability-configuration/architecture.png create mode 100644 static/images/v1.8/guides/operations/grafana-example.png create mode 100644 static/images/v1.8/guides/read-replica-and-rotator/architecture.png create mode 100644 static/images/v1.8/overview/component/agent/ngt.png create mode 100644 static/images/v1.8/overview/component/agent/sidecar_backup.png create mode 100644 static/images/v1.8/overview/component/agent/sidecar_restore.png create mode 100644 static/images/v1.8/overview/component/filter-gateway/egress_filtering.svg create mode 100644 static/images/v1.8/overview/component/filter-gateway/ingress_filtering_blob.svg create mode 100644 static/images/v1.8/overview/component/filter-gateway/ingress_filtering_vector.svg create mode 100644 static/images/v1.8/overview/component/mirror-gateway/full-mesh-connection.png create mode 100644 static/images/v1.8/overview/component/mirror-gateway/mirror-gateway.png create mode 100644 static/images/v1.8/overview/component/mirror-gateway/request-forwarding.png create mode 100644 static/images/v1.8/overview/insert_flow.svg create mode 100644 static/images/v1.8/overview/remove_flow.svg create mode 100644 static/images/v1.8/overview/search_flow.svg create mode 100644 static/images/v1.8/overview/update_flow.svg create mode 100644 static/images/v1.8/overview/upsert_flow.svg create mode 100644 static/images/v1.8/overview/vald_basic_architecture.svg create mode 100644 static/images/v1.8/performance/benchmark-grafana.png create mode 100644 static/images/v1.8/troubleshooting/provisioning_flow_chart.svg create mode 100644 static/images/v1.8/tutorial/getstarted.svg create mode 100644 static/images/v1.8/tutorial/vald-agent-standalone-on-k8s.svg create mode 100644 static/images/v1.8/tutorial/vald-multicluster-on-k8s.png create mode 100644 static/images/v1.8/usecase/usecase_audio.png create mode 100644 static/images/v1.8/usecase/usecase_data.png create mode 100644 static/images/v1.8/usecase/usecase_image.png create mode 100644 static/images/v1.8/usecase/usecase_text.png diff --git a/VERSIONS/GO_VERSION b/VERSIONS/GO_VERSION index 6521720b4..f250c8f60 100644 --- a/VERSIONS/GO_VERSION +++ b/VERSIONS/GO_VERSION @@ -1 +1 @@ -1.24.5 +1.26.6 diff --git a/VERSIONS/VALD_LATEST_VERSION b/VERSIONS/VALD_LATEST_VERSION index 63c16ced9..27f9cd322 100644 --- a/VERSIONS/VALD_LATEST_VERSION +++ b/VERSIONS/VALD_LATEST_VERSION @@ -1 +1 @@ -1.7.17 +1.8.0 diff --git a/content/docs/api/build_proto.md b/content/docs/api/build_proto.md index 4a0dc700f..5e877b2d0 100644 --- a/content/docs/api/build_proto.md +++ b/content/docs/api/build_proto.md @@ -1,6 +1,6 @@ --- title: "Build_proto_api" -date: 2024-08-08T00:23:48+09:00 +date: 2026-09-16T04:39:39Z draft: false weight: 1100 description: How to build gRPC proto files for calling API to your Vald cluster @@ -41,12 +41,10 @@ Let's build proto files using your favorite programming language. There are 3 steps to building API proto: 1. Install gRPC tools - - gRPC official document provides [the way to install for each language](https://grpc.io/docs/languages/).
If your favorite programming language is not there, you can find 3rd party tools for building. 1. Download Vald api proto files and external dependence - - [vald api proto](https://github.com/vdaas/vald/tree/main/apis/proto/v1/vald) - [vald payload proto](https://github.com/vdaas/vald/tree/main/apis/proto/v1/payload) - [googleapis](https://github.com/googleapis/googleapis) @@ -96,7 +94,7 @@ There are many tools for building proto in Rust, we use [tonic](https://github.c [package] name = "vald-grpc" version = "0.1.0" - edition = "2021" + edition = "2024" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -154,7 +152,6 @@ There are many tools for building proto in Rust, we use [tonic](https://github.c ``` 1. Implement `build.rs` and Build proto - 1. `build.rs` ```rust @@ -207,7 +204,6 @@ There are many tools for building proto in Rust, we use [tonic](https://github.c ``` 1. Implement code using client - 1. `lib.rs` Import build proto in `src/lib.rs` @@ -241,7 +237,6 @@ There are many tools for building proto in Rust, we use [tonic](https://github.c 1. `src/client.rs` There are 4 steps in `src/client.rs`: - 1. Load dataset 1. Insert vector to Vald cluster 1. Search nearest neighbor vectors from Vald cluster after indexing finished diff --git a/content/docs/contributing/coding-style.md b/content/docs/contributing/coding-style.md index 5ac722223..981c48d1c 100644 --- a/content/docs/contributing/coding-style.md +++ b/content/docs/contributing/coding-style.md @@ -1,6 +1,6 @@ --- title: "Coding Style_contributing" -date: 2024-10-16T16:03:39+09:00 +date: 2026-09-16T04:39:37Z draft: false weight: 300 description: Coding style for Vald project @@ -815,7 +815,6 @@ tests := map[string]func(t *testing.T) test { Test case names should be readable, meaningful, and understandable easily. If you create a new test, the test name should be named based on the below-naming templates - - Success cases: - Start with `success` or `{verb} success` or `success {verb}` - End with the condition `when {condition}` or `with {condition} @@ -988,7 +987,6 @@ Still, in some cases, you may need to change the generated code to meet your req 1. goleak usage There are two methods for valid goroutine leak: - 1. Use `goleak.VerifyNone()` to validate it on each test cases. 1. Use `goleak.VerifyTestMain()` to validate it on each package. @@ -1135,7 +1133,6 @@ Still, in some cases, you may need to change the generated code to meet your req By default, when testing the function of the struct, the target struct initialization is implemented by setting the data from the `fields` defined in the test case. This initialization method has a few disadvantages: - 1. When there are many fields in the struct, it is hard to set them all. 1. The default value is the zero value of the type, not the struct default value from the struct initialization function. diff --git a/content/docs/contributing/contributing-guide.md b/content/docs/contributing/contributing-guide.md index 67e675b7d..6acac0304 100644 --- a/content/docs/contributing/contributing-guide.md +++ b/content/docs/contributing/contributing-guide.md @@ -1,6 +1,6 @@ --- title: "Contributing Guide_contributing" -date: 2024-01-10T18:16:39+09:00 +date: 2026-09-16T04:39:37Z draft: false weight: 100 description: Contribute guideline for the Vald project @@ -30,11 +30,11 @@ We appreciate your help! ## Contributing Issue -We use [Github Issues](https://github.com/vdaas/vald/issues) to track issues within this repository. -If you can determine the problem you are facing is a bug or issue, you can easily submit the issues. +We use [GitHub Issues](https://github.com/vdaas/vald/issues) to track issues within this repository. +If you can determine the problem you are facing is a bug or issue, you can submit the issue. -If you are unsure whether you have found a bug or security-related issues, please consider asking in the [Vald Slack](https://join.slack.com/t/vald-community/shared_invite/zt-db2ky9o4-R_9p2sVp8xRwztVa8gfnPA) first. -If the behavior you are seeing is confirmed as a bug or issue, it can easily be re-raised in the issue tracker. +When unsure whether you have found a bug or security-related issue, please consider asking in the [Vald Slack](https://join.slack.com/t/vald-community/shared_invite/zt-db2ky9o4-R_9p2sVp8xRwztVa8gfnPA) first. +If the behavior you are seeing is confirmed as a bug or issue, it can be re-raised in the issue tracker. ### Bug Report @@ -46,7 +46,7 @@ Please try to write as detailed as possible in your bug report. When filing an issue, make sure to answer these five questions: -1. What version of Vald are you using (vald docker image version)? +1. What version of Vald are you using (Vald Docker image version)? 2. What operating system and processor architecture are you using? 3. What did you do? 4. What did you expect to see? @@ -60,7 +60,7 @@ The Vald is being developed based on the design-driven process.
The significant change to the library or the architecture should be discussed first. We may ask for detailed documentation before your proposal is accepted.
-Your proposal will be reviewed, discussed, and decided whether it is accepted or declined. +Your proposal will be reviewed, discussed, and accepted or declined. Please submit the proposal [here](https://github.com/vdaas/vald/issues/new?assignees=&labels=type%2Ffeature%2C+priority%2Flow%2C+team%2Fcore&template=feature_request&title=) @@ -102,7 +102,7 @@ Follow these steps to make a contribution to any of our open source repositories 2. Set your name and email (these should match the information on your submitted CLA) ```bash - git config --global user.name "Firstname Lastname" + git config --global user.name "Your Name" git config --global user.email "your_email@example.com" ``` @@ -115,9 +115,9 @@ Follow these steps to make a contribution to any of our open source repositories 5. Push to the forked branch (git push origin my-new-feature) 6. Create new Pull Request -Each pull request and commit should be small enough to contain only one purpose. +Each pull request and commit should be small enough to address a single purpose. -Your pull request is much more likely to be accepted if: +Your pull request has a higher chance of being accepted if: - Your pull request includes tests @@ -127,14 +127,14 @@ Your pull request is much more likely to be accepted if: ### How to contribute unit test code -To contribute unit test code, the steps are almost the same as [contribute source code](#how-to-contribute-source-code), but there are some extra steps when implementing a test function which is not implemented before: +To contribute unit test code, the steps are almost the same as [contribute source code](#how-to-contribute-source-code), but there are some extra steps when adding a test function that has not been added before: 1. Execute `make gotests/gen` command under Vald repository -1. Move the test function which you would like to implement above the placeholder `// NOT IMPLEMENTED BELOW` -1. Uncomment and implement the test function +1. Move the test function which you would like to write above the placeholder `// NOT IMPLEMENTED BELOW` +1. Remove the comment markers and write the test function -Vald implmentes unit test code to ensure the quality of Vald. -Each implementation file comes with its unit test file, with `*_test.go` postfix. +Vald implements unit test code to ensure the quality of Vald. +Each implementation file comes with its unit test file, with `*_test.go` suffix. In Vald, unit test files are looks like: ```go @@ -197,10 +197,10 @@ Name your branches with prefixes: `[type]/[area]/[description]` | Field | Explanation | Naming Rule | | :---------- | :------------------------------------ | :------------------------------------------------------------------------------------------------------------------------ | -| type | The PR type | The type of PR can be feature, bug, refactoring, benchmark, security, documentation, dependencies, ci, test, or etc... | +| type | The PR type | Accepted PR types: feature, bug, refactoring, benchmark, security, documentation, dependencies, ci, test, etc. | | area | Area of context | The area of PR can be gateway, agent, agent-sidecar, lb-gateway, or etc... | | description | Summarized description of your branch | The description must be hyphenated. Please use [a-zA-Z0-9] and hyphen as characters, and do not use any other characters. | -(\*) If you changed multiple areas, please list each area with "-". +(\*) If you changed more than one area, please list each area with `-`. -For example, when you add a new feature for internal/servers, the name of the branch will be `feature/internal/add-newfeature-for-servers`. +For example, when you add a new feature for internal/servers, the name of the branch will be `feature/internal/add-new-feature-for-servers`. diff --git a/content/docs/overview/about-vald.md b/content/docs/overview/about-vald.md index 97d24c1d4..b8b2833aa 100644 --- a/content/docs/overview/about-vald.md +++ b/content/docs/overview/about-vald.md @@ -1,6 +1,6 @@ --- title: "About Vald_overview" -date: 2025-09-02T04:53:44Z +date: 2026-09-16T04:39:38Z draft: false weight: 100 description: What is Vald and what you can do with Vald @@ -21,7 +21,7 @@ Vald is a highly scalable distributed fast approximate nearest neighbor dense ve Vald is designed and implemented based on Cloud-Native architecture. -It uses the fastest ANN Algorithm [NGT](https://github.com/yahoojapan/NGT) to search neighbors. +It uses the fastest ANN Algorithm [NGT](https://github.com/NGT-labs/NGT) to search neighbors. Vald has automatic vector indexing and index backup, and horizontal scaling which made for searching from billions of feature vector data. @@ -32,39 +32,31 @@ Vald is easy to use, feature-rich and highly customizable as you needed. - Asynchronous Auto Indexing - - Usually the graph requires locking during indexing, which causes stop-the-world. But Vald uses distributed index graphs so it continues to work during indexing. - Customizable Ingress/Egress Filtering - - Vald implements it's own highly customizable Ingress/Egress filter. - Which can be configured to fit the gRPC interface. - Ingress Filter: Ability to Vectorize through filter on request. - Egress Filter: rerank or filter the searching result with your own algorithm. - Cloud-native based vector searching engine - - Horizontal scalable on memory and CPU for your demand. - Auto Backup for Index data - - Vald supports to backup Vald Agent index data using Object Storage or Persistent Volume. - Distributed Indexing - - Vald distributes vector index to multiple agents, and each agent stores different index. - Index Replication - - Vald stores each index in multiple agents which enables index replicas. - Automatically rebalancing the replica when some Vald agent goes down. - Easy to use - - Vald can be easily installed in a few steps. - Highly customizable - - You can configure the number of vector dimensions, the number of replica and etc. - Multi language supported @@ -85,7 +77,7 @@ Vald supports similarity searching. Vald is based on Kubernetes and Cloud-Native architecture, which means Vald is highly scalable. You can easily scale Vald by changing Vald's configuration. -Vald uses the fastest ANN Algorithm [NGT](https://github.com/yahoojapan/NGT) to search neighbors by default, but users can switch to another vector searching engine in Vald to support the best performance for your use case. +Vald uses the fastest ANN Algorithm [NGT](https://github.com/NGT-labs/NGT) to search neighbors by default, but users can switch to another vector searching engine in Vald to support the best performance for your use case. Also, Vald supports auto-healing, to reduce running and maintenance costs. Vald implements the backup mechanism to support disaster recovery. Whenever one of the Vald Agent instances is down, the new Vald Agent instance will be created automatically and the data will be recovered automatically. diff --git a/content/docs/overview/architecture.md b/content/docs/overview/architecture.md index 84dffe0ff..435d200e7 100644 --- a/content/docs/overview/architecture.md +++ b/content/docs/overview/architecture.md @@ -1,6 +1,6 @@ --- title: "Architecture_overview" -date: 2024-08-08T00:23:48+09:00 +date: 2026-09-16T04:39:38Z draft: false weight: 200 description: High-Level architecture design and overview of each component @@ -35,7 +35,7 @@ Vald is based on the following technologies. Vald contains multiple components and configurations. Helm helps us to manage those manifests and provides a better and easy way to deploy and configure Vald. -- [NGT](https://github.com/yahoojapan/NGT) +- [NGT](https://github.com/NGT-labs/NGT) NGT is one of the core components of Vald. NGT is a super-fast vector search engine used by Vald to guarantee the high performance of Vald. @@ -63,7 +63,6 @@ Here are the concepts of Vald. - Kubernetes based Vald can integrate with Kubernetes which enables the following features. - - Orchestrated Kubernetes supports container orchestration. All components in Vald can be managed by Kubernetes automatically. @@ -96,7 +95,7 @@ We will introduce each component and why it is needed in Vald. ### Vald Agent Vald Agent is the core component of Vald, the approximate nearest neighbor search engine, and stores the graph tree construction on memory for indexing the vectors. -Vald Agent uses [yahoojapan/NGT](https://github.com/yahoojapan/ngt) as a core library. +Vald Agent uses [NGT-labs/NGT](https://github.com/NGT-labs/NGT) as a core library. ### Vald LB Gateway diff --git a/content/docs/overview/component/_index.md b/content/docs/overview/component/_index.md index 74fa21165..a8521c638 100644 --- a/content/docs/overview/component/_index.md +++ b/content/docs/overview/component/_index.md @@ -1,6 +1,6 @@ --- title: "Component_overview/Component" -date: 2024-02-15T17:10:08+09:00 +date: 2026-09-16T04:39:38Z draft: false weight: 0 --- @@ -73,7 +73,7 @@ In this section, we will describe what is Vald Agent and the corresponding compo ### Vald Agent Vald Agent provides functionalities to perform approximate nearest neighbor search. -Agent-NGT uses [yahoojapan/NGT](https://github.com/yahoojapan/NGT) as a core library. +Agent-NGT uses [NGT-labs/NGT](https://github.com/NGT-labs/NGT) as a core library. And Agent-Faiss uses [facebookresearch/faiss](https://github.com/facebookresearch/faiss) as a core library. Each Vald Agent pod has its own vector data space because only several Vald Agents are selected to be inserted/updated in a single insert/update request. diff --git a/content/docs/overview/component/agent.md b/content/docs/overview/component/agent.md index 091f2e5c0..fa8496f81 100644 --- a/content/docs/overview/component/agent.md +++ b/content/docs/overview/component/agent.md @@ -1,6 +1,6 @@ --- title: "Agent_overview/Component" -date: 2024-05-23T16:15:24+09:00 +date: 2026-09-16T04:39:38Z draft: false weight: 100 description: Stores index data and Search ANN vectors @@ -45,7 +45,7 @@ as core algorithm layer. #### Vald Agent NGT -Vald Agent NGT uses [NGT](https://github.com/yahoojapan/NGT) as an algorithm. +Vald Agent NGT uses [NGT](https://github.com/NGT-labs/NGT) as an algorithm. The main functions are the followings: diff --git a/content/docs/overview/component/mirror-gateway.md b/content/docs/overview/component/mirror-gateway.md index befcff1ae..ddb5a9cd3 100644 --- a/content/docs/overview/component/mirror-gateway.md +++ b/content/docs/overview/component/mirror-gateway.md @@ -1,6 +1,6 @@ --- title: "Mirror Gateway_overview/Component" -date: 2024-02-15T17:10:09+09:00 +date: 2026-09-16T04:39:38Z draft: false weight: 600 description: Gateway for operating Vald cluster compatible with Multi-region and Multi AZ @@ -66,27 +66,23 @@ Here's an overview of how the Mirror Gateway handles failures for each type of r For more information about status code, please refer to [Mirror Gateway Troubleshooting](/docs/troubleshooting/mirror-gateway). - Insert Request - - If the target host returns a status code of `ALREADY_EXISTS`, the Update request is sent to this host. - If the target host returns a status code other than `OK`, `ALREADY_EXISTS`, the Mirror Gateway returns that status code without continuous processing. - If all target hosts return a status code `ALREADY_EXISTS`, the Mirror Gateway returns `ALREADY_EXISTS`. - If all target hosts return a status code `OK` or `ALREADY_EXISTS`, the Mirror Gateway returns `OK`. - Update Request - - If the target host returns a status code `NOT_FOUND`, the Insert request is sent to this host. - If the target host returns a status code other than `OK`, `ALREADY_EXISTS`, the Mirror Gateway returns that status code without continuous processing. - If all target hosts return a status code `ALREADY_EXISTS`, the Mirror Gateway returns `ALREADY_EXISTS`. - If all target hosts return a status code `OK` or `ALREADY_EXISTS`, the Mirror Gateway returns `OK`. - Upsert Request - - If all target hosts return a status code `ALREADY_EXISTS`, the Mirror Gateway returns `ALREADY_EXISTS`. - If the target host returns a status code other than `OK` or `ALREADY_EXISTS`, the Mirror Gateway returns that status code without continuous processing. - If all target hosts return a status code `OK` or `ALREADY_EXISTS`, the Mirror Gateway returns `OK`. - Remove/RemoveByTimestamp Request - - If all target hosts return a status code `NOT_FOUND`, the Mirror Gateway returns `NOT_FOUND`. - If the target host returns a status code other than `OK` or `NOT_FOUND`, the Mirror Gateway returns that status code without continuous processing. - If all target hosts return a status code `OK` or `NOT_FOUND`, the Mirror Gateway returns `OK`. diff --git a/content/docs/performance/continuous-benchmark.md b/content/docs/performance/continuous-benchmark.md index ec27a5b6a..d549986ac 100644 --- a/content/docs/performance/continuous-benchmark.md +++ b/content/docs/performance/continuous-benchmark.md @@ -1,6 +1,6 @@ --- title: "Continuous Benchmark_performance" -date: 2024-10-16T16:03:40+09:00 +date: 2026-09-16T04:39:40Z draft: false weight: 300 description: Performance verification using continuous benchmark tool @@ -50,7 +50,7 @@ Benchmark Operator manages benchmark job according to the applied manifest. ### ValdBenchmarkJob -[`ValdBenchmarkJob`](https://github.com/vdaas/vald/blob/main/charts/vald-benchmark-operator/crds/valdbenchmarkjob.yaml) is used for executing single benchmark job. +[`ValdBenchmarkJob`](https://github.com/vdaas/vald/blob/main/charts/operator/benchmark/crds/valdbenchmarkjob.yaml) is used for executing single benchmark job. And, Benchmark Operator also applies it to the Kubernetes cluster based on `ValdBenchmarkScenarioResource`. @@ -184,7 +184,7 @@ And, Benchmark Operator also applies it to the Kubernetes cluster based on `Vald ### ValdBenchmarkScenario -[`ValdBenchmarkScenario`](https://github.com/vdaas/vald/blob/main/charts/vald-benchmark-operator/crds/valdbenchmarkscenario.yaml) is used for executing single or multiple benchmark job. +[`ValdBenchmarkScenario`](https://github.com/vdaas/vald/blob/main/charts/operator/benchmark/crds/valdbenchmarkscenario.yaml) is used for executing one or more benchmark jobs. Benchmark Operator decomposes manifest and creates benchmark resources one by one. The `target` and `dataset` property are the global config for scenario, they can be overwritten when each job has own config. @@ -457,7 +457,7 @@ logging: -For more details of the configuration of `vald-benchmark-operator-release`, please refer to [here](https://github.com/vdaas/vald/blob/main/charts/vald-benchmark-operator/values.yaml) +For more details of the configuration of `vald-benchmark-operator-release`, please refer to [here](https://github.com/vdaas/vald/blob/main/charts/operator/benchmark/values.yaml) 1. Add Vald repo into the helm repo diff --git a/content/docs/performance/loadtest.md b/content/docs/performance/loadtest.md index e05375b3c..5a82eda23 100644 --- a/content/docs/performance/loadtest.md +++ b/content/docs/performance/loadtest.md @@ -1,6 +1,6 @@ --- title: "Loadtest_performance" -date: 2025-09-02T04:53:40Z +date: 2026-09-16T04:39:40Z draft: false weight: 200 description: Shows how to perform load test in the Vald cluster @@ -43,14 +43,14 @@ Please refer to the [Sample configuration file](https://github.com/vdaas/vald/bl Here are the important configurations and following the explanation. -| Name | Description | Example | -| :---------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------ | -| service | By setting the value to the gateway, you can test the Vald cluster through the Vald gateway.
By setting the value to agent, you can test the Vald agent directly. | gateway or agent | +| Name | Description | Example | +| :---------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------- | +| service | By setting the value to the gateway, you can test the Vald cluster through the Vald gateway.
By setting the value to agent, you can test the Vald agent directly. | gateway or agent | | operation | To perform the specific load test action to the Vald cluster. | insert, streaminsert, search or streamsearch | -| dataset | The dataset is used in insert and stream insert operation. | | -| concurrency | The number of concurrent execution of the load test. | | -| batch_size | The batch size of the dataset. | | -| addr | The cluster you want to test | | +| dataset | The dataset is used in insert and stream insert operation. | | +| concurrency | The number of concurrent execution of the load test. | | +| batch_size | The batch size of the dataset. | | +| addr | The cluster you want to test | | ## Execute load test diff --git a/content/docs/performance/tuning-search-performance.md b/content/docs/performance/tuning-search-performance.md index 8da2953f3..fbc8c6a23 100644 --- a/content/docs/performance/tuning-search-performance.md +++ b/content/docs/performance/tuning-search-performance.md @@ -1,6 +1,6 @@ --- title: "Tuning Search Performance_performance" -date: 2024-02-15T17:10:10+09:00 +date: 2026-09-16T04:39:40Z draft: false weight: 0 description: "" @@ -59,17 +59,17 @@ There is a trade-off between search speed and accuracy, so tuning accuracy at fi On the client side, parameters of [`Search.Config`](/docs/docs/api/search#input) will affect the search result. -| | description | how does it affect? | memo | -| :---------- | :---------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------ | -| radius | the search radius for NGT
[ref: search_radius](https://github.com/yahoojapan/NGT/tree/main/bin/ngt#search) | Define the search range when NGT searches the nearest neighbors | recommended value: `-1` | -| epsilon | the search coefficient for NGT
[ref: search_range_coefficient](https://github.com/yahoojapan/NGT/tree/main/bin/ngt#search) | Expansion factor of the NGT search range.
Search operation time increases when the epsilon is big. | recommended value range: `0.01 ~ 0.3`
default value: `0.1` | -| timeout(ns) | max time duration until receiving search results. | An error will be returned if the set `num` search results cannot be obtained within the set time.
By setting `min_num`, the search results will be returned if more than `min_num` can be searched within the time. | default value: `3,000,000,000ns` | +| | description | how does it affect? | memo | +| :---------- | :-------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------ | +| radius | the search radius for NGT
[ref: search_radius](https://github.com/NGT-labs/NGT/tree/main/bin/ngt#search) | Define the search range when NGT searches the nearest neighbors | recommended value: `-1` | +| epsilon | the search coefficient for NGT
[ref: search_range_coefficient](https://github.com/NGT-labs/NGT/tree/main/bin/ngt#search) | Expansion factor of the NGT search range.
Search operation time increases when the epsilon is big. | recommended value range: `0.01 ~ 0.3`
default value: `0.1` | +| timeout(ns) | max time duration until receiving search results. | An error will be returned if the set `num` search results cannot be obtained within the set time.
By setting `min_num`, the search results will be returned if more than `min_num` can be searched within the time. | default value: `3,000,000,000ns` | ### Cluster-side On the cluster side, these parameters can be set by `values.yaml`, affect the search result. -| | description | how does it affect? | Memo | -| :--------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------ | -| agent.ngt.creation_edge_size | Number of nodes connected to one node
[ref: no_of_edges](https://github.com/yahoojapan/NGT/tree/main/bin/ngt#create) | It helps reduce unreachable edges.
The larger it is, the denser the graph structure will be, but the memory usage, search speed, and index construction time will increase accordingly. | default value: `20` | -| agent.ngt.search_edge_size | Number of nodes to search from the origin node when searching
[ref: no_of_edges_at_search_at](https://github.com/yahoojapan/NGT/tree/main/bin/ngt#create) | The number of nodes to search will increase.
Accuracy will be higher, but speed will be lower.
Adjust if adjusting the radius and epsilon does not improve the situation. | default value: `10` | +| | description | how does it affect? | Memo | +| :--------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------ | +| agent.ngt.creation_edge_size | Number of nodes connected to one node
[ref: no_of_edges](https://github.com/NGT-labs/NGT/tree/main/bin/ngt#create) | It helps reduce unreachable edges.
The larger it is, the denser the graph structure will be, but the memory usage, search speed, and index construction time will increase accordingly. | default value: `20` | +| agent.ngt.search_edge_size | Number of nodes to search from the origin node when searching
[ref: no_of_edges_at_search_at](https://github.com/NGT-labs/NGT/tree/main/bin/ngt#create) | The number of nodes to search will increase.
Accuracy will be higher, but speed will be lower.
Adjust if adjusting the radius and epsilon does not improve the situation. | default value: `10` | diff --git a/content/docs/release/changelog.md b/content/docs/release/changelog.md index 932ea3c6b..4c7b42194 100644 --- a/content/docs/release/changelog.md +++ b/content/docs/release/changelog.md @@ -1,6 +1,6 @@ --- title: "Changelog_release" -date: 2025-09-02T04:53:42Z +date: 2026-09-16T04:39:37Z draft: false weight: 0 description: "" @@ -11,6 +11,233 @@ menu: # CHANGELOG +## v1.8.0 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.8.0
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.8.0 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.8.0
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.8.0 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.8.0
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.8.0 +
Gateways + docker pull vdaas/vald-lb-gateway:v1.8.0
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.8.0
+ docker pull vdaas/vald-filter-gateway:v1.8.0
+ docker pull ghcr.io/vdaas/vald/vald-filter-gateway:v1.8.0 +
Index Manager + docker pull vdaas/vald-manager-index:v1.8.0
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.8.0 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.8.0
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.8.0 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.8.0) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.8.0/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.8.0/charts/operator/helm/README.md) + +### Changes + + + +### Description + +:sparkle: New feature + +- [Feature] Add Vald Operator (enabled multiple cluster operation) [#3557] +- feat(helm): add configurable image registry for all images [#3538] +- Support dynamic edge size parameter for Search API [#3480] +- add creator.creation_pool_size [#3443] +- Add TiKV client for Metadata API [#3401] +- Apply default epsilon to index creation as well on local branch [#3320] +- hot-reload (minimal test impl, opt-in) [#3247] +- add txnkv [#3311] +- Add resource stats gRPC API [#3243] +- add internal kvs [#3281] +- add metadata manifest [#3260] +- Add optional tls support for gateway ingress [#3226] +- add executeRPC function for gRPC client [#3079] +- [Feature] Add Persistent VQueue [#3058] +- [Feature] Add Production Bidirectional KVS [#3057] + +:zap: Improve performance + +- resolve broadcast concurrency and tune connection pool [#3518] +- improve rangeConns's performance for Release v1.7.17 [#3039] + +:recycle: Refactor + +- Refactor gRPC codes and update libs & feat: Migrate from npm to bun [#3264] +- ♻️ Refactor: bidirectional map module structure [#3218] +- refactor: add missing k8s type aliases to types.go [#3439] +- Migrate from bincode to wincode [#3448] +- Omit usearch implementation from Go [#3319] +- Update default parameters for gRPC [#3239] +- Migrate go tool [#3209] +- Add strings.TrimForCompare func [#3064] +- check port-forward enabled/disabled [#3061] +- Add RBAC permissions for DaemonSet access [#3059] + +:bug: Bugfix + +- hotfix: Remove stray ci-container in release workflow [#3584] +- fix: skip NGT AVX2 on arm64 [#3580] +- fix SBOM [#3565] +- [Bugfix] fix SIGABRT problem for Agent NGT [#3531] +- fix typo [#3453] +- fix(rust/proto): resolve build errors by correcting prost_types usage [#3403] +- :bug: fix grpc benchmark [#3342] +- hotfix: Use init instead of once.Do to prevent data race [#3328] +- fix docker build condition [#3289] +- fix tag [#3277] +- fix extra tag process [#3275] +- [Bugfix] unknown serviceAccount use for serviceAccountName [#3252] +- fix faiss_create_index_ivfpq [#3249] +- Added serviceAccountName to lb/filter gateways and bugfix [#3230] +- Fix dashboard variables in overview board [#3030] + +:pencil2: Document + +- add ADOPTERS.md, MAINTAINERS.md [#3556] +- Add AGENTS.md for AI Co-Worker standardization [#3473] +- restore loadtest.md [#3187] + +:white_check_mark: Testing + +- feat(e2e): Implement and refactor metrics collection for E2E tests [#3418] +- Add support for patial execution of e2e v2 [#3297] +- Unified E2E V2 YAML setttings using envvars [#3105] +- [VALD-350] Add retry_until_success_timeout and use it in readreplica CI [#3104] +- Run E2E Test as a Kubernetes Job [#3073] +- [VALD-326] E2E with Kind & Support readreplica/rollout case [#3054] +- Add Agent Execution for E2E V2 [#3074] +- Added Loop test case [#3063] + +:green_heart: CI + +- fix CI permission [#3609] +- fix scan docker image job [#3602] +- hotfix: Pin sha of trivy and fix permission [#3601] +- hotfix: Fix and update CodeQL versions [#3600] +- fix update helm chart job [#3599] +- fix release workflow [#3594] +- Use ubuntu-latest in release workflow [#3590] +- hotfix: Install chaos-mesh using Helm [#3521] +- chore(coderabbit): add coderabbit configuration [#3464] +- Merge dev and ci container [#3425] +- Use golangci-lint as a formatter [#3405] +- Use ubuntu-slim for lightweight jobs [#3312] +- Add wait --for=create [#3303] +- hotfix: Support workflow_dispatch images for e2e v2 [#3295] +- hotfix: build target architecture for main branch [#3268] +- Fix joining architectures [#3262] +- hotfix: docker build failure on GitHub Actions [#3258] +- Multi platform build with native amd64/arm64 GitHub hosted runner [#3251] +- Add GitHub actions workflow linter [#3213] +- hotfix: ARM64 dev-container build [#3229] +- hotfix: Unify image tag by nightly for format [#3214] +- Use --retry for get.helm.sh [#3195] +- revert: Platform Separation [#3192] +- hotfix: Use extra tags to push nightly [#3189] +- hotfix: separated unification job [#3186] +- Avoid overwrite for ghcr.io [#3182] +- Try to separate amd64/arm64 builds [#3178] +- hotfix: Support debugging for ChatOps [#3055] +- hotfix: Handle cherrypick without diff [#3047] +- [VALD-361] Backport changes into release v1.7 by default [#3050] +- [VALD-359] Support Mac C flags [#3040] + +:chart_with_upwards_trend: metrics/tracing + +- Add opentelemetry collector dashboard [#3208] +- [VALD-147] Implement MetricMiddleware [#3010] +- hotfix: Support prometheus without container label [#3043] + +:arrow_up: Update dependencies + +- Update dependencies [#3606] +- update dependencies and modules [#3440] +- update rust dependencies and migration [#3436] + +:lock: Security + +:art: Design + +:handshake: Contributor + +- add new chatops permissions for contributor [#3548] +- docs: add nac-39 as a contributor for infra [#3234] +- docs: add Sh1ragami as a contributor for tool, and code [#3210] +- docs: add jumpei527 as a contributor for code, and maintenance [#3205] + +### Related Issue + + + + + + +### Versions + + + +- Vald Version: v1.7.17 +- Go Version: v1.26.5 +- Rust Version: v1.97.1 +- Docker Version: v29.6.2 +- Kubernetes Version: v1.36.3 +- Helm Version: v4.2.3 +- NGT Version: v2.7.4 +- Faiss Version: v1.14.3 + +### Checklist + + + + +- [x] I have read the [CONTRIBUTING](https://github.com/vdaas/vald/blob/main/CONTRIBUTING.md) document and completed [our CLA agreement](https://cla-assistant.io/vdaas/vald). +- [x] I have checked open [Pull Requests](https://github.com/vdaas/vald/pulls) for the similar feature or fixes? + +### Special notes for your reviewer + + + ## v1.7.17 ### Docker images @@ -70,7 +297,7 @@ menu: - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.7.17) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.7.17/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.17/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.17/charts/operator/helm/README.md) ### Changes @@ -189,7 +416,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.7.16) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.7.16/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.16/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.16/charts/operator/helm/README.md) ### Changes @@ -270,7 +497,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.7.15) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.7.15/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.15/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.15/charts/operator/helm/README.md) ### Changes @@ -373,7 +600,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.7.14) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.7.14/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.14/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.14/charts/operator/helm/README.md) ### Changes @@ -512,7 +739,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.7.13) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.7.13/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.13/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.13/charts/operator/helm/README.md) ### Changes @@ -653,7 +880,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.7.12) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.7.12/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.12/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.12/charts/operator/helm/README.md) ### Changes @@ -760,7 +987,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.7.11) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.7.11/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.11/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.11/charts/operator/helm/README.md) ### Changes @@ -884,7 +1111,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.7.10) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.7.10/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.10/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.10/charts/operator/helm/README.md) ### Changes @@ -976,7 +1203,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.7.9) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.7.9/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.9/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.9/charts/operator/helm/README.md) ### Changes @@ -1090,7 +1317,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.7.8) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.7.8/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.8/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.8/charts/operator/helm/README.md) ### Changes @@ -1159,7 +1386,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.7.7) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.7.7/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.7/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.7/charts/operator/helm/README.md) ### Changes @@ -1304,7 +1531,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.7.6) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.7.6/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.6/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.6/charts/operator/helm/README.md) ### Changes @@ -1424,7 +1651,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.7.5) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.7.5/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.5/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.5/charts/operator/helm/README.md) ### Changes @@ -1530,7 +1757,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.7.4) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.7.4/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.4/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.4/charts/operator/helm/README.md) ### Changes @@ -1609,7 +1836,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.7.3) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.7.3/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.3/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.3/charts/operator/helm/README.md) ### Changes @@ -1692,7 +1919,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.7.2) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.7.2/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.2/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.2/charts/operator/helm/README.md) ### Changes @@ -1768,7 +1995,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.7.1) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.7.1/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.1/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.1/charts/operator/helm/README.md) ### Changes @@ -1842,7 +2069,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.7.0) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.7.0/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.0/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.0/charts/operator/helm/README.md) ### Changes @@ -2000,7 +2227,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.6.3) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.6.3/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.6.3/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.6.3/charts/operator/helm/README.md) ### Changes @@ -2075,7 +2302,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.6.2) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.6.2/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.6.2/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.6.2/charts/operator/helm/README.md) ### Changes @@ -2152,7 +2379,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.6.1) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.6.1/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.6.1/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.6.1/charts/operator/helm/README.md) ### Changes @@ -2223,7 +2450,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.6.0) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.6.0/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.6.0/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.6.0/charts/operator/helm/README.md) ### Changes @@ -2333,7 +2560,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.5.6) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.5.6/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.5.6/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.5.6/charts/operator/helm/README.md) ### Changes @@ -2413,7 +2640,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.5.5) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.5.5/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.5.5/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.5.5/charts/operator/helm/README.md) ### Changes @@ -2506,7 +2733,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.5.4) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.5.4/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.5.4/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.5.4/charts/operator/helm/README.md) ### Changes @@ -2597,7 +2824,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.5.3) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.5.3/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.5.3/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.5.3/charts/operator/helm/README.md) ### Changes @@ -2701,7 +2928,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.5.2) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.5.2/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.5.2/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.5.2/charts/operator/helm/README.md) ### Changes @@ -2801,7 +3028,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.5.1) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.5.1/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.5.1/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.5.1/charts/operator/helm/README.md) ### Changes @@ -2887,7 +3114,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.5.0) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.5.0/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.5.0/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.5.0/charts/operator/helm/README.md) ### Changes @@ -2984,7 +3211,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.4.1) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.4.1/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.4.1/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.4.1/charts/operator/helm/README.md) ### Changes @@ -3050,7 +3277,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.4.0) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.4.0/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.4.0/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.4.0/charts/operator/helm/README.md) ### Changes @@ -3171,7 +3398,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.3.1) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.3.1/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.3.1/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.3.1/charts/operator/helm/README.md) ### Changes @@ -3240,7 +3467,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.3.0) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.3.0/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.3.0/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.3.0/charts/operator/helm/README.md) ### Changes @@ -3306,7 +3533,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.2.4) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.2.4/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.2.4/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.2.4/charts/operator/helm/README.md) ### Changes @@ -3411,7 +3638,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.2.3) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.2.3/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.2.3/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.2.3/charts/operator/helm/README.md) ### Changes @@ -3512,7 +3739,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.2.2) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.2.2/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.2.2/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.2.2/charts/operator/helm/README.md) ### Changes @@ -3619,7 +3846,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.2.1) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.2.1/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.2.1/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.2.1/charts/operator/helm/README.md) ### Changes @@ -3729,7 +3956,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.2.0) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.2.0/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.2.0/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.2.0/charts/operator/helm/README.md) ### Changes @@ -3855,7 +4082,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.1.2) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.1.2/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.1.2/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.1.2/charts/operator/helm/README.md) ### Changes @@ -3970,7 +4197,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.1.1) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.1.1/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.1.1/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.1.1/charts/operator/helm/README.md) ### Changes @@ -4084,7 +4311,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.1.0) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.1.0/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.1.0/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.1.0/charts/operator/helm/README.md) ### Changes @@ -4277,7 +4504,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.0.4) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.0.4/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.0.4/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.0.4/charts/operator/helm/README.md) ### Changes @@ -4379,7 +4606,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.0.3) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.0.3/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.0.3/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.0.3/charts/operator/helm/README.md) ### Changes @@ -4486,7 +4713,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.0.2) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.0.2/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.0.2/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.0.2/charts/operator/helm/README.md) ### Changes @@ -4595,7 +4822,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.0.1) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.0.1/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.0.1/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.0.1/charts/operator/helm/README.md) ### Changes @@ -4697,7 +4924,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.0.0) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.0.0/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.0.0/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.0.0/charts/operator/helm/README.md) ### Changes @@ -4836,7 +5063,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.66) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.66/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.66/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.66/charts/operator/helm/README.md) ### Changes @@ -4940,7 +5167,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.65) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.65/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.65/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.65/charts/operator/helm/README.md) ### Changes @@ -5040,7 +5267,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.64) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.64/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.64/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.64/charts/operator/helm/README.md) ### Changes @@ -5145,7 +5372,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.63) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.63/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.63/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.63/charts/operator/helm/README.md) ### Changes @@ -5260,7 +5487,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.62) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.62/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.62/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.62/charts/operator/helm/README.md) ### Changes @@ -5360,7 +5587,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.61) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.61/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.61/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.61/charts/operator/helm/README.md) ### Changes @@ -5459,7 +5686,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.60) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.60/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.60/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.60/charts/operator/helm/README.md) ### Changes @@ -5489,7 +5716,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.59) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.59/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.59/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.59/charts/operator/helm/README.md) ### Changes @@ -5518,7 +5745,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.58) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.58/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.58/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.58/charts/operator/helm/README.md) ### Changes @@ -5558,7 +5785,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.57) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.57/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.57/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.57/charts/operator/helm/README.md) ### Changes @@ -5592,7 +5819,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.56) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.56/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.56/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.56/charts/operator/helm/README.md) ### Changes @@ -5630,7 +5857,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.55) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.55/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.55/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.55/charts/operator/helm/README.md) ### Changes @@ -5659,7 +5886,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.54) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.54/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.54/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.54/charts/operator/helm/README.md) ### Changes @@ -5688,7 +5915,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.53) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.53/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.53/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.53/charts/operator/helm/README.md) ### Changes @@ -5720,7 +5947,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.52) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.52/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.52/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.52/charts/operator/helm/README.md) ### Changes @@ -5750,7 +5977,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.51) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.51/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.51/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.51/charts/operator/helm/README.md) ### Changes @@ -5817,7 +6044,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.50) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.50/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.50/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.50/charts/operator/helm/README.md) ### Changes @@ -5854,7 +6081,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.49) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.49/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.49/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.49/charts/operator/helm/README.md) ### Changes @@ -5883,7 +6110,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.48) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.48/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.48/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.48/charts/operator/helm/README.md) ### Changes @@ -5914,7 +6141,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.47) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.47/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.47/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.47/charts/operator/helm/README.md) ### Changes @@ -5944,7 +6171,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.46) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.46/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.46/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.46/charts/operator/helm/README.md) ### Changes @@ -5981,7 +6208,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.45) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.45/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.45/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.45/charts/operator/helm/README.md) ### Changes @@ -6026,7 +6253,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.44) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.44/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.44/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.44/charts/operator/helm/README.md) ### Changes @@ -6065,7 +6292,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.43) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.43/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.43/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.43/charts/operator/helm/README.md) ### Changes @@ -6098,7 +6325,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.42) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.42/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.42/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.42/charts/operator/helm/README.md) ### Changes @@ -6132,7 +6359,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.41) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.41/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.41/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.41/charts/operator/helm/README.md) ### Changes @@ -6170,7 +6397,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.40) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.40/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.40/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.40/charts/operator/helm/README.md) ### Changes @@ -6208,7 +6435,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.39) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.39/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.39/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.39/charts/operator/helm/README.md) ### Changes @@ -6247,7 +6474,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.38) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.38/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.38/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.38/charts/operator/helm/README.md) ### Changes @@ -6293,7 +6520,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.37) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.37/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.37/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.37/charts/operator/helm/README.md) ### Changes @@ -6322,7 +6549,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.36) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.36/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.36/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.36/charts/operator/helm/README.md) ### Changes @@ -6357,7 +6584,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.35) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.35/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.35/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.35/charts/operator/helm/README.md) ### Changes @@ -6388,7 +6615,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.34) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.34/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.34/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.34/charts/operator/helm/README.md) ### Changes @@ -6420,7 +6647,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.33) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.33/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.33/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.33/charts/operator/helm/README.md) ### Changes @@ -6451,7 +6678,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.32) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.32/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.32/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.32/charts/operator/helm/README.md) ### Changes @@ -6480,7 +6707,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.31) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.31/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.31/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.31/charts/operator/helm/README.md) ### Changes @@ -6507,7 +6734,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.30) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.30/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.30/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.30/charts/operator/helm/README.md) ### Changes @@ -6542,7 +6769,7 @@ feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#298 - [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.26) - [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.26/charts/vald/README.md) -- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.26/charts/vald-helm-operator/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.26/charts/operator/helm/README.md) ### Changes diff --git a/content/docs/tutorial/get-started-with-faiss-agent.md b/content/docs/tutorial/get-started-with-faiss-agent.md index f866123a6..3655df843 100644 --- a/content/docs/tutorial/get-started-with-faiss-agent.md +++ b/content/docs/tutorial/get-started-with-faiss-agent.md @@ -1,6 +1,6 @@ --- title: "Get Started With Faiss Agent_tutorial" -date: 2024-10-16T16:03:40+09:00 +date: 2026-09-16T04:39:38Z draft: false weight: 200 description: Running Vald cluster with faiss Agent on Kubernetes and execute client codes @@ -200,9 +200,7 @@ If you are interested, please refer to [SDKs](/docs/user-guides/sdks).
The detailed explanation of example code is here
This will execute 6 steps. - 1. init - - Import packages
example code
@@ -228,7 +226,6 @@ If you are interested, please refer to [SDKs](/docs/user-guides/sdks).
- Set variables - - The constant number of training datasets and test datasets.
example code
@@ -269,7 +266,6 @@ If you are interested, please refer to [SDKs](/docs/user-guides/sdks).
1. load - - Loading from Fashion-MNIST dataset and set id for each vector that is loaded. This step will return the training dataset, test dataset, and ids list of ids when loading is completed with success.
example code
@@ -300,7 +296,6 @@ If you are interested, please refer to [SDKs](/docs/user-guides/sdks).
1. Insert and Index - - Insert and Indexing 400 training datasets to the Vald agent.
example code
@@ -338,7 +333,6 @@ If you are interested, please refer to [SDKs](/docs/user-guides/sdks).
1. Search - - Search 10 neighbor vectors for each 20 test datasets and return a list of the neighbor vectors. - When getting approximate vectors, the Vald client sends search config and vector to the server via gRPC. @@ -369,7 +363,6 @@ If you are interested, please refer to [SDKs](/docs/user-guides/sdks).
1. Remove - - Remove 400 indexed training datasets from the Vald agent.
example code
diff --git a/content/docs/tutorial/get-started.md b/content/docs/tutorial/get-started.md index 7110d2ec4..ce2c4f8ee 100644 --- a/content/docs/tutorial/get-started.md +++ b/content/docs/tutorial/get-started.md @@ -1,6 +1,6 @@ --- title: "Get Started_tutorial" -date: 2024-10-16T16:03:40+09:00 +date: 2026-09-16T04:39:38Z draft: false weight: 100 description: Running Vald cluster with NGT Agent on Kubernetes and execute client codes @@ -255,9 +255,7 @@ If you are interested, please refer to [SDKs](/docs/user-guides/sdks).
The detailed explanation of example code is here
This will execute 6 steps. - 1. init - - Import packages
example code
@@ -283,7 +281,6 @@ If you are interested, please refer to [SDKs](/docs/user-guides/sdks).
- Set variables - - The constant number of training datasets and test datasets.
example code
@@ -324,7 +321,6 @@ If you are interested, please refer to [SDKs](/docs/user-guides/sdks).
1. load - - Loading from Fashion-MNIST dataset and set id for each vector that is loaded. This step will return the training dataset, test dataset, and ids list of ids when loading is completed with success.
example code
@@ -355,7 +351,6 @@ If you are interested, please refer to [SDKs](/docs/user-guides/sdks).
1. Insert and Index - - Insert and Indexing 400 training datasets to the Vald agent.
example code
@@ -393,7 +388,6 @@ If you are interested, please refer to [SDKs](/docs/user-guides/sdks).
1. Search - - Search 10 neighbor vectors for each 20 test datasets and return a list of the neighbor vectors. - When getting approximate vectors, the Vald client sends search config and vector to the server via gRPC. @@ -424,7 +418,6 @@ If you are interested, please refer to [SDKs](/docs/user-guides/sdks).
1. Remove - - Remove 200 indexed training datasets from the Vald agent.
example code
@@ -447,7 +440,6 @@ If you are interested, please refer to [SDKs](/docs/user-guides/sdks).
1. Flush - - Remove all remaining training datasets from the Vald agent.
example code
diff --git a/content/docs/tutorial/vald-agent-standalone-on-docker.md b/content/docs/tutorial/vald-agent-standalone-on-docker.md index be2eec7a4..0f647a12f 100644 --- a/content/docs/tutorial/vald-agent-standalone-on-docker.md +++ b/content/docs/tutorial/vald-agent-standalone-on-docker.md @@ -1,6 +1,6 @@ --- title: "Vald Agent Standalone on Docker_tutorial" -date: 2024-02-15T17:10:12+09:00 +date: 2026-09-16T04:39:37Z draft: false weight: 400 description: Running Vald Agent on Docker and execute client codes @@ -142,7 +142,6 @@ This chapter will use NGT for the core engine of Vald Agent. To deploy Vald agent on docker with `config.yaml`, you can run the below command. Note: - - Please check whether there is `config.yaml` file and `backup` directory in your current directory. ```bash diff --git a/content/docs/tutorial/vald-agent-standalone-on-k8s.md b/content/docs/tutorial/vald-agent-standalone-on-k8s.md index 168a662a6..a9acbf63a 100644 --- a/content/docs/tutorial/vald-agent-standalone-on-k8s.md +++ b/content/docs/tutorial/vald-agent-standalone-on-k8s.md @@ -1,6 +1,6 @@ --- title: "Vald Agent Standalone on K8s_tutorial" -date: 2025-09-02T04:53:43Z +date: 2026-09-16T04:39:38Z draft: false weight: 300 description: Running only Vald Agent on Kubernetes and execute client codes @@ -78,7 +78,7 @@ brew install hdf5 ## Deploy Vald Agent Standalone on Kubernetes Cluster This chapter will show you how to deploy a standalone Vald Agent using Helm and run it on your Kubernetes cluster.
-This chapter uses [NGT](https://github.com/yahoojapan/ngt) as Vald Agent to perform vector insertion operation, indexing, and searching operation.
+This chapter uses [NGT](https://github.com/NGT-labs/NGT) as Vald Agent to perform vector insertion operation, indexing, and searching operation.
1. Clone the repository @@ -158,9 +158,7 @@ This chapter uses [NGT](https://github.com/yahoojapan/ngt) as Vald Agent to perf
The detailed explanation of example code is here
This will execute 6 steps. - 1. init - - Import packages
example code
@@ -187,7 +185,6 @@ This chapter uses [NGT](https://github.com/yahoojapan/ngt) as Vald Agent to perf
- Set variables - - The constant number of training datasets and test datasets.
example code
@@ -228,7 +225,6 @@ This chapter uses [NGT](https://github.com/yahoojapan/ngt) as Vald Agent to perf
1. load - - Loading from Fashion-MNIST dataset and set id for each vector that is loaded. This step will return the training dataset, test dataset, and ids list of ids when loading is completed with success.
example code
@@ -258,7 +254,6 @@ This chapter uses [NGT](https://github.com/yahoojapan/ngt) as Vald Agent to perf
1. Insert and Index - - Insert and Indexing 400 training datasets to the Vald agent.
example code
@@ -314,7 +309,6 @@ This chapter uses [NGT](https://github.com/yahoojapan/ngt) as Vald Agent to perf
1. Search - - Search 10 neighbor vectors for each 20 test datasets and return a list of neighbor vectors. - When getting approximate vectors, the Vald client sends search config and vector to the server via gRPC. @@ -345,7 +339,6 @@ This chapter uses [NGT](https://github.com/yahoojapan/ngt) as Vald Agent to perf
1. Remove - - Remove indexed 400 training datasets from the Vald agent.
example code
diff --git a/content/docs/usecase/usage-example.md b/content/docs/usecase/usage-example.md index 769f43b8a..c7c6e7a4d 100644 --- a/content/docs/usecase/usage-example.md +++ b/content/docs/usecase/usage-example.md @@ -1,6 +1,6 @@ --- title: "Usage Example_usecase" -date: 2024-01-10T18:16:44+09:00 +date: 2026-09-16T04:39:40Z draft: false weight: 0 description: "" @@ -12,7 +12,7 @@ menu: # Use cases This document will introduce you to the example of what Vald can do. -Vald is a highly scalable distributed fast approximate nearest neighbor dense vector search engine, which uses [NGT](https://github.com/yahoojapan/NGT) as the core engine of Vald, and Vald manages to integrate with Kubernetes. +Vald is a highly scalable distributed fast approximate nearest neighbor dense vector search engine, which uses [NGT](https://github.com/NGT-labs/NGT) as the core engine of Vald, and Vald manages to integrate with Kubernetes. You cannot generally search your unstructured data using the inverted index, like images and videos. Applying a model like BERT or VGG can convert your unstructured data into vectors. diff --git a/content/docs/user-guides/client-api-config.md b/content/docs/user-guides/client-api-config.md index 798348264..ae3456543 100644 --- a/content/docs/user-guides/client-api-config.md +++ b/content/docs/user-guides/client-api-config.md @@ -1,6 +1,6 @@ --- title: "Client Api Config_user Guides" -date: 2024-10-16T16:03:40+09:00 +date: 2026-09-16T04:39:39Z draft: false weight: 1000 description: Shows more detailed explanation about APIs configuration @@ -387,20 +387,17 @@ When `timestamp` is not set, the current time will be used. Vald provides four types of search services. 1. Search - - `Search` is the `ANN(Approximate Nearest Neighbor)` search with query vector. It is a fast search even though large dimension vector. The search duration is quick but less accurate than `LinearSearch`. The search algorithm depends on each core algorithm. 1. SearchById - - `SearchById` is the `ANN(Approximate Nearest Neighbor)` search with the stored vector's ID. The ID should already exist in the NGT indexes before the search process. The search algorithm is the same as `Search`. 1. LinearSearch - - `LinearSearch` is the most general search algorithm with a query vector. It searches all indexed vectors and calculates the distance between the query. It returns accurate results but requires more processing time than `Search` (ANN search) as it needs to calculate from all indexed vectors instead of only a subset of indexed vectors. diff --git a/content/docs/user-guides/configuration.md b/content/docs/user-guides/configuration.md index a322ff6b7..5919c03d4 100644 --- a/content/docs/user-guides/configuration.md +++ b/content/docs/user-guides/configuration.md @@ -1,6 +1,6 @@ --- title: "Configuration_user Guides" -date: 2024-08-08T00:23:47+09:00 +date: 2026-09-16T04:39:40Z draft: false weight: 100 description: Applies basic configuration for running Vald cluster @@ -155,7 +155,7 @@ Please refer to [the Vald operation guide](/docs/user-guides/configuration) for #### NGT -Vald Agent NGT uses [yahoojapan/NGT][yj-ngt] as a core library for searching vectors. +Vald Agent NGT uses [NGT-labs/NGT][yj-ngt] as a core library for searching vectors. The behaviors of NGT can be configured by setting `agent.ngt` field object. The important parameters are the followings: @@ -408,7 +408,7 @@ For further details, there are references to the Helm values in the Vald GitHub [vald-helm-chart]: https://github.com/vdaas/vald/tree/main/charts/vald -[vald-helm-operator-chart]: https://github.com/vdaas/vald/tree/main/charts/vald-helm-operator +[vald-helm-operator-chart]: https://github.com/vdaas/vald/tree/main/charts/operator/helm [vald-apis-docs]: https://github.com/vdaas/vald/tree/main/apis/docs [vald-swagger-specs]: https://github.com/vdaas/vald/tree/main/apis/swagger [google-pprof]: https://github.com/google/pprof @@ -416,7 +416,7 @@ For further details, there are references to the Helm values in the Vald GitHub [kubernetes-liveness-readiness]: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/ [kubernetes-affinity-antiaffinity]: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity [kubernetes-topology-spread-constraints]: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ -[yj-ngt]: https://github.com/yahoojapan/NGT -[yj-ngt-wiki]: https://github.com/yahoojapan/NGT/wiki +[yj-ngt]: https://github.com/NGT-labs/NGT +[yj-ngt-wiki]: https://github.com/NGT-labs/NGT/wiki [faiss]: https://github.com/facebookresearch/faiss [faiss-wiki]: https://github.com/facebookresearch/faiss/wiki diff --git a/content/docs/user-guides/deployment.md b/content/docs/user-guides/deployment.md index 53a60beff..6ed2c0a7a 100644 --- a/content/docs/user-guides/deployment.md +++ b/content/docs/user-guides/deployment.md @@ -1,6 +1,6 @@ --- title: "Deployment_user Guides" -date: 2024-08-08T00:23:47+09:00 +date: 2026-09-16T04:39:40Z draft: false weight: 600 description: How to launch Vald cluster on your Kubernetes cluster @@ -14,7 +14,6 @@ menu: There are two major ways for the deployment of the Vald cluster ways: Using the Helm command with `values.yaml` or without Helm command using operator called `vald-helm-operator`. - Using Helm command with `values.yaml` - - Easy to deploy - Allow editing Vald configuration values when the user executes Helm command with inlining. - Need Helm command when applying configuration @@ -257,7 +256,7 @@ spec:
-For more details of the configuration of vald-helm-operator-release, please refer to [here](https://github.com/vdaas/vald/tree/main/charts/vald-helm-operator#configuration). +For more details of the configuration of `vald-helm-operator-release`, please refer to [here](https://github.com/vdaas/vald/tree/main/charts/operator/helm#configuration). After setting `vr.yaml` (and `vhor.yaml`), you can deploy by the following steps. diff --git a/content/docs/user-guides/operations.md b/content/docs/user-guides/operations.md index 6699dbd55..91030527d 100644 --- a/content/docs/user-guides/operations.md +++ b/content/docs/user-guides/operations.md @@ -1,6 +1,6 @@ --- title: "Operations_user Guides" -date: 2024-02-15T17:10:15+09:00 +date: 2026-09-16T04:39:40Z draft: false weight: 700 description: Describes the basic operation for the Vald cluster @@ -128,8 +128,8 @@ VERSION=v1.4.1 ``` ```bash -kubectl replace -f https://raw.githubusercontent.com/vdaas/vald/${VERSION}/charts/vald-helm-operator/crds/valdrelease.yaml && \ -kubectl replace -f https://raw.githubusercontent.com/vdaas/vald/${VERSION}/charts/vald-helm-operator/crds/valdhelmoperatorrelease.yaml +kubectl replace -f https://raw.githubusercontent.com/vdaas/vald/${VERSION}/charts/operator/helm/crds/valdrelease.yaml && \ +kubectl replace -f https://raw.githubusercontent.com/vdaas/vald/${VERSION}/charts/operator/helm/crds/valdhelmoperatorrelease.yaml ``` After upgrading CRDs, please upgrade the operator. @@ -146,7 +146,7 @@ The operator will automatically detect the changes and update the deployed Vald - [CHANGELOG][changelog] [vald-helm-chart]: https://github.com/vdaas/vald/tree/main/charts/vald -[vald-helm-operator-chart]: https://github.com/vdaas/vald/tree/main/charts/vald-helm-operator +[vald-helm-operator-chart]: https://github.com/vdaas/vald/tree/main/charts/operator/helm [changelog]: https://vald.vdaas.org/docs/release/changelog/ [pod-priority-preemption]: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/ [resource-quota]: https://kubernetes.io/docs/concepts/policy/resource-quotas/ diff --git a/content/docs/user-guides/read-replica-and-rotator.md b/content/docs/user-guides/read-replica-and-rotator.md index a18fc151d..5012fcf49 100644 --- a/content/docs/user-guides/read-replica-and-rotator.md +++ b/content/docs/user-guides/read-replica-and-rotator.md @@ -1,6 +1,6 @@ --- title: "Read Replica and Rotator_user Guides" -date: 2024-05-21T13:22:43+09:00 +date: 2026-09-16T04:39:39Z draft: false weight: 1500 description: How to improve search request speed @@ -30,19 +30,19 @@ The read replica is managed with a separate chart from the Vald cluster and is d ```yaml agent: ngt: - export_index_info_to_k8s: true + export_index_info_to_k8s: true readreplica: - enabled: true - minReplicas: 1 # if you don't use hpa, this will be the replicas of the Deployment - maxReplicas: 3 - hpa: - enabled: true # if you prefer to use hpa - targetCPUUtilizationPercentage: 80 + enabled: true + minReplicas: 1 # if you don't use hpa, this will be the replicas of the Deployment + maxReplicas: 3 + hpa: + enabled: true # if you prefer to use hpa + targetCPUUtilizationPercentage: 80 manager: index: - operator: - enabled: true - rotation_job_concurrency: 2 + operator: + enabled: true + rotation_job_concurrency: 2 ``` 1. Deploy Vald cluster diff --git a/content/docs/user-guides/upgrade-cluster.md b/content/docs/user-guides/upgrade-cluster.md index b0f67f083..139c68d1f 100644 --- a/content/docs/user-guides/upgrade-cluster.md +++ b/content/docs/user-guides/upgrade-cluster.md @@ -1,6 +1,6 @@ --- title: "Upgrade Cluster_user Guides" -date: 2024-08-08T00:23:47+09:00 +date: 2026-09-16T04:39:40Z draft: false weight: 800 description: Describes hot to upgrade Vald version for the running Vald cluster @@ -104,7 +104,6 @@ If there is major or minor upgrading, the chart structure may have changed. ``` 1. Edit `values.yaml` - - We recommend setting a specific version as an image tag. ```yaml @@ -128,8 +127,8 @@ The upgrading steps are below if you use `vald-helm-operator` for the deployment 1. Upgrade CRDs ```bash - kubectl replace -f https://raw.githubusercontent.com/vdaas/vald//charts/vald-helm-operator/crds/valdrelease.yaml - kubectl replace -f https://raw.githubusercontent.com/vdaas/vald//charts/vald-helm-operator/crds/valdhelmoperatorrelease.yaml + kubectl replace -f https://raw.githubusercontent.com/vdaas/vald//charts/operator/helm/crds/valdrelease.yaml + kubectl replace -f https://raw.githubusercontent.com/vdaas/vald//charts/operator/helm/crds/valdhelmoperatorrelease.yaml ``` 1. Update `vhor` @@ -141,7 +140,6 @@ The upgrading steps are below if you use `vald-helm-operator` for the deployment - Also, you can upgrade manually by editing `vhor.yaml` and applying it. 1. Edit `vr.yaml` - - We recommend setting a specific version as an image tag. ```yaml diff --git a/content/docs/user-guides/vald-operator/_index.md b/content/docs/user-guides/vald-operator/_index.md new file mode 100644 index 000000000..17fb4b6aa --- /dev/null +++ b/content/docs/user-guides/vald-operator/_index.md @@ -0,0 +1,384 @@ +--- +title: "Vald Operator_user Guides/Vald Operator" +date: 2026-09-16T04:39:39Z +draft: false +weight: 0 +--- + +# Vald Operator + +The Vald Operator is a Kubernetes controller that manages the lifecycle of Vald clusters through a single custom resource. +It watches `ValdOperatorRelease` (short name `vor`, API group `vald.vdaas.org/v1`) resources and generates +[`ValdRelease`](https://github.com/vdaas/vald-helm-operator) (VRS) manifests consumed by the Vald Helm Operator (VHO). + +``` +ValdOperatorRelease (vor) ──reconcile──▶ ValdRelease (VRS) × (active infra × clusters) + minimal input consumed by vald-helm-operator +``` + +The controller _generates_ VRS definitions. +It does not run Vald itself; a VHO running in the target cluster turns each VRS into Vald pods. +This separation lets a management cluster emit VRS definitions and distribute them to other clusters. + +## Prerequisites + +- Go 1.23+ +- Docker 17.03+ +- kubectl 1.11.3+ and access to a Kubernetes 1.11.3+ cluster +- A working [Vald Helm Operator (VHO)](https://vald.vdaas.org/docs/operator/vald-helm-operator/) in the target cluster + +## Deployment + +### Build and push the controller image + +```sh +make docker/build/operator/vald +``` + +### Install CRDs and deploy the controller + +```sh +make k8s/operator/vald/deploy +``` + +### Apply a sample ValdOperatorRelease + +```sh +kubectl apply -f cmd/operator/vald/sample.yaml +``` + +### Uninstall + +```sh +kubectl delete -f cmd/operator/vald/sample.yaml +make k8s/operator/vald/delete +``` + +## CRD: ValdOperatorRelease (`vor`) + +The goal of the `ValdOperatorRelease` resource is to collapse the large `ValdRelease` configuration surface into what a user must supply. +There are two input groups: + +1. **Infrastructure / node-pool information** — `spec.infrastructure[]` +2. **Minimal Vald settings** — `spec.vectorEngine.vald` + +### Spec example + +```yaml +apiVersion: vald.vdaas.org/v1 +kind: ValdOperatorRelease +metadata: + name: my-vor + namespace: vald +spec: + infrastructure: + - role: green # arbitrary role label (e.g. green/blue, hot/cold) + type: kind # cluster type hint (informational) + active: true + clusters: + - id: "abc-123" # cluster ID (populated by external system) + name: "cluster-a" # human-readable cluster name + nodePools: + general: # gateway / discoverer / manager + name: general-pool + replicas: 3 + machineResource: + cpu: "4" + memory: "16Gi" + storage: "100Gi" + agent: # optional dedicated agent pool + name: agent-pool + replicas: 6 + machineResource: + cpu: "8" + memory: "32Gi" + vectorEngine: + name: my-vald + vald: + defaults: + logLevel: warn + agent: + ngt: + dimension: 768 + distanceType: l2 + objectType: float + creationEdgeSize: 20 + searchEdgeSize: 10 + persistentVolume: + enabled: true + storageClass: standard + accessMode: ReadWriteOnce + indexer: + indexSchedule: "@every 1m" + concurrency: 2 + manager: true + indexDuration: 1h + saveDuration: 1h + gateway: + indexReplica: 3 + serviceType: LoadBalancer + ingress: + enabled: true + host: vald.example.com + discoverer: + kind: DaemonSet # DaemonSet | Deployment + overlay: {} # arbitrary JSON merged onto the generated VRS +``` + +### `spec.infrastructure[]` + +| Field | Notes | +| ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `role` | Free-form role label (e.g. `hot`, `standby`, `blue`, `green`). Copied to VRS labels. | +| `type` | Cluster type label. | +| `active` | When `false`, the entry is skipped during VRS generation. | +| `clusters[]` | `{ id, name }`. The `id` field is typically filled by an external system; `name` must be set. | +| `nodePools` | Map keyed by pool type: `general` (required) and `agent` (optional). Each pool carries `name`, `replicas`, and `machineResource{ cpu, memory, storage }`. | + +#### Node pool types + +| Key | Hosts | +| --------- | ------------------------------------- | +| `general` | gateway-lb, discoverer, manager-index | +| `agent` | `vald-agent` (NGT) | + +When no `agent` pool is defined, the `general` pool is used for agent resource sizing and replica count as well. + +### `spec.vectorEngine.vald` + +| Field | Description | +| ------------------------ | --------------------------------------------------------------------------------------------------------------- | +| `defaults.logLevel` | Log level for all Vald components. | +| `agent.ngt` | NGT settings: `dimension` (required, >= 2), `creationEdgeSize`, `searchEdgeSize`, `distanceType`, `objectType`. | +| `agent.persistentVolume` | Optional. `{ enabled, storageClass, accessMode }`. Falls back to environment defaults when omitted. | +| `indexer` | `indexSchedule`, `saveSchedule`, `concurrency`, `manager`, suspend flags, durations. | +| `gateway` | `indexReplica`, `serviceType`, `ingress{ enabled, host }`. | +| `discoverer.kind` | `DaemonSet` or `Deployment`. | +| `overlay` | Arbitrary JSON patch merged onto the generated VRS (Helm-style patch). | + +### Status + +```yaml +status: + phase: WaitingCreateVrs + progress: + total: 3 + completed: 1 + conditions: + - type: WaitingClusterCreate + status: "True" + reason: Succeeded + - type: WaitingCreateVrs + status: Unknown + reason: Progressing +``` + +| Field | Description | +| -------------------- | --------------------------------------------------------------- | +| `phase` | The condition type currently being evaluated. | +| `progress.total` | Total lifecycle phase count. | +| `progress.completed` | Phases that have reached `True`. | +| `conditions` | Accumulated `metav1.Condition` array. Phases are never removed. | + +## Lifecycle + +Reconciliation is modeled as an ordered flow of phases. +Each phase carries a `Condition` plus an optional `Builder` (creates/updates resources) and `Checker` (reports readiness). + +| Phase | Builder | Checker | Purpose | +| ---------------------- | ------- | ------- | -------------------------------------------------------------------- | +| `WaitingClusterCreate` | -- | yes | Validate infra config; wait until every cluster has `id` and `name`. | +| `WaitingCreateVrs` | yes | yes | Build VRS objects and wait until they are ready. | +| `Completed` | -- | -- | Terminal phase. | + +### Accumulating, self-healing conditions + +`status.conditions` **accumulates** as phases progress (conditions are never removed), and every reconcile re-evaluates **all** conditions. +If a `True` condition later breaks (e.g. a generated VRS is deleted), the controller detects it and restarts work from that phase. + +``` +Reconcile N: [WaitingClusterCreate=True] + → WaitingCreateVrs seeded as Progressing + +Reconcile N+1: [WaitingClusterCreate=True, WaitingCreateVrs=Unknown] + → VRS created, but Get returns NotFound → stay Progressing + +Reconcile N+2: [WaitingClusterCreate=True, WaitingCreateVrs=True] + → Completed seeded as Succeeded + +--- ValdRelease deleted externally --- + +Reconcile N+3: [WaitingClusterCreate=True, WaitingCreateVrs=False] + → controller detects break → VRS is recreated → recovers +``` + +### Readiness states + +| Result | Condition Status | Meaning | +| ------------- | ---------------- | ----------------------------------------------------------- | +| `Progressing` | Unknown | The controller is actively working. | +| `Pending` | Unknown | Waiting for an external event (e.g. cluster ID assignment). | +| `Succeeded` | True | Phase is complete. | +| `Failed` | False | Misconfiguration or unrecoverable error. | + +## VRS Generation + +`VrsBuilder.Build` is a pure function of `(CR, Config, NodePoolCapability)` — it makes no Kubernetes API calls. + +1. **Validate** the CR (`infrastructure` non-empty, each cluster has a `name`). +2. **Iterate `infrastructure[]`**, skipping inactive entries (and, when node-pool matching is enabled, entries with no matching general pool). +3. **Assemble VRS spec**: defaults, gateway, agent (NGT), manager, discoverer. +4. **Resolve resources from node pools**: derive replicas and per-component CPU/memory from the agent node pool. +5. **Apply optional settings**: persistent volume, node affinities. +6. **Per cluster**: for each `infra.clusters[]`, set name, apply labels, then merge the overlay. + One VRS is produced for each cluster, so a single VOR can yield one VRS per target cluster. + +### Overlay + +`spec.vectorEngine.vald.overlay` is a JSON patch merged onto the generated VRS, layered on top of the default VRS template loaded at startup (`DEFAULT_VRS_PATH`). +This provides an escape hatch for any VRS field not directly exposed in the VOR spec. + +### Resource management + +All generated resources have a controller owner reference pointing to the `ValdOperatorRelease` CR, enabling garbage collection when the CR is deleted. +Every resource is labelled `managed-generation: `. +On each reconcile, resources owned by the CR but absent from the current build output are pruned (deleted). + +## Resource Allocation and Topology + +### Agent resource calculation + +Agent pods request a fraction of the node's resources so that `AgentPodsPerNode` (default 2) pods fit on one node: + +``` +CPU request = nodeCPU * 0.6 / AgentPodsPerNode +RAM request = nodeRAM * 0.6 / AgentPodsPerNode +CPU limit = nodeCPU * 0.6 +RAM limit = (not set) +``` + +The 0.6 ratio reserves 60% of the node for agent pods; the remaining 40% covers OS, DaemonSets, and system processes. +Memory limit is intentionally absent because the NGT index uses memory-mapped files and a hard limit would trigger OOM kills. + +### Agent pod count + +``` +agentPodCount = agentNodeCount * AgentPodsPerNode +``` + +`MinReplicas = MaxReplicas = agentPodCount` (agent uses a StatefulSet with no HPA). + +### Gateway replica scaling + +``` +minReplicas = max(agentPodCount / 2, 1) +maxReplicas = max(agentPodCount * 2, 1) +``` + +### Gateway / discoverer / manager resources + +These components use fixed defaults: + +| Component | CPU req | RAM req | CPU lim | RAM lim | +| ------------- | ------- | ------- | ------- | ------- | +| gateway-lb | 200m | 150Mi | 2000m | 700Mi | +| discoverer | 200m | 65Mi | 600m | 200Mi | +| manager-index | 200m | 80Mi | 1000m | 500Mi | + +### Topology spread constraints + +All components receive a `topologySpreadConstraints` entry to spread pods across nodes: + +```yaml +topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: DoNotSchedule + labelSelector: + matchLabels: + app.kubernetes.io/component: +``` + +## Multi-Cluster Distribution + +The same VOR can be distributed across clusters and generates a VRS where a matching node pool exists. + +### Node-pool matching + +When `REQUIRE_NODEPOOL_MATCH=true`, the controller lists Nodes and resolves a `NodePoolCapability`. +A VRS is generated when a `general` pool is present. +Nodes are matched by labels: + +``` +/namespace = +/type = general | agent +``` + +`prefix` defaults to empty and can be configured via `NODEPOOL_LABEL_PREFIX`. + +When `REQUIRE_NODEPOOL_MATCH=false` (default), the controller skips node listing and treats every infra entry as schedulable. + +### Use cases + +- **Single cluster**: Deploy VOR once; VRS generates in that cluster. +- **Multi-cluster same config**: Deploy VOR to all clusters; generate VRS where node-pool labels match. +- **Blue/green deployments**: Use separate VOR objects with different roles and endpoints. +- **VRS generation**: Generate VRS manifests in a management cluster and distribute them to workload clusters. + +## Configuration + +All configuration is loaded once at startup from a YAML config file, following the same convention as every other Vald component. +See [`cmd/operator/vald/sample.yaml`](https://github.com/vdaas/vald/blob/main/cmd/operator/vald/sample.yaml) for a complete example; when deploying via the Helm chart, every key is exposed through `charts/operator/vald/values.yaml`. + +The reconciler-facing settings live under the `operator` key: + +| Config Key | Default | Description | +| ---------------------------------------------------------- | ------------------------------------------ | ----------------------------------------------------------------- | +| `operator.vrs.default_vrs_path` | `/opt/valdoperatorrelease/config/vrs.yaml` | Default VRS template merged with the overlay. | +| `operator.vrs.log_level` | `warn` | Log level passed through to the generated VRS. | +| `operator.node_pool.require_match` | `false` | Only generate VRS where matching node pools exist. | +| `operator.node_pool.label_prefix` | `""` | Prefix for the `namespace`/`type`/`role` node labels. | +| `operator.node_pool.agent_pods_per_node` | `2` | Agent pods packed per node when computing replicas. | +| `operator.persistent_volume.default_storage_class` | `standard` | PV storage class fallback. | +| `operator.persistent_volume.default_access_mode` | `ReadWriteOnce` | PV access mode fallback. | +| `operator.persistent_volume.buffer_ratio` | `1.5` | PV size = `max(memoryRequest * ratio, min)`. | +| `operator.persistent_volume.min_size_bytes` | `1073741824` | Minimum PV size in bytes. | +| `operator.networking.enable_ingress` | `true` | Enable gateway ingress generation. | +| `operator.networking.gateway_ingress_annotations` | `{}` | Annotations applied to the gateway ingress. | +| `operator.networking.gateway_service_type` | `NodePort` | Gateway service type (`NodePort` / `ClusterIP` / `LoadBalancer`). | +| `operator.networking.discoverer_daemonset_max_surge` | `30%` | Discoverer DaemonSet rolling-update `maxSurge`. | +| `operator.networking.discoverer_daemonset_max_unavailable` | `0%` | Discoverer DaemonSet rolling-update `maxUnavailable`. | + +Controller-level settings (leader election, `requeue` intervals, reconcile concurrency, watched namespaces) live under `operator.controller`, and server/observability settings (`server_config`, `observability`) follow the standard Vald component layout. + +## Development + +### Common tasks + +```sh +# Run unit tests +make test/operator/vald + +# Regenerate k8s manifests after changing charts/operator/vald +make k8s/manifest/operator/vald/update + +# Lint +make lint + +# Build and push the controller image +make docker/build/operator/vald + +# Deploy the controller to the current cluster +make k8s/operator/vald/deploy +``` + +Run `make help` for the full list of targets. + +## Further Reading + +| Document | Description | +| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------- | +| [Design Overview](design) | Controller architecture, CRD spec, reconcile lifecycle, and environment-variable reference. | +| [Controller Specification](controller-spec) | Detailed reconcile model, desired-state model, and resource-management rules. | +| [Resource and Topology Strategy](resource-and-topology) | Agent resource calculation, replica scaling formulas, and topology spread constraints. | +| [Test Strategy](test-strategy) | Test layers, golden file tests, and coverage philosophy. | diff --git a/content/docs/user-guides/vald-operator/controller-spec.md b/content/docs/user-guides/vald-operator/controller-spec.md new file mode 100644 index 000000000..8761d2c77 --- /dev/null +++ b/content/docs/user-guides/vald-operator/controller-spec.md @@ -0,0 +1,346 @@ +--- +title: "Controller Spec_user Guides/Vald Operator" +date: 2026-09-16T04:39:39Z +draft: false +weight: 0 +description: "" +menu: + valdoperator: + parent: Vald Operator +--- + +# ValdOperatorRelease Controller Specification + +## Overview + +The ValdOperatorRelease controller manages the lifecycle of [ValdRelease](https://vald.vdaas.org) resources +on a Kubernetes cluster. Users describe their desired Vald configuration via a single `ValdOperatorRelease` +CR; the controller generates and maintains the corresponding `ValdRelease` objects, adjusting them +whenever the CR changes. + +```text +User applies ValdOperatorRelease CR + │ + ▼ +┌───────────────────────┐ +│ ValdOperatorRelease │ +│ Controller │ +│ │ +│ 1. Check infrastructure│ +│ 2. Build ValdRelease │ +│ 3. Create / update │ +│ 4. Prune stale VRS │ +└───────────────────────┘ + │ + ▼ + ValdRelease CR(s) + (consumed by VHO) +``` + +ValdRelease objects are consumed by the [Vald Helm Operator (VHO)](https://vald.vdaas.org/docs/operator/vald-helm-operator/), +which deploys the actual Vald components. + +--- + +## CRD: ValdOperatorRelease (`vor`) + +### Spec + +```yaml +spec: + infrastructure: + - role: green # arbitrary role label (e.g. green/blue, hot/cold) + type: kind # cluster type hint (informational) + active: true # whether this infra entry is currently active + clusters: + - id: "abc-123" # cluster ID (populated by external system) + name: "cluster-a" # human-readable cluster name + nodePools: + general: # NodePoolTypeGeneral — gateway/discoverer/manager + name: general-pool + replicas: 3 + machineResource: + cpu: "4" + memory: "16Gi" + storage: "100Gi" + agent: # NodePoolTypeValdAgent — optional dedicated agent pool + name: agent-pool + replicas: 6 + machineResource: + cpu: "8" + memory: "32Gi" + vectorEngine: + name: my-vald + vald: + defaults: + logLevel: warn + agent: + ngt: + dimension: 768 + distanceType: l2 + objectType: float + creationEdgeSize: 20 + searchEdgeSize: 10 + persistentVolume: # optional + enabled: true + storageClass: standard # falls back to DEFAULT_STORAGE_CLASS + accessMode: ReadWriteOnce # falls back to DEFAULT_ACCESS_MODE + indexer: + indexSchedule: "@every 1m" + concurrency: 2 + manager: true + indexDuration: 1h + saveDuration: 1h + gateway: + indexReplica: 3 + serviceType: LoadBalancer + ingress: # optional + enabled: true + host: vald.example.com + discoverer: + kind: DaemonSet # DaemonSet | Deployment + overlay: {} # arbitrary JSON merged last (Helm-style patch) +``` + +#### `infrastructure[].clusters` + +| Field | Required | Description | +| ------ | ---------------------------- | ----------------------------------------------------------------- | +| `id` | populated by external system | Cluster identifier. Empty while the cluster is being provisioned. | +| `name` | yes | Human-readable name used to derive the ValdRelease object name. | + +#### `infrastructure[].nodePools` + +Two pool types are recognized: + +| Key | Constant | Hosts | +| --------- | ----------------------- | ------------------------------------- | +| `general` | `NodePoolTypeGeneral` | gateway-lb, discoverer, manager-index | +| `agent` | `NodePoolTypeValdAgent` | vald-agent (NGT) | + +When no `agent` pool is defined, the `general` pool is used for agent resource +sizing and replica count as well. See [Resource Allocation and Topology](resource-and-topology) +for the sizing formulas. + +### Status + +```yaml +status: + phase: WaitingCreateVrs # type of the condition currently being processed + progress: + total: 3 # total number of lifecycle phases + completed: 1 # number of phases that have reached True + conditions: + - type: WaitingClusterCreate + status: "True" + reason: Succeeded + message: "Waiting for Cluster Creation." + lastTransitionTime: "2025-01-01T00:00:00Z" + - type: WaitingCreateVrs + status: Unknown + reason: Progressing + message: "Waiting for VRS creation." + lastTransitionTime: "2025-01-01T00:00:01Z" +``` + +| Field | Description | +| -------------------- | --------------------------------------------------------------------------------------------------------- | +| `phase` | The `type` of the condition currently being evaluated. Updated on every reconcile. | +| `progress.total` | Total lifecycle phase count. Set on every reconcile from the lifecycle definition. | +| `progress.completed` | Count of phases that have transitioned to `True`. Updated when a new phase is seeded. | +| `conditions` | Accumulated standard `metav1.Condition` array. Phases are never removed; each phase appears at most once. | + +#### kubectl columns + +```text +NAME PHASE PROGRESS STATUS AGE +my-vor WaitingCreateVrs 3 True 5m +``` + +> **Note**: The `STATUS` column shows `conditions[0].status`, which is always the +> first phase (`WaitingClusterCreate`). This reflects whether cluster provisioning +> has completed, not the overall readiness. Use `PHASE` for current processing state. + +--- + +## Lifecycle Phases + +The controller defines three phases in order: + +| Order | Condition Type | Desired | Description | +| ----- | ---------------------- | ------------------ | -------------------------------------------------------------------------------------- | +| 0 | `WaitingClusterCreate` | `desired.Prop` | Validates that all clusters in `spec.infrastructure` have non-empty `id` and `name`. | +| 1 | `WaitingCreateVrs` | `desired.Resource` | Builds and creates `ValdRelease` objects; waits for them to exist in the API server. | +| 2 | `Completed` | `nil` | Terminal phase. No desired-state check; automatically marked `Succeeded` when reached. | + +--- + +## Reconciliation Model + +### Conditions Accumulation + +Conditions are **never removed** from `Status.Conditions`. As phases progress, entries +are appended to the array. Every reconcile loop re-evaluates **all** accumulated conditions. + +This provides self-healing: if a previously `True` condition later breaks (e.g. a +`ValdRelease` is deleted, or `spec.infrastructure` is modified), the controller detects +it on the next reconcile and halts at that phase until it recovers. + +```text +Reconcile N: [WaitingClusterCreate=True] + → WaitingCreateVrs seeded as Progressing + +Reconcile N+1: [WaitingClusterCreate=True, WaitingCreateVrs=Unknown] + → VRS created, but Get returns NotFound → stay Progressing + +Reconcile N+2: [WaitingClusterCreate=True, WaitingCreateVrs=True] + → Completed seeded as Succeeded + +--- ValdRelease deleted externally --- + +Reconcile N+3: [WaitingClusterCreate=True, WaitingCreateVrs=False, Completed=True] + → WaitingCreateVrs fails → controller stops, returns error → requeued + → VRS is recreated, WaitingCreateVrs recovers to True on next pass +``` + +### Reconcile Loop (simplified) + +```go +// 1. First reconcile: no conditions yet → seed the first phase +if len(status.Conditions) == 0 { + seed(phases[0], Progressing) + return +} + +// 2. Re-evaluate every accumulated condition +for _, cond := range status.Conditions { + phase := phases.GetByType(cond.Type) + if phase.Desired == nil { continue } + + createOrUpdateResources(phase) // idempotent + result := phase.Desired.IsReady(ctx) // returns desired.Result + updateCondition(cond.Type, result) + + if result != Succeeded { + return error // halt; controller-runtime requeues + } +} + +// 3. If all known phases passed, seed the next one +if next := phases.GetNext(); next exists { + seed(next, Progressing or Succeeded) +} +``` + +### Phase Seeding + +When a new phase is seeded: + +- `Desired == nil` (e.g. `Completed`): seeded with `Succeeded` immediately. +- `Desired != nil`: seeded with `Progressing`; evaluated on the next reconcile. + +--- + +## Desired State Model + +Each lifecycle phase optionally holds a `Desired` implementation that answers two questions: + +1. **`Build(ctx)`** — what Kubernetes resources should exist? +2. **`IsReady(ctx)`** — are those resources in the expected state? + +### `desired.Result` + +`IsReady` returns a `desired.Result` which maps directly to a `metav1.Condition`: + +| Constructor | `Status` | `Reason` | Meaning | +| ------------------ | --------- | ------------- | -------------------------------------------------------------------------- | +| `Progressing(msg)` | `Unknown` | `Progressing` | Controller is actively working (e.g. building resources). | +| `Pending(msg)` | `Unknown` | `Pending` | Waiting for an external event (e.g. external system setting `cluster.id`). | +| `Succeeded()` | `True` | `Succeeded` | Phase is complete. | +| `Failed(err)` | `False` | `Failed` | Unrecoverable error (e.g. missing required configuration). | + +**`Pending` vs `Failed`** + +`Pending` indicates an expected transient state — the system is waiting for something +outside its control. `Failed` indicates a configuration error that requires human intervention. + +Example — `WaitingClusterCreate`: + +| Condition | Result | +| --------------------------------- | ---------------------------------------------------------------------- | +| `spec.infrastructure` is empty | `Failed` — required field, user must fix | +| `infra.clusters` is empty | `Failed` — required field, user must fix | +| `cluster.id == ""` | `Pending` — external system has started but has not assigned an ID yet | +| `cluster.name == ""` | `Pending` — same reason | +| all clusters have `id` and `name` | `Succeeded` | + +### `desired.Prop` + +Used for pure in-memory checks (no Kubernetes API calls). Holds a `Check func() Result`. +`Build` is a no-op. + +### `desired.Resource` + +Used when checking Kubernetes resources via the API server. Holds a `Builder` (which produces +the list of expected objects) and a `Client`. `Build` calls the builder and stores the list; +`IsReady` iterates the list and calls `client.Get` for each object. + +| Get result | `IsReady` returns | +| ------------------------------- | --------------------------------------------- | +| `NotFound` | `Progressing("")` — resources not created yet | +| other error | `Failed(err)` | +| found, `Check` returns non-True | returned as-is | +| all found and passing | `Succeeded()` | + +--- + +## Resource Management + +### NodePool Match (optional) + +When the environment variable `REQUIRE_NODEPOOL_MATCH=true` is set, the controller +only generates `ValdRelease` objects for infra entries that have matching Kubernetes +`Node` objects. Nodes are matched by labels: + +```text +/namespace = +/type = general | agent +``` + +`prefix` defaults to empty (bare `namespace`/`type` keys) and can be configured via +`NODEPOOL_LABEL_PREFIX`. + +### Generation Tracking + +Every created/updated resource is labelled `managed-generation: `. +On each reconcile, resources owned by the `ValdOperatorRelease` CR but absent from the current +build output are **pruned** (deleted). + +### Owner References + +All generated resources have a controller owner reference pointing to the `ValdOperatorRelease` CR, +enabling garbage collection when the CR is deleted. + +--- + +## Configuration + +| Environment Variable | Default | Description | +| ------------------------ | ------- | -------------------------------------------------- | +| `REQUIRE_NODEPOOL_MATCH` | `false` | Only generate VRS when matching Node labels exist. | +| `NODEPOOL_LABEL_PREFIX` | `""` | Prefix for node pool label keys. | + +See [design](design#configuration-environment-variables) for the full +environment-variable reference (PV sizing, ingress, gateway service type, discoverer +DaemonSet strategy, etc.). + +--- + +## Open Items + +- **Requeue behavior**: Pending/Progressing conditions currently return an `error`, causing + exponential back-off and noisy logs. These should return `ctrl.Result{RequeueAfter: duration}` + instead. The duration should be configurable. + +- **`STATUS` printcolumn**: Shows `conditions[0].status` (always the first phase). + Should be changed to reflect the current overall state more meaningfully. diff --git a/content/docs/user-guides/vald-operator/design.md b/content/docs/user-guides/vald-operator/design.md new file mode 100644 index 000000000..4ee5ac314 --- /dev/null +++ b/content/docs/user-guides/vald-operator/design.md @@ -0,0 +1,204 @@ +--- +title: "Design_user Guides/Vald Operator" +date: 2026-09-16T04:39:39Z +draft: false +weight: 0 +description: "" +menu: + valdoperator: + parent: Vald Operator +--- + +# Design Overview + +This document explains what the `valdoperatorrelease` controller does and how it works. +It is grounded in the current code; see the referenced files for the source of truth. + +## What it is + +A Kubernetes controller that manages the lifecycle of [Vald](https://vald.vdaas.org/) +clusters through a single custom resource, `ValdOperatorRelease` (short name `vor`, +API group `vald.vdaas.org/v1`). + +From a small, high-level input the controller generates one or more +[`ValdRelease`](https://github.com/vdaas/vald-helm-operator) (VRS) resources — the +manifests that the Vald Helm Operator (VHO) reconciles into a running Vald cluster. + +``` +ValdOperatorRelease (vor) ──reconcile──▶ ValdRelease (VRS) × (active infra × clusters) + minimal input consumed by vald-helm-operator +``` + +The controller only _generates_ VRS definitions. It does not run Vald itself; a VHO +running in the target cluster turns each VRS into Vald pods. This separation lets a +management cluster emit VRS definitions and distribute them to other clusters (for +example via an external workflow engine). + +## The `ValdOperatorRelease` (MVRS) resource + +The goal of MVRS is to collapse the large `ValdRelease` configuration surface into the +minimum a user must supply. There are two input groups. + +1. **Infrastructure / node-pool information** — `spec.infrastructure[]` +2. **Minimal Vald settings** — `spec.vectorEngine.vald` + +Source of truth: `internal/k8s/vald/operator/api/v1/valdoperatorrelease_types.go`. + +### `spec.infrastructure[]` + +A list of infrastructure entries. Each entry produces VRS objects for its clusters. + +| Field | Notes | +| ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `role` | Free-form role label (e.g. `hot`, `standby`, `blue`, `green`). Copied to VRS labels. | +| `type` | Cluster type label. | +| `active` | When `false`, the entry is skipped during VRS generation. | +| `clusters[]` | `{ id, name }`. The `id` field is typically filled in by an external system once the target cluster exists; `name` must be set. | +| `nodePools` | Map keyed by pool type: `general` (required) and `agent` (optional). Each pool carries `name`, `replicas`, and `machineResource{ cpu, memory, storage }`. | + +The `machineResource` values drive per-component resource and replica calculations for +the generated VRS. + +### `spec.vectorEngine.vald` + +The minimal Vald configuration reflected into each VRS: + +- `defaults.logLevel` +- `agent.ngt` — `dimension` (required, ≥2), `creationEdgeSize`, `searchEdgeSize`, `distanceType`, `objectType` +- `agent.persistentVolume` — `{ enabled, storageClass, accessMode }` (storageClass/accessMode fall back to env defaults when omitted) +- `indexer` — `indexSchedule`, `saveSchedule`, `concurrency`, `manager`, suspend flags, durations +- `gateway` — `indexReplica`, `serviceType`, `ingress{ enabled, host }` +- `discoverer.kind` — `DaemonSet` or `Deployment` +- `overlay` — an arbitrary JSON patch merged onto the generated VRS (see _Overlay_ below) + +## Lifecycle + +Reconciliation is modeled as an ordered flow of phases. Each phase carries a `Condition` +plus an optional `Builder` (creates/updates resources) and `Checker` (reports readiness). + +Phases (`pkg/operator/vald/service/phase.go`): + +| Phase (`status.phase`) | Builder | Checker | Purpose | +| ---------------------- | ------- | ------- | -------------------------------------------------------------------- | +| `WaitingClusterCreate` | – | yes | Validate infra config; wait until every cluster has `id` and `name`. | +| `WaitingCreateVrs` | yes | yes | Build the VRS objects and wait until they are ready. | +| `Completed` | – | – | Terminal phase. | + +### Accumulating, self-healing conditions + +`status.conditions` **accumulates** as phases progress (conditions are never removed), +and every reconcile re-evaluates **all** conditions. This is intentional: if a +previously-`True` condition later breaks (e.g. a generated VRS is deleted, or the spec +changes), the controller detects it and restarts work from that phase. `status.phase` +tracks the condition currently being processed. + +See `pkg/operator/vald/service/reconciler.go` (`reconcileValdOperatorRelease` / +`reconcilePhase`) and `pkg/operator/vald/service/phase.go`. + +### Readiness result states + +A phase check returns one of four `result` states +(`pkg/operator/vald/service/phase.go`): + +| Result | Condition status | Meaning | +| ----------------- | --------------------- | ---------------------------------------------------------------------------- | +| `Progressing("")` | Unknown / Progressing | The controller is actively working. | +| `Pending("msg")` | Unknown / Pending | Waiting on something external (e.g. external system assigning a cluster ID). | +| `Succeeded()` | True / Succeeded | Done. | +| `Failed(err)` | False / Failed | Misconfiguration or unrecoverable error. | + +For the cluster-create check: a missing `cluster.id`/`cluster.name` yields **Pending** +(external system not done yet), while empty `clusters` or empty `infrastructure` yields +**Failed** (misconfiguration). See `pkg/operator/vald/service/reconciler.go`. + +## VRS generation flow + +Implemented in `pkg/operator/vald/service/builder.go`. `vrsBuilder.Build` is a pure +function of `(CR, Config, Capability)` — it makes no Kubernetes API calls. + +1. **Validate** the CR (`infrastructure` non-empty, each cluster has a `name`). +2. **Iterate `infrastructure[]`**, skipping entries where `active == false` (and, when + node-pool matching is enabled, entries with no matching general pool). +3. **Assemble the VRS spec** from the CR (all in `pkg/operator/vald/service/builder.go`): + - `buildDefaults` — log level + - `buildGateway` — `indexReplica`, service type, ingress + - `buildAgent` — NGT settings + - `buildManager` — Manager mode when `indexer.manager == true`, otherwise Creator/Saver mode + - `buildDiscoverer` — kind + namespaced RBAC names +4. **Resolve resources from node pools** — `SetScaledResources` derives replicas and + per-component CPU/memory from the resolved agent node pool. The general-pool fallback + rule lives in `resolveAgentNodePool` (`pkg/operator/vald/service/rules.go`). +5. **Optional settings** — persistent volume and node affinities (both in `builder.go`). +6. **Per cluster** — for each `infra.clusters[]`, set `name = -` + (truncated to 63 chars), apply labels (`namespace`, `type`, `role`), then merge the + overlay. One VRS is produced per cluster, so a single MVRS can yield many VRS objects. + +### Applying generated resources (`ResourceSyncer`) + +The controller delegates the write side to `resource.Syncer` +(`internal/k8s/resource/syncer.go`): + +- `Build` the desired objects, then `CreateOrUpdate` each with an owner reference and a + `managed-generation` label set to the owner's `.metadata.generation`. +- **Prune**: list owned resources of the same kind and delete any that the current + `Build` no longer produces (stale generations). + +### Overlay + +`spec.vectorEngine.vald.overlay` is merged onto the generated VRS on top of the default +VRS template loaded at startup (`DEFAULT_VRS_PATH`). The merge uses `internal/config.Merge` +(reflection-based deep merge, later elements override earlier). See `mergeOverlay` in +`pkg/operator/vald/service/builder.go`. + +## Multi-cluster distribution and node-pool matching + +The same MVRS can be distributed to multiple clusters, generating a VRS only where a +matching node pool exists. + +- With `REQUIRE_NODEPOOL_MATCH=true`, the controller lists Nodes and resolves a + `NodePoolCapability`; a VRS is generated only when a `general` pool is present + (`pkg/operator/vald/service/capability.go`). +- Node match is by labels `namespace=` and `type=general` (required); + `type=agent` is optional — when present, agent pods are scheduled onto `type=agent`, + otherwise they fall back to `type=general`. +- Label keys default to `namespace` / `type`; set `NODEPOOL_LABEL_PREFIX` + (e.g. `vald.vdaas.org`) to namespace them. +- With `REQUIRE_NODEPOOL_MATCH=false` (default) the controller skips node listing and + treats every infra entry as schedulable (`AlwaysAvailable`). + +## Configuration (environment variables) + +Loaded once at startup into `config.Config` (`pkg/operator/vald/config/config.go`). + +| Env var | Default | Purpose | +| ------------------------------- | ------------------------------------------ | ----------------------------------------------------------------- | +| `DEFAULT_VRS_PATH` | `/opt/valdoperatorrelease/config/vrs.yaml` | Default VRS template merged with the overlay. | +| `REQUIRE_NODEPOOL_MATCH` | `false` | Only generate VRS where matching node pools exist. | +| `NODEPOOL_LABEL_PREFIX` | `""` | Prefix for the `namespace`/`type`/`role` node labels. | +| `AGENT_PODS_PER_NODE` | `2` | Agent pods packed per node when computing replicas. | +| `DEFAULT_STORAGE_CLASS` | `standard` | PV storage class fallback. | +| `DEFAULT_ACCESS_MODE` | `ReadWriteOnce` | PV access mode fallback. | +| `PV_BUFFER_RATIO` | `1.5` | PV size = `max(memoryRequest * ratio, min)`. | +| `PV_MIN_SIZE_BYTES` | `1073741824` | Minimum PV size in bytes. | +| `ENABLE_INGRESS` | `true` | Enable gateway ingress generation. | +| `GATEWAY_INGRESS_ANNOTATIONS` | `""` | YAML map of annotations applied to the gateway ingress. | +| `GATEWAY_SERVICE_TYPE` | `NodePort` | Gateway service type (`NodePort` / `ClusterIP` / `LoadBalancer`). | +| `VRS_LOG_LEVEL` | `warn` | Log level passed through to the generated VRS. | +| `DISCOVERER_DS_MAX_SURGE` | `30%` | Discoverer DaemonSet rolling-update `maxSurge`. | +| `DISCOVERER_DS_MAX_UNAVAILABLE` | `0%` | Discoverer DaemonSet rolling-update `maxUnavailable`. | + +## Source map + +| Concern | Location | +| ---------------------- | --------------------------------------------- | +| CRD types | `internal/k8s/vald/operator/api/v1/` | +| Reconciler | `pkg/operator/vald/service/reconciler.go` | +| Resource apply / prune | `internal/k8s/resource/syncer.go` | +| Phase / conditions | `pkg/operator/vald/service/phase.go` | +| Node-pool rules | `pkg/operator/vald/service/rules.go` | +| VRS builder | `pkg/operator/vald/service/builder.go` | +| Node-pool capability | `pkg/operator/vald/service/capability.go` | +| VRS API model | `internal/k8s/vald/operator/api/valdrelease/` | +| Config / env | `pkg/operator/vald/config/config.go` | +| Entry point | `cmd/operator/vald/main.go` | +| Manifests / CRDs | `k8s/operator/vald/` | diff --git a/content/docs/user-guides/vald-operator/resource-and-topology.md b/content/docs/user-guides/vald-operator/resource-and-topology.md new file mode 100644 index 000000000..b499f393a --- /dev/null +++ b/content/docs/user-guides/vald-operator/resource-and-topology.md @@ -0,0 +1,110 @@ +--- +title: "Resource and Topology_user Guides/Vald Operator" +date: 2026-09-16T04:39:39Z +draft: false +weight: 0 +description: "" +menu: + valdoperator: + parent: Vald Operator +--- + +# Resource Allocation and Topology Strategy + +## Background + +Each `ValdOperatorRelease` CR describes one or more node pools. A "general" pool hosts the +gateway, discoverer, and manager components. An optional "agent" (ValdAgent) pool +hosts the NGT vector index pods. + +When there is no dedicated agent pool the controller falls back to the general pool +for agent placement (via `effectiveNodePoolType`). Before this fix that fallback was +not applied to resource and replica sizing, producing zero-replica agent deployments +in no-agent-pool configurations. + +## Node Pool Fallback for Agent Resources + +The agent node-pool selection rule lives in +`pkg/operator/vald/service/rules.go` (`resolveAgentNodePool`) and is called directly by +the builder. This keeps `vrsBuilder.Build` a pure function of `(CR, Config, Capability)`: + +```go +// pkg/operator/vald/service/builder.go +agentPool := resolveAgentNodePool(infra) +row.SetScaledResources(agentPool.NodeCount, agentPool.MachineResource, resourceParams) +``` + +`ResolveAgentNodePool` returns the dedicated `agent` pool when present, and otherwise +falls back to the `general` pool for both replica count and machine resources — so a +config with no agent pool no longer produces zero-replica agent deployments. + +`effectiveNodePoolType` (`pkg/operator/vald/service/builder.go`) applies the same fallback +to NodeSelector/Tolerations, so placement and resource sizing stay consistent. +`resourceParams` carries `AgentPodsPerNode` and the discoverer DaemonSet rolling-update +values from `Config`. + +## Agent Resource Calculation + +Agent pods request a fraction of the node's resources so that +`AgentPodsPerNode` (default 2) pods fit on one node: + +``` +CPU request = nodeCPU × AgentResourceRatio / AgentPodsPerNode +RAM request = nodeRAM × AgentResourceRatio / AgentPodsPerNode +CPU limit = nodeCPU × AgentResourceRatio (all pods on this node share the limit) +RAM limit = (not set) +``` + +`AgentResourceRatio = 0.6` — 60% of the node is reserved for agent pods combined; the +remaining 40% covers OS, DaemonSets, and other system processes. + +Memory limit is intentionally absent: the NGT index grows on disk after startup and +is then mmap'd into address space. A hard memory limit would trigger OOM kills as the +index expands. + +## Agent Pod Count + +``` +agentPodCount = agentNodeCount × AgentPodsPerNode +``` + +`MinReplicas = MaxReplicas = agentPodCount` (agent uses a StatefulSet with no HPA). + +## Gateway Replica Scaling + +Gateway replicas track agent replica count with an HPA range: + +``` +minReplicas = max(agentPodCount / 2, 1) +maxReplicas = max(agentPodCount × 2, 1) +``` + +## Gateway / Discoverer / Manager Resources + +These components are not node-resource-dependent; they run against vald upstream +defaults and are unaffected by node pool sizing: + +| Component | CPU req | RAM req | CPU lim | RAM lim | +| ------------- | ------- | ------- | ------- | ------- | +| gateway-lb | 200m | 150Mi | 2000m | 700Mi | +| discoverer | 200m | 65Mi | 600m | 200Mi | +| manager-index | 200m | 80Mi | 1000m | 500Mi | + +## Topology Spread Constraints + +All four components receive a `topologySpreadConstraints` entry so Kubernetes +spreads pods across nodes rather than relying on implicit scheduler behaviour: + +```yaml +topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: DoNotSchedule + labelSelector: + matchLabels: + app.kubernetes.io/component: +``` + +`maxSkew: 1` with `DoNotSchedule` ensures at most one extra pod per node compared +to the least-loaded node. This replaces the previous approach of inflating resource +requests to crowd out extra pods, which was fragile and hard to reason about. diff --git a/content/docs/user-guides/vald-operator/test-strategy.md b/content/docs/user-guides/vald-operator/test-strategy.md new file mode 100644 index 000000000..906fa1b58 --- /dev/null +++ b/content/docs/user-guides/vald-operator/test-strategy.md @@ -0,0 +1,60 @@ +--- +title: "Test Strategy_user Guides/Vald Operator" +date: 2026-09-16T04:39:39Z +draft: false +weight: 0 +description: "" +menu: + valdoperator: + parent: Vald Operator +--- + +# Test Strategy + +## Background + +The controller uses a single test style: plain `testing` + testify unit tests. +All behavior is covered at the unit level — there are no integration tests or envtest +dependencies. + +## Test layers + +### Unit tests (`testing` + testify) + +Cover pure Go logic — anything that does not depend on a live Kubernetes API server. + +| Package | Under test | +| ---------------------------------------- | ------------------------------------------------------------------------------------ | +| `pkg/operator/vald/service` (builder) | `validate()`, component builders, `Build()`, `mergeOverlay` (golden file) | +| `pkg/operator/vald/service` (capability) | `AlwaysAvailable`, `ResolveNodePoolCapability` | +| `pkg/operator/vald/service` (rules) | `resolveAgentNodePool` fallback logic | +| `pkg/operator/vald/service` (reconciler) | Reconcile loop, phase transitions, status updates (client faked via interface mocks) | +| `pkg/operator/vald/service` (operator) | `New`, `Operator.Start` cancellation | +| `pkg/operator/vald/config` | Config loading and defaults | + +## What is intentionally not unit-tested, and why + +- **`zz_generated.deepcopy.go`** — generated code; not hand-written, so out of scope. + It lowers the raw `go test -cover` number without being a real gap. +- **`reconcilePhase` Kubernetes side-effects** — `CreateOrUpdate` / `Prune` against a real + API server are not exercised in unit tests. The reconciler tests fake the client, so + the unit suite validates the phase logic, not the wire protocol. E2E tests cover the + full reconcile against a real cluster. + +## Golden file test + +`TestVrsBuilder_Build` compares the first item of the generated `ValdRelease` list +against `testdata/vrs.golden.yaml`. The golden file is updated when the test is run with +`-update`, so intentional changes must be reviewed as part of the diff. + +## Coverage philosophy + +We prioritize unit coverage of hand-written logic, excluding generated code. The overall +`go test -cover` number looks low because generated code drags it down; per-function, the +main paths of the hand-written code are covered. + +## Framework choice + +| Use | Framework | Reason | +| ---------- | ------------------- | ------------------------------------------------ | +| Unit tests | `testing` + testify | Standard, minimal deps, easy table-driven tests. | diff --git a/content/docs/v1.8/_index.md b/content/docs/v1.8/_index.md new file mode 100644 index 000000000..ea2c6c095 --- /dev/null +++ b/content/docs/v1.8/_index.md @@ -0,0 +1,6 @@ +--- +title: "V1.8/Index" +date: 2026-09-16T04:40:22Z +draft: false +--- + diff --git a/content/docs/v1.8/api/_index.md b/content/docs/v1.8/api/_index.md new file mode 100644 index 000000000..99c9a26a8 --- /dev/null +++ b/content/docs/v1.8/api/_index.md @@ -0,0 +1,37 @@ +--- +title: "Api_v1.8/Api" +date: 2026-09-16T04:40:20Z +draft: false +weight: 0 +--- + +# Vald APIs + +Vald provides 6 kinds of API for handling vector with Vald cluster.
+Vald provide 2 API interface: gRPC and REST API. +Using **gRPC** is preferred for better performance. + +## Vald APIs Overview + +The APIs overview tables is here: + +| Service | Description | API NAMES | LINK | +| :------------: | :------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------- | +| Insert Service | Insert new vector(s) into the Vald Agent Pods. | [Insert](/docs/v1.8/api/insert#insert-rpc)
[StreamInsert](/docs/v1.8/api/insert#streaminsert-rpc)
[MultiInsert](/docs/v1.8/api/insert#multiinsert-rpc) | [Vald Insert APIs](/docs/v1.8/api/insert) | +| Update Service | Update the exists vector(s) in the Vald Agent Pods. | [Update](/docs/v1.8/api/update#update-rpc)
[StreamUpdate](/docs/v1.8/api/update#streamupdate-rpc)
[MultiUpdate](/docs/v1.8/api/update#multiupdate-rpc) | [Vald Update APIs](/docs/v1.8/api/update) | +| Upsert Service | Update the exists vector(s) in the Vald Agent Pods or Insert new vector(s) into the Vald Agent Pods if the vector is not exists. | [Upsert](/docs/v1.8/api/upsert#upsert-rpc)
[StreamUpsert](/docs/v1.8/api/upsert#streamupsert-rpc)
[MultiUpdate](/docs/v1.8/api/upsert#multiupsert-rpc) | [Vald Upsert APIs](/docs/v1.8/api/upsert) | +| Search Service | Search nearest neighbor vectors using query (vectors or IDs).
There are 2 types of Search methods: Search (means ANN Search) and LinearSearch. | [Search](/docs/v1.8/api/search#search-rpc)
[SearchByID](/docs/v1.8/api/search#searchbyid-rpc)
[StreamSearch](/docs/v1.8/api/search#streamsearch-rpc)
[StreamSearchByID](/docs/v1.8/api/search#streamsearchbyid-rpc)
[MultiSearch](/docs/v1.8/api/search#multisearch-rpc)
[MultiSearchByID](/docs/v1.8/api/search#multisearchbyid-rpc)
[LinearSearch](/docs/v1.8/api/search#linearsearch-rpc)
[LinearSearchByID](/docs/v1.8/api/search#linearsearchbyid-rpc)
[StreamLinearSearch](/docs/v1.8/api/search#streamlinearsearch-rpc)
[StreamLinearSearchByID](/docs/v1.8/api/search#streamlinearsearchbyid-rpc)
[MultiLinearSearch](/docs/v1.8/api/search#multilinearsearch-rpc)
[MultiLinearSearchByID](/docs/v1.8/api/search#multilinearsearchbyid-rpc) | [Vald Search APIs](/docs/v1.8/api/search) | +| Remove Service | Remove the exists vector(s) from the Vald Agent Pods. | [Remove](/docs/v1.8/api/remove#remove-rpc)
[StreamRemove](/docs/v1.8/api/remove#streamremove-rpc)
[MultiRemove](/docs/v1.8/api/remove#multiremove-rpc) | [Vald Remove APIs](/docs/v1.8/api/remove) | +| Flush Service | Remove the exists all vector(s) from the Vald Agent Pods. | [Flush](/docs/v1.8/api/flush#flush-rpc)
| [Vald Flush APIs](/docs/v1.8/api/flush) | +| Object Service | Get information of vector(s) indexed into the Vald Agent Pods. | [Exists](/docs/v1.8/api/object#exists-rpc)
[GetObject](/docs/v1.8/api/object#getobject-rpc)
[StreamGetObject](/docs/v1.8/api/object#streamgetobject-rpc) | [Vald Object APIs](/docs/v1.8/api/object) | + +
+
+
+linear search services are available in v1.4.0 and later version. +
+ +## Client Library + +Vald provides [the public client libraries](/docs/v1.8/user-guides/sdks) for some programming languages. +If there is no client library for your favorite programming language, please refer to [build proto docs](/docs/v1.8/api/build_proto) to build on yourself. diff --git a/content/docs/v1.8/api/build_proto.md b/content/docs/v1.8/api/build_proto.md new file mode 100644 index 000000000..3a6e3b51a --- /dev/null +++ b/content/docs/v1.8/api/build_proto.md @@ -0,0 +1,383 @@ +--- +title: "Build_proto_v1.8/Api" +date: 2026-09-16T04:40:20Z +draft: false +weight: 1100 +description: How to build gRPC proto files for calling API to your Vald cluster +menu: + api: + parent: Api +--- + +# Building gRPC proto + +This page shows how to build gRPC proto file for calling API to your Vald Cluster. + +
+Vald provides the official client libraries (see: SDK document). +If you can use one of the SDKs we recommend using it. +
+ +## Target proto files + +Vald defines the proto file for each API. +Let's check the below table for the details. + +| API service name | proto | dependencies | usage | +| :--------------: | :-------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------- | +| Insert | [insert.proto](https://github.com/vdaas/vald/blob/main/apis/proto/v1/vald/insert.proto) | [payload.proto](https://github.com/vdaas/vald/blob/main/apis/proto/v1/payload/payload.proto)
[annotation.proto](https://github.com/googleapis/googleapis/blob/master/google/api/annotations.proto) | Insert vectors into Vald Agent | +| Update | [update.proto](https://github.com/vdaas/vald/blob/main/apis/proto/v1/vald/update.proto) | [payload.proto](https://github.com/vdaas/vald/blob/main/apis/proto/v1/payload/payload.proto)
[annotation.proto](https://github.com/googleapis/googleapis/blob/master/google/api/annotations.proto) | Update vectors stored in Vald Agent | +| Upsert | [upsert.proto](https://github.com/vdaas/vald/blob/main/apis/proto/v1/vald/upsert.proto) | [payload.proto](https://github.com/vdaas/vald/blob/main/apis/proto/v1/payload/payload.proto)
[annotation.proto](https://github.com/googleapis/googleapis/blob/master/google/api/annotations.proto) | Update vectors stored Vald Agent or Insert vectors into Vald Agent | +| Search | [search.proto](https://github.com/vdaas/vald/blob/main/apis/proto/v1/vald/search.proto) | [payload.proto](https://github.com/vdaas/vald/blob/main/apis/proto/v1/payload/payload.proto)
[annotation.proto](https://github.com/googleapis/googleapis/blob/master/google/api/annotations.proto) | Search similar vectors with query | +| Remove | [remove.proto](https://github.com/vdaas/vald/blob/main/apis/proto/v1/vald/remove.proto) | [payload.proto](https://github.com/vdaas/vald/blob/main/apis/proto/v1/payload/payload.proto)
[annotation.proto](https://github.com/googleapis/googleapis/blob/master/google/api/annotations.proto) | Remove stored vectors from Vald Agent | +| Object | [object.proto](https://github.com/vdaas/vald/blob/main/apis/proto/v1/vald/object.proto) | [payload.proto](https://github.com/vdaas/vald/blob/main/apis/proto/v1/payload/payload.proto)
[annotation.proto](https://github.com/googleapis/googleapis/blob/master/google/api/annotations.proto) | Get object information of vector stored in Vald Agent | + +## How to build protobuf + +### The way to build proto files + +Let's build proto files using your favorite programming language. + +There are 3 steps to building API proto: + +1. Install gRPC tools + - gRPC official document provides [the way to install for each language](https://grpc.io/docs/languages/).
+ If your favorite programming language is not there, you can find 3rd party tools for building. + +1. Download Vald api proto files and external dependence + - [vald api proto](https://github.com/vdaas/vald/tree/main/apis/proto/v1/vald) + - [vald payload proto](https://github.com/vdaas/vald/tree/main/apis/proto/v1/payload) + - [googleapis](https://github.com/googleapis/googleapis) + - [PGV](https://github.com/envoyproxy/protoc-gen-validate) + +1. Choose proto file(s) and build + +### Example: Build proto files in Rust + +This section shows the example steps for building proto files using Rust. + +There are many tools for building proto in Rust, we use [tonic](https://github.com/hyperium/tonic) as an example. + +1. Check version + + This example runs in this environment. + + ```bash + $ cargo version + cargo 1.58.0 (7f08ace4f 2021-11-24) + $ rustc -V + rustc 1.58.0 (02072b482 2022-01-11) + $ rustup -V + rustup 1.24.3 (ce5817a94 2021-05-31) + info: This is the version for the rustup toolchain manager, not the rustc compiler. + info: The currently active `rustc` version is `rustc 1.57.0 (f1edd0429 2021-11-29)` + $ rustup show + Default host: x86_64-unknown-linux-gnu + rustup home: /home/user/.rustup + + stable-x86_64-unknown-linux-gnu (default) + rustc 1.58.0 (02072b482 2022-01-11) + ``` + +1. Create project + + ```bash + cargo new --lib vald-grpc + ``` + +1. Edit `Cargo.toml` + + ```bash + cd vald-grpc && \ + vim Cargo.toml + --- + [package] + name = "vald-grpc" + version = "0.1.0" + edition = "2024" + + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + + [[bin]] + name = "client" + path = "src/client.rs" + + [dependencies] + tonic = "0.6.2" + tokio = "1.15" + prost = "0.9" + prost-types = "0.9" + hdf5 = "0.8.1" + chrono = "^0.4" + + [build-dependencies] + tonic-build = "0.6.2" + ``` + +1. Download Vald proto files and dependence proto files + + ```bash + // create proto file root dir + mkdir -p proto + ``` + + ```bash + // download vald proto files + git clone https://github.com/vdaas/vald \ + && cp -R vald/apis/proto/v1/vald proto/vald \ + && cp -R vald/apis/proto/v1/payload proto/payload \ + && rm -rf vald + ``` + + ```bash + // download googleapis + git clone https://github.com/googleapis/googleapis \ + && cp -R googleapis/google proto/google \ + && rm -rf googleapis + ``` + + ```bash + // download protoc-gen-validate + git clone https://github.com/envoyproxy/protoc-gen-validate \ + && mv protoc-gen-validate proto/protoc-gen-validate + ``` + +1. Fixing import path + + ```bash + find proto/vald -type f -name "*.proto" | xargs sed -i "s/apis\/proto\/v1\///g" && \ + find proto/vald -type f -name "*.proto" | xargs sed -i "s/github\.com\/googleapis\/googleapis\///g" && \ + find proto/payload -type f -name "*.proto" | xargs sed -i "s/github\.com\/googleapis\/googleapis\///g" && \ + find proto/payload -type f -name "*.proto" | xargs sed -i "s/github\.com\/envoyproxy\///g" + ``` + +1. Implement `build.rs` and Build proto + 1. `build.rs` + + ```rust + fn main() -> Result<(), Box> { + let insert_proto = "./proto/vald/insert.proto"; + let update_proto = "./proto/vald/update.proto"; + let upsert_proto = "./proto/vald/upsert.proto"; + let search_proto = "./proto/vald/search.proto"; + let remove_proto = "./proto/vald/remove.proto"; + let object_proto = "./proto/vald/object.proto"; + + tonic_build::configure() + .build_client(true) + .out_dir("./src/proto") + .compile( + &[ + insert_proto, + update_proto, + upsert_proto, + search_proto, + remove_proto, + object_proto, + ], + &["./proto"], + ) + .unwrap_or_else(|e| panic!("protobuf compile error: {}", e)); + Ok(()) + } + ``` + + 1. build proto + + ```bash + cargo build + ``` + +1. Edit `Cargo.toml` + + ```bash + cd vald-grpc && \ + vim Cargo.toml + --- + [package] + ... + + [[bin]] + name = "client" + path = "src/client.rs" + ... + ``` + +1. Implement code using client + 1. `lib.rs` + + Import build proto in `src/lib.rs` + + ```rust + pub mod vald { + pub mod v1 { + include!("./proto/vald.v1.rs"); + } + } + + pub mod payload { + pub mod v1 { + include!("./proto/payload.v1.rs"); + } + } + + pub mod google { + pub mod rpc { + include!("./proto/google.rpc.rs"); + } + pub mod api { + include!("./proto/google.api.rs"); + } + pub mod protobuf { + include!("./proto/google.protobuf.rs"); + } + } + ``` + + 1. `src/client.rs` + + There are 4 steps in `src/client.rs`: + 1. Load dataset + 1. Insert vector to Vald cluster + 1. Search nearest neighbor vectors from Vald cluster after indexing finished + 1. Remove indexed vectors from Vald cluster + + The example is here: + + ```rust + // import packages + use chrono::Utc; + use hdf5::{File, Result}; + use std::thread::sleep; + use std::time; + use tonic::transport::Endpoint; + + // import vald protos + use vald_sample_rust_client::payload::v1::insert; + use vald_sample_rust_client::payload::v1::object; + use vald_sample_rust_client::payload::v1::remove; + use vald_sample_rust_client::payload::v1::search; + use vald_sample_rust_client::vald::v1::insert_client; + use vald_sample_rust_client::vald::v1::remove_client; + use vald_sample_rust_client::vald::v1::search_client; + + // Dataset file name + static FILE: &str = "fashion-mnist-784-euclidean.hdf5"; + // Dataset name + static DATASET: &str = "train"; + // set Vald cluster host + static HOST: &str = "http://localhost:8080"; + // Time duration for waiting to finish `CreateIndex` and `SaveIndex` + static DURATION: u64 = 15; + + // load data + fn read_file() -> Result>> { + let file = File::open(FILE).unwrap_or_else(|e| panic!("[ERR] failed to read file: {}", e)); + let data = file + .dataset(DATASET) + .unwrap_or_else(|e| panic!("[ERR] failed to get dataset: {}", e)); + let mut vector = Vec::new(); + for train in data.read_2d::()?.outer_iter() { + let mut vec: Vec = Vec::new(); + vec.append(&mut train.to_vec()); + vector.push(vec); + if vector.len() == 500 { + break; + } + } + Ok(vector) + } + + #[tokio::main(flavor = "current_thread")] + async fn main() -> Result<(), Box> { + print!("[Start] Load {} file\n", FILE); + let vec = read_file()?; + print!("[End] Success to load {} file\n", FILE); + + print!("[Start] Insert phase\n"); + // create insert client + let mut insert_client = + insert_client::InsertClient::connect(Endpoint::from_static(HOST)).await?; + let mut ids: Vec = Vec::new(); + for v in vec.iter() { + let id = Utc::now().timestamp_nanos().to_string(); + ids.push(id.to_string()); + // insert vector + let _ = insert_client + .insert(insert::Request { + vector: Some(object::Vector { + id: id.to_string(), + vector: v.to_vec(), + }), + config: Some(insert::Config { + skip_strict_exist_check: true, + filters: None, + timestamp: Utc::now().timestamp(), + }), + }) + .await?; + } + print!("[End] Finish Insert Phase\n"); + + print!("[Sleep] Waiting SaveIndex is completed...\n"); + sleep(time::Duration::from_secs(DURATION)); + + print!("[Start] Search phase\n"); + // create search client + let mut search_client = + search_client::SearchClient::connect(Endpoint::from_static(HOST)).await?; + for id in ids.clone() { + // search nearest neighbor vectors using searchById method + let res = search_client + .search_by_id(search::IdRequest { + id: id.to_string(), + config: Some(search::Config { + request_id: id.to_string(), + num: 10, + radius: -1.0, + epsilon: -1.0, + timeout: 500, + ingress_filters: None, + egress_filters: None, + }), + }) + .await?; + print!("[Id]: {:?}\n", id.to_string()); + print!("[Result]: {:#?}\n", res.into_inner().results); + } + print!("[End] Finish Search Phase\n"); + + print!("[Start] Remove phase\n"); + // create remove client + let mut remove_client = + remove_client::RemoveClient::connect(Endpoint::from_static(HOST)).await?; + for id in ids.clone() { + // remove vectors + let _ = remove_client + .remove(remove::Request { + id: Some(object::Id { id: id.to_string() }), + config: None, + }) + .await?; + } + print!("[End] Finish Remove phase\n"); + + Ok(()) + } + ``` + +1. Build + + Build implemented codes before running example code. + + ```bash + cargo build + ``` + +1. Running example + + After creating Vald cluster, you can run an example code by the following command. + + ```bash + cargo run src/client.rs + ``` diff --git a/content/docs/v1.8/api/filter-gateway.md b/content/docs/v1.8/api/filter-gateway.md new file mode 100644 index 000000000..3479f4ff2 --- /dev/null +++ b/content/docs/v1.8/api/filter-gateway.md @@ -0,0 +1,1643 @@ +--- +title: "Filter Gateway_v1.8/Api" +date: 2026-09-16T04:40:20Z +draft: false +weight: 800 +description: How to use CRUD API with filter gateway +menu: + api: + parent: Api +--- + +# Vald Filter Gateway APIs + +## Overview + +Filter Server is responsible for providing insert, update, upsert and search interface for `Vald Filter Gateway`. + +Vald Filter Gateway forward user request to user-defined ingress/egress filter components allowing user to run custom logic. + +## Insert RPC + +Insert RPC is the method to insert object through Vald Filter Gateway. + +```rpc +service Filter { + rpc InsertObject(payload.v1.Insert.ObjectRequest) + returns (payload.v1.Object.Location) { + option (google.api.http) = { + post : "/insert/object" + body : "*" + }; + } +} +``` + +### Input + +- the scheme of `payload.v1.Insert.ObjectRequest` + + ```rpc + message Insert { + message ObjectRequest { + Object.Blob object = 1; + Config config = 2; + Filter.Target vectorizer = 3; + } + + message Config { + bool skip_strict_exist_check = 1; + Filter.Config filters = 2; + int64 timestamp = 3; + } + } + + message Object { + message Blob { + string id = 1 [ (validate.rules).string.min_len = 1 ]; + bytes object = 2; + } + } + + message Filter { + message Target { + string host = 1; + uint32 port = 2; + } + + message Config { + repeated Target targets = 1; + } + } + ``` + + - Insert.ObjectRequest + + | field | type | label | required | desc. | + | :--------: | :------------ | :---- | :------: | :-------------------------------------- | + | object | Object.Blob | | \* | the binary object to be inserted | + | config | Config | | \* | the configuration of the insert request | + | vectorizer | Filter.Target | | \* | filter target | + + - Object.Blob + + | field | type | label | required | desc. | + | :----: | :----- | :---- | :------: | :---------------- | + | id | string | | \* | the object ID | + | object | bytes | | \* | the binary object | + + - Insert.Config + + | field | type | label | required | desc. | + | :---------------------: | :------------ | :---- | :------: | :--------------------------------------------------------------------------------------------------- | + | skip_strict_exist_check | bool | | | check the same vector is already inserted or not.
the ID should be unique if the value is `true`. | + | timestamp | int64 | | | the timestamp of the vector inserted.
if it is N/A, the current time will be used. | + | filters | Filter.Config | | | configuration for filter | + + - Filter.Target + + | field | type | label | required | desc. | + | :---: | :----- | :---- | :------: | :------------------ | + | host | string | | \* | the target hostname | + | port | port | | \* | the target port | + + - Filter.Config + + | field | type | label | required | desc. | + | :-----: | :------------ | :----------------------------- | :------: | :------------------------------ | + | targets | Filter.Target | repeated(Array[Filter.Target]) | \* | the filter target configuration | + +### Output + +- the scheme of `payload.v1.Object.Location` + + ```rpc + message Object { + message Location { + string name = 1; + string uuid = 2; + repeated string ips = 3; + } + } + ``` + + - Object.Location + + | field | type | label | desc. | + | :---: | :----- | :---------------------- | :--------------------------------------------------------------------- | + | name | string | | the name of vald agent pod where the request vector is inserted. | + | uuid | string | | the ID of the inserted vector. It is the same as an `Object.Vector`. | + | ips | string | repeated(Array[string]) | the IP list of `vald-agent` pods where the request vector is inserted. | + +### Status Code + +| code | desc. | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 6 | ALREADY_EXISTS | +| 13 | INTERNAL | + +## StreamInsert RPC + +StreamInsert RPC is the method to add new multiple object using the [bidirectional streaming RPC](https://grpc.io/docs/what-is-grpc/core-concepts/#bidirectional-streaming-rpc). + +By using the bidirectional streaming RPC, the insert request can be communicated in any order between client and server. +Each Insert request and response are independent. +It's the recommended method to insert a large number of objects. + +```rpc +service Filter { + rpc StreamInsertObject(stream payload.v1.Insert.ObjectRequest) + returns (stream payload.v1.Object.StreamLocation) {} +} +``` + +### Input + +- the scheme of `payload.v1.Insert.ObjectRequest` + + ```rpc + message Insert { + message ObjectRequest { + Object.Blob object = 1; + Config config = 2; + Filter.Target vectorizer = 3; + } + + message Config { + bool skip_strict_exist_check = 1; + Filter.Config filters = 2; + int64 timestamp = 3; + } + } + + message Object { + message Blob { + string id = 1 [ (validate.rules).string.min_len = 1 ]; + bytes object = 2; + } + } + + message Filter { + message Target { + string host = 1; + uint32 port = 2; + } + + message Config { + repeated Target targets = 1; + } + } + ``` + + - Insert.ObjectRequest + + | field | type | label | required | desc. | + | :--------: | :------------ | :---- | :------: | :-------------------------------------- | + | object | Object.Blob | | \* | the binary object to be inserted | + | config | Config | | \* | the configuration of the insert request | + | vectorizer | Filter.Target | | \* | filter configurations | + + - Object.Blob + + | field | type | label | required | desc. | + | :----: | :----- | :---- | :------: | :---------------- | + | id | string | | \* | the object ID | + | object | bytes | | \* | the binary object | + + - Insert.Config + + | field | type | label | required | desc. | + | :---------------------: | :------------ | :---- | :------: | :--------------------------------------------------------------------------------------------------- | + | skip_strict_exist_check | bool | | | check the same vector is already inserted or not.
the ID should be unique if the value is `true`. | + | timestamp | int64 | | | the timestamp of the vector inserted.
if it is N/A, the current time will be used. | + | filters | Filter.Config | | | configuration for filter | + + - Filter.Target + + | field | type | label | required | desc. | + | :---: | :----- | :---- | :------: | :------------------ | + | host | string | | \* | the target hostname | + | port | port | | \* | the target port | + + - Filter.Config + + | field | type | label | required | desc. | + | :-----: | :------------ | :----------------------------- | :------: | :------------------------------ | + | targets | Filter.Target | repeated(Array[Filter.Target]) | \* | the filter target configuration | + +### Output + +- the scheme of `payload.v1.Object.StreamLocation` + + ```rpc + message Object { + message StreamLocation { + oneof payload { + Location location = 1; + google.rpc.Status status = 2; + } + } + + message Location { + string name = 1; + string uuid = 2; + repeated string ips = 3; + } + } + ``` + + - Object.StreamLocation + + | field | type | label | desc. | + | :------: | :---------------- | :---- | :----------------------------------------- | + | location | Object.Location | | the information of `Object.Location` data. | + | status | google.rpc.Status | | the status of google RPC. | + + - Object.Location + + | field | type | label | desc. | + | :---: | :----- | :---------------------- | :--------------------------------------------------------------------- | + | name | string | | the name of vald agent pod where the request vector is inserted. | + | uuid | string | | the ID of the inserted vector. It is the same as an `Object.Vector`. | + | ips | string | repeated(Array[string]) | the IP list of `vald-agent` pods where the request vector is inserted. | + + - [google.rpc.Status](https://github.com/googleapis/googleapis/blob/master/google/rpc/status.proto) + + | field | type | label | desc. | + | :-----: | :------------------ | :------------------- | :-------------------------------------- | + | code | int32 | | status code (code list is next section) | + | message | string | | error message | + | details | google.protobuf.Any | repeated(Array[any]) | the details error message list | + +### Status Code + +| code | desc. | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 6 | ALREADY_EXISTS | +| 13 | INTERNAL | + +## MultiInsert RPC + +MultiInsert RPC is the method to add multiple new objects in **1** request. + +```rpc +service Filter { + rpc MultiInsertObject(payload.v1.Insert.MultiObjectRequest) + returns (payload.v1.Object.Locations) { + option (google.api.http) = { + post : "/insert/object/multiple" + body : "*" + }; + } +} +``` + +### Input + +- the scheme of `payload.v1.Insert.MultiObjectRequest` + + ```rpc + message Insert { + message MultiObjectRequest { + repeated ObjectRequest requests = 1; + } + + message ObjectRequest { + Object.Blob object = 1; + Config config = 2; + Filter.Target vectorizer = 3; + } + + message Config { + bool skip_strict_exist_check = 1; + Filter.Config filters = 2; + int64 timestamp = 3; + } + } + + message Object { + message Blob { + string id = 1 [ (validate.rules).string.min_len = 1 ]; + bytes object = 2; + } + } + + message Filter { + message Target { + string host = 1; + uint32 port = 2; + } + + message Config { + repeated Target targets = 1; + } + } + ``` + + - Insert.MultiObjectRequest + + | field | type | label | required | desc. | + | :------: | :------------ | :------------------------------------ | :------: | :--------------------------------------------------- | + | requests | ObjectRequest | repeated(Array[Insert.ObjectRequest]) | \* | the multiple search by binary object request content | + + - Insert.ObjectRequest + + | field | type | label | required | desc. | + | :--------: | :------------ | :---- | :------: | :-------------------------------------- | + | object | Object.Blob | | \* | the binary object to be inserted | + | config | Config | | \* | the configuration of the insert request | + | vectorizer | Filter.Target | | \* | filter configurations | + + - Object.Blob + + | field | type | label | required | desc. | + | :----: | :----- | :---- | :------: | :---------------- | + | id | string | | \* | the object ID | + | object | bytes | | \* | the binary object | + + - Insert.Config + + | field | type | label | required | desc. | + | :---------------------: | :------------ | :---- | :------: | :--------------------------------------------------------------------------------------------------- | + | skip_strict_exist_check | bool | | | check the same vector is already inserted or not.
the ID should be unique if the value is `true`. | + | timestamp | int64 | | | the timestamp of the vector inserted.
if it is N/A, the current time will be used. | + | filters | Filter.Config | | | configuration for filter | + + - Filter.Target + + | field | type | label | required | desc. | + | :---: | :----- | :---- | :------: | :------------------ | + | host | string | | \* | the target hostname | + | port | port | | \* | the target port | + + - Filter.Config + + | field | type | label | required | desc. | + | :-----: | :------------ | :----------------------------- | :------: | :------------------------------ | + | targets | Filter.Target | repeated(Array[Filter.Target]) | \* | the filter target configuration | + +### Output + +- the scheme of `payload.v1.Object.Locations` + + ```rpc + message Object { + message Locations { repeated Location locations = 1; } + + message Location { + string name = 1; + string uuid = 2; + repeated string ips = 3; + } + } + ``` + + - Object.Locations + + | field | type | label | desc. | + | :------: | :-------------- | :------------------------------- | :----------------------------- | + | location | Object.Location | repeated(Array[Object.Location]) | the list of `Object.Location`. | + + - Object.Location + + | field | type | label | desc. | + | :---: | :----- | :---------------------- | :--------------------------------------------------------------------- | + | name | string | | the name of vald agent pod where the request vector is inserted. | + | uuid | string | | the ID of the inserted vector. It is the same as an `Object.Vector`. | + | ips | string | repeated(Array[string]) | the IP list of `vald-agent` pods where the request vector is inserted. | + +### Status Code + +| code | desc. | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 6 | ALREADY_EXISTS | +| 13 | INTERNAL | + +## Update RPC + +Update RPC is the method to update a single vector. + +```rpc +service Filter { + rpc UpdateObject(payload.v1.Update.ObjectRequest) + returns (payload.v1.Object.Location) { + option (google.api.http) = { + post : "/update/object" + body : "*" + }; + } +} +``` + +### Input + +- the scheme of `payload.v1.Update.ObjectRequest` + + ```rpc + message Update { + message ObjectRequest { + Object.Blob object = 1; + Config config = 2; + Filter.Target vectorizer = 3; + } + + message Config { + bool skip_strict_exist_check = 1; + Filter.Config filters = 2; + int64 timestamp = 3; + } + } + + message Object { + message Blob { + string id = 1 [ (validate.rules).string.min_len = 1 ]; + bytes object = 2; + } + } + + message Filter { + message Target { + string host = 1; + uint32 port = 2; + } + + message Config { + repeated Target targets = 1; + } + } + ``` + + - Update.ObjectRequest + + | field | type | label | required | desc. | + | :--------: | :------------ | :---- | :------: | :-------------------------------------- | + | object | Object.Blob | | \* | the binary object to be updated | + | config | Config | | \* | the configuration of the update request | + | vectorizer | Filter.Target | | \* | filter target | + + - Object.Blob + + | field | type | label | required | desc. | + | :----: | :----- | :---- | :------: | :---------------- | + | id | string | | \* | the object ID | + | object | bytes | | \* | the binary object | + + - Update.Config + + | field | type | label | required | desc. | + | :---------------------: | :------------ | :---- | :------: | :-------------------------------------------------------------------------------------------------- | + | skip_strict_exist_check | bool | | | check the same vector is already updated or not.
the ID should be unique if the value is `true`. | + | timestamp | int64 | | | the timestamp of the vector updated.
if it is N/A, the current time will be used. | + | filters | Filter.Config | | | configuration for filter | + | disable_balanced_update | bool | | | A flag to disable balanced update (split remove -> insert operation) during update operation. | + + - Filter.Target + + | field | type | label | required | desc. | + | :---: | :----- | :---- | :------: | :------------------ | + | host | string | | \* | the target hostname | + | port | port | | \* | the target port | + + - Filter.Config + + | field | type | label | required | desc. | + | :-----: | :------------ | :----------------------------- | :------: | :------------------------------ | + | targets | Filter.Target | repeated(Array[Filter.Target]) | \* | the filter target configuration | + +### Output + +- the scheme of `payload.v1.Object.Location` + + ```rpc + message Object { + message Location { + string name = 1; + string uuid = 2; + repeated string ips = 3; + } + } + ``` + + - Object.Location + + | field | type | label | desc. | + | :---: | :----- | :---------------------- | :-------------------------------------------------------------------- | + | name | string | | the name of vald agent pod where the request vector is updated. | + | uuid | string | | the ID of the updated vector. It is the same as an `Object.Vector`. | + | ips | string | repeated(Array[string]) | the IP list of `vald-agent` pods where the request vector is updated. | + +### Status Code + +| code | desc. | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 6 | ALREADY_EXISTS | +| 13 | INTERNAL | + +## Stream Update RPC + +StreamUpdate RPC is the method to update multiple objects using the [bidirectional streaming RPC](https://grpc.io/docs/what-is-grpc/core-concepts/#bidirectional-streaming-rpc).
+By using the bidirectional streaming RPC, the update request can be communicated in any order between client and server. +Each Update request and response are independent. +It's the recommended method to update the large amount of objects. + +```rpc +service Filter { + rpc StreamUpdateObject(stream payload.v1.Update.ObjectRequest) + returns (stream payload.v1.Object.StreamLocation) {} +} +``` + +### Input + +- the scheme of `payload.v1.Update.ObjectRequest stream` + + ```rpc + message Update { + message ObjectRequest { + Object.Blob object = 1; + Config config = 2; + Filter.Target vectorizer = 3; + } + + message Config { + bool skip_strict_exist_check = 1; + Filter.Config filters = 2; + int64 timestamp = 3; + } + } + + message Object { + message Blob { + string id = 1 [ (validate.rules).string.min_len = 1 ]; + bytes object = 2; + } + } + + message Filter { + message Target { + string host = 1; + uint32 port = 2; + } + + message Config { + repeated Target targets = 1; + } + } + ``` + + - Update.ObjectRequest + + | field | type | label | required | desc. | + | :--------: | :------------ | :---- | :------: | :-------------------------------------- | + | object | Object.Blob | | \* | the binary object to be updated | + | config | Config | | \* | the configuration of the update request | + | vectorizer | Filter.Target | | \* | filter target | + + - Object.Blob + + | field | type | label | required | desc. | + | :----: | :----- | :---- | :------: | :---------------- | + | id | string | | \* | the object ID | + | object | bytes | | \* | the binary object | + + - Update.Config + + | field | type | label | required | desc. | + | :---------------------: | :------------ | :---- | :------: | :-------------------------------------------------------------------------------------------------- | + | skip_strict_exist_check | bool | | | check the same vector is already updated or not.
the ID should be unique if the value is `true`. | + | timestamp | int64 | | | the timestamp of the vector updated.
if it is N/A, the current time will be used. | + | filters | Filter.Config | | | configuration for filter | + | disable_balanced_update | bool | | | A flag to disable balanced update (split remove -> insert operation) during update operation. | + + - Filter.Target + + | field | type | label | required | desc. | + | :---: | :----- | :---- | :------: | :------------------ | + | host | string | | \* | the target hostname | + | port | port | | \* | the target port | + + - Filter.Config + + | field | type | label | required | desc. | + | :-----: | :------------ | :----------------------------- | :------: | :------------------------------ | + | targets | Filter.Target | repeated(Array[Filter.Target]) | \* | the filter target configuration | + +### Output + +- the scheme of `payload.v1.Object.StreamLocation` + + ```rpc + message Object { + message StreamLocation { + oneof payload { + Location location = 1; + google.rpc.Status status = 2; + } + } + + message Location { + string name = 1; + string uuid = 2; + repeated string ips = 3; + } + } + ``` + + - Object.StreamLocation + + | field | type | label | desc. | + | :------: | :---------------- | :---- | :----------------------------------------- | + | location | Object.Location | | the information of `Object.Location` data. | + | status | google.rpc.Status | | the status of google RPC. | + + - Object.Location + + | field | type | label | desc. | + | :---: | :----- | :---------------------- | :-------------------------------------------------------------------- | + | name | string | | the name of vald agent pod where the request vector is updated . | + | uuid | string | | the ID of the updated vector. It is the same as an `Object.Vector`. | + | ips | string | repeated(Array[string]) | the IP list of `vald-agent` pods where the request vector is updated. | + + - [google.rpc.Status](https://github.com/googleapis/googleapis/blob/master/google/rpc/status.proto) + + | field | type | label | desc. | + | :-----: | :------------------ | :------------------- | :-------------------------------------- | + | code | int32 | | status code (code list is next section) | + | message | string | | error message | + | details | google.protobuf.Any | repeated(Array[any]) | the details error message list | + +### Status Code + +| code | desc. | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 6 | ALREADY_EXISTS | +| 13 | INTERNAL | + +## MultiUpdate RPC + +MultiUpdate is the method to update multiple objects in **1** request. + +
+gRPC has the message size limitation.
+Please be careful that the size of the request exceed the limit. +
+ +```rpc +service Filter { + rpc MultiUpdateObject(payload.v1.Update.MultiObjectRequest) + returns (payload.v1.Object.Locations) { + option (google.api.http) = { + post : "/update/object/multiple" + body : "*" + }; + } +} +``` + +### Input + +- the scheme of `payload.v1.Update.MultiObjectRequest` + + ```rpc + message Update { + message MultiObjectRequest { + repeated ObjectRequest requests = 1; + } + + message ObjectRequest { + Object.Blob object = 1; + Config config = 2; + Filter.Target vectorizer = 3; + } + + message Config { + bool skip_strict_exist_check = 1; + Filter.Config filters = 2; + int64 timestamp = 3; + } + } + + message Object { + message Blob { + string id = 1 [ (validate.rules).string.min_len = 1 ]; + bytes object = 2; + } + } + + message Filter { + message Target { + string host = 1; + uint32 port = 2; + } + + message Config { + repeated Target targets = 1; + } + } + ``` + + - Update.MultiObjectRequest + + | field | type | label | required | desc. | + | :------: | :------------------- | :------------------------------------ | :------: | :--------------- | + | requests | Insert.ObjectRequest | repeated(Array[Insert.ObjectRequest]) | \* | the request list | + + - Update.ObjectRequest + + | field | type | label | required | desc. | + | :--------: | :------------ | :---- | :------: | :-------------------------------------- | + | object | Object.Blob | | \* | the binary object to be updated | + | config | Config | | \* | the configuration of the update request | + | vectorizer | Filter.Target | | \* | filter target | + + - Object.Blob + + | field | type | label | required | desc. | + | :----: | :----- | :---- | :------: | :---------------- | + | id | string | | \* | the object ID | + | object | bytes | | \* | the binary object | + + - Update.Config + + | field | type | label | required | desc. | + | :---------------------: | :------------ | :---- | :------: | :-------------------------------------------------------------------------------------------------- | + | skip_strict_exist_check | bool | | | check the same vector is already updated or not.
the ID should be unique if the value is `true`. | + | timestamp | int64 | | | the timestamp of the vector updated.
if it is N/A, the current time will be used. | + | filters | Filter.Config | | | configuration for filter | + | disable_balanced_update | bool | | | A flag to disable balanced update (split remove -> insert operation) during update operation. | + + - Filter.Target + + | field | type | label | required | desc. | + | :---: | :----- | :---- | :------: | :------------------ | + | host | string | | \* | the target hostname | + | port | port | | \* | the target port | + + - Filter.Config + + | field | type | label | required | desc. | + | :-----: | :------------ | :----------------------------- | :------: | :------------------------------ | + | targets | Filter.Target | repeated(Array[Filter.Target]) | \* | the filter target configuration | + +### Output + +- the scheme of `payload.v1.Object.Locations` + + ```rpc + message Object { + message Locations { repeated Location locations = 1; } + + message Location { + string name = 1; + string uuid = 2; + repeated string ips = 3; + } + } + ``` + + - Object.Locations + + | field | type | label | desc. | + | :------: | :-------------- | :------------------------------- | :----------------------------- | + | location | Object.Location | repeated(Array[Object.Location]) | the list of `Object.Location`. | + + - Object.Location + + | field | type | label | desc. | + | :---: | :----- | :---------------------- | :-------------------------------------------------------------------- | + | name | string | | the name of vald agent pod where the request vector is updated. | + | uuid | string | | the ID of the updated vector. It is the same as an `Object.Vector`. | + | ips | string | repeated(Array[string]) | the IP list of `vald-agent` pods where the request vector is updated. | + +### Status Code + +| code | desc. | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 6 | ALREADY_EXISTS | +| 13 | INTERNAL | + +## Upsert RPC + +Upsert RPC is the method to update a single object and add a new single object. + +```rpc +service Filter { + rpc UpsertObject(payload.v1.Upsert.ObjectRequest) + returns (payload.v1.Object.Location) { + option (google.api.http) = { + post : "/upsert/object" + body : "*" + }; + } +} +``` + +### Input + +- the scheme of `payload.v1.Upsert.ObjectRequest` + + ```rpc + message Upsert { + message ObjectRequest { + Object.Blob object = 1; + Config config = 2; + Filter.Target vectorizer = 3; + } + + message Config { + bool skip_strict_exist_check = 1; + Filter.Config filters = 2; + int64 timestamp = 3; + } + } + + message Object { + message Blob { + string id = 1 [ (validate.rules).string.min_len = 1 ]; + bytes object = 2; + } + } + + message Filter { + message Target { + string host = 1; + uint32 port = 2; + } + + message Config { + repeated Target targets = 1; + } + } + ``` + + - Upsert.ObjectRequest + + | field | type | label | required | desc. | + | :--------: | :------------ | :---- | :------: | :-------------------------------------- | + | object | Object.Blob | | \* | the binary object to be upserted | + | config | Config | | \* | the configuration of the upsert request | + | vectorizer | Filter.Target | | \* | filter target | + + - Object.Blob + + | field | type | label | required | desc. | + | :----: | :----- | :---- | :------: | :---------------- | + | id | string | | \* | the object ID | + | object | bytes | | \* | the binary object | + + - Update.Config + + | field | type | label | required | desc. | + | :---------------------: | :------------ | :---- | :------: | :--------------------------------------------------------------------------------------------------- | + | skip_strict_exist_check | bool | | | check the same vector is already upserted or not.
the ID should be unique if the value is `true`. | + | timestamp | int64 | | | the timestamp of the vector upserted.
if it is N/A, the current time will be used. | + | filters | Filter.Config | | | configuration for filter | + | disable_balanced_update | bool | | | A flag to disable balanced update (split remove -> insert operation) during update operation. | + + - Filter.Target + + | field | type | label | required | desc. | + | :---: | :----- | :---- | :------: | :------------------ | + | host | string | | \* | the target hostname | + | port | port | | \* | the target port | + + - Filter.Config + + | field | type | label | required | desc. | + | :-----: | :------------ | :----------------------------- | :------: | :------------------------------ | + | targets | Filter.Target | repeated(Array[Filter.Target]) | \* | the filter target configuration | + +### Output + +- the scheme of `payload.v1.Object.Location` + + ```rpc + message Object { + message Location { + string name = 1; + string uuid = 2; + repeated string ips = 3; + } + } + ``` + + - Object.Location + + | field | type | label | desc. | + | :---: | :----- | :---------------------- | :--------------------------------------------------------------------- | + | name | string | | the name of vald agent pod where the request vector is upserted. | + | uuid | string | | the ID of the upserted vector. It is the same as an `Object.Vector`. | + | ips | string | repeated(Array[string]) | the IP list of `vald-agent` pods where the request vector is upserted. | + +### Status Code + +| code | desc. | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 6 | ALREADY_EXISTS | +| 13 | INTERNAL | + +## StreamUpsert RPC + +Upsert RPC is the method to update a single object and add a new single object. + +```rpc +service Filter { + rpc StreamUpsertObject(stream payload.v1.Upsert.ObjectRequest) + returns (stream payload.v1.Object.StreamLocation) {} +} +``` + +### Input + +- the scheme of `payload.v1.Upsert.ObjectRequest stream` + + ```rpc + message Upsert { + message ObjectRequest { + Object.Blob object = 1; + Config config = 2; + Filter.Target vectorizer = 3; + } + + message Config { + bool skip_strict_exist_check = 1; + Filter.Config filters = 2; + int64 timestamp = 3; + } + } + + message Object { + message Blob { + string id = 1 [ (validate.rules).string.min_len = 1 ]; + bytes object = 2; + } + } + + message Filter { + message Target { + string host = 1; + uint32 port = 2; + } + + message Config { + repeated Target targets = 1; + } + } + ``` + + - Upsert.ObjectRequest + + | field | type | label | required | desc. | + | :--------: | :------------ | :---- | :------: | :-------------------------------------- | + | object | Object.Blob | | \* | the binary object to be upserted | + | config | Config | | \* | the configuration of the upsert request | + | vectorizer | Filter.Target | | \* | filter target | + + - Object.Blob + + | field | type | label | required | desc. | + | :----: | :----- | :---- | :------: | :---------------- | + | id | string | | \* | the object ID | + | object | bytes | | \* | the binary object | + + - Update.Config + + | field | type | label | required | desc. | + | :---------------------: | :------------ | :---- | :------: | :--------------------------------------------------------------------------------------------------- | + | skip_strict_exist_check | bool | | | check the same vector is already upserted or not.
the ID should be unique if the value is `true`. | + | timestamp | int64 | | | the timestamp of the vector upserted.
if it is N/A, the current time will be used. | + | filters | Filter.Config | | | configuration for filter | + | disable_balanced_update | bool | | | A flag to disable balanced update (split remove -> insert operation) during update operation. | + + - Filter.Target + + | field | type | label | required | desc. | + | :---: | :----- | :---- | :------: | :------------------ | + | host | string | | \* | the target hostname | + | port | port | | \* | the target port | + + - Filter.Config + + | field | type | label | required | desc. | + | :-----: | :------------ | :----------------------------- | :------: | :------------------------------ | + | targets | Filter.Target | repeated(Array[Filter.Target]) | \* | the filter target configuration | + +### Output + +- the scheme of `payload.v1.Object.StreamLocation` + + ```rpc + message Object { + message StreamLocation { + oneof payload { + Location location = 1; + google.rpc.Status status = 2; + } + } + + message Location { + string name = 1; + string uuid = 2; + repeated string ips = 3; + } + } + ``` + + - Object.StreamLocation + + | field | type | label | desc. | + | :------: | :---------------- | :---- | :----------------------------------------- | + | location | Object.Location | | the information of `Object.Location` data. | + | status | google.rpc.Status | | the status of google RPC. | + + - Object.Location + + | field | type | label | desc. | + | :---: | :----- | :---------------------- | :--------------------------------------------------------------------- | + | name | string | | the name of vald agent pod where the request vector is upserted. | + | uuid | string | | the ID of the upserted vector. It is the same as an `Object.Vector`. | + | ips | string | repeated(Array[string]) | the IP list of `vald-agent` pods where the request vector is upserted. | + + - [google.rpc.Status](https://github.com/googleapis/googleapis/blob/master/google/rpc/status.proto) + + | field | type | label | desc. | + | :-----: | :------------------ | :------------------- | :-------------------------------------- | + | code | int32 | | status code (code list is next section) | + | message | string | | error message | + | details | google.protobuf.Any | repeated(Array[any]) | the details error message list | + +### Status Code + +| code | desc. | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 6 | ALREADY_EXISTS | +| 13 | INTERNAL | + +## MultiUpsert RPC + +MultiUpsert is the method to update existing multiple objects and add new multiple objects in **1** request. + +
+gRPC has a message size limitation.
+Please be careful that the size of the request exceeds the limit. +
+ +```rpc +service Filter { + rpc MultiUpsertObject(payload.v1.Upsert.MultiObjectRequest) + returns (payload.v1.Object.Locations) { + option (google.api.http) = { + post : "/upsert/object/multiple" + body : "*" + }; + } +} +``` + +### Input + +- the scheme of `payload.v1.Upsert.MultiObjectRequest` + + ```rpc + message Upsert { + message MultiObjectRequest { + repeated ObjectRequest requests = 1; + } + + message ObjectRequest { + Object.Blob object = 1; + Config config = 2; + Filter.Target vectorizer = 3; + } + + message Config { + bool skip_strict_exist_check = 1; + Filter.Config filters = 2; + int64 timestamp = 3; + } + } + + message Object { + message Blob { + string id = 1 [ (validate.rules).string.min_len = 1 ]; + bytes object = 2; + } + } + + message Filter { + message Target { + string host = 1; + uint32 port = 2; + } + + message Config { + repeated Target targets = 1; + } + } + ``` + + - Upsert.MultiObjectRequest + + | field | type | label | required | desc. | + | :------: | :------------------- | :------------------------------------ | :------: | :--------------- | + | requests | Upsert.ObjectRequest | repeated(Array[Upsert.ObjectRequest]) | \* | the request list | + + - Upsert.ObjectRequest + + | field | type | label | required | desc. | + | :--------: | :------------ | :---- | :------: | :-------------------------------------- | + | object | Object.Blob | | \* | the binary object to be upserted | + | config | Config | | \* | the configuration of the upsert request | + | vectorizer | Filter.Target | | \* | filter target | + + - Object.Blob + + | field | type | label | required | desc. | + | :----: | :----- | :---- | :------: | :---------------- | + | id | string | | \* | the object ID | + | object | bytes | | \* | the binary object | + + - Update.Config + + | field | type | label | required | desc. | + | :---------------------: | :------------ | :---- | :------: | :--------------------------------------------------------------------------------------------------- | + | skip_strict_exist_check | bool | | | check the same vector is already upserted or not.
the ID should be unique if the value is `true`. | + | timestamp | int64 | | | the timestamp of the vector upserted.
if it is N/A, the current time will be used. | + | filters | Filter.Config | | | configuration for filter | + | disable_balanced_update | bool | | | A flag to disable balanced update (split remove -> insert operation) during update operation. | + + - Filter.Target + + | field | type | label | required | desc. | + | :---: | :----- | :---- | :------: | :------------------ | + | host | string | | \* | the target hostname | + | port | port | | \* | the target port | + + - Filter.Config + + | field | type | label | required | desc. | + | :-----: | :------------ | :----------------------------- | :------: | :------------------------------ | + | targets | Filter.Target | repeated(Array[Filter.Target]) | \* | the filter target configuration | + +### Output + +- the scheme of `payload.v1.Object.Locations` + + ```rpc + message Object { + message Locations { repeated Location locations = 1; } + + message Location { + string name = 1; + string uuid = 2; + repeated string ips = 3; + } + } + ``` + + - Object.Locations + + | field | type | label | desc. | + | :------: | :-------------- | :------------------------------- | :----------------------------- | + | location | Object.Location | repeated(Array[Object.Location]) | the list of `Object.Location`. | + + - Object.Location + + | field | type | label | desc. | + | :---: | :----- | :---------------------- | :--------------------------------------------------------------------- | + | name | string | | the name of vald agent pod where the request vector is upserted. | + | uuid | string | | the ID of the upserted vector. It is the same as an `Object.Vector`. | + | ips | string | repeated(Array[string]) | the IP list of `vald-agent` pods where the request vector is upserted. | + +### Status Code + +| code | desc. | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 6 | ALREADY_EXISTS | +| 13 | INTERNAL | + +## Search RPC + +Search RPC is the method to search object(s) similar to request object. + +```rpc +service Filter { + rpc SearchObject(payload.v1.Search.ObjectRequest) + returns (payload.v1.Search.Response) { + option (google.api.http) = { + post : "/search/object" + body : "*" + }; + } +} +``` + +### Input + +- the scheme of `payload.v1.Search.ObjectRequest` + + ```rpc + message Search { + message ObjectRequest { + bytes object = 1; + Config config = 2; + Filter.Target vectorizer = 3; + } + + message Config { + string request_id = 1; + uint32 num = 2 [ (validate.rules).uint32.gte = 1 ]; + float radius = 3; + float epsilon = 4; + int64 timeout = 5; + Filter.Config ingress_filters = 6; + Filter.Config egress_filters = 7; + uint32 min_num = 8 [ (validate.rules).uint32.gte = 0 ]; + } + } + + message Filter { + message Target { + string host = 1; + uint32 port = 2; + } + + message Config { + repeated Target targets = 1; + } + } + ``` + + - Search.ObjectRequest + + | field | type | label | required | desc. | + | :--------: | :------------ | :---- | :------: | :-------------------------------------- | + | object | bytes | | \* | the binary object to be searched | + | config | Config | | \* | the configuration of the search request | + | vectorizer | Filter.Target | | \* | filter target | + + - Search.Config + + | field | type | label | required | desc. | + | :-------------: | :------------ | :---- | :------: | :---------------------------------------------------- | + | request_id | string | | | unique request ID | + | num | uint32 | | \* | the maximum number of result to be returned | + | radius | float | | \* | the search radius | + | epsilon | float | | \* | the search coefficient (default value is `0.1`) | + | timeout | int64 | | | Search timeout in nanoseconds (default value is `5s`) | + | ingress_filters | Filter.Config | | | Ingress Filter configuration | + | egress_filters | Filter.Config | | | Egress Filter configuration | + | min_num | uint32 | | | the minimum number of result to be returned | + +### Output + +- the scheme of `payload.v1.Search.Response`. + + ```rpc + message Search { + message Response { + string request_id = 1; + repeated Object.Distance results = 2; + } + } + + message Object { + message Distance { + string id = 1; + float distance = 2; + } + } + ``` + + - Search.Response + + | field | type | label | desc. | + | :--------: | :-------------- | :------------------------------- | :-------------------- | + | request_id | string | | the unique request ID | + | results | Object.Distance | repeated(Array[Object.Distance]) | search results | + + - Object.Distance + + | field | type | label | desc. | + | :------: | :----- | :---- | :---------------------------------------------------- | + | id | string | | the vector ID | + | distance | float | | the distance between result vector and request vector | + +### Status Code + +| code | desc. | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 5 | NOT_FOUND | +| 13 | INTERNAL | + +## StreamSearch RPC + +StreamSearch RPC is the method to search vectors with multi queries(objects) using the [bidirectional streaming RPC](https://grpc.io/docs/what-is-grpc/core-concepts/#bidirectional-streaming-rpc).
+By using the bidirectional streaming RPC, the search request can be communicated in any order between client and server. +Each Search request and response are independent. + +```rpc +service Filter { + rpc StreamSearchObject(stream payload.v1.Search.ObjectRequest) + returns (stream payload.v1.Search.StreamResponse) {} +} +``` + +### Input + +- the scheme of `payload.v1.Search.ObjectRequest stream` + + ```rpc + message Search { + message ObjectRequest { + bytes object = 1; + Config config = 2; + Filter.Target vectorizer = 3; + } + + message Config { + string request_id = 1; + uint32 num = 2 [ (validate.rules).uint32.gte = 1 ]; + float radius = 3; + float epsilon = 4; + int64 timeout = 5; + Filter.Config ingress_filters = 6; + Filter.Config egress_filters = 7; + uint32 min_num = 8 [ (validate.rules).uint32.gte = 0 ]; + } + } + + message Filter { + message Target { + string host = 1; + uint32 port = 2; + } + + message Config { + repeated Target targets = 1; + } + } + ``` + + - Search.ObjectRequest + + | field | type | label | required | desc. | + | :--------: | :------------ | :---- | :------: | :-------------------------------------- | + | object | bytes | | \* | the binary object to be searched | + | config | Config | | \* | the configuration of the search request | + | vectorizer | Filter.Target | | \* | filter target | + + - Search.Config + + | field | type | label | required | desc. | + | :-------------: | :------------ | :---- | :------: | :---------------------------------------------------- | + | request_id | string | | | unique request ID | + | num | uint32 | | \* | the maximum number of result to be returned | + | radius | float | | \* | the search radius | + | epsilon | float | | \* | the search coefficient (default value is `0.1`) | + | timeout | int64 | | | Search timeout in nanoseconds (default value is `5s`) | + | ingress_filters | Filter.Config | | | Ingress Filter configuration | + | egress_filters | Filter.Config | | | Egress Filter configuration | + | min_num | uint32 | | | the minimum number of result to be returned | + +### Output + +- the scheme of `payload.v1.Search.StreamResponse`. + + ```rpc + message Search { + message StreamResponse { + oneof payload { + Response response = 1; + google.rpc.Status status = 2; + } + } + + message Response { + string request_id = 1; + repeated Object.Distance results = 2; + } + } + + message Object { + message Distance { + string id = 1; + float distance = 2; + } + } + ``` + + - Search.StreamResponse + + | field | type | label | desc. | + | :------: | :---------------- | :---- | :------------------------- | + | response | Response | | the search result response | + | status | google.rpc.Status | | the status of google RPC | + + - Search.Response + + | field | type | label | desc. | + | :--------: | :-------------- | :------------------------------- | :-------------------- | + | request_id | string | | the unique request ID | + | results | Object.Distance | repeated(Array[Object.Distance]) | search results | + + - Object.Distance + + | field | type | label | desc. | + | :------: | :----- | :---- | :---------------------------------------------------- | + | id | string | | the vector ID | + | distance | float | | the distance between result vector and request vector | + +### Status Code + +| code | desc. | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 5 | NOT_FOUND | +| 13 | INTERNAL | + +## MultiSearch RPC + +MultiSearch RPC is the method to search objects with multiple objects in **1** request. + +
+gRPC has a message size limitation.
+Please be careful that the size of the request exceeds the limit. +
+ +```rpc +service Filter { + rpc MultiSearchObject(payload.v1.Search.MultiObjectRequest) + returns (payload.v1.Search.Responses) { + option (google.api.http) = { + post : "/search/object/multiple" + body : "*" + }; + } +} +``` + +### Input + +- the scheme of `payload.v1.Search.MultiObjectRequest` + + ```rpc + message Search { + message MultiObjectRequest { + repeated ObjectRequest requests = 1; + } + + message ObjectRequest { + bytes object = 1; + Config config = 2; + Filter.Target vectorizer = 3; + } + + message Config { + string request_id = 1; + uint32 num = 2 [ (validate.rules).uint32.gte = 1 ]; + float radius = 3; + float epsilon = 4; + int64 timeout = 5; + Filter.Config ingress_filters = 6; + Filter.Config egress_filters = 7; + uint32 min_num = 8 [ (validate.rules).uint32.gte = 0 ]; + } + } + + message Filter { + message Target { + string host = 1; + uint32 port = 2; + } + + message Config { + repeated Target targets = 1; + } + } + ``` + + - Search.MultiObjectRequest + + | field | type | label | required | desc. | + | :------: | :---------------------------------- | :---- | :------: | :---------------------- | + | requests | repeated(Array[MultiObjectRequest]) | | \* | the search request list | + + - Search.ObjectRequest + + | field | type | label | required | desc. | + | :--------: | :------------ | :---- | :------: | :-------------------------------------- | + | object | bytes | | \* | the binary object to be searched | + | config | Config | | \* | the configuration of the search request | + | vectorizer | Filter.Target | | \* | filter target | + + - Search.Config + + | field | type | label | required | desc. | + | :-------------: | :------------ | :---- | :------: | :---------------------------------------------------- | + | request_id | string | | | unique request ID | + | num | uint32 | | \* | the maximum number of result to be returned | + | radius | float | | \* | the search radius | + | epsilon | float | | \* | the search coefficient (default value is `0.1`) | + | timeout | int64 | | | Search timeout in nanoseconds (default value is `5s`) | + | ingress_filters | Filter.Config | | | Ingress Filter configuration | + | egress_filters | Filter.Config | | | Egress Filter configuration | + | min_num | uint32 | | | the minimum number of result to be returned | + +### Output + +- the scheme of `payload.v1.Search.Responses`. + + ```rpc + message Search { + message Responses { + repeated Response responses = 1; + } + + message Response { + string request_id = 1; + repeated Object.Distance results = 2; + } + } + + message Object { + message Distance { + string id = 1; + float distance = 2; + } + } + ``` + + - Search.Responses + + | field | type | label | desc. | + | :-------: | :------- | :------------------------ | :---------------------------------- | + | responses | Response | repeated(Array[Response]) | the list of search results response | + + - Search.Response + + | field | type | label | desc. | + | :--------: | :-------------- | :------------------------------- | :-------------------- | + | request_id | string | | the unique request ID | + | results | Object.Distance | repeated(Array[Object.Distance]) | search results | + + - Object.Distance + + | field | type | label | desc. | + | :------: | :----- | :---- | :---------------------------------------------------- | + | id | string | | the vector ID | + | distance | float | | the distance between result vector and request vector | + +### Status Code + +| code | desc. | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 5 | NOT_FOUND | +| 13 | INTERNAL | diff --git a/content/docs/v1.8/api/flush.md b/content/docs/v1.8/api/flush.md new file mode 100644 index 000000000..eb6877f3a --- /dev/null +++ b/content/docs/v1.8/api/flush.md @@ -0,0 +1,78 @@ +--- +title: "Flush_v1.8/Api" +date: 2026-09-16T04:40:20Z +draft: false +weight: 600 +description: Remove all indexes from the Vald cluster +menu: + api: + parent: Api +--- + +# Vald Flush APIs + +## Overview + +Flush Service is responsible for removing all vectors that are indexed and uncommitted in the `vald-agent`. + +```rpc +service Flush { + + rpc Flush(payload.v1.Flush.Request) returns (payload.v1.Info.Index.Count) {} + +} +``` + +## Flush RPC + +Flush RPC is the method to remove all vectors. + +### Input + +- the scheme of `payload.v1.Flush.Request` + + ```rpc + message Flush { + message Request { + } + } + ``` + + - Flush.Request + + empty + +### Output + +- the scheme of `payload.v1.Info.Index.Count` + + ```rpc + message Object { + message Info_Index_Count { + uint32 stored = 0; + uint32 uncommitted = 0; + bool indexing = false; + bool saving = false; + } + } + ``` + + Object.Info_Index_Count + + | field | type | label | desc. | + | :---------: | :----- | :---- | :------------------------------------------------------------------------- | + | stored | uint32 | | count of indices. | + | uncommitted | uint32 | | count of uncommitted indices. | + | indexing | bool | | the state indicating whether `vald-agent` pods is present in the indexing. | + | saving | bool | | the state indicating whether `vald-agent` pods is present in the saving. | + +### Status Code + +| code | desc. | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 5 | NOT_FOUND | +| 13 | INTERNAL | diff --git a/content/docs/v1.8/api/insert.md b/content/docs/v1.8/api/insert.md new file mode 100644 index 000000000..34c502f3c --- /dev/null +++ b/content/docs/v1.8/api/insert.md @@ -0,0 +1,377 @@ +--- +title: "Insert_v1.8/Api" +date: 2026-09-16T04:40:20Z +draft: false +weight: 100 +description: Insert vectors to the Vald cluster +menu: + api: + parent: Api +--- + +# Vald Insert APIs + +## Overview + +Insert Service is responsible for inserting new vectors into the `vald-agent`. + +```rpc +service Insert { + rpc Insert(payload.v1.Insert.Request) returns (payload.v1.Object.Location) {} + + rpc StreamInsert(stream payload.v1.Insert.Request) returns (stream payload.v1.Object.Location) {} + + rpc MultiInsert(payload.v1.Insert.MultiRequest) returns (payload.v1.Object.Locations) {} +} +``` + +## Insert RPC + +Insert RPC is the method to add a new single vector. + +### Input + +- the scheme of `payload.v1.Insert.Request` + + ```rpc + message Insert { + message Request { + Object.Vector vector = 1 [ (validate.rules).repeated .min_items = 2 ]; + Config config = 2; + } + + message Config { + bool skip_strict_exist_check = 1; + Filter.Config filters = 2; + int64 timestamp = 3; + } + } + + message Object { + message Vector { + string id = 1 [ (validate.rules).string.min_len = 1 ]; + repeated float vector = 2 [ (validate.rules).repeated .min_items = 2 ]; + } + } + ``` + + - Insert.Request + + | field | type | label | required | description | + | :----: | :------------ | :---- | :------: | :--------------------------------------- | + | vector | Object.Vector | | \* | The information of vector. | + | config | Config | | \* | The configuration of the insert request. | + + - Insert.Config + + | field | type | label | required | description | + | :---------------------: | :------------ | :---- | :------: | :------------------------------------------------------------------------------------------------------------ | + | skip_strict_exist_check | bool | | | Check whether the same vector is already inserted or not.
The ID should be unique if the value is `true`. | + | timestamp | int64 | | | The timestamp of the vector inserted.
If it is N/A, the current time will be used. | + | filters | Filter.Config | | | Configuration for filter. | + + - Object.Vector + + | field | type | label | required | description | + | :----: | :----- | :--------------------- | :------: | :------------------------------------------------------------- | + | id | string | | \* | The ID of a vector. ID should consist of 1 or more characters. | + | vector | float | repeated(Array[float]) | \* | The vector data. Its dimension is between 2 and 65,536. | + +### Output + +- the scheme of `payload.v1.Object.Location` + + ```rpc + message Object { + message Location { + string name = 1; + string uuid = 2; + repeated string ips = 3; + } + } + ``` + + - Object.Location + + | field | type | label | description | + | :---: | :----- | :---------------------- | :--------------------------------------------------------------------- | + | name | string | | The name of vald agent pod where the request vector is inserted. | + | uuid | string | | The ID of the inserted vector. It is the same as an `Object.Vector`. | + | ips | string | repeated(Array[string]) | The IP list of `vald-agent` pods where the request vector is inserted. | + +### Status Code + +| code | name | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 6 | ALREADY_EXISTS | +| 10 | ABORTED | +| 13 | INTERNAL | + +Please refer to [Response Status Code](/docs/v1.8/status) for more details. + +### Troubleshooting + +The request process may not be completed when the response code is NOT `0 (OK)`. + +Here are some common reasons and how to resolve each error. + +| name | common reason | how to resolve | +| :---------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------- | +| CANCELLED | Executed cancel() of rpc from client/server-side or network problems between client and server. | Check the code, especially around timeout and connection management, and fix if needed. | +| INVALID_ARGUMENT | The Dimension of the request vector is NOT the same as Vald Agent's config, the requested vector's ID is empty, or some request payload is invalid. | Check Agent config, request payload, and fix request payload or Agent config. | +| DEADLINE_EXCEEDED | The RPC timeout setting is too short on the client/server side. | Check the gRPC timeout setting on both the client and server sides and fix it if needed. | +| ALREADY_EXISTS | Request ID is already inserted. | Change request ID. | +| INTERNAL | Target Vald cluster or network route has some critical error. | Check target Vald cluster first and check network route including ingress as second. | + +## StreamInsert RPC + +StreamInsert RPC is the method to add new multiple vectors using the [bidirectional streaming RPC](https://grpc.io/docs/what-is-grpc/core-concepts/#bidirectional-streaming-rpc).
+Using the bidirectional streaming RPC, the insert request can be communicated in any order between client and server. +Each Insert request and response are independent. +It's the recommended method to insert a large number of vectors. + +### Input + +- the scheme of `payload.v1.Insert.Request stream` + + ```rpc + message Insert { + message Request { + Object.Vector vector = 1 [ (validate.rules).repeated .min_items = 2 ]; + Config config = 2; + } + message Config { + bool skip_strict_exist_check = 1; + Filter.Config filters = 2; + int64 timestamp = 3; + } + } + + message Object { + message Vector { + string id = 1 [ (validate.rules).string.min_len = 1 ]; + repeated float vector = 2 [ (validate.rules).repeated .min_items = 2 ]; + } + } + ``` + + - Insert.Request + + | field | type | label | required | description | + | :----: | :------------ | :---- | :------: | :--------------------------------------- | + | vector | Object.Vector | | \* | The information of vector. | + | config | Config | | \* | The configuration of the insert request. | + + - Insert.Config + + | field | type | label | required | description | + | :---------------------: | :------------ | :---- | :------: | :------------------------------------------------------------------------------------------------------------ | + | skip_strict_exist_check | bool | | | Check whether the same vector is already inserted or not.
The ID should be unique if the value is `true`. | + | timestamp | int64 | | | The timestamp of the vector inserted.
If it is N/A, the current time will be used. | + | filters | Filter.Config | | | Configuration for the filter targets. | + + - Object.Vector + + | field | type | label | required | description | + | :----: | :----- | :--------------------- | :------: | :--------------------------------------------------------------- | + | id | string | | \* | The ID of the vector. ID should consist of 1 or more characters. | + | vector | float | repeated(Array[float]) | \* | The vector data. Its dimension is between 2 and 65,536. | + +### Output + +- the scheme of `payload.v1.Object.StreamLocation` + + ```rpc + message Object { + message StreamLocation { + oneof payload { + Location location = 1; + google.rpc.Status status = 2; + } + } + + message Location { + string name = 1; + string uuid = 2; + repeated string ips = 3; + } + } + ``` + + - Object.StreamLocation + + | field | type | label | description | + | :------: | :---------------- | :---- | :----------------------------------------- | + | location | Object.Location | | The information of `Object.Location` data. | + | status | google.rpc.Status | | The status of Google RPC. | + + - Object.Location + + | field | type | label | description | + | :---: | :----- | :---------------------- | :--------------------------------------------------------------------- | + | name | string | | The name of vald agent pod where the request vector is inserted. | + | uuid | string | | The ID of the inserted vector. It is the same as an `Object.Vector`. | + | ips | string | repeated(Array[string]) | The IP list of `vald-agent` pods where the request vector is inserted. | + + - [google.rpc.Status](https://github.com/googleapis/googleapis/blob/master/google/rpc/status.proto) + + | field | type | label | description | + | :-----: | :------------------ | :------------------- | :-------------------------------------- | + | code | int32 | | Status code (code list is next section) | + | message | string | | Error message | + | details | google.protobuf.Any | repeated(Array[any]) | The details error message list | + +### Status Code + +| code | name | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 6 | ALREADY_EXISTS | +| 10 | ABORTED | +| 13 | INTERNAL | + +Please refer to [Response Status Code](/docs/v1.8/status) for more details. + +### Troubleshooting + +The request process may not be completed when the response code is NOT `0 (OK)`. + +Here are some common reasons and how to resolve each error. + +| name | common reason | how to resolve | +| :---------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------- | +| CANCELLED | Executed cancel() of rpc from client/server-side or network problems between client and server. | Check the code, especially around timeout and connection management, and fix if needed. | +| INVALID_ARGUMENT | The Dimension of the request vector is NOT the same as Vald Agent's config, the requested vector's ID is empty, or some request payload is invalid. | Check Agent config, request payload, and fix request payload or Agent config. | +| DEADLINE_EXCEEDED | The RPC timeout setting is too short on the client/server side. | Check the gRPC timeout setting on both the client and server sides and fix it if needed. | +| ALREADY_EXISTS | Request ID is already inserted. | Change request ID. | +| INTERNAL | Target Vald cluster or network route has some critical error. | Check target Vald cluster first and check network route including ingress as second. | + +## MultiInsert RPC + +MultiInsert RPC is the method to add multiple new vectors in **1** request. + +
+gRPC has a message size limitation.
+Please be careful that the size of the request exceeds the limit. +
+ +### Input + +- the scheme of `payload.v1.Insert.MultiRequest` + + ```rpc + message Insert { + message MultiRequest { repeated Request requests = 1; } + + message Request { + Object.Vector vector = 1 [ (validate.rules).repeated .min_items = 2 ]; + Config config = 2; + } + + message Config { + bool skip_strict_exist_check = 1; + Filter.Config filters = 2; + int64 timestamp = 3; + } + } + + message Object { + message Vector { + string id = 1 [ (validate.rules).string.min_len = 1 ]; + repeated float vector = 2 [ (validate.rules).repeated .min_items = 2 ]; + } + } + ``` + + - Insert.MultiRequest + + | field | type | label | required | description | + | :------: | :------------- | :------------------------------ | :------: | :---------------- | + | requests | Insert.Request | repeated(Array[Insert.Request]) | \* | The request list. | + + - Insert.Request + + | field | type | label | required | description | + | :----: | :------------ | :---- | :------: | :--------------------------------------- | + | vector | Object.Vector | | \* | The information of vector. | + | config | Config | | \* | The configuration of the insert request. | + + - Insert.Config + + | field | type | label | required | description | + | :---------------------: | :------------ | :---- | :------: | :------------------------------------------------------------------------------------------------------------ | + | skip_strict_exist_check | bool | | | Check whether the same vector is already inserted or not.
The ID should be unique if the value is `true`. | + | timestamp | int64 | | | The timestamp of the vector inserted.
If it is N/A, the current time will be used. | + | filters | Filter.Config | | | Configuration for the filter targets. | + + - Object.Vector + + | field | type | label | required | description | + | :----: | :----- | :--------------------- | :------: | :------------------------------------------------------------- | + | id | string | | \* | The ID of a vector. ID should consist of 1 or more characters. | + | vector | float | repeated(Array[float]) | \* | The vector data. Its dimension is between 2 and 65,536. | + +### Output + +- the scheme of `payload.v1.Object.Locations`. + + ```rpc + message Object { + message Locations { repeated Location locations = 1; } + + message Location { + string name = 1; + string uuid = 2; + repeated string ips = 3; + } + } + ``` + + - Object.Locations + + | field | type | label | description | + | :------: | :-------------- | :------------------------------- | :----------------------------- | + | location | Object.Location | repeated(Array[Object.Location]) | The list of `Object.Location`. | + + - Object.Location + + | field | type | label | description | + | :---: | :----- | :---------------------- | :--------------------------------------------------------------------- | + | name | string | | The name of vald agent pod where the request vector is inserted. | + | uuid | string | | The ID of the inserted vector. It is the same as an `Object.Vector`. | + | ips | string | repeated(Array[string]) | The IP list of `vald-agent` pods where the request vector is inserted. | + +### Status Code + +| code | name | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 6 | ALREADY_EXISTS | +| 10 | ABORTED | +| 13 | INTERNAL | + +Please refer to [Response Status Code](/docs/v1.8/status) for more details. + +### Troubleshooting + +The request process may not be completed when the response code is NOT `0 (OK)`. + +Here are some common reasons and how to resolve each error. + +| name | common reason | how to resolve | +| :---------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------- | +| CANCELLED | Executed cancel() of rpc from client/server-side or network problems between client and server. | Check the code, especially around timeout and connection management, and fix if needed. | +| INVALID_ARGUMENT | The Dimension of the request vector is NOT the same as Vald Agent's config, the requested vector's ID is empty, or some request payload is invalid. | Check Agent config, request payload, and fix request payload or Agent config. | +| DEADLINE_EXCEEDED | The RPC timeout setting is too short on the client/server side. | Check the gRPC timeout setting on both the client and server sides and fix it if needed. | +| ALREADY_EXISTS | Request ID is already inserted. | Change request ID. | +| INTERNAL | Target Vald cluster or network route has some critical error. | Check target Vald cluster first and check network route including ingress as second. | diff --git a/content/docs/v1.8/api/mirror-gateway.md b/content/docs/v1.8/api/mirror-gateway.md new file mode 100644 index 000000000..e787808de --- /dev/null +++ b/content/docs/v1.8/api/mirror-gateway.md @@ -0,0 +1,96 @@ +--- +title: "Mirror Gateway_v1.8/Api" +date: 2026-09-16T04:40:20Z +draft: false +weight: 900 +description: API description when operating Vald Mirror Gateway +menu: + api: + parent: Api +--- + +# Vald Mirror Gateway APIs + +## Overview + +Mirror Service is responsible for providing the `Register` interface for the Vald Mirror Gateway. + +```rpc +service Mirror { + rpc Register(payload.v1.Mirror.Targets) returns (payload.v1.Mirror.Targets) {} +} +``` + +## Register RPC + +Register RPC is the method to register other Vald Mirror Gateway targets. + +### Input + +- the scheme of `payload.v1.Mirror.Targets`. + + ```rpc + message Mirror { + message Target { + string host = 1; + uint32 port = 2; + } + + message Targets { + repeated Target targets = 1; + } + } + ``` + + - Mirror.Targets + + | field | type | label | required | desc. | + | :-----: | :------------ | :----------------------------- | :------: | :------------------------------- | + | targets | Mirror.Target | repeated(Array[Mirror.Target]) | \* | The multiple target information. | + + - Mirror.Target + + | field | type | label | required | desc. | + | :---: | :----- | :---- | :------: | :------------------- | + | host | string | | \* | The target hostname. | + | port | uint32 | | \* | The target port. | + +### Output + +- the scheme of `payload.v1.Mirror.Targets`. + + ```rpc + message Mirror { + message Target { + string host = 1; + uint32 port = 2; + } + + message Targets { + repeated Target targets = 1; + } + } + ``` + + - Mirror.Targets + + | field | type | label | required | desc. | + | :-----: | :------------ | :----------------------------- | :------: | :------------------------------- | + | targets | Mirror.Target | repeated(Array[Mirror.Target]) | | The multiple target information. | + + - Mirror.Target + + | field | type | label | required | desc. | + | :---: | :----- | :---- | :------: | :------------------- | + | host | string | | \* | The target hostname. | + | port | uint32 | | \* | The target port. | + +### Status Code + +| code | desc. | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 13 | INTERNAL | diff --git a/content/docs/v1.8/api/object.md b/content/docs/v1.8/api/object.md new file mode 100644 index 000000000..91440d8d0 --- /dev/null +++ b/content/docs/v1.8/api/object.md @@ -0,0 +1,270 @@ +--- +title: "Object_v1.8/Api" +date: 2026-09-16T04:40:20Z +draft: false +weight: 700 +description: Get object information in the Vald cluster +menu: + api: + parent: Api +--- + +# Vald Object APIs + +## Overview + +Object Service is responsible for getting inserted vectors and checking whether vectors are inserted into the `vald-agent`. + +```rpc +service Object { + rpc Exists(payload.v1.Object.ID) returns (payload.v1.Object.ID) {} + + rpc GetObject(payload.v1.Object.VectorRequest) + returns (payload.v1.Object.Vector) {} + + rpc StreamGetObject(stream payload.v1.Object.VectorRequest) + returns (stream payload.v1.Object.StreamVector) {} +} +``` + +## Exists RPC + +Exists RPC is the method to check that a vector exists in the `vald-agent`. + +### Input + +- the scheme of `payload.v1.Object.ID` + + ```rpc + message Object { + message ID { + string id = 1 [ (validate.rules).string.min_len = 1 ]; + } + } + ``` + + - Object.ID + + | field | type | label | required | description | + | :---: | :----- | :---- | :------: | :------------------------------------------------------------- | + | id | string | | \* | The ID of a vector. ID should consist of 1 or more characters. | + +### Output + +- the scheme of `payload.v1.Object.ID` + + ```rpc + message Object { + message ID { + string id = 1 [ (validate.rules).string.min_len = 1 ]; + } + } + ``` + + - Object.ID + + | field | type | label | description | + | :---: | :----- | :---- | :------------------------------------------------------------- | + | id | string | | The ID of a vector. ID should consist of 1 or more characters. | + +### Status Code + +| code | name | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 5 | NOT_FOUND | +| 13 | INTERNAL | + +Please refer to [Response Status Code](/docs/v1.8/status) for more details. + +### Troubleshooting + +The request process may not be completed when the response code is NOT `0 (OK)`. + +Here are some common reasons and how to resolve each error. + +| name | common reason | how to resolve | +| :---------------- | :---------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------- | +| CANCELLED | Executed cancel() of rpc from client/server-side or network problems between client and server. | Check the code, especially around timeout and connection management, and fix if needed. | +| INVALID_ARGUMENT | The Requested vector's ID is empty, or some request payload is invalid. | Check request payload and fix request payload. | +| DEADLINE_EXCEEDED | The RPC timeout setting is too short on the client/server side. | Check the gRPC timeout setting on both the client and server sides and fix it if needed. | +| NOT_FOUND | Requested ID is NOT inserted. | Send a request with an ID that is already inserted. | +| INTERNAL | Target Vald cluster or network route has some critical error. | Check target Vald cluster first and check network route including ingress as second. | + +## GetObject RPC + +GetObject RPC is the method to get the metadata of a vector inserted into the `vald-agent`. + +### Input + +- the scheme of `payload.v1.Object.VectorRequest` + + ```rpc + message Object { + message VectorRequest { + ID id = 1 [ (validate.rules).repeated .min_items = 2 ]; + Filter.Config filters = 2; + } + + message ID { + string id = 1 [ (validate.rules).string.min_len = 1 ]; + } + } + ``` + + - Object.VectorRequest + + | field | type | label | required | description | + | :-----: | :------------ | :---- | :------: | :------------------------------------------------------------- | + | id | Object.ID | | \* | The ID of a vector. ID should consist of 1 or more characters. | + | filters | Filter.Config | | | Configuration for filter. | + + - Object.ID + + | field | type | label | required | description | + | :---: | :----- | :---- | :------: | :------------------------------------------------------------- | + | id | string | | \* | The ID of a vector. ID should consist of 1 or more characters. | + +### Output + +- the scheme of `payload.v1.Object.Vector` + + ```rpc + message Object { + message Vector { + string id = 1 [ (validate.rules).string.min_len = 1 ]; + repeated float vector = 2 [ (validate.rules).repeated .min_items = 2 ]; + } + } + ``` + + - Object.Vector + + | field | type | label | description | + | :----: | :----- | :--------------------- | :------------------------------------------------------------- | + | id | string | | The ID of a vector. ID should consist of 1 or more characters. | + | vector | float | repeated(Array[float]) | The vector data. Its dimension is between 2 and 65,536. | + +### Status Code + +| code | name | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 5 | NOT_FOUND | +| 13 | INTERNAL | + +Please refer to [Response Status Code](/docs/v1.8/status) for more details. + +### Troubleshooting + +The request process may not be completed when the response code is NOT `0 (OK)`. + +Here are some common reasons and how to resolve each error. + +| name | common reason | how to resolve | +| :---------------- | :---------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------- | +| CANCELLED | Executed cancel() of rpc from client/server-side or network problems between client and server. | Check the code, especially around timeout and connection management, and fix if needed. | +| INVALID_ARGUMENT | The Requested vector's ID is empty, or some request payload is invalid. | Check request payload and fix request payload. | +| DEADLINE_EXCEEDED | The RPC timeout setting is too short on the client/server side. | Check the gRPC timeout setting on both the client and server sides and fix it if needed. | +| NOT_FOUND | Requested ID is NOT inserted. | Send a request with an ID that is already inserted. | +| INTERNAL | Target Vald cluster or network route has some critical error. | Check target Vald cluster first and check network route including ingress as second. | + +## StreamGetObject RPC + +StreamGetObject RPC is the method to get the metadata of multiple existing vectors using the [bidirectional streaming RPC](https://grpc.io/docs/what-is-grpc/core-concepts/#bidirectional-streaming-rpc).
+Using the bidirectional streaming RPC, the GetObject request can be communicated in any order between client and server. +Each Upsert request and response are independent. + +- the scheme of `payload.v1.Object.VectorRequest stream` + + ```rpc + message Object { + message VectorRequest { + ID id = 1 [ (validate.rules).repeated .min_items = 2 ]; + Filter.Config filters = 2; + } + + message ID { + string id = 1 [ (validate.rules).string.min_len = 1 ]; + } + } + ``` + + - Object.VectorRequest + + | field | type | label | required | description | + | :-----: | :------------ | :---- | :------: | :------------------------------------------------------------- | + | id | Object.ID | | \* | The ID of a vector. ID should consist of 1 or more characters. | + | filters | Filter.Config | | | Configuration for the filter targets. | + + - Object.ID + + | field | type | label | required | description | + | :---: | :----- | :---- | :------: | :------------------------------------------------------------- | + | id | string | | \* | The ID of a vector. ID should consist of 1 or more characters. | + +### Output + +- the scheme of `payload.v1.Object.StreamVector` + + ```rpc + message Object { + message StreamVector { + oneof payload { + Vector vector = 1; + google.rpc.Status status = 2; + } + } + message Vector { + string id = 1 [ (validate.rules).string.min_len = 1 ]; + repeated float vector = 2 [ (validate.rules).repeated .min_items = 2 ]; + } + } + ``` + + - Object.StreamVector + + | field | type | label | description | + | :----: | :---------------- | :---- | :------------------------------------- | + | vector | Vector | | The information of Object.Vector data. | + | status | google.rpc.Status | | The status of Google RPC. | + + - Object.Vector + + | field | type | label | description | + | :----: | :----- | :--------------------- | :------------------------------------------------------------- | + | id | string | | The ID of a vector. ID should consist of 1 or more characters. | + | vector | float | repeated(Array[float]) | The vector data. Its dimension is between 2 and 65,536. | + +### Status Code + +| code | name | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 5 | NOT_FOUND | +| 13 | INTERNAL | + +Please refer to [Response Status Code](/docs/v1.8/status) for more details. + +### Troubleshooting + +The request process may not be completed when the response code is NOT `0 (OK)`. + +Here are some common reasons and how to resolve each error. + +| name | common reason | how to resolve | +| :---------------- | :---------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------- | +| CANCELLED | Executed cancel() of rpc from client/server-side or network problems between client and server. | Check the code, especially around timeout and connection management, and fix if needed. | +| INVALID_ARGUMENT | The Requested vector's ID is empty, or some request payload is invalid. | Check request payload and fix request payload. | +| DEADLINE_EXCEEDED | The RPC timeout setting is too short on the client/server side. | Check the gRPC timeout setting on both the client and server sides and fix it if needed. | +| NOT_FOUND | Requested ID is NOT inserted. | Send a request with an ID that is already inserted. | +| INTERNAL | Target Vald cluster or network route has some critical error. | Check target Vald cluster first and check network route including ingress as second. | diff --git a/content/docs/v1.8/api/remove.md b/content/docs/v1.8/api/remove.md new file mode 100644 index 000000000..b6c7f1650 --- /dev/null +++ b/content/docs/v1.8/api/remove.md @@ -0,0 +1,493 @@ +--- +title: "Remove_v1.8/Api" +date: 2026-09-16T04:40:20Z +draft: false +weight: 500 +description: Remove indexes from the Vald cluster +menu: + api: + parent: Api +--- + +# Vald Remove APIs + +## Overview + +Remove Service is responsible for removing vectors indexed in the `vald-agent`. + +```rpc +service Remove { + + rpc Remove(payload.v1.Remove.Request) returns (payload.v1.Object.Location) {} + + rpc RemoveByTimestamp(payload.v1.Remove.TimestampRequest) returns (payload.v1.Object.Locations) {} + + rpc StreamRemove(stream payload.v1.Remove.Request) + returns (stream payload.v1.Object.StreamLocation) {} + + rpc MultiRemove(payload.v1.Remove.MultiRequest) + returns (payload.v1.Object.Locations) {} +} +``` + +## Remove RPC + +Remove RPC is the method to remove a single vector. + +### Input + +- the scheme of `payload.v1.Remove.Request` + + ```rpc + message Remove { + message Request { + Object.ID id = 1; + Config config = 2; + } + + message Config { + bool skip_strict_exist_check = 1; + int64 timestamp = 3; + } + } + + message Object { + message ID { + string id = 1 [ (validate.rules).string.min_len = 1 ]; + } + } + ``` + + - Remove.Request + + | field | type | label | required | description | + | :----: | :-------- | :---- | :------: | :--------------------------------------- | + | id | Object.ID | | \* | The ID of vector. | + | config | Config | | \* | The configuration of the remove request. | + + - Remove.Config + + | field | type | label | required | description | + | :---------------------: | :---- | :---- | :------: | :----------------------------------------------------------------------------------------------------------- | + | skip_strict_exist_check | bool | | | Check whether the same vector is already inserted or not.
The ID should be unique if the value is `true`. | + | timestamp | int64 | | | The timestamp of the vector removed.
If it is N/A, the current time will be used. | + + - Object.ID + + | field | type | label | required | description | + | :---: | :----- | :---- | :------: | :------------------------------------------------------------- | + | id | string | | \* | The ID of a vector. ID should consist of 1 or more characters. | + +### Output + +- the scheme of `payload.v1.Object.Location` + + ```rpc + message Object { + message Location { + string name = 1; + string uuid = 2; + repeated string ips = 3; + } + } + ``` + + - Object.Location + + | field | type | label | description | + | :---: | :----- | :---------------------- | :-------------------------------------------------------------------- | + | name | string | | The name of vald agent pod where the request vector is removed. | + | uuid | string | | The ID of the removed vector. It is the same as an `Object.ID`. | + | ips | string | repeated(Array[string]) | The IP list of `vald-agent` pods where the request vector is removed. | + +### Status Code + +| code | name | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 5 | NOT_FOUND | +| 10 | ABORTED | +| 13 | INTERNAL | + +Please refer to [Response Status Code](/docs/v1.8/status) for more details. + +### Troubleshooting + +The request process may not be completed when the response code is NOT `0 (OK)`. + +Here are some common reasons and how to resolve each error. + +| name | common reason | how to resolve | +| :---------------- | :---------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------- | +| CANCELLED | Executed cancel() of rpc from client/server-side or network problems between client and server. | Check the code, especially around timeout and connection management, and fix if needed. | +| INVALID_ARGUMENT | The Requested vector's ID is empty, or some request payload is invalid. | Check request payload and fix request payload. | +| DEADLINE_EXCEEDED | The RPC timeout setting is too short on the client/server side. | Check the gRPC timeout setting on both the client and server sides and fix it if needed. | +| NOT_FOUND | Requested ID is NOT inserted. | Send a request with an ID that is already inserted. | +| INTERNAL | Target Vald cluster or network route has some critical error. | Check target Vald cluster first and check network route including ingress as second. | + +## RemoveByTimestamp RPC + +RemoveByTimestamp RPC is the method to remove vectors based on timestamp. + +### Input + +- the scheme of `payload.v1.Remove.TimestampRequest` + + ```rpc + message Remove { + message TimestampRequest { + repeated Timestamp timestamps = 1; + } + + message Timestamp { + enum Operator { + Eq = 0; + Ne = 1; + Ge = 2; + Gt = 3; + Le = 4; + Lt = 5; + } + int64 timestamp = 1; + Operator operator = 2; + } + } + + message Object { + message ID { + string id = 1 [ (validate.rules).string.min_len = 1 ]; + } + } + ``` + + - Remove.TimestampRequest + + | field | type | label | required | description | + | :--------: | :--------------- | :-------------------------------- | :------: | :-------------------------------------------------------------------------------------------- | + | timestamps | Remove.Timestamp | repeated(Array[Remove.Timestamp]) | \* | The timestamp comparison list.
If more than one is specified, the `AND` search is applied. | + + - Remove.Timestamp + + | field | type | label | required | description | + | :-------: | :------------------------ | :---- | :------: | :------------------------------------------------- | + | timestamp | int64 | | \* | The timestamp. | + | operator | Remove.Timestamp.Operator | | | The conditional operator. (default value is `Eq`). | + + - Remove.Timestamp.Operator + + | value | description | + | :---: | :--------------------- | + | Eq | Equal. | + | Ne | Not Equal. | + | Ge | Greater than or Equal. | + | Gt | Greater than. | + | Le | Less than or Equal. | + | Lt | Less than. | + +
+ In the TimestampRequest message, the 'timestamps' field is repeated, allowing the inclusion of multiple Timestamp.
+ When multiple Timestamps are provided, it results in an `AND` condition, enabling the realization of deletions with specified ranges.
+ This design allows for versatile deletion operations, facilitating tasks such as removing data within a specific time range. +
+ +### Output + +- the scheme of `payload.v1.Object.Locations`. + + ```rpc + message Object { + message Locations { repeated Location locations = 1; } + + message Location { + string name = 1; + string uuid = 2; + repeated string ips = 3; + } + } + ``` + + - Object.Locations + + | field | type | label | description | + | :------: | :-------------- | :------------------------------- | :----------------------------- | + | location | Object.Location | repeated(Array[Object.Location]) | The list of `Object.Location`. | + + - Object.Location + + | field | type | label | description | + | :---: | :----- | :---------------------- | :-------------------------------------------------------------------- | + | name | string | | The name of vald agent pod where the request vector is removed. | + | uuid | string | | The ID of the removed vector. It is the same as an `Object.ID`. | + | ips | string | repeated(Array[string]) | The IP list of `vald-agent` pods where the request vector is removed. | + +### Status Code + +| code | name | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 4 | DEADLINE_EXCEEDED | +| 5 | NOT_FOUND | +| 13 | INTERNAL | + +Please refer to [Response Status Code](/docs/v1.8/status) for more details. + +### Troubleshooting + +The request process may not be completed when the response code is NOT `0 (OK)`. + +Here are some common reasons and how to resolve each error. + +| name | common reason | how to resolve | +| :---------------- | :---------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------- | +| CANCELLED | Executed cancel() of rpc from client/server-side or network problems between client and server. | Check the code, especially around timeout and connection management, and fix if needed. | +| DEADLINE_EXCEEDED | The RPC timeout setting is too short on the client/server side. | Check the gRPC timeout setting on both the client and server sides and fix it if needed. | +| NOT_FOUND | No vectors in the system match the specified timestamp conditions. | Check whether vectors matching the specified timestamp conditions exist in the system, and fix conditions if needed. | +| INTERNAL | Target Vald cluster or network route has some critical error. | Check target Vald cluster first and check network route including ingress as second. | + +## StreamRemove RPC + +StreamRemove RPC is the method to remove multiple vectors using the [bidirectional streaming RPC](https://grpc.io/docs/what-is-grpc/core-concepts/#bidirectional-streaming-rpc).
+Using the bidirectional streaming RPC, the remove request can be communicated in any order between client and server. +Each Remove request and response are independent. +It's the recommended method to remove a large number of vectors. + +### Input + +- the scheme of `payload.v1.Remove.Request stream` + + ```rpc + message Remove { + message Request { + Object.ID id = 1; + Config config = 2; + } + + message Config { + bool skip_strict_exist_check = 1; + int64 timestamp = 3; + } + } + + message Object { + message ID { + string id = 1 [ (validate.rules).string.min_len = 1 ]; + } + } + ``` + + - Remove.Request + + | field | type | label | required | description | + | :----: | :-------- | :---- | :------: | :--------------------------------------- | + | id | Object.ID | | \* | The ID of vector. | + | config | Config | | \* | The configuration of the insert request. | + + - Remove.Config + + | field | type | label | required | description | + | :---------------------: | :---- | :---- | :------: | :----------------------------------------------------------------------------------------------------------- | + | skip_strict_exist_check | bool | | | Check whether the same vector is already inserted or not.
The ID should be unique if the value is `true`. | + | timestamp | int64 | | | The timestamp of the vector removed.
If it is N/A, the current time will be used. | + + - Object.ID + + | field | type | label | required | description | + | :---: | :----- | :---- | :------: | :------------------------------------------------------------- | + | id | string | | \* | The ID of a vector. ID should consist of 1 or more characters. | + +### Output + +- the scheme of `payload.v1.Object.StreamLocation` + + ```rpc + message Object { + message StreamLocation { + oneof payload { + Location location = 1; + google.rpc.Status status = 2; + } + } + + message Location { + string name = 1; + string uuid = 2; + repeated string ips = 3; + } + } + ``` + + - Object.StreamLocation + + | field | type | label | description | + | :------: | :---------------- | :---- | :----------------------------------------- | + | location | Object.Location | | The information of `Object.Location` data. | + | status | google.rpc.Status | | The status of Google RPC | + + - Object.Location + + | field | type | label | description | + | :---: | :----- | :---------------------- | :-------------------------------------------------------------------- | + | name | string | | The name of vald agent pod where the request vector is removed. | + | uuid | string | | The ID of the removed vector. It is the same as an `Object.ID`. | + | ips | string | repeated(Array[string]) | The IP list of `vald-agent` pods where the request vector is removed. | + + - [google.rpc.Status](https://github.com/googleapis/googleapis/blob/master/google/rpc/status.proto) + + | field | type | label | description | + | :-----: | :------------------ | :------------------- | :-------------------------------------- | + | code | int32 | | Status code (code list is next section) | + | message | string | | Error message | + | details | google.protobuf.Any | repeated(Array[any]) | The details error message list | + +### Status Code + +| code | name | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 5 | NOT_FOUND | +| 10 | ABORTED | +| 13 | INTERNAL | + +Please refer to [Response Status Code](/docs/v1.8/status) for more details. + +### Troubleshooting + +The request process may not be completed when the response code is NOT `0 (OK)`. + +Here are some common reasons and how to resolve each error. + +| name | common reason | how to resolve | +| :---------------- | :---------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------- | +| CANCELLED | Executed cancel() of rpc from client/server-side or network problems between client and server. | Check the code, especially around timeout and connection management, and fix if needed. | +| INVALID_ARGUMENT | The Requested vector's ID is empty, or some request payload is invalid. | Check request payload and fix request payload. | +| DEADLINE_EXCEEDED | The RPC timeout setting is too short on the client/server side. | Check the gRPC timeout setting on both the client and server sides and fix it if needed. | +| NOT_FOUND | Requested ID is NOT inserted. | Send a request with an ID that is already inserted. | +| INTERNAL | Target Vald cluster or network route has some critical error. | Check target Vald cluster first and check network route including ingress as second. | + +## MultiRemove RPC + +MultiRemove is the method to remove multiple vectors in **1** request. + +
+gRPC has a message size limitation.
+Please be careful that the size of the request exceeds the limit. +
+ +### Input + +- the scheme of `payload.v1.Remove.MultiRequest` + + ```rpc + message Remove { + message MultiRequest { + repeated Request requests = 1; + } + + message Request { + Object.ID id = 1; + Config config = 2; + } + + message Config { + bool skip_strict_exist_check = 1; + int64 timestamp = 3; + } + } + + message Object { + message ID { + string id = 1 [ (validate.rules).string.min_len = 1 ]; + } + } + ``` + + - Remove.MultiRequest + + | field | type | label | required | description | + | :------: | :------------- | :------------------------------ | :------: | :--------------- | + | requests | Remove.Request | repeated(Array[Insert.Request]) | \* | the request list | + + - Remove.Request + + | field | type | label | required | description | + | :----: | :-------- | :---- | :------: | :--------------------------------------- | + | id | Object.ID | | \* | The ID of vector. | + | config | Config | | \* | The configuration of the remove request. | + + - Remove.Config + + | field | type | label | required | description | + | :---------------------: | :---- | :---- | :------: | :----------------------------------------------------------------------------------------------------------- | + | skip_strict_exist_check | bool | | | Check whether the same vector is already inserted or not.
The ID should be unique if the value is `true`. | + | timestamp | int64 | | | The timestamp of the vector removed.
If it is N/A, the current time will be used. | + + - Object.ID + + | field | type | label | required | description | + | :---: | :----- | :---- | :------: | :------------------------------------------------------------- | + | id | string | | \* | The ID of a vector. ID should consist of 1 or more characters. | + +### Output + +- the scheme of `payload.v1.Object.Locations`. + + ```rpc + message Object { + message Locations { repeated Location locations = 1; } + + message Location { + string name = 1; + string uuid = 2; + repeated string ips = 3; + } + } + ``` + + - Object.Locations + + | field | type | label | description | + | :------: | :-------------- | :------------------------------- | :----------------------------- | + | location | Object.Location | repeated(Array[Object.Location]) | The list of `Object.Location`. | + + - Object.Location + + | field | type | label | description | + | :---: | :----- | :---------------------- | :-------------------------------------------------------------------- | + | name | string | | The name of vald agent pod where the request vector is removed. | + | uuid | string | | The ID of the removed vector. It is the same as an `Object.ID`. | + | ips | string | repeated(Array[string]) | The IP list of `vald-agent` pods where the request vector is removed. | + +### Status Code + +| code | name | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 5 | NOT_FOUND | +| 10 | ABORTED | +| 13 | INTERNAL | + +Please refer to [Response Status Code](/docs/v1.8/status) for more details. + +### Troubleshooting + +The request process may not be completed when the response code is NOT `0 (OK)`. + +Here are some common reasons and how to resolve each error. + +| name | common reason | how to resolve | +| :---------------- | :---------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------- | +| CANCELLED | Executed cancel() of rpc from client/server-side or network problems between client and server. | Check the code, especially around timeout and connection management, and fix if needed. | +| INVALID_ARGUMENT | The Requested vector's ID is empty, or some request payload is invalid. | Check request payload and fix request payload. | +| DEADLINE_EXCEEDED | The RPC timeout setting is too short on the client/server side. | Check the gRPC timeout setting on both the client and server sides and fix it if needed. | +| NOT_FOUND | Requested ID is NOT inserted. | Send a request with an ID that is already inserted. | +| INTERNAL | Target Vald cluster or network route has some critical error. | Check target Vald cluster first and check network route including ingress as second. | diff --git a/content/docs/v1.8/api/search.md b/content/docs/v1.8/api/search.md new file mode 100644 index 000000000..0de2c30d9 --- /dev/null +++ b/content/docs/v1.8/api/search.md @@ -0,0 +1,1639 @@ +--- +title: "Search_v1.8/Api" +date: 2026-09-16T04:40:19Z +draft: false +weight: 400 +description: Search ANN vectors from the Vald cluster +menu: + api: + parent: Api +--- + +# Vald Search APIs + +## Overview + +Search Service is responsible for searching vectors similar to the user request vector from `vald-agent`. + +```rpc +service Search { + rpc Search(payload.v1.Search.Request) returns (payload.v1.Search.Response) {} + + rpc SearchByID(payload.v1.Search.IDRequest) + returns (payload.v1.Search.Response) {} + + rpc StreamSearch(stream payload.v1.Search.Request) + returns (stream payload.v1.Search.StreamResponse) {} + + rpc StreamSearchByID(stream payload.v1.Search.IDRequest) + returns (stream payload.v1.Search.StreamResponse) {} + + rpc MultiSearch(payload.v1.Search.MultiRequest) + returns (payload.v1.Search.Responses) {} + + rpc MultiSearchByID(payload.v1.Search.MultiIDRequest) + returns (payload.v1.Search.Responses) {} + + rpc LinearSearch(payload.v1.Search.Request) returns (payload.v1.Search.Response) {} + + rpc LinearSearchByID(payload.v1.Search.IDRequest) + returns (payload.v1.Search.Response) {} + + rpc StreamLinearSearch(stream payload.v1.Search.Request) + returns (stream payload.v1.Search.StreamResponse) {} + + rpc StreamLinearSearchByID(stream payload.v1.Search.IDRequest) + returns (stream payload.v1.Search.StreamResponse) {} + + rpc MultiLinearSearch(payload.v1.Search.MultiRequest) + returns (payload.v1.Search.Responses) {} + + rpc MultiLinearSearchByID(payload.v1.Search.MultiIDRequest) + returns (payload.v1.Search.Responses) {} +} +``` + +## Search RPC + +Search RPC is the method to search vector(s) similar to the request vector. + +### Input + +- the scheme of `payload.v1.Search.Request` + + ```rpc + message Search { + message Request { + repeated float vector = 1 [ (validate.rules).repeated .min_items = 2 ]; + Config config = 2; + } + + message Config { + string request_id = 1; + uint32 num = 2 [ (validate.rules).uint32.gte = 1 ]; + float radius = 3; + float epsilon = 4; + int64 timeout = 5; + Filter.Config ingress_filters = 6; + Filter.Config egress_filters = 7; + uint32 min_num = 8; + AggregationAlgorithm aggregation_algorithm = 9; + } + } + + enum AggregationAlgorithm { + Unknown = 0; + ConcurrentQueue = 1; + SortSlice = 2; + SortPoolSlice = 3; + PairingHeap = 4; + } + ``` + + - Search.Request + + | field | type | label | required | description | + | :----: | :----- | :--------------------- | :------: | :------------------------------------------------------ | + | vector | float | repeated(Array[float]) | \* | The vector data. Its dimension is between 2 and 65,536. | + | config | Config | | \* | The configuration of the search request. | + + - Search.Config + + | field | type | label | required | description | + | :-------------------: | :------------------- | :---- | :------: | :---------------------------------------------------------------------------- | + | request_id | string | | | Unique request ID. | + | num | uint32 | | \* | The maximum number of results to be returned. | + | radius | float | | \* | The search radius. | + | epsilon | float | | \* | The search coefficient (default value is `0.1`). | + | timeout | int64 | | | Search timeout in nanoseconds (default value is `5s`). | + | ingress_filters | Filter.Config | | | Ingress Filter configuration. | + | egress_filters | Filter.Config | | | Egress Filter configuration. | + | min_num | uint32 | | | The minimum number of results to be returned. | + | aggregation_algorithm | AggregationAlgorithm | | | The search aggregation algorithm option (default value is `ConcurrentQueue`). | + +### Output + +- the scheme of `payload.v1.Search.Response`. + + ```rpc + message Search { + message Response { + string request_id = 1; + repeated Object.Distance results = 2; + } + } + + message Object { + message Distance { + string id = 1; + float distance = 2; + } + } + ``` + + - Search.Response + + | field | type | label | description | + | :--------: | :-------------- | :------------------------------- | :--------------------- | + | request_id | string | | The unique request ID. | + | results | Object.Distance | repeated(Array[Object.Distance]) | Search results. | + + - Object.Distance + + | field | type | label | description | + | :------: | :----- | :---- | :------------------------------------------------------------- | + | id | string | | The vector ID. | + | distance | float | | The distance between the result vector and the request vector. | + +### Status Code + +| code | name | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 5 | NOT_FOUND | +| 10 | ABORTED | +| 13 | INTERNAL | + +Please refer to [Response Status Code](/docs/v1.8/status) for more details. + +### Troubleshooting + +The request process may not be completed when the response code is NOT `0 (OK)`. + +Here are some common reasons and how to resolve each error. + +| name | common reason | how to resolve | +| :---------------- | :-------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------- | +| CANCELLED | Executed cancel() of rpc from client/server-side or network problems between client and server. | Check the code, especially around timeout and connection management, and fix if needed. | +| INVALID_ARGUMENT | The Dimension of the request vector is NOT the same as Vald Agent's config, or some request payload is invalid. | Check Agent config, request payload, and fix request payload or Agent config. | +| DEADLINE_EXCEEDED | The RPC timeout setting is too short on the client/server side. | Check the gRPC timeout setting on both the client and server sides and fix it if needed. | +| NOT_FOUND | Search result is empty or insufficient to request result length. | Send a request with another vector or set min_num to a smaller value. | +| INTERNAL | Target Vald cluster or network route has some critical error. | Check target Vald cluster first and check network route including ingress as second. | + +## SearchByID RPC + +SearchByID RPC is the method to search similar vectors using a user-defined vector ID.
+The vector with the same requested ID should be indexed into the `vald-agent` before searching. + +### Input + +- the scheme of `payload.v1.Search.IDRequest` + + ```rpc + message Search { + message IDRequest { + string id = 1; + Config config = 2; + } + + message Config { + string request_id = 1; + uint32 num = 2 [ (validate.rules).uint32.gte = 1 ]; + float radius = 3; + float epsilon = 4; + int64 timeout = 5; + Filter.Config ingress_filters = 6; + Filter.Config egress_filters = 7; + uint32 min_num = 8; + AggregationAlgorithm aggregation_algorithm = 9; + } + } + + enum AggregationAlgorithm { + Unknown = 0; + ConcurrentQueue = 1; + SortSlice = 2; + SortPoolSlice = 3; + PairingHeap = 4; + } + ``` + + - Search.IDRequest + + | field | type | label | required | description | + | :----: | :----- | :---- | :------: | :--------------------------------------- | + | id | string | | \* | The vector ID to be searched. | + | config | Config | | \* | The configuration of the search request. | + + - Search.Config + + | field | type | label | required | description | + | :-------------------: | :------------------- | :---- | :------: | :---------------------------------------------------------------------------- | + | request_id | string | | | Unique request ID. | + | num | uint32 | | \* | The maximum number of results to be returned. | + | radius | float | | \* | The search radius. | + | epsilon | float | | \* | The search coefficient (default value is `0.1`). | + | timeout | int64 | | | Search timeout in nanoseconds (default value is `5s`). | + | ingress_filters | Filter.Config | | | Ingress Filter configuration. | + | egress_filters | Filter.Config | | | Egress Filter configuration. | + | min_num | uint32 | | | The minimum number of results to be returned. | + | aggregation_algorithm | AggregationAlgorithm | | | The search aggregation algorithm option (default value is `ConcurrentQueue`). | + +### Output + +- the scheme of `payload.v1.Search.Response`. + + ```rpc + message Search { + message Response { + string request_id = 1; + repeated Object.Distance results = 2; + } + } + + message Object { + message Distance { + string id = 1; + float distance = 2; + } + } + ``` + + - Search.Response + + | field | type | label | description | + | :--------: | :-------------- | :------------------------------- | :--------------------- | + | request_id | string | | The unique request ID. | + | results | Object.Distance | repeated(Array[Object.Distance]) | Search results. | + + - Object.Distance + + | field | type | label | description | + | :------: | :----- | :---- | :------------------------------------------------------------- | + | id | string | | The vector ID. | + | distance | float | | The distance between the result vector and the request vector. | + +### Status Code + +| code | name | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 5 | NOT_FOUND | +| 10 | ABORTED | +| 13 | INTERNAL | + +Please refer to [Response Status Code](/docs/v1.8/status) for more details. + +### Troubleshooting + +The request process may not be completed when the response code is NOT `0 (OK)`. + +Here are some common reasons and how to resolve each error. + +| name | common reason | how to resolve | +| :---------------- | :------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------- | +| CANCELLED | Executed cancel() of rpc from client/server-side or network problems between client and server. | Check the code, especially around timeout and connection management, and fix if needed. | +| INVALID_ARGUMENT | The Requested vector's ID is empty, or some request payload is invalid. | Check request payload and fix request payload. | +| DEADLINE_EXCEEDED | The RPC timeout setting is too short on the client/server side. | Check the gRPC timeout setting on both the client and server sides and fix it if needed. | +| NOT_FOUND | The Requested ID is not inserted on the target Vald cluster, or the search result is insufficient to the required result length. | Send a request with another vector or set min_num to a smaller value. | +| INTERNAL | Target Vald cluster or network route has some critical error. | Check target Vald cluster first and check network route including ingress as second. | + +## StreamSearch RPC + +StreamSearch RPC is the method to search vectors with multi queries(vectors) using the [bidirectional streaming RPC](https://grpc.io/docs/what-is-grpc/core-concepts/#bidirectional-streaming-rpc).
+Using the bidirectional streaming RPC, the search request can be communicated in any order between the client and server. +Each Search request and response are independent. + +### Input + +- the scheme of `payload.v1.Search.Request stream` + + ```rpc + message Search { + message Request { + repeated float vector = 1 [ (validate.rules).repeated .min_items = 2 ]; + Config config = 2; + } + + message Config { + string request_id = 1; + uint32 num = 2 [ (validate.rules).uint32.gte = 1 ]; + float radius = 3; + float epsilon = 4; + int64 timeout = 5; + Filter.Config ingress_filters = 6; + Filter.Config egress_filters = 7; + uint32 min_num = 8; + AggregationAlgorithm aggregation_algorithm = 9; + } + } + + enum AggregationAlgorithm { + Unknown = 0; + ConcurrentQueue = 1; + SortSlice = 2; + SortPoolSlice = 3; + PairingHeap = 4; + } + ``` + + - Search.Request + + | field | type | label | required | description | + | :----: | :----- | :--------------------- | :------: | :------------------------------------------------------ | + | vector | float | repeated(Array[float]) | \* | The vector data. Its dimension is between 2 and 65,536. | + | config | Config | | \* | The configuration of the search request. | + + - Search.Config + + | field | type | label | required | description | + | :-------------------: | :------------------- | :---- | :------: | :---------------------------------------------------------------------------- | + | request_id | string | | | Unique request ID. | + | num | uint32 | | \* | The maximum number of results to be returned. | + | radius | float | | \* | The search radius. | + | epsilon | float | | \* | The search coefficient (default value is `0.1`). | + | timeout | int64 | | | Search timeout in nanoseconds (default value is `5s`). | + | ingress_filters | Filter.Config | | | Ingress Filter configuration. | + | egress_filters | Filter.Config | | | Egress Filter configuration. | + | min_num | uint32 | | | The minimum number of results to be returned. | + | aggregation_algorithm | AggregationAlgorithm | | | The search aggregation algorithm option (default value is `ConcurrentQueue`). | + +### Output + +- the scheme of `payload.v1.Search.StreamResponse`. + + ```rpc + message Search { + message StreamResponse { + oneof payload { + Response response = 1; + google.rpc.Status status = 2; + } + } + + message Response { + string request_id = 1; + repeated Object.Distance results = 2; + } + } + + message Object { + message Distance { + string id = 1; + float distance = 2; + } + } + ``` + + - Search.StreamResponse + + | field | type | label | description | + | :------: | :---------------- | :---- | :-------------------------- | + | response | Response | | The search result response. | + | status | google.rpc.Status | | The status of Google RPC. | + + - Search.Response + + | field | type | label | description | + | :--------: | :-------------- | :------------------------------- | :--------------------- | + | request_id | string | | The unique request ID. | + | results | Object.Distance | repeated(Array[Object.Distance]) | Search results. | + + - Object.Distance + + | field | type | label | description | + | :------: | :----- | :---- | :------------------------------------------------------------- | + | id | string | | The vector ID. | + | distance | float | | The distance between the result vector and the request vector. | + +### Status Code + +| code | name | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 5 | NOT_FOUND | +| 10 | ABORTED | +| 13 | INTERNAL | + +Please refer to [Response Status Code](/docs/v1.8/status) for more details. + +### Troubleshooting + +The request process may not be completed when the response code is NOT `0 (OK)`. + +Here are some common reasons and how to resolve each error. + +| name | common reason | how to resolve | +| :---------------- | :-------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------- | +| CANCELLED | Executed cancel() of rpc from client/server-side or network problems between client and server. | Check the code, especially around timeout and connection management, and fix if needed. | +| INVALID_ARGUMENT | The Dimension of the request vector is NOT the same as Vald Agent's config, or some request payload is invalid. | Check Agent config, request payload, and fix request payload or Agent config. | +| DEADLINE_EXCEEDED | The RPC timeout setting is too short on the client/server side. | Check the gRPC timeout setting on both the client and server sides and fix it if needed. | +| NOT_FOUND | Search result is empty or insufficient to request result length. | Send a request with another vector or set min_num to a smaller value. | +| INTERNAL | Target Vald cluster or network route has some critical error. | Check target Vald cluster first and check network route including ingress as second. | + +## StreamSearchByID RPC + +StreamSearchByID RPC is the method to search vectors with multi queries(IDs) using the [bidirectional streaming RPC](https://grpc.io/docs/what-is-grpc/core-concepts/#bidirectional-streaming-rpc).
+Using the bidirectional streaming RPC, the search request can be communicated in any order between the client and server. +Each SearchByID request and response are independent. + +### Input + +- the scheme of `payload.v1.Search.IDRequest stream` + + ```rpc + message Search { + message IDRequest { + string id = 1; + Config config = 2; + } + + message Config { + string request_id = 1; + uint32 num = 2 [ (validate.rules).uint32.gte = 1 ]; + float radius = 3; + float epsilon = 4; + int64 timeout = 5; + Filter.Config ingress_filters = 6; + Filter.Config egress_filters = 7; + uint32 min_num = 8; + AggregationAlgorithm aggregation_algorithm = 9; + } + } + + enum AggregationAlgorithm { + Unknown = 0; + ConcurrentQueue = 1; + SortSlice = 2; + SortPoolSlice = 3; + PairingHeap = 4; + } + ``` + + - Search.IDRequest + + | field | type | label | required | description | + | :----: | :----- | :---- | :------: | :--------------------------------------- | + | id | string | | \* | The vector ID to be searched. | + | config | Config | | \* | The configuration of the search request. | + + - Search.Config + + | field | type | label | required | description | + | :-------------------: | :------------------- | :---- | :------: | :---------------------------------------------------------------------------- | + | request_id | string | | | Unique request ID. | + | num | uint32 | | \* | The maximum number of results to be returned. | + | radius | float | | \* | The search radius. | + | epsilon | float | | \* | The search coefficient (default value is `0.1`). | + | timeout | int64 | | | Search timeout in nanoseconds (default value is `5s`). | + | ingress_filters | Filter.Config | | | Ingress Filter configuration. | + | egress_filters | Filter.Config | | | Egress Filter configuration. | + | min_num | uint32 | | | The minimum number of results to be returned. | + | aggregation_algorithm | AggregationAlgorithm | | | The search aggregation algorithm option (default value is `ConcurrentQueue`). | + +### Output + +- the scheme of `payload.v1.Search.StreamResponse`. + + ```rpc + message Search { + message StreamResponse { + oneof payload { + Response response = 1; + google.rpc.Status status = 2; + } + } + + message Response { + string request_id = 1; + repeated Object.Distance results = 2; + } + } + + message Object { + message Distance { + string id = 1; + float distance = 2; + } + } + ``` + + - Search.StreamResponse + + | field | type | label | description | + | :------: | :---------------- | :---- | :-------------------------- | + | response | Response | | The search result response. | + | status | google.rpc.Status | | The status of Google RPC. | + + - Search.Response + + | field | type | label | description | + | :--------: | :-------------- | :------------------------------- | :--------------------- | + | request_id | string | | The unique request ID. | + | results | Object.Distance | repeated(Array[Object.Distance]) | Search results. | + + - Object.Distance + + | field | type | label | description | + | :------: | :----- | :---- | :------------------------------------------------------------- | + | id | string | | The vector ID. | + | distance | float | | The distance between the result vector and the request vector. | + +### Status Code + +| code | name | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 5 | NOT_FOUND | +| 10 | ABORTED | +| 13 | INTERNAL | + +Please refer to [Response Status Code](/docs/v1.8/status) for more details. + +### Troubleshooting + +The request process may not be completed when the response code is NOT `0 (OK)`. + +Here are some common reasons and how to resolve each error. + +| name | common reason | how to resolve | +| :---------------- | :------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------- | +| CANCELLED | Executed cancel() of rpc from client/server-side or network problems between client and server. | Check the code, especially around timeout and connection management, and fix if needed. | +| INVALID_ARGUMENT | The Requested vector's ID is empty, or some request payload is invalid. | Check request payload and fix request payload. | +| DEADLINE_EXCEEDED | The RPC timeout setting is too short on the client/server side. | Check the gRPC timeout setting on both the client and server sides and fix it if needed. | +| NOT_FOUND | The Requested ID is not inserted on the target Vald cluster, or the search result is insufficient to the required result length. | Send a request with another vector or set min_num to a smaller value. | +| INTERNAL | Target Vald cluster or network route has some critical error. | Check target Vald cluster first and check network route including ingress as second. | + +## MultiSearch RPC + +MultiSearch RPC is the method to search vectors with multiple vectors in **1** request. + +
+gRPC has a message size limitation.
+Please be careful that the size of the request exceeds the limit. +
+ +### Input + +- the scheme of `payload.v1.Search.MultiRequest` + + ```rpc + message Search { + message MultiRequest { + repeated Request requests = 1; + } + + message Request { + repeated float vector = 1 [ (validate.rules).repeated .min_items = 2 ]; + Config config = 2; + } + + message Config { + string request_id = 1; + uint32 num = 2 [ (validate.rules).uint32.gte = 1 ]; + float radius = 3; + float epsilon = 4; + int64 timeout = 5; + Filter.Config ingress_filters = 6; + Filter.Config egress_filters = 7; + uint32 min_num = 8; + AggregationAlgorithm aggregation_algorithm = 9; + } + } + + enum AggregationAlgorithm { + Unknown = 0; + ConcurrentQueue = 1; + SortSlice = 2; + SortPoolSlice = 3; + PairingHeap = 4; + } + ``` + + - Search.MultiRequest + + | field | type | label | required | description | + | :------: | :----------------------- | :---- | :------: | :----------------------- | + | requests | repeated(Array[Request]) | | \* | The search request list. | + + - Search.Request + + | field | type | label | required | description | + | :----: | :----- | :--------------------- | :------: | :------------------------------------------------------ | + | vector | float | repeated(Array[float]) | \* | The vector data. Its dimension is between 2 and 65,536. | + | config | Config | | \* | The configuration of the search request. | + + - Search.Config + + | field | type | label | required | description | + | :-------------------: | :------------------- | :---- | :------: | :---------------------------------------------------------------------------- | + | request_id | string | | | Unique request ID. | + | num | uint32 | | \* | The maximum number of results to be returned. | + | radius | float | | \* | The search radius. | + | epsilon | float | | \* | The search coefficient (default value is `0.1`). | + | timeout | int64 | | | Search timeout in nanoseconds (default value is `5s`). | + | ingress_filters | Filter.Config | | | Ingress Filter configuration. | + | egress_filters | Filter.Config | | | Egress Filter configuration. | + | min_num | uint32 | | | The minimum number of results to be returned. | + | aggregation_algorithm | AggregationAlgorithm | | | The search aggregation algorithm option (default value is `ConcurrentQueue`). | + +### Output + +- the scheme of `payload.v1.Search.Responses`. + + ```rpc + message Search { + message Responses { + repeated Response responses = 1; + } + + message Response { + string request_id = 1; + repeated Object.Distance results = 2; + } + } + + message Object { + message Distance { + string id = 1; + float distance = 2; + } + } + ``` + + - Search.Responses + + | field | type | label | description | + | :-------: | :------- | :------------------------ | :----------------------------------- | + | responses | Response | repeated(Array[Response]) | The list of search results response. | + + - Search.Response + + | field | type | label | description | + | :--------: | :-------------- | :------------------------------- | :--------------------- | + | request_id | string | | The unique request ID. | + | results | Object.Distance | repeated(Array[Object.Distance]) | Search results. | + + - Object.Distance + + | field | type | label | description | + | :------: | :----- | :---- | :------------------------------------------------------------- | + | id | string | | The vector ID. | + | distance | float | | The distance between the result vector and the request vector. | + +### Status Code + +| code | name | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 5 | NOT_FOUND | +| 10 | ABORTED | +| 13 | INTERNAL | + +Please refer to [Response Status Code](/docs/v1.8/status) for more details. + +### Troubleshooting + +The request process may not be completed when the response code is NOT `0 (OK)`. + +Here are some common reasons and how to resolve each error. + +| name | common reason | how to resolve | +| :---------------- | :-------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------- | +| CANCELLED | Executed cancel() of rpc from client/server-side or network problems between client and server. | Check the code, especially around timeout and connection management, and fix if needed. | +| INVALID_ARGUMENT | The Dimension of the request vector is NOT the same as Vald Agent's config, or some request payload is invalid. | Check Agent config, request payload, and fix request payload or Agent config. | +| DEADLINE_EXCEEDED | The RPC timeout setting is too short on the client/server side. | Check the gRPC timeout setting on both the client and server sides and fix it if needed. | +| NOT_FOUND | Search result is empty or insufficient to request result length. | Send a request with another vector or set min_num to a smaller value. | +| INTERNAL | Target Vald cluster or network route has some critical error. | Check target Vald cluster first and check network route including ingress as second. | + +## MultiSearchByID RPC + +MultiSearchByID RPC is the method to search vectors with multiple IDs in **1** request. + +
+gRPC has a message size limitation.
+Please be careful that the size of the request exceeds the limit. +
+ +### Input + +- the scheme of `payload.v1.Search.MultiIDRequest stream` + + ```rpc + message Search { + + message MultiIDRequest { + repeated IDRequest requests = 1; + } + + message IDRequest { + string id = 1; + Config config = 2; + } + + message Config { + string request_id = 1; + uint32 num = 2 [ (validate.rules).uint32.gte = 1 ]; + float radius = 3; + float epsilon = 4; + int64 timeout = 5; + Filter.Config ingress_filters = 6; + Filter.Config egress_filters = 7; + uint32 min_num = 8; + AggregationAlgorithm aggregation_algorithm = 9; + } + } + + enum AggregationAlgorithm { + Unknown = 0; + ConcurrentQueue = 1; + SortSlice = 2; + SortPoolSlice = 3; + PairingHeap = 4; + } + ``` + + - Search.MultiIDRequest + + | field | type | label | required | description | + | :------: | :-------- | :------------------------- | :------: | :--------------------------- | + | requests | IDRequest | repeated(Array[IDRequest]) | \* | The searchByID request list. | + + - Search.IDRequest + + | field | type | label | required | description | + | :----: | :----- | :---- | :------: | :--------------------------------------- | + | id | string | | \* | The vector ID to be searched. | + | config | Config | | \* | The configuration of the search request. | + + - Search.Config + + | field | type | label | required | description | + | :-------------------: | :------------------- | :---- | :------: | :---------------------------------------------------------------------------- | + | request_id | string | | | Unique request ID. | + | num | uint32 | | \* | The maximum number of results to be returned. | + | radius | float | | \* | The search radius. | + | epsilon | float | | \* | The search coefficient (default value is `0.1`). | + | timeout | int64 | | | Search timeout in nanoseconds (default value is `5s`). | + | ingress_filters | Filter.Config | | | Ingress Filter configuration. | + | egress_filters | Filter.Config | | | Egress Filter configuration. | + | min_num | uint32 | | | The minimum number of results to be returned. | + | aggregation_algorithm | AggregationAlgorithm | | | The search aggregation algorithm option (default value is `ConcurrentQueue`). | + +### Output + +- the scheme of `payload.v1.Search.Responses`. + + ```rpc + message Search { + message Responses { + repeated Response responses = 1; + } + + message Response { + string request_id = 1; + repeated Object.Distance results = 2; + } + } + + message Object { + message Distance { + string id = 1; + float distance = 2; + } + } + ``` + + - Search.Responses + + | field | type | label | description | + | :-------: | :------- | :------------------------ | :----------------------------------- | + | responses | Response | repeated(Array[Response]) | The list of search results response. | + + - Search.Response + + | field | type | label | description | + | :--------: | :-------------- | :------------------------------- | :--------------------- | + | request_id | string | | The unique request ID. | + | results | Object.Distance | repeated(Array[Object.Distance]) | Search results. | + + - Object.Distance + + | field | type | label | description | + | :------: | :----- | :---- | :------------------------------------------------------------- | + | id | string | | The vector ID. | + | distance | float | | The distance between the result vector and the request vector. | + +### Status Code + +| code | name | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 5 | NOT_FOUND | +| 10 | ABORTED | +| 13 | INTERNAL | + +Please refer to [Response Status Code](/docs/v1.8/status) for more details. + +### Troubleshooting + +The request process may not be completed when the response code is NOT `0 (OK)`. + +Here are some common reasons and how to resolve each error. + +| name | common reason | how to resolve | +| :---------------- | :------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------- | +| CANCELLED | Executed cancel() of rpc from client/server-side or network problems between client and server. | Check the code, especially around timeout and connection management, and fix if needed. | +| INVALID_ARGUMENT | The Requested vector's ID is empty, or some request payload is invalid. | Check request payload and fix request payload. | +| DEADLINE_EXCEEDED | The RPC timeout setting is too short on the client/server side. | Check the gRPC timeout setting on both the client and server sides and fix it if needed. | +| NOT_FOUND | The Requested ID is not inserted on the target Vald cluster, or the search result is insufficient to the required result length. | Send a request with another vector or set min_num to a smaller value. | +| INTERNAL | Target Vald cluster or network route has some critical error. | Check target Vald cluster first and check network route including ingress as second. | + +## LinearSearch RPC + +LinearSearch RPC is the method to linear search vector(s) similar to the request vector. + +### Input + +- the scheme of `payload.v1.Search.Request` + + ```rpc + message Search { + message Request { + repeated float vector = 1 [ (validate.rules).repeated .min_items = 2 ]; + Config config = 2; + } + + message Config { + string request_id = 1; + uint32 num = 2 [ (validate.rules).uint32.gte = 1 ]; + int64 timeout = 5; + Filter.Config ingress_filters = 6; + Filter.Config egress_filters = 7; + uint32 min_num = 8; + AggregationAlgorithm aggregation_algorithm = 9; + } + } + + enum AggregationAlgorithm { + Unknown = 0; + ConcurrentQueue = 1; + SortSlice = 2; + SortPoolSlice = 3; + PairingHeap = 4; + } + ``` + + - Search.Request + + | field | type | label | required | description | + | :----: | :----- | :--------------------- | :------: | :------------------------------------------------------ | + | vector | float | repeated(Array[float]) | \* | The vector data. Its dimension is between 2 and 65,536. | + | config | Config | | \* | The configuration of the search request. | + + - Search.Config + + | field | type | label | required | description | + | :-------------------: | :------------------- | :---- | :------: | :---------------------------------------------------------------------------- | + | request_id | string | | | Unique request ID. | + | num | uint32 | | \* | The maximum number of results to be returned. | + | timeout | int64 | | | Search timeout in nanoseconds (default value is `5s`). | + | ingress_filters | Filter.Config | | | Ingress Filter configuration. | + | egress_filters | Filter.Config | | | Egress Filter configuration. | + | min_num | uint32 | | | The minimum number of results to be returned. | + | aggregation_algorithm | AggregationAlgorithm | | | The search aggregation algorithm option (default value is `ConcurrentQueue`). | + +### Output + +- the scheme of `payload.v1.Search.Response`. + + ```rpc + message Search { + message Response { + string request_id = 1; + repeated Object.Distance results = 2; + } + } + + message Object { + message Distance { + string id = 1; + float distance = 2; + } + } + ``` + + - Search.Response + + | field | type | label | description | + | :--------: | :-------------- | :------------------------------- | :--------------------- | + | request_id | string | | The unique request ID. | + | results | Object.Distance | repeated(Array[Object.Distance]) | Search results. | + + - Object.Distance + + | field | type | label | description | + | :------: | :----- | :---- | :------------------------------------------------------------- | + | id | string | | The vector ID. | + | distance | float | | The distance between the result vector and the request vector. | + +### Status Code + +| code | name | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 5 | NOT_FOUND | +| 10 | ABORTED | +| 13 | INTERNAL | + +Please refer to [Response Status Code](/docs/v1.8/status) for more details. + +### Troubleshooting + +The request process may not be completed when the response code is NOT `0 (OK)`. + +Here are some common reasons and how to resolve each error. + +| name | common reason | how to resolve | +| :---------------- | :-------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------- | +| CANCELLED | Executed cancel() of rpc from client/server-side or network problems between client and server. | Check the code, especially around timeout and connection management, and fix if needed. | +| INVALID_ARGUMENT | The Dimension of the request vector is NOT the same as Vald Agent's config, or some request payload is invalid. | Check Agent config, request payload, and fix request payload or Agent config. | +| DEADLINE_EXCEEDED | The RPC timeout setting is too short on the client/server side. | Check the gRPC timeout setting on both the client and server sides and fix it if needed. | +| NOT_FOUND | Search result is empty or insufficient to request result length. | Send a request with another vector or set min_num to a smaller value. | +| INTERNAL | Target Vald cluster or network route has some critical error. | Check target Vald cluster first and check network route including ingress as second. | + +## LinearSearchByID RPC + +LinearSearchByID RPC is the method to linear search similar vectors using a user-defined vector ID.
+The vector with the same requested ID should be indexed into the `vald-agent` before searching. +You will get a `NOT_FOUND` error if the vector isn't stored. + +### Input + +- the scheme of `payload.v1.Search.IDRequest` + + ```rpc + message Search { + message IDRequest { + string id = 1; + Config config = 2; + } + + message Config { + string request_id = 1; + uint32 num = 2 [ (validate.rules).uint32.gte = 1 ]; + int64 timeout = 5; + Filter.Config ingress_filters = 6; + Filter.Config egress_filters = 7; + uint32 min_num = 8; + AggregationAlgorithm aggregation_algorithm = 9; + } + } + + enum AggregationAlgorithm { + Unknown = 0; + ConcurrentQueue = 1; + SortSlice = 2; + SortPoolSlice = 3; + PairingHeap = 4; + } + ``` + + - Search.IDRequest + + | field | type | label | required | description | + | :----: | :----- | :---- | :------: | :--------------------------------------- | + | id | string | | \* | The vector ID to be searched. | + | config | Config | | \* | The configuration of the search request. | + + - Search.Config + + | field | type | label | required | description | + | :-------------------: | :------------------- | :---- | :------: | :---------------------------------------------------------------------------- | + | request_id | string | | | Unique request ID. | + | num | uint32 | | \* | The maximum number of results to be returned. | + | timeout | int64 | | | Search timeout in nanoseconds (default value is `5s`). | + | ingress_filters | Filter.Config | | | Ingress Filter configuration. | + | egress_filters | Filter.Config | | | Egress Filter configuration. | + | min_num | uint32 | | | The minimum number of results to be returned. | + | aggregation_algorithm | AggregationAlgorithm | | | The search aggregation algorithm option (default value is `ConcurrentQueue`). | + +### Output + +- the scheme of `payload.v1.Search.Response`. + + ```rpc + message Search { + message Response { + string request_id = 1; + repeated Object.Distance results = 2; + } + } + + message Object { + message Distance { + string id = 1; + float distance = 2; + } + } + ``` + + - Search.Response + + | field | type | label | description | + | :--------: | :-------------- | :------------------------------- | :--------------------- | + | request_id | string | | The unique request ID. | + | results | Object.Distance | repeated(Array[Object.Distance]) | Search results. | + + - Object.Distance + + | field | type | label | description | + | :------: | :----- | :---- | :------------------------------------------------------------- | + | id | string | | The vector ID. | + | distance | float | | The distance between the result vector and the request vector. | + +### Status Code + +| code | name | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 5 | NOT_FOUND | +| 10 | ABORTED | +| 13 | INTERNAL | + +Please refer to [Response Status Code](/docs/v1.8/status) for more details. + +### Troubleshooting + +The request process may not be completed when the response code is NOT `0 (OK)`. + +Here are some common reasons and how to resolve each error. + +| name | common reason | how to resolve | +| :---------------- | :------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------- | +| CANCELLED | Executed cancel() of rpc from client/server-side or network problems between client and server. | Check the code, especially around timeout and connection management, and fix if needed. | +| INVALID_ARGUMENT | The Requested vector's ID is empty, or some request payload is invalid. | Check request payload and fix request payload. | +| DEADLINE_EXCEEDED | The RPC timeout setting is too short on the client/server side. | Check the gRPC timeout setting on both the client and server sides and fix it if needed. | +| NOT_FOUND | The Requested ID is not inserted on the target Vald cluster, or the search result is insufficient to the required result length. | Send a request with another vector or set min_num to a smaller value. | +| INTERNAL | Target Vald cluster or network route has some critical error. | Check target Vald cluster first and check network route including ingress as second. | + +## StreamLinearSearch RPC + +StreamLinearSearch RPC is the method to linear search vectors with multi queries(vectors) using the [bidirectional streaming RPC](https://grpc.io/docs/what-is-grpc/core-concepts/#bidirectional-streaming-rpc).
+Using the bidirectional streaming RPC, the linear search request can be communicated in any order between the client and server. +Each LinearSearch request and response are independent. + +### Input + +- the scheme of `payload.v1.Search.Request stream` + + ```rpc + message Search { + message Request { + repeated float vector = 1 [ (validate.rules).repeated .min_items = 2 ]; + Config config = 2; + } + + message Config { + string request_id = 1; + uint32 num = 2 [ (validate.rules).uint32.gte = 1 ]; + int64 timeout = 5; + Filter.Config ingress_filters = 6; + Filter.Config egress_filters = 7; + uint32 min_num = 8; + AggregationAlgorithm aggregation_algorithm = 9; + } + } + + enum AggregationAlgorithm { + Unknown = 0; + ConcurrentQueue = 1; + SortSlice = 2; + SortPoolSlice = 3; + PairingHeap = 4; + } + ``` + + - Search.Request + + | field | type | label | required | description | + | :----: | :----- | :--------------------- | :------: | :------------------------------------------------------ | + | vector | float | repeated(Array[float]) | \* | The vector data. Its dimension is between 2 and 65,536. | + | config | Config | | \* | The configuration of the search request. | + + - Search.Config + + | field | type | label | required | description | + | :-------------------: | :------------------- | :---- | :------: | :---------------------------------------------------------------------------- | + | request_id | string | | | Unique request ID. | + | num | uint32 | | \* | The maximum number of results to be returned. | + | timeout | int64 | | | Search timeout in nanoseconds (default value is `5s`). | + | ingress_filters | Filter.Config | | | Ingress Filter configuration. | + | egress_filters | Filter.Config | | | Egress Filter configuration. | + | min_num | uint32 | | | The minimum number of results to be returned. | + | aggregation_algorithm | AggregationAlgorithm | | | The search aggregation algorithm option (default value is `ConcurrentQueue`). | + +### Output + +- the scheme of `payload.v1.Search.StreamResponse`. + + ```rpc + message Search { + message StreamResponse { + oneof payload { + Response response = 1; + google.rpc.Status status = 2; + } + } + + message Response { + string request_id = 1; + repeated Object.Distance results = 2; + } + } + + message Object { + message Distance { + string id = 1; + float distance = 2; + } + } + ``` + + - Search.StreamResponse + + | field | type | label | description | + | :------: | :---------------- | :---- | :-------------------------- | + | response | Response | | The search result response. | + | status | google.rpc.Status | | The status of Google RPC. | + + - Search.Response + + | field | type | label | description | + | :--------: | :-------------- | :------------------------------- | :--------------------- | + | request_id | string | | The unique request ID. | + | results | Object.Distance | repeated(Array[Object.Distance]) | Search results. | + + - Object.Distance + + | field | type | label | description | + | :------: | :----- | :---- | :------------------------------------------------------------- | + | id | string | | The vector ID. | + | distance | float | | The distance between the result vector and the request vector. | + +### Status Code + +| code | name | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 5 | NOT_FOUND | +| 10 | ABORTED | +| 13 | INTERNAL | + +Please refer to [Response Status Code](/docs/v1.8/status) for more details. + +### Troubleshooting + +The request process may not be completed when the response code is NOT `0 (OK)`. + +Here are some common reasons and how to resolve each error. + +| name | common reason | how to resolve | +| :---------------- | :-------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------- | +| CANCELLED | Executed cancel() of rpc from client/server-side or network problems between client and server. | Check the code, especially around timeout and connection management, and fix if needed. | +| INVALID_ARGUMENT | The Dimension of the request vector is NOT the same as Vald Agent's config, or some request payload is invalid. | Check Agent config, request payload, and fix request payload or Agent config. | +| DEADLINE_EXCEEDED | The RPC timeout setting is too short on the client/server side. | Check the gRPC timeout setting on both the client and server sides and fix it if needed. | +| NOT_FOUND | Search result is empty or insufficient to request result length. | Send a request with another vector or set min_num to a smaller value. | +| INTERNAL | Target Vald cluster or network route has some critical error. | Check target Vald cluster first and check network route including ingress as second. | + +## StreamLinearSearchByID RPC + +StreamLinearSearchByID RPC is the method to linear search vectors with multi queries(IDs) using the [bidirectional streaming RPC](https://grpc.io/docs/what-is-grpc/core-concepts/#bidirectional-streaming-rpc).
+Using the bidirectional streaming RPC, the linear search request can be communicated in any order between the client and server. +Each LinearSearchByID request and response are independent. + +### Input + +- the scheme of `payload.v1.Search.IDRequest stream` + + ```rpc + message Search { + message IDRequest { + string id = 1; + Config config = 2; + } + + message Config { + string request_id = 1; + uint32 num = 2 [ (validate.rules).uint32.gte = 1 ]; + int64 timeout = 5; + Filter.Config ingress_filters = 6; + Filter.Config egress_filters = 7; + uint32 min_num = 8; + AggregationAlgorithm aggregation_algorithm = 9; + } + } + + enum AggregationAlgorithm { + Unknown = 0; + ConcurrentQueue = 1; + SortSlice = 2; + SortPoolSlice = 3; + PairingHeap = 4; + } + ``` + + - Search.IDRequest + + | field | type | label | required | description | + | :----: | :----- | :---- | :------: | :--------------------------------------- | + | id | string | | \* | The vector ID to be searched. | + | config | Config | | \* | The configuration of the search request. | + + - Search.Config + + | field | type | label | required | description | + | :-------------------: | :------------------- | :---- | :------: | :---------------------------------------------------------------------------- | + | request_id | string | | | Unique request ID. | + | num | uint32 | | \* | The maximum number of results to be returned. | + | timeout | int64 | | | Search timeout in nanoseconds (default value is `5s`). | + | ingress_filters | Filter.Config | | | Ingress Filter configuration. | + | egress_filters | Filter.Config | | | Egress Filter configuration. | + | min_num | uint32 | | | The minimum number of results to be returned. | + | aggregation_algorithm | AggregationAlgorithm | | | The search aggregation algorithm option (default value is `ConcurrentQueue`). | + +### Output + +- the scheme of `payload.v1.Search.StreamResponse`. + + ```rpc + message Search { + message StreamResponse { + oneof payload { + Response response = 1; + google.rpc.Status status = 2; + } + } + + message Response { + string request_id = 1; + repeated Object.Distance results = 2; + } + } + + message Object { + message Distance { + string id = 1; + float distance = 2; + } + } + ``` + + - Search.StreamResponse + + | field | type | label | description | + | :------: | :---------------- | :---- | :-------------------------- | + | response | Response | | The search result response. | + | status | google.rpc.Status | | The status of Google RPC. | + + - Search.Response + + | field | type | label | description | + | :--------: | :-------------- | :------------------------------- | :--------------------- | + | request_id | string | | The unique request ID. | + | results | Object.Distance | repeated(Array[Object.Distance]) | Search results. | + + - Object.Distance + + | field | type | label | description | + | :------: | :----- | :---- | :------------------------------------------------------------- | + | id | string | | The vector ID. | + | distance | float | | The distance between the result vector and the request vector. | + +### Status Code + +| code | name | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 5 | NOT_FOUND | +| 10 | ABORTED | +| 13 | INTERNAL | + +Please refer to [Response Status Code](/docs/v1.8/status) for more details. + +### Troubleshooting + +The request process may not be completed when the response code is NOT `0 (OK)`. + +Here are some common reasons and how to resolve each error. + +| name | common reason | how to resolve | +| :---------------- | :------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------- | +| CANCELLED | Executed cancel() of rpc from client/server-side or network problems between client and server. | Check the code, especially around timeout and connection management, and fix if needed. | +| INVALID_ARGUMENT | The Requested vector's ID is empty, or some request payload is invalid. | Check request payload and fix request payload. | +| DEADLINE_EXCEEDED | The RPC timeout setting is too short on the client/server side. | Check the gRPC timeout setting on both the client and server sides and fix it if needed. | +| NOT_FOUND | The Requested ID is not inserted on the target Vald cluster, or the search result is insufficient to the required result length. | Send a request with another vector or set min_num to a smaller value. | +| INTERNAL | Target Vald cluster or network route has some critical error. | Check target Vald cluster first and check network route including ingress as second. | + +## MultiLinearSearch RPC + +MultiLinearSearch RPC is the method to linear search vectors with multiple vectors in **1** request. + +
+gRPC has a message size limitation.
+Please be careful that the size of the request exceeds the limit. +
+ +### Input + +- the scheme of `payload.v1.Search.MultiRequest` + + ```rpc + message Search { + message MultiRequest { + repeated Request requests = 1; + } + + message Request { + repeated float vector = 1 [ (validate.rules).repeated .min_items = 2 ]; + Config config = 2; + } + + message Config { + string request_id = 1; + uint32 num = 2 [ (validate.rules).uint32.gte = 1 ]; + int64 timeout = 5; + Filter.Config ingress_filters = 6; + Filter.Config egress_filters = 7; + uint32 min_num = 8; + AggregationAlgorithm aggregation_algorithm = 9; + } + } + + enum AggregationAlgorithm { + Unknown = 0; + ConcurrentQueue = 1; + SortSlice = 2; + SortPoolSlice = 3; + PairingHeap = 4; + } + ``` + + - Search.MultiRequest + + | field | type | label | required | description | + | :------: | :----------------------- | :---- | :------: | :---------------------- | + | requests | repeated(Array[Request]) | | \* | The search request list | + + - Search.Request + + | field | type | label | required | description | + | :----: | :----- | :--------------------- | :------: | :------------------------------------------------------ | + | vector | float | repeated(Array[float]) | \* | The vector data. Its dimension is between 2 and 65,536. | + | config | Config | | \* | The configuration of the search request. | + + - Search.Config + + | field | type | label | required | description | + | :-------------------: | :------------------- | :---- | :------: | :---------------------------------------------------------------------------- | + | request_id | string | | | Unique request ID. | + | num | uint32 | | \* | The maximum number of results to be returned. | + | timeout | int64 | | | Search timeout in nanoseconds (default value is `5s`). | + | ingress_filters | Filter.Config | | | Ingress Filter configuration. | + | egress_filters | Filter.Config | | | Egress Filter configuration. | + | min_num | uint32 | | | The minimum number of results to be returned. | + | aggregation_algorithm | AggregationAlgorithm | | | The search aggregation algorithm option (default value is `ConcurrentQueue`). | + +### Output + +- the scheme of `payload.v1.Search.Responses`. + + ```rpc + message Search { + message Responses { + repeated Response responses = 1; + } + + message Response { + string request_id = 1; + repeated Object.Distance results = 2; + } + } + + message Object { + message Distance { + string id = 1; + float distance = 2; + } + } + ``` + + - Search.Responses + + | field | type | label | description | + | :-------: | :------- | :------------------------ | :---------------------------------- | + | responses | Response | repeated(Array[Response]) | The list of search results response | + + - Search.Response + + | field | type | label | description | + | :--------: | :-------------- | :------------------------------- | :--------------------- | + | request_id | string | | The unique request ID. | + | results | Object.Distance | repeated(Array[Object.Distance]) | Search results. | + + - Object.Distance + + | field | type | label | description | + | :------: | :----- | :---- | :------------------------------------------------------------- | + | id | string | | The vector ID. | + | distance | float | | The distance between the result vector and the request vector. | + +### Status Code + +| code | name | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 5 | NOT_FOUND | +| 10 | ABORTED | +| 13 | INTERNAL | + +Please refer to [Response Status Code](/docs/v1.8/status) for more details. + +### Troubleshooting + +The request process may not be completed when the response code is NOT `0 (OK)`. + +Here are some common reasons and how to resolve each error. + +| name | common reason | how to resolve | +| :---------------- | :-------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------- | +| CANCELLED | Executed cancel() of rpc from client/server-side or network problems between client and server. | Check the code, especially around timeout and connection management, and fix if needed. | +| INVALID_ARGUMENT | The Dimension of the request vector is NOT the same as Vald Agent's config, or some request payload is invalid. | Check Agent config, request payload, and fix request payload or Agent config. | +| DEADLINE_EXCEEDED | The RPC timeout setting is too short on the client/server side. | Check the gRPC timeout setting on both the client and server sides and fix it if needed. | +| NOT_FOUND | Search result is empty or insufficient to request result length. | Send a request with another vector or set min_num to a smaller value. | +| INTERNAL | Target Vald cluster or network route has some critical error. | Check target Vald cluster first and check network route including ingress as second. | + +## MultiLinearSearchByID RPC + +MultiLinearSearchByID RPC is the method to linear search vectors with multiple IDs in **1** request. + +
+gRPC has a message size limitation.
+Please be careful that the size of the request exceeds the limit. +
+ +### Input + +- the scheme of `payload.v1.Search.MultiIDRequest stream` + + ```rpc + message Search { + + message MultiIDRequest { + repeated IDRequest requests = 1; + } + + message IDRequest { + string id = 1; + Config config = 2; + } + + message Config { + string request_id = 1; + uint32 num = 2 [ (validate.rules).uint32.gte = 1 ]; + int64 timeout = 5; + Filter.Config ingress_filters = 6; + Filter.Config egress_filters = 7; + uint32 min_num = 8; + AggregationAlgorithm aggregation_algorithm = 9; + } + } + + enum AggregationAlgorithm { + Unknown = 0; + ConcurrentQueue = 1; + SortSlice = 2; + SortPoolSlice = 3; + PairingHeap = 4; + } + ``` + + - Search.MultiIDRequest + + | field | type | label | required | description | + | :------: | :-------- | :------------------------- | :------: | :--------------------------- | + | requests | IDRequest | repeated(Array[IDRequest]) | \* | The searchByID request list. | + + - Search.IDRequest + + | field | type | label | required | description | + | :----: | :----- | :---- | :------: | :--------------------------------------- | + | id | string | | \* | The vector ID to be searched. | + | config | Config | | \* | The configuration of the search request. | + + - Search.Config + + | field | type | label | required | description | + | :-------------------: | :------------------- | :---- | :------: | :---------------------------------------------------------------------------- | + | request_id | string | | | Unique request ID. | + | num | uint32 | | \* | The maximum number of results to be returned. | + | timeout | int64 | | | Search timeout in nanoseconds (default value is `5s`). | + | ingress_filters | Filter.Config | | | Ingress Filter configuration. | + | egress_filters | Filter.Config | | | Egress Filter configuration. | + | min_num | uint32 | | | The minimum number of results to be returned. | + | aggregation_algorithm | AggregationAlgorithm | | | The search aggregation algorithm option (default value is `ConcurrentQueue`). | + +### Output + +- the scheme of `payload.v1.Search.Responses`. + + ```rpc + message Search { + message Responses { + repeated Response responses = 1; + } + + message Response { + string request_id = 1; + repeated Object.Distance results = 2; + } + } + + message Object { + message Distance { + string id = 1; + float distance = 2; + } + } + ``` + + - Search.Responses + + | field | type | label | description | + | :-------: | :------- | :------------------------ | :----------------------------------- | + | responses | Response | repeated(Array[Response]) | The list of search results response. | + + - Search.Response + + | field | type | label | description | + | :--------: | :-------------- | :------------------------------- | :--------------------- | + | request_id | string | | The unique request ID. | + | results | Object.Distance | repeated(Array[Object.Distance]) | Search results. | + + - Object.Distance + + | field | type | label | description | + | :------: | :----- | :---- | :------------------------------------------------------------- | + | id | string | | The vector ID. | + | distance | float | | The distance between the result vector and the request vector. | + +### Status Code + +| code | name | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 5 | NOT_FOUND | +| 10 | ABORTED | +| 13 | INTERNAL | + +Please refer to [Response Status Code](/docs/v1.8/status) for more details. + +### Troubleshooting + +The request process may not be completed when the response code is NOT `0 (OK)`. + +Here are some common reasons and how to resolve each error. + +| name | common reason | how to resolve | +| :---------------- | :------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------- | +| CANCELLED | Executed cancel() of rpc from client/server-side or network problems between client and server. | Check the code, especially around timeout and connection management, and fix if needed. | +| INVALID_ARGUMENT | The Requested vector's ID is empty, or some request payload is invalid. | Check request payload and fix request payload. | +| DEADLINE_EXCEEDED | The RPC timeout setting is too short on the client/server side. | Check the gRPC timeout setting on both the client and server sides and fix it if needed. | +| NOT_FOUND | The Requested ID is not inserted on the target Vald cluster, or the search result is insufficient to the required result length. | Send a request with another vector or set min_num to a smaller value. | +| INTERNAL | Target Vald cluster or network route has some critical error. | Check target Vald cluster first and check network route including ingress as second. | diff --git a/content/docs/v1.8/api/status.md b/content/docs/v1.8/api/status.md new file mode 100644 index 000000000..19fcdca55 --- /dev/null +++ b/content/docs/v1.8/api/status.md @@ -0,0 +1,151 @@ +--- +title: "Status_v1.8/Api" +date: 2026-09-16T04:40:19Z +draft: false +weight: 1000 +description: API response description for each status +menu: + api: + parent: Api +--- + +# Response Status Code + +This page describes each status code from the API response. + +## Status Codes + +This table shows the main status code and its name using Vald. +The sections below describe the meaning of each code and why API returns. + +| code | name | +| :--: | :---------------------------------------- | +| 0 | [OK](#OK) | +| 1 | [CANCELLED](#CANCELLED) | +| 3 | [INVALID_ARGUMENT](#INVALID_ARGUMENT) | +| 4 | [DEADLINE_EXCEEDED](#DEADLINE_EXCEEDED) | +| 5 | [NOT_FOUND](#NOT_FOUND) | +| 6 | [ALREADY_EXISTS](#ALREADY_EXISTS) | +| 8 | [RESOURCE_EXHAUSTED](#RESOURCE_EXHAUSTED) | +| 10 | [ABORTED](#ABORTED) | +| 13 | [INTERNAL](#INTERNAL) | +| 14 | [UNAVAILABLE](#UNAVAILABLE) | + +## OK + +`OK` means complete process with success. + +Services that return this code are all services. + +## CANCELLED + +`CANCELLED` means the operation was cancelled. + +Services that return this code are all services. + +## INVALID_ARGUMENT + +`INVALID_ARGUMENT` means something wrong in the request argument. + +Services that return status are all services. +If you get this code, please verify your request payload is correct. + +## DEADLINE_EXCEEDED + +`DEADLINE_EXCEEDED` returns when the process ends due to timeout. + +Services that return status are: + +- [Object Service(only Exists RPC)](/docs/v1.8/api/object#Exists-RPC) +- [Insert Service](/docs/v1.8/api/insert) +- [Remove Service](/docs/v1.8/api/insert) +- [Search Service](/docs/v1.8/api/search) + +The timeout configuration is on the Vald cluster side. +If it appears constantly, you need to review the cluster settings. +However, only when using a search service can you overwrite the timeout configuration by lengthening the time setting in the search request config. + +## NOT_FOUND + +`NOT_FOUND` appears when there is no result corresponding to the request. + +The example cases are: + +- No search result when using SearchById RPC. +- No index data corresponding to the request vector when using Update/Exists/GetObject RPC + +Services that return status are: + +- [Object Service](/docs/v1.8/api/object) +- [Update Service](/docs/v1.8/api/update) +- [Remove Service](/docs/v1.8/api/insert) +- [Search Service](/docs/v1.8/api/search) + +The possible reasons are: + +- There is no index data in Vald Agent components or running the indexing process in the Vald Agent components. + When the Vald Agent component runs the indexing process (createIndex/saveIndex), any process won't run, and it will return with no result. +- The request query vector or ID is wrong when using the search/update/remove service. + Notably, both update service and remove service require the ID of vector Vald Agent component already indexed. + +## ALREADY_EXISTS + +`ALREADY_EXISTS` means that the Vald Agent component already stores the vector index same as the query vector when set `skip_strict_exist_check` as `true` in request config. + +Services that return status are: + +- [Insert Service](/docs/v1.8/api/insert) +- [Update Service](/docs/v1.8/api/update) + +You have to change the query vector with `skip_strict_exist_check` as `true` or set `skip_strict_exist_check` as `false` instead of changing the query vector. + +## RESOURCE_EXHAUSTED + +`RESOURCE_EXHAUSTED` means that some resources have been exhausted. + +It appears when: + +- There is out-of-memory in gRPC payload, in networks, etc. +- There are some server overload situations. +- The sent or received message is larger than the configured limit (default is 4 MB). + +Services that return status are all services. +The most case in the Vald is that the query vector is too large. +In other words, the vector dimension size in configuration is too large. + +## ABORTED + +`ABORTED` means that the operation was aborted, usually due to a concurrency issue. + +It appears when: + +- A request is made during the process of creating indices. +- A request is made during the flushing process. + +Services that return status are: + +- [Insert Service](/docs/v1.8/api/insert) +- [Update Service](/docs/v1.8/api/update) +- [Upsert Service](/docs/v1.8/api/upsert) +- [Remove Service](/docs/v1.8/api/remove) +- [Search Service](/docs/v1.8/api/search) + +You have to wait for the completion of creating indices or flushing process before making the request again. + +## INTERNAL + +`INTERNAL` appears when some wrong happens in the Vald cluster. +It is there are the serious problems with the Vald cluster. + +Services that return status are all services. +If you get it, please verify the state of the Vald cluster. + +
+If an internal server error appears, it should have cared and researched logs metrics. +
+ +## UNAVAILABLE + +`UNAVAILABLE` means the gRPC message cannot reach the Vald cluster. + +It would be best to verify whether the Vald cluster is running and the host and port are correct. diff --git a/content/docs/v1.8/api/update.md b/content/docs/v1.8/api/update.md new file mode 100644 index 000000000..341534221 --- /dev/null +++ b/content/docs/v1.8/api/update.md @@ -0,0 +1,386 @@ +--- +title: "Update_v1.8/Api" +date: 2026-09-16T04:40:20Z +draft: false +weight: 200 +description: Update vectors indexed in the Vald cluster +menu: + api: + parent: Api +--- + +# Vald Update APIs + +## Overview + +Update Service updates to new vector from inserted vector in the `vald-agent` components. + +```rpc +service Update { + rpc Update(payload.v1.Update.Request) returns (payload.v1.Object.Location) {} + + rpc StreamUpdate(stream payload.v1.Update.Request) returns (stream payload.v1.Object.Location) {} + + rpc MultiUpdate(payload.v1.Update.MultiRequest) returns (stream payload.v1.Object.Locations) {} +} +``` + +## Update RPC + +Update RPC is the method to update a single vector. + +### Input + +- the scheme of `payload.v1.Update.Request` + + ```rpc + message Update { + message Request { + Object.Vector vector = 1 [ (validate.rules).repeated .min_items = 2 ]; + Config config = 2; + } + + message Config { + bool skip_strict_exist_check = 1; + Filter.Config filters = 2; + int64 timestamp = 3; + } + } + + message Object { + message Vector { + string id = 1 [ (validate.rules).string.min_len = 1 ]; + repeated float vector = 2 [ (validate.rules).repeated.min_items = 2 ]; + } + } + ``` + + - Update.Request + + | field | type | label | required | description | + | :----: | :------------ | :---- | :------: | :--------------------------------------- | + | vector | Object.Vector | | \* | The information of vector. | + | config | Config | | \* | The configuration of the update request. | + + - Update.Config + + | field | type | label | required | description | + | :---------------------: | :------------ | :---- | :------: | :------------------------------------------------------------------------------------------------------------ | + | skip_strict_exist_check | bool | | | Check whether the same vector is already inserted or not.
The ID should be unique if the value is `true`. | + | timestamp | int64 | | | The timestamp of the vector inserted.
If it is N/A, the current time will be used. | + | filters | Filter.Config | | | Configuration for filter. | + | disable_balanced_update | bool | | | A flag to disable balanced update (split remove -> insert operation) during update operation. | + + - Object.Vector + + | field | type | label | required | description | + | :----: | :----- | :--------------------- | :------: | :------------------------------------------------------------- | + | id | string | | \* | The ID of a vector. ID should consist of 1 or more characters. | + | vector | float | repeated(Array[float]) | \* | The vector data. Its dimension is between 2 and 65,536. | + +### Output + +- the scheme of `payload.v1.Object.Location` + + ```rpc + message Object { + message Location { + string name = 1; + string uuid = 2; + repeated string ips = 3; + } + } + ``` + + - Object.Location + + | field | type | label | description | + | :---: | :----- | :---------------------- | :-------------------------------------------------------------------- | + | name | string | | The name of vald agent pod where the request vector is updated. | + | uuid | string | | The ID of the updated vector. It is the same as an `Object.Vector`. | + | ips | string | repeated(Array[string]) | The IP list of `vald-agent` pods where the request vector is updated. | + +### Status Code + +| code | name | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 5 | NOT_FOUND | +| 6 | ALREADY_EXISTS | +| 10 | ABORTED | +| 13 | INTERNAL | + +Please refer to [Response Status Code](/docs/v1.8/status) for more details. + +### Troubleshooting + +The request process may not be completed when the response code is NOT `0 (OK)`. + +Here are some common reasons and how to resolve each error. + +| name | common reason | how to resolve | +| :---------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------- | +| CANCELLED | Executed cancel() of rpc from client/server-side or network problems between client and server. | Check the code, especially around timeout and connection management, and fix if needed. | +| INVALID_ARGUMENT | The Dimension of the request vector is NOT the same as Vald Agent's config, the requested vector's ID is empty, or some request payload is invalid. | Check Agent config, request payload, and fix request payload or Agent config. | +| DEADLINE_EXCEEDED | The RPC timeout setting is too short on the client/server side. | Check the gRPC timeout setting on both the client and server sides and fix it if needed. | +| NOT_FOUND | Requested ID is NOT inserted. | Send a request with an ID that is already inserted. | +| ALREADY_EXISTS | Request pair of ID and vector is already inserted. | Change request ID. | +| INTERNAL | Target Vald cluster or network route has some critical error. | Check target Vald cluster first and check network route including ingress as second. | + +## StreamUpdate RPC + +StreamUpdate RPC is the method to update multiple vectors using the [bidirectional streaming RPC](https://grpc.io/docs/what-is-grpc/core-concepts/#bidirectional-streaming-rpc).
+Using the bidirectional streaming RPC, the update request can be communicated in any order between client and server. +Each Update request and response are independent. +It's the recommended method to update the large amount of vectors. + +### Input + +- the scheme of `payload.v1.Update.Request stream` + + ```rpc + message Update { + message Request { + Object.Vector vector = 1 [ (validate.rules).repeated .min_items = 2 ]; + Config config = 2; + } + message Config { + bool skip_strict_exist_check = 1; + Filter.Config filters = 2; + int64 timestamp = 3; + } + } + + message Object { + message Vector { + string id = 1 [ (validate.rules).string.min_len = 1 ]; + repeated float vector = 2 [ (validate.rules).repeated .min_items = 2 ]; + } + } + ``` + + - Update.Request + + | field | type | label | required | description | + | :----: | :------------ | :---- | :------: | :--------------------------------------- | + | vector | Object.Vector | | \* | The information of vector. | + | config | Config | | \* | The configuration of the update request. | + + - Update.Config + + | field | type | label | required | description | + | :---------------------: | :------------ | :---- | :------: | :------------------------------------------------------------------------------------------------------------ | + | skip_strict_exist_check | bool | | | Check whether the same vector is already inserted or not.
The ID should be unique if the value is `true`. | + | timestamp | int64 | | | The timestamp of the vector inserted.
If it is N/A, the current time will be used. | + | filters | Filter.Config | | | Configuration for filter. | + | disable_balanced_update | bool | | | A flag to disable balanced update (split remove -> insert operation) during update operation. | + + - Object.Vector + + | field | type | label | required | description | + | :----: | :----- | :--------------------- | :------: | :------------------------------------------------------------- | + | id | string | | \* | The ID of a vector. ID should consist of 1 or more characters. | + | vector | float | repeated(Array[float]) | \* | The vector data. Its dimension is between 2 and 65,536. | + +### Output + +- the scheme of `payload.v1.Object.StreamLocation` + + ```rpc + message Object { + message StreamLocation { + oneof payload { + Location location = 1; + google.rpc.Status status = 2; + } + } + + message Location { + string name = 1; + string uuid = 2; + repeated string ips = 3; + } + } + ``` + + - Object.StreamLocation + + | field | type | label | description | + | :------: | :---------------- | :---- | :--------------------------------------- | + | location | Object.Location | | The information of Object.Location data. | + | status | google.rpc.Status | | The status of Google RPC. | + + - Object.Location + + | field | type | label | description | + | :---: | :----- | :---------------------- | :-------------------------------------------------------------------- | + | name | string | | The name of vald agent pod where the request vector is updated. | + | uuid | string | | The ID of the updated vector. It is the same as an `Object.Vector`. | + | ips | string | repeated(Array[string]) | The IP list of `vald-agent` pods where the request vector is updated. | + + - [google.rpc.Status](https://github.com/googleapis/googleapis/blob/master/google/rpc/status.proto) + + | field | type | label | description | + | :-----: | :------------------ | :------------------- | :-------------------------------------- | + | code | int32 | | Status code (code list is next section) | + | message | string | | Error message | + | details | google.protobuf.Any | repeated(Array[any]) | The details error message list | + +### Status Code + +| code | name | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 5 | NOT_FOUND | +| 6 | ALREADY_EXISTS | +| 10 | ABORTED | +| 13 | INTERNAL | + +Please refer to [Response Status Code](/docs/v1.8/status) for more details. + +### Troubleshooting + +The request process may not be completed when the response code is NOT `0 (OK)`. + +Here are some common reasons and how to resolve each error. + +| name | common reason | how to resolve | +| :---------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------- | +| CANCELLED | Executed cancel() of rpc from client/server-side or network problems between client and server. | Check the code, especially around timeout and connection management, and fix if needed. | +| INVALID_ARGUMENT | The Dimension of the request vector is NOT the same as Vald Agent's config, the requested vector's ID is empty, or some request payload is invalid. | Check Agent config, request payload, and fix request payload or Agent config. | +| DEADLINE_EXCEEDED | The RPC timeout setting is too short on the client/server side. | Check the gRPC timeout setting on both the client and server sides and fix it if needed. | +| NOT_FOUND | Requested ID is NOT inserted. | Send a request with an ID that is already inserted. | +| ALREADY_EXISTS | Request pair of ID and vector is already inserted. | Change request ID. | +| INTERNAL | Target Vald cluster or network route has some critical error. | Check target Vald cluster first and check network route including ingress as second. | + +## MultiUpdate RPC + +MultiUpdate is the method to update multiple vectors in **1** request. + +
+gRPC has a message size limitation.
+Please be careful that the size of the request exceeds the limit. +
+ +### Input + +- the scheme of `payload.v1.Update.MultiRequest` + + ```rpc + message Update { + message MultiRequest { repeated Request requests = 1; } + + message Request { + Object.Vector vector = 1 [ (validate.rules).repeated .min_items = 2 ]; + Config config = 2; + } + + message Config { + bool skip_strict_exist_check = 1; + Filter.Config filters = 2; + int64 timestamp = 3; + } + } + + message Object { + message Vector { + string id = 1 [ (validate.rules).string.min_len = 1 ]; + repeated float vector = 2 [ (validate.rules).repeated .min_items = 2 ]; + } + } + ``` + + - Update.MultiRequest + + | field | type | label | required | description | + | :------: | :------------- | :------------------------------ | :------: | :---------------- | + | requests | Insert.Request | repeated(Array[Insert.Request]) | \* | The request list. | + + - Update.Request + + | field | type | label | required | description | + | :----: | :------------ | :---- | :------: | :--------------------------------------- | + | vector | Object.Vector | | \* | The information of vector. | + | config | Config | | \* | The configuration of the update request. | + + - Update.Config + + | field | type | label | required | description | + | :---------------------: | :------------ | :---- | :------: | :------------------------------------------------------------------------------------------------------------ | + | skip_strict_exist_check | bool | | | Check whether the same vector is already inserted or not.
The ID should be unique if the value is `true`. | + | timestamp | int64 | | | The timestamp of the vector inserted.
If it is N/A, the current time will be used. | + | filters | Filter.Config | | | Configuration for filter. | + | disable_balanced_update | bool | | | A flag to disable balanced update (split remove -> insert operation) during update operation. | + + - Object.Vector + + | field | type | label | required | description | + | :----: | :----- | :--------------------- | :------: | :------------------------------------------------------------- | + | id | string | | \* | The ID of a vector. ID should consist of 1 or more characters. | + | vector | float | repeated(Array[float]) | \* | The vector data. Its dimension is between 2 and 65,536. | + +### Output + +- the scheme of `payload.v1.Object.Locations`. + + ```rpc + message Object { + message Locations { repeated Location locations = 1; } + + message Location { + string name = 1; + string uuid = 2; + repeated string ips = 3; + } + } + ``` + + - Object.Locations + + | field | type | label | description | + | :------: | :-------------- | :------------------------------- | :----------------------------- | + | location | Object.Location | repeated(Array[Object.Location]) | The list of `Object.Location`. | + + - Object.Location + + | field | type | label | description | + | :---: | :----- | :---------------------- | :-------------------------------------------------------------------- | + | name | string | | The name of vald agent pod where the request vector is updated. | + | uuid | string | | The ID of the updated vector. It is the same as an `Object.Vector`. | + | ips | string | repeated(Array[string]) | The IP list of `vald-agent` pods where the request vector is updated. | + +### Status Code + +| code | name | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 5 | NOT_FOUND | +| 6 | ALREADY_EXISTS | +| 10 | ABORTED | +| 13 | INTERNAL | + +Please refer to [Response Status Code](/docs/v1.8/status) for more details. + +### Troubleshooting + +The request process may not be completed when the response code is NOT `0 (OK)`. + +Here are some common reasons and how to resolve each error. + +| name | common reason | how to resolve | +| :---------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------- | +| CANCELLED | Executed cancel() of rpc from client/server-side or network problems between client and server. | Check the code, especially around timeout and connection management, and fix if needed. | +| INVALID_ARGUMENT | The Dimension of the request vector is NOT the same as Vald Agent's config, the requested vector's ID is empty, or some request payload is invalid. | Check Agent config, request payload, and fix request payload or Agent config. | +| DEADLINE_EXCEEDED | The RPC timeout setting is too short on the client/server side. | Check the gRPC timeout setting on both the client and server sides and fix it if needed. | +| NOT_FOUND | Requested ID is NOT inserted. | Send a request with an ID that is already inserted. | +| ALREADY_EXISTS | Request pair of ID and vector is already inserted. | Change request ID. | +| INTERNAL | Target Vald cluster or network route has some critical error. | Check target Vald cluster first and check network route including ingress as second. | diff --git a/content/docs/v1.8/api/upsert.md b/content/docs/v1.8/api/upsert.md new file mode 100644 index 000000000..e4b96f27f --- /dev/null +++ b/content/docs/v1.8/api/upsert.md @@ -0,0 +1,384 @@ +--- +title: "Upsert_v1.8/Api" +date: 2026-09-16T04:40:20Z +draft: false +weight: 300 +description: Update or Insert vectors to the Vald cluster +menu: + api: + parent: Api +--- + +# Vald Upsert APIs + +## Overview + +Upsert Service is responsible for updating existing vectors in the `vald-agent` or inserting new vectors into the `vald-agent` if the vector does not exist. + +```rpc +service Upsert { + + rpc Upsert(payload.v1.Upsert.Request) + returns (payload.v1.Object.Location) {} + + rpc StreamUpsert(stream payload.v1.Upsert.Request) + returns (stream payload.v1.Object.StreamLocation) {} + + rpc MultiUpsert(payload.v1.Upsert.MultiRequest) + returns (payload.v1.Object.Locations) {} +} +``` + +## Upsert RPC + +Upsert RPC is the method to update the inserted vector to a new single vector or add a new single vector if not inserted before. + +### Input + +- the scheme of `payload.v1.Upsert.Request` + + ```rpc + message Upsert { + message Request { + Object.Vector vector = 1 [ (validate.rules).repeated .min_items = 2 ]; + Config config = 2; + } + + message Config { + bool skip_strict_exist_check = 1; + Filter.Config filters = 2; + int64 timestamp = 3; + } + } + + message Object { + message Vector { + string id = 1 [ (validate.rules).string.min_len = 1 ]; + repeated float vector = 2 [ (validate.rules).repeated .min_items = 2 ]; + } + } + ``` + + - Upsert.Request + + | field | type | label | required | description | + | :----: | :------------ | :---- | :------: | :--------------------------------------- | + | vector | Object.Vector | | \* | The information of vector. | + | config | Config | | \* | The configuration of the update request. | + + - Upsert.Config + + | field | type | label | required | description | + | :---------------------: | :------------ | :---- | :------: | :------------------------------------------------------------------------------------------------------------ | + | skip_strict_exist_check | bool | | | Check whether the same vector is already inserted or not.
The ID should be unique if the value is `true`. | + | timestamp | int64 | | | The timestamp of the vector inserted.
If it is N/A, the current time will be used. | + | filters | Filter.Config | | | Configuration for filter. | + | disable_balanced_update | bool | | | A flag to disable balanced update (split remove -> insert operation) during update operation. | + + - Object.Vector + + | field | type | label | required | description | + | :----: | :----- | :--------------------- | :------: | :------------------------------------------------------------- | + | id | string | | \* | The ID of a vector. ID should consist of 1 or more characters. | + | vector | float | repeated(Array[float]) | \* | The vector data. Its dimension is between 2 and 65,536. | + +### Output + +- the scheme of `payload.v1.Object.Location` + + ```rpc + message Object { + message Location { + string name = 1; + string uuid = 2; + repeated string ips = 3; + } + } + ``` + + - Object.Location + + | field | type | label | description | + | :---: | :----- | :---------------------- | :----------------------------------------------------------------------------- | + | name | string | | The name of vald agent pod where the request vector is updated/inserted. | + | uuid | string | | The ID of the updated/inserted vector. It is the same as an `Object.Vector`. | + | ips | string | repeated(Array[string]) | The IP list of `vald-agent` pods where the request vector is updated/inserted. | + +### Status Code + +| code | name | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 6 | ALREADY_EXISTS | +| 10 | ABORTED | +| 13 | INTERNAL | + +Please refer to [Response Status Code](/docs/v1.8/status) for more details. + +### Troubleshooting + +The request process may not be completed when the response code is NOT `0 (OK)`. + +Here are some common reasons and how to resolve each error. + +| name | common reason | how to resolve | +| :---------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------- | +| CANCELLED | Executed cancel() of rpc from client/server-side or network problems between client and server. | Check the code, especially around timeout and connection management, and fix if needed. | +| INVALID_ARGUMENT | The Dimension of the request vector is NOT the same as Vald Agent's config, the requested vector's ID is empty, or some request payload is invalid. | Check Agent config, request payload, and fix request payload or Agent config. | +| DEADLINE_EXCEEDED | The RPC timeout setting is too short on the client/server side. | Check the gRPC timeout setting on both the client and server sides and fix it if needed. | +| ALREADY_EXISTS | Requested pair of ID and vector is already inserted | Change request payload or nothing to do if update is unnecessary. | +| INTERNAL | Target Vald cluster or network route has some critical error. | Check target Vald cluster first and check network route including ingress as second. | + +## StreamUpsert RPC + +StreamUpsert RPC is the method to update multiple existing vectors or add new multiple vectors using the [bidirectional streaming RPC](https://grpc.io/docs/what-is-grpc/core-concepts/#bidirectional-streaming-rpc).
+Using the bidirectional streaming RPC, the upsert request can be communicated in any order between the client and server. +Each Upsert request and response are independent. +It’s the recommended method to upsert a large number of vectors. + +### Input + +- the scheme of `payload.v1.Upsert.Request stream` + + ```rpc + message Upsert { + message Request { + Object.Vector vector = 1 [ (validate.rules).repeated .min_items = 2 ]; + Config config = 2; + } + message Config { + bool skip_strict_exist_check = 1; + Filter.Config filters = 2; + int64 timestamp = 3; + } + } + + message Object { + message Vector { + string id = 1 [ (validate.rules).string.min_len = 1 ]; + repeated float vector = 2 [ (validate.rules).repeated .min_items = 2 ]; + } + } + ``` + + - Upsert.Request + + | field | type | label | required | description | + | :----: | :------------ | :---- | :------: | :--------------------------------------- | + | vector | Object.Vector | | \* | The information of vector. | + | config | Config | | \* | The configuration of the update request. | + + - Upsert.Config + + | field | type | label | required | description | + | :---------------------: | :------------ | :---- | :------: | :------------------------------------------------------------------------------------------------------------ | + | skip_strict_exist_check | bool | | | Check whether the same vector is already inserted or not.
The ID should be unique if the value is `true`. | + | timestamp | int64 | | | The timestamp of the vector inserted.
If it is N/A, the current time will be used. | + | filters | Filter.Config | | | Configuration for filter. | + | disable_balanced_update | bool | | | A flag to disable balanced update (split remove -> insert operation) during update operation. | + + - Object.Vector + + | field | type | label | required | description | + | :----: | :----- | :--------------------- | :------: | :------------------------------------------------------------- | + | id | string | | \* | The ID of a vector. ID should consist of 1 or more characters. | + | vector | float | repeated(Array[float]) | \* | The vector data. Its dimension is between 2 and 65,536. | + +### Output + +- the scheme of `payload.v1.Object.StreamLocation` + + ```rpc + message Object { + message StreamLocation { + oneof payload { + Location location = 1; + google.rpc.Status status = 2; + } + } + + message Location { + string name = 1; + string uuid = 2; + repeated string ips = 3; + } + } + ``` + + - Object.StreamLocation + + | field | type | label | description | + | :------: | :---------------- | :---- | :--------------------------------------- | + | location | Object.Location | | The information of Object.Location data. | + | status | google.rpc.Status | | The status of Google RPC. | + + - Object.Location + + | field | type | label | description | + | :---: | :----- | :---------------------- | :----------------------------------------------------------------------------- | + | name | string | | The name of vald agent pod where the request vector is updated/inserted. | + | uuid | string | | The ID of the updated/inserted vector. It is the same as an `Object.Vector`. | + | ips | string | repeated(Array[string]) | The IP list of `vald-agent` pods where the request vector is updated/inserted. | + + - [google.rpc.Status](https://github.com/googleapis/googleapis/blob/master/google/rpc/status.proto) + + | field | type | label | description | + | :-----: | :------------------ | :------------------- | :-------------------------------------- | + | code | int32 | | Status code (code list is next section) | + | message | string | | Error message | + | details | google.protobuf.Any | repeated(Array[any]) | The details error message list | + +### Status Code + +| code | name | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 6 | ALREADY_EXISTS | +| 10 | ABORTED | +| 13 | INTERNAL | + +Please refer to [Response Status Code](/docs/v1.8/status) for more details. + +### Troubleshooting + +The request process may not be completed when the response code is NOT `0 (OK)`. + +Here are some common reasons and how to resolve each error. + +| name | common reason | how to resolve | +| :---------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------- | +| CANCELLED | Executed cancel() of rpc from client/server-side or network problems between client and server. | Check the code, especially around timeout and connection management, and fix if needed. | +| INVALID_ARGUMENT | The Dimension of the request vector is NOT the same as Vald Agent's config, the requested vector's ID is empty, or some request payload is invalid. | Check Agent config, request payload, and fix request payload or Agent config. | +| DEADLINE_EXCEEDED | The RPC timeout setting is too short on the client/server side. | Check the gRPC timeout setting on both the client and server sides and fix it if needed. | +| ALREADY_EXISTS | Requested pair of ID and vector is already inserted | Change request payload or nothing to do if update is unnecessary. | +| INTERNAL | Target Vald cluster or network route has some critical error. | Check target Vald cluster first and check network route including ingress as second. | + +## MultiUpsert RPC + +MultiUpsert is the method to update existing multiple vectors and add new multiple vectors in **1** request. + +
+gRPC has a message size limitation.
+Please be careful that the size of the request exceeds the limit. +
+ +### Input + +- the scheme of `payload.v1.Upsert.MultiRequest` + + ```rpc + message Upsert { + message MultiRequest { repeated Request requests = 1; } + + message Request { + Object.Vector vector = 1 [ (validate.rules).repeated .min_items = 2 ]; + Config config = 2; + } + + message Config { + bool skip_strict_exist_check = 1; + Filter.Config filters = 2; + int64 timestamp = 3; + } + } + + message Object { + message Vector { + string id = 1 [ (validate.rules).string.min_len = 1 ]; + repeated float vector = 2 [ (validate.rules).repeated .min_items = 2 ]; + } + } + ``` + + - Upsert.MultiRequest + + | field | type | label | required | description | + | :------: | :------------- | :------------------------------ | :------: | :---------------- | + | requests | Upsert.Request | repeated(Array[Insert.Request]) | \* | The request list. | + + - Upsert.Request + + | field | type | label | required | description | + | :----: | :------------ | :---- | :------: | :--------------------------------------- | + | vector | Object.Vector | | \* | The information of vector. | + | config | Config | | \* | The configuration of the update request. | + + - Upsert.Config + + | field | type | label | required | description | + | :---------------------: | :------------ | :---- | :------: | :------------------------------------------------------------------------------------------------------------ | + | skip_strict_exist_check | bool | | | Check whether the same vector is already inserted or not.
The ID should be unique if the value is `true`. | + | timestamp | int64 | | | The timestamp of the vector inserted.
If it is N/A, the current time will be used. | + | filters | Filter.Config | | | Configuration for filter. | + | disable_balanced_update | bool | | | A flag to disable balanced update (split remove -> insert operation) during update operation. | + + - Object.Vector + + | field | type | label | required | description | + | :----: | :----- | :--------------------- | :------: | :------------------------------------------------------------- | + | id | string | | \* | The ID of a vector. ID should consist of 1 or more characters. | + | vector | float | repeated(Array[float]) | \* | The vector data. Its dimension is between 2 and 65,536. | + +### Output + +- the scheme of `payload.v1.Object.Locations`. + + ```rpc + message Object { + message Locations { repeated Location locations = 1; } + + message Location { + string name = 1; + string uuid = 2; + repeated string ips = 3; + } + } + ``` + + - Object.Locations + + | field | type | label | description | + | :------: | :-------------- | :------------------------------- | :----------------------------- | + | location | Object.Location | repeated(Array[Object.Location]) | The list of `Object.Location`. | + + - Object.Location + + | field | type | label | description | + | :---: | :----- | :---------------------- | :----------------------------------------------------------------------------- | + | name | string | | The name of vald agent pod where the request vector is updated/inserted. | + | uuid | string | | The ID of the updated/inserted vector. It is the same as an `Object.Vector`. | + | ips | string | repeated(Array[string]) | The IP list of `vald-agent` pods where the request vector is updated/inserted. | + +### Status Code + +| code | name | +| :--: | :---------------- | +| 0 | OK | +| 1 | CANCELLED | +| 3 | INVALID_ARGUMENT | +| 4 | DEADLINE_EXCEEDED | +| 6 | ALREADY_EXISTS | +| 10 | ABORTED | +| 13 | INTERNAL | + +Please refer to [Response Status Code](/docs/v1.8/status) for more details. + +### Troubleshooting + +The request process may not be completed when the response code is NOT `0 (OK)`. + +Here are some common reasons and how to resolve each error. + +| name | common reason | how to resolve | +| :---------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------- | +| CANCELLED | Executed cancel() of rpc from client/server-side or network problems between client and server. | Check the code, especially around timeout and connection management, and fix if needed. | +| INVALID_ARGUMENT | The Dimension of the request vector is NOT the same as Vald Agent's config, the requested vector's ID is empty, or some request payload is invalid. | Check Agent config, request payload, and fix request payload or Agent config. | +| DEADLINE_EXCEEDED | The RPC timeout setting is too short on the client/server side. | Check the gRPC timeout setting on both the client and server sides and fix it if needed. | +| ALREADY_EXISTS | Requested pair of ID and vector is already inserted | Change request payload or nothing to do if update is unnecessary. | +| INTERNAL | Target Vald cluster or network route has some critical error. | Check target Vald cluster first and check network route including ingress as second. | diff --git a/content/docs/v1.8/contributing/_index.md b/content/docs/v1.8/contributing/_index.md new file mode 100644 index 000000000..19b682839 --- /dev/null +++ b/content/docs/v1.8/contributing/_index.md @@ -0,0 +1,7 @@ +--- +title: "Contributing_v1.8/Contributing" +date: 2026-09-16T04:40:21Z +draft: false +weight: 0 +--- + diff --git a/content/docs/v1.8/contributing/coding-style.md b/content/docs/v1.8/contributing/coding-style.md new file mode 100644 index 000000000..25b0fde77 --- /dev/null +++ b/content/docs/v1.8/contributing/coding-style.md @@ -0,0 +1,1418 @@ +--- +title: "Coding Style_v1.8/Contributing" +date: 2026-09-16T04:40:18Z +draft: false +weight: 300 +description: Coding style for Vald project +menu: + contributing: + parent: Contributing +--- + +# Go Style Guide in Vald + +## Introduction + +This guideline includes the coding style for all Vald contributors and reviewers. +Everyone should follow this guideline to keep the style consistent, so everyone can understand and contribute to Vald easier once they learn it. +You should have the basic knowledge of how to write Go before contributing to Vald. +If you find any bugs, please create [a GitHub issue](https://github.com/vdaas/vald/issues/new?assignees=&labels=type%2Fbug%2C+priority%2Fmedium%2C+team%2Fcore&template=bug_report&title=), and we will work on it. + +Please also read the [Contribution guideline](/docs/v1.8/contributing/contributing-guide) before you start contributing to Vald. + +## Code Formatting and Naming Convention + +Code formatting and naming conventions affect coding readability and maintainability. +Every developer has a different coding style. +Luckily Go provides tools to format source code and check for potential issues in the source code. +We recommend using [golines](https://github.com/segmentio/golines), [gofumpt](https://github.com/mvdan/gofumpt), and [goimports](https://github.com/golang/tools/tree/master/cmd/goimports) and [crlfmt](https://github.com/cockroachdb/crlfmt) to format the source code in Vald and [golangci-lint](https://github.com/golangci/golangci-lint) with the `--enable-all` option. +We suggest everyone install the plugin for your editor to format the code once you edit the code automatically and use `make format/go` command if you want to format the source code manually. + +But having tools to format source code does not mean you do not need to care about the formatting of the code, for example: + + + +
BadGood
+ +```go +badStr := "apiVersion: v1\n" + + "kind: Service\n" + + "metadata:\n" + + " name: grafana\n" + + "spec:\n" + + " ports:\n" + + " - name: grafana\n" + + " port: 3000\n" + + " targetPort: 3000\n" + + " protocol: TCP\n" +``` + + + +```go +goodStr := `apiVersion: v1 +kind: Service +metadata: + name: grafana +spec: + ports: + - name: grafana + port: 3000 + targetPort: 3000 + protocol: TCP +` +``` + +
+ +### Project Layout + +The project layout includes the folder and the file structure in the project. We follow the [project-layout](https://github.com/golang-standards/project-layout) example in Vald. + +### Packages + +The package defines the context of the objects in the package; for example, the corresponding methods and structs belong to the corresponding package. +Unlike other languages like Java, in Go, we use the package name to declare which context of the object we are going to use. +For example, the [time package](https://golang.org/pkg/time/) defines all the objects about time, like `time.Now()` method to get the current time. + +Here are the naming conventions of the package: + +- All lower case. + + ```go + // bad + package ioUtil + + // good + package ioutil + ``` + +- No plurals. + + ```go + // bad + package times + + // good + package time + ``` + +- Should be the same as the folder name. +- Should keep as simple as it should and contain only one specific context in the package. + + ```go + // bad + package encode + + // good + package base64 // inside the encoding/base64 folder + ``` + +- Should not be too general, for example, `util` or `helper`, which will cause all the objects from different contexts to be stored in one package. + If you want to name the package as `util`, please define the more specific package name more `ioutil` or `httputil`. + +All packages should contain a `doc.go` file under the package to describe what is the package is. For example, under the folder name called `cache`, should contain a file named `doc.go`, which contains the package documentation. Here is the example `doc.go` of the cache package. + + ```go + // Package cache provides an implementation of cache + package cache + ```` + +### General style + +This section describes the general guideline for the Vald programming style, every Vald contributor should keep these general guidelines in mind while working on the implementation of Vald. + +#### Order of declaration + +Put the higher priority or frequently used declaration on top of other declarations. +It makes it easier for Vald to read and search its target source code in Vald. + +For example, the interface declaration should have higher priority than the struct or function declaration; hence it should be put above other declarations. + + + +
BadGood
+ +```go +type S struct {} + +func (s *S) fn() {} + +type I interface {} +``` + + + +```go +type I interface {} + +type S struct {} + +func (s *S) fn() {} +``` + +
+ +#### Group similar definition + +Group similar definitions such as a struct or an interface declaration. +We should not group an interface and a struct declaration in the same block, for example: + + + +
BadGood
+ +```go +type ( + I interface {} + I2 interface {} + + s struct {} + s2 struct {} +) +``` + + + +```go +type ( + I interface {} + I2 interface {} +) + +type ( + s struct {} + s2 struct {} +) +``` + +
+ +### Interfaces + +The interface defines the program interface for usability and future extendability. +Unlike other languages like Java, Go supports implicit interface implementation. +The type implements do not need to specify the interface name; to "implement" the interface, the structs only need to define the methods the same as the interface, so please be careful to define the method name inside the interface. + +The interface should be named as: + +- Use MixedCaps + + ```go + // bad + type Roundtripper interface { + // interface definition + } + + // good + type RoundTripper interface { + // interface definition + } + ``` + +- Use understandable common short form. + + ```go + // bad + type ATSigner interface { + // interface definition + } + + // good + type AccessTokenSigner interface { + // interface definition + } + + // good + type HTTPServer interface { + // interface definition + } + ``` + +### Structs + +Structs in Go is the object definition; we can attach any fields and methods to the struct. The naming convention is the same as the interface one. +If the structs are implementing the interface, the structs name should be related to the interface, for example: + +```go +type Listener interface { + // interface definition +} + +// Listener instance for file +type FileListener struct { + // implement listener interface +} + +// Listener instance for HTTP +type HTTPListener struct { + // implement listener interface +} +``` + +#### Struct initialization + +There are many ways to initialize structs in Go; based on the use case, we can decide which way to initialize structs in Go. +To initialize struct, it is suggested to use `new(T)` instead of `&T{}` unless you need to initialize with values. For example: + +```go +type Something struct { + name string +} + +// good +a := new(Something) + +// bad +b := new(Something) +b.name = "Mary" + +// use this syntax instead of b +c := &Something{ + name: "Mary", +} +``` + +To initialize complex structs, we can use a [functional option pattern](https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis). Please read [server.go](https://github.com/vdaas/vald/blob/main/internal/servers/servers.go) and [option.go](https://github.com/vdaas/vald/blob/main/internal/servers/option.go) for the reference implementation. +The options implementation should be separated as another file called `option.go` to improve the readability of the source code, and the method name should start with `With` word to differentiate it from other methods. + +### Variables and Constant + +The variable and the constant should be named as: + +- Use MixedCaps + + ```go + // bad + yamlProcessor := new(something) + + // good + yamlProcessor := new(something) + ``` + +- Use short form. + + ```go + // bad + yamlString := "something" + + // good + yamlStr := "something" + + // in some case it is acceptable and actually if it is easier to read + s := new(something) + ``` + +The variable and the constant name may lead to misunderstanding or confusion, so if the variable and constant name are different to understand, please write some comments, even if it is a private member. + +```go +// somebody may not understand this variable, so write a simple comment to the variable definition +sac := new(something) // signed access token +``` + +If the multiple variables and the constants have the same grouping, please use the grouping name as the prefix of the variable and constant name. + +```go +// Same group of variable (error), so add a prefix `Err` to each error variables +ErrInvalidCacherType = New("invalid cacher type") +// ErrXXXXXXX +``` + +### Methods + +In this section, rules also apply to the `function` (without receiver). The method name should be named as: + +- Use MixedCaps. + + ```go + // bad + func (s *something) someMethod() {} + + // bad + func (s *something) some_method() {} + + // good + func (s *something) someMethod() {} + ``` + +- Avoid using long function name. +- Do not use short form unless the function name is too long. + + ```go + // bad + func (s *something) generateRandomNumber() int {} + + // good + func (s *something) genRandNum() int {} + ``` + +- It should be understandable for everyone even if it is a private method. + +#### Getter and Setter + +The Getter and Setter are almost the same as other languages, but the naming convention of the Getter method is different from other languages. Instead of `GetVar1()`, the getter of `Var1` should be the same as the variable named `Var1()`. + +```go +// getter of the `signedTok` +func (s *something) SignedTok() string{ + return s.signedTok +} + +// setter of the `signedTok` +func (s *something) SetSignedTok(st string) { + s.signedTok = st +} +``` + +### Unused Variables + +An unused variable may increase the complexity of the source code. +It may confuse the developer hence introducing a new bug. +So please delete the unused variable. + +Generally, the unused variable should be reported during compilation, but in some cases, the compiler may not report an error. +This is an example of an unused variable declaration that does not cause a compilation error. + +```go +// In this case, this example are not using `port` field, but dose not cause a compilation error. +// So please delete `port` field of `server`. + +type server struct { + addr string + port int // you have to delete this field. +} + +// The `port` field of `server` is not used. +srv := &server { + addr: "192.168.33.10:1234", +} + +if err := srv.Run(); err != nil { + log.Fatal(err) +} +``` + +### Error handling + +All errors should define in [internal/errors package](https://github.com/vdaas/vald/blob/main/internal/errors). All errors should be start with `Err` prefix, and all errors should be handle if possible. + +Please use [internal/sync/errgroup](https://github.com/vdaas/vald/blob/main/internal/sync/errgroup) for synchronized error handling on multi-goroutine processing. + +### Error checking + +All functions return an `error` if the function can fail. +It is very important to ensure that error checking is performed. +To reduce human mistakes that miss the error checking, please check the error using the following style: + + + +
BadGood
+ +```go +err := fn() +if err != nil { + // handle error +} +``` + + + +```go +if err := fn(); err != nil { + // handle error +} +``` + +
+ +If you need the value outside the if statement, please use the following style: + + + +
BadGood
+ +```go +if conn, err := net.Dial("tcp", "localhost:80"); err != nil { + // handle error +} else { + // use the conn +} +``` + + + +```go +conn, err := net.Dial("tcp", "localhost:80") +if err != nil { + // handle error +} +// use the conn +``` + +
+ +### Logging + +We define our own logging interface in [internal/log package](https://github.com/vdaas/vald/blob/main/internal/log). +By default, we use [glg](https://github.com/kpango/glg) to do the logging internally. +We defined the following logging levels. + +| Log level | Description | Example situation | Example message | +| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| DEBUG | Fine-grained information for debugging and diagnosing the application.
Enabling this logging level in the production environment may cause performance issues. | An entry that will insert into the database with details.
An HTTP request and response will send and receive from the server with details. | User 1 will insert into the database, name: Mary, Age: 19.
An HTTP request will send to http://example.com, with the body:
key1:value1, key2:value2.
The response of the HTTP request: body: HTTPResponseBody | +| INFO | Normal application behavior to trace what is happening inside the application. | Inserted entry into the database without details.
HTTP requests are sent to the server without details.
The server is started or stopped. | User 1 is inserted into the database.
The HTTP request is sent to XXX server successfully. | +| WARN | The message that indicates the application may have an issue or occurring unusual situation,
but does not affect the application behavior.
Someone should investigate the warning later. | Failed to insert an entry into the database, but successful with the retry.
Failed to update the cache, and the cache is not important. | User 1 is successfully inserted into the database with retry,
retry count: 1, error: ErrMsg1, retry count: 2, error: ErrMsg2 | +| ERROR | The message indicates the application has a serious issue or,
represents the failure of some important thing going on in the application.
It does not cause the application to go down.
Someone must investigate the error later. | Failed to insert an entry into the database, with retry count exceeded.
Failed to update the cache, and the cache is not important. | User 1 is failed to insert in the database with errors:
retry count: 1, error: ErrMsg1, retry count: 2, error: ErrMsg2, .... | +| FATAL | The message indicates the application is corrupting or has a serious issue.
The application will go down after logging the fatal error.
Someone must investigate and resolve the fatality as soon as possible. | Failed to init the required cache during the application start. | | + +## Implementation + +This section includes some examples of general implementation widely used in Vald. +The implementation may differ based on your use case. + +### Functional Option + +The functional option pattern is widely used in Vald. +You can refer to [this section](#Struct-initialization) for more details of the use case of this pattern. + +We provide the following errors to describe the error to apply the option. + +| Error | Description | +| ------------------------ | ----------------------------------------------------------------------------------------- | +| errors.ErrInvalidOption | Error to apply the option, and the error is ignorable and the warning log will be printed | +| errors.ErrCriticalOption | Critical error to apply the option, the error cannot be ignored and should be handled | +| errors.ErrIgnoredOption | The option is ignored, and the debug log will be printed | + +We strongly recommend the following implementation to set the value using the functional option. + +If an invalid value is set to the functional option, the `ErrInvalidOption` error defined in the [internal/errors/option.go](https://github.com/vdaas/vald/blob/main/internal/errors/option.go) should be returned. + +The name argument (the first argument) of the `ErrInvalidOption` error should be the same as the functional option name without the `With` prefix. + +For example, the functional option name `WithVersion` should return the error with the argument `name` as `version`. + +```go +func WithVersion(version string) Option { + return func(c *client) error { + if len(version) == 0 { + return errors.NewErrInvalidOption("version", version) + } + c.version = version + return nil + } +} +``` + +We recommend the following implementation to parse the time string and set the time to the target struct. + +```go +func WithTimeout(dur string) Option { + func(c *client) error { + if dur == "" { + return errors.NewErrInvalidOption("timeout", dur) + } + d, err := timeutil.Parse(dur) + if err != nil { + return errors.NewErrInvalidOption("timeout", dur, err) + } + c.timeout = d + return nil + } +} +``` + +We recommend the following implementation to append the value to the slice if the value is not nil. + +```go +func WithHosts(hosts ...string) Option { + return func(c *client) error { + if len(hosts) == 0 { + return errors.NewErrInvalidOption("hosts", hosts) + } + if c.hosts == nil { + c.hosts = hosts + } else { + c.hosts = append(c.hosts, hosts...) + } + return nil + } +} +``` + +If the functional option error is critical, we should return `ErrCriticalOption` error instead of `ErrInvalidOption`. + +```go +func WithConnectTimeout(dur string) Option { + return func(c *client) error { + if dur == "" { + return errors.NewErrInvalidOption("connectTimeout", dur) + } + d, err := timeutil.Parse(dur) + if err != nil { + return errors.NewErrCriticalOption("connectTimeout", dur, err) + } + + c.connectTimeout = d + return nil + } +} +``` + +We need to handle the error returned from the functional option on the caller side. + +If the option fails to apply, an error wrapped with `ErrOptionFailed` defined in the [internal/errors/errors.go](https://github.com/vdaas/vald/blob/main/internal/errors/errors.go) should be returned. + +We recommend the following implementation to apply the options. + +```go +func New(opts ...Option) (Server, error) { + srv := new(server) + for _, opt := range opts { + if err := opt(srv); err != nil { + werr := errors.ErrOptionFailed(err, reflect.ValueOf(opt)) + + e := new(errors.ErrCriticalOption) + if errors.As(err, &e) { + log.Error(werr) + return nil, werr + } + log.Warn(werr) + } + } +} +``` + +### Constructor + +In Vald, the functional option pattern is widely used when we create an object. + +When setting the value with the functional option, the value is validated inside the option method. + +However, we may forget to set the required fields when creating the object; hence the target object will remain nil. +Therefore, we strongly suggest validating the object during initialization. + +If we forget to set the options method, an error will be returned so we can handle it properly. + +```go +func func New(opts ...Option) (Server, error) { + srv := new(server) + for _, opt := range append(defaultOptions, opts...) { + if err := opt(srv); err != nil { + werr := errors.ErrOptionFailed(err, reflect.ValueOf(opt)) + + e := new(errors.ErrCriticalOption) + if errors.As(err, &e) { + log.Error(werr) + return nil, werr + } + ue := new(errors.ErrIgnoredOption) + if errors.As(err, &ue) { + log.Debug(werr) + } else { + log.Warn(werr) + } + } + } + + if srv.eg == nil { + return nil, errors.NewErrInvalidOption("eg", srv.eg) + } + + return srv, nil +} +``` + +We also recommend that you use the default options and the unexported functional option to set the objects so that we cannot use them externally. + +```go +var defaultOptions = []Option { + func(s *server) error { + s.ctxio = io.New() + return nil + }, +} +``` + +## Program comments + +Program comments make it easier to understand the source code. +We suggest not writing many comments inside the source code unless the source code is very complicated and confusing; otherwise, we should divide the source code into methods to keep readability and usability of the source code. + +Everyone should comment on all the public objects on your source code, like public packages, interfaces, structs, methods, and even public constants and variables. +The `Godoc` will be generated based on the comment of the source code. + +## Documentation + +Documentation is generated from the program comments. Please refer to [Godoc](https://pkg.go.dev/github.com/vdaas/vald) for the program documentation. + +## Internal packages + +Vald implements its internal package to extend and customize the standard library and third-party library functionality. +We should use the internal package instead of the standard library to implement Vald. +Please refer to [Godoc](https://pkg.go.dev/github.com/vdaas/vald/internal) for the internal package document. + +## Dependency management and Build + +We should use `go mod tidy` to manage the `go.mod` file in the project. + +## Test + +The testing guideline has three essential rules for coding quality and readability: + +1. Use Table-Driven-Test +1. Keep code coverage over 85% + - test coverage != high testing quality, but low coverage means bad testing quality + - check with the following commands `go test -cover ./...` +1. Test all use cases and error cases + +### Table-Driven-Test + +Use table-driven tests with subtests to avoid duplicating code. + + + + + + + + + +
BadGood
+ +```go +## case 1 +host, port, err := net.SplitHostPort("192.0.2.0:8000") +if err != nil { + t.Errorf("error is not nil: %v", err) +} + +if want, got := "192.0.2.0", host; want != got { + t.Errorf("host is not equals. want: %s, but got: %s", want: %s, got) +} + +if want, got := "8000", port; want != got { + t.Errorf("port is not equals. want: %s, but got: %s", want: %s, got) +} + +## case2 +host, port, err = net.SplitHostPort("192.0.2.0:http") +if err != nil { + t.Errorf("error is not nil: %v", err) +} + +if want, got := "192.0.2.0", host; want != got { + t.Errorf("host is not equals. want: %s, but got: %s", want: %s, got) +} + +if want, got := "http", port; want != got { + t.Errorf("port is not equals. want: %s, but got: %s", want: %s, got) +} +``` + + + +```go +tests := []struct { + str string + wantHost string + wantPort string +} { + ## case 1 + { + str: "192.0.2.0:8000", + wantHost: "192.0.2.0", + wantPort: "8000", + }, + ## case 2 + { + str: "192.0.2.0:8000", + wantHost: "192.0.2.0", + wantPort: "http", + }, +} + +for _, tt := range tests { + t.Run(tt.str, func(tt *testing.T) { + host, port, err := net.SplitHostPort(tt.str) + if err != nil { + t.Errorf("error is not nil: %v", err) + } + if want, got := tt.wantHost, host; want != got { + t.Errorf("host is not equals. want: %s, but got: %s", want: %s, got) + } + if want, got := tt.wantPort, port; want != got { + t.Errorf("port is not equals. want: %s, but got: %s", want: %s, got) + } + }) +} +``` + +
+ +Table-Driven-Test makes it easy to add a new test case. + +We define the test case table as `map[string]func(*testing.T)test`, which is referred to as the test case name and the test case implementation `tt`. + +```go +tests := map[string]func(t *testing.T) test { + "test case name": func(tt *testing.T) test { + return test { + args: args { + host: "host", + port: "port", + }, + field: field { + timeout: 1 * time.Second, + }, + } + } +} +``` + +### Create a Table-Driven-Test + +#### Structures + +1. `args` structure + + If two or more arguments are passed to the method, create an `args` structure. If there is only one argument, do not create an `args` structure. + + ```go + type args struct { + host string + port string + } + ``` + +1. `field` structure + + If you create an object and test its methods, create a `field` struct if the object has two or more fields to initialize. If there is only one field, do not create a `field` structure. + + ```go + type field struct { + host string + port string + } + ``` + +1. `test` structure + + `test` structure has `args` and `field` structure and `checkFunc` function. + Create a field or an args structure if you need it. + The `checkFunc` function is used to check the return value of the function being tested. + + ```go + type test struct { + args args + field field + checkFunc func(t *testing.T, err error) + } + ``` + +#### Inputs + +1. test case name + + Test case names should be readable, meaningful, and understandable easily. + If you create a new test, the test name should be named based on the below-naming templates + - Success cases: + - Start with `success` or `{verb} success` or `success {verb}` + - End with the condition `when {condition}` or `with {condition} + - e.g.: `set success when the value is default value` + - Fail/Error cases: + - Start with `fail` or `{verb} fail` or `fail {verb}` + - End with the condition `when {condition}` or `with {condition} + - e.g.: `fail option setting when value is invalid value(string)` + - Return cases: + - If the test case do not match with `Success` or `Fails`, please use `Return` pattern. + - Start with `Returns {Object}` + - End with the condition `when {condition}` or `with {condition} + - e.g.: `return invalid error when the input is invalid` + +1. testing arguments + + Input arguments for testing should be a meaningful value. + We should test with more realistic values to produce a more realistic testing result. + + For example, to test the function with `host` argument, you should set your hostname (e.g., `vald.vdaas.com`) as the input value to the `host` argument. + +Example: + +```go +type args struct { + host string + port string +} + +type field struct { + timeout time.Duration +} + +type want struct { + err error +} + +type test struct { + name string + args args + field field + want want + checkFunc func(err error) error +} + +defaultCheckFunc := func(w want, err error) error { + if err != w.err { + return errors.Errorf("got error: %v, want: %v", err, w.err) + } +} + +tests := []test { + { + name: "success send when host and port are correct value", + args: args { + host: "vdaas.vald.org", + port: "80", + }, + field: field { + host: "vdaas.vald.org", + port: "80", + }, + want: want { + err: nil, + }, + }, +} + +for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + checkFunc = defaultCheckFunc + if test.checkFunc != nil { + checkFunc = test.checkFunc + } + + c := client { + timeout: test.field.timeout, + } + err := c.Send(test.args.addr, test.args.txt) + if err := checkFunc(err); err != nil { + tt.Errorf("error = %v", err) + } + }) +} + +``` + +### Generate test code + +We implement our own [gotests](https://github.com/cweill/gotests) template to generate test code. +If you want to install `gotest` tools, please execute the following command under the project root directory. + +```bash +make gotests/install +``` + +If you use the following command to generate the missing test code. + +```bash +make gotests/gen +``` + +After executing the command above, the file `*target*_test.go` will be generated for each Go source file. +The test code generated follows the table-driven test format. +You can implement your test code under the `tests` variable generated following the table-driven test format. + +### Customize test case + +We do not suggest modifying the generated code other than the `tests` variable +Still, in some cases, you may need to change the generated code to meet your requirement, for example: + +1. init() function + + init() function is executed automatically before the test is started. + You may need to initialize some singleton before running your test cases. + For example, Vald uses [glg](https://github.com/kpango/glg) library for logging by default; if the logger is not initialized before the test, the nil pointer error may be thrown during the test is running. + You may need to implement `init()` function like: + + ```go + func init() { + log.Init() + } + ``` + + And place it on the header of the test file. + +1. goleak option + + The generated test code will default use the [goleak](https://github.com/uber-go/goleak) library to test if there is any goroutine leak. + Sometimes you may want to skip the detection; for example, Vald uses the [fastime](https://github.com/kpango/fastime) library, but the internal goroutine is not closed due to the needs of the library. + To skip the goleak detection, we need to create the following variable to store the ignore function. + + ```go + var ( + // goroutine leak is detected by `fastime`, but it should be ignored in the test because it is an external package. + goleakIgnoreOptions = []goleak.Option{ + goleak.IgnoreTopFunction("github.com/kpango/fastime.(*fastime).StartTimerD.func1"), + } + ) + ``` + + And use it in `VerifyNone()` in the test case. + + ```go + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + // modify the following line + defer goleak.VerifyNone(tt, goleakIgnoreOptions...) + ``` + + In Vald, we implemented [internal goleak package](https://github.com/vdaas/vald/blob/main/internal/test/goleak/goleak.go) and wrap the goleak validation logic to ignore the common goleak functions. + In test implementation, we can easily import the internal goleak package and ignore all the necessary goleak functions by default. + + ```go + // import internal goleak package + import ( + "github.com/vdaas/vald/internal/test/goleak" + ) + + ... + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + // use internal goleak to ignore necessary function by default + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + ``` + +1. goleak usage + + There are two methods for valid goroutine leak: + 1. Use `goleak.VerifyNone()` to validate it on each test cases. + 1. Use `goleak.VerifyTestMain()` to validate it on each package. + + By default, the goroutine leak validation is executed in each test case. + + ```go + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + ``` + + In some cases, it may not work as some cleanup or asynchronous processes remaining in the background, and validating it on the test case using `goleak.VerifyNone()` may cause a false alarm. + + To resolve it, we can consider using `goleak.VerifyTestMain()` to validate goleak when all test cases are passed in each package. + + ```go + // implement TestMain and verify it at package level + func TestMain(m *testing.M) { + goleak.VerifyTestMain(m) + } + + ... + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + // use VerifyTestMain() above to detect goroutine leak + // defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + ``` + + Please note that the `TestMain()` can only implement once in each package, you may need to find the right place to implement this function on the package. + +1. Defer function + + By default, the template provides `beforeFunc()` and `afterFunc()` to initialize and finalize the test case, but in some cases, it may not support your use case. + For example, `recover()` function only works in `defer()` function. + If you need to use the `recover()` function to handle the panic in your test code, you may need to implement your custom `defer()` function and change the generated test code. + + For example: + + ```go + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + defer goleak.VerifyNone(tt, goleak.IgnoreCurrent()) + + // insert your defer function here + defer func(w want, tt *testing.T) { + // implement your defer func logic + if err:= recover(); err != nil { + // check the panic + } + }(test.want, tt) + + if test.beforeFunc != nil { + test.beforeFunc(test.args) + } + // generated test code + ``` + +1. Unused fields + + By default, the template provides the `fields` structure to initialize an object of the test target. + But in some cases, not all `fields` are needed, so please delete the unnecessary fields. + For example, the following struct and the corresponding function: + + ```go + type server struct { + addr string + port int + } + func (s *server) Addr() string { + return s.addr + } + ``` + + And the generated test code is: + + ```go + func Test_server_Addr(t *testing.T) { + type fields struct { + addr string + port int + } + type want struct { + // generated test code + ``` + + Since the `port` variable is not used in this test case, you can delete the `port` definition in the test case. + + ```go + func Test_server_Addr(t *testing.T) { + type fields struct { + addr string + // port int <-- this line should be deleted + } + type want struct { + // generated test code + ``` + +1. Context + + If the target function accepts context as an input argument, the test code generated will include it in `args`. + + ```go + type args struct { + ctx context.Context + } + ... + ``` + + But in test implementation, it is hard to manage the context lifecycle. + We want to guarantee the context is closed after each test is executed to avoid any missing termination of the process. + + In Vald, we suggest customizing the test implementation from the generated test code to create the context and manage the lifecycle in every test. + + We can remove the context from the `args` struct and create the context in the test execution code. + + ```go + // we can remove the ctx from args list + type args struct { + // ctx context.Context + } + ... + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + // create context here + ctx, cancel := context.WithCancel(context.Background()) + // handle the context lifecycle by canceling the context after the test is executed + defer cancel() + + ... + + // use the context created above + got, gotErr := someFunc(ctx) + + ... + ``` + +1. Struct initialization + + By default, when testing the function of the struct, the target struct initialization is implemented by setting the data from the `fields` defined in the test case. + This initialization method has a few disadvantages: + 1. When there are many fields in the struct, it is hard to set them all. + 1. The default value is the zero value of the type, not the struct default value from the struct initialization function. + + To resolve these problems, we can modify the test implementation to use the struct initialization function instead of setting the struct fields on the test cases. + + For example, the current implementation may look like this: + + ```go + func Test_server_SaveIndex(t *testing.T) { + ... + + // all the fields to initialize the target struct + type fields struct { + name string + ip string + ngt service.NGT + eg errgroup.Group + streamConcurrency int + } + + ... + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + ... + + // we initialize the target struct using fields defined in test case + s := &server{ + name: test.fields.name, + ip: test.fields.ip, + ngt: test.fields.ngt, + eg: test.fields.eg, + streamConcurrency: test.fields.streamConcurrency, + // we may have more fields defined in the struct, + // and we need to set them all here + } + + gotRes, err := s.SaveIndex(test.args.ctx, test.args.in1) + ... + ``` + + We can customize the implementation to use the struct initialization function. + + ```go + func Test_server_SaveIndex(t *testing.T) { + ... + + // we customize the fields to initialize the target struct + type fields struct { + // options to configure server struct + srvOpts []Option + + // options to configure ngt + svcCfg *config.NGT + svcOpts []service.Option + } + + ... + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + ... + + // initialize required fields of server struct + eg, _ := errgroup.New(ctx) + ngt, err := service.New(test.fields.svcCfg, append(test.fields.svcOpts, service.WithErrGroup(eg))...) + if err != nil { + tt.Errorf("failed to init ngt service, error = %v", err) + } + + // we use the struct initialization function instead of setting the fields, + // by combining the use of the functional option, we can configure the struct, + // and the default values of the struct will be set automatically if we are not defining it. + s, err := New(append(test.fields.srvOpts, WithNGT(ngt), WithErrGroup(eg))...) + if err != nil { + tt.Errorf("failed to init server, error= %v", err) + } + + gotRes, err := s.SaveIndex(test.args.ctx, test.args.in1) + ... + ``` + +### Parallel test + +In Vald, we use parallel tests to accelerate the execution of tests by default. +There are two layers of enabling parallel tests. + +1. Parallel for the test function +1. Parallel for the subtests in the test function + +The generated test case will enable these two parallel modes by default. It is implemented by: + +```go +func Test_server_CreateIndex(t *testing.T) { + t.Parallel() // parallel for the test function + type args struct { + + ... + + for _, tc := range tests { + test := tc + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() // parallel for subtests + ... +``` + +Be careful of using the parallel test, avoid using share object used in the test, to avoid race detector to detect the race in test and fail the test. + +### Helper function + +In golang testing package, they defined the helper functions in testing types to indicate the current function is a test helper function and skip printing the file and line information to the output. + +In Vald, we can apply it to the different helper functions like `beforeFunc()` or `afterFunc()` defined in the test case struct. + +```go +type test struct { + ... + beforeFunc func(*testing.T) // helper function to initialize testing + afterFunc func(*testing.T) // helper function to cleanup +} + +tests := []test { + { + name: "send success when host and port are correct value", + args: args { + host: "vdaas.vald.org", + port: "80", + }, + field: field { + host: "vdaas.vald.org", + port: "80", + }, + beforeFunc: func(t *testing.T) { + t.Helper() // indicate the current function is helper function + /* initialization logic of testing environment */ + }, + want: want { + err: nil, + }, + afterFunc: func(t *testing.T) { + t.Helper() // indicate the current function is helper function + /* cleanup logic */ + } + }, +} +``` + +### Using Mock + +In Vald, we use a lot of external libraries, and there are a lot of dependencies between libraries. + +As a result, due to the complexity, it has become hard to determine whether or not to mock dependencies. + +#### Condition + +When dependencies have the following factor, you can decide to mock the dependencies. + +- Incomplete implementation +- I/O + - e.g., Network access, disk operation, etc. +- Hardware dependent + - e.g., CPU, Memory usage, disk I/O, etc. +- Hard to create the error of dependencies +- Difficult to initialize + - e.g., Random number and time, file I/O initialization, environment dependent, etc. +- The test result may change in each runtime + - e.g., the only test result may change in each runtime, System call inside implementation, etc. + +#### Risk + +Before applying mock to the object, you should understand the following risks. + +- We **do not** know whether the dependencies are correctly implemented or not. +- We cannot notice the changes in dependencies. + +#### Implementation + +The implementation of the mock object should be: + +- Same package as the mock target. +- File name is `xxx_mock.go` +- Struct name is `Mock{Interface name}` + +For example, we decided to mock the following implementation `Encoder`. + +```go +package json + +type Encoder interface { + Encode(any) ([]byte, error) +} +``` + +```go +type encoder struct { + encoder json.Encoder +} + +func (e *encoder) Encode(obj any) ([]byte, error) { + return e.encoder.Encode(obj) +} +``` + +The following is an example of mock implementation: + +```go +package json + +type MockEncoder struct { + EncoderFunc func(any) ([]byte, error) +} + +func (m *MockEncoder) Encode(obj any) ([]byte, error) { + return m.EncodeFunc(obj) +} +``` + +The following is an example implementation of test code to create the mock object and mock the implementation. + +```go +tests := []test { + { + name: "returns (byte{}, nil) when encode success" + fields: fields { + encoding: &json.MockEncoder { + EncoderFunc: func(any) ([]byte, error) { + return []byte{}, nil + }, + }, + } + ...... + } +} +``` + +## Testing policy + +In Vald, the implementation code is divided into different packages based on the context of the implementation. + +Based on the context of the package, we decided to apply different testing policies on each package to fit their needs. + +### Internal package + +Internal package `./internal` contains internal used library code. It is written to be reusable to solve common problems. + +It is very important to make sure that the commonly used library code works correctly, so we decided to apply C1 coverage (branch coverage) to the `./internal` package. + +C1 coverage means to cover the test on each condition and ensure each condition (true and false) is evaluated on the test code. + +### Pkg package + +Pkg package `./pkg` contains the business logic of the component in Vald. + +In the `./pkg` package, the business logic implementation is divided into each component, and then in each component package, the specific business logic is divided into different packages. + +Here is a common example of the package structure in `./pkg` package. + +- ./pkg/{component}/config +- ./pkg/{component}/handler +- ./pkg/{component}/router +- ./pkg/{component}/service +- ./pkg/{component}/usecase + +For example, the implementation of the use case layer of the Vald LB gateway will be `./pkg/gateway/lb/usecase`. + +Since each package has its purpose, we decided to apply different strategies to each package to fit its purpose. + +| Package | Testing strategy | +| ------------------------------ | -------------------------------------------------------------------------------------------------------------- | +| ./pkg/{component}/config | Only test if config file can be read and the corresponding value is set | +| ./pkg/{component}/handler/rest | No need to test | +| ./pkg/{component}/handler/grpc | Basically no test, other than testing bugfix (Detailed tests of business logic should be written in E2E tests) | +| ./pkg/{component}/router | No need to test | +| ./pkg/{component}/service | Test only interface functions | +| ./pkg/{component}/usecase | Test only New() function | + +For the rest of the `./pkg` packages, we decided to implement the unit test for the exported function only. + +Please follow the [unit test guideline](/docs/v1.8/unit-test-guideline) for more details on how to implement good unit test. diff --git a/content/docs/v1.8/contributing/contributing-guide.md b/content/docs/v1.8/contributing/contributing-guide.md new file mode 100644 index 000000000..9ccf0a5ee --- /dev/null +++ b/content/docs/v1.8/contributing/contributing-guide.md @@ -0,0 +1,206 @@ +--- +title: "Contributing Guide_v1.8/Contributing" +date: 2026-09-16T04:40:18Z +draft: false +weight: 100 +description: Contribute guideline for the Vald project +menu: + contributing: + parent: Contributing +--- + +# Contributing to Vald + +Vald is an open source project. + +We appreciate your help! + +## Table of Contents + +- [Contributing Issue](#contributing-issue) + - [Bug Report](#bug-report) + - [Proposal](#proposal) + - [Feature Request](#feature-request) + - [Security Issue Report](#security-issue-report) +- [Contributing Source Code](#contributing-source-code) + - [Before contributing source code](#before-contributing-source-code) + - [How to contribute source code](#how-to-contribute-source-code) + - [How to contribute unit test code](#how-to-contribute-unit-test-code) + - [Branch naming convention](#branch-naming-convention) + +## Contributing Issue + +We use [GitHub Issues](https://github.com/vdaas/vald/issues) to track issues within this repository. +If you can determine the problem you are facing is a bug or issue, you can submit the issue. + +When unsure whether you have found a bug or security-related issue, please consider asking in the [Vald Slack](https://join.slack.com/t/vald-community/shared_invite/zt-db2ky9o4-R_9p2sVp8xRwztVa8gfnPA) first. +If the behavior you are seeing is confirmed as a bug or issue, it can be re-raised in the issue tracker. + +### Bug Report + +A bug is a demonstrable problem that produces an incorrect result or behaves in unintended ways.
+ +Bug reports are helpful for developers who maintain the Vald project.
+A good bug report should not leave others needing to constantly ask you for more information.
+Please try to write as detailed as possible in your bug report. + +When filing an issue, make sure to answer these five questions: + +1. What version of Vald are you using (Vald Docker image version)? +2. What operating system and processor architecture are you using? +3. What did you do? +4. What did you expect to see? +5. What did you see instead? + +Please submit the bug report [here](https://github.com/vdaas/vald/issues/new?assignees=&labels=type%2Fbug%2C+priority%2Fmedium%2C+team%2Fcore&template=bug_report&title=) + +### Proposal + +The Vald is being developed based on the design-driven process.
+The significant change to the library or the architecture should be discussed first. + +We may ask for detailed documentation before your proposal is accepted.
+Your proposal will be reviewed, discussed, and accepted or declined. + +Please submit the proposal [here](https://github.com/vdaas/vald/issues/new?assignees=&labels=type%2Ffeature%2C+priority%2Flow%2C+team%2Fcore&template=feature_request&title=) + +### Feature Request + +Feature request is welcome. + +Before opening an issue, please make sure your idea fits the project.
+You can request the feature and convince the project maintainers to accept your feature request. + +Please provide the problem and solution associated with the feature request in as much detail as possible. + +NOTE: If you’d like to contribute to the new feature which may affect the current Vald architecture or design, you should discuss it with the Vald team first. + +Please submit the feature request [here](https://github.com/vdaas/vald/issues/new?assignees=&labels=type%2Ffeature%2C+priority%2Flow%2C+team%2Fcore&template=feature_request&title=) + +### Security Issue Report + +The Vald team and community have a serious concern about security issues. + +Sensitive security-related issues should be reported to the security channel in [Vald Slack](https://join.slack.com/t/vald-community/shared_invite/zt-db2ky9o4-R_9p2sVp8xRwztVa8gfnPA). + +We appreciate your efforts to disclose your findings.
+If the security issue is caused by a third-party module, we will contact the module owner and ask for a fix. + +We will consider using another third-party module if the vulnerable module is not actively maintained anymore. + +Please submit the security issue report [here](https://github.com/vdaas/vald/issues/new?assignees=&labels=type%2Fsecurity%2C+priority%2Fmedium%2C+team%2Fcore%2C+team%2Fsre&template=security_issue_report&title=) + +## Contributing Source Code + +Please refer to our [development documentation](/docs/v1.8/contributing/development) for information on setting up a development environment. + +### Before contributing source code + +Follow these steps to make a contribution to any of our open source repositories: + +1. Ensure that you have completed our [CLA Agreement](https://cla-assistant.io/vdaas/vald) +2. Set your name and email (these should match the information on your submitted CLA) + + ```bash + git config --global user.name "Your Name" + git config --global user.email "your_email@example.com" + ``` + +### How to contribute source code + +1. Fork the repository ( https://github.com/vdaas/vald/fork ) +2. Create your feature branch (git checkout -b [`[type]/[area]/[description]`](#Branch-naming-convention)) +3. Commit your changes on your branch (git commit -am 'Add some feature') +4. Run tests (make test) +5. Push to the forked branch (git push origin my-new-feature) +6. Create new Pull Request + +Each pull request and commit should be small enough to address a single purpose. + +Your pull request has a higher chance of being accepted if: + +- Your pull request includes tests + +- Your pull request includes benchmark results + +- Your pull request is small and focused with a clear message that conveys the intent of your change + +### How to contribute unit test code + +To contribute unit test code, the steps are almost the same as [contribute source code](#how-to-contribute-source-code), but there are some extra steps when adding a test function that has not been added before: + +1. Execute `make gotests/gen` command under Vald repository +1. Move the test function which you would like to write above the placeholder `// NOT IMPLEMENTED BELOW` +1. Remove the comment markers and write the test function + +Vald implements unit test code to ensure the quality of Vald. +Each implementation file comes with its unit test file, with `*_test.go` suffix. +In Vald, unit test files are looks like: + +```go +func Test_implementedTest(t *testing.T) { + // this unit test is implemented +} + +// NOT IMPLEMENTED BELOW +// +// func Test_notImplementedTest(t *testing.T) { +// // this unit test function is not implemented yet +// } +``` + +Vald defines a placeholder `// NOT IMPLEMENTED BELOW` to separate the implemented unit test from the unimplemented unit test. +Everything before the placeholder are already implemented, and everything below are not implemented yet. + +If no test functions are implemented in the test file, the placeholder will be placed below the package definition. + +```go +package test + +// NOT IMPLEMENTED BELOW +// func Test_unimplementedTest(t *testing.T) { +// // this unit test function is not implemented yet +// } + +// func Test_unimplementedTest2(t *testing.T) { +// // this unit test function is not implemented yet +// } +``` + +If all test functions are implemented, the placeholder will be placed on the bottom of the file. + +```go +package test + +func Test_implementedTest(t *testing.T) { + // this unit test is implemented +} + +func Test_implementedTest2(t *testing.T) { + // this unit test is implemented +} + +// NOT IMPLEMENTED BELOW +``` + +
+This implementation structure is generated by the make gotests/gen command for all unit test files by applying the custom unit test template using gotests. +
+When the test template is updated, the generated unit test functions will not be updated accordingly, so we need this unit test structure to help us regenerate the implementation with the latest template. +For the implemented unit test, we cannot deal with it by executing make gotests/gen command. +But we don't have to update implemented test soon. +
+ +### Branch naming convention + +Name your branches with prefixes: `[type]/[area]/[description]` + +| Field | Explanation | Naming Rule | +| :---------- | :------------------------------------ | :------------------------------------------------------------------------------------------------------------------------ | +| type | The PR type | Accepted PR types: feature, bug, refactoring, benchmark, security, documentation, dependencies, ci, test, etc. | +| area | Area of context | The area of PR can be gateway, agent, agent-sidecar, lb-gateway, or etc... | +| description | Summarized description of your branch | The description must be hyphenated. Please use [a-zA-Z0-9] and hyphen as characters, and do not use any other characters. | + +(\*) If you changed more than one area, please list each area with `-`. + +For example, when you add a new feature for internal/servers, the name of the branch will be `feature/internal/add-new-feature-for-servers`. diff --git a/content/docs/v1.8/contributing/development.md b/content/docs/v1.8/contributing/development.md new file mode 100644 index 000000000..c0f8fcd25 --- /dev/null +++ b/content/docs/v1.8/contributing/development.md @@ -0,0 +1,81 @@ +--- +title: "Development_v1.8/Contributing" +date: 2026-09-16T04:40:18Z +draft: false +weight: 200 +description: Basic development guide for the Vald project +menu: + contributing: + parent: Contributing +--- + +# Development documentation + +This document describes how to set up the development environment and how to develop Vald. + +## Set up environment + +### Prerequisites + +#### OS + +- When using Docker related environment, you can use any OS that supports Docker. +- When using native environment, `Linux` is required. + +#### Architecture + +`amd64` is recommended because `NGT`, the vector search library we use, is optimized for `amd64`. +But you can also build and test `Vald` on `arm64` with the same way as described below. + +### Devcontainer + +This is the easiest way to start developing `Vald`. You can just open our [devcontainer.json](https://github.com/vdaas/vald/blob/main/.devcontainer/devcontainer.json) with `VS Code` and go. + +### Other + +We don't officially have a setup documentation for now, but you can take a look at the [`Dockerfile`](https://github.com/vdaas/vald/blob/main/dockers/dev/Dockerfile). +That's everything you need to build and test `Vald`, so you can use it as a reference. + +> If you would like to use the `Dockerfile` directly, please note that `docker-in-docker` environment is required to run our E2E tests. +> In devcontainer, [`VS Code` handles it for us](https://github.com/devcontainers/features/tree/main/src/docker-in-docker). + +## Run tests + +### Unit tests + +The command below will run all the unit tests. + +```bash +make test +``` + +### E2E tests + +The steps below will deploy a Vald cluster to the local `k3d` cluster and run the E2E tests. + +1. Change `example/helm/values.yaml` to `dimensions: 784` and `distance_type: l2`. +2. Run the commands below. + +```bash +# Download the dataset +make hack/benchmark/assets/dataset/fashion-mnist-784-euclidean.hdf5 + +# Start k3d +make k3d/start + +# Wait for a while until the cluster is ready +# You might want to use k9s for this + +# Deploy Vald +make k8s/vald/deploy HELM_VALUES=example/helm/values.yaml + +# Wait for a while until the deployment is ready + +# Run E2E tests +make e2e E2E_WAIT_FOR_CREATE_INDEX_DURATION=3m + +# The result will be shown in three minutes or so + +# Delete the cluster +make k8s/vald/delete +``` diff --git a/content/docs/v1.8/contributing/reviewer-guideline.md b/content/docs/v1.8/contributing/reviewer-guideline.md new file mode 100644 index 000000000..2725b3c43 --- /dev/null +++ b/content/docs/v1.8/contributing/reviewer-guideline.md @@ -0,0 +1,92 @@ +--- +title: "Reviewer Guideline_v1.8/Contributing" +date: 2026-09-16T04:40:18Z +draft: false +weight: 0 +description: "" +menu: + contributing: + parent: Contributing +--- + +# Guideline for the Pull Request reviewers + +This document serves as a guideline for those reviewing Pull Requests. + +This guideline is intended for internal developers. A separate guideline for external contributors is planned. + +## Purpose + +The purpose of this guideline is to clarify what reviewers should be aware of and to optimize communication, thereby facilitating efficient development of Vald. + +## Preparation + +This guideline assumes that the content of the Pull Request is appropriate. + +Consider splitting the Pull Request if the changes are extensive, have multiple intentions, or if the tests are insufficient. This makes it easier to review. + +Regarding the amount of change, opinions may vary, but in this project, we aim to keep the implementation of logic under 1000 lines, excluding automatically generated code such as Protocol Buffers. + +## Review request + +In this project, merging a Pull Request requires approval from two people. If you want a specific person to review, assign them explicitly; otherwise, assign random people. + +### Request notification + +People may notice requests through different means, but in this project, we prioritize the following methods of communication: + +1. If you are available to communicate with us via online meeting systems like Zoom, please feel free to notify us directly. +1. Send a mention in some way. For example, we use [Slack](https://join.slack.com/t/vald-community/shared_invite/zt-db2ky9o4-R_9p2sVp8xRwztVa8gfnPA) and we have [X account](https://twitter.com/vdaas_vald). +1. Email from GitHub is automatically sent when someone is assigned. + +If the reviewer does not notice the request, we follow the same priority for re-notifying. + +#### When Requested for Review + +Unless there are high-priority issues, you are in a meeting, or deeply focused on development, start the review immediately. + +## Approve condition + +- The content of the Pull Request is understood. +- The content can be understood at a glance. + - If it cannot be understood at a glance, request additional comments. +- There are no deficiencies in the test cases. +- The quality is suitable for release. + - Compatibility + - Security + - Performance + +Regarding quality, while there is always room for improvement, if the quality is sufficient for release, it is acceptable to approve. If there is potential for improvement, suggest it in the comments, and let the implementer decide whether to address it in the same Pull Request or a separate one. + +### What Reviewers Should Do + +- If the title or description is unclear, ask questions before reviewing the code. +- If any part of the code is not understood after a brief review, ask for clarification or additional comments instead of trying to understand it yourself. +- Verify if the test cases are sufficient. +- Approve immediately if there are no comments. +- If interrupted, respond even if the review is incomplete. +- Ignore other reviewers' comments. + +### What Reviewers Should Not Do + +- Try to decipher something that is not immediately understandable. +- Close the browser or tab or start something else if it is not understood. +- Attempt to verify the implementation in detail. + +### Communication Methods + +All communications do not need to be completed on GitHub. Using Slack or Zoom can make the review process more efficient. + +The following criteria often determine the choice of communication tool: + +- On GitHub Mainly used for specific questions or comments about the code. If the exchange is unsuitable for comments, use Slack or Zoom. +- On Slack, used for brief exchanges. If the discussion is likely to be lengthy or complex, switch to Zoom. +- On Zoom Used when more complex communication is necessary. + +Regardless of where communication takes place, ensure that the results are documented on GitHub or in the code as a record of the communication. + +## Effect measurement + +Once a month, a report is created at [Vald Issues](https://github.com/vdaas/vald/issues). + +This report includes the time taken from when a Pull Request is opened to when it is merged and a summary of this data. Regularly reflect on this information. diff --git a/content/docs/v1.8/contributing/unit-test-guideline.md b/content/docs/v1.8/contributing/unit-test-guideline.md new file mode 100644 index 000000000..3e4bd6365 --- /dev/null +++ b/content/docs/v1.8/contributing/unit-test-guideline.md @@ -0,0 +1,217 @@ +--- +title: "Unit Test Guideline_v1.8/Contributing" +date: 2026-09-16T04:40:18Z +draft: false +weight: 400 +description: Unit test guideline for Vald project +menu: + contributing: + parent: Contributing +--- + +# Unit Test Guideline + +## About + +The goal of the unit test is to check whether the unit is implemented correctly in various cases. +A good unit test is checking various cases we do not expect as supposed usage. +When we create the unit test for each function or package, we have to consider test coverage, not only code coverage. +This guideline will help you to create good unit tests. + +## Unit Test + +Before considering the unit test, we should define what the unit is. +A unit is a set of procedures or functions in a procedural or functional language. +Also, it is a class, and its needed classes, in an object or object-oriented language. +Unit testing validates the basic units of the program in isolation. + +## Guideline + +### Strategy + +Before creating the unit tests, we must know the essentials of strategy. + +1. Know what you are testing + - Purpose/Consumers of target function or classes or etc / designed by defensive contract program. +1. Test behaviors and results, not implementation + - Ensures that tests only fail when there is an actual effect and not due to internal changes. +1. Test one thing at a time + - Each test should have a clear, concise, singular objective. +1. Make tests readable and understandable + - Hold test code to a similar standard as production code. +1. Make tests deterministic + - A test should pass all the time or fail all the time until fixed. +1. Make tests independent and self-sufficient + - Setup, execution, and verification steps in a given test should not depend on running other tests. To keep unit tests simple, fast running, and easy to debug, it may be necessary to isolate the class under test. +1. Repeat yourself when necessary + - It is okay to violate the 'do not repeat yourself' principle if it makes tests simpler and easier to read. +1. Measure code coverage but focus on test coverage + - Do not simply archive code coverage. + +### Test case + +#### Basic + +It is not perfect for improving test coverage, but we should first try to cover all codes. +To cover all code, the basic way is to create test cases to complete all branches in the target unit. + +Let's see the below function. + +```go +func calcSum(val ...int32) (sum int32) { + if len(val) == 0 { + return + } else { + for _, v := range val { + sum += v + } + return + } +} +``` + +When the above function is given, we should create 2 tests to achieve 100% code coverage at least. + +- When val is not given + - In other words, the default value is given +- When val(int32) is given + - `var val int32 = 1` + +It seems enough for the given function, but we have to take care of some remaining test cases to improve test coverage. +There is at least one more test case to complete test coverage in this case. + +- When val([]int32) is given + - `var val []int32 = {1, 2}` + - `var val []int32 = {math.MaxInt32, -1}` + - `var val []int32 = {math.MaxInt32, 0}` + - `var val []int32 = {math.MaxInt32, 1}` + - `var val []int32 = {math.MinInt32, -1}` + - `var val []int32 = {math.MinInt32, 0}` + - `var val []int32 = {math.MinInt32, -1}` + - `var val []int32 = {math.MaxInt32, math.MaxInt32}` + - `var val []int32 = {math.MinInt32, math.MinInt32}` + +That is the focus on test coverage. + +Therefore, we should consider all cases to improve test coverage for the target unit. +To enhance test coverage, the basic but critical thinking way is thinking about input patterns. +It is not only a single input, but also multi inputs. + +If a function or method accepts multiple inputs, we should try to create test cases to cover all the inputs. + +(Note: The below `calcSum()` is different function from `calcSum(val ...int32)` which we mention before.) + +```go +func calcAverageDiff(val1 []int32, val2 []int32) (diff float64) { + var ave1, ave2 float64 + if len(val1) == 0 && len(val2) == 0 { + return + } + if len(val1) != 0 { + ave1 = float64(calcSum(val1)) / float64(len(val1)) + } + if len(val2) != 0 { + ave2 = float64(calcSum(val2)) / float64(len(val2)) + } + + diff = math.Abs(ave1 - ave2) + return +} +``` + +When `calcAverageDiff()` is given, the test patterns are below: + +| len(val1) | len(val2) | option | +| :-------: | :-------: | :------------------------------------------------ | +| 0 | 0 | - | +| 0 | >0 | - | +| >0 | 0 | - | +| >0 | >0 | ave1 > ave2 | +| >0 | >0 | ave1 < ave2 | +| >0 | >0 | ave1 = ave2 | +| >0 | >0 | ave1 = {math.MinInt32}, ave2 = {math.MaxInt32} | +| >0 | >0 | ave1 = {math.MaxInt32, math.MaxInt32}, ave2 = {0} | + +At a glance, one of the last 2 patterns is enough, but these will help us notice the dependency bug. +It will avoid unexpected errors due to updated dependencies. + +You have to create unit tests for error patterns as the same as success patterns. + +#### Advanced + +##### Robust boundary test + +The previous section is about the basic test cases. +The (robust) boundary test should be applied to cover more test coverage. +A boundary test tests values on or near the boundaries of allowed inputs. + +```go +func AgeTest(val int32) (ok bool) { + if val >= 20 && val < 65 { + ok = true + } + return +} +``` + +When the above function is given as a target, the minimum number of tests based on the "Robust Boundary" test is 7. + +- the input `val` cases: 19, 20, 21, 30, 64, 65, 66 + +The robust boundary test requires 6N+1 test cases (N is the number of inputs for the target function). +Increasing the input arguments will also increase the number of required test cases. +Sometimes, it requires a lot of resources to create/maintain tests. +So, you may test the critical cases using boundary values and have a sense of purpose. + +#### Equivalence Class Testing + +Equivalence Class Testing groups the values together such that all members of the group are considered the same and tests one value from each. + +```go +func AgeTest(val int32) (ok bool) { + if val >= 20 && val < 65 { + ok = true + } + return +} +``` + +When the above function is given as a target, you have to create three groups and pick one value from each group. + +| Desc | Input range | Chose Input | +| :----------: | :-----------: | :---------: | +| Under 20 | {0, ..., 19 } | 5 | +| worker range | {20, ..., 64} | 29 | +| Senior | {65, ... } | 72 | + +### Vald Style + +In the Vald, we create unit tests based on the basic test case. +And, you may also create unit tests based on robust boundary tests or equivalence class tests if needed. + +But, we have to take care the Vald is developed using Go. +As you know as Go has many coding features as other languages. +One of the features is that Go will convert a single value to a slice value when the Function or Method receives a variadic argument (e.g., `...[]int`, `...[]string`, `...any`, or etc.) as the input. + +And we apply the table-driven test for running unit tests. +For example, when we create the unit test of `func getMeta(...[]int)`, the test code will be more complex than other functions' tests which don't use variadic argument as the input, if we create the test for all input patterns. +So, we define the basic unit case slightly different from [the basic test case](#Basic). + +This change is very clear and you can apply it easily. +Our basic test case depends on the type of two variadic arguments. + +1. When input is `...any` + - We have to write all test cases which satisfy `...any` as same as a [basic test case](#Basic). For example, `val = 1`, `val = "input"`, `val = []float64{2020.12}` and so on. +1. When input is not `...any` but `...[]int`, `...[]string` or etc. + - We have to create only slice pattern test cases, which is the same as not creating test cases with a single value. + - We should test with boundary cases, for example, we should test with `val = []int{math.MaxInt64()}` when the input value is `...[]int`. + +Summarize Vald unit test guideline: + +- Apply basic test case, but take care of input variable pattern, in particular, the variadic argument (`...any` or not) +- Apply robust boundary tests, including edge cases (e.g., `math.MaxInt64()`) +- Apply equivalence class testing when needed. + +## Coding Style + +Please refer [here](/docs/v1.8/contributing/coding-style#Test) diff --git a/content/docs/v1.8/overview/_index.md b/content/docs/v1.8/overview/_index.md new file mode 100644 index 000000000..871d70e41 --- /dev/null +++ b/content/docs/v1.8/overview/_index.md @@ -0,0 +1,7 @@ +--- +title: "Overview_v1.8/Overview" +date: 2026-09-16T04:40:22Z +draft: false +weight: 0 +--- + diff --git a/content/docs/v1.8/overview/about-vald.md b/content/docs/v1.8/overview/about-vald.md new file mode 100644 index 000000000..37cd3aee5 --- /dev/null +++ b/content/docs/v1.8/overview/about-vald.md @@ -0,0 +1,94 @@ +--- +title: "About Vald_v1.8/Overview" +date: 2026-09-16T04:40:19Z +draft: false +weight: 100 +description: What is Vald and what you can do with Vald +menu: + overview: + parent: Overview +--- + +# About Vald + +This document gives an overview of what is Vald and what you can do with Vald. + +## What is Vald? + + + +Vald is a highly scalable distributed fast approximate nearest neighbor dense vector search engine. + +Vald is designed and implemented based on Cloud-Native architecture. + +It uses the fastest ANN Algorithm [NGT](https://github.com/NGT-labs/NGT) to search neighbors. + +Vald has automatic vector indexing and index backup, and horizontal scaling which made for searching from billions of feature vector data. + +Vald is easy to use, feature-rich and highly customizable as you needed. + +### What Vald can do? + + + +- Asynchronous Auto Indexing + - Usually the graph requires locking during indexing, which causes stop-the-world. But Vald uses distributed index graphs so it continues to work during indexing. + +- Customizable Ingress/Egress Filtering + - Vald implements it's own highly customizable Ingress/Egress filter. + - Which can be configured to fit the gRPC interface. + - Ingress Filter: Ability to Vectorize through filter on request. + - Egress Filter: rerank or filter the searching result with your own algorithm. + +- Cloud-native based vector searching engine + - Horizontal scalable on memory and CPU for your demand. + +- Auto Backup for Index data + - Vald supports to backup Vald Agent index data using Object Storage or Persistent Volume. + +- Distributed Indexing + - Vald distributes vector index to multiple agents, and each agent stores different index. + +- Index Replication + - Vald stores each index in multiple agents which enables index replicas. + - Automatically rebalancing the replica when some Vald agent goes down. + +- Easy to use + - Vald can be easily installed in a few steps. + +- Highly customizable + - You can configure the number of vector dimensions, the number of replica and etc. + +- Multi language supported + - Go, Java, Node.js, and Python client library are supported. + - gRPC APIs can be triggered by any programming languages which support gRPC. + - REST API is also supported. + +### Use cases + +Vald supports similarity searching. + +- Related image search +- Speech recognition +- Everything you can vectorize :) + +## Why Vald? + +Vald is based on Kubernetes and Cloud-Native architecture, which means Vald is highly scalable. +You can easily scale Vald by changing Vald's configuration. + +Vald uses the fastest ANN Algorithm [NGT](https://github.com/NGT-labs/NGT) to search neighbors by default, but users can switch to another vector searching engine in Vald to support the best performance for your use case. + +Also, Vald supports auto-healing, to reduce running and maintenance costs. Vald implements the backup mechanism to support disaster recovery. +Whenever one of the Vald Agent instances is down, the new Vald Agent instance will be created automatically and the data will be recovered automatically. + +## How does Vald work? + +Vald implements its custom resource and custom controller to integrate with Kubernetes. +You can take all the benefits from Kubernetes. + +Please refer to the [architecture overview](/docs/v1.8/overview/architecture) for more details about the architecture and how each component in Vald works together. + +## Try Vald + +Please refer to [Get Started](/docs/v1.8/tutorial/get-started) to try Vald :) diff --git a/content/docs/v1.8/overview/architecture.md b/content/docs/v1.8/overview/architecture.md new file mode 100644 index 000000000..956bb7a23 --- /dev/null +++ b/content/docs/v1.8/overview/architecture.md @@ -0,0 +1,115 @@ +--- +title: "Architecture_v1.8/Overview" +date: 2026-09-16T04:40:19Z +draft: false +weight: 200 +description: High-Level architecture design and overview of each component +menu: + overview: + parent: Overview +--- + +# Vald Architecture + +This document describes the high-level architecture design of Vald and explains each component in Vald. + +## Overview + +Vald uses a cloud-native architecture focusing on [Kubernetes](https://kubernetes.io/). +Some components in Vald use Kubernetes API to control the behavior of distributed vector indexes. +Before reading this document, you need to have some understanding of the basic idea of cloud-native architecture and Kubernetes. + +### Technologies used by Vald + +Vald is based on the following technologies. + +- [Kubernetes](https://kubernetes.io/) + + To easily scale and manage Vald, it is used by deploying and running on [Kubernetes](https://kubernetes.io/). + Vald takes all of the advantages of using Kubernetes. + For more details please read the [next section](#concept). + +- [Helm](https://helm.sh/) + + Helm helps you to deploy and configure Vald. + Vald contains multiple components and configurations. + Helm helps us to manage those manifests and provides a better and easy way to deploy and configure Vald. + +- [NGT](https://github.com/NGT-labs/NGT) + + NGT is one of the core components of Vald. + NGT is a super-fast vector search engine used by Vald to guarantee the high performance of Vald. + +### Concept + +Here are the concepts of Vald. + +- Microservice based + + Vald is designed based on the microservice architecture. Vald components are highly decoupled into small components and connected, which increases the overall agility and maintainability of Vald. + +- Containerized + + All components in Vald are containerized, which means you can easily deploy Vald components in any environment. + +- Observability & Real-time monitoring + + All Vald components support Cloud-Native based observability features such as Prometheus and Jaeger exporter. + +- Distributed vector spaces + + All the vector data and indexes are distributed to Vald Agents in the Vald cluster. Whenever you search a vector in Vald cluster, all Vald agents can process parallel and merge the result by Vald LB Gateway. + +- Kubernetes based + + Vald can integrate with Kubernetes which enables the following features. + - Orchestrated + + Kubernetes supports container orchestration. All components in Vald can be managed by Kubernetes automatically. + + - Horizontal scalable + + All Vald components are designed and implemented to be scalable. You can add any node in the Kubernetes cluster at any time to scale your Kubernetes cluster or change the number of replicas to scale Vald. + + - Auto-healing + + Kubernetes supports the auto-healing feature. The pod can start a new instance automatically whenever the pod is down. + + - Data persistency + + Vald implements backup features. Whenever a Vald Agent is down, and Kubernetes start a new Vald Agent instance, the data is automatically restored to the new instance to prevent data loss. + + - Easy to manage + + Vald can be deployed easily on your Kubernetes cluster by using Helm charts. The custom resources and custom controllers are useful to manage your Vald cluster. + +## Basic Architecture + +Vald is based on microservice architecture, which means Vald is composited by multiple components, you can deploy part of the components to your cluster depending on your needs. +In this section, we will introduce the basic architecture of Vald. + + + +We will introduce each component and why it is needed in Vald. + +### Vald Agent + +Vald Agent is the core component of Vald, the approximate nearest neighbor search engine, and stores the graph tree construction on memory for indexing the vectors. +Vald Agent uses [NGT-labs/NGT](https://github.com/NGT-labs/NGT) as a core library. + +### Vald LB Gateway + +Vald LB Gateway is a gateway to load balance the user request and forward user request to the Vald Agent based on the resource usage of the Vald Agent and the corresponding cluster node. +In addition, it summarizes the search results from each Vald Agent and returns the final search result to the client. + +### Vald Discoverer + +Vald Discoverer provides Vald Agent discovery service to discover active Vald Agent pods in the Kubernetes cluster. +It also retrieves the corresponding Vald Agent resource usage including pod and node resource usage for Vald LB gateway to determine the priority of which Vald Agent handles the user request. + +### Vald Index Manager + +Vald Index Manager controls the timing of the indexing of the Vald Agent. +Since the search operation will no work during the Vald Agent index operation is running, thanks to controlling the timing of indexing by Vald Index Manager, index operation of Vald Agent can be triggered and controlled by Vald Index Manager intelligently. + +It retrieves the active Vald Agent pods from the Vald Discoverer and triggers the indexing action on each Vald Agent. diff --git a/content/docs/v1.8/overview/component/_index.md b/content/docs/v1.8/overview/component/_index.md new file mode 100644 index 000000000..530d410f2 --- /dev/null +++ b/content/docs/v1.8/overview/component/_index.md @@ -0,0 +1,112 @@ +--- +title: "Component_v1.8/Overview/Component" +date: 2026-09-16T04:40:19Z +draft: false +weight: 0 +--- + +# Components + +This document will give you an overview of all the components developed and used in Vald. + +## Table of Contents + +- [Vald Filter](#vald-filter) + - [Vald Ingress Filter](#vald-ingress-filter) + - [Vald Egress Filter](#vald-egress-filter) + - [Vald Filter Gateway](#vald-filter-gateway) +- [Vald Load Balancing](#vald-load-balancing) + - [Vald LB Gateway](#vald-lb-gateway) + - [Agent Discoverer](#agent-discoverer) +- [Vald Core Engine](#vald-core-engine) + - [Vald Agent](#vald-agent) + - [Vald Agent Scheduler](#vald-agent-scheduler) + - [Vald Index Manager](#vald-index-manager) +- [Kubernetes Components](#kubernetes-components) + - [Kube-apiserver](#kube-apiserver) + - [Custom Resources](#custom-resources) + +## Vald Filter + +Vald Filter is an optional functionality in Vald. +User can implement the custom filtering logic and integrate with Vald. + +Vald Filter provides the following functionalities. + +- Custom filter based on request query +- Custom filter for the searching result + +### Vald Ingress Filter + +Vald Ingress Filter filters the incoming request before processing it. + +Users can implement custom filtering logic such as changing the vectors or filtering based on user ID. + +### Vald Egress Filter + +Vald Egress Filter filters the response before sending it to the user. + +This component can reorder the searching result from multiple Vald Agents based on the user-defined ranking. + +### Vald Filter Gateway + +Vald Filter Gateway forwards the request to Vald Ingress Filter before processing it and forwards the response to the Vald Egress Filter before returning the searching result to the user. + +## Vald Load Balancing + +Load balancing is one of the important concepts in distributed computing, which means it distributes a set of tasks over a set of resources aiming for making the overall processing more efficient. +Vald implements its own load balancing controller. +Vald can load balance the request base on node resources. + +### Vald LB Gateway + +Vald LB Gateway loads balance the user request base on the node resources results from the Agent Discoverer. + +### Agent Discoverer + +Agent Discoverer discovers active Vald pods and the corresponding node's resources usage via [kube-apiserver](https://github.com/kubernetes/kubernetes/tree/master/cmd/kube-apiserver). + +## Vald Core Engine + +In this section, we will describe what is Vald Agent and the corresponding components to support Vald Agent. + +### Vald Agent + +Vald Agent provides functionalities to perform approximate nearest neighbor search. +Agent-NGT uses [NGT-labs/NGT](https://github.com/NGT-labs/NGT) as a core library. +And Agent-Faiss uses [facebookresearch/faiss](https://github.com/facebookresearch/faiss) as a core library. + +Each Vald Agent pod has its own vector data space because only several Vald Agents are selected to be inserted/updated in a single insert/update request. + +When searching a vector in Vald, each Vald Agent return different results of _k_-nearest neighbors depending on their index, and you'll get the merged result of them. + +### Vald Agent Scheduler + +Vald Agent Scheduler is the scheduler of the Vald Agent. +It implements it's own custom scheduling logic to increase the scalability of the Vald Agent. + +It schedules Vald Agent base on the Node CPU and memory usage, and the amount of the indexes. + +### Vald Index Manager + +Vald Index Manager controls the timing of the indexing inserted vectors on the Vald Agent. +The index is used to increase the performance of the search action. + +It retrieves the active Vald Agent pods from the Vald Discoverer and triggers the indexing action on each Vald Agent. + +## Kubernetes Components + +Vald is base on the Kubernetes platform. +In this section we will explain the Kubernetes component used in Vald and why we need them. + +### Kube-apiserver + +Kube-apiserver is a component of Kubernetes. +The main responsibility of Kube-apiserver in Vald is to provide node resource information for Vald agent scalability. + +For more information about Kube-apiserver, please refer to [the official document](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/). + +### Custom Resources + +Custom Resources in Vald is a [Custom Resource Definition](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/) implementation. +It provides flexibility for users to control the Vald deployment such as pod startup sequence, etc. diff --git a/content/docs/v1.8/overview/component/agent.md b/content/docs/v1.8/overview/component/agent.md new file mode 100644 index 000000000..084e6c4d3 --- /dev/null +++ b/content/docs/v1.8/overview/component/agent.md @@ -0,0 +1,158 @@ +--- +title: "Agent_v1.8/Overview/Component" +date: 2026-09-16T04:40:19Z +draft: false +weight: 100 +description: Stores index data and Search ANN vectors +menu: + component: + parent: Component +--- + +# Vald Agent + +Vald Agent is the core component of the Vald cluster. + +Mainly, it consists of 1 or 2 small components, Vald Core and Vald Agent sidecar, depending on the use case. + +This page introduces the overview and features of Vald Agent. + +## Responsibility + +Vald Agent is responsible for: + +- Store index data along to the user requests + - The store destination is In-Memory, Volume Mounts, Persistent Volume, or External Storage. +- Search the nearest neighbor vectors of the request vector and return the search result + +## Features + +Vald Agent has two components, `core` and `sidecar`. + +This chapter shows the characteristics of each small component. + +### Core + +`Core` is responsible for the main features of Vald Agent. + +It uses a specific algorithm, and you can choose one algorithm depending on your needs. + +Vald provides: + +- Vald Agent NGT + +as core algorithm layer. + +#### Vald Agent NGT + +Vald Agent NGT uses [NGT](https://github.com/NGT-labs/NGT) as an algorithm. + +The main functions are the followings: + +- Insert + - Request to insert new vectors into the NGT. + - Requested vectors are stored in the `vqueue`. +- Search + - Get the nearest neighbor vectors of the request vector from NGT indexes. +- Update + - Create a request to update the specific vectors to the new vectors. + - Requested vectors are stored in the `vqueue`. +- Remove + - Create a request to remove the specific vectors from NGT indexes. + - Requested vectors are stored in the `vqueue`. +- GetObject + - Get the information on the indexed vectors. +- Exist + - Check whether the specific vectors are already inserted or not. +- CreateIndex + - Create a new NGT index structure in memory using vectors stored in the `vqueue` and the existing NGT index structure if it exists. +- SaveIndex + - Save metadata about NGT index information to the internal storage. + +
+You have to control the duration of CreateIndex and SaveIndex by configuration. + +These methods don’t always run when getting the request. + +
+ +
+As you see, Vald Agent NGT can only search the nearest neighbors from the NGT index. + +You have to wait to complete the CreateIndex and SaveIndex functions before searching. + +
+ +This image shows the mechanism to create NGT index. + +NGT Index Creation Mechanism + +Please refer to [Go Doc](https://pkg.go.dev/github.com/vdaas/vald@v1.8.0/pkg/agent/core/ngt/service) for other functions. + +#### Vald Agent Faiss + +Vald Agent Faiss uses [Faiss](https://github.com/facebookresearch/faiss) as an algorithm. + +The main functions are the followings: + +- Insert + - Request to insert new vectors into the Faiss. + - Requested vectors are stored in the `vqueue`. + - Cache a fixed number of verctors for Faiss training. + - Once Faiss trained in CreateIndex, the vector is never cached for Faiss training. +- Search + - Get the nearest neighbor vectors of the request vector from Faiss indexes. + - radius/epsilon is search config for NGT and has no meaning in Faiss. +- Update + - Create a request to update the specific vectors to the new vectors. + - Requested vectors are stored in the `vqueue`. +- Remove + - Create a request to remove the specific vectors from Faiss indexes. + - Requested vectors are stored in the `vqueue`. +- Exist + - Check whether the specific vectors are already inserted or not. +- CreateIndex + - Create a new Faiss index structure in memory using vectors stored in the `vqueue` and the existing Faiss index structure if it exists. + - If a certain number of vectors required for Faiss training are not cached, they will not be trained. + - If Faiss is not trained, no index is generated. +- SaveIndex + - Save metadata about Faiss index information to the internal storage. + +Unimplemented functions are the followings: + +- GetObject +- SearchByID +- StreamXXX +- MultiXXX + +
+Same as Agent NGT, You have to control the duration of CreateIndex and SaveIndex by configuration. + +These methods don’t always run when getting the request. + +
+ +
+As you see, Vald Agent Faiss can only search the nearest neighbors from the Faiss index. + +You have to wait to complete the CreateIndex and SaveIndex functions before searching. + +
+ +### Sidecar + +`Sidecar` saves the index metadata file to external storage like Amazon S3 or Google Cloud Storage. + +The main functions are: + +- Backup + - When `Agent Core` completes creating the index metadata files, `Sidecar` hooks to store them in the external storage. + + + +- Restore + - When the Vald Agent Pod restarts, the index structure is restored from the external backup files. + + + + diff --git a/content/docs/v1.8/overview/component/discoverer.md b/content/docs/v1.8/overview/component/discoverer.md new file mode 100644 index 000000000..ea105d39e --- /dev/null +++ b/content/docs/v1.8/overview/component/discoverer.md @@ -0,0 +1,81 @@ +--- +title: "Discoverer_v1.8/Overview/Component" +date: 2026-09-16T04:40:19Z +draft: false +weight: 400 +description: Discovers Pod information and helps automate operation +menu: + component: + parent: Component +--- + +# Vald Discoverer + +Vald Discoverer runs individually and helps automate operations for the Vald cluster. + +It is also the only component that communicates with [kube-apiserver](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/). + +## Responsibility + +Vald Discoverer is responsible for retrieving each Node and Pod resource usage from [kube-apiserver](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/) and sharing it with other components in the Vald cluster. + +## Feature + +### Getting Node and Pod metrics + +Vald Discoverer requires [kube-apiserver](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/) to get Node and Pod metrics in the Vald cluster. + +It synchronizes the metrics when it changes by the `Reconcile function`, one of the internal functions. + +The kind of metrics are here: + +- Node + + | Key name | Description | + | ------------ | ------------------------------- | + | Name | node name | + | InternalAddr | node internal address | + | ExternalAddr | node external address | + | CPU | CPU limit and CPU request | + | Memory | Memory limit and Memory request | + | Pods | Pod information in the node | + +- Pod + + | Key name | Description | + | --------- | ------------------------------- | + | AppName | AppName of pod | + | Name | pod name | + | Namespace | namespace where pod runs | + | IP | IP address | + | CPU | CPU limit and CPU request | + | Memory | Memory limit and Memory request | + +When syncing success, Vald Discoverer chooses the necessary metrics from the result and stores them into their four kinds of `Map` on their local memory: + +| Map name | Description | +| --------------- | ------------------------------------ | +| podsByNode | pod metrics list group by node | +| podsByNamespace | pod metrics list group by namespace | +| podsByName | pod metrics list group by pod name | +| nodebyName | node metrics list group by node name | + + + +### Sharing Node and Pod metrics + +Vald Discoverer provides the internal client for another component. + +The component, which requires the metrics information, uses when it needs. + +Vald LB Gateway and Vald Manager Index in the Vald cluster require it to achieve its responsibility. + +For example, Vald LB Gateway creates the new `discoverer client` with config parameters and binds it to the new `gateway lb client` when Vald LB Gateway starts the container. + +When the `initContainer` successes, Vald LB Gateway can get the metrics asynchronously according to its set parameters. + + + +### Cluster role configurations + +Please refer [here](/docs/v1.8/user-guides/cluster-role-binding) for more information about the cluster role configuration. diff --git a/content/docs/v1.8/overview/component/filter-gateway.md b/content/docs/v1.8/overview/component/filter-gateway.md new file mode 100644 index 000000000..06379a59d --- /dev/null +++ b/content/docs/v1.8/overview/component/filter-gateway.md @@ -0,0 +1,164 @@ +--- +title: "Filter Gateway_v1.8/Overview/Component" +date: 2026-09-16T04:40:19Z +draft: false +weight: 300 +description: Communicates with user-defined ingress/egress filter component +menu: + component: + parent: Component +--- + +# Vald Filter Gateway + +Vald Filter Gateway is the component to communicate user-defined ingress/egress filter components with requests. + +## Responsibility + +Vald Filter Gateway is an optional component in the Vald cluster allowing the user to run user-defined custom logic with ingress/egress filter components. +The responsibility is to bypass the user request or response between the Vald cluster and the user-defined ingress filter (or egress filter) component. + +## Features + +### Ingress Filtering + +Ingress filtering is the pre-process before the request is processed by the Vald LB Gateway. + +This is useful, e.g., when you want to vectorize object data (when you want to handle object data directly as the request query) or when you want to pad the vector, etc. + +Here are the steps of pre-processing with the ingress filter: + +1. The user's request flows from the filter gateway to the ingress filter component when using the Ingress filter. +1. Ingress filter performs arbitrary processing for each user and returns the result to the filter gateway. +1. The filter gateway forwards the ingress filter result to the Vald LB Gateway.(The Vald LB Gateway migration process is the same as when the ingress filter is not used. Click [here](/docs/v1.8/overview/data-flow) for a detailed data flow.) + +example data flow of ingress filtering with blob data + +example data flow of ingress filtering with vector + +There are two types of ingress filtering methods: generating the vector and the vector filtering. + +In generating the vector, for example, you can vectorize object data using your favorite model. + +The filtering vector allows you to filter out vectors that contain outliers so that they are not indexed. + +Vald officially offers two types of ingress filter components. + +- [vald-onnx-ingress-filter](https://github.com/vdaas/vald-onnx-ingress-filter) +- [vald-tensorflow-ingress-filter](https://github.com/vdaas/vald-tensorflow-ingress-filter) + +Of course, you can use your own implemented ingress filter component. +When using it, please make sure to meet the following interface. + +- The scheme of ingress filter service + + ```rpc + // https://github.com/vdaas/vald/blob/main/apis/proto/v1/filter/ingress/ingress_filter.proto + service Filter { + // Represent the RPC to generate the vector. + rpc GenVector(payload.v1.Object.Blob) returns (payload.v1.Object.Vector) { + option (google.api.http) = { + post : "/filter/ingress/object" + body : "*" + }; + } + + // Represent the RPC to filter the vector. + rpc FilterVector(payload.v1.Object.Vector) + returns (payload.v1.Object.Vector) { + option (google.api.http) = { + post : "/filter/ingress/vector" + body : "*" + }; + } + } + ``` + +- The scheme of `payload.v1.Object.Blob` and `payload.v1.Object.Vector` + + ```rpc + // https://github.com/vdaas/vald/blob/main/apis/proto/v1/payload/payload.proto + // Represent the binary object. + message Blob { + // The object ID. + string id = 1 [ (validate.rules).string.min_len = 1 ]; + // The binary object. + bytes object = 2; + } + + // Represent a vector. + message Vector { + // The vector ID. + string id = 1 [ (validate.rules).string.min_len = 1 ]; + // The vector. + repeated float vector = 2 [ (validate.rules).repeated .min_items = 2 ]; + } + ``` + +### Egress Filtering + +Egress filtering is mainly used as the post-processing for the search result returned from the Vald LB Gateway. +Egress filtering returns the search results filtered by the egress filter component to the requesting user. + +Here are the steps of pre-processing with the egress filter: + +1. The filter gateway receives the search results from the Vald LB Gateway and forwards them to the egress filter component. +1. The egress filter component filters the search results and returns the filtered search results to the Filter Gateway. +1. The filter gateway returns the filtered search results to the user. + +search data flow with egress filtering + +There are two types of egress filtering methods: distance filtering and vector filtering. + +In distance filtering, for example, you can add a process to exclude vectors that exceed the upper limit of distance from the search results. + +Vector filtering allows you to add the process: for example, to remove different categories' vectors from the search results. (e.g., excluding kid's T-shirts from the search for adult's T-shirts.) + +If you want to use this feature, please deploy your own egress filter component, which meets the following interface. + +- The scheme of egress filter service + + ```rpc + // https://github.com/vdaas/vald/blob/main/apis/proto/v1/filter/ingress/egress_filter.proto + service Filter { + + // Represent the RPC to filter the distance. + rpc FilterDistance(payload.v1.Object.Distance) + returns (payload.v1.Object.Distance) { + option (google.api.http) = { + post : "/filter/egress/distance" + body : "*" + }; + } + + // Represent the RPC to filter the vector. + rpc FilterVector(payload.v1.Object.Vector) + returns (payload.v1.Object.Vector) { + option (google.api.http) = { + post : "/filter/egress/vector" + body : "*" + }; + } + } + ``` + +- The scheme of `payload.v1.Object.Distance` and `payload.v1.Object.Vector` + + ```rpc + // https://github.com/vdaas/vald/blob/main/apis/proto/v1/payload/payload.proto + // Represent the ID and distance pair. + message Distance { + // The vector ID. + string id = 1; + // The distance. + float distance = 2; + } + + // Represent a vector. + message Vector { + // The vector ID. + string id = 1 [ (validate.rules).string.min_len = 1 ]; + // The vector. + repeated float vector = 2 [ (validate.rules).repeated .min_items = 2 ]; + } + ``` diff --git a/content/docs/v1.8/overview/component/index-manager.md b/content/docs/v1.8/overview/component/index-manager.md new file mode 100644 index 000000000..82089d5d1 --- /dev/null +++ b/content/docs/v1.8/overview/component/index-manager.md @@ -0,0 +1,55 @@ +--- +title: "Index Manager_v1.8/Overview/Component" +date: 2026-09-16T04:40:19Z +draft: false +weight: 500 +description: Controls indexing process of Vald Agent Pods +menu: + component: + parent: Component +--- + +# Vald Index Manager + +Vald Index Manager is the component that controls the indexing process of Vald Agent Pods in the same Vald cluster. + +## Responsibility + +Vald Index Manager has a unique and simple role in controlling the indexing timing for all Vald Agent pods in the Vald cluster. + +It requires `Vald Discoverer` to fulfill its responsibility. + +## Features + +This chapter shows the main features to fulfill Vald Index Manager’s role. + +### Syncing data from Vald Discoverer + +Vald Index Manager gets the IP addresses of each Vald Agent pod from Vald Discoverer when its container starts. + +In addition, when IP address changes, Vald Index Manager gets the new IP address from Vald Discoverer. + +Vald Index Manager uses these for the controlling indexing process. + +### Controlling Indexing process + +When Vald Agent Pod creates or saves indexes on its container memory, it blocks all searching requests from the user and returns an error instead of a search result. + +Stop-the-world happens when all Vald Agent pods run the function involved in the indexing operation, e.g., `createIndex`, simultaneously. + +Vald Index Manager manages the indexing process of all Vald Agent pods to prevent this event. + +Vald Index Manager uses a Vald Agent pods' IP address list from Vald Discoverer and index information, including the stored index count, uncommitted index count, creating index count, and saving index count, from each Vald Agent pod. + +The control process is Vald Index Manager sends `createIndex` requests for concurrency simultaneously, sends a new request when the job is finished and continues until it sends to all agents. + +At the end of each process, Vald Index Manager updates the index information from each Vald Agent pod. + +Vald Index Manager runs this process periodically by set time intervals. + +
+Concurrency means the number of Vald Agent pods for simultaneously sending requests for the indexing operation.
+ +When the Vald Agent pod has no uncommitted index or is running the indexing function already, it does not send the request. + +
diff --git a/content/docs/v1.8/overview/component/lb-gateway.md b/content/docs/v1.8/overview/component/lb-gateway.md new file mode 100644 index 000000000..a4021a87c --- /dev/null +++ b/content/docs/v1.8/overview/component/lb-gateway.md @@ -0,0 +1,85 @@ +--- +title: "Lb Gateway_v1.8/Overview/Component" +date: 2026-09-16T04:40:19Z +draft: false +weight: 200 +description: Operates various type processes with Vald cluster +menu: + component: + parent: Component +--- + +# Vald LB Gateway + +Vald LB Gateway is the component to handle requests in the Vald cluster. + +This component is essential to operate the Vald cluster because all requests should pass it, and other components depend on it. + +This page introduces the overview and features of Vald Gateway. + +## Responsibility + +Vald LB Gateway is responsible for: + +- Pass and load balancing requests to other Vald components +- Control processing with a timeout setting +- Aggregate search results from each Vald Agent +- Sort and combine into one search result + +The next chapter shows the main features. + +## Features + +Vald LB Gateway is a mandatory component for the Vald cluster. + +Each following section introduces each feature. + +### Pass and Control all requests + +Vald LB Gateway is the only component that connects to Kubernetes Ingress in the Vald cluster’s components in basic. + +User requests have passed Kubernetes Ingress reaches to Vald LB Gateway. + +Then, Vald LB Gateway will suspend processing according to timeout and return a response when the internal processing takes a long time. + +
+ +If Vald Filter Gateway runs and the request has the filter gateway option, Kubernetes Ingress will pass the request to Vald Filter Gateway. + +Then, Vald Filter Gateway sends the processed request to the Vald LB Gateway. + +
+ +### Control insert vectors + +As its name shows, Vald LB Gateway has the load balancing feature. + +Vald LB Gateway controls insert vector requests based on `index replica` and each Vald Agent Pod resource usage, which [Vald Discoverer](/docs/v1.8/overview/component/discoverer) provides, to avoid uneven resource usage. + +### Broadcast search request and aggregate search result + +Vald LB Gateway broadcasts searching requests, e.g., `Search`, `GetObject`, `Exist`, to all Vald Agent Pods and gets their result. + +In the `Search` phase, Vald LB Gateway receives search results from all Vald Agent Pods within the user-defined timeout time and sorts combining results by shortest distance. +Then, Vald LB Gateway returns the Top-_N_ of the sorted search results (_N_ is the user-defined number). + +
+ +When Vald Agent Pod could not give search result by the timeout limit, Vald LB Gateway will cancel the searching requests to the Vald Agent Pod. + +
+ +### Work together with Vald Filter Gateway + +Vald LB Gateway is the only component to connect to the ingress (or egress) filter component via Vald Filter Gateway. + +When the ingress component runs, Vald LB Gateway will pass the object of request to the Ingress, then get the vector converted from it. +Vald LB Gateway will send the search result to the Egress when the egress component runs and get the filtered result. + +
+ +For more information about Vald Filter Gateway, please refer to [Vald Filter Gateway Overview](/docs/v1.8/overview/component/filter-gateway). + +
+ + diff --git a/content/docs/v1.8/overview/component/mirror-gateway.md b/content/docs/v1.8/overview/component/mirror-gateway.md new file mode 100644 index 000000000..ac5083aa3 --- /dev/null +++ b/content/docs/v1.8/overview/component/mirror-gateway.md @@ -0,0 +1,88 @@ +--- +title: "Mirror Gateway_v1.8/Overview/Component" +date: 2026-09-16T04:40:19Z +draft: false +weight: 600 +description: Gateway for operating Vald cluster compatible with Multi-region and Multi AZ +menu: + component: + parent: Component +--- + +# Vald Mirror Gateway + +Vald Mirror Gateway is an optional component of the Vald, allowing the vector data to be synchronized across multiple Vald clusters. + +This component makes it possible to enhance availability during a cluster failure. + + + +## Responsibility + +Vald Mirror Gateway is responsible for the followings: + +- Forward user requests ([Insert](/docs/v1.8/api/insert) / [Upsert](/docs/v1.8/api/upsert) / [Update](/docs/v1.8/api/update) / [Remove](/docs/v1.8/api/remove)) to the other Vald Mirror Gateways in the same group. +- Manages the state of indexes stored in all clusters to ensure they are consistent. + +## Features + +This chapter shows the main features to fulfill Vald Mirror Gateway’s role: + +- Full mesh connection +- Request forwarding +- Automatic rollback on failure + +### Full mesh connection + + + +The Vald Mirror Gateway is designed to interconnect with Vald Mirror Gateways in other Vald clusters. + +Vald Mirror Gateway uses a Custom Resource called the `ValdMirrorTarget` to manage the connection destination information between Vald Mirror Gateways. + +The `ValdMirrorTarget` is a Custom Resource related to the connection destination to other Vald Mirror Gateway. + +When two Vald clusters contain Vald Mirror Gateways, Vald Mirror Gateways can send the request to each other by applying `ValdMirrorTarget`. + +For more information about `ValdMirrorTarget` configuration, please refer to [Custom Resource Configuration](/docs/v1.8/user-guides/mirroring-configuration). + +### Request forwarding + + + +The Vald Mirror Gateway forwards the incoming user request ([Insert](/docs/v1.8/api/insert) / [Upsert](/docs/v1.8/api/upsert) / [Update](/docs/v1.8/api/update) / [Remove](/docs/v1.8/api/remove)) to other Vald Mirror Gateways. +Then, while forwarding the user request, the Vald Mirror Gateway bypasses the incoming user request to Vald LB Gateway in its own cluster. + +On the other hand, if the incoming user request is an [Object API](/docs/v1.8/api/object) or [Search API](/docs/v1.8/api/search), it is bypassed to only a Vald LB Gateway in its own cluster without forwarding it to other Vald Mirror Gateways. + +### Continuous processing on failure + +The request may fail at the forwarding destination or the bypass destination. + +If some of the requests fails, the processing continues based on their status code. + +Here's an overview of how the Mirror Gateway handles failures for each type of request. + +For more information about status code, please refer to [Mirror Gateway Troubleshooting](/docs/v1.8/troubleshooting/mirror-gateway). + +- Insert Request + - If the target host returns a status code of `ALREADY_EXISTS`, the Update request is sent to this host. + - If the target host returns a status code other than `OK`, `ALREADY_EXISTS`, the Mirror Gateway returns that status code without continuous processing. + - If all target hosts return a status code `ALREADY_EXISTS`, the Mirror Gateway returns `ALREADY_EXISTS`. + - If all target hosts return a status code `OK` or `ALREADY_EXISTS`, the Mirror Gateway returns `OK`. + +- Update Request + - If the target host returns a status code `NOT_FOUND`, the Insert request is sent to this host. + - If the target host returns a status code other than `OK`, `ALREADY_EXISTS`, the Mirror Gateway returns that status code without continuous processing. + - If all target hosts return a status code `ALREADY_EXISTS`, the Mirror Gateway returns `ALREADY_EXISTS`. + - If all target hosts return a status code `OK` or `ALREADY_EXISTS`, the Mirror Gateway returns `OK`. + +- Upsert Request + - If all target hosts return a status code `ALREADY_EXISTS`, the Mirror Gateway returns `ALREADY_EXISTS`. + - If the target host returns a status code other than `OK` or `ALREADY_EXISTS`, the Mirror Gateway returns that status code without continuous processing. + - If all target hosts return a status code `OK` or `ALREADY_EXISTS`, the Mirror Gateway returns `OK`. + +- Remove/RemoveByTimestamp Request + - If all target hosts return a status code `NOT_FOUND`, the Mirror Gateway returns `NOT_FOUND`. + - If the target host returns a status code other than `OK` or `NOT_FOUND`, the Mirror Gateway returns that status code without continuous processing. + - If all target hosts return a status code `OK` or `NOT_FOUND`, the Mirror Gateway returns `OK`. diff --git a/content/docs/v1.8/overview/data-flow.md b/content/docs/v1.8/overview/data-flow.md new file mode 100644 index 000000000..b13c1ad8d --- /dev/null +++ b/content/docs/v1.8/overview/data-flow.md @@ -0,0 +1,113 @@ +--- +title: "Data Flow_v1.8/Overview" +date: 2026-09-16T04:40:19Z +draft: false +weight: 300 +description: Data flow inside Vald for each operation +menu: + overview: + parent: Overview +--- + +# Data Flow + +This page describes the data flow inside Vald and how to store vector indexes in the Vald Cluster. +It will help you to understand what Vald does from users' requests. + +The below image is the basic Vald architecture. + + + +We will explain using this image in the following sections. + +- [Data Flow](#data-flow) + - [Insert](#insert) + - [Search](#search) + - [Update](#update) + - [Upsert](#upsert) + - [Remove](#remove) + +## Insert + +Users can insert a vector to the Vald cluster using Insert API. When the insert command is processed, Vald will intelligently insert the index into the most suitable Vald Agent(s) based on the memory usage of the Vald Agent and the node. + +To obtain the memory usage of Vald Agent and the node, Vald Discoverer must rank which is the most suitable agent to perform the insert request by talking with the kube-apiserver. + +It requires the `CreateIndex` instruction to Vald Agent by Vald Index Manager or self-updating the index by Vald Agent to provide high searching performance to users because the insert command is not responsible for indexing. + +
+A single Vald cluster supports only one embedding space.
+If you want to support multiple embedded spaces, you may need to consider deploying multiple Vald clusters to support this use case. +
+ + + +When the user inserts data into Vald: + +1. Vald LB Gateway receives the request from the user. The request includes the vector and the vector ID. The vector ID is the user-defined unique ID for each vector. +2. Vald LB Gateway will determine which Vald Agent(s) to process the request based on the resource usage of the nodes and pods and the number of vector replicas. +3. Vald LB Gateway will generate the UUID for each inserting vector and forward the generated UUID and the vector data to the selected Vald Agents in parallel. Vald Agent will insert the vector and UUID in an on-memory vector queue. +4. A vector queue will be committed to an ANN graph index by a `CreateIndex` instruction executed by the Vald Index Manager. +5. Vald Agent will start to save index data into the file by `SaveIndex instruction` after Vald Agent successfully `CreateIndex`. + +## Search + +Users can perform a _k_ nearest neighbor vector searching in Vald. The searching performance is extremely high because of the on-memory graph index structure and optimized Vald structure. + +Vald LB Gateway will broadcast the search request to all Vald Agent Pods to search the top _k_ nearest neighbor vectors from each Vald Agent, and the result will be combined and sorted by the distance of the target vector by the Vald LB Gateway. + + + +When the user searches a vector from Vald: + +1. Vald LB Gateway receives a search request from the user. Vald provides two searching interfaces, search by vector and search by the vector ID, to the user. +2. Vald LB Gateway will forward the request to all Vald Agents in parallel. Each Vald Agent will search the _k_ nearest neighbor vectors on a memory graph index. +3. Vald Agent returns the search result to the Vald LB Gateway. The search result includes the UUID, the vector distance, and the vector. The number of each outcome will be the same as requested. +4. Vald LB Gateway will aggregate all searching results from all Vald Agents, then rank the result by the vector distance. +5. Vald LB Gateway will return the aggregated searching result to the user. + +## Update + +Users can update a vector by sending an update request to Vald. +Vald will execute deleting and inserting instructions to perform a single update command. + + + +When the user updates a vector from Vald: + +1. Vald LB Gateway receives the request from the user. The request includes the existing vector ID(s) and the new vector(s) to be updated. +2. Vald LB Gateway will generate the UUID(s) for each vector and broadcast the remove request with the generated UUID(s) to the Vald Agents. Each Vald Agent will remove the vector data and the metadata if the corresponding UUID(s) exist on the in-memory graph index. +3. Each Vald Agent will return the result to the Vald LB Gateway if completed to remove the requested data successfully. +4. The insertion step will start after the deletion steps. It is the same as insert flow; please refer to the [Insert](#insert) section. +5. If Vald Agent successfully inserts the request data, it will return success (e.g., IP address of pod) to the Vald LB Gateway. +6. Vald LB Gateway will return the result to the user. + +## Upsert + +Upsert request updates the existing vector if the same vector ID already exists or inserts the vector into Vald. + + + +When the user upsert a vector to Vald: + +1. Vald LB Gateway receives the request from the user, including the user's vector ID(s) and the vector(s). +2. Vald LB Gateway will broadcast an existing check request to the Vald Agent(s) to check if the vector exists. +3. Vald Agent returns the existing check result to Vald LB Gateway. +4. If the vector with the same vector ID exists, Vald LB Gateway will send an update request to Vald Agent(s), same as the [update flow](#update) step 2 to step 4. If the vector does not exist, Vald LB Gateway will process the [insert flow](#insert) from step 2 to step 3. Vald Index Manager will send `CreateIndex` requests to each Vald Agent regularly. +5. Vald Agent(s) return the result to Vald LB Gateway. +6. Vald Filter Gateway will return the result to the user. + +## Remove + +Remove request will remove the vector in the Vald cluster. +Vald will broadcast the remove request to all Vald Agents to remove the vector inside the cluster. +It requires the `CreateIndex` instruction to Vald Agent by the Vald Index Manager or self-updating the index by Vald Agent because the remove command is not responsible for deleting indexes. + + + +When the user removes a vector that exists in the in-memory graph index in Vald Agent: + +1. Vald LB Gateway receives the remove request from the user. The request includes the vector ID(s), which the user specifies. +2. Vald LB Gateway will broadcast the remove request with UUID(s) to the Vald Agents. Each Vald Agent will remove the vector data and the metadata if the corresponding UUID(s) exists in the in-memory graph index. +3. If Vald Agent successfully removes the request data, it will return success to the Vald LB Gateway. +4. Vald LB Gateway will return the result to the user. diff --git a/content/docs/v1.8/performance/_index.md b/content/docs/v1.8/performance/_index.md new file mode 100644 index 000000000..42b6e9ec7 --- /dev/null +++ b/content/docs/v1.8/performance/_index.md @@ -0,0 +1,7 @@ +--- +title: "Performance_v1.8/Performance" +date: 2026-09-16T04:40:22Z +draft: false +weight: 0 +--- + diff --git a/content/docs/v1.8/performance/benchmark.md b/content/docs/v1.8/performance/benchmark.md new file mode 100644 index 000000000..e5d0e324f --- /dev/null +++ b/content/docs/v1.8/performance/benchmark.md @@ -0,0 +1,40 @@ +--- +title: "Benchmark_v1.8/Performance" +date: 2026-09-16T04:40:21Z +draft: false +weight: 100 +description: Measuring performance of Vald cluster +menu: + performance: + parent: Performance +--- + +# Benchmark + +This document will show and explain the benchmark of an approximate nearest neighbor on the Vald cluster, such as [ann-benchmarks](https://github.com/erikbern/ann-benchmarks), and also explain how to perform the ann-benchmark test on your Vald cluster. + + + +## Overview + +In this document, we will perform the [ann-benchmarks](https://github.com/erikbern/ann-benchmarks) on Vald cluster. + +The test will perform on the whole Vald cluster, which means the request will broadcast to multiple Vald agents instead of a single Vald agent, and return the aggregated result. + +The result may be different from your environment due to the network configuration and overhead of the Vald cluster. +In this document, we will also explain how to perform the ann-benchmark testing on your environment. + +## Benchmark matrix + +Under construction... + +### Result + +Under construction... + +## Benchmarking tools + +Under construction... diff --git a/content/docs/v1.8/performance/continuous-benchmark.md b/content/docs/v1.8/performance/continuous-benchmark.md new file mode 100644 index 000000000..a21a3485a --- /dev/null +++ b/content/docs/v1.8/performance/continuous-benchmark.md @@ -0,0 +1,513 @@ +--- +title: "Continuous Benchmark_v1.8/Performance" +date: 2026-09-16T04:40:21Z +draft: false +weight: 300 +description: Performance verification using continuous benchmark tool +menu: + performance: + parent: Performance +--- + +# Continuous Benchmark Tool + +## What is the Continuous Benchmark Tool? + +Continuous Benchmark Tool allows you to continuously benchmark the Vald cluster. + +Assumed use case is: + +- Verification with workload close to the production environment +- Verification before service installation when Vald version up + +## Architecture + +Continuous Benchmark Tool has following 2 components: + +- Benchmark Operator: Manages benchmark jobs +- Benchmark Job: Executes CRUD request to the target Vald cluster + +## Benchmark component and its feature + +### Benchmark Operator + +- Manages benchmark jobs according to applied manifest. +- Apply method: + - Scenario method: one manifest with multiple benchmark jobs + - Job method: one manifest with one benchmark job + +### Benchmark Job + +- Executes CRUD request to the target Vald cluster based on defined config. +- Execute steps are: + 1. Load dataset (valid only for HDF5 format) + 1. Execute request with load dataset + +## Benchmark CRD + +Benchmark workload can be set by applying the Kubernetes Custom Resources(CRDs), `ValdBenchmarkScenarioResource` or `ValdBenchmarkJobResource`. +Benchmark Operator manages benchmark job according to the applied manifest. + +### ValdBenchmarkJob + +[`ValdBenchmarkJob`](https://github.com/vdaas/vald/blob/main/charts/operator/benchmark/crds/valdbenchmarkjob.yaml) is used for executing single benchmark job. + +And, Benchmark Operator also applies it to the Kubernetes cluster based on `ValdBenchmarkScenarioResource`. + +**main properties** + +| Name | mandatory | Description | type | sample | +| :------------------------- | :-------- | :-------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------- | +| target | \* | target Vald cluster | object | ref: [target](#target-prop) | +| dataset | \* | dataset information | object | ref: [dataset](#dataset-prop) | +| job_type | \* | execute job type | string enum: [insert, update, upsert, remove, search, getobject, exists] | search | +| repetition | | the number of job repetitions
default: `1` | integer | 1 | +| replica | | the number of job concurrent job executions
default: `1` | integer | 2 | +| rps | | designed request per sec to the target cluster
default: `1000` | integer | 1000 | +| concurrency_limit | | goroutine count limit for rps adjustment
default: `200` | integer | 20 | +| ttl_seconds_after_finished | | time until deletion of Pod after job end
default: `600` | integer | 120 | +| insert_config | | request config for insert job | object | ref: [config](#insert-cfg-props) | +| update_config | | request config for update job | object | ref: [config](#update-cfg-props) | +| upsert_config | | request config for upsert job | object | ref: [config](#upsert-cfg-props) | +| search_config | | request config for search job | object | ref: [config](#search-cfg-props) | +| remove_config | | request config for remove job | object | ref: [config](#remove-cfg-props) | +| object_config | | request config for object job | object | ref: [config](#object-cfg-props) | +| client_config | | gRPC client config for running benchmark job
Tune if can not getting the expected performance with default config. | object | ref: [defaults.grpc](https://github.com/vdaas/vald/blob/main/charts/vald/values.yaml) | +| server_config | | server config for benchmark job pod
Tune if can not getting the expected performance with default config. | object | ref: [defaults.server_config](https://github.com/vdaas/vald/blob/main/charts/vald/values.yaml) | + + + +**target** + +- target Vald cluster information +- type: object + +| property | mandatory | description | type | sample | +| :------- | :-------- | :-------------------- | :------ | :-------- | +| host | \* | target cluster's host | string | localhost | +| port | \* | target cluster's port | integer | 8081 | + + + +**dataset** + +- dataset which is used for executing job operation +- type: object + +| property | mandatory | description | type | sample | +| :---------- | :-------- | :--------------------------------------------------------------------------------------------------- | :------------------------------------- | :------------ | +| name | \* | dataset name | string enum: [fashion-mnist, original] | fashion-mnist | +| group | \* | group name | string enum: [train, test, neighbors] | train | +| indexes | \* | amount of index size | integer | 1000000 | +| range | \* | range of indexes to be used (if there are many indexes, the range will be corrected on the job side) | object | - | +| range.start | \* | start of range | integer | 1 | +| range.end | \* | end of range | integer | 1000000 | +| url | | the dataset url. It should be set when set `name` as `original` | string | | + + + +**insert_config** + +- rpc config for insert request +- type: object + +| property | mandatory | description | type | sample | +| :---------------------- | :-------- | :----------------------------------------------------------------------------------------------------------- | :----- | :--------- | +| skip_strict_exist_check | | Check whether the same vector is already inserted or not.
The ID should be unique if the value is `true`. | bool | false | +| timestamp | | The timestamp of the vector inserted.
If it is N/A, the current time will be used. | string | 1707272658 | + +
+ +**update_config** + +- rpc config for update request +- type: object + +| property | mandatory | description | type | sample | +| :---------------------- | :-------- | :----------------------------------------------------------------------------------------------------------- | :----- | :--------- | +| skip_strict_exist_check | | Check whether the same vector is already inserted or not.
The ID should be unique if the value is `true`. | bool | false | +| timestamp | | The timestamp of the vector inserted.
If it is N/A, the current time will be used. | string | 1707272658 | +| disable_balanced_update | | A flag to disable balanced update (split remove -> insert operation) during update operation. | bool | false | + +
+ +**upsert_config** + +- rpc config for upsert request +- type: object + +| property | mandatory | description | type | sample | +| :---------------------- | :-------- | :----------------------------------------------------------------------------------------------------------- | :----- | :--------- | +| skip_strict_exist_check | | Check whether the same vector is already inserted or not.
The ID should be unique if the value is `true`. | bool | false | +| timestamp | | The timestamp of the vector inserted.
If it is N/A, the current time will be used. | string | 1707272658 | +| disable_balanced_update | | A flag to disable balanced update (split remove -> insert operation) during update operation. | bool | false | + +
+ +**upsert_config** + +- rpc config for search request +- type: object + +| property | mandatory | description | type | sample | +| :-------------------- | :-------- | :---------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------- | :----- | +| radius | | The search radius.
default: `-1` | number | -1 | +| epsilon | | The search coefficient.
default: `0.05` | number | 0.05 | +| num | \* | The maximum number of results to be returned. | integer | 10 | +| min_num | | The minimum number of results to be returned. | integer | 5 | +| timeout | | Search timeout in nanoseconds
default: `10s` | string | 3s | +| enable_linear_search | | A flag to enable linear search operation for estimating search recall.
If it is `true`, search operation with linear operation will execute. | bool | false | +| aggregation_algorithm | | The search aggregation algorithm option.
default: `Unknown` | string enum: ["Unknown", "ConcurrentQueue", "SortSlice", "SortPoolSlice", "PairingHeap"] | | + +
+ +**remove_config** + +- rpc config for remove request +- type: object + +| property | mandatory | description | type | sample | +| :---------------------- | :-------- | :----------------------------------------------------------------------------------------------------------- | :----- | :--------- | +| skip_strict_exist_check | | Check whether the same vector is already inserted or not.
The ID should be unique if the value is `true`. | bool | false | +| timestamp | | The timestamp of the vector inserted.
If it is N/A, the current time will be used. | string | 1707272658 | + +
+ +**object_config** + +- rpc config for get object request +- type: object + +| property | mandatory | description | type | sample | +| :-------------------- | :-------- | :---------------------------------------------------------- | :------- | :----- | +| filter_config.targets | | filter target host and port for bypassing filter component. | []object | | + +### ValdBenchmarkScenario + +[`ValdBenchmarkScenario`](https://github.com/vdaas/vald/blob/main/charts/operator/benchmark/crds/valdbenchmarkscenario.yaml) is used for executing one or more benchmark jobs. + +Benchmark Operator decomposes manifest and creates benchmark resources one by one. +The `target` and `dataset` property are the global config for scenario, they can be overwritten when each job has own config. + +**main properties** + +| property | mandatory | description | type | sample | +| :------- | :-------- | :------------------------------------------------------------------------------------- | :----- | :-------------------------------------- | +| target | \* | target Vald cluster information
It will be overwritten when each job has own config | object | ref: [target](#target-prop) | +| dataset | \* | dataset information
It will be overwritten when each job has own config | object | ref: [dataset](#dataset-prop) | +| jobs | \* | benchmark job config
The jobs written above will be executed in order. | object | ref: [benchmark job](#valdbenchmarkjob) | + +## Deploy Benchmark Operator + +Continuous benchmark operator can be applied with `Helm` same as Vald cluster. + +It requires `ValdBenchmarkOperatorRelease` for deploying `vald-benchmark-operator`. + +It is not must to apply, so please edit and apply as necessary. + +
+`job.client_config` is used as the default gRPC client configuration for all benchmark jobs.
+Although it is possible to override using `ValdBenchmarkScenarioRelease` or `ValdBenchmarkJobRelease`, we recommend configuring common setting items here. +
+ +
Sample ValdBenchmarkOperatorRelease YAML + +```yaml +# @schema {"name": "name", "type": "string"} +# name -- name of the deployment +name: vald-benchmark-operator +# @schema {"name": "time_zone", "type": "string"} +# time_zone -- time_zone +time_zone: "" +# @schema {"name": "image", "type": "object"} +image: + # @schema {"name": "image.repository", "type": "string"} + # image.repository -- image repository + repository: vdaas/vald-benchmark-operator + # @schema {"name": "image.tag", "type": "string"} + # image.tag -- image tag + tag: v1.7.5 + # @schema {"name": "image.pullPolicy", "type": "string", "enum": ["Always", "Never", "IfNotPresent"]} + # image.pullPolicy -- image pull policy + pullPolicy: Always +# @schema {"name": "job", "type": "object"} +job: + # @schema {"name": "job.image", "type": "object"} + image: + # @schema {"name": "job.image.repository", "type": "string"} + # image.repository -- job image repository + repository: vdaas/vald-benchmark-job + # @schema {"name": "job.image.tag", "type": "string"} + # image.tag -- image tag for job docker image + tag: v1.7.12 + # @schema {"name": "job.image.pullPolicy", "type": "string", "enum": ["Always", "Never", "IfNotPresent"]} + # image.pullPolicy -- image pull policy + pullPolicy: Always + # @schema {"name": "job.client_config", "type": "object"} + # client_config -- gRPC client config for request to the Vald cluster + client_config: + # @schema {"name": "job.client_config.addrs", "type": "array", "items": {"type": "string"}} + # job.client_config.addrs -- gRPC client addresses + addrs: [] + # @schema {"name": "job.client_config.health_check_duration", "type": "string"} + # job.client_config.health_check_duration -- gRPC client health check duration + health_check_duration: "1s" + # @schema {"name": "job.client_config.connection_pool", "type": "object"} + connection_pool: + # @schema {"name": "job.client_config.connection_pool.enable_dns_resolver", "type": "boolean"} + # job.client_config.connection_pool.enable_dns_resolver -- enables gRPC client connection pool dns resolver, when enabled vald uses ip handshake exclude dns discovery which improves network performance + enable_dns_resolver: true + # @schema {"name": "job.client_config.connection_pool.enable_rebalance", "type": "boolean"} + # job.client_config.connection_pool.enable_rebalance -- enables gRPC client connection pool rebalance + enable_rebalance: true + # @schema {"name": "job.client_config.connection_pool.rebalance_duration", "type": "string"} + # job.client_config.connection_pool.rebalance_duration -- gRPC client connection pool rebalance duration + rebalance_duration: 30m + # @schema {"name": "job.client_config.connection_pool.size", "type": "integer"} + # job.client_config.connection_pool.size -- gRPC client connection pool size + size: 3 + # @schema {"name": "job.client_config.connection_pool.old_conn_close_duration", "type": "string"} + # job.client_config.connection_pool.old_conn_close_duration -- makes delay before gRPC client connection closing during connection pool rebalance + old_conn_close_duration: "2m" + # @schema {"name": "job.client_config.backoff", "type": "object", "anchor": "backoff"} + backoff: + # @schema {"name": "job.client_config.backoff.initial_duration", "type": "string"} + # job.client_config.backoff.initial_duration -- gRPC client backoff initial duration + initial_duration: 5ms + # @schema {"name": "job.client_config.backoff.backoff_time_limit", "type": "string"} + # job.client_config.backoff.backoff_time_limit -- gRPC client backoff time limit + backoff_time_limit: 5s + # @schema {"name": "job.client_config.backoff.maximum_duration", "type": "string"} + # job.client_config.backoff.maximum_duration -- gRPC client backoff maximum duration + maximum_duration: 5s + # @schema {"name": "job.client_config.backoff.jitter_limit", "type": "string"} + # job.client_config.backoff.jitter_limit -- gRPC client backoff jitter limit + jitter_limit: 100ms + # @schema {"name": "job.client_config.backoff.backoff_factor", "type": "number"} + # job.client_config.backoff.backoff_factor -- gRPC client backoff factor + backoff_factor: 1.1 + # @schema {"name": "job.client_config.backoff.retry_count", "type": "integer"} + # job.client_config.backoff.retry_count -- gRPC client backoff retry count + retry_count: 100 + # @schema {"name": "job.client_config.backoff.enable_error_log", "type": "boolean"} + # job.client_config.backoff.enable_error_log -- gRPC client backoff log enabled + enable_error_log: true + # @schema {"name": "job.client_config.circuit_breaker", "type": "object"} + circuit_breaker: + # @schema {"name": "job.client_config.circuit_breaker.closed_error_rate", "type": "number"} + # job.client_config.circuit_breaker.closed_error_rate -- gRPC client circuitbreaker closed error rate + closed_error_rate: 0.7 + # @schema {"name": "job.client_config.circuit_breaker.half_open_error_rate", "type": "number"} + # job.client_config.circuit_breaker.half_open_error_rate -- gRPC client circuitbreaker half-open error rate + half_open_error_rate: 0.5 + # @schema {"name": "job.client_config.circuit_breaker.min_samples", "type": "integer"} + # job.client_config.circuit_breaker.min_samples -- gRPC client circuitbreaker minimum sampling count + min_samples: 1000 + # @schema {"name": "job.client_config.circuit_breaker.open_timeout", "type": "string"} + # job.client_config.circuit_breaker.open_timeout -- gRPC client circuitbreaker open timeout + open_timeout: "1s" + # @schema {"name": "job.client_config.circuit_breaker.closed_refresh_timeout", "type": "string"} + # job.client_config.circuit_breaker.closed_refresh_timeout -- gRPC client circuitbreaker closed refresh timeout + closed_refresh_timeout: "10s" + # @schema {"name": "job.client_config.call_option", "type": "object"} + call_option: + # @schema {"name": "job.client_config.wait_for_ready", "type": "boolean"} + # job.client_config.call_option.wait_for_ready -- gRPC client call option wait for ready + wait_for_ready: true + # @schema {"name": "job.client_config.max_retry_rpc_buffer_size", "type": "integer"} + # job.client_config.call_option.max_retry_rpc_buffer_size -- gRPC client call option max retry rpc buffer size + max_retry_rpc_buffer_size: 0 + # @schema {"name": "job.client_config.max_recv_msg_size", "type": "integer"} + # job.client_config.call_option.max_recv_msg_size -- gRPC client call option max receive message size + max_recv_msg_size: 0 + # @schema {"name": "job.client_config.max_send_msg_size", "type": "integer"} + # job.client_config.call_option.max_send_msg_size -- gRPC client call option max send message size + max_send_msg_size: 0 + # @schema {"name": "job.client_config.dial_option", "type": "object"} + dial_option: + # @schema {"name": "job.client_config.dial_option.write_buffer_size", "type": "integer"} + # job.client_config.dial_option.write_buffer_size -- gRPC client dial option write buffer size + write_buffer_size: 0 + # @schema {"name": "job.client_config.dial_option.read_buffer_size", "type": "integer"} + # job.client_config.dial_option.read_buffer_size -- gRPC client dial option read buffer size + read_buffer_size: 0 + # @schema {"name": "job.client_config.dial_option.initial_window_size", "type": "integer"} + # job.client_config.dial_option.initial_window_size -- gRPC client dial option initial window size + initial_window_size: 0 + # @schema {"name": "job.client_config.dial_option.initial_connection_window_size", "type": "integer"} + # job.client_config.dial_option.initial_connection_window_size -- gRPC client dial option initial connection window size + initial_connection_window_size: 0 + # @schema {"name": "job.client_config.dial_option.max_msg_size", "type": "integer"} + # job.client_config.dial_option.max_msg_size -- gRPC client dial option max message size + max_msg_size: 0 + # @schema {"name": "job.client_config.dial_option.backoff_max_delay", "type": "string"} + # job.client_config.dial_option.backoff_max_delay -- gRPC client dial option max backoff delay + backoff_max_delay: "120s" + # @schema {"name": "job.client_config.dial_option.backoff_base_delay", "type": "string"} + # job.client_config.dial_option.backoff_base_delay -- gRPC client dial option base backoff delay + backoff_base_delay: "1s" + # @schema {"name": "job.client_config.dial_option.backoff_multiplier", "type": "number"} + # job.client_config.dial_option.backoff_multiplier -- gRPC client dial option base backoff delay + backoff_multiplier: 1.6 + # @schema {"name": "job.client_config.dial_option.backoff_jitter", "type": "number"} + # job.client_config.dial_option.backoff_jitter -- gRPC client dial option base backoff delay + backoff_jitter: 0.2 + # @schema {"name": "job.client_config.dial_option.min_connection_timeout", "type": "string"} + # job.client_config.dial_option.min_connection_timeout -- gRPC client dial option minimum connection timeout + min_connection_timeout: "20s" + # @schema {"name": "job.client_config.dial_option.enable_backoff", "type": "boolean"} + # job.client_config.dial_option.enable_backoff -- gRPC client dial option backoff enabled + enable_backoff: false + # @schema {"name": "job.client_config.dial_option.insecure", "type": "boolean"} + # job.client_config.dial_option.insecure -- gRPC client dial option insecure enabled + insecure: true + # @schema {"name": "job.client_config.dial_option.timeout", "type": "string"} + # job.client_config.dial_option.timeout -- gRPC client dial option timeout + timeout: "" + # @schema {"name": "job.client_config.dial_option.interceptors", "type": "array", "items": {"type": "string", "enum": ["TraceInterceptor"]}} + # job.client_config.dial_option.interceptors -- gRPC client interceptors + interceptors: [] + # @schema {"name": "job.client_config.dial_option.net", "type": "object", "anchor": "net"} + net: + # @schema {"name": "job.client_config.dial_option.net.dns", "type": "object"} + dns: + # @schema {"name": "job.client_config.dial_option.net.dns.cache_enabled", "type": "boolean"} + # job.client_config.dial_option.net.dns.cache_enabled -- gRPC client TCP DNS cache enabled + cache_enabled: true + # @schema {"name": "job.client_config.dial_option.net.dns.refresh_duration", "type": "string"} + # job.client_config.dial_option.net.dns.refresh_duration -- gRPC client TCP DNS cache refresh duration + refresh_duration: 30m + # @schema {"name": "job.client_config.dial_option.net.dns.cache_expiration", "type": "string"} + # job.client_config.dial_option.net.dns.cache_expiration -- gRPC client TCP DNS cache expiration + cache_expiration: 1h + # @schema {"name": "job.client_config.dial_option.net.dialer", "type": "object"} + dialer: + # @schema {"name": "job.client_config.dial_option.net.dialer.timeout", "type": "string"} + # job.client_config.dial_option.net.dialer.timeout -- gRPC client TCP dialer timeout + timeout: "" + # @schema {"name": "job.client_config.dial_option.net.dialer.keepalive", "type": "string"} + # job.client_config.dial_option.net.dialer.keepalive -- gRPC client TCP dialer keep alive + keepalive: "" + # @schema {"name": "job.client_config.dial_option.net.dialer.dual_stack_enabled", "type": "boolean"} + # job.client_config.dial_option.net.dialer.dual_stack_enabled -- gRPC client TCP dialer dual stack enabled + dual_stack_enabled: true + # @schema {"name": "job.client_config.dial_option.net.socket_option", "type": "object"} + socket_option: + # @schema {"name": "job.client_config.dial_option.net.socket_option.reuse_port", "type": "boolean"} + # job.client_config.dial_option.net.socket_option.reuse_port -- server listen socket option for reuse_port functionality + reuse_port: true + # @schema {"name": "job.client_config.dial_option.net.socket_option.reuse_addr", "type": "boolean"} + # job.client_config.dial_option.net.socket_option.reuse_addr -- server listen socket option for reuse_addr functionality + reuse_addr: true + # @schema {"name": "job.client_config.dial_option.net.socket_option.tcp_fast_open", "type": "boolean"} + # job.client_config.dial_option.net.socket_option.tcp_fast_open -- server listen socket option for tcp_fast_open functionality + tcp_fast_open: true + # @schema {"name": "job.client_config.dial_option.net.socket_option.tcp_no_delay", "type": "boolean"} + # job.client_config.dial_option.net.socket_option.tcp_no_delay -- server listen socket option for tcp_no_delay functionality + tcp_no_delay: true + # @schema {"name": "job.client_config.dial_option.net.socket_option.tcp_cork", "type": "boolean"} + # job.client_config.dial_option.net.socket_option.tcp_cork -- server listen socket option for tcp_cork functionality + tcp_cork: false + # @schema {"name": "job.client_config.dial_option.net.socket_option.tcp_quick_ack", "type": "boolean"} + # job.client_config.dial_option.net.socket_option.tcp_quick_ack -- server listen socket option for tcp_quick_ack functionality + tcp_quick_ack: true + # @schema {"name": "job.client_config.dial_option.net.socket_option.tcp_defer_accept", "type": "boolean"} + # job.client_config.dial_option.net.socket_option.tcp_defer_accept -- server listen socket option for tcp_defer_accept functionality + tcp_defer_accept: true + # @schema {"name": "job.client_config.dial_option.net.socket_option.ip_transparent", "type": "boolean"} + # job.client_config.dial_option.net.socket_option.ip_transparent -- server listen socket option for ip_transparent functionality + ip_transparent: false + # @schema {"name": "job.client_config.dial_option.net.socket_option.ip_recover_destination_addr", "type": "boolean"} + # job.client_config.dial_option.net.socket_option.ip_recover_destination_addr -- server listen socket option for ip_recover_destination_addr functionality + ip_recover_destination_addr: false + # @schema {"name": "job.client_config.dial_option.keepalive", "type": "object"} + keepalive: + # @schema {"name": "job.client_config.dial_option.keepalive.time", "type": "string"} + # job.client_config.dial_option.keepalive.time -- gRPC client keep alive time + time: "120s" + # @schema {"name": "job.client_config.dial_option.keepalive.timeout", "type": "string"} + # job.client_config.dial_option.keepalive.timeout -- gRPC client keep alive timeout + timeout: "30s" + # @schema {"name": "job.client_config.dial_option.keepalive.permit_without_stream", "type": "boolean"} + # job.client_config.dial_option.keepalive.permit_without_stream -- gRPC client keep alive permit without stream + permit_without_stream: true +# @schema {"name": "resources", "type": "object"} +# resources -- kubernetes resources of pod +resources: + # @schema {"name": "resources.limits", "type": "object"} + limits: + cpu: 300m + memory: 300Mi + # @schema {"name": "resources.requests", "type": "object"} + requests: + cpu: 200m + memory: 200Mi +# @schema {"name": "logging", "type": "object"} +logging: + # @schema {"name": "logging.logger", "type": "string", "enum": ["glg", "zap"]} + # logging.logger -- logger name. + logger: glg + # @schema {"name": "logging.level", "type": "string", "enum": ["debug", "info", "warn", "error", "fatal"]} + # logging.level -- logging level. + level: debug + # @schema {"name": "logging.format", "type": "string", "enum": ["raw", "json"]} + # logging.format -- logging format. + format: raw +``` + +
+ +For more details of the configuration of `vald-benchmark-operator-release`, please refer to [here](https://github.com/vdaas/vald/blob/main/charts/operator/benchmark/values.yaml) + +1. Add Vald repo into the helm repo + + ```bash + helm repo add vald https://vdaas.vald.org + ``` + +1. Deploy `vald-benchmark-operator-release` + + ```bash + helm install vald-benchmark-operator-release vald/vald-benchmark-operator + ``` + +1. Apply `vbor.yaml` (optional) + + ```bash + kubectl apply -f vbor.yaml + ``` + +## Running Continuous Benchmarks + +After deploy the benchmark operator, you can execute continuous benchmark by applying `ValdBenchmarkScenarioRelease` or `ValdBenchmarkJobRelease`. + +Please configure designed benchmark and apply by `kubectl` command. + +The sample manifests are [here](https://github.com/vdaas/vald/tree/main/example/helm/benchmark). + +## Monitoring Benchmark Job Metrics + +Metrics monitoring can be set in the same way as Vald cluster. +For information on building a monitoring environment, please refer to [Observability Configuration](/docs/v1.8/user-guides/observability-configuration). + +To monitor metrics about continuous benchmarking, please edit `ValdBenchmarkOperatorRelease` as follows: + +```yaml +--- +# @schema {"name": "observability", "type": "object"} +observability: + # @schema {"name": "observability.enabled", "type": "boolean"} + enabled: true + # @schema {"name": "observability.otlp", "type": "object"} + otlp: + # @schema {"name": "observability.otlp.collector_endpoint", "type": "string"} + # Please confirm correct collector_endpoint + collector_endpoint: "opentelemetry-collector-collector.default.svc.cluster.local:4317" + trace: + # @schema {"name": "observability.trace.enabled", "type": "boolean"} + enabled: true +``` + +After apply it, the metrics can be shown on the Grafana dashboard like as below. + +Grafana dashboard showing benchmark metrics diff --git a/content/docs/v1.8/performance/loadtest.md b/content/docs/v1.8/performance/loadtest.md new file mode 100644 index 000000000..85e3c8c9e --- /dev/null +++ b/content/docs/v1.8/performance/loadtest.md @@ -0,0 +1,117 @@ +--- +title: "Loadtest_v1.8/Performance" +date: 2026-09-16T04:40:21Z +draft: false +weight: 200 +description: Shows how to perform load test in the Vald cluster +menu: + performance: + parent: Performance +--- + +# Load Testing + +**DEPRECATED** + +This document describes how to perform the load testing in the Vald cluster or Vald Agent using Vald Load Testing Tools. + +## Overview + +We develop our Load Testing Tools to perform the load testing in the Vald cluster or the Vald Agent. +You can perform the load test on the Vald cluster to test the insert and search performance in Vald. + +## Prerequisite + +- Vald Cluster or Vald Agent + + Please refer to [here](https://vald.vdaas.org/docs/tutorial/get-started/) for the installation guide of Vald Cluster or Vald Agent. + +- Docker: v19.03 ~ + +## Install load test tools + +We provide a docker image for the load testing. +Please download the image using the below command. + +```bash +docker pull vdaas/vald-loadtest +``` + +## Configure load test tools + +Please refer to the [Sample configuration file](https://github.com/vdaas/vald/blob/main/cmd/tools/cli/loadtest/sample.yaml) to configure the Vald Load Testing Tools. + +Here are the important configurations and following the explanation. + +| Name | Description | Example | +| :---------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------- | +| service | By setting the value to the gateway, you can test the Vald cluster through the Vald gateway.
By setting the value to agent, you can test the Vald agent directly. | gateway or agent | +| operation | To perform the specific load test action to the Vald cluster. | insert, streaminsert, search or streamsearch | +| dataset | The dataset is used in insert and stream insert operation. | | +| concurrency | The number of concurrent execution of the load test. | | +| batch_size | The batch size of the dataset. | | +| addr | The cluster you want to test | | + +## Execute load test + +Download the sample configuration file. + +```bash +wget https://raw.githubusercontent.com/vdaas/vald/main/cmd/tools/cli/loadtest/sample.yaml +mv sample.yaml config.yaml +``` + +Please refer to the [configure load test tools](#configure-load-test-tools) section to configure the load testing tool. + +```bash +vi config.yaml +``` + +Execute the load test in docker. + +```bash +docker run -it --rm -v $(pwd):/etc/server vald-load-test +``` + +## Result + +After the Vald Load Testing tools finished, the following output will be displayed. + +```bash +2020-07-13 08:10:45 [INFO]: maxprocs: Leaving GOMAXPROCS=8: CPU quota undefined +2020-07-13 08:10:45 [INFO]: service load test v0.0.0 starting... +2020-07-13 08:10:45 [INFO]: executing daemon pre-start function +2020-07-13 08:10:45 [DEBG]: start loading: random-786-100000 +2020-07-13 17:10:49 [INFO]: executing daemon start function +2020-07-13 17:10:49 [INFO]: start load test(Gateway, Insert) +2020-07-13 17:10:52 [INFO]: progress 177 requests, 5899.977806[vps], error: 0 +2020-07-13 17:10:55 [INFO]: progress 370 requests, 6166.631312[vps], error: 0 +2020-07-13 17:10:58 [INFO]: progress 558 requests, 6199.932718[vps], error: 0 +2020-07-13 17:11:01 [INFO]: progress 727 requests, 6058.303243[vps], error: 0 +2020-07-13 17:11:04 [INFO]: progress 910 requests, 6066.658704[vps], error: 0 +2020-07-13 17:11:05 [INFO]: result:Gateway 32 100 6060.743248 +2020-07-13 17:11:05 [WARN]: terminated signal received daemon will stopping soon... +2020-07-13 17:11:05 [INFO]: executing daemon pre-stop function +2020-07-13 17:11:05 [INFO]: executing daemon stop function +2020-07-13 17:11:05 [INFO]: executing daemon post-stop function +2020-07-13 17:11:05 [ERR]: error occurred in runner.Wait at load test: context canceled +2020-07-13 17:11:05 [WARN]: daemon stopped +``` + +These lines show the result of the load test. + +```bash +2020-07-13 08:10:45 [DEBG]: start loading: random-786-100000 +... +2020-07-13 17:10:49 [INFO]: start load test(Gateway, Insert) +... +2020-07-13 17:11:05 [INFO]: result:Gateway 32 100 6060.743248 +``` + +This means that the `Insert Gateway` mode is used, which means the dataset `random-786-100000` will insert into the Vald Cluster. + +The line `result:Gateway 32 100 6060.743248` means that the Gateway mode is used, with `32` concurrent execution and `100` batch size, with the VPS (Vector Per Sec.) of `6060.743248`. + +It means that is performed the insertion of 6060 vectors into the Vald Cluster per second from the Vald Load Testing tools. + +The result includes all the network latency and filtering latency. diff --git a/content/docs/v1.8/performance/tuning-search-performance.md b/content/docs/v1.8/performance/tuning-search-performance.md new file mode 100644 index 000000000..2463e9c1a --- /dev/null +++ b/content/docs/v1.8/performance/tuning-search-performance.md @@ -0,0 +1,75 @@ +--- +title: "Tuning Search Performance_v1.8/Performance" +date: 2026-09-16T04:40:21Z +draft: false +weight: 0 +description: "" +menu: + performance: + parent: Performance +--- + +# Tuning Search Performance + +ANN is fast, but sometimes it can be improved more by tuning parameters. +This page shows the essence for improve ANN search by the Vald cluster. + +## Tuning Guideline + +When the search results do NOT satisfy the expected result, it can be improved by tuning parameters. + +First of all, we recommend tuning by following the steps below without doing it blindly. + +```mermaid +flowchart TD + A[Perform Linear Search API] + B{Is satisfies?} + C[Tuning Parameters to improve precision] + D[Tuning Embedding Models] + E[Perform Search API] + F[Tuning Parameters to improve latency] + A-->B + B-- Yes -->C + B-- No -->D + C--> E + E--> C + E--> F + F--> E +``` + +The best practice is: + +1. Measure [Linear Search](/docs/v1.8/docs/api/search#linearsearch-rpc) performance and use it as a baseline for Search API +1. Repeat tuning to improve precision and measure Search API until the conditions are met +1. Repeat tuning to improve latency and measure Search API until the conditions are met + +
+When the results are not good by Linear Search API, it may need to rethink the embedding model for vectorization. +
+ +## Tuning parameters + +There are two viewpoints, client-side and cluster-side, for improving search performance. + +
+There is a trade-off between search speed and accuracy, so tuning accuracy at first is recommended. +
+ +### Client side + +On the client side, parameters of [`Search.Config`](/docs/v1.8/docs/api/search#input) will affect the search result. + +| | description | how does it affect? | memo | +| :---------- | :-------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------ | +| radius | the search radius for NGT
[ref: search_radius](https://github.com/NGT-labs/NGT/tree/main/bin/ngt#search) | Define the search range when NGT searches the nearest neighbors | recommended value: `-1` | +| epsilon | the search coefficient for NGT
[ref: search_range_coefficient](https://github.com/NGT-labs/NGT/tree/main/bin/ngt#search) | Expansion factor of the NGT search range.
Search operation time increases when the epsilon is big. | recommended value range: `0.01 ~ 0.3`
default value: `0.1` | +| timeout(ns) | max time duration until receiving search results. | An error will be returned if the set `num` search results cannot be obtained within the set time.
By setting `min_num`, the search results will be returned if more than `min_num` can be searched within the time. | default value: `3,000,000,000ns` | + +### Cluster-side + +On the cluster side, these parameters can be set by `values.yaml`, affect the search result. + +| | description | how does it affect? | Memo | +| :--------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------ | +| agent.ngt.creation_edge_size | Number of nodes connected to one node
[ref: no_of_edges](https://github.com/NGT-labs/NGT/tree/main/bin/ngt#create) | It helps reduce unreachable edges.
The larger it is, the denser the graph structure will be, but the memory usage, search speed, and index construction time will increase accordingly. | default value: `20` | +| agent.ngt.search_edge_size | Number of nodes to search from the origin node when searching
[ref: no_of_edges_at_search_at](https://github.com/NGT-labs/NGT/tree/main/bin/ngt#create) | The number of nodes to search will increase.
Accuracy will be higher, but speed will be lower.
Adjust if adjusting the radius and epsilon does not improve the situation. | default value: `10` | diff --git a/content/docs/v1.8/release/_index.md b/content/docs/v1.8/release/_index.md new file mode 100644 index 000000000..33d3ab8e2 --- /dev/null +++ b/content/docs/v1.8/release/_index.md @@ -0,0 +1,7 @@ +--- +title: "Release_v1.8/Release" +date: 2026-09-16T04:40:21Z +draft: false +weight: 0 +--- + diff --git a/content/docs/v1.8/release/changelog.md b/content/docs/v1.8/release/changelog.md new file mode 100644 index 000000000..fa7e1a96e --- /dev/null +++ b/content/docs/v1.8/release/changelog.md @@ -0,0 +1,6780 @@ +--- +title: "Changelog_v1.8/Release" +date: 2026-09-16T04:40:18Z +draft: false +weight: 0 +description: "" +menu: + release: + parent: Release +--- + +# CHANGELOG + +## v1.8.0 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.8.0
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.8.0 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.8.0
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.8.0 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.8.0
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.8.0 +
Gateways + docker pull vdaas/vald-lb-gateway:v1.8.0
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.8.0
+ docker pull vdaas/vald-filter-gateway:v1.8.0
+ docker pull ghcr.io/vdaas/vald/vald-filter-gateway:v1.8.0 +
Index Manager + docker pull vdaas/vald-manager-index:v1.8.0
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.8.0 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.8.0
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.8.0 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.8.0) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.8.0/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.8.0/charts/operator/helm/README.md) + +### Changes + + + +### Description + +:sparkle: New feature + +- [Feature] Add Vald Operator (enabled multiple cluster operation) [#3557] +- feat(helm): add configurable image registry for all images [#3538] +- Support dynamic edge size parameter for Search API [#3480] +- add creator.creation_pool_size [#3443] +- Add TiKV client for Metadata API [#3401] +- Apply default epsilon to index creation as well on local branch [#3320] +- hot-reload (minimal test impl, opt-in) [#3247] +- add txnkv [#3311] +- Add resource stats gRPC API [#3243] +- add internal kvs [#3281] +- add metadata manifest [#3260] +- Add optional tls support for gateway ingress [#3226] +- add executeRPC function for gRPC client [#3079] +- [Feature] Add Persistent VQueue [#3058] +- [Feature] Add Production Bidirectional KVS [#3057] + +:zap: Improve performance + +- resolve broadcast concurrency and tune connection pool [#3518] +- improve rangeConns's performance for Release v1.7.17 [#3039] + +:recycle: Refactor + +- Refactor gRPC codes and update libs & feat: Migrate from npm to bun [#3264] +- ♻️ Refactor: bidirectional map module structure [#3218] +- refactor: add missing k8s type aliases to types.go [#3439] +- Migrate from bincode to wincode [#3448] +- Omit usearch implementation from Go [#3319] +- Update default parameters for gRPC [#3239] +- Migrate go tool [#3209] +- Add strings.TrimForCompare func [#3064] +- check port-forward enabled/disabled [#3061] +- Add RBAC permissions for DaemonSet access [#3059] + +:bug: Bugfix + +- hotfix: Remove stray ci-container in release workflow [#3584] +- fix: skip NGT AVX2 on arm64 [#3580] +- fix SBOM [#3565] +- [Bugfix] fix SIGABRT problem for Agent NGT [#3531] +- fix typo [#3453] +- fix(rust/proto): resolve build errors by correcting prost_types usage [#3403] +- :bug: fix grpc benchmark [#3342] +- hotfix: Use init instead of once.Do to prevent data race [#3328] +- fix docker build condition [#3289] +- fix tag [#3277] +- fix extra tag process [#3275] +- [Bugfix] unknown serviceAccount use for serviceAccountName [#3252] +- fix faiss_create_index_ivfpq [#3249] +- Added serviceAccountName to lb/filter gateways and bugfix [#3230] +- Fix dashboard variables in overview board [#3030] + +:pencil2: Document + +- add ADOPTERS.md, MAINTAINERS.md [#3556] +- Add AGENTS.md for AI Co-Worker standardization [#3473] +- restore loadtest.md [#3187] + +:white_check_mark: Testing + +- feat(e2e): Implement and refactor metrics collection for E2E tests [#3418] +- Add support for patial execution of e2e v2 [#3297] +- Unified E2E V2 YAML setttings using envvars [#3105] +- [VALD-350] Add retry_until_success_timeout and use it in readreplica CI [#3104] +- Run E2E Test as a Kubernetes Job [#3073] +- [VALD-326] E2E with Kind & Support readreplica/rollout case [#3054] +- Add Agent Execution for E2E V2 [#3074] +- Added Loop test case [#3063] + +:green_heart: CI + +- fix CI permission [#3609] +- fix scan docker image job [#3602] +- hotfix: Pin sha of trivy and fix permission [#3601] +- hotfix: Fix and update CodeQL versions [#3600] +- fix update helm chart job [#3599] +- fix release workflow [#3594] +- Use ubuntu-latest in release workflow [#3590] +- hotfix: Install chaos-mesh using Helm [#3521] +- chore(coderabbit): add coderabbit configuration [#3464] +- Merge dev and ci container [#3425] +- Use golangci-lint as a formatter [#3405] +- Use ubuntu-slim for lightweight jobs [#3312] +- Add wait --for=create [#3303] +- hotfix: Support workflow_dispatch images for e2e v2 [#3295] +- hotfix: build target architecture for main branch [#3268] +- Fix joining architectures [#3262] +- hotfix: docker build failure on GitHub Actions [#3258] +- Multi platform build with native amd64/arm64 GitHub hosted runner [#3251] +- Add GitHub actions workflow linter [#3213] +- hotfix: ARM64 dev-container build [#3229] +- hotfix: Unify image tag by nightly for format [#3214] +- Use --retry for get.helm.sh [#3195] +- revert: Platform Separation [#3192] +- hotfix: Use extra tags to push nightly [#3189] +- hotfix: separated unification job [#3186] +- Avoid overwrite for ghcr.io [#3182] +- Try to separate amd64/arm64 builds [#3178] +- hotfix: Support debugging for ChatOps [#3055] +- hotfix: Handle cherrypick without diff [#3047] +- [VALD-361] Backport changes into release v1.7 by default [#3050] +- [VALD-359] Support Mac C flags [#3040] + +:chart_with_upwards_trend: metrics/tracing + +- Add opentelemetry collector dashboard [#3208] +- [VALD-147] Implement MetricMiddleware [#3010] +- hotfix: Support prometheus without container label [#3043] + +:arrow_up: Update dependencies + +- Update dependencies [#3606] +- update dependencies and modules [#3440] +- update rust dependencies and migration [#3436] + +:lock: Security + +:art: Design + +:handshake: Contributor + +- add new chatops permissions for contributor [#3548] +- docs: add nac-39 as a contributor for infra [#3234] +- docs: add Sh1ragami as a contributor for tool, and code [#3210] +- docs: add jumpei527 as a contributor for code, and maintenance [#3205] + +### Related Issue + + + + + + +### Versions + + + +- Vald Version: v1.7.17 +- Go Version: v1.26.5 +- Rust Version: v1.97.1 +- Docker Version: v29.6.2 +- Kubernetes Version: v1.36.3 +- Helm Version: v4.2.3 +- NGT Version: v2.7.4 +- Faiss Version: v1.14.3 + +### Checklist + + + + +- [x] I have read the [CONTRIBUTING](https://github.com/vdaas/vald/blob/main/CONTRIBUTING.md) document and completed [our CLA agreement](https://cla-assistant.io/vdaas/vald). +- [x] I have checked open [Pull Requests](https://github.com/vdaas/vald/pulls) for the similar feature or fixes? + +### Special notes for your reviewer + + + +## v1.7.17 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.7.17
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.7.17 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.7.17
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.7.17 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.7.17
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.7.17 +
Gateways + docker pull vdaas/vald-lb-gateway:v1.7.17
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.7.17
+ docker pull vdaas/vald-filter-gateway:v1.7.17
+ docker pull ghcr.io/vdaas/vald/vald-filter-gateway:v1.7.17 +
Index Manager + docker pull vdaas/vald-manager-index:v1.7.17
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.7.17 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.7.17
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.7.17 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.7.17) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.7.17/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.17/charts/operator/helm/README.md) + +### Changes + +:sparkles: New feature +[VALD-125] feat: Implement index exporter [#2746](https://github.com/vdaas/vald/pull/2746) + +[VALD-147] add server_config [#2958](https://github.com/vdaas/vald/pull/2958) + +[VALD-147] implement AccessLogMiddleware [#2991](https://github.com/vdaas/vald/pull/2991) + +[VALD-148] Generate grafana boards by grafana-foundation-sdk Go [#2937](https://github.com/vdaas/vald/pull/2937) + +[VALD-322] e2e/v2: Support selector for wait action [#2956](https://github.com/vdaas/vald/pull/2956) + +[VALD-325] E2E V2: Index Correction Job [#3000](https://github.com/vdaas/vald/pull/3000) + +[VALD-336] Implement expect in E2E v2 to assert API results [#2971](https://github.com/vdaas/vald/pull/2971) + +[VALD-351] Add Rust version VQueue prototype implementation [#2998](https://github.com/vdaas/vald/pull/2998) + +Add git-lfs to dev container image [#2978](https://github.com/vdaas/vald/pull/2978) + +Fix dashboard variables in overview board (#3030) (#3042) + +[VALD-359] Support Mac C flags (#3040) (#3044) + +Support prometheus without container label (#3043) (#3045) + +:zap: Improve performance + +improve rangeConns's performance for Release v1.7.17 (#3039) (#3041) + +:bug: Bugfix +[BUGFIX] use client config for tls dialer [#2938](https://github.com/vdaas/vald/pull/2938) + +[Bugfix] prevent nil pointer panic for internal/config/grpc.go [#3017](https://github.com/vdaas/vald/pull/3017) + +[VALD-148] hotfix: Fix name & despcription of metrics [#2981](https://github.com/vdaas/vald/pull/2981) + +Avoid concurrent assignment of stream client in RoundRobin [#2954](https://github.com/vdaas/vald/pull/2954) + +fix: make format has conflicts in some targets [#2977](https://github.com/vdaas/vald/pull/2977) + +:pencil2: Document +docs: Fix a typo in the value that specified the service name [#3005](https://github.com/vdaas/vald/pull/3005) + +docs: add Kynea0b as a contributor for doc [#3006](https://github.com/vdaas/vald/pull/3006) + +docs: add Matts966 as a contributor for code, infra, and 2 more [#3007](https://github.com/vdaas/vald/pull/3007) + +:white_check_mark: Testing +E2E V2 CI [#2950](https://github.com/vdaas/vald/pull/2950) + +:green_heart: CI +feat: automatically resolve go.mod & go.sum conflicts in backport workflow [#2980](https://github.com/vdaas/vald/pull/2980) + +:chart_with_upwards_trend: Metrics/Tracing +[VALD-148] Generate grafana boards by grafana-foundation-sdk Go [#2937](https://github.com/vdaas/vald/pull/2937) + +## v1.7.16 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.7.16
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.7.16 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.7.16
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.7.16 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.7.16
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.7.16 +
Gateways + docker pull vdaas/vald-lb-gateway:v1.7.16
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.7.16
+ docker pull vdaas/vald-filter-gateway:v1.7.16
+ docker pull ghcr.io/vdaas/vald/vald-filter-gateway:v1.7.16 +
Index Manager + docker pull vdaas/vald-manager-index:v1.7.16
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.7.16 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.7.16
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.7.16 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.7.16) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.7.16/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.16/charts/operator/helm/README.md) + +### Changes + +:recycle: Refactor + +- Fix format of proto files [#2778](https://github.com/vdaas/vald/pull/2778) ([#2783](https://github.com/vdaas/vald/pull/2783)) +- Refactor merge docker and github actions workflow gen logic [#2769](https://github.com/vdaas/vald/pull/2769) ([#2774](https://github.com/vdaas/vald/pull/2774)) + +:pencil2: Document + +- Change symlink API documents [#2741](https://github.com/vdaas/vald/pull/2741) ([#2776](https://github.com/vdaas/vald/pull/2776)) + +:green_heart: CI + +- Refactor github actions [#2773](https://github.com/vdaas/vald/pull/2773) ([#2779](https://github.com/vdaas/vald/pull/2779)) + Change make command [#2765](https://github.com/vdaas/vald/pull/2765) ([#2770](https://github.com/vdaas/vald/pull/2770)) + +:arrow_up: Update dependencies + +- Update libs dependency [#2775](https://github.com/vdaas/vald/pull/2775) ([#2785](https://github.com/vdaas/vald/pull/2785)) + +## v1.7.15 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.7.15
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.7.15 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.7.15
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.7.15 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.7.15
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.7.15 +
Gateways + docker pull vdaas/vald-lb-gateway:v1.7.15
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.7.15
+ docker pull vdaas/vald-filter-gateway:v1.7.15
+ docker pull ghcr.io/vdaas/vald/vald-filter-gateway:v1.7.15 +
Index Manager + docker pull vdaas/vald-manager-index:v1.7.15
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.7.15 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.7.15
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.7.15 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.7.15) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.7.15/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.15/charts/operator/helm/README.md) + +### Changes + +:sparkles: New feature + +- Add affinity to jobTemplate [#2758](https://github.com/vdaas/vald/pull/2758) ([#2760](https://github.com/vdaas/vald/pull/2760)) +- feat: Implement delete expired index job [#2702](https://github.com/vdaas/vald/pull/2702) ([#2722](https://github.com/vdaas/vald/pull/2722)) +- Add QUIC support [#1771](https://github.com/vdaas/vald/pull/1771) +- add example-client docker image [#2705](https://github.com/vdaas/vald/pull/2705) ([#2709](https://github.com/vdaas/vald/pull/2709)) + +:recycle: Refactor + +- refactor dockerfiles and update gitattributes [#2743](https://github.com/vdaas/vald/pull/2743) ([#2745](https://github.com/vdaas/vald/pull/2745)) + +:bug: Bugfix + +- :bug: Fix update deps workflow: buf is not found [#2737](https://github.com/vdaas/vald/pull/2737) ([#2739](https://github.com/vdaas/vald/pull/2739)) +- [BUGFIX] resolve agent GetGraphStatistics API double-free error problem [#2733](https://github.com/vdaas/vald/pull/2733) +- fix rust-analyzer [#2731](https://github.com/vdaas/vald/pull/2731) ([#2732](https://github.com/vdaas/vald/pull/2732)) +- Fix installation command for arm64 [#2729](https://github.com/vdaas/vald/pull/2729) ([#2730](https://github.com/vdaas/vald/pull/2730)) +- fix not found error [#2726](https://github.com/vdaas/vald/pull/2726) ([#2727](https://github.com/vdaas/vald/pull/2727)) +- Fix bind DOCKER_OPTS option [#2718](https://github.com/vdaas/vald/pull/2718) ([#2719](https://github.com/vdaas/vald/pull/2719)) + +:pencil2: Document + +- Update README.md [#2724](https://github.com/vdaas/vald/pull/2724) ([#2725](https://github.com/vdaas/vald/pull/2725)) +- :pencil: Remove clj link [#2710](https://github.com/vdaas/vald/pull/2710) ([#2714](https://github.com/vdaas/vald/pull/2714)) + +:green_heart: CI + +- :green_heart: Multi-PF build for example-client [#2713](https://github.com/vdaas/vald/pull/2713) +- Add auto deps version update workflow [#2707](https://github.com/vdaas/vald/pull/2707) ([#2717](https://github.com/vdaas/vald/pull/2717)) + +:arrow_up: Update dependencies + +- :green_heart: use ci-container for update deps cron job [#2744](https://github.com/vdaas/vald/pull/2744) ([#2748](https://github.com/vdaas/vald/pull/2748)) +- update ubuntu version for devcontainer [#2736](https://github.com/vdaas/vald/pull/2736) ([#2750](https://github.com/vdaas/vald/pull/2750)) +- :arrow_up: update versions/BUF_VERSION [#2703](https://github.com/vdaas/vald/pull/2703) ([#2704](https://github.com/vdaas/vald/pull/2704)) + +:handshake: Contributor + +- docs: add highpon as a contributor for code [#2721](https://github.com/vdaas/vald/pull/2721) + +## v1.7.14 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.7.14
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.7.14 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.7.14
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.7.14 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.7.14
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.7.14 +
Gateways + docker pull vdaas/vald-lb-gateway:v1.7.14
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.7.14
+ docker pull vdaas/vald-filter-gateway:v1.7.14
+ docker pull ghcr.io/vdaas/vald/vald-filter-gateway:v1.7.14 +
Index Manager + docker pull vdaas/vald-manager-index:v1.7.14
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.7.14 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.7.14
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.7.14 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.7.14) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.7.14/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.14/charts/operator/helm/README.md) + +### Changes + +### :sparkles: New feature + +- Add String sorted topologicalSort [#2696](https://github.com/vdaas/vald/pull/2696) [#2698](https://github.com/vdaas/vald/pull/2698) +- Add CPU_INFO_FLAGS for Apple Silicon [#2694](https://github.com/vdaas/vald/pull/2694) [#2697](https://github.com/vdaas/vald/pull/2697) +- Add New gRPC Options and Add Reconnect Logic for connection Pool [#2685](https://github.com/vdaas/vald/pull/2685) [#2693](https://github.com/vdaas/vald/pull/2693) +- Add option to disable dns resolve [#2634](https://github.com/vdaas/vald/pull/2634) [#2641](https://github.com/vdaas/vald/pull/2641) +- Backport PR #2584 to release/v1.7 for Implement ngt property get API [#2588](https://github.com/vdaas/vald/pull/2588) +- add HTTP2 support for http.Client and Vald HTTP Server [#2572](https://github.com/vdaas/vald/pull/2572) [#2575](https://github.com/vdaas/vald/pull/2575) + +### :zap: Improve performance + +- Refactor grpc/status.withDetails function for performance [#2664](https://github.com/vdaas/vald/pull/2664) [#2668](https://github.com/vdaas/vald/pull/2668) + +### :recycle: Refactor + +- Refactor use Absolute path for Makefile [#2673](https://github.com/vdaas/vald/pull/2673) +- Refactor internal/net/grpc/client.go [#2675](https://github.com/vdaas/vald/pull/2675) +- modify ParseError to FromError for agent handler [#2667](https://github.com/vdaas/vald/pull/2667) [#2679](https://github.com/vdaas/vald/pull/2679) +- Backport PR #2674 to release/v1.7 for Refactor internal/net/grpc/client.go [#2675](https://github.com/vdaas/vald/pull/2675) +- Backport PR #2670 to release/v1.7 for Refactor use Absolute path for Makefile [#2673](https://github.com/vdaas/vald/pull/2673) +- Refactor grpc/status.withDetails function for performance [#2664](https://github.com/vdaas/vald/pull/2664) [#2668](https://github.com/vdaas/vald/pull/2668) +- Refactor for release v1.7.14 [#2639](https://github.com/vdaas/vald/pull/2639) [#2648](https://github.com/vdaas/vald/pull/2648) +- refactor(gateway): delete unused file [#2644](https://github.com/vdaas/vald/pull/2644) [#2646](https://github.com/vdaas/vald/pull/2646) +- Refactor test checkFunc condition [#2599](https://github.com/vdaas/vald/pull/2599) [#2602](https://github.com/vdaas/vald/pull/2602) +- Backport PR #2586 to release/v1.7 for modify rust package structure [#2590](https://github.com/vdaas/vald/pull/2590) +- Backport PR #2577 to release/v1.7 for refactor docker and change buildkit-syft-scanner reference to ghcr.io [#2578](https://github.com/vdaas/vald/pull/2578) + +### :bug: Bugfix + +- Fix gRPC error handling for gateway/filter handler [#2669](https://github.com/vdaas/vald/pull/2669) [#2689](https://github.com/vdaas/vald/pull/2689) +- fix: increase limit [#2683](https://github.com/vdaas/vald/pull/2683) [#2686](https://github.com/vdaas/vald/pull/2686) +- Fix gRPC error handling for mirror-gateway handler [#2665](https://github.com/vdaas/vald/pull/2665) [#2681](https://github.com/vdaas/vald/pull/2681) +- Fix gRPC error msg handling for lb-gateway handler [#2663](https://github.com/vdaas/vald/pull/2663) [#2682](https://github.com/vdaas/vald/pull/2682) +- Bugfix ingress route settings [#2636](https://github.com/vdaas/vald/pull/2636) [#2642](https://github.com/vdaas/vald/pull/2642) +- Fix broken links in the document files [#2611](https://github.com/vdaas/vald/pull/2611) [#2614](https://github.com/vdaas/vald/pull/2614) +- Fix: make command name [#2610](https://github.com/vdaas/vald/pull/2610) [#2612](https://github.com/vdaas/vald/pull/2612) +- Bugfix NGT flush logic [#2598](https://github.com/vdaas/vald/pull/2598) [#2606](https://github.com/vdaas/vald/pull/2606) + +### :pencil2: Document + +- Fix broken links in the document files [#2611](https://github.com/vdaas/vald/pull/2611) [#2614](https://github.com/vdaas/vald/pull/2614) + +### :white_check_mark: Testing + +- Refactor test checkFunc condition [#2599](https://github.com/vdaas/vald/pull/2599) [#2602](https://github.com/vdaas/vald/pull/2602) + +### :green_heart: CI + +- Buf CLI migrate to v2 [#2691](https://github.com/vdaas/vald/pull/2691) [#2695](https://github.com/vdaas/vald/pull/2695) +- [create-pull-request] automated change [#2677](https://github.com/vdaas/vald/pull/2677) [#2678](https://github.com/vdaas/vald/pull/2678) +- automatically generate workflows [#2595](https://github.com/vdaas/vald/pull/2595) [#2603](https://github.com/vdaas/vald/pull/2603) + +### :chart_with_upwards_trend: Metrics/Tracing + +- Introduce an observability crate using opentelemetry-rust [#2535](https://github.com/vdaas/vald/pull/2535) [#2609](https://github.com/vdaas/vald/pull/2609) + + + +## Summary by CodeRabbit + +- **New Features** + - Added several new contributors to the project, enhancing community involvement. + - Introduced a new configuration file for spell checking, improving documentation quality. + - Expanded the project with new configuration files, documentation, and source code for enhanced functionality. + +- **Bug Fixes** + - Updated version information in issue templates for accuracy. + +- **Documentation** + - Improved clarity in the pull request template and updated version information. + +- **Chores** + - Modified GitHub Actions for better handling of Docker image tags. + + + +## v1.7.13 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.7.13
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.7.13 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.7.13
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.7.13 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.7.13
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.7.13 +
Gateways + docker pull vdaas/vald-lb-gateway:v1.7.13
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.7.13
+ docker pull vdaas/vald-filter-gateway:v1.7.13
+ docker pull ghcr.io/vdaas/vald/vald-filter-gateway:v1.7.13 +
Index Manager + docker pull vdaas/vald-manager-index:v1.7.13
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.7.13 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.7.13
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.7.13 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.7.13) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.7.13/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.13/charts/operator/helm/README.md) + +### Changes + +### [Bugfix] + +- **General Fixes** + 1. Fix index correction process [#2565](https://github.com/vdaas/vald/pull/2565) ([#2566](https://github.com/vdaas/vald/pull/2566)) + 2. libquadmath is not available on ARM [#2559](https://github.com/vdaas/vald/pull/2559) + 3. fix: add checkout option [#2545](https://github.com/vdaas/vald/pull/2545) ([#2546](https://github.com/vdaas/vald/pull/2546)) + 4. fix: make format [#2534](https://github.com/vdaas/vald/pull/2534) ([#2540](https://github.com/vdaas/vald/pull/2540)) + 5. fix conflict bug [#2537](https://github.com/vdaas/vald/pull/2537) + 6. Bugfix that caused an error when argument has 3 or more nil arguments [#2517](https://github.com/vdaas/vald/pull/2517) ([#2520](https://github.com/vdaas/vald/pull/2520)) + 7. Bugfix recreate benchmark job when operator reboot [#2463](https://github.com/vdaas/vald/pull/2463) ([#2464](https://github.com/vdaas/vald/pull/2464)) + 8. Fix agent-faiss build failed [#2418](https://github.com/vdaas/vald/pull/2418) ([#2419](https://github.com/vdaas/vald/pull/2419)) + 9. Fix the logic to determine docker image [#2410](https://github.com/vdaas/vald/pull/2410) ([#2420](https://github.com/vdaas/vald/pull/2420)) + +- **Backport and Release-Related** + 1. Fix workflow trigger for backport pr creation [#2471](https://github.com/vdaas/vald/pull/2471) ([#2472](https://github.com/vdaas/vald/pull/2472)) + 2. Fix output settings to determine-docker-image-tag action and release branch build tag name [#2423](https://github.com/vdaas/vald/pull/2423) ([#2425](https://github.com/vdaas/vald/pull/2425)) + +- **E2E and Index** + 1. Fix e2e for read replica and add e2e for index operator [#2455](https://github.com/vdaas/vald/pull/2455) ([#2459](https://github.com/vdaas/vald/pull/2459)) + 2. Fix index job logic to pass DNS A record [#2438](https://github.com/vdaas/vald/pull/2438) ([#2448](https://github.com/vdaas/vald/pull/2448)) + +- **Documentation and Other** + 1. fix: typo of execution rule [#2426](https://github.com/vdaas/vald/pull/2426) ([#2427](https://github.com/vdaas/vald/pull/2427)) + 2. :pencil: Fix typo of file name [#2413](https://github.com/vdaas/vald/pull/2413) ([#2415](https://github.com/vdaas/vald/pull/2415)) + +### [Enhancement] + +- **General Improvements** + 1. Update dependencies, C++ standard, and improve Dockerfiles for better build systems and localization [#2549](https://github.com/vdaas/vald/pull/2549) ([#2557](https://github.com/vdaas/vald/pull/2557)) + 2. Implement ngt Statistics API [#2539](https://github.com/vdaas/vald/pull/2539) ([#2547](https://github.com/vdaas/vald/pull/2547)) + 3. refactor index manager service add index service API to expose index informations [#2525](https://github.com/vdaas/vald/pull/2525) ([#2532](https://github.com/vdaas/vald/pull/2532)) + +- **API and Logic Changes** + 1. Change default image tag from latest to nightly [#2516](https://github.com/vdaas/vald/pull/2516) ([#2518](https://github.com/vdaas/vald/pull/2518)) + 2. update large top-K ratio handling logic [#2509](https://github.com/vdaas/vald/pull/2509) ([#2511](https://github.com/vdaas/vald/pull/2511)) + 3. add inner product distance type for ngt [#2454](https://github.com/vdaas/vald/pull/2454) ([#2458](https://github.com/vdaas/vald/pull/2458)) + 4. Implement index operator logic for read replica rotation [#2444](https://github.com/vdaas/vald/pull/2444) ([#2456](https://github.com/vdaas/vald/pull/2456)) + +- **Performance and Optimization** + 1. update deps & add validation for Flush API when agent is Read Only [#2433](https://github.com/vdaas/vald/pull/2433) ([#2436](https://github.com/vdaas/vald/pull/2436)) + 2. Add `index-operator` template implementation [#2375](https://github.com/vdaas/vald/pull/2375) ([#2424](https://github.com/vdaas/vald/pull/2424)) + +- **Testing and Metrics** + 1. Implement client metrics interceptor for continuous benchmark job [#2477](https://github.com/vdaas/vald/pull/2477) ([#2480](https://github.com/vdaas/vald/pull/2480)) + 2. Add tests for index information export [#2412](https://github.com/vdaas/vald/pull/2412) ([#2414](https://github.com/vdaas/vald/pull/2414)) + +### [CI] + +1. [create-pull-request] automated change [#2552](https://github.com/vdaas/vald/pull/2552) ([#2556](https://github.com/vdaas/vald/pull/2556)) +2. Add workflow to check git conflict for backport PR [#2548](https://github.com/vdaas/vald/pull/2548) ([#2550](https://github.com/vdaas/vald/pull/2550)) +3. [CI] Add workflow to synchronize ubuntu base image [#2526](https://github.com/vdaas/vald/pull/2526) ([#2527](https://github.com/vdaas/vald/pull/2527)) +4. Automatically add backport main label for release-pr [#2473](https://github.com/vdaas/vald/pull/2473) ([#2475](https://github.com/vdaas/vald/pull/2475)) +5. change external docker image reference to ghcr.io registry [#2567](https://github.com/vdaas/vald/pull/2567) ([#2568](https://github.com/vdaas/vald/pull/2568)) + +### [Backport] + +1. Backport PR #2542, #2538 to release/v1.7 [#2543](https://github.com/vdaas/vald/pull/2543) +2. Backport docs updates to release/v1.7 [#2521](https://github.com/vdaas/vald/pull/2521) +3. Backport Flush API [#2434](https://github.com/vdaas/vald/pull/2434) + +### [Documentation] + +1. capitalize faq [#2512](https://github.com/vdaas/vald/pull/2512) ([#2522](https://github.com/vdaas/vald/pull/2522)) +2. add faiss in values.yaml & valdrelease.yaml [#2514](https://github.com/vdaas/vald/pull/2514) ([#2519](https://github.com/vdaas/vald/pull/2519)) +3. add read replica and rotator docs [#2497](https://github.com/vdaas/vald/pull/2497) ([#2499](https://github.com/vdaas/vald/pull/2499)) +4. Update continuous benchmark docs [#2485](https://github.com/vdaas/vald/pull/2485) ([#2486](https://github.com/vdaas/vald/pull/2486)) +5. docs: add hrichiksite as a contributor for doc [#2441](https://github.com/vdaas/vald/pull/2441) ([#2442](https://github.com/vdaas/vald/pull/2442)) + +### [Other] + +1. Add base of benchmark operator dashboard [#2430](https://github.com/vdaas/vald/pull/2430) ([#2453](https://github.com/vdaas/vald/pull/2453)) +2. Add client metrics panels for continuous benchmark job [#2481](https://github.com/vdaas/vald/pull/2481) ([#2483](https://github.com/vdaas/vald/pull/2483)) +3. Add unit tests for index operator [#2460](https://github.com/vdaas/vald/pull/2460) ([#2461](https://github.com/vdaas/vald/pull/2461)) +4. add reviewer guideline [#2507](https://github.com/vdaas/vald/pull/2507) ([#2508](https://github.com/vdaas/vald/pull/2508)) +5. Sync release/v1.7 to main [#2495](https://github.com/vdaas/vald/pull/2495) +6. Add snapshot timestamp annotations to read replica agent [#2428](https://github.com/vdaas/vald/pull/2428) ([#2443](https://github.com/vdaas/vald/pull/2443)) +7. Update build rule for nightly image [#2421](https://github.com/vdaas/vald/pull/2421) ([#2422](https://github.com/vdaas/vald/pull/2422)) + +## v1.7.12 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.7.12
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.7.12 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.7.12
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.7.12 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.7.12
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.7.12 +
Gateways + docker pull vdaas/vald-lb-gateway:v1.7.12
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.7.12
+ docker pull vdaas/vald-filter-gateway:v1.7.12
+ docker pull ghcr.io/vdaas/vald/vald-filter-gateway:v1.7.12 +
Index Manager + docker pull vdaas/vald-manager-index:v1.7.12
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.7.12 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.7.12
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.7.12 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.7.12) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.7.12/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.12/charts/operator/helm/README.md) + +### Changes + +:sparkles: New feature + +- Add rotate-all option to rotator [#2305](https://github.com/vdaas/vald/pull/2305) [#2393](https://github.com/vdaas/vald/pull/2393) +- Make agent export index metrics to Pod k8s resource [#2319](https://github.com/vdaas/vald/pull/2319) [#2372](https://github.com/vdaas/vald/pull/2372) + +:recycle: Refactor + +- Delete unnecessary code for mirror [#2366](https://github.com/vdaas/vald/pull/2366) [#2391](https://github.com/vdaas/vald/pull/2391) + +:bug: Bugfix + +- Resolve kvs already closed before last saving [#2390](https://github.com/vdaas/vald/pull/2390) [#2394](https://github.com/vdaas/vald/pull/2394) + +:pencil2: Document + +- Create continous benchmark doc [#2352](https://github.com/vdaas/vald/pull/2352) [#2395](https://github.com/vdaas/vald/pull/2395) + +:white_check_mark: Testing + +- Fix: build error of internal kvs test [#2396](https://github.com/vdaas/vald/pull/2396) [#2398](https://github.com/vdaas/vald/pull/2398) + +:green_heart: CI + +- Fix: disable protobuf dispatch for client [#2401](https://github.com/vdaas/vald/pull/2401) [#2403](https://github.com/vdaas/vald/pull/2403) +- Add Con-Bench helm chart to the Vald charts [#2388](https://github.com/vdaas/vald/pull/2388) [#2389](https://github.com/vdaas/vald/pull/2389) +- Update workflow to release readreplica chart [#2383](https://github.com/vdaas/vald/pull/2383) [#2387](https://github.com/vdaas/vald/pull/2387) +- Backport ci deps others [#2386](https://github.com/vdaas/vald/pull/2386) +- Update docker build target platform selection rules [#2370](https://github.com/vdaas/vald/pull/2370) [#2374](https://github.com/vdaas/vald/pull/2374) +- Add commit hash build image [#2359](https://github.com/vdaas/vald/pull/2359) [#2371](https://github.com/vdaas/vald/pull/2371) +- Refactor code using golangci-lint [#2362](https://github.com/vdaas/vald/pull/2362) [#2365](https://github.com/vdaas/vald/pull/2365) +- Change docker scan timeout longer [#2363](https://github.com/vdaas/vald/pull/2363) [#2364](https://github.com/vdaas/vald/pull/2364) + +:arrow_up: Update dependencies + +- Update deps [#2404](https://github.com/vdaas/vald/pull/2404) [#2405](https://github.com/vdaas/vald/pull/2405) + +:lock: Security + +- Create SECURITY.md [#2367](https://github.com/vdaas/vald/pull/2367) [#2368](https://github.com/vdaas/vald/pull/2368) + +:art: Design + +- Change JP logo to EN logo [#2369](https://github.com/vdaas/vald/pull/2369) [#2392](https://github.com/vdaas/vald/pull/2392) + +## v1.7.11 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.7.11
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.7.11 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.7.11
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.7.11 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.7.11
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.7.11 +
Gateways + docker pull vdaas/vald-lb-gateway:v1.7.11
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.7.11
+ docker pull vdaas/vald-filter-gateway:v1.7.11
+ docker pull ghcr.io/vdaas/vald/vald-filter-gateway:v1.7.11 +
Index Manager + docker pull vdaas/vald-manager-index:v1.7.11
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.7.11 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.7.11
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.7.11 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.7.11) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.7.11/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.11/charts/operator/helm/README.md) + +### Changes + +:sparkles: New feature + +- Add HPA for read replica [#2307](https://github.com/vdaas/vald/pull/2307) +- Add internal KVS pogreb package [#2302](https://github.com/vdaas/vald/pull/2302) +- Two version deploy support [#2171](https://github.com/vdaas/vald/pull/2171) +- Add mirror gateway definitions and Implementations [#2262](https://github.com/vdaas/vald/pull/2262) +- Initialize dev env for Rust agent [#2293](https://github.com/vdaas/vald/pull/2293) +- Add new grafana dashboard for agent memory metrics [#2279](https://github.com/vdaas/vald/pull/2279) +- Implement continuous benchmark tool [#2216](https://github.com/vdaas/vald/pull/2216) + +:recycle: Refactor + +- Add newline between params to avoid false formatting [#2347](https://github.com/vdaas/vald/pull/2347) +- Fix golangci-lint config and apply tagalign [#2326](https://github.com/vdaas/vald/pull/2326) +- Refactor postAttachCommand [#2312](https://github.com/vdaas/vald/pull/2312) +- Refactor ignore rule [#2339](https://github.com/vdaas/vald/pull/2339) +- Fix NGT default params [#2332](https://github.com/vdaas/vald/pull/2332) +- Format yaml using google/yamlfmt & update go version and dependencies [#2322](https://github.com/vdaas/vald/pull/2322) +- Refactor update opentelemetry-go & faiss [#2303](https://github.com/vdaas/vald/pull/2303) +- Change discoverer client to broadcast to read replicas [#2276](https://github.com/vdaas/vald/pull/2276) +- Add stern and telepresence [#2320](https://github.com/vdaas/vald/pull/2320) +- Add issue metrics [#2308](https://github.com/vdaas/vald/pull/2308) +- Add dispatch workflow for update contents of vdaas/web repo [#2294](https://github.com/vdaas/vald/pull/2294) +- Fix: add release build for bench and mirror [#2300](https://github.com/vdaas/vald/pull/2300) +- Fix deeepsource errors [#2299](https://github.com/vdaas/vald/pull/2299) +- Add go cache for improvement docker build performance [#2297](https://github.com/vdaas/vald/pull/2297) +- Add detailed log for readreplica rotator [#2281](https://github.com/vdaas/vald/pull/2281) +- Add isSymlink function and test to gen license to avoid for symlink to become normal file. [#2290](https://github.com/vdaas/vald/pull/2290) +- Add owner reference to the resources made by rotator to delete them when read replica resources are deleted [#2287](https://github.com/vdaas/vald/pull/2287) +- Make vald-readreplica values.yaml to symbolic link [#2286](https://github.com/vdaas/vald/pull/2286) +- Separate readreplica chart [#2283](https://github.com/vdaas/vald/pull/2283) +- Happy New Year 2024 [#2284](https://github.com/vdaas/vald/pull/2284) + +:bug: Bugfix + +- Fix: disable arm64 [#2354](https://github.com/vdaas/vald/pull/2354) +- gcc environment for ARM [#2334](https://github.com/vdaas/vald/pull/2334) +- Revert dev Dockerfile to use official devcontainer image [#2335](https://github.com/vdaas/vald/pull/2335) +- Revert docker-image.yaml change [#2336](https://github.com/vdaas/vald/pull/2336) +- Fix release pr workflow [#2333](https://github.com/vdaas/vald/pull/2333) +- Fix e2e regressions [#2327](https://github.com/vdaas/vald/pull/2327) +- Bugfix grpc ip direct connection status check [#2316](https://github.com/vdaas/vald/pull/2316) +- Fix k3d connectivity error [#2317](https://github.com/vdaas/vald/pull/2317) +- Change lincense/gen/main.go to skip shebang [#2313](https://github.com/vdaas/vald/pull/2313) +- Stop using ENV ARCH and add --platform in Dockerfile [#2304](https://github.com/vdaas/vald/pull/2304) +- gRPC pool connection health check for DNS Addr may fail during VIP member disconnection [#2277](https://github.com/vdaas/vald/pull/2277) +- Fix isSymlink function to correctly check for symbolic links [#2292](https://github.com/vdaas/vald/pull/2292) +- Disable disconnection during non-IP-direct connection [#2291](https://github.com/vdaas/vald/pull/2291) +- Fix git add chart directory for release [#2356](https://github.com/vdaas/vald/pull/2356) + +:pencil2: Document + +- Add search optimization document [#2306](https://github.com/vdaas/vald/pull/2306) +- Update capacity planning doc [#2295](https://github.com/vdaas/vald/pull/2295) + +:green_heart: CI + +- Change to dynamically switch CI container image tag [#2310](https://github.com/vdaas/vald/pull/2310) +- Add E2E tests for read replica feature [#2298](https://github.com/vdaas/vald/pull/2298) +- CI, Docker EXTRA_ARGS not working problem [#2278](https://github.com/vdaas/vald/pull/2278) + +## v1.7.10 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.7.10
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.7.10 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.7.10
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.7.10 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.7.10
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.7.10 +
Gateways + docker pull vdaas/vald-lb-gateway:v1.7.10
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.7.10
+ docker pull vdaas/vald-filter-gateway:v1.7.10
+ docker pull ghcr.io/vdaas/vald/vald-filter-gateway:v1.7.10 +
Index Manager + docker pull vdaas/vald-manager-index:v1.7.10
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.7.10 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.7.10
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.7.10 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.7.10) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.7.10/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.10/charts/operator/helm/README.md) + +### Changes + +:sparkles: New feature + +- Implement malloc metrics [#2161](https://github.com/vdaas/vald/pull/2161) +- Add svc discoverer for readreplica svc [#2263](https://github.com/vdaas/vald/pull/2263) +- Add agent readreplica resources [#2258](https://github.com/vdaas/vald/pull/2258) +- Add cronjob for readreplica rotator [#2242](https://github.com/vdaas/vald/pull/2242) + +:recycle: Refactor + +- Apply make proto/all [#2266](https://github.com/vdaas/vald/pull/2266) +- Migratation to buf [#2236](https://github.com/vdaas/vald/pull/2236) +- Update schema [#2265](https://github.com/vdaas/vald/pull/2265) + +:bug: Bugfix + +- Resolve duplicated cluster wide resources name problem [#2274](https://github.com/vdaas/vald/pull/2274) + +:pencil2: Document + +- Add caution sentence for deploy multi-Vald clusters [#2271](https://github.com/vdaas/vald/pull/2271) + +:green_heart: CI + +- Disable BUILDKIT_INLINE_CACHE on GitHub Actions [#2270](https://github.com/vdaas/vald/pull/2270) +- Fix docker build for scanning [#2269](https://github.com/vdaas/vald/pull/2269) +- change login user and token for ghcr.io & small refactor [#2268](https://github.com/vdaas/vald/pull/2268) +- Add e2e job for index management job [#2239](https://github.com/vdaas/vald/pull/2239) +- Add docker buildx cache [#2261](https://github.com/vdaas/vald/pull/2261) + +## v1.7.9 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.7.9
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.7.9 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.7.9
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.7.9 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.7.9
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.7.9 +
Gateways + docker pull vdaas/vald-lb-gateway:v1.7.9
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.7.9
+ docker pull vdaas/vald-filter-gateway:v1.7.9
+ docker pull ghcr.io/vdaas/vald/vald-filter-gateway:v1.7.9 +
Index Manager + docker pull vdaas/vald-manager-index:v1.7.9
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.7.9 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.7.9
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.7.9 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.7.9) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.7.9/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.9/charts/operator/helm/README.md) + +### Changes + +:sparkles: New feature + +- Add read replica rotator [#2241](https://github.com/vdaas/vald/pull/2241) +- Implement manifests for Index Management Job [#2235](https://github.com/vdaas/vald/pull/2235) +- Add job error to report index correction error status [#2231](https://github.com/vdaas/vald/pull/2231) +- Add implementation for save index job [#2227](https://github.com/vdaas/vald/pull/2227) +- Add implementation for create index job [#2223](https://github.com/vdaas/vald/pull/2223) +- Add index correction metrics [#2215](https://github.com/vdaas/vald/pull/2215) +- Add index correction document [#2217](https://github.com/vdaas/vald/pull/2217) +- Add make command to update template [#2212](https://github.com/vdaas/vald/pull/2212) +- Add job to check format difference [#2214](https://github.com/vdaas/vald/pull/2214) +- Add verification for index correction e2e and add clusterrole cronjobs for operator to deploy index correction [#2205](https://github.com/vdaas/vald/pull/2205) +- Add StreamListObject to LB [#2203](https://github.com/vdaas/vald/pull/2203) +- Add index correction helm templates and E2E [#2200](https://github.com/vdaas/vald/pull/2200) +- Add index correction internal logic [#2194](https://github.com/vdaas/vald/pull/2194) +- Add bbolt as internal/db/kvs [#2177](https://github.com/vdaas/vald/pull/2177) + +:zap: Improve performance + +- Improve index correction performance [#2234](https://github.com/vdaas/vald/pull/2234) + +:recycle: Refactor + +- Refactor Index Management Job [#2232](https://github.com/vdaas/vald/pull/2232) +- Fix invalid network policy schema [#2230](https://github.com/vdaas/vald/pull/2230) +- Add minikube to create volume snapshot development environment locally [#2228](https://github.com/vdaas/vald/pull/2228) +- Enable ingress resource in the get started document [#2211](https://github.com/vdaas/vald/pull/2211) +- Add step to get k3s latest version [#2206](https://github.com/vdaas/vald/pull/2206) +- Update telepresence and helm-docs installer and update deps [#2195](https://github.com/vdaas/vald/pull/2195) +- Replace x/slices with standard slices pkg [#2193](https://github.com/vdaas/vald/pull/2193) +- add benchmark and check program for core ngt [#2179](https://github.com/vdaas/vald/pull/2179) + +:bug: Bugfix + +- Revert vtpool for ResourceExhausted problem [#2255](https://github.com/vdaas/vald/pull/2255) +- Fix deleted contour ingress controller apply [#2229](https://github.com/vdaas/vald/pull/2229) + +:pencil2: Document + +- Add document for RemoveByTimestamp RPC [#2238](https://github.com/vdaas/vald/pull/2238) + +:green_heart: CI + +- Disable exhaustruct [#2240](https://github.com/vdaas/vald/pull/2240) +- Fix fails when there are format differences [#2226](https://github.com/vdaas/vald/pull/2226) + +:arrow_up: Update dependencies + +- update deps [#2208](https://github.com/vdaas/vald/pull/2208) +- update dependencies [#2260](https://github.com/vdaas/vald/pull/2260) + +## v1.7.8 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.7.8
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.7.8 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.7.8
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.7.8 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.7.8
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.7.8 +
Gateways + docker pull vdaas/vald-lb-gateway:v1.7.8
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.7.8
+ docker pull vdaas/vald-filter-gateway:v1.7.8
+ docker pull ghcr.io/vdaas/vald/vald-filter-gateway:v1.7.8 +
Index Manager + docker pull vdaas/vald-manager-index:v1.7.8
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.7.8 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.7.8
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.7.8 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.7.8) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.7.8/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.8/charts/operator/helm/README.md) + +### Changes + +♻️ Refactor + +- change default creation poolsize [#2190](https://github.com/vdaas/vald/pull/2190) +- List kvs and vqueue data [#2188](https://github.com/vdaas/vald/pull/2188) +- refactor semver ci [#2189](https://github.com/vdaas/vald/pull/2189) + +## v1.7.7 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.7.7
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.7.7 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.7.7
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.7.7 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.7.7
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.7.7 +
Gateways + docker pull vdaas/vald-lb-gateway:v1.7.7
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.7.7
+ docker pull vdaas/vald-filter-gateway:v1.7.7
+ docker pull ghcr.io/vdaas/vald/vald-filter-gateway:v1.7.7 +
Index Manager + docker pull vdaas/vald-manager-index:v1.7.7
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.7.7 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.7.7
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.7.7 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.7.7) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.7.7/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.7/charts/operator/helm/README.md) + +### Changes + +:sparkles: New feature + +- Add CopyBuffer to copy.go [#2167](https://github.com/vdaas/vald/pull/2167) +- Add Vald standard internal/sync package [#2153](https://github.com/vdaas/vald/pull/2153) +- Add RemoveByTimestamp RPC [#2158](https://github.com/vdaas/vald/pull/2158) +- Implement StreamListObject and its tests [#2145](https://github.com/vdaas/vald/pull/2145) +- Add apiversion capabilities check to helm template [#2137](https://github.com/vdaas/vald/pull/2137) +- Add timestamp field to Object.Vector [#2136](https://github.com/vdaas/vald/pull/2136) +- Add gache's generic Map as internal/sync.Map and replace standard sync.Map with it [#2115](https://github.com/vdaas/vald/pull/2115) +- Make internal/cache generic [#2104](https://github.com/vdaas/vald/pull/2104) +- Install additional tools for docker image for devcontainer [#2101](https://github.com/vdaas/vald/pull/2101) +- Install buf and apply buf format [#2094](https://github.com/vdaas/vald/pull/2094) +- Add backup origin when CoW enabled and failed to load primary [#2091](https://github.com/vdaas/vald/pull/2091) +- Add decode kvsdb tool [#2059](https://github.com/vdaas/vald/pull/2059) +- Add user custom network policy [#2078](https://github.com/vdaas/vald/pull/2078) + +:recycle: Refactor + +- Refactor agent ngt core. [#2172](https://github.com/vdaas/vald/pull/2172) +- Refactor proto [#2173](https://github.com/vdaas/vald/pull/2173) +- Refactor search status [#2168](https://github.com/vdaas/vald/pull/2168) +- Refactor internal/core/algorithm/ngt mutex lock timing [#2144](https://github.com/vdaas/vald/pull/2144) +- Refactor github actions [#2141](https://github.com/vdaas/vald/pull/2141) +- Update license text [#2169](https://github.com/vdaas/vald/pull/2169) +- Refactor agent error not to wrap with details for performance issue [#2154](https://github.com/vdaas/vald/pull/2154) +- Use internal comparator instead of go-cmp [#2132](https://github.com/vdaas/vald/pull/2132) +- Refactor context [#2121](https://github.com/vdaas/vald/pull/2121) +- Propagate context to Search operation. [#2117](https://github.com/vdaas/vald/pull/2117) +- Refactor fix url http to https [#2090](https://github.com/vdaas/vald/pull/2090) +- Update "make gotests/gen" command [#2085](https://github.com/vdaas/vald/pull/2085) + +:bug: Bugfix + +- Fix duplicate make command [#2165](https://github.com/vdaas/vald/pull/2165) +- Add timestamp check for GetObject e2e [#2142](https://github.com/vdaas/vald/pull/2142) +- Modified apiversion capabilities check [#2149](https://github.com/vdaas/vald/pull/2149) +- Fix ngt index path of test case [#2130](https://github.com/vdaas/vald/pull/2130) +- Fix hack/benchmark search interface change [#2129](https://github.com/vdaas/vald/pull/2129) +- Fix internal/gache definition variable type [#2123](https://github.com/vdaas/vald/pull/2123) +- Use GOBIN instead of GOPATH/bin [#2102](https://github.com/vdaas/vald/pull/2102) +- Fix jaeger operator wait logic [#2114](https://github.com/vdaas/vald/pull/2114) +- Fix make k8s/metrics/jaeger/deploy failure [#2077](https://github.com/vdaas/vald/pull/2077) +- Bugfix Makefile KUBECONFIG recursive reference [#2089](https://github.com/vdaas/vald/pull/2089) +- Fix deploy command [#2088](https://github.com/vdaas/vald/pull/2088) +- Fix non-trusted module problem of v1.7.6 and disable not found debug message [#2076](https://github.com/vdaas/vald/pull/2076) +- Bugfix lb gateway pacicked caused by pairing heap search aggregator makes nil pointer when empty search result [#2181](https://github.com/vdaas/vald/pull/2181) + +:pencil2: Document + +- Update testing guideline for updated testing policy [#2131](https://github.com/vdaas/vald/pull/2131) +- Add troubleshooting for each rpc [#2163](https://github.com/vdaas/vald/pull/2163) +- Fix format network policy document [#2108](https://github.com/vdaas/vald/pull/2108) +- Add broken index backup document [#2096](https://github.com/vdaas/vald/pull/2096) +- Add network policy document [#2095](https://github.com/vdaas/vald/pull/2095) +- Fix 404 URL link [#2098](https://github.com/vdaas/vald/pull/2098) +- Update observability document [#2086](https://github.com/vdaas/vald/pull/2086) +- Fix typo of contribution guide [#2087](https://github.com/vdaas/vald/pull/2087) +- Update docs: search API and client API config [#2081](https://github.com/vdaas/vald/pull/2081) + +:white_check_mark: Testing + +- Re-Generate test codes [#2107](https://github.com/vdaas/vald/pull/2107) +- Update golangci-lint configuration: use white-list configuration pattern [#2106](https://github.com/vdaas/vald/pull/2106) + +:green_heart: CI + +- Fix coverage CI error [#2150](https://github.com/vdaas/vald/pull/2150) +- Remove some linters to make ci faster [#2116](https://github.com/vdaas/vald/pull/2116) + +:chart_with_upwards_trend: Metrics/Tracing + +- Divide latency of CreateIndex and SaveIndex metrics [#2099](https://github.com/vdaas/vald/pull/2099) +- Add broken index count metrics [#2083](https://github.com/vdaas/vald/pull/2083) + +:arrow_up: Update dependencies + +- Update go modules [#2092](https://github.com/vdaas/vald/pull/2092) + +:art: Design + +- Modified svg images [#2178](https://github.com/vdaas/vald/pull/2178) + +## v1.7.6 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.7.6
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.7.6 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.7.6
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.7.6 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.7.6
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.7.6 +
Gateways + docker pull vdaas/vald-lb-gateway:v1.7.6
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.7.6
+ docker pull vdaas/vald-filter-gateway:v1.7.6
+ docker pull ghcr.io/vdaas/vald/vald-filter-gateway:v1.7.6 +
Index Manager + docker pull vdaas/vald-manager-index:v1.7.6
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.7.6 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.7.6
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.7.6 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.7.6) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.7.6/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.6/charts/operator/helm/README.md) + +### Changes + +⚠️ ⚠️ ⚠️ Warning ⚠️ ⚠️ ⚠️ + +- `v1.7.6` does not support `vald-agent-sidecar` for some reason. +- You can use the `vald-agent-sidecar` by setting the `vald-agent-ngt` image tag as `v1.7.5` or earlier. +- We will support `vald-agent-sidecar` in the future version again. + +:sparkles: New feature + +- Add search algorithm benchmark and update search aggregation algo [#2044](https://github.com/vdaas/vald/pull/2044) +- Add broken index backup [#2034](https://github.com/vdaas/vald/pull/2034) +- Add network policy [#2022](https://github.com/vdaas/vald/pull/2022) + +:recycle: Refactor + +- Add save index operation log [#2048](https://github.com/vdaas/vald/pull/2048) +- Added flg that can disable to ingress defaultBackend [#1976](https://github.com/vdaas/vald/pull/1976) +- Refactor and Add test for service/ngt.go [#2040](https://github.com/vdaas/vald/pull/2040) +- Add e2e envs to devcontainer [#2032](https://github.com/vdaas/vald/pull/2032) +- Update RoundTrip retry condition [#2033](https://github.com/vdaas/vald/pull/2033) + +:bug: Bugfix + +- Fix fp16 problems [#2049](https://github.com/vdaas/vald/pull/2049) +- Add KUBECTL_VERSION value to workflow [#2052](https://github.com/vdaas/vald/pull/2052) +- Remove sudo from kubectl and small refactor around os/arch [#2037](https://github.com/vdaas/vald/pull/2037) +- Disable vtproto pooling due to the performance degradation [#2063](https://github.com/vdaas/vald/pull/2063) +- Fix to create index_path when it does not exists [#2060](https://github.com/vdaas/vald/pull/2060) + +:pencil2: Document + +- Add documentation for devcontiner [#2042](https://github.com/vdaas/vald/pull/2042) +- Create README for each docker image [#2014](https://github.com/vdaas/vald/pull/2014) + +:green_heart: CI + +- Disable deepsource TestCoverage due to the Deepsource Coverage collect server timeout is too short for Vald testing [#2038](https://github.com/vdaas/vald/pull/2038) +- Update Docker Build workflow with forked sources [#2036](https://github.com/vdaas/vald/pull/2036) +- Fix e2e-max-dim test [#2028](https://github.com/vdaas/vald/pull/2028) +- Fix E2E actions on PR [#2025](https://github.com/vdaas/vald/pull/2025) +- Change E2E actions to use local charts on PR [#2024](https://github.com/vdaas/vald/pull/2024) +- Update format chatops [#2021](https://github.com/vdaas/vald/pull/2021) +- Format code with prettier and gofumpt [#2015](https://github.com/vdaas/vald/pull/2015) + +:chart_with_upwards_trend: Metrics/Tracing + +- Add command to deploy monitoring stack [#2030](https://github.com/vdaas/vald/pull/2030) +- Fixed duplicate counting in CPU graphs [#2019](https://github.com/vdaas/vald/pull/2019) + +:arrow_up: Update dependencies + +- Update go modules [#2053](https://github.com/vdaas/vald/pull/2053) +- Update NGT version [#2026](https://github.com/vdaas/vald/pull/2026) + +:handshake: Contributor + +- Add takuyaymd as a contributor for maintenance [#2020](https://github.com/vdaas/vald/pull/2020) + +## v1.7.5 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.7.5
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.7.5 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.7.5
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.7.5 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.7.5
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.7.5 +
Gateways + docker pull vdaas/vald-lb-gateway:v1.7.5
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.7.5
+ docker pull vdaas/vald-filter-gateway:v1.7.5
+ docker pull ghcr.io/vdaas/vald/vald-filter-gateway:v1.7.5 +
Index Manager + docker pull vdaas/vald-manager-index:v1.7.5
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.7.5 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.7.5
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.7.5 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.7.5) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.7.5/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.5/charts/operator/helm/README.md) + +### Changes + +:sparkles: New features + +- Add index with timestamp [#1984](https://github.com/vdaas/vald/pull/1984) + +:recycle: Refactor + +- Improve errors.Join performance [#2010](https://github.com/vdaas/vald/pull/2010) +- Refactor error returning timing of doSearch function [#1996](https://github.com/vdaas/vald/pull/1996) +- Refactor makefile for non GOPATH strict environment #2 [#1998](https://github.com/vdaas/vald/pull/1998) +- Refactor makefile for non GOPATH strict environment [#1997](https://github.com/vdaas/vald/pull/1997) + +:bug: Bugfix + +- Correction of a bug that returned NotFound as success 0 when balancedUpdate is disabled and all ReplicaAgents are AlreadyExists (already have the exact same Index). [#2011](https://github.com/vdaas/vald/pull/2011) +- Refactor replace errors wrap with join [#2001](https://github.com/vdaas/vald/pull/2001) +- Remove nvimlog [#1994](https://github.com/vdaas/vald/pull/1994) + +:green_heart: CI + +- Fix chatops format workflow [#2007](https://github.com/vdaas/vald/pull/2007) +- Fix incorrect error output of gen-test chatopts command [#2004](https://github.com/vdaas/vald/pull/2004) +- Fix Makefile bug and update deps for checking bugfix [#2002](https://github.com/vdaas/vald/pull/2002) +- Output error to chatops comment [#1999](https://github.com/vdaas/vald/pull/1999) +- Fix ChatOpts /gen-test command error [#1993](https://github.com/vdaas/vald/pull/1993) + +:pencil2: Document + +- Update unit test guideline for unimplemented test [#1983](https://github.com/vdaas/vald/pull/1983) + +:white_check_mark: Testing + +- Implement generic function tests [#2008](https://github.com/vdaas/vald/pull/2008) +- Generate empty test using /gen-test ChatOpts command [#2005](https://github.com/vdaas/vald/pull/2005) +- Update internal/info test and add new case for coverage [#2003](https://github.com/vdaas/vald/pull/2003) + +:arrow_up: Update dependencies + +- Update go module and libs [#2012](https://github.com/vdaas/vald/pull/2012) + +:handshake: Contributor + +- Add ykadowak as a contributor for code, and test [#2009](https://github.com/vdaas/vald/pull/2009) + +## v1.7.4 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.7.4
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.7.4 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.7.4
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.7.4 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.7.4
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.7.4 +
Gateways + docker pull vdaas/vald-lb-gateway:v1.7.4
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.7.4
+ docker pull vdaas/vald-filter-gateway:v1.7.4
+ docker pull ghcr.io/vdaas/vald/vald-filter-gateway:v1.7.4 +
Index Manager + docker pull vdaas/vald-manager-index:v1.7.4
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.7.4 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.7.4
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.7.4 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.7.4) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.7.4/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.4/charts/operator/helm/README.md) + +### Changes + +:bug: Bug fix + +- Fix range concurrency branch rule [#1986](https://github.com/vdaas/vald/pull/1986) +- Update makefile for "not implemented" placeholder [#1967](https://github.com/vdaas/vald/pull/1977) +- Non-gRPC style error parse result returns Unknown status, it should be re-parse to find inside status [#1981](https://github.com/vdaas/vald/pull/1981) +- Enable gorules [#1980](https://github.com/vdaas/vald/pull/1980) +- Format code with prettier and gofumpt [#1971](https://github.com/vdaas/vald/pull/1971) + +:memo: Document + +- Fix documentation typo disable_balanced_update [#1978](https://github.com/vdaas/vald/pull/1978) + +:handshake: Contributor + +- docs: add junsei-ando as a contributor for doc [#1979](https://github.com/vdaas/vald/pull/1979) + +## v1.7.3 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.7.3
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.7.3 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.7.3
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.7.3 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.7.3
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.7.3 +
Gateways + docker pull vdaas/vald-lb-gateway:v1.7.3
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.7.3
+ docker pull vdaas/vald-filter-gateway:v1.7.3
+ docker pull ghcr.io/vdaas/vald/vald-filter-gateway:v1.7.3 +
Index Manager + docker pull vdaas/vald-manager-index:v1.7.3
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.7.3 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.7.3
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.7.3 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.7.3) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.7.3/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.3/charts/operator/helm/README.md) + +### Changes + +:sparkles: New feature + +- Add disable balanced update features & use generic type for BidirectionalStream [#1964](https://github.com/vdaas/vald/pull/1964) + +:bug: Bug fix + +- Fix grafana dashboard query for backoff retry count [#1961](https://github.com/vdaas/vald/pull/1961) + +:recycle: Refactor + +- Refactor conv.go [#1968](https://github.com/vdaas/vald/pull/1968) + +:memo: Document + +- Add new API parameter and update observability docs [#1966](https://github.com/vdaas/vald/pull/1966) + +:arrow_up: Dependency update + +- Update deps [#1969](https://github.com/vdaas/vald/pull/1969) + +## v1.7.2 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.7.2
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.7.2 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.7.2
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.7.2 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.7.2
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.7.2 +
Gateways + docker pull vdaas/vald-lb-gateway:v1.7.2
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.7.2
+ docker pull vdaas/vald-filter-gateway:v1.7.2
+ docker pull ghcr.io/vdaas/vald/vald-filter-gateway:v1.7.2 +
Index Manager + docker pull vdaas/vald-manager-index:v1.7.2
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.7.2 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.7.2
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.7.2 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.7.2) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.7.2/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.2/charts/operator/helm/README.md) + +### Changes + +:bug: Bug fix + +- Vald gRPC Client and Pool logic makes huge backoff [#1953](https://github.com/vdaas/vald/pull/1953) +- Missing backoff metrics [#1958](https://github.com/vdaas/vald/pull/1958) + +:recycle: Refactor + +- Update test template to exclude deepsource warning [#1954](https://github.com/vdaas/vald/pull/1954) + +:white_check_mark: Test + +- Remove non-implemented test [#1952](https://github.com/vdaas/vald/pull/1952) + +## v1.7.1 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.7.1
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.7.1 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.7.1
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.7.1 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.7.1
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.7.1 +
Gateways + docker pull vdaas/vald-lb-gateway:v1.7.1
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.7.1
+ docker pull vdaas/vald-filter-gateway:v1.7.1
+ docker pull ghcr.io/vdaas/vald/vald-filter-gateway:v1.7.1 +
Index Manager + docker pull vdaas/vald-manager-index:v1.7.1
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.7.1 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.7.1
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.7.1 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.7.1) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.7.1/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.1/charts/operator/helm/README.md) + +### Changes + +#### 🐛 Bugfix + +- [bugfix] add target length validation for each gRPC client exection method [#1939](https://github.com/vdaas/vald/pull/1939) + +#### ♻️ Refactor + +- update gRPC status code for API docs [#1943](https://github.com/vdaas/vald/pull/1943) +- Refactor: Add t.Helper() on test helper function [#1935](https://github.com/vdaas/vald/pull/1935) +- Fix syntax error on dump context workflow [#1936](https://github.com/vdaas/vald/pull/1936) +- format codes [#1934](https://github.com/vdaas/vald/pull/1934) + +## v1.7.0 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.7.0
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.7.0 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.7.0
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.7.0 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.7.0
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.7.0 +
Gateways + docker pull vdaas/vald-lb-gateway:v1.7.0
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.7.0
+ docker pull vdaas/vald-filter-gateway:v1.7.0
+ docker pull ghcr.io/vdaas/vald/vald-filter-gateway:v1.7.0 +
Index Manager + docker pull vdaas/vald-manager-index:v1.7.0
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.7.0 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.7.0
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.7.0 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.7.0) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.7.0/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.7.0/charts/operator/helm/README.md) + +### Changes + +:sparkles: New Feature + +- Introduce OTLP for metrics and trace [#1824](https://github.com/vdaas/vald/pull/1824) +- Add manifest to deploy opentelemetry-operator [#1819](https://github.com/vdaas/vald/pull/1819) + +:arrow_up: Dependencies update + +- Update go modules and go version [#1922](https://github.com/vdaas/vald/pull/1922) +- Update go modules [#1904](https://github.com/vdaas/vald/pull/1904) + +:bug: Bug fix + +- Divide agent errors for QBG and Faiss implementation [#1924](https://github.com/vdaas/vald/pull/1924) +- Bugfix status handling for idle connection [#1921](https://github.com/vdaas/vald/pull/1921) +- Fix invalid character error [#1914](https://github.com/vdaas/vald/pull/1914) +- Fixed duplicate counts of working_memory_set_bytes [#1911](https://github.com/vdaas/vald/pull/1911) +- Bug fix using undefined a variable at maxDimensionTest [#1856](https://github.com/vdaas/vald/pull/1856) +- Bug fix prometheus export return value [#1817](https://github.com/vdaas/vald/pull/1817) + +:recycle: Refactor + +- Happy New Year 2023 [#1918](https://github.com/vdaas/vald/pull/1918) +- Add auto-update libs & deps make command [#1917](https://github.com/vdaas/vald/pull/1917) +- Add canceled status for CreateIndex API [#1892](https://github.com/vdaas/vald/pull/1892) +- Update concurrent cancellation group name [#1912](https://github.com/vdaas/vald/pull/1912) +- Remove blank when all parameters are not used and Add ErrJobFuncNotFound [#1879](https://github.com/vdaas/vald/pull/1879) +- Rename doXXX() [#1878](https://github.com/vdaas/vald/pull/1878) +- Remove deprecated functional option (internal/net/grpc) [#1877](https://github.com/vdaas/vald/pull/1877) +- Fix deepsource: RVV-B0001 Confusing naming of struct fields or methods [#1875](https://github.com/vdaas/vald/pull/1875) +- Fix deepsource: VET-V0008 lock erroneously passed by value internal/net test [#1874](https://github.com/vdaas/vald/pull/1874) +- Fix deepsource: RVV-B0001 confusing naming of struct fields or methods [#1844](https://github.com/vdaas/vald/pull/1844) +- Fix deepsource: SCC-U1000 Unused code [#1873](https://github.com/vdaas/vald/pull/1873) +- Fix deepsource: RVV-B0006 Method modifies receiver [#1872](https://github.com/vdaas/vald/pull/1872) +- Fix deepsource: SCC-SA4006 Value assigned to a variable is never read before being overwritten [#1871](https://github.com/vdaas/vald/pull/1871) +- Fix deepsource: VET-V0008 Lock erroneously passed by value (pkg/agent) [#1868](https://github.com/vdaas/vald/pull/1868) +- Fix deepsource: VET-V0008 lock erroneously passed by value internal/info test [#1869](https://github.com/vdaas/vald/pull/1869) +- Fix deepsource: DOK-W1001 found consecutive run command [#1870](https://github.com/vdaas/vald/pull/1870) +- Fix deepsource: VET-V0008 lock erroneously passed by value (internal/net) [#1867](https://github.com/vdaas/vald/pull/1867) +- Fix deepsource: CRT-D0001 append possibly assigns to a wrong variable [#1866](https://github.com/vdaas/vald/pull/1866) +- Fix deepsource: VET-V0008 Lock erroneously passed by value (pkg/manager) [#1861](https://github.com/vdaas/vald/pull/1861) +- Fix deepsource: VET-V0008 lock erroneously passed by value pkg/discoverer [#1857](https://github.com/vdaas/vald/pull/1857) +- Fix deepsource: RVV-B0006 Method modifies receiver [#1865](https://github.com/vdaas/vald/pull/1865) +- Fix deepsource: VET-V0008 Lock erroneously passed by value (internal/test, singleflight, observability) [#1863](https://github.com/vdaas/vald/pull/1863) +- Fix deepsource: VET-V0008 lock erroneously passed by value info [#1864](https://github.com/vdaas/vald/pull/1864) +- Fix deepsource: VET-V0008 Lock erroneously passed by value internal/client [#1862](https://github.com/vdaas/vald/pull/1862) +- Fix deepsource: VET-V0008 lock erroneously passed by value internal/info,iocopy,errgroup [#1860](https://github.com/vdaas/vald/pull/1860) +- Fix deepsource: VET-V0008 Lock erroneously passed by value internal/db, backoff, circuitbreaker [#1859](https://github.com/vdaas/vald/pull/1859) +- Fix deepsource: RVV-B0009 Redefinition of builtin [#1858](https://github.com/vdaas/vald/pull/1858) +- Fix deepsource: CRT-A0014 switch single case can be rewritten as if or if-else [#1855](https://github.com/vdaas/vald/pull/1855) +- Fix deepsource: RVV-A0003 Exit inside non-main function ./hack [#1854](https://github.com/vdaas/vald/pull/1854) +- Fix deepsource: SCC-S1003 replace call to strings.Index with strings.Contains [#1853](https://github.com/vdaas/vald/pull/1853) +- Fix deepsource: RVV-B0013 Unused method receiver [#1852](https://github.com/vdaas/vald/pull/1852) +- Fix deepsource: CRT-D0007 Duplicate cases found in switch statement [#1851](https://github.com/vdaas/vald/pull/1851) +- Fix deepsource: GO-W1009 using a deprecated function, variable, constant or field [#1846](https://github.com/vdaas/vald/pull/1846) +- Fix deepsource: RVV-B0011 exported function returning value of unexported type [#1848](https://github.com/vdaas/vald/pull/1848) +- Fix deepsource: GSC-G103 Function call made to an unsafe package [#1850](https://github.com/vdaas/vald/pull/1850) +- Fix deepsource: RVV-B0012 Unused parameter in the function +- Fix deepsource: DOK-SC2002, DOK-W1001 Useless cat and Multiple consecutive RUN [#1847](https://github.com/vdaas/vald/pull/1847) +- Fix deepsource: dockerfile warning [#1835](https://github.com/vdaas/vald/pull/1835) +- Fix deepsource RVV-B0013 [#1832](https://github.com/vdaas/vald/pull/1832) +- Fix deepsource VET-V0007 unkeyed composite literals [#1837](https://github.com/vdaas/vald/pull/1837) +- Fix deepsource: Audit required: Insecure gRPC server [#1833](https://github.com/vdaas/vald/pull/1833) +- Fix deepsource: Potential slowloris attack [#1834](https://github.com/vdaas/vald/pull/1834) +- Fix deepsource: Unsafe defer of os.Close [#1836](https://github.com/vdaas/vald/pull/1836) +- Fix deepsource: RVV-A0003 exit inside non-main function [#1838](https://github.com/vdaas/vald/pull/1838) +- Fix deepsource: GSC-G404 Audit the random number generation source (rand) [#1839](https://github.com/vdaas/vald/pull/1839) +- Fix makefile [#1828](https://github.com/vdaas/vald/pull/1828) +- Refactor circuitbreaker [#1816](https://github.com/vdaas/vald/pull/1816) + +:white_check_mark: Test + +- Refactor Insert Upsert Testing [#1919](https://github.com/vdaas/vald/pull/1919) + +:green_heart: CI + +- Ci/GitHub action docker/update docker login action [#1903](https://github.com/vdaas/vald/pull/1903) +- Add actions workflow validation [#1902](https://github.com/vdaas/vald/pull/1902) +- Change docker build permission [#1901](https://github.com/vdaas/vald/pull/1901) +- Update docker login action [#1900](https://github.com/vdaas/vald/pull/1900) +- Format code with prettier and gofumpt [#1886](https://github.com/vdaas/vald/pull/1886) +- Update deepsource configuration [#1881](https://github.com/vdaas/vald/pull/1881) +- Update gotestfmt org [#1880](https://github.com/vdaas/vald/pull/1880) +- Add escape for e2e workflow [#1845](https://github.com/vdaas/vald/pull/1845) +- Resolve GitHub Actions warning [#1818](https://github.com/vdaas/vald/pull/1818) + +:memo: Document + +- Create observability configuration document [#1882](https://github.com/vdaas/vald/pull/1882) +- Add takuyaymd as a contributor for bug, and code [#1913](https://github.com/vdaas/vald/pull/1913) +- Update PULL_REQUEST_TEMPLATE and ISSUE_TEMPLATE [#1885](https://github.com/vdaas/vald/pull/1885) +- Fix typo comment [#1831](https://github.com/vdaas/vald/pull/1831) +- Add filter gateway api doc [#1821](https://github.com/vdaas/vald/pull/1821) +- Fix dead link [#1823](https://github.com/vdaas/vald/pull/1823) +- Update pull request template [#1820](https://github.com/vdaas/vald/pull/1820) + +## v1.6.3 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.6.3
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.6.3 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.6.3
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.6.3 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.6.3
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.6.3 +
Gateways + docker pull vdaas/vald-lb-gateway:v1.6.3
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.6.3
+ docker pull vdaas/vald-filter-gateway:v1.6.3
+ docker pull ghcr.io/vdaas/vald/vald-filter-gateway:v1.6.3 +
Index Manager + docker pull vdaas/vald-manager-index:v1.6.3
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.6.3 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.6.3
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.6.3 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.6.3) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.6.3/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.6.3/charts/operator/helm/README.md) + +### Changes + +🐛 Bugfix + +- Bugfix Circuit Breaker half-open error handling [#1811](https://github.com/vdaas/vald/pull/1811) + +📝 Document fix + +- Fix dead link [#1807](https://github.com/vdaas/vald/pull/1807) + +:arrow_up: Dependencies + +- Update go modules and add small test for strings [#1812](https://github.com/vdaas/vald/pull/1812) + +## v1.6.2 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.6.2
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.6.2 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.6.2
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.6.2 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.6.2
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.6.2 +
Gateways + docker pull vdaas/vald-lb-gateway:v1.6.2
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.6.2
+ docker pull vdaas/vald-filter-gateway:v1.6.2
+ docker pull ghcr.io/vdaas/vald/vald-filter-gateway:v1.6.2 +
Index Manager + docker pull vdaas/vald-manager-index:v1.6.2
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.6.2 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.6.2
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.6.2 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.6.2) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.6.2/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.6.2/charts/operator/helm/README.md) + +### Changes + +🐛 Bugfix + +- Bugfix success handling in the half-open and add flow control [#1805](https://github.com/vdaas/vald/pull/1805) +- Fix string concat buffer overflow [#1806](https://github.com/vdaas/vald/pull/1806) + +:white_check_mark: Test + +- Implement pkg/agent/handler createAndSaveIndex test case [#1794](https://github.com/vdaas/vald/pull/1794) + +:pencil: Document + +- Add cluster role document [#1796](https://github.com/vdaas/vald/pull/1796) +- Fix document format [#1804](https://github.com/vdaas/vald/pull/1804) + +## v1.6.1 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.6.1
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.6.1 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.6.1
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.6.1 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.6.1
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.6.1 +
Gateways + docker pull vdaas/vald-lb-gateway:v1.6.1
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.6.1
+ docker pull vdaas/vald-filter-gateway:v1.6.1
+ docker pull ghcr.io/vdaas/vald/vald-filter-gateway:v1.6.1 +
Index Manager + docker pull vdaas/vald-manager-index:v1.6.1
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.6.1 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.6.1
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.6.1 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.6.1) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.6.1/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.6.1/charts/operator/helm/README.md) + +### Changes + +🐛 Bugfix + +- fix metrics bug [#1800](https://github.com/vdaas/vald/pull/1800) + +:white_check_mark: Test + +- Add test for attributesFromError method [#1801](https://github.com/vdaas/vald/pull/1801) + +## v1.6.0 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.6.0
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.6.0 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.6.0
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.6.0 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.6.0
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.6.0 +
Gateways + docker pull vdaas/vald-lb-gateway:v1.6.0
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.6.0
+ docker pull vdaas/vald-filter-gateway:v1.6.0
+ docker pull ghcr.io/vdaas/vald/vald-filter-gateway:v1.6.0 +
Index Manager + docker pull vdaas/vald-manager-index:v1.6.0
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.6.0 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.6.0
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.6.0 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.6.0) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.6.0/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.6.0/charts/operator/helm/README.md) + +### Changes + +### Description: + +:sparkles: New feature + +- Introduce opentelemetry [#1778](https://github.com/vdaas/vald/pull/1778) +- Change opencensus tracing to opentelemetry tracing [#1767](https://github.com/vdaas/vald/pull/1767) +- Add circuit breaker implementation [#1738](https://github.com/vdaas/vald/pull/1738) + +⬆️ Dependency update + +- Deleted deprecated dependency for OTEL [#1795](https://github.com/vdaas/vald/pull/1795) +- Delete stackdriver dependencies [#1761](https://github.com/vdaas/vald/pull/1761) + +🐛 Bug fix + +- Fix fossa workflow bug [#1787](https://github.com/vdaas/vald/pull/1787) +- Fix failed github workflow [#1745](https://github.com/vdaas/vald/pull/1745) +- Add safe directory configuration [#1748](https://github.com/vdaas/vald/pull/1748) + +:recycle: Refactor + +- Refactor internal package (net, file, logger) [#1768](https://github.com/vdaas/vald/pull/1768) +- :recycle: Set default image tag as latest [#1766](https://github.com/vdaas/vald/pull/1766) +- Upgrade ubuntu version [#1743](https://github.com/vdaas/vald/pull/1743) +- Deleted vald\_ prefix of dashboard [#1785](https://github.com/vdaas/vald/pull/1785) +- Use gotestfmt instead of tparse [#1788](https://github.com/vdaas/vald/pull/1788) + +✅ Test + +- Implement agent handler saveIndex test case [#1731](https://github.com/vdaas/vald/pull/1731) +- Ignore gorules test [#1790](https://github.com/vdaas/vald/pull/1790) +- Fix chaos test [#1757](https://github.com/vdaas/vald/pull/1757) +- Implement agent handler createIndex test case [#1710](https://github.com/vdaas/vald/pull/1710) +- Implement agent handler indexInfo test case [#1708](https://github.com/vdaas/vald/pull/1708) + +📝 Document + +- Update testing guideline & template [#1791](https://github.com/vdaas/vald/pull/1791) +- Add Client API Config document [#1783](https://github.com/vdaas/vald/pull/1783) +- Add backup configuration document [#1754](https://github.com/vdaas/vald/pull/1754) +- Add upgrade document [#1777](https://github.com/vdaas/vald/pull/1777) +- Update document by feedback [#1773](https://github.com/vdaas/vald/pull/1733) +- Add filter config document [#1755](https://github.com/vdaas/vald/pull/1755) +- Add deployment document [#1758](https://github.com/vdaas/vald/pull/1758) +- Update the images of agent page [#1753](https://github.com/vdaas/vald/pull/1753) +- Update config document [#1751](https://github.com/vdaas/vald/pull/1751) + +## v1.5.6 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.5.6
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.5.6 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.5.6
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.5.6 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.5.6
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.5.6 +
Gateways + docker pull vdaas/vald-lb-gateway:v1.5.6
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.5.6
+ docker pull vdaas/vald-filter-gateway:v1.5.6
+ docker pull ghcr.io/vdaas/vald/vald-filter-gateway:v1.5.6 +
Index Manager + docker pull vdaas/vald-manager-index:v1.5.6
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.5.6 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.5.6
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.5.6 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.5.6) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.5.6/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.5.6/charts/operator/helm/README.md) + +### Changes + +✨ Feature + +- Add distance type [#1732](https://github.com/vdaas/vald/pull/1732) +- Add vald-helm-operator E2E [#1722](https://github.com/vdaas/vald/pull/1722) + +⬆️ update dependencies + +- Delete tensorflow [#1723](https://github.com/vdaas/vald/pull/1723) +- Update deps [#1724](https://github.com/vdaas/vald/pull/1724) + +✏️ Documents + +- Add capacity planning document [#1714](https://github.com/vdaas/vald/pull/1714) +- Update filter gateway document [#1721](https://github.com/vdaas/vald/pull/1721) +- Fix capacity planning doc [#1736](https://github.com/vdaas/vald/pull/1736) +- Fix file name of capacity planning document [#1737](https://github.com/vdaas/vald/pull/1737) + +## v1.5.5 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.5.5
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.5.5 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.5.5
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.5.5 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.5.5
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.5.5 +
Gateways + docker pull vdaas/vald-lb-gateway:v1.5.5
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.5.5
+ docker pull vdaas/vald-filter-gateway:v1.5.5
+ docker pull ghcr.io/vdaas/vald/vald-filter-gateway:v1.5.5 +
Index Manager + docker pull vdaas/vald-manager-index:v1.5.5
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.5.5 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.5.5
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.5.5 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.5.5) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.5.5/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.5.5/charts/operator/helm/README.md) + +### Changes + +♻️ Refactor + +- Make format [#1717](https://github.com/vdaas/vald/pull/1717) +- Remove unnecessary import path on pkg test [#1712](https://github.com/vdaas/vald/pull/1712) +- Fix to get only backoff metrics of discover RPC [#1706](https://github.com/vdaas/vald/pull/1706) + +🐛 Bugfix + +- Return uuid when exits rpc called [#1709](https://github.com/vdaas/vald/pull/1709) + +✏️ Documents + +- Add textlint for document [#1715](https://github.com/vdaas/vald/pull/1715) +- Modified design of troubleshooting image [#1705](https://github.com/vdaas/vald/pull/1705) +- Update tutorial images [#1704](https://github.com/vdaas/vald/pull/1704) +- Add troubleshooting flow chart document [#1688](https://github.com/vdaas/vald/pull/1688) +- Update data-flow docs for new images and using remove instead of delete [#1693](https://github.com/vdaas/vald/pull/1693) + +:white_check_mark: Test + +- Implement agent handler getObject test case [#1707](https://github.com/vdaas/vald/pull/1707) +- Implement stream insert test case [#1697](https://github.com/vdaas/vald/pull/1697) +- Implement upsert test cases [#1685](https://github.com/vdaas/vald/pull/1685) + +⬆️ Update dependencies + +- Update deps [#1719](https://github.com/vdaas/vald/pull/1719) +- Update deps [#1702](https://github.com/vdaas/vald/pull/1702) +- Automatically update k8s manifests [#1701](https://github.com/vdaas/vald/pull/1701) + +## v1.5.4 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.5.4
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.5.4 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.5.4
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.5.4 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.5.4
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.5.4 +
Gateways + docker pull vdaas/vald-lb-gateway:v1.5.4
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.5.4
+ docker pull vdaas/vald-filter-gateway:v1.5.4
+ docker pull ghcr.io/vdaas/vald/vald-filter-gateway:v1.5.4 +
Index Manager + docker pull vdaas/vald-manager-index:v1.5.4
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.5.4 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.5.4
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.5.4 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.5.4) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.5.4/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.5.4/charts/operator/helm/README.md) + +### Changes + +✨ New features + +- Add backoff metrics panel [#1694](https://github.com/vdaas/vald/pull/1694) +- Add backoff metrics for grpc [#1684](https://github.com/vdaas/vald/pull/1684) +- Setup jaeger operator [#1682](https://github.com/vdaas/vald/pull/1682) + +⬆️ update dependencies + +- Update deps [#1695](https://github.com/vdaas/vald/pull/1695) +- Update deps [#1699](https://github.com/vdaas/vald/pull/1699) + +♻️ Refactor + +- Split agent pkg handler implementation [#1690](https://github.com/vdaas/vald/pull/1690) +- Refactor pkg test helper functions [#1678](https://github.com/vdaas/vald/pull/1678) + +🐛 Bugfix + +- Fix error handling in readyForUpdate and return NotFound error when delete fails in multiUpdate [#1681](https://github.com/vdaas/vald/pull/1681) +- Fix race error of server package [#1689](https://github.com/vdaas/vald/pull/1689) + +✏️ Documents + +- Add API status code description [#1679](https://github.com/vdaas/vald/pull/1679) +- Modified data flow images [#1687](https://github.com/vdaas/vald/pull/1687) +- Correspond to update omission [#1686](https://github.com/vdaas/vald/pull/1686) +- Renew basic architecture image [#1680](https://github.com/vdaas/vald/pull/1680) + +## v1.5.3 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.5.3
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.5.3 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.5.3
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.5.3 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.5.3
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.5.3 +
Gateways + docker pull vdaas/vald-lb-gateway:v1.5.3
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.5.3
+ docker pull vdaas/vald-filter-gateway:v1.5.3
+ docker pull ghcr.io/vdaas/vald/vald-filter-gateway:v1.5.3 +
Index Manager + docker pull vdaas/vald-manager-index:v1.5.3
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.5.3 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.5.3
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.5.3 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.5.3) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.5.3/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.5.3/charts/operator/helm/README.md) + +### Changes + +:sparkles: Feature + +- add invalid id argument handling [#1667](https://github.com/vdaas/vald/pull/1667) + +:bug: Bugfix + +- fix update bug [#1660](https://github.com/vdaas/vald/pull/1660) +- fix typo argument in Makefile and Makefile.d/function.mk [#1673](https://github.com/vdaas/vald/pull/1673) + +:green_heart: CI + +- fix chaos test temporarily [#1665](https://github.com/vdaas/vald/pull/1665) + +:memo: Document + +- add search config details document [#1661](https://github.com/vdaas/vald/pull/1661) + +:white_check_mark: Test + +- implement pkg handler exists test cases [#1628](https://github.com/vdaas/vald/pull/1628) +- implement multi insert test case for pkg agent handler [#1612](https://github.com/vdaas/vald/pull/1612) +- create investigation test of max dim for NGT [#1633](https://github.com/vdaas/vald/pull/1633) +- implement pkg handler remove test cases [#1644](https://github.com/vdaas/vald/pull/1644) +- add e2e test for maxDimensionTest [#1650](https://github.com/vdaas/vald/pull/1650) +- implement update handler test cases [#1657](https://github.com/vdaas/vald/pull/1657) + +:arrow_up: Update dependencies + +- update manifests version [#1642](https://github.com/vdaas/vald/pull/1642) +- update go module [#1643](https://github.com/vdaas/vald/pull/1643) +- fix go tool installation [#1649](https://github.com/vdaas/vald/pull/1649) +- update kind version [#1668](https://github.com/vdaas/vald/pull/1668) + +:art: Design + +- update dataflow image [#1647](https://github.com/vdaas/vald/pull/1647) + +:lock: Security + +- fix CWE-285 [#1654](https://github.com/vdaas/vald/pull/1654) + +## v1.5.2 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.5.2
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.5.2 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.5.2
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.5.2 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.5.2
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.5.2 +
Gateways + docker pull vdaas/vald-lb-gateway:v1.5.2
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.5.2
+ docker pull vdaas/vald-filter-gateway:v1.5.2
+ docker pull ghcr.io/vdaas/vald/vald-filter-gateway:v1.5.2 +
Index Manager + docker pull vdaas/vald-manager-index:v1.5.2
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.5.2 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.5.2
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.5.2 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.5.2) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.5.2/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.5.2/charts/operator/helm/README.md) + +### Changes + +:arrow_up: update dependencies + +- update libs version (#1636) +- update libs version (#1632) + +:sparkles: feature + +- use ReadWriteOncePod instead of ReadWriteOnce and remove initializer (#1627) + +:recycle: refactor + +- refactor: improve memory high usage of vald-agent (#1617) + +:lock: security + +- security: fix vulnerability problem of helm operator (#1625) +- [Security] Fix vulnerability problem of helm operator (#1611) +- security fix Vulnerability due to usage of old golang.org/x:net in example depentency (#1641) + +:green_heart: ci + +- ci: Fix CodeQL warning (#1629) +- fix fails actions job & update version (#1620) +- [CI] Allow e2e deploy action jobs to run in parallel (#1616) + +:white_check_mark: test + +- fix superfluous response.WriteHeader call (#1631) +- implement search-by-id pkg test (#1624) + +:memo: document + +- document: update formats (#1634) +- docs: add dotdc as a contributor for doc (#1623) +- doc: fixed architecture link in get-started.md (#1619) +- add FAQ and Troubleshooting document (#1591) + +## v1.5.1 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.5.1
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.5.1 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.5.1
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.5.1 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.5.1
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.5.1 +
Gateways + docker pull vdaas/vald-lb-gateway:v1.5.1
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.5.1
+ docker pull vdaas/vald-filter-gateway:v1.5.1
+ docker pull ghcr.io/vdaas/vald/vald-filter-gateway:v1.5.1 +
Index Manager + docker pull vdaas/vald-manager-index:v1.5.1
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.5.1 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.5.1
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.5.1 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.5.1) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.5.1/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.5.1/charts/operator/helm/README.md) + +### Changes + +:arrow_up: update dependencies + +- Upgrade pyroscope version (#1605) + +:bug: bugfix + +- bugfix internal/file and add CreateTemp function and resolve go module failure (#1608) + +:white_check_mark: Test + +- fix search handler test (#1613) +- fix fails test of e2e chaos (#1603) +- Implement pkg ngt handler insert test (#1552) +- implement agent ngt handler search test (#1557) + +:memo: document + +- add vald users (#1601) +- update brand guidelines pdf (#1600) +- fix file name of search api (#1599) +- [ImgBot] Optimize images (#1598) +- cleanup document images (#1595) + +## v1.5.0 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.5.0
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.5.0 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.5.0
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.5.0 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.5.0
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.5.0 +
Gateways + docker pull vdaas/vald-lb-gateway:v1.5.0
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.5.0
+ docker pull vdaas/vald-filter-gateway:v1.5.0
+ docker pull ghcr.io/vdaas/vald/vald-filter-gateway:v1.5.0 +
Index Manager + docker pull vdaas/vald-manager-index:v1.5.0
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.5.0 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.5.0
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.5.0 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.5.0) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.5.0/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.5.0/charts/operator/helm/README.md) + +### Changes + +:sparkles: New features + +- Add values yaml for the back up the agent index data (#1566) +- Implement uniform/gaussian distributed random float32/uint8 vector generator (#1573) +- Add min_num in each search service api (#1576) +- Add copy on write (#1578) +- Add example values for using Pyroscope (#1582) + +:recycle: Refactor + +- Deleted resource limits of agent ngt and added in memory mode example (#1571) +- Improve string conversion performace (#1577) +- Update dependencies version (#1593) + +:pencil2: Documents + +- Add gateway component overview document (#1549) +- Update API docs and fix format (#1568) +- Update dataflow images (#1572) +- Add discoverer component overview document (#1574) +- Add index manager component overview document (#1575) +- Update README (#1584) + +:bug: Bugfix + +- Add error handling when there is no data in google cloud storage. (#1556) +- Reviewdog markdown workflow (#1585) +- Fix invalid URL (#1589) + +:white_check_mark: Test + +- Implement test for net/grpc codec,logger and server (#1530) +- Update test for go1.17 update & -race test (#1431) + +## v1.4.1 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.4.1
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.4.1 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.4.1
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.4.1 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.4.1
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.4.1 +
Gateways + docker pull vdaas/vald-lb-gateway:v1.4.1
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.4.1
+ docker pull vdaas/vald-filter-gateway:v1.4.1
+ docker pull ghcr.io/vdaas/vald/vald-filter-gateway:v1.4.1 +
Index Manager + docker pull vdaas/vald-manager-index:v1.4.1
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.4.1 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.4.1
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.4.1 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.4.1) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.4.1/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.4.1/charts/operator/helm/README.md) + +### Changes + +- [bugfix] fix miss param for fp16 (#1563) +- [bugfix] add missing empty dir mount for s3 backup without pvs (#1562) + +## v1.4.0 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.4.0
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.4.0 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.4.0
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.4.0 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.4.0
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.4.0 +
Gateways + docker pull vdaas/vald-lb-gateway:v1.4.0
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.4.0
+ docker pull vdaas/vald-filter-gateway:v1.4.0
+ docker pull ghcr.io/vdaas/vald/vald-filter-gateway:v1.4.0 +
Index Manager + docker pull vdaas/vald-manager-index:v1.4.0
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.4.0 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.4.0
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.4.0 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.4.0) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.4.0/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.4.0/charts/operator/helm/README.md) + +### Changes + + + +:sparkles: New features + +- Add agent minnum field and support fp16 (#1558) +- Add pyroscope manifest running on persistent volume (#1551) +- Add settings for profiling with pyroscope (#1539) +- Add pyroscope manifest (#1520) +- Add linear search handler for gateway-lb / ingress-filter / agent-core-ngt (#1511) +- Add grpc custom codec (#1490) +- Update grpc codes (#1489) +- Add NGT linear search API (#1504) + +:recycle: Refactor + +- Delete unsupported library sptag (#1559) +- Improved search operation (#1546) +- Add description timeout of search config (#1541) +- Update dependencies version (#1538) +- Happy new year (#1525) +- Update libs version (#1524) +- Add the missing go.sum (#1517) +- Update license headers / Format codes (#1514) +- Add .gitattributes (#1512) +- Update get started with using kubernetes ingress (#1510) +- Fix command template (#1508) +- Update dependencies version (#1501) +- modify .pb.go & swagger (#1493) +- Add reshape vector proto, remove meta/backup proto (#1492) + +:pencil2: Documents + +- Add agent component overview document (#1544) +- Add build api proto document (#1540) +- Add remove/object api document (#1536) +- Add search api document (#1534) +- Add upsert api document (#1533) +- Add update api document (#1529) +- Add insert api document (#1516) + +:bug: Bugfix + +- Fix vulnerability problem of helm-operator (#1535) + +:white_check_mark: Test + +- Implement internal/net/grpc metrics & proto & types test (#1507) +- Implement and modify internal/io tests (#1509) +- Implement internal/net net.go&dialer.go test (#1505) +- Implement internal/net/grpc/credentials and health test (#1502) +- Implement internal/net/control test (#1500) +- Remove unsupported feature from public document (#1497) + +:tada: Cellebration! + +- Add liusy182 as a contributor for example (#1519) +- Add zchee as a contributor for a11y (#1513) + +## v1.3.1 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.3.1
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.3.1 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.3.1
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.3.1 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.3.1
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.3.1 +
Gateways + docker pull vdaas/vald-lb-gateway:v1.3.1
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.3.1
+ docker pull vdaas/vald-filter-gateway:v1.3.1
+ docker pull ghcr.io/vdaas/vald/vald-filter-gateway:v1.3.1 +
Index Manager + docker pull vdaas/vald-manager-index:v1.3.1
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.3.1 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.3.1
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.3.1 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.3.1) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.3.1/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.3.1/charts/operator/helm/README.md) + +### Changes + +- Add documentation comments to proto files (#1452) +- Add ngt index count panel to agent grafana dashboard (#1483) +- add grpc keepalive EnforcementPolicy support (#1487) +- fix timing of removeInvalidIndex (#1481) +- [bugfix] add validation to agent service option InitialDelayMaxDuration (#1482) + +## v1.3.0 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.3.0
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.3.0 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.3.0
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.3.0 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.3.0
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.3.0 +
Gateways + docker pull vdaas/vald-lb-gateway:v1.3.0
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.3.0
+ docker pull vdaas/vald-filter-gateway:v1.3.0
+ docker pull ghcr.io/vdaas/vald/vald-filter-gateway:v1.3.0 +
Index Manager + docker pull vdaas/vald-manager-index:v1.3.0
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.3.0 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.3.0
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.3.0 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.3.0) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.3.0/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.3.0/charts/operator/helm/README.md) + +### Changes + +- add startupProbe support (#1473) +- add label / field selectors for discoverer (#1472) + +## v1.2.4 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.2.4
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.2.4 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.2.4
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.2.4 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.2.4
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.2.4 +
Gateways + docker pull vdaas/vald-lb-gateway:v1.2.4
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.2.4
+ docker pull vdaas/vald-filter-gateway:v1.2.4
+ docker pull ghcr.io/vdaas/vald/vald-filter-gateway:v1.2.4 +
Index Manager + docker pull vdaas/vald-manager-index:v1.2.4
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.2.4 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.2.4
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.2.4 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.2.4) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.2.4/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.2.4/charts/operator/helm/README.md) + +### Changes + +- update go patch version (#1464) +- [bugfix] sidecar e2e testing (#1465) +- update dependencies version including NGT (#1461) +- fix unlimited gorountine processing in kvsdb (#1458) +- Refactor hack pkg agent e2e benchmark (#1430) +- Remove non-exist components from doc and ci (#1450) +- :recycle: :pencil: add default_pool_size in example yml (#1457) +- reduce memory usage around ngt implementation & refactor agent/lb & auto-generate unit test (#1449) +- Remove rinx from several yamls (#1451) +- Add E2E scenario with SkipStrictExistCheck enabled (#1415) +- fix filter-gateway chart (#1454) +- Implement pkg/agent/core/ngt/service/option test (#1429) + +## v1.2.3 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.2.3
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.2.3 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.2.3
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.2.3 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.2.3
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.2.3 +
Gateways + docker pull vdaas/vald-gateway:v1.2.3
+ docker pull ghcr.io/vdaas/vald/vald-gateway:v1.2.3
+ docker pull vdaas/vald-backup-gateway:v1.2.3
+ docker pull ghcr.io/vdaas/vald/vald-backup-gateway:v1.2.3
+ docker pull vdaas/vald-lb-gateway:v1.2.3
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.2.3
+ docker pull vdaas/vald-meta-gateway:v1.2.3
+ docker pull ghcr.io/vdaas/vald/vald-meta-gateway:v1.2.3 +
Backup managers + docker pull vdaas/vald-manager-backup-mysql:v1.2.3
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-mysql:v1.2.3
+ docker pull vdaas/vald-manager-backup-cassandra:v1.2.3
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-cassandra:v1.2.3 +
Compressor + docker pull vdaas/vald-manager-compressor:v1.2.3
+ docker pull ghcr.io/vdaas/vald/vald-manager-compressor:v1.2.3 +
Metas + docker pull vdaas/vald-meta-redis:v1.2.3
+ docker pull ghcr.io/vdaas/vald/vald-meta-redis:v1.2.3
+ docker pull vdaas/vald-meta-cassandra:v1.2.3
+ docker pull ghcr.io/vdaas/vald/vald-meta-cassandra:v1.2.3 +
Index Manager + docker pull vdaas/vald-manager-index:v1.2.3
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.2.3 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.2.3
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.2.3 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.2.3) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.2.3/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.2.3/charts/operator/helm/README.md) + +### Changes + +- update go module dependencies & update go patch version to 1.17.1 (#1445) +- [bugfix] fix unknown status problems in search operation (#1439) +- Add tools.mk for installing CI tools (#1442) +- Fix icon image path in Chart.yaml (#1441) +- fix(Makefile): :bug: Remove components (#1440) +- Change golangci-lint version to the latest and Fix docker build error (#1435) +- Fix golangci-lint rule (#1380) +- Add vqueue test for Exists and GetVector method (#1425) + +## v1.2.2 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.2.2
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.2.2 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.2.2
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.2.2 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.2.2
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.2.2 +
Gateways + docker pull vdaas/vald-gateway:v1.2.2
+ docker pull ghcr.io/vdaas/vald/vald-gateway:v1.2.2
+ docker pull vdaas/vald-backup-gateway:v1.2.2
+ docker pull ghcr.io/vdaas/vald/vald-backup-gateway:v1.2.2
+ docker pull vdaas/vald-lb-gateway:v1.2.2
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.2.2
+ docker pull vdaas/vald-meta-gateway:v1.2.2
+ docker pull ghcr.io/vdaas/vald/vald-meta-gateway:v1.2.2 +
Backup managers + docker pull vdaas/vald-manager-backup-mysql:v1.2.2
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-mysql:v1.2.2
+ docker pull vdaas/vald-manager-backup-cassandra:v1.2.2
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-cassandra:v1.2.2 +
Compressor + docker pull vdaas/vald-manager-compressor:v1.2.2
+ docker pull ghcr.io/vdaas/vald/vald-manager-compressor:v1.2.2 +
Metas + docker pull vdaas/vald-meta-redis:v1.2.2
+ docker pull ghcr.io/vdaas/vald/vald-meta-redis:v1.2.2
+ docker pull vdaas/vald-meta-cassandra:v1.2.2
+ docker pull ghcr.io/vdaas/vald/vald-meta-cassandra:v1.2.2 +
Index Manager + docker pull vdaas/vald-manager-index:v1.2.2
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.2.2 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.2.2
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.2.2 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.2.2) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.2.2/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.2.2/charts/operator/helm/README.md) + +### Changes + +- Add stateful PBT for NGT service (#1384) +- add default logger when logger not initialized (#1424) +- feat: :sparkles: Add LB gateway dashboard (#1420) +- chore-deps: :arrow_up: Upgrade OSDK to v1.11.0 (#1422) +- change default epsilon value to 0.1 from 0.0.1 (#1421) +- add go vet for checking cpu compatibility and update deps and refactor small code (#1418) +- Delete insert and delete channel of vqueue (#1400) +- downgrade cloud.google.com/go to resolve runtime panic (#1413) +- [bugfix] resolve errgroup limitation channel close panic (#1412) +- Update PULL_REQUEST_TEMPLATE and ISSUE_TEMPLATE (#1409) +- update go version to 1.17 and update go module dependencies (#1404) +- refactor grpc logging and use os.PathSeparator instead of / (#1405) +- Remove schema of security contexts (#1406) +- change security context uid to distroless nonroot uid (#1402) + +## v1.2.1 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.2.1
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.2.1 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.2.1
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.2.1 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.2.1
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.2.1 +
Gateways + docker pull vdaas/vald-gateway:v1.2.1
+ docker pull ghcr.io/vdaas/vald/vald-gateway:v1.2.1
+ docker pull vdaas/vald-backup-gateway:v1.2.1
+ docker pull ghcr.io/vdaas/vald/vald-backup-gateway:v1.2.1
+ docker pull vdaas/vald-lb-gateway:v1.2.1
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.2.1
+ docker pull vdaas/vald-meta-gateway:v1.2.1
+ docker pull ghcr.io/vdaas/vald/vald-meta-gateway:v1.2.1 +
Backup managers + docker pull vdaas/vald-manager-backup-mysql:v1.2.1
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-mysql:v1.2.1
+ docker pull vdaas/vald-manager-backup-cassandra:v1.2.1
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-cassandra:v1.2.1 +
Compressor + docker pull vdaas/vald-manager-compressor:v1.2.1
+ docker pull ghcr.io/vdaas/vald/vald-manager-compressor:v1.2.1 +
Metas + docker pull vdaas/vald-meta-redis:v1.2.1
+ docker pull ghcr.io/vdaas/vald/vald-meta-redis:v1.2.1
+ docker pull vdaas/vald-meta-cassandra:v1.2.1
+ docker pull ghcr.io/vdaas/vald/vald-meta-cassandra:v1.2.1 +
Index Manager + docker pull vdaas/vald-manager-index:v1.2.1
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.2.1 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.2.1
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.2.1 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.2.1) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.2.1/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.2.1/charts/operator/helm/README.md) + +### Changes + +- remove global errgroup from kvsdb goroutine limitation (#1398) +- add google grpc healthz & logger (#1394) +- fix e2e multi apis test code (#1397) +- :pencil: fix command in vald agent standalone on docker (#1393) +- add Makefile & Makefile.d to build kick rule (#1391) +- format codes (#1389) +- [proposal] add golines as vald default formatter (#1337) +- replace gogo protobuf to vt protobuf (#1378) +- refactor gateway request result location aggregation logic & use xxh3 for kvsdb hashing (#1376) +- Add E2E tests for multi-APIs (#1353) +- Add test for the function to get length of vqueue (#1382) +- remove unused path of format/yaml command (#1383) +- [Documentation] fix image filename (#1377) +- Vald moves to a vector search engine that enables more simple and high-speed retrieval. This is the first step of Simple-Vald. (#1365) +- Add e2e-profiling job (#1356) +- fix svg error on dataflow image (#1375) +- remove invalid gateway component reference from chart.NOTES (#1371) + +## v1.2.0 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.2.0
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.2.0 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.2.0
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.2.0 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.2.0
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.2.0 +
Gateways + docker pull vdaas/vald-gateway:v1.2.0
+ docker pull ghcr.io/vdaas/vald/vald-gateway:v1.2.0
+ docker pull vdaas/vald-backup-gateway:v1.2.0
+ docker pull ghcr.io/vdaas/vald/vald-backup-gateway:v1.2.0
+ docker pull vdaas/vald-lb-gateway:v1.2.0
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.2.0
+ docker pull vdaas/vald-meta-gateway:v1.2.0
+ docker pull ghcr.io/vdaas/vald/vald-meta-gateway:v1.2.0 +
Backup managers + docker pull vdaas/vald-manager-backup-mysql:v1.2.0
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-mysql:v1.2.0
+ docker pull vdaas/vald-manager-backup-cassandra:v1.2.0
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-cassandra:v1.2.0 +
Compressor + docker pull vdaas/vald-manager-compressor:v1.2.0
+ docker pull ghcr.io/vdaas/vald/vald-manager-compressor:v1.2.0 +
Metas + docker pull vdaas/vald-meta-redis:v1.2.0
+ docker pull ghcr.io/vdaas/vald/vald-meta-redis:v1.2.0
+ docker pull vdaas/vald-meta-cassandra:v1.2.0
+ docker pull ghcr.io/vdaas/vald/vald-meta-cassandra:v1.2.0 +
Index Manager + docker pull vdaas/vald-manager-index:v1.2.0
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.2.0 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.2.0
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.2.0 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.2.0) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.2.0/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.2.0/charts/operator/helm/README.md) + +### Changes + +# Release Vald v1.2.0 + +## Changes: + +- fix build failure on arm cpu due to the xxh3 dependency version (#1368) +- Add core algorithm ngt test (#1364) +- add mote accurate directory detection logic for removing (#1366) +- remove meta backup compressor from charts (#1334) +- update go module version (#1363) +- Update architecture overview (#1304) +- Improve internal package tests (#1227) +- fix bug of bulkinsert when error occurs and refactor error message (#1361) +- change initial-index-directory removal logic (#1359) +- ci: :construction_worker: Add gnupg to ci-container (#1362) +- ci: :construction_worker: add condition to trigger importing gpg key (#1360) +- fix: :bug: Fix mount paths when using persistent volume claim template (#1358) +- ci: :construction_worker: Add Upsert operation tests (#1347) +- config: :gear: Use GPG key for signing commits (#1351) +- add time validation for vqueue (#1352) +- chore-deps: :arrow_up: Upgrade tools (#1355) +- comment out backup/meta/compressor build command in Makefile (#1346) +- remove: :heavy_minus_sign: Remove backup, meta components from CI (#1331) +- add line trace logging when log mode is glg and level is debug (#1348) +- update go version to 1.16.6 and update go module dependencies (#1345) +- [bugfix] agent createindex operation's time.Ticker purges too slow & buffer overflow due to the unnecessary error wrapping (#1343) +- [bugfix] change kvsdb and vqueue check order for Exists operation (#1341) +- update go module dependencies (#1336) +- bugfix nil pointer panic in agent's MultiUpsert operation (#1335) +- [bugfix] agent.GetObject API returns old indexed vector problem instead of vqueue's new data (#1333) +- add timestamp handler for agent timestamp controlled update (#1324) +- fix: :bug: Fix typo (#1330) +- remove owner and description info from resource info rich error for each grpc handler (#1327) +- Update documents: configurations (#1289 + +## v1.1.2 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.1.2
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.1.2 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.1.2
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.1.2 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.1.2
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.1.2 +
Gateways + docker pull vdaas/vald-gateway:v1.1.2
+ docker pull ghcr.io/vdaas/vald/vald-gateway:v1.1.2
+ docker pull vdaas/vald-backup-gateway:v1.1.2
+ docker pull ghcr.io/vdaas/vald/vald-backup-gateway:v1.1.2
+ docker pull vdaas/vald-lb-gateway:v1.1.2
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.1.2
+ docker pull vdaas/vald-meta-gateway:v1.1.2
+ docker pull ghcr.io/vdaas/vald/vald-meta-gateway:v1.1.2 +
Backup managers + docker pull vdaas/vald-manager-backup-mysql:v1.1.2
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-mysql:v1.1.2
+ docker pull vdaas/vald-manager-backup-cassandra:v1.1.2
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-cassandra:v1.1.2 +
Compressor + docker pull vdaas/vald-manager-compressor:v1.1.2
+ docker pull ghcr.io/vdaas/vald/vald-manager-compressor:v1.1.2 +
Metas + docker pull vdaas/vald-meta-redis:v1.1.2
+ docker pull ghcr.io/vdaas/vald/vald-meta-redis:v1.1.2
+ docker pull vdaas/vald-meta-cassandra:v1.1.2
+ docker pull ghcr.io/vdaas/vald/vald-meta-cassandra:v1.1.2 +
Index Manager + docker pull vdaas/vald-manager-index:v1.1.2
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.1.2 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.1.2
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.1.2 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.1.2) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.1.2/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.1.2/charts/operator/helm/README.md) + +### Changes + +- bugfix remove api return wrong error of non exsiting replicas (#1318) +- update go modules and update go version to 1.16.5 from 1.16.4 (#1306) +- Implement test for internal/core/alogrithm/ngt/option and refactor (#1251) +- fix build error due to e2e benchmark invalid creating new client (#1221) +- bugfix correct error backoff handling for lb gateway (#1309) +- Apply ruleguard fixes (#1302) +- Add ruleguard rules (#1301) +- bugfix add feature vector duplication checking for LB-GW Upsert operation. (#1303) +- fix fails race condition test (#1299) +- Update dashboards, add operator dashboard and several panels (#1280) +- Add composite actions for E2E tests (#1257) +- add save index wait duration to index-manager for multiple agent index upload delay (#1292) +- Bugfix index count problem of ngt (#1288) +- fix example/helm/values.yaml (#1290) +- Fix time condition of agent & lb gateway (#1285) +- Use gofumpt for format workflow (#1281) +- [bugfix] kvsdb: do not increment the counter if key exists (#1282) +- Revert ":art: Use gofumpt as a default formatter (#1278)" (#1279) +- :art: Use gofumpt as a default formatter (#1278) +- :pencil: fix some wrongs (#1274) +- Refactor E2E tests: split operations into a new package (#1220) +- :bug: Fix the chart: agent-sidecar initContainer mode (#1271) + +## v1.1.1 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.1.1
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.1.1 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.1.1
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.1.1 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.1.1
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.1.1 +
Gateways + docker pull vdaas/vald-gateway:v1.1.1
+ docker pull ghcr.io/vdaas/vald/vald-gateway:v1.1.1
+ docker pull vdaas/vald-backup-gateway:v1.1.1
+ docker pull ghcr.io/vdaas/vald/vald-backup-gateway:v1.1.1
+ docker pull vdaas/vald-lb-gateway:v1.1.1
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.1.1
+ docker pull vdaas/vald-meta-gateway:v1.1.1
+ docker pull ghcr.io/vdaas/vald/vald-meta-gateway:v1.1.1 +
Backup managers + docker pull vdaas/vald-manager-backup-mysql:v1.1.1
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-mysql:v1.1.1
+ docker pull vdaas/vald-manager-backup-cassandra:v1.1.1
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-cassandra:v1.1.1 +
Compressor + docker pull vdaas/vald-manager-compressor:v1.1.1
+ docker pull ghcr.io/vdaas/vald/vald-manager-compressor:v1.1.1 +
Metas + docker pull vdaas/vald-meta-redis:v1.1.1
+ docker pull ghcr.io/vdaas/vald/vald-meta-redis:v1.1.1
+ docker pull vdaas/vald-meta-cassandra:v1.1.1
+ docker pull ghcr.io/vdaas/vald/vald-meta-cassandra:v1.1.1 +
Index Manager + docker pull vdaas/vald-manager-index:v1.1.1
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.1.1 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.1.1
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.1.1 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.1.1) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.1.1/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.1.1/charts/operator/helm/README.md) + +### Changes + +- [patch] release Vald v1.1.1 ([#1268](https://github.com/vdaas/vald/pull/1268)) +- fix copy CI error ([#1269](https://github.com/vdaas/vald/pull/1269)) +- bugfix nil internal/net/grpc.Client.Reconnect nil pointer fix ([#1270](https://github.com/vdaas/vald/pull/1270)) +- Docs/readme/replace architecture overview ([#1267](https://github.com/vdaas/vald/pull/1267)) +- format code ([#1266](https://github.com/vdaas/vald/pull/1266)) +- :robot: Update license headers / Format Go codes and YAML files ([#1265](https://github.com/vdaas/vald/pull/1265)) +- [bugfix] append timing of vqueue existing map ([#1264](https://github.com/vdaas/vald/pull/1264)) +- add resource type infos to grpc error response ([#1262](https://github.com/vdaas/vald/pull/1262)) +- :wrench: Fix clusterrole of scylla-cluster member ([#1263](https://github.com/vdaas/vald/pull/1263)) +- refactor file I/O & replace io.Copy to vald original faster Copy function ([#1261](https://github.com/vdaas/vald/pull/1261)) +- [bugfix] change exists cheking for agent vqueue ([#1256](https://github.com/vdaas/vald/pull/1256)) +- :robot: Update license headers / Format Go codes and YAML files ([#1255](https://github.com/vdaas/vald/pull/1255)) +- :fire: Remove invalid initialization option ([#1252](https://github.com/vdaas/vald/pull/1252)) +- add single connection client for agent & vald ([#1254](https://github.com/vdaas/vald/pull/1254)) +- bugfix remove unneccessary error return & add gRPC status code handling for backoff ([#1253](https://github.com/vdaas/vald/pull/1253)) +- bugfix agent vqueue & refactor tools/deps ([#1250](https://github.com/vdaas/vald/pull/1250)) +- Add test for pkg/agent/core/ngt/service/vqueue/option ([#1233](https://github.com/vdaas/vald/pull/1233)) +- Add config agent core ngt service kvs test ([#1223](https://github.com/vdaas/vald/pull/1223)) +- Fix update-helm-chart workflow ([#1249](https://github.com/vdaas/vald/pull/1249)) +- :robot: Automatically update k8s manifests ([#1248](https://github.com/vdaas/vald/pull/1248)) +- Fix path to the yq binary ([#1247](https://github.com/vdaas/vald/pull/1247)) + +## v1.1.0 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.1.0
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.1.0 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.1.0
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.1.0 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.1.0
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.1.0 +
Gateways + docker pull vdaas/vald-gateway:v1.1.0
+ docker pull ghcr.io/vdaas/vald/vald-gateway:v1.1.0
+ docker pull vdaas/vald-backup-gateway:v1.1.0
+ docker pull ghcr.io/vdaas/vald/vald-backup-gateway:v1.1.0
+ docker pull vdaas/vald-lb-gateway:v1.1.0
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.1.0
+ docker pull vdaas/vald-meta-gateway:v1.1.0
+ docker pull ghcr.io/vdaas/vald/vald-meta-gateway:v1.1.0 +
Backup managers + docker pull vdaas/vald-manager-backup-mysql:v1.1.0
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-mysql:v1.1.0
+ docker pull vdaas/vald-manager-backup-cassandra:v1.1.0
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-cassandra:v1.1.0 +
Compressor + docker pull vdaas/vald-manager-compressor:v1.1.0
+ docker pull ghcr.io/vdaas/vald/vald-manager-compressor:v1.1.0 +
Metas + docker pull vdaas/vald-meta-redis:v1.1.0
+ docker pull ghcr.io/vdaas/vald/vald-meta-redis:v1.1.0
+ docker pull vdaas/vald-meta-cassandra:v1.1.0
+ docker pull ghcr.io/vdaas/vald/vald-meta-cassandra:v1.1.0 +
Index Manager + docker pull vdaas/vald-manager-index:v1.1.0
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.1.0 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.1.0
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.1.0 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.1.0) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.1.0/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.1.0/charts/operator/helm/README.md) + +### Changes + +#### Feature + +- add correct gRPC status and error handling (#1224) +- Add Cloud Storage mode of Agent Sidecar. (#519) +- Add general info metrics package / Add NGT info metrics (#1195) +- improve internal/info implementation (#1177) +- Add saving flag to index.info payload (#1200) +- Fix mysql and cassandra schema definitions in initialize jobs (#1186) +- Remove values-xxx.yaml from Helm packages (#1107) +- Feature/internal net/use netaddr (#1154) +- Add Nop Logger for empty logger message (#1158) +- Remove arm64 build of filter-ingress-tf (#1153) +- add tensorflow filter (#951) +- Add OpenAPIv3 schema to CRDs (#1068) +- PR for review all logging statements (#1052) + +#### Bugfix + +- bugfix gateway search result merging process (#1238) +- remove nil check in Error() method in internal/errors (#1185) +- Bug fix of parallel test and goleak (#1231) +- :rotating_light: Fix linting errors (#1179) +- Fix mutex deadlock when calling error occurred (#1225) +- fix nil pointer panic when err is nil in Is() (#1216) +- Fix typo & missed schema definition (#1156) +- :lock: Fix validation about zip slip (#1150) +- :lock: Fix security issue: Add validation about path of extracted file (#1145) +- fix: nil pointer bug when config is nil and refactor variable name (#1139) +- :bug: Fix gateway service selectors in Helm chart (#1109) +- Bugfix correct error handling for agent apis (#1144) + +#### Document + +- separate tutorials into each document and add images (#1230) +- Update Tutotial/Get-Started (#1203) +- Update unit-test-guideline.md (#1213) +- create docs/user-guides/sdks (#1182) +- Fix typo in README. (#1163) + +#### CI/CD + +- Upgrade to GitHub-native Dependabot (#1211) +- Use kubectl create/replace for upgrading CRDs (#1199) +- :wrench: Fix scylla deploy task (#1159) + +#### Test Code + +- Add config agent core ngt test (#1219) +- implement pkg/agent/core/ngt/router test & refactor router implementation (#1214) +- Implement pkg/agent/core/ngt/handler/grpc/option test (#1215) +- refactor config error handling (#1190) +- Implement pkg/agent/core/ngt/router/option test (#1206) +- Implement safety bench code (#1171) +- create internal/config/redis test (#1147) +- Implement internal/config/transport test (#1172) +- Add internal/config/sidecar test (#1173) +- Add benchmark for internal/timeutil/time.go (#1086) +- Add internal/config/observability unit test (#1155) +- Add test for internal/config/mysql (#1151) +- create internal/config/cassandra test (#1117) +- create test for internal/config/server (#1175) +- Add internal/config/lb test (#1134) +- create internal/config/net test (#1140) +- :white_check_mark: create internal/config/meta test (#1133) +- Add internal/config/index test (#1129) +- :white_check_mark: create internal/config/backup test (#1132) +- delete internal/config/debug file (#1124) +- create internal/config/grpc test (#1130) +- Add internal/config/discoverer test (#1122) +- Add internal/config/filter.go test (#1125) +- create internal/config/gateway test (#1128) +- Implement internal/info/info.go benchmark (#1093) +- Add internal/config/client.go test (#1121) +- Implement internal/timeutil/location/loc.go benchmark test (#1091) +- :white_check_mark: remove unused variable from mysql test (#1120) +- feat: add blob test and comment (#1114) +- create bench code for internal/rand (#1089) +- Add test internal/config/compress.go (#1097) +- create internal/config/backoff test (#1104) +- change params for passing test of internal/backoff (#1108) +- remove unnecessary tests and update test (#1061) +- add internal backoff test (#1085) +- Add internal/core/algorithm/ngt/util.go test (#1066) +- Fix hack/tools/metrics failed builds (#1094) +- make timeouts for e2e and chaos tests longer (#1092) + +#### Dependencies + +- update go version to 1.16.4 (#1239) +- :arrow_up: Upgrade helm, valdcli, kubelinter and osdk (#1181) +- update codecov version for vulnerability (#1207) + +#### Others + +- update go modules (#1242) +- [ImgBot] Optimize images (#1241) +- Update license headers / Format codes (#1212) +- remove ngt version in filter-ingress-tensorflow (#1157) +- remove license from bot config json (#1164) +- improve internal/timeutil/location implementation (#1176) + +## v1.0.4 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.0.4
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.0.4 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.0.4
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.0.4 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.0.4
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.0.4 +
Gateways + docker pull vdaas/vald-gateway:v1.0.4
+ docker pull ghcr.io/vdaas/vald/vald-gateway:v1.0.4
+ docker pull vdaas/vald-backup-gateway:v1.0.4
+ docker pull ghcr.io/vdaas/vald/vald-backup-gateway:v1.0.4
+ docker pull vdaas/vald-lb-gateway:v1.0.4
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.0.4
+ docker pull vdaas/vald-meta-gateway:v1.0.4
+ docker pull ghcr.io/vdaas/vald/vald-meta-gateway:v1.0.4 +
Backup managers + docker pull vdaas/vald-manager-backup-mysql:v1.0.4
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-mysql:v1.0.4
+ docker pull vdaas/vald-manager-backup-cassandra:v1.0.4
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-cassandra:v1.0.4 +
Compressor + docker pull vdaas/vald-manager-compressor:v1.0.4
+ docker pull ghcr.io/vdaas/vald/vald-manager-compressor:v1.0.4 +
Metas + docker pull vdaas/vald-meta-redis:v1.0.4
+ docker pull ghcr.io/vdaas/vald/vald-meta-redis:v1.0.4
+ docker pull vdaas/vald-meta-cassandra:v1.0.4
+ docker pull ghcr.io/vdaas/vald/vald-meta-cassandra:v1.0.4 +
Index Manager + docker pull vdaas/vald-manager-index:v1.0.4
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.0.4 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.0.4
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.0.4 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.0.4) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.0.4/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.0.4/charts/operator/helm/README.md) + +### Changes + +- Fix error handling of SearchByID API in lb gateway ([#1084](https://github.com/vdaas/vald/pull/1084)) +- Update Agent dashboard ([#1069](https://github.com/vdaas/vald/pull/1069)) +- Revise gRPC error statuses and details in meta (Redis/Cassandra) ([#1013](https://github.com/vdaas/vald/pull/1013)) +- add remove sample code for tutorial ([#1053](https://github.com/vdaas/vald/pull/1053)) +- add grpc reflection ([#1064](https://github.com/vdaas/vald/pull/1064)) +- remove vcache for vald agent due to vcache delete timing control failure and time ordered concurrent vector queue called vqueue ([#1028](https://github.com/vdaas/vald/pull/1028)) +- refactor discoverer client ([#1056](https://github.com/vdaas/vald/pull/1056)) +- bugfix nil pointer no target discovered ([#1055](https://github.com/vdaas/vald/pull/1055)) +- Upgrade Operator SDK version to v1.4.2 ([#1038](https://github.com/vdaas/vald/pull/1038)) + +## v1.0.3 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.0.3
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.0.3 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.0.3
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.0.3 +
Discoverers + docker pull vdaas/vald-discoverer-k8s:v1.0.3
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.0.3 +
Gateways + docker pull vdaas/vald-gateway:v1.0.3
+ docker pull ghcr.io/vdaas/vald/vald-gateway:v1.0.3
+ docker pull vdaas/vald-backup-gateway:v1.0.3
+ docker pull ghcr.io/vdaas/vald/vald-backup-gateway:v1.0.3
+ docker pull vdaas/vald-lb-gateway:v1.0.3
+ docker pull ghcr.io/vdaas/vald/vald-lb-gateway:v1.0.3
+ docker pull vdaas/vald-meta-gateway:v1.0.3
+ docker pull ghcr.io/vdaas/vald/vald-meta-gateway:v1.0.3 +
Backup managers + docker pull vdaas/vald-manager-backup-mysql:v1.0.3
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-mysql:v1.0.3
+ docker pull vdaas/vald-manager-backup-cassandra:v1.0.3
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-cassandra:v1.0.3 +
Compressor + docker pull vdaas/vald-manager-compressor:v1.0.3
+ docker pull ghcr.io/vdaas/vald/vald-manager-compressor:v1.0.3 +
Metas + docker pull vdaas/vald-meta-redis:v1.0.3
+ docker pull ghcr.io/vdaas/vald/vald-meta-redis:v1.0.3
+ docker pull vdaas/vald-meta-cassandra:v1.0.3
+ docker pull ghcr.io/vdaas/vald/vald-meta-cassandra:v1.0.3 +
Index Manager + docker pull vdaas/vald-manager-index:v1.0.3
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.0.3 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.0.3
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.0.3 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.0.3) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.0.3/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.0.3/charts/operator/helm/README.md) + +### Changes + +- fix MySQL panic test ([#996](https://github.com/vdaas/vald/pull/996)) +- Update tutorial to support v1 ([#1009](https://github.com/vdaas/vald/pull/1009)) +- remove not supported control flag on darwin ([#1025](https://github.com/vdaas/vald/pull/1025)) +- Add strategy section to Docker build workflows ([#1024](https://github.com/vdaas/vald/pull/1024), [#1019](https://github.com/vdaas/vald/pull/1019)) +- move internal/net/tcp package to internal/net package and support unix domain socket ([#1010](https://github.com/vdaas/vald/pull/1010)) +- Implement internal/info/info test ([#862](https://github.com/vdaas/vald/pull/862)) +- add test for internal/errors/runner ([#1007](https://github.com/vdaas/vald/pull/1007)) +- add logo guideline ([#973](https://github.com/vdaas/vald/pull/973)) +- Fix invalid changelogs / update changelog workflows ([#1002](https://github.com/vdaas/vald/pull/1002)) +- Add test case for internal/errors/observability.go ([#993](https://github.com/vdaas/vald/pull/993)) + +## v1.0.2 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.0.2
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.0.2 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.0.2
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.0.2 +
Discoverer k8s + docker pull vdaas/vald-discoverer-k8s:v1.0.2
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.0.2 +
Gateway + docker pull vdaas/vald-gateway:v1.0.2
+ docker pull ghcr.io/vdaas/vald/vald-gateway:v1.0.2 +
Backup manager MySQL + docker pull vdaas/vald-manager-backup-mysql:v1.0.2
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-mysql:v1.0.2 +
Backup manager Cassandra + docker pull vdaas/vald-manager-backup-cassandra:v1.0.2
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-cassandra:v1.0.2 +
Compressor + docker pull vdaas/vald-manager-compressor:v1.0.2
+ docker pull ghcr.io/vdaas/vald/vald-manager-compressor:v1.0.2 +
Meta Redis + docker pull vdaas/vald-meta-redis:v1.0.2
+ docker pull ghcr.io/vdaas/vald/vald-meta-redis:v1.0.2 +
Meta Cassandra + docker pull vdaas/vald-meta-cassandra:v1.0.2
+ docker pull ghcr.io/vdaas/vald/vald-meta-cassandra:v1.0.2 +
Index Manager + docker pull vdaas/vald-manager-index:v1.0.2
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.0.2 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.0.2
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.0.2 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.0.2) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.0.2/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.0.2/charts/operator/helm/README.md) + +### Changes + +- v1.0.2 Release ([#998](https://github.com/vdaas/vald/pull/998)) +- :robot: Automatically update PULL_REQUEST_TEMPLATE and ISSUE_TEMPLATE ([#997](https://github.com/vdaas/vald/pull/997)) +- Bug fix on StreamGetObject API and DNS cache expiration / refactor net connection ([#986](https://github.com/vdaas/vald/pull/986)) +- Remove base docker image ([#995](https://github.com/vdaas/vald/pull/995)) +- Use namespaced names for priorityclasses of new gateways ([#992](https://github.com/vdaas/vald/pull/992)) +- Add E2E Chaos tests running on GitHub Actions ([#899](https://github.com/vdaas/vald/pull/899)) +- Add zap logger to chart schema ([#985](https://github.com/vdaas/vald/pull/985)) +- add test for internal/errors/runtime ([#984](https://github.com/vdaas/vald/pull/984)) +- add test for internal/errors/tensorflow ([#982](https://github.com/vdaas/vald/pull/982)) +- add test for internal/errors/unit ([#979](https://github.com/vdaas/vald/pull/979)) +- update chatops permission ([#990](https://github.com/vdaas/vald/pull/990)) +- :robot: Automatically update k8s manifests ([#981](https://github.com/vdaas/vald/pull/981)) + +## v1.0.1 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.0.1
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.0.1 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.0.1
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.0.1 +
Discoverer k8s + docker pull vdaas/vald-discoverer-k8s:v1.0.1
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.0.1 +
Gateway + docker pull vdaas/vald-gateway:v1.0.1
+ docker pull ghcr.io/vdaas/vald/vald-gateway:v1.0.1 +
Backup manager MySQL + docker pull vdaas/vald-manager-backup-mysql:v1.0.1
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-mysql:v1.0.1 +
Backup manager Cassandra + docker pull vdaas/vald-manager-backup-cassandra:v1.0.1
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-cassandra:v1.0.1 +
Compressor + docker pull vdaas/vald-manager-compressor:v1.0.1
+ docker pull ghcr.io/vdaas/vald/vald-manager-compressor:v1.0.1 +
Meta Redis + docker pull vdaas/vald-meta-redis:v1.0.1
+ docker pull ghcr.io/vdaas/vald/vald-meta-redis:v1.0.1 +
Meta Cassandra + docker pull vdaas/vald-meta-cassandra:v1.0.1
+ docker pull ghcr.io/vdaas/vald/vald-meta-cassandra:v1.0.1 +
Index Manager + docker pull vdaas/vald-manager-index:v1.0.1
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.0.1 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.0.1
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.0.1 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.0.1) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.0.1/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.0.1/charts/operator/helm/README.md) + +### Changes + +- bugfix lb-gateway's Insert rpc nil pointer panic ([#980](https://github.com/vdaas/vald/pull/980)) +- Implement internal/errors/worker test ([#952](https://github.com/vdaas/vald/pull/952)) +- create test for internal/errors/errors.go ([#929](https://github.com/vdaas/vald/pull/929)) +- Add test case for internal/errors/net.go ([#969](https://github.com/vdaas/vald/pull/969)) +- :robot: Automatically update k8s manifests ([#975](https://github.com/vdaas/vald/pull/975)) + +## v1.0.0 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v1.0.0
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v1.0.0 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v1.0.0
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v1.0.0 +
Discoverer k8s + docker pull vdaas/vald-discoverer-k8s:v1.0.0
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v1.0.0 +
Gateway + docker pull vdaas/vald-gateway:v1.0.0
+ docker pull ghcr.io/vdaas/vald/vald-gateway:v1.0.0 +
Backup manager MySQL + docker pull vdaas/vald-manager-backup-mysql:v1.0.0
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-mysql:v1.0.0 +
Backup manager Cassandra + docker pull vdaas/vald-manager-backup-cassandra:v1.0.0
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-cassandra:v1.0.0 +
Compressor + docker pull vdaas/vald-manager-compressor:v1.0.0
+ docker pull ghcr.io/vdaas/vald/vald-manager-compressor:v1.0.0 +
Meta Redis + docker pull vdaas/vald-meta-redis:v1.0.0
+ docker pull ghcr.io/vdaas/vald/vald-meta-redis:v1.0.0 +
Meta Cassandra + docker pull vdaas/vald-meta-cassandra:v1.0.0
+ docker pull ghcr.io/vdaas/vald/vald-meta-cassandra:v1.0.0 +
Index Manager + docker pull vdaas/vald-manager-index:v1.0.0
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v1.0.0 +
Helm Operator + docker pull vdaas/vald-helm-operator:v1.0.0
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v1.0.0 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v1.0.0) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v1.0.0/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v1.0.0/charts/operator/helm/README.md) + +### Changes + +- v1.0.0 Release ([#974](https://github.com/vdaas/vald/pull/974)) +- Bug fix for gateway ([#963](https://github.com/vdaas/vald/pull/963)) +- Add test case for internal/errors/ngt.go ([#936](https://github.com/vdaas/vald/pull/936)) +- create new test and refactor for errors/option ([#950](https://github.com/vdaas/vald/pull/950)) +- Use checksum of configmap for sidecar-enabled Agents. ([#970](https://github.com/vdaas/vald/pull/970)) +- :wrench: Use cass-operator to deploy cassandra for dev cluster ([#968](https://github.com/vdaas/vald/pull/968)) +- add test for internal/errors/vald ([#958](https://github.com/vdaas/vald/pull/958)) +- Remove actions/cache to improve workflow speed / Refactoring docker-build workflows ([#957](https://github.com/vdaas/vald/pull/957)) +- add filter gateway ([#948](https://github.com/vdaas/vald/pull/948)) +- :robot: Update license headers / Format Go codes and YAML files ([#954](https://github.com/vdaas/vald/pull/954)) +- Add Zap logger and access log interceptor ([#944](https://github.com/vdaas/vald/pull/944)) +- Add test case for internal/errors/mysql.go ([#918](https://github.com/vdaas/vald/pull/918)) +- Fix invalid data loading code in example ([#949](https://github.com/vdaas/vald/pull/949)) +- Add option to use networking.k8s.io/v1beta1 ingresses ([#945](https://github.com/vdaas/vald/pull/945)) +- bugfix gateway-lb nil pointer panic due to nil filter configuration ([#943](https://github.com/vdaas/vald/pull/943)) +- update rbac.authorization.k8s.io v1beta1 to v1 ([#942](https://github.com/vdaas/vald/pull/942)) +- Update Grafana dashboards ([#940](https://github.com/vdaas/vald/pull/940)) +- Add a gRPC interceptor for embedding payloads into trace spans ([#900](https://github.com/vdaas/vald/pull/900)) +- add goleak.IgnoreCurrent option for Parallel testing ([#941](https://github.com/vdaas/vald/pull/941)) +- update go modules ([#939](https://github.com/vdaas/vald/pull/939)) +- :robot: Automatically update PULL_REQUEST_TEMPLATE and ISSUE_TEMPLATE ([#937](https://github.com/vdaas/vald/pull/937)) +- :zap: Revise build command for multiplatforms ([#890](https://github.com/vdaas/vald/pull/890)) +- update dependencies ([#935](https://github.com/vdaas/vald/pull/935)) +- E2E deploy test: rewrite in go tests ([#814](https://github.com/vdaas/vald/pull/814)) +- create unit test guideline ([#869](https://github.com/vdaas/vald/pull/869)) +- feature/apis/change grpc error object ([#934](https://github.com/vdaas/vald/pull/934)) +- Add test case for internal/errors/http.go ([#908](https://github.com/vdaas/vald/pull/908)) +- :robot: Update license headers / Format Go codes and YAML files ([#933](https://github.com/vdaas/vald/pull/933)) +- revise kubelinter config / Add securityContext section to Helm chart ([#833](https://github.com/vdaas/vald/pull/833)) +- :robot: Update license headers / Format Go codes and YAML files ([#932](https://github.com/vdaas/vald/pull/932)) +- :robot: Automatically update PULL_REQUEST_TEMPLATE and ISSUE_TEMPLATE ([#931](https://github.com/vdaas/vald/pull/931)) +- os.free nil pointer failure in ngt cgo due to create index hang up ([#930](https://github.com/vdaas/vald/pull/930)) +- change grpc bidi-stream error handling and change grpc API interface ([#928](https://github.com/vdaas/vald/pull/928)) +- fix unclosed string literal in Dockerfile's ARG MAINTAINER ([#923](https://github.com/vdaas/vald/pull/923)) +- Revise building workflow of ci and dev containers ([#922](https://github.com/vdaas/vald/pull/922)) +- bugfix add nil check for gRPC connection pool objects in grpc/client.go ([#921](https://github.com/vdaas/vald/pull/921)) +- remove unneccessary pr-tag definition from chart ([#920](https://github.com/vdaas/vald/pull/920)) +- :pencil: Fix typo in gateway-vald configmap template ([#919](https://github.com/vdaas/vald/pull/919)) +- change docker base image PRIMARY_TAG name from nightly to latest ([#917](https://github.com/vdaas/vald/pull/917)) +- :green_heart: Use vdaas-ci token for making commits ([#895](https://github.com/vdaas/vald/pull/895)) +- Vald V1 New Design APIs ([#826](https://github.com/vdaas/vald/pull/826)) +- :robot: Automatically update k8s manifests ([#914](https://github.com/vdaas/vald/pull/914)) + +## v0.0.66 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v0.0.66
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v0.0.66 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v0.0.66
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v0.0.66 +
Discoverer k8s + docker pull vdaas/vald-discoverer-k8s:v0.0.66
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v0.0.66 +
Gateway + docker pull vdaas/vald-gateway:v0.0.66
+ docker pull ghcr.io/vdaas/vald/vald-gateway:v0.0.66 +
Backup manager MySQL + docker pull vdaas/vald-manager-backup-mysql:v0.0.66
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-mysql:v0.0.66 +
Backup manager Cassandra + docker pull vdaas/vald-manager-backup-cassandra:v0.0.66
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-cassandra:v0.0.66 +
Compressor + docker pull vdaas/vald-manager-compressor:v0.0.66
+ docker pull ghcr.io/vdaas/vald/vald-manager-compressor:v0.0.66 +
Meta Redis + docker pull vdaas/vald-meta-redis:v0.0.66
+ docker pull ghcr.io/vdaas/vald/vald-meta-redis:v0.0.66 +
Meta Cassandra + docker pull vdaas/vald-meta-cassandra:v0.0.66
+ docker pull ghcr.io/vdaas/vald/vald-meta-cassandra:v0.0.66 +
Index Manager + docker pull vdaas/vald-manager-index:v0.0.66
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v0.0.66 +
Helm Operator + docker pull vdaas/vald-helm-operator:v0.0.66
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v0.0.66 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.66) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.66/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.66/charts/operator/helm/README.md) + +### Changes + +- bugfix: do not create metadata file when create/append flag is not set for the agent/agent-sidecar ([#904](https://github.com/vdaas/vald/pull/904)) +- :robot: Update license headers / Format Go codes and YAML files ([#913](https://github.com/vdaas/vald/pull/913)) +- :green_heart: Add formatter for main branch ([#911](https://github.com/vdaas/vald/pull/911)) +- :page_facing_up: Update license headers for .github yamls ([#907](https://github.com/vdaas/vald/pull/907)) +- Add test case for internal/errors/io.go ([#910](https://github.com/vdaas/vald/pull/910)) +- Add test case for internal/errors/grpc.go ([#903](https://github.com/vdaas/vald/pull/903)) +- :robot: Automatically update k8s manifests ([#906](https://github.com/vdaas/vald/pull/906)) + +## v0.0.65 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v0.0.65
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v0.0.65 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v0.0.65
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v0.0.65 +
Discoverer k8s + docker pull vdaas/vald-discoverer-k8s:v0.0.65
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v0.0.65 +
Gateway + docker pull vdaas/vald-gateway:v0.0.65
+ docker pull ghcr.io/vdaas/vald/vald-gateway:v0.0.65 +
Backup manager MySQL + docker pull vdaas/vald-manager-backup-mysql:v0.0.65
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-mysql:v0.0.65 +
Backup manager Cassandra + docker pull vdaas/vald-manager-backup-cassandra:v0.0.65
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-cassandra:v0.0.65 +
Compressor + docker pull vdaas/vald-manager-compressor:v0.0.65
+ docker pull ghcr.io/vdaas/vald/vald-manager-compressor:v0.0.65 +
Meta Redis + docker pull vdaas/vald-meta-redis:v0.0.65
+ docker pull ghcr.io/vdaas/vald/vald-meta-redis:v0.0.65 +
Meta Cassandra + docker pull vdaas/vald-meta-cassandra:v0.0.65
+ docker pull ghcr.io/vdaas/vald/vald-meta-cassandra:v0.0.65 +
Index Manager + docker pull vdaas/vald-manager-index:v0.0.65
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v0.0.65 +
Helm Operator + docker pull vdaas/vald-helm-operator:v0.0.65
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v0.0.65 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.65) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.65/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.65/charts/operator/helm/README.md) + +### Changes + +- Happy new year ([#905](https://github.com/vdaas/vald/pull/905)) +- :white_check_mark: Add test case for internal/errors/file.go ([#893](https://github.com/vdaas/vald/pull/893)) +- :robot: Automatically update k8s manifests ([#902](https://github.com/vdaas/vald/pull/902)) + +## v0.0.64 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v0.0.64
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v0.0.64 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v0.0.64
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v0.0.64 +
Discoverer k8s + docker pull vdaas/vald-discoverer-k8s:v0.0.64
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v0.0.64 +
Gateway + docker pull vdaas/vald-gateway:v0.0.64
+ docker pull ghcr.io/vdaas/vald/vald-gateway:v0.0.64 +
Backup manager MySQL + docker pull vdaas/vald-manager-backup-mysql:v0.0.64
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-mysql:v0.0.64 +
Backup manager Cassandra + docker pull vdaas/vald-manager-backup-cassandra:v0.0.64
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-cassandra:v0.0.64 +
Compressor + docker pull vdaas/vald-manager-compressor:v0.0.64
+ docker pull ghcr.io/vdaas/vald/vald-manager-compressor:v0.0.64 +
Meta Redis + docker pull vdaas/vald-meta-redis:v0.0.64
+ docker pull ghcr.io/vdaas/vald/vald-meta-redis:v0.0.64 +
Meta Cassandra + docker pull vdaas/vald-meta-cassandra:v0.0.64
+ docker pull ghcr.io/vdaas/vald/vald-meta-cassandra:v0.0.64 +
Index Manager + docker pull vdaas/vald-manager-index:v0.0.64
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v0.0.64 +
Helm Operator + docker pull vdaas/vald-helm-operator:v0.0.64
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v0.0.64 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.64) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.64/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.64/charts/operator/helm/README.md) + +### Changes + +- :wrench: Rename PriorityClass names to contain namespace ([#901](https://github.com/vdaas/vald/pull/901)) +- :bug: Fix bug on updating status of VR & VHOR resources ([#892](https://github.com/vdaas/vald/pull/892)) +- :white_check_mark: Implement internal/errors/blob test ([#888](https://github.com/vdaas/vald/pull/888)) +- :white_check_mark: Add test for cassandra error ([#865](https://github.com/vdaas/vald/pull/865)) +- :white_check_mark: Add test case for internal/errors/discoverer.go ([#874](https://github.com/vdaas/vald/pull/874)) +- :bug: :white_check_mark: remove invalid import package from internal/errors/mysql_test.go ([#894](https://github.com/vdaas/vald/pull/894)) +- :white_check_mark: Add test for internal/errors/compressor.go ([#870](https://github.com/vdaas/vald/pull/870)) +- :robot: Automatically update k8s manifests ([#887](https://github.com/vdaas/vald/pull/887)) + +## v0.0.63 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v0.0.63
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v0.0.63 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v0.0.63
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v0.0.63 +
Discoverer k8s + docker pull vdaas/vald-discoverer-k8s:v0.0.63
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v0.0.63 +
Gateway + docker pull vdaas/vald-gateway:v0.0.63
+ docker pull ghcr.io/vdaas/vald/vald-gateway:v0.0.63 +
Backup manager MySQL + docker pull vdaas/vald-manager-backup-mysql:v0.0.63
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-mysql:v0.0.63 +
Backup manager Cassandra + docker pull vdaas/vald-manager-backup-cassandra:v0.0.63
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-cassandra:v0.0.63 +
Compressor + docker pull vdaas/vald-manager-compressor:v0.0.63
+ docker pull ghcr.io/vdaas/vald/vald-manager-compressor:v0.0.63 +
Meta Redis + docker pull vdaas/vald-meta-redis:v0.0.63
+ docker pull ghcr.io/vdaas/vald/vald-meta-redis:v0.0.63 +
Meta Cassandra + docker pull vdaas/vald-meta-cassandra:v0.0.63
+ docker pull ghcr.io/vdaas/vald/vald-meta-cassandra:v0.0.63 +
Index Manager + docker pull vdaas/vald-manager-index:v0.0.63
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v0.0.63 +
Helm Operator + docker pull vdaas/vald-helm-operator:v0.0.63
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v0.0.63 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.63) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.63/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.63/charts/operator/helm/README.md) + +### Changes + +- Remove go mod tidy from base image / Add valid token to semver job ([#886](https://github.com/vdaas/vald/pull/886)) +- bugfix agent duplicated data update execution ([#885](https://github.com/vdaas/vald/pull/885)) +- :package: Build schemas when building helm-operator image ([#879](https://github.com/vdaas/vald/pull/879)) +- :white_check_mark: Add s3 test and Refactor ([#837](https://github.com/vdaas/vald/pull/837)) +- :white_check_mark: Add internal/net/http/client test ([#858](https://github.com/vdaas/vald/pull/858)) +- :white_check_mark: add test case for json package ([#857](https://github.com/vdaas/vald/pull/857)) +- Add FOSSA scan workflow & .fossa.yml ([#846](https://github.com/vdaas/vald/pull/846)) +- :white_check_mark: create internal/net/http/client option test ([#831](https://github.com/vdaas/vald/pull/831)) +- :green_heart: fix checkout-v2 fetch depths ([#832](https://github.com/vdaas/vald/pull/832)) +- :wrench: update Helm 3.4.1, helm-docs ([#829](https://github.com/vdaas/vald/pull/829)) +- :white_check_mark: test/internal/nosql/cassandra test ([#809](https://github.com/vdaas/vald/pull/809)) +- :pencil: fix coding style for mock ([#806](https://github.com/vdaas/vald/pull/806)) +- CI: Add reviewdog-k8s ([#824](https://github.com/vdaas/vald/pull/824)) +- Fix e2e-bench-agent CI to fail correctly ([#800](https://github.com/vdaas/vald/pull/800)) +- Add internal/db/cassandra/conviction test ([#799](https://github.com/vdaas/vald/pull/799)) +- add test ([#798](https://github.com/vdaas/vald/pull/798)) +- :pencil: Fix coding guideline about constructor due to mock implementation ([#792](https://github.com/vdaas/vald/pull/792)) +- :robot: Automatically update k8s manifests ([#781](https://github.com/vdaas/vald/pull/781)) + +## v0.0.62 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v0.0.62
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v0.0.62 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v0.0.62
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v0.0.62 +
Discoverer k8s + docker pull vdaas/vald-discoverer-k8s:v0.0.62
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v0.0.62 +
Gateway + docker pull vdaas/vald-gateway:v0.0.62
+ docker pull ghcr.io/vdaas/vald/vald-gateway:v0.0.62 +
Backup manager MySQL + docker pull vdaas/vald-manager-backup-mysql:v0.0.62
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-mysql:v0.0.62 +
Backup manager Cassandra + docker pull vdaas/vald-manager-backup-cassandra:v0.0.62
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-cassandra:v0.0.62 +
Compressor + docker pull vdaas/vald-manager-compressor:v0.0.62
+ docker pull ghcr.io/vdaas/vald/vald-manager-compressor:v0.0.62 +
Meta Redis + docker pull vdaas/vald-meta-redis:v0.0.62
+ docker pull ghcr.io/vdaas/vald/vald-meta-redis:v0.0.62 +
Meta Cassandra + docker pull vdaas/vald-meta-cassandra:v0.0.62
+ docker pull ghcr.io/vdaas/vald/vald-meta-cassandra:v0.0.62 +
Index Manager + docker pull vdaas/vald-manager-index:v0.0.62
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v0.0.62 +
Helm Operator + docker pull vdaas/vald-helm-operator:v0.0.62
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v0.0.62 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.62) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.62/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.62/charts/operator/helm/README.md) + +### Changes + +- add 3 new distance type support for agent-ngt ([#780](https://github.com/vdaas/vald/pull/780)) +- upgrade KinD, Helm, valdcli, telepresence, tensorlfow, operator-sdk, helm-docs ([#776](https://github.com/vdaas/vald/pull/776)) +- :robot: Automatically update k8s manifests ([#774](https://github.com/vdaas/vald/pull/774)) + +## v0.0.61 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v0.0.61
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v0.0.61 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v0.0.61
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v0.0.61 +
Discoverer k8s + docker pull vdaas/vald-discoverer-k8s:v0.0.61
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v0.0.61 +
Gateway + docker pull vdaas/vald-gateway:v0.0.61
+ docker pull ghcr.io/vdaas/vald/vald-gateway:v0.0.61 +
Backup manager MySQL + docker pull vdaas/vald-manager-backup-mysql:v0.0.61
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-mysql:v0.0.61 +
Backup manager Cassandra + docker pull vdaas/vald-manager-backup-cassandra:v0.0.61
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-cassandra:v0.0.61 +
Compressor + docker pull vdaas/vald-manager-compressor:v0.0.61
+ docker pull ghcr.io/vdaas/vald/vald-manager-compressor:v0.0.61 +
Meta Redis + docker pull vdaas/vald-meta-redis:v0.0.61
+ docker pull ghcr.io/vdaas/vald/vald-meta-redis:v0.0.61 +
Meta Cassandra + docker pull vdaas/vald-meta-cassandra:v0.0.61
+ docker pull ghcr.io/vdaas/vald/vald-meta-cassandra:v0.0.61 +
Index Manager + docker pull vdaas/vald-manager-index:v0.0.61
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v0.0.61 +
Helm Operator + docker pull vdaas/vald-helm-operator:v0.0.61
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v0.0.61 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.61) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.61/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.61/charts/operator/helm/README.md) + +### Changes + +- fix search result sorting codes ([#772](https://github.com/vdaas/vald/pull/772)) +- :robot: Automatically update k8s manifests ([#771](https://github.com/vdaas/vald/pull/771)) + +## v0.0.60 + +### Docker images + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
componentDocker pull
Agent NGT + docker pull vdaas/vald-agent-ngt:v0.0.60
+ docker pull ghcr.io/vdaas/vald/vald-agent-ngt:v0.0.60 +
Agent sidecar + docker pull vdaas/vald-agent-sidecar:v0.0.60
+ docker pull ghcr.io/vdaas/vald/vald-agent-sidecar:v0.0.60 +
Discoverer k8s + docker pull vdaas/vald-discoverer-k8s:v0.0.60
+ docker pull ghcr.io/vdaas/vald/vald-discoverer-k8s:v0.0.60 +
Gateway + docker pull vdaas/vald-gateway:v0.0.60
+ docker pull ghcr.io/vdaas/vald/vald-gateway:v0.0.60 +
Backup manager MySQL + docker pull vdaas/vald-manager-backup-mysql:v0.0.60
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-mysql:v0.0.60 +
Backup manager Cassandra + docker pull vdaas/vald-manager-backup-cassandra:v0.0.60
+ docker pull ghcr.io/vdaas/vald/vald-manager-backup-cassandra:v0.0.60 +
Compressor + docker pull vdaas/vald-manager-compressor:v0.0.60
+ docker pull ghcr.io/vdaas/vald/vald-manager-compressor:v0.0.60 +
Meta Redis + docker pull vdaas/vald-meta-redis:v0.0.60
+ docker pull ghcr.io/vdaas/vald/vald-meta-redis:v0.0.60 +
Meta Cassandra + docker pull vdaas/vald-meta-cassandra:v0.0.60
+ docker pull ghcr.io/vdaas/vald/vald-meta-cassandra:v0.0.60 +
Index Manager + docker pull vdaas/vald-manager-index:v0.0.60
+ docker pull ghcr.io/vdaas/vald/vald-manager-index:v0.0.60 +
Helm Operator + docker pull vdaas/vald-helm-operator:v0.0.60
+ docker pull ghcr.io/vdaas/vald/vald-helm-operator:v0.0.60 +
+ +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.60) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.60/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.60/charts/operator/helm/README.md) + +### Changes + +- Fix fails test for s3 reader ([#770](https://github.com/vdaas/vald/pull/770)) +- CI: Make docker builds fast again ([#756](https://github.com/vdaas/vald/pull/756)) +- :robot: Automatically update k8s manifests ([#769](https://github.com/vdaas/vald/pull/769)) + +## v0.0.59 + +### Docker images + +| component | docker pull | +| ------------------------ | --------------------------------------------------------- | +| agent NGT | `docker pull vdaas/vald-agent-ngt:v0.0.59` | +| agent sidecar | `docker pull vdaas/vald-agent-sidecar:v0.0.59` | +| discoverer K8s | `docker pull vdaas/vald-discoverer-k8s:v0.0.59` | +| gateway | `docker pull vdaas/vald-gateway:v0.0.59` | +| backup manager MySQL | `docker pull vdaas/vald-manager-backup-mysql:v0.0.59` | +| backup manager Cassandra | `docker pull vdaas/vald-manager-backup-cassandra:v0.0.59` | +| compressor | `docker pull vdaas/vald-manager-compressor:v0.0.59` | +| meta Redis | `docker pull vdaas/vald-meta-redis:v0.0.59` | +| meta Cassandra | `docker pull vdaas/vald-meta-cassandra:v0.0.59` | +| index manager | `docker pull vdaas/vald-manager-index:v0.0.59` | +| Helm operator | `docker pull vdaas/vald-helm-operator:v0.0.59` | + +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.59) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.59/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.59/charts/operator/helm/README.md) + +### Changes + +- bugfix gateway index out of bounds ([#768](https://github.com/vdaas/vald/pull/768)) +- :robot: Automatically update k8s manifests ([#766](https://github.com/vdaas/vald/pull/766)) + +## v0.0.58 + +### Docker images + +| component | docker pull | +| ------------------------ | --------------------------------------------------------- | +| agent NGT | `docker pull vdaas/vald-agent-ngt:v0.0.58` | +| agent sidecar | `docker pull vdaas/vald-agent-sidecar:v0.0.58` | +| discoverer K8s | `docker pull vdaas/vald-discoverer-k8s:v0.0.58` | +| gateway | `docker pull vdaas/vald-gateway:v0.0.58` | +| backup manager MySQL | `docker pull vdaas/vald-manager-backup-mysql:v0.0.58` | +| backup manager Cassandra | `docker pull vdaas/vald-manager-backup-cassandra:v0.0.58` | +| compressor | `docker pull vdaas/vald-manager-compressor:v0.0.58` | +| meta Redis | `docker pull vdaas/vald-meta-redis:v0.0.58` | +| meta Cassandra | `docker pull vdaas/vald-meta-cassandra:v0.0.58` | +| index manager | `docker pull vdaas/vald-manager-index:v0.0.58` | +| Helm operator | `docker pull vdaas/vald-helm-operator:v0.0.58` | + +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.58) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.58/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.58/charts/operator/helm/README.md) + +### Changes + +- change gateway vald's mutex lock ([#765](https://github.com/vdaas/vald/pull/765)) +- patch add more effective Close function for internal/core ([#764](https://github.com/vdaas/vald/pull/764)) +- :white_check_mark: Fix mysql test failure ([#750](https://github.com/vdaas/vald/pull/750)) +- :green_heart: remove deprecated set-env commands ([#752](https://github.com/vdaas/vald/pull/752)) +- bugfix discoverer nil map reference ([#745](https://github.com/vdaas/vald/pull/745)) +- add test of internal/db/rdb/mysql ([#659](https://github.com/vdaas/vald/pull/659)) +- :white_check_mark: :recycle: Add test for internal/db/storage/blob/s3/reader ([#718](https://github.com/vdaas/vald/pull/718)) +- :white_check_mark: Cassandra option test (part 2) ([#724](https://github.com/vdaas/vald/pull/724)) +- CI: Build multi-platform Docker images ([#727](https://github.com/vdaas/vald/pull/727)) +- :white_check_mark: Add test for s3 session option ([#736](https://github.com/vdaas/vald/pull/736)) +- :white_check_mark: Create s3/session test ([#702](https://github.com/vdaas/vald/pull/702)) +- :fire: remove dependencies to gql proto ([#731](https://github.com/vdaas/vald/pull/731)) +- :robot: Automatically update k8s manifests ([#730](https://github.com/vdaas/vald/pull/730)) + +## v0.0.57 + +### Docker images + +| component | docker pull | +| ------------------------ | --------------------------------------------------------- | +| agent NGT | `docker pull vdaas/vald-agent-ngt:v0.0.57` | +| agent sidecar | `docker pull vdaas/vald-agent-sidecar:v0.0.57` | +| discoverer K8s | `docker pull vdaas/vald-discoverer-k8s:v0.0.57` | +| gateway | `docker pull vdaas/vald-gateway:v0.0.57` | +| backup manager MySQL | `docker pull vdaas/vald-manager-backup-mysql:v0.0.57` | +| backup manager Cassandra | `docker pull vdaas/vald-manager-backup-cassandra:v0.0.57` | +| compressor | `docker pull vdaas/vald-manager-compressor:v0.0.57` | +| meta Redis | `docker pull vdaas/vald-meta-redis:v0.0.57` | +| meta Cassandra | `docker pull vdaas/vald-meta-cassandra:v0.0.57` | +| index manager | `docker pull vdaas/vald-manager-index:v0.0.57` | +| Helm operator | `docker pull vdaas/vald-helm-operator:v0.0.57` | + +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.57) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.57/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.57/charts/operator/helm/README.md) + +### Changes + +- fix duplicated search result ([#729](https://github.com/vdaas/vald/pull/729)) +- :recycle: enable to inject only agent-sidecar on initContainer mode without enabling sidecar mode ([#726](https://github.com/vdaas/vald/pull/726)) +- :sparkles: implement billion scale data ([#612](https://github.com/vdaas/vald/pull/612)) +- Add devcontainer ([#620](https://github.com/vdaas/vald/pull/620)) +- :white_check_makr: :recycle: Add test for s3/writer and Refactor. ([#672](https://github.com/vdaas/vald/pull/672)) +- CI-container: upgrade dependencies of & remove workdir contents ([#711](https://github.com/vdaas/vald/pull/711)) +- :robot: Automatically update k8s manifests ([#708](https://github.com/vdaas/vald/pull/708)) + +## v0.0.56 + +### Docker images + +| component | docker pull | +| ------------------------ | --------------------------------------------------------- | +| agent NGT | `docker pull vdaas/vald-agent-ngt:v0.0.56` | +| agent sidecar | `docker pull vdaas/vald-agent-sidecar:v0.0.56` | +| discoverer K8s | `docker pull vdaas/vald-discoverer-k8s:v0.0.56` | +| gateway | `docker pull vdaas/vald-gateway:v0.0.56` | +| backup manager MySQL | `docker pull vdaas/vald-manager-backup-mysql:v0.0.56` | +| backup manager Cassandra | `docker pull vdaas/vald-manager-backup-cassandra:v0.0.56` | +| compressor | `docker pull vdaas/vald-manager-compressor:v0.0.56` | +| meta Redis | `docker pull vdaas/vald-meta-redis:v0.0.56` | +| meta Cassandra | `docker pull vdaas/vald-meta-cassandra:v0.0.56` | +| index manager | `docker pull vdaas/vald-manager-index:v0.0.56` | +| Helm operator | `docker pull vdaas/vald-helm-operator:v0.0.56` | + +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.56) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.56/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.56/charts/operator/helm/README.md) + +### Changes + +- add C.free & delete ivc before core.BulkInsert C' function executing for reducing memory usage ([#701](https://github.com/vdaas/vald/pull/701)) +- Add cassandra option test ([#644](https://github.com/vdaas/vald/pull/644)) +- :memo: build single artifact from pbdocs task ([#699](https://github.com/vdaas/vald/pull/699)) +- improve CI builds: use DOCKER_BUILDKIT ([#706](https://github.com/vdaas/vald/pull/706)) +- :robot: Automatically update PULL_REQUEST_TEMPLATE and ISSUE_TEMPLATE ([#695](https://github.com/vdaas/vald/pull/695)) +- :pencil: add AVX2 to Requirements section ([#686](https://github.com/vdaas/vald/pull/686)) +- :pencil: update contributing guide ([#678](https://github.com/vdaas/vald/pull/678)) +- Use runtime.GC for reducing indexing memory & replace saveMu with atomic busy loop for race control ([#682](https://github.com/vdaas/vald/pull/682)) +- :white_check_mark: :recycle: Implement zstd test ([#676](https://github.com/vdaas/vald/pull/676)) +- :sparkles: use internal client ([#618](https://github.com/vdaas/vald/pull/618)) +- :robot: Automatically update k8s manifests ([#684](https://github.com/vdaas/vald/pull/684)) + +## v0.0.55 + +### Docker images + +| component | docker pull | +| ------------------------ | --------------------------------------------------------- | +| agent NGT | `docker pull vdaas/vald-agent-ngt:v0.0.55` | +| agent sidecar | `docker pull vdaas/vald-agent-sidecar:v0.0.55` | +| discoverer K8s | `docker pull vdaas/vald-discoverer-k8s:v0.0.55` | +| gateway | `docker pull vdaas/vald-gateway:v0.0.55` | +| backup manager MySQL | `docker pull vdaas/vald-manager-backup-mysql:v0.0.55` | +| backup manager Cassandra | `docker pull vdaas/vald-manager-backup-cassandra:v0.0.55` | +| compressor | `docker pull vdaas/vald-manager-compressor:v0.0.55` | +| meta Redis | `docker pull vdaas/vald-meta-redis:v0.0.55` | +| meta Cassandra | `docker pull vdaas/vald-meta-cassandra:v0.0.55` | +| index manager | `docker pull vdaas/vald-manager-index:v0.0.55` | +| Helm operator | `docker pull vdaas/vald-helm-operator:v0.0.55` | + +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.55) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.55/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.55/charts/operator/helm/README.md) + +### Changes + +- pass CFLAGS, CXXFLAGS to NGT build command ([#683](https://github.com/vdaas/vald/pull/683)) +- :robot: Automatically update k8s manifests ([#681](https://github.com/vdaas/vald/pull/681)) + +## v0.0.54 + +### Docker images + +| component | docker pull | +| ------------------------ | --------------------------------------------------------- | +| agent NGT | `docker pull vdaas/vald-agent-ngt:v0.0.54` | +| agent sidecar | `docker pull vdaas/vald-agent-sidecar:v0.0.54` | +| discoverer K8s | `docker pull vdaas/vald-discoverer-k8s:v0.0.54` | +| gateway | `docker pull vdaas/vald-gateway:v0.0.54` | +| backup manager MySQL | `docker pull vdaas/vald-manager-backup-mysql:v0.0.54` | +| backup manager Cassandra | `docker pull vdaas/vald-manager-backup-cassandra:v0.0.54` | +| compressor | `docker pull vdaas/vald-manager-compressor:v0.0.54` | +| meta Redis | `docker pull vdaas/vald-meta-redis:v0.0.54` | +| meta Cassandra | `docker pull vdaas/vald-meta-cassandra:v0.0.54` | +| index manager | `docker pull vdaas/vald-manager-index:v0.0.54` | +| Helm operator | `docker pull vdaas/vald-helm-operator:v0.0.54` | + +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.54) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.54/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.54/charts/operator/helm/README.md) + +### Changes + +- bugfix error assertion ([#680](https://github.com/vdaas/vald/pull/680)) +- :robot: Automatically update k8s manifests ([#679](https://github.com/vdaas/vald/pull/679)) + +## v0.0.53 + +### Docker images + +| component | docker pull | +| ------------------------ | --------------------------------------------------------- | +| agent NGT | `docker pull vdaas/vald-agent-ngt:v0.0.53` | +| agent sidecar | `docker pull vdaas/vald-agent-sidecar:v0.0.53` | +| discoverer K8s | `docker pull vdaas/vald-discoverer-k8s:v0.0.53` | +| gateway | `docker pull vdaas/vald-gateway:v0.0.53` | +| backup manager MySQL | `docker pull vdaas/vald-manager-backup-mysql:v0.0.53` | +| backup manager Cassandra | `docker pull vdaas/vald-manager-backup-cassandra:v0.0.53` | +| compressor | `docker pull vdaas/vald-manager-compressor:v0.0.53` | +| meta Redis | `docker pull vdaas/vald-meta-redis:v0.0.53` | +| meta Cassandra | `docker pull vdaas/vald-meta-cassandra:v0.0.53` | +| index manager | `docker pull vdaas/vald-manager-index:v0.0.53` | +| Helm operator | `docker pull vdaas/vald-helm-operator:v0.0.53` | + +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.53) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.53/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.53/charts/operator/helm/README.md) + +### Changes + +- remove cockroachdb/errors ([#677](https://github.com/vdaas/vald/pull/677)) +- :white_check_mark: Add test case for storage/blob/s3/writer/option ([#656](https://github.com/vdaas/vald/pull/656)) +- :white_check_mark: fix: failing tset ([#671](https://github.com/vdaas/vald/pull/671)) +- :bug: fix & upgrade manifests to operator-sdk v1.0.0 compatible ([#667](https://github.com/vdaas/vald/pull/667)) +- :robot: Automatically update k8s manifests ([#666](https://github.com/vdaas/vald/pull/666)) + +## v0.0.52 + +### Docker images + +| component | docker pull | +| ------------------------ | --------------------------------------------------------- | +| agent NGT | `docker pull vdaas/vald-agent-ngt:v0.0.52` | +| agent sidecar | `docker pull vdaas/vald-agent-sidecar:v0.0.52` | +| discoverer K8s | `docker pull vdaas/vald-discoverer-k8s:v0.0.52` | +| gateway | `docker pull vdaas/vald-gateway:v0.0.52` | +| backup manager MySQL | `docker pull vdaas/vald-manager-backup-mysql:v0.0.52` | +| backup manager Cassandra | `docker pull vdaas/vald-manager-backup-cassandra:v0.0.52` | +| compressor | `docker pull vdaas/vald-manager-compressor:v0.0.52` | +| meta Redis | `docker pull vdaas/vald-meta-redis:v0.0.52` | +| meta Cassandra | `docker pull vdaas/vald-meta-cassandra:v0.0.52` | +| index manager | `docker pull vdaas/vald-manager-index:v0.0.52` | +| Helm operator | `docker pull vdaas/vald-helm-operator:v0.0.52` | + +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.52) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.52/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.52/charts/operator/helm/README.md) + +### Changes + +- add build stage for operator-sdk docker v1.0.0 permission changes ([#665](https://github.com/vdaas/vald/pull/665)) +- :robot: Automatically update PULL_REQUEST_TEMPLATE and ISSUE_TEMPLATE ([#663](https://github.com/vdaas/vald/pull/663)) +- :robot: Automatically update k8s manifests ([#664](https://github.com/vdaas/vald/pull/664)) + +## v0.0.51 + +### Docker images + +| component | docker pull | +| ------------------------ | --------------------------------------------------------- | +| agent NGT | `docker pull vdaas/vald-agent-ngt:v0.0.51` | +| agent sidecar | `docker pull vdaas/vald-agent-sidecar:v0.0.51` | +| discoverer K8s | `docker pull vdaas/vald-discoverer-k8s:v0.0.51` | +| gateway | `docker pull vdaas/vald-gateway:v0.0.51` | +| backup manager MySQL | `docker pull vdaas/vald-manager-backup-mysql:v0.0.51` | +| backup manager Cassandra | `docker pull vdaas/vald-manager-backup-cassandra:v0.0.51` | +| compressor | `docker pull vdaas/vald-manager-compressor:v0.0.51` | +| meta Redis | `docker pull vdaas/vald-meta-redis:v0.0.51` | +| meta Cassandra | `docker pull vdaas/vald-meta-cassandra:v0.0.51` | +| index manager | `docker pull vdaas/vald-manager-index:v0.0.51` | +| Helm operator | `docker pull vdaas/vald-helm-operator:v0.0.51` | + +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.51) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.51/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.51/charts/operator/helm/README.md) + +### Changes + +- update deps ([#660](https://github.com/vdaas/vald/pull/660)) +- add metrics for indexer and sidecar ([#642](https://github.com/vdaas/vald/pull/642)) +- :pencil2: fix indents in helm chart of vald-helm-operator ([#658](https://github.com/vdaas/vald/pull/658)) +- :white_check_mark: Add test for internal/compress/gob.go ([#646](https://github.com/vdaas/vald/pull/646)) +- Upgrade go mod default: k8s.io/xxx v0.18.8 ([#645](https://github.com/vdaas/vald/pull/645)) +- [Coding guideline] Add implementation and grouping section ([#641](https://github.com/vdaas/vald/pull/641)) +- :white_check_mark: add internal/compress/lz4 test ([#643](https://github.com/vdaas/vald/pull/643)) +- Add test case for `s3/option.go` ([#640](https://github.com/vdaas/vald/pull/640)) +- Refactoring and Add test code for `compress` ([#622](https://github.com/vdaas/vald/pull/622)) +- [ImgBot] Optimize images ([#639](https://github.com/vdaas/vald/pull/639)) +- Add operation guide ([#541](https://github.com/vdaas/vald/pull/541)) +- :white_check_mark: add internal/s3/reader/option test ([#630](https://github.com/vdaas/vald/pull/630)) +- :bug: Fix indexer's creation_pool_size field ([#637](https://github.com/vdaas/vald/pull/637)) +- :wrench: revise languagetool rules: disable EN_QUOTES ([#635](https://github.com/vdaas/vald/pull/635)) +- :wrench: revise languagetool rules: disable TYPOS, DASH_RULE ([#634](https://github.com/vdaas/vald/pull/634)) +- :wrench: revise languagetool rules ([#633](https://github.com/vdaas/vald/pull/633)) +- [ImgBot] Optimize images ([#632](https://github.com/vdaas/vald/pull/632)) +- Add upsert flow in architecture doc ([#627](https://github.com/vdaas/vald/pull/627)) +- Add DB metrics & traces: Redis, MySQL ([#623](https://github.com/vdaas/vald/pull/623)) +- :white_check_mark: add internal/db/rdb/mysql/model test ([#628](https://github.com/vdaas/vald/pull/628)) +- :wrench: upload sarif only for HIGH or CRITICAL ([#629](https://github.com/vdaas/vald/pull/629)) +- :white_check_mark: add internal/db/rdb/mysql/option test ([#626](https://github.com/vdaas/vald/pull/626)) +- Add internal/net test ([#615](https://github.com/vdaas/vald/pull/615)) +- :white_check_mark: Add test for gzip_option ([#625](https://github.com/vdaas/vald/pull/625)) +- :pencil: change showing image method ([#624](https://github.com/vdaas/vald/pull/624)) +- :white_check_mark: Add internal/compress/zstd_option test ([#621](https://github.com/vdaas/vald/pull/621)) +- use distroless for base image ([#605](https://github.com/vdaas/vald/pull/605)) +- :pencil: Coding guideline: Add error checking section ([#614](https://github.com/vdaas/vald/pull/614)) +- :white_check_mark: add internal/compress/lz4_option test ([#619](https://github.com/vdaas/vald/pull/619)) +- :white_check_mark: fix test fail ([#616](https://github.com/vdaas/vald/pull/616)) +- :white_check_mark: Add test of internal/worker/queue_option ([#613](https://github.com/vdaas/vald/pull/613)) +- [ImgBot] Optimize images ([#617](https://github.com/vdaas/vald/pull/617)) +- :pencil: Add update dataflow in architecture document ([#601](https://github.com/vdaas/vald/pull/601)) +- Add internal/worker/worker test ([#602](https://github.com/vdaas/vald/pull/602)) +- :recycle: refactor load test ([#552](https://github.com/vdaas/vald/pull/552)) +- :white_check_mark: Add test case for internal/kvs/redis/option.go ([#611](https://github.com/vdaas/vald/pull/611)) +- :white_check_mark: create internal/worker/queue test ([#606](https://github.com/vdaas/vald/pull/606)) +- :white_check_mark: :recycle: Add internal roundtrip test code ([#589](https://github.com/vdaas/vald/pull/589)) +- :pencil: Documentation/performance/loadtest ([#610](https://github.com/vdaas/vald/pull/610)) +- :robot: Automatically update k8s manifests ([#609](https://github.com/vdaas/vald/pull/609)) + +## v0.0.50 + +### Docker images + +| component | docker pull | +| ------------------------ | --------------------------------------------------------- | +| agent NGT | `docker pull vdaas/vald-agent-ngt:v0.0.50` | +| agent sidecar | `docker pull vdaas/vald-agent-sidecar:v0.0.50` | +| discoverer K8s | `docker pull vdaas/vald-discoverer-k8s:v0.0.50` | +| gateway | `docker pull vdaas/vald-gateway:v0.0.50` | +| backup manager MySQL | `docker pull vdaas/vald-manager-backup-mysql:v0.0.50` | +| backup manager Cassandra | `docker pull vdaas/vald-manager-backup-cassandra:v0.0.50` | +| compressor | `docker pull vdaas/vald-manager-compressor:v0.0.50` | +| meta Redis | `docker pull vdaas/vald-meta-redis:v0.0.50` | +| meta Cassandra | `docker pull vdaas/vald-meta-cassandra:v0.0.50` | +| index manager | `docker pull vdaas/vald-manager-index:v0.0.50` | +| Helm operator | `docker pull vdaas/vald-helm-operator:v0.0.50` | + +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.50) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.50/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.50/charts/operator/helm/README.md) + +### Changes + +- Add warn logging messages to agent-sidecar & ignore io.EOF error when reading metadata.json ([#608](https://github.com/vdaas/vald/pull/608)) +- Add DB metrics: Cassandra ([#587](https://github.com/vdaas/vald/pull/587)) +- :recycle: Improve Singleflight performance ([#580](https://github.com/vdaas/vald/pull/580)) +- [ImgBot] Optimize images ([#607](https://github.com/vdaas/vald/pull/607)) +- :pencil: add delete dataflow in architecture document ([#591](https://github.com/vdaas/vald/pull/591)) +- :recycle: Add gaussian random vector generation ([#595](https://github.com/vdaas/vald/pull/595)) +- :recycle: :white_check_mark: Refactoring `internal/db/kvs/redis` package ([#590](https://github.com/vdaas/vald/pull/590)) +- :green_heart: Add reviewdog - markdown: LanguageTool ([#604](https://github.com/vdaas/vald/pull/604)) +- :green_heart: Add reviewdog - hadolint ([#603](https://github.com/vdaas/vald/pull/603)) +- :robot: Automatically update k8s manifests ([#599](https://github.com/vdaas/vald/pull/599)) + +## v0.0.49 + +### Docker images + +| component | docker pull | +| ------------------------ | --------------------------------------------------------- | +| agent NGT | `docker pull vdaas/vald-agent-ngt:v0.0.49` | +| agent sidecar | `docker pull vdaas/vald-agent-sidecar:v0.0.49` | +| discoverer K8s | `docker pull vdaas/vald-discoverer-k8s:v0.0.49` | +| gateway | `docker pull vdaas/vald-gateway:v0.0.49` | +| backup manager MySQL | `docker pull vdaas/vald-manager-backup-mysql:v0.0.49` | +| backup manager Cassandra | `docker pull vdaas/vald-manager-backup-cassandra:v0.0.49` | +| compressor | `docker pull vdaas/vald-manager-compressor:v0.0.49` | +| meta Redis | `docker pull vdaas/vald-meta-redis:v0.0.49` | +| meta Cassandra | `docker pull vdaas/vald-meta-cassandra:v0.0.49` | +| index manager | `docker pull vdaas/vald-manager-index:v0.0.49` | +| Helm operator | `docker pull vdaas/vald-helm-operator:v0.0.49` | + +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.49) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.49/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.49/charts/operator/helm/README.md) + +### Changes + +- :bug: fix agent sidecar behavior ([#598](https://github.com/vdaas/vald/pull/598)) +- :robot: Automatically update k8s manifests ([#597](https://github.com/vdaas/vald/pull/597)) + +## v0.0.48 + +### Docker images + +| component | docker pull | +| ------------------------ | --------------------------------------------------------- | +| agent NGT | `docker pull vdaas/vald-agent-ngt:v0.0.48` | +| agent sidecar | `docker pull vdaas/vald-agent-sidecar:v0.0.48` | +| discoverer K8s | `docker pull vdaas/vald-discoverer-k8s:v0.0.48` | +| gateway | `docker pull vdaas/vald-gateway:v0.0.48` | +| backup manager MySQL | `docker pull vdaas/vald-manager-backup-mysql:v0.0.48` | +| backup manager Cassandra | `docker pull vdaas/vald-manager-backup-cassandra:v0.0.48` | +| compressor | `docker pull vdaas/vald-manager-compressor:v0.0.48` | +| meta Redis | `docker pull vdaas/vald-meta-redis:v0.0.48` | +| meta Cassandra | `docker pull vdaas/vald-meta-cassandra:v0.0.48` | +| index manager | `docker pull vdaas/vald-manager-index:v0.0.48` | +| Helm operator | `docker pull vdaas/vald-helm-operator:v0.0.48` | + +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.48) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.48/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.48/charts/operator/helm/README.md) + +### Changes + +- :bug: fix behavior when index path is empty ([#596](https://github.com/vdaas/vald/pull/596)) +- :white_check_mark: add internal/net/http/transport/option test ([#594](https://github.com/vdaas/vald/pull/594)) +- tensorflow savedmodel warmup ([#539](https://github.com/vdaas/vald/pull/539)) +- :robot: Automatically update k8s manifests ([#592](https://github.com/vdaas/vald/pull/592)) + +## v0.0.47 + +### Docker images + +| component | docker pull | +| ------------------------ | --------------------------------------------------------- | +| agent NGT | `docker pull vdaas/vald-agent-ngt:v0.0.47` | +| agent sidecar | `docker pull vdaas/vald-agent-sidecar:v0.0.47` | +| discoverer K8s | `docker pull vdaas/vald-discoverer-k8s:v0.0.47` | +| gateway | `docker pull vdaas/vald-gateway:v0.0.47` | +| backup manager MySQL | `docker pull vdaas/vald-manager-backup-mysql:v0.0.47` | +| backup manager Cassandra | `docker pull vdaas/vald-manager-backup-cassandra:v0.0.47` | +| compressor | `docker pull vdaas/vald-manager-compressor:v0.0.47` | +| meta Redis | `docker pull vdaas/vald-meta-redis:v0.0.47` | +| meta Cassandra | `docker pull vdaas/vald-meta-cassandra:v0.0.47` | +| index manager | `docker pull vdaas/vald-manager-index:v0.0.47` | +| Helm operator | `docker pull vdaas/vald-helm-operator:v0.0.47` | + +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.47) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.47/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.47/charts/operator/helm/README.md) + +### Changes + +- [agent-NGT, sidecar] Improve S3 backup/recover behavior ([#556](https://github.com/vdaas/vald/pull/556)) +- :white_check_mark: add internal/cache/option test ([#586](https://github.com/vdaas/vald/pull/586)) +- :robot: Automatically update k8s manifests ([#588](https://github.com/vdaas/vald/pull/588)) + +## v0.0.46 + +### Docker images + +| component | docker pull | +| ------------------------ | --------------------------------------------------------- | +| agent NGT | `docker pull vdaas/vald-agent-ngt:v0.0.46` | +| agent sidecar | `docker pull vdaas/vald-agent-sidecar:v0.0.46` | +| discoverer K8s | `docker pull vdaas/vald-discoverer-k8s:v0.0.46` | +| gateway | `docker pull vdaas/vald-gateway:v0.0.46` | +| backup manager MySQL | `docker pull vdaas/vald-manager-backup-mysql:v0.0.46` | +| backup manager Cassandra | `docker pull vdaas/vald-manager-backup-cassandra:v0.0.46` | +| compressor | `docker pull vdaas/vald-manager-compressor:v0.0.46` | +| meta Redis | `docker pull vdaas/vald-meta-redis:v0.0.46` | +| meta Cassandra | `docker pull vdaas/vald-meta-cassandra:v0.0.46` | +| index manager | `docker pull vdaas/vald-manager-index:v0.0.46` | +| Helm operator | `docker pull vdaas/vald-helm-operator:v0.0.46` | + +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.46) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.46/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.46/charts/operator/helm/README.md) + +### Changes + +- Test/internal/tcp ([#501](https://github.com/vdaas/vald/pull/501)) +- :white_check_mark: add internal/cache/gache test ([#583](https://github.com/vdaas/vald/pull/583)) +- :white_check_mark: add cache test ([#576](https://github.com/vdaas/vald/pull/576)) +- :white_check_mark: add internal/cache/gache/option test ([#575](https://github.com/vdaas/vald/pull/575)) +- :bug: :white_check_mark: fix: fails test ([#578](https://github.com/vdaas/vald/pull/578)) +- :white_check_mark: Add test for `internal/file/watch` ([#526](https://github.com/vdaas/vald/pull/526)) +- :art: update k8s manifests only on publish tags ([#574](https://github.com/vdaas/vald/pull/574)) +- :robot: Automatically update k8s manifests ([#572](https://github.com/vdaas/vald/pull/572)) +- :robot: Automatically update k8s manifests ([#571](https://github.com/vdaas/vald/pull/571)) +- :robot: Automatically update PULL_REQUEST_TEMPLATE and ISSUE_TEMPLATE ([#570](https://github.com/vdaas/vald/pull/570)) + +## v0.0.45 + +### Docker images + +| component | docker pull | +| ------------------------ | --------------------------------------------------------- | +| agent NGT | `docker pull vdaas/vald-agent-ngt:v0.0.45` | +| agent sidecar | `docker pull vdaas/vald-agent-sidecar:v0.0.45` | +| discoverer K8s | `docker pull vdaas/vald-discoverer-k8s:v0.0.45` | +| gateway | `docker pull vdaas/vald-gateway:v0.0.45` | +| backup manager MySQL | `docker pull vdaas/vald-manager-backup-mysql:v0.0.45` | +| backup manager Cassandra | `docker pull vdaas/vald-manager-backup-cassandra:v0.0.45` | +| compressor | `docker pull vdaas/vald-manager-compressor:v0.0.45` | +| meta Redis | `docker pull vdaas/vald-meta-redis:v0.0.45` | +| meta Cassandra | `docker pull vdaas/vald-meta-cassandra:v0.0.45` | +| index manager | `docker pull vdaas/vald-manager-index:v0.0.45` | +| Helm operator | `docker pull vdaas/vald-helm-operator:v0.0.45` | + +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.45) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.45/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.45/charts/operator/helm/README.md) + +### Changes + +- bugfix gateway & internal/net/grpc ([#569](https://github.com/vdaas/vald/pull/569)) +- fix update-k8s workflow & update sample manifests ([#567](https://github.com/vdaas/vald/pull/567)) +- :white_check_mark: Add test for `internal/config/mysql.go` ([#563](https://github.com/vdaas/vald/pull/563)) +- :bug: :white_check_mark: fix failed test ([#561](https://github.com/vdaas/vald/pull/561)) +- :white_check_mark: internal/tls test ([#485](https://github.com/vdaas/vald/pull/485)) +- pass tparse by tee command ([#562](https://github.com/vdaas/vald/pull/562)) +- fix global cache ([#560](https://github.com/vdaas/vald/pull/560)) +- :white_check_mark: add internal/config/ngt test ([#554](https://github.com/vdaas/vald/pull/554)) +- :white_check_mark: internal/cache/cacher test ([#553](https://github.com/vdaas/vald/pull/553)) +- :white_check_mark: Add test case for `internal/file` ([#550](https://github.com/vdaas/vald/pull/550)) +- :white_check_mark: add internal/sync/singleflight test ([#542](https://github.com/vdaas/vald/pull/542)) +- not to force rebuild gotests ([#548](https://github.com/vdaas/vald/pull/548)) +- :pencil: Add use case document ([#482](https://github.com/vdaas/vald/pull/482)) +- :white_check_mark: add internal/log/mock/retry test ([#549](https://github.com/vdaas/vald/pull/549)) +- feat: options test ([#518](https://github.com/vdaas/vald/pull/518)) +- :white_check_mark: add log/mock/logger test ([#538](https://github.com/vdaas/vald/pull/538)) +- :bug: Fix condition check of chatops ([#544](https://github.com/vdaas/vald/pull/544)) +- exclude hack codes ([#543](https://github.com/vdaas/vald/pull/543)) + +## v0.0.44 + +### Docker images + +| component | docker pull | +| ------------------------ | --------------------------------------------------------- | +| agent NGT | `docker pull vdaas/vald-agent-ngt:v0.0.44` | +| agent sidecar | `docker pull vdaas/vald-agent-sidecar:v0.0.44` | +| discoverer K8s | `docker pull vdaas/vald-discoverer-k8s:v0.0.44` | +| gateway | `docker pull vdaas/vald-gateway:v0.0.44` | +| backup manager MySQL | `docker pull vdaas/vald-manager-backup-mysql:v0.0.44` | +| backup manager Cassandra | `docker pull vdaas/vald-manager-backup-cassandra:v0.0.44` | +| compressor | `docker pull vdaas/vald-manager-compressor:v0.0.44` | +| meta Redis | `docker pull vdaas/vald-meta-redis:v0.0.44` | +| meta Cassandra | `docker pull vdaas/vald-meta-cassandra:v0.0.44` | +| index manager | `docker pull vdaas/vald-manager-index:v0.0.44` | +| Helm operator | `docker pull vdaas/vald-helm-operator:v0.0.44` | + +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.44) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.44/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.44/charts/operator/helm/README.md) + +### Changes + +- use Len and InsertVCacheLen method for IndexInfo / add mutex for (Create|Save)Index ([#536](https://github.com/vdaas/vald/pull/536)) +- documentation: tutorial/agent-on-docker ([#516](https://github.com/vdaas/vald/pull/516)) +- Revise log messages along with the coding guideline ([#504](https://github.com/vdaas/vald/pull/504)) +- :art: Add images of usecase ([#537](https://github.com/vdaas/vald/pull/537)) +- :white_check_mark: add internal/config/tls test ([#534](https://github.com/vdaas/vald/pull/534)) +- :bug: Add cancel hook for file watcher ([#535](https://github.com/vdaas/vald/pull/535)) +- :green_heart: Add test workflow ([#531](https://github.com/vdaas/vald/pull/531)) +- added internal/config/log test ([#530](https://github.com/vdaas/vald/pull/530)) +- add codeql config ([#532](https://github.com/vdaas/vald/pull/532)) +- Added test case for `internal/info` pacakge. ([#514](https://github.com/vdaas/vald/pull/514)) +- Add `internal/runner` test ([#505](https://github.com/vdaas/vald/pull/505)) +- Added test case for `internal/unit` pacakge ([#515](https://github.com/vdaas/vald/pull/515)) + +## v0.0.43 + +### Docker images + +| component | docker pull | +| ------------------------ | --------------------------------------------------------- | +| agent NGT | `docker pull vdaas/vald-agent-ngt:v0.0.43` | +| agent sidecar | `docker pull vdaas/vald-agent-sidecar:v0.0.43` | +| discoverer K8s | `docker pull vdaas/vald-discoverer-k8s:v0.0.43` | +| gateway | `docker pull vdaas/vald-gateway:v0.0.43` | +| backup manager MySQL | `docker pull vdaas/vald-manager-backup-mysql:v0.0.43` | +| backup manager Cassandra | `docker pull vdaas/vald-manager-backup-cassandra:v0.0.43` | +| compressor | `docker pull vdaas/vald-manager-compressor:v0.0.43` | +| meta Redis | `docker pull vdaas/vald-meta-redis:v0.0.43` | +| meta Cassandra | `docker pull vdaas/vald-meta-cassandra:v0.0.43` | +| index manager | `docker pull vdaas/vald-manager-index:v0.0.43` | +| Helm operator | `docker pull vdaas/vald-helm-operator:v0.0.43` | + +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.43) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.43/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.43/charts/operator/helm/README.md) + +### Changes + +- Revise S3 reader/writer: compatible with IBM Cloud Object Storage ([#509](https://github.com/vdaas/vald/pull/509)) +- :bug: Close [#502](https://github.com/vdaas/vald/pull/502) / Fix roundtrip error handling (#508) +- Feature/drawio ([#500](https://github.com/vdaas/vald/pull/500)) +- Added test case for `internal/errorgroup` ([#494](https://github.com/vdaas/vald/pull/494)) +- Update Helm Chart info ([#496](https://github.com/vdaas/vald/pull/496)) +- Revise triggers of workflow run & Fix reading changelogs from PR comments ([#495](https://github.com/vdaas/vald/pull/495)) + +## v0.0.42 + +### Docker images + +| component | docker pull | +| ------------------------ | --------------------------------------------------------- | +| agent NGT | `docker pull vdaas/vald-agent-ngt:v0.0.42` | +| agent sidecar | `docker pull vdaas/vald-agent-sidecar:v0.0.42` | +| discoverer K8s | `docker pull vdaas/vald-discoverer-k8s:v0.0.42` | +| gateway | `docker pull vdaas/vald-gateway:v0.0.42` | +| backup manager MySQL | `docker pull vdaas/vald-manager-backup-mysql:v0.0.42` | +| backup manager Cassandra | `docker pull vdaas/vald-manager-backup-cassandra:v0.0.42` | +| compressor | `docker pull vdaas/vald-manager-compressor:v0.0.42` | +| meta Redis | `docker pull vdaas/vald-meta-redis:v0.0.42` | +| meta Cassandra | `docker pull vdaas/vald-meta-cassandra:v0.0.42` | +| index manager | `docker pull vdaas/vald-manager-index:v0.0.42` | +| Helm operator | `docker pull vdaas/vald-helm-operator:v0.0.42` | + +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.42) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.42/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.42/charts/operator/helm/README.md) + +### Changes + +- ✨ Add Stackdriver Monitoring, Tracing and Profiler support ([#479](https://github.com/vdaas/vald/pull/479)) +- :green_heart: Add CodeQL workflow instead of LGTM.com ([#486](https://github.com/vdaas/vald/pull/486)) +- Add `internal/params` pacakge test ([#474](https://github.com/vdaas/vald/pull/474)) +- :sparkles: aws region can be specified with empty string ([#477](https://github.com/vdaas/vald/pull/477)) +- Fix failed test case of internal/safety package ([#464](https://github.com/vdaas/vald/pull/464)) +- send a request to GoProxy after a new version is published ([#475](https://github.com/vdaas/vald/pull/475)) +- internal/db/storage/blob/s3: remove ctx from struct ([#473](https://github.com/vdaas/vald/pull/473)) + +## v0.0.41 + +### Docker images + +| component | docker pull | +| ------------------------ | --------------------------------------------------------- | +| agent NGT | `docker pull vdaas/vald-agent-ngt:v0.0.41` | +| agent sidecar | `docker pull vdaas/vald-agent-sidecar:v0.0.41` | +| discoverer K8s | `docker pull vdaas/vald-discoverer-k8s:v0.0.41` | +| gateway | `docker pull vdaas/vald-gateway:v0.0.41` | +| backup manager MySQL | `docker pull vdaas/vald-manager-backup-mysql:v0.0.41` | +| backup manager Cassandra | `docker pull vdaas/vald-manager-backup-cassandra:v0.0.41` | +| compressor | `docker pull vdaas/vald-manager-compressor:v0.0.41` | +| meta Redis | `docker pull vdaas/vald-meta-redis:v0.0.41` | +| meta Cassandra | `docker pull vdaas/vald-meta-cassandra:v0.0.41` | +| index manager | `docker pull vdaas/vald-manager-index:v0.0.41` | +| Helm operator | `docker pull vdaas/vald-helm-operator:v0.0.41` | + +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.41) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.41/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.41/charts/operator/helm/README.md) + +### Changes + +- Refactor agent-sidecar: fix S3 reader & add backoff logic ([#467](https://github.com/vdaas/vald/pull/467)) +- :bug: :pencil: fix link ([#471](https://github.com/vdaas/vald/pull/471)) +- Fix /changelog command format ([#470](https://github.com/vdaas/vald/pull/470)) +- fix: failing test ([#469](https://github.com/vdaas/vald/pull/469)) +- fix: failing test ([#468](https://github.com/vdaas/vald/pull/468)) +- ✨ Add options for AWS client ([#460](https://github.com/vdaas/vald/pull/460)) +- Fix /format command ([#466](https://github.com/vdaas/vald/pull/466)) +- Fix /format command ([#465](https://github.com/vdaas/vald/pull/465)) +- Fix `internal/log/retry` pacakge ([#458](https://github.com/vdaas/vald/pull/458)) +- [ImgBot] Optimize images ([#461](https://github.com/vdaas/vald/pull/461)) +- :art: trim white margin at data flow images ([#459](https://github.com/vdaas/vald/pull/459)) + +## v0.0.40 + +### Docker images + +| component | docker pull | +| ------------------------ | --------------------------------------------------------- | +| agent NGT | `docker pull vdaas/vald-agent-ngt:v0.0.40` | +| agent sidecar | `docker pull vdaas/vald-agent-sidecar:v0.0.40` | +| discoverer K8s | `docker pull vdaas/vald-discoverer-k8s:v0.0.40` | +| gateway | `docker pull vdaas/vald-gateway:v0.0.40` | +| backup manager MySQL | `docker pull vdaas/vald-manager-backup-mysql:v0.0.40` | +| backup manager Cassandra | `docker pull vdaas/vald-manager-backup-cassandra:v0.0.40` | +| compressor | `docker pull vdaas/vald-manager-compressor:v0.0.40` | +| meta Redis | `docker pull vdaas/vald-meta-redis:v0.0.40` | +| meta Cassandra | `docker pull vdaas/vald-meta-cassandra:v0.0.40` | +| index manager | `docker pull vdaas/vald-manager-index:v0.0.40` | +| Helm operator | `docker pull vdaas/vald-helm-operator:v0.0.40` | + +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.40) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.40/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.40/charts/operator/helm/README.md) + +### Changes + +- Documentation: add concept section to the architecture document ([#438](https://github.com/vdaas/vald/pull/438)) +- feat: level pacakge test ([#455](https://github.com/vdaas/vald/pull/455)) +- [ImgBot] Optimize images ([#457](https://github.com/vdaas/vald/pull/457)) +- fix document and added png images ([#456](https://github.com/vdaas/vald/pull/456)) +- Fix test template bug ([#452](https://github.com/vdaas/vald/pull/452)) +- Add WithOperation func to loadtest usecase ([#454](https://github.com/vdaas/vald/pull/454)) +- :bug: Fix k8s manifests for loadtest jobs ([#453](https://github.com/vdaas/vald/pull/453)) +- bugfix change final stage of loadtest ([#451](https://github.com/vdaas/vald/pull/451)) +- :bug: Fix bug on /changelog command ([#450](https://github.com/vdaas/vald/pull/450)) +- add loadtest job and container ([#449](https://github.com/vdaas/vald/pull/449)) +- 🐛 Fix bug on changelog command ([#448](https://github.com/vdaas/vald/pull/448)) + +## v0.0.39 + +### Docker images + +| component | docker pull | +| ------------------------ | --------------------------------------------------------- | +| agent NGT | `docker pull vdaas/vald-agent-ngt:v0.0.39` | +| agent sidecar | `docker pull vdaas/vald-agent-sidecar:v0.0.39` | +| discoverer K8s | `docker pull vdaas/vald-discoverer-k8s:v0.0.39` | +| gateway | `docker pull vdaas/vald-gateway:v0.0.39` | +| backup manager MySQL | `docker pull vdaas/vald-manager-backup-mysql:v0.0.39` | +| backup manager Cassandra | `docker pull vdaas/vald-manager-backup-cassandra:v0.0.39` | +| compressor | `docker pull vdaas/vald-manager-compressor:v0.0.39` | +| meta Redis | `docker pull vdaas/vald-meta-redis:v0.0.39` | +| meta Cassandra | `docker pull vdaas/vald-meta-cassandra:v0.0.39` | +| index manager | `docker pull vdaas/vald-manager-index:v0.0.39` | +| Helm operator | `docker pull vdaas/vald-helm-operator:v0.0.39` | + +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.39) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.39/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.39/charts/operator/helm/README.md) + +### Changes + +- [patch] fix doc file path ([#444](https://github.com/vdaas/vald/pull/444)) +- Add changelog command (ChatOps) ([#447](https://github.com/vdaas/vald/pull/447)) +- Fix inconsistent wording ([#442](https://github.com/vdaas/vald/pull/442)) +- [ImgBot] Optimize images ([#443](https://github.com/vdaas/vald/pull/443)) +- [Document] Apply design template to flow diagram ([#441](https://github.com/vdaas/vald/pull/441)) +- Document to deploy standalone agent ([#407](https://github.com/vdaas/vald/pull/407)) +- implement load tester prototype ([#363](https://github.com/vdaas/vald/pull/363)) +- 🐛 Add gRPC interceptor to recover panic in handlers ([#440](https://github.com/vdaas/vald/pull/440)) +- tensorflow test ([#378](https://github.com/vdaas/vald/pull/378)) +- :bento: update architecture overview svg to add agent sidecar ([#437](https://github.com/vdaas/vald/pull/437)) +- Example program: Add indexing interval description & fix logging message ([#405](https://github.com/vdaas/vald/pull/405)) +- :pencil2: Fix typo ([#436](https://github.com/vdaas/vald/pull/436)) + +## v0.0.38 + +### Docker images + +| component | docker pull | +| ------------------------ | --------------------------------------------------------- | +| agent NGT | `docker pull vdaas/vald-agent-ngt:v0.0.38` | +| agent sidecar | `docker pull vdaas/vald-agent-sidecar:v0.0.38` | +| discoverer K8s | `docker pull vdaas/vald-discoverer-k8s:v0.0.38` | +| gateway | `docker pull vdaas/vald-gateway:v0.0.38` | +| backup manager MySQL | `docker pull vdaas/vald-manager-backup-mysql:v0.0.38` | +| backup manager Cassandra | `docker pull vdaas/vald-manager-backup-cassandra:v0.0.38` | +| compressor | `docker pull vdaas/vald-manager-compressor:v0.0.38` | +| meta Redis | `docker pull vdaas/vald-meta-redis:v0.0.38` | +| meta Cassandra | `docker pull vdaas/vald-meta-cassandra:v0.0.38` | +| index manager | `docker pull vdaas/vald-manager-index:v0.0.38` | +| Helm operator | `docker pull vdaas/vald-helm-operator:v0.0.38` | + +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.38) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.38/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.38/charts/operator/helm/README.md) + +### Changes + +- send PR when K8s manifests are updated ([#435](https://github.com/vdaas/vald/pull/435)) +- Implementation of agent-sidecar storage backup logic ([#409](https://github.com/vdaas/vald/pull/409)) +- Fix structure of grpc java package ([#431](https://github.com/vdaas/vald/pull/431)) +- Remove AUTHORS/CONTRIBUTORS file ([#428](https://github.com/vdaas/vald/pull/428)) +- Contribute document ([#390](https://github.com/vdaas/vald/pull/390)) +- Separate the component section from architecture doc ([#430](https://github.com/vdaas/vald/pull/430)) +- fix: delete fch channel because fch causes channel blocking ([#429](https://github.com/vdaas/vald/pull/429)) +- Update Operator SDK version ([#412](https://github.com/vdaas/vald/pull/412)) +- Documentation: About vald ([#374](https://github.com/vdaas/vald/pull/374)) +- Vald architecture document ([#366](https://github.com/vdaas/vald/pull/366)) +- Add JSON schema for Vald Helm Chart ([#365](https://github.com/vdaas/vald/pull/365)) +- Revise ChatOps not to add go.mod & go.sum when /format runs ([#406](https://github.com/vdaas/vald/pull/406)) +- add agent sidecar flame for implementation ([#404](https://github.com/vdaas/vald/pull/404)) +- Upgrade Operator SDK version / Remove useless GO111MODULE=off ([#402](https://github.com/vdaas/vald/pull/402)) +- Upgrade tools version ([#399](https://github.com/vdaas/vald/pull/399)) +- Add app.kubernetes.io/xxx labels to all resources ([#397](https://github.com/vdaas/vald/pull/397)) +- Vald contacts document ([#373](https://github.com/vdaas/vald/pull/373)) +- add trace spans and metrics for agent-ngt and index-manager ([#389](https://github.com/vdaas/vald/pull/389)) +- Add gen-test command for chatops ([#379](https://github.com/vdaas/vald/pull/379)) +- Add internal/db/storage/blob ([#388](https://github.com/vdaas/vald/pull/388)) + +## v0.0.37 + +### Docker images + +| component | docker pull | +| ------------------------ | --------------------------------------------------------- | +| agent NGT | `docker pull vdaas/vald-agent-ngt:v0.0.37` | +| discoverer K8s | `docker pull vdaas/vald-discoverer-k8s:v0.0.37` | +| gateway | `docker pull vdaas/vald-gateway:v0.0.37` | +| backup manager MySQL | `docker pull vdaas/vald-manager-backup-mysql:v0.0.37` | +| backup manager Cassandra | `docker pull vdaas/vald-manager-backup-cassandra:v0.0.37` | +| compressor | `docker pull vdaas/vald-manager-compressor:v0.0.37` | +| meta Redis | `docker pull vdaas/vald-meta-redis:v0.0.37` | +| meta Cassandra | `docker pull vdaas/vald-meta-cassandra:v0.0.37` | +| index manager | `docker pull vdaas/vald-manager-index:v0.0.37` | +| Helm operator | `docker pull vdaas/vald-helm-operator:v0.0.37` | + +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.37) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.37/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.37/charts/operator/helm/README.md) + +### Changes + +- add agent auto save indexing feature ([#385](https://github.com/vdaas/vald/pull/385)) +- :bug: fix ngt `distance_type` ([#384](https://github.com/vdaas/vald/pull/384)) +- Add topology spread constraints ([#383](https://github.com/vdaas/vald/pull/383)) + +## v0.0.36 + +### Docker images + +| component | docker pull | +| ------------------------ | --------------------------------------------------------- | +| agent NGT | `docker pull vdaas/vald-agent-ngt:v0.0.36` | +| discoverer K8s | `docker pull vdaas/vald-discoverer-k8s:v0.0.36` | +| gateway | `docker pull vdaas/vald-gateway:v0.0.36` | +| backup manager MySQL | `docker pull vdaas/vald-manager-backup-mysql:v0.0.36` | +| backup manager Cassandra | `docker pull vdaas/vald-manager-backup-cassandra:v0.0.36` | +| compressor | `docker pull vdaas/vald-manager-compressor:v0.0.36` | +| meta Redis | `docker pull vdaas/vald-meta-redis:v0.0.36` | +| meta Cassandra | `docker pull vdaas/vald-meta-cassandra:v0.0.36` | +| index manager | `docker pull vdaas/vald-manager-index:v0.0.36` | +| Helm operator | `docker pull vdaas/vald-helm-operator:v0.0.36` | + +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.36) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.36/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.36/charts/operator/helm/README.md) + +### Changes + +- update dependencies version ([#381](https://github.com/vdaas/vald/pull/381)) +- Fix missing value on compressor health servers ([#377](https://github.com/vdaas/vald/pull/377)) +- Fix compressor readiness shutdown_duration / Fix cassandra … ([#376](https://github.com/vdaas/vald/pull/376)) +- Bump sigs.k8s.io/yaml from 2.2.8 to 2.3.0 ([#375](https://github.com/vdaas/vald/pull/375)) +- Fix`internal/log/format` to match the test template ([#369](https://github.com/vdaas/vald/pull/369)) +- Fix `internal/log/logger` to match the test template ([#371](https://github.com/vdaas/vald/pull/371)) +- Fix failing tests of `internal/log` and modified to match the test template ([#368](https://github.com/vdaas/vald/pull/368)) +- Add enabled flag to each component in Helm chart ([#372](https://github.com/vdaas/vald/pull/372)) +- Add configurations.md ([#356](https://github.com/vdaas/vald/pull/356)) + +## v0.0.35 + +### Docker images + +| component | docker pull | +| ------------------------ | --------------------------------------------------------- | +| agent NGT | `docker pull vdaas/vald-agent-ngt:v0.0.35` | +| discoverer K8s | `docker pull vdaas/vald-discoverer-k8s:v0.0.35` | +| gateway | `docker pull vdaas/vald-gateway:v0.0.35` | +| backup manager MySQL | `docker pull vdaas/vald-manager-backup-mysql:v0.0.35` | +| backup manager Cassandra | `docker pull vdaas/vald-manager-backup-cassandra:v0.0.35` | +| compressor | `docker pull vdaas/vald-manager-compressor:v0.0.35` | +| meta Redis | `docker pull vdaas/vald-meta-redis:v0.0.35` | +| meta Cassandra | `docker pull vdaas/vald-meta-cassandra:v0.0.35` | +| index manager | `docker pull vdaas/vald-manager-index:v0.0.35` | +| Helm operator | `docker pull vdaas/vald-helm-operator:v0.0.35` | + +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.35) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.35/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.35/charts/operator/helm/README.md) + +### Changes + +- add storage backup option to agent ([#367](https://github.com/vdaas/vald/pull/367)) +- Add client-node dispatcher ([#370](https://github.com/vdaas/vald/pull/370)) +- Bump github.com/tensorflow/tensorflow ([#364](https://github.com/vdaas/vald/pull/364)) +- change fmt.Errorf to errors.Errorf ([#361](https://github.com/vdaas/vald/pull/361)) +- add goleak ([#359](https://github.com/vdaas/vald/pull/359)) + +## v0.0.34 + +### Docker images + +| component | docker pull | +| ------------------------ | --------------------------------------------------------- | +| agent NGT | `docker pull vdaas/vald-agent-ngt:v0.0.34` | +| discoverer K8s | `docker pull vdaas/vald-discoverer-k8s:v0.0.34` | +| gateway | `docker pull vdaas/vald-gateway:v0.0.34` | +| backup manager MySQL | `docker pull vdaas/vald-manager-backup-mysql:v0.0.34` | +| backup manager Cassandra | `docker pull vdaas/vald-manager-backup-cassandra:v0.0.34` | +| compressor | `docker pull vdaas/vald-manager-compressor:v0.0.34` | +| meta Redis | `docker pull vdaas/vald-meta-redis:v0.0.34` | +| meta Cassandra | `docker pull vdaas/vald-meta-cassandra:v0.0.34` | +| index manager | `docker pull vdaas/vald-manager-index:v0.0.34` | +| Helm operator | `docker pull vdaas/vald-helm-operator:v0.0.34` | + +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.34) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.34/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.34/charts/operator/helm/README.md) + +### Changes + +- feature/internal/cassandra/add option ([#358](https://github.com/vdaas/vald/pull/358)) +- update helm docs when version is published ([#355](https://github.com/vdaas/vald/pull/355)) +- upgrade tools ([#354](https://github.com/vdaas/vald/pull/354)) +- bugfix protoc-gen-validate resolve failure ([#353](https://github.com/vdaas/vald/pull/353)) +- Fix conflicts between formatter and helm template ([#350](https://github.com/vdaas/vald/pull/350)) +- Add more options and remove valdhelmoperatorrelease, valdrelease from vald-helm-operator chart ([#334](https://github.com/vdaas/vald/pull/334)) + +## v0.0.33 + +### Docker images + +| component | docker pull | +| ------------------------ | --------------------------------------------------------- | +| agent NGT | `docker pull vdaas/vald-agent-ngt:v0.0.33` | +| discoverer K8s | `docker pull vdaas/vald-discoverer-k8s:v0.0.33` | +| gateway | `docker pull vdaas/vald-gateway:v0.0.33` | +| backup manager MySQL | `docker pull vdaas/vald-manager-backup-mysql:v0.0.33` | +| backup manager Cassandra | `docker pull vdaas/vald-manager-backup-cassandra:v0.0.33` | +| compressor | `docker pull vdaas/vald-manager-compressor:v0.0.33` | +| meta Redis | `docker pull vdaas/vald-meta-redis:v0.0.33` | +| meta Cassandra | `docker pull vdaas/vald-meta-cassandra:v0.0.33` | +| index manager | `docker pull vdaas/vald-manager-index:v0.0.33` | +| Helm operator | `docker pull vdaas/vald-helm-operator:v0.0.33` | + +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.33) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.33/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.33/charts/operator/helm/README.md) + +### Changes + +- update k8s dependencies ([#349](https://github.com/vdaas/vald/pull/349)) +- create missing test files by the our original test template ([#348](https://github.com/vdaas/vald/pull/348)) +- create test template for using gotests ([#327](https://github.com/vdaas/vald/pull/327)) +- Revise coverage CI settings ([#347](https://github.com/vdaas/vald/pull/347)) +- fix tensorflow.go, option.go ([#261](https://github.com/vdaas/vald/pull/261)) + +## v0.0.32 + +### Docker images + +| component | docker pull | +| ------------------------ | --------------------------------------------------------- | +| agent NGT | `docker pull vdaas/vald-agent-ngt:v0.0.32` | +| discoverer K8s | `docker pull vdaas/vald-discoverer-k8s:v0.0.32` | +| gateway | `docker pull vdaas/vald-gateway:v0.0.32` | +| backup manager MySQL | `docker pull vdaas/vald-manager-backup-mysql:v0.0.32` | +| backup manager Cassandra | `docker pull vdaas/vald-manager-backup-cassandra:v0.0.32` | +| compressor | `docker pull vdaas/vald-manager-compressor:v0.0.32` | +| meta Redis | `docker pull vdaas/vald-meta-redis:v0.0.32` | +| meta Cassandra | `docker pull vdaas/vald-meta-cassandra:v0.0.32` | +| index manager | `docker pull vdaas/vald-manager-index:v0.0.32` | +| Helm operator | `docker pull vdaas/vald-helm-operator:v0.0.32` | + +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.32) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.32/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.32/charts/operator/helm/README.md) + +### Changes + +- bugfix ip discoverer disconnection too slow ([#344](https://github.com/vdaas/vald/pull/344)) +- Compressor: backup vectors in queue using PostStop function ([#345](https://github.com/vdaas/vald/pull/345)) +- Revise backup/meta Cassandra default values ([#336](https://github.com/vdaas/vald/pull/336)) + +## v0.0.31 + +### Docker images + +| component | docker pull | +| ------------------------ | --------------------------------------------------------- | +| agent NGT | `docker pull vdaas/vald-agent-ngt:v0.0.31` | +| discoverer K8s | `docker pull vdaas/vald-discoverer-k8s:v0.0.31` | +| gateway | `docker pull vdaas/vald-gateway:v0.0.31` | +| backup manager MySQL | `docker pull vdaas/vald-manager-backup-mysql:v0.0.31` | +| backup manager Cassandra | `docker pull vdaas/vald-manager-backup-cassandra:v0.0.31` | +| compressor | `docker pull vdaas/vald-manager-compressor:v0.0.31` | +| meta Redis | `docker pull vdaas/vald-meta-redis:v0.0.31` | +| meta Cassandra | `docker pull vdaas/vald-meta-cassandra:v0.0.31` | +| index manager | `docker pull vdaas/vald-manager-index:v0.0.31` | +| Helm operator | `docker pull vdaas/vald-helm-operator:v0.0.31` | + +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.31) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.31/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.31/charts/operator/helm/README.md) + +### Changes + +- Resolve busy-loop on worker ([#339](https://github.com/vdaas/vald/pull/339)) + +## v0.0.30 + +### Docker images + +| component | docker pull | +| ------------------------ | --------------------------------------------------------- | +| agent NGT | `docker pull vdaas/vald-agent-ngt:v0.0.30` | +| discoverer K8s | `docker pull vdaas/vald-discoverer-k8s:v0.0.30` | +| gateway | `docker pull vdaas/vald-gateway:v0.0.30` | +| backup manager MySQL | `docker pull vdaas/vald-manager-backup-mysql:v0.0.30` | +| backup manager Cassandra | `docker pull vdaas/vald-manager-backup-cassandra:v0.0.30` | +| compressor | `docker pull vdaas/vald-manager-compressor:v0.0.30` | +| meta Redis | `docker pull vdaas/vald-meta-redis:v0.0.30` | +| meta Cassandra | `docker pull vdaas/vald-meta-cassandra:v0.0.30` | +| index manager | `docker pull vdaas/vald-manager-index:v0.0.30` | +| Helm operator | `docker pull vdaas/vald-helm-operator:v0.0.30` | + +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.30) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.30/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.30/charts/operator/helm/README.md) + +### Changes + +- async compressor +- optimized gRPC connection pool +- update helm chart API version +- internal gRPC client for Vald +- Cassandra NewConvictionPolicy +- dicoverer now returns clone object +- new internal/sync/singleflight package +- new internal/net package +- coding guideline + +## v0.0.26 + +### Docker images + +| component | docker pull | +| ------------------------ | --------------------------------------------------------- | +| agent NGT | `docker pull vdaas/vald-agent-ngt:v0.0.26` | +| discoverer K8s | `docker pull vdaas/vald-discoverer-k8s:v0.0.26` | +| gateway | `docker pull vdaas/vald-gateway:v0.0.26` | +| backup manager MySQL | `docker pull vdaas/vald-manager-backup-mysql:v0.0.26` | +| backup manager Cassandra | `docker pull vdaas/vald-manager-backup-cassandra:v0.0.26` | +| compressor | `docker pull vdaas/vald-manager-compressor:v0.0.26` | +| meta Redis | `docker pull vdaas/vald-meta-redis:v0.0.26` | +| meta Cassandra | `docker pull vdaas/vald-meta-cassandra:v0.0.26` | +| index manager | `docker pull vdaas/vald-manager-index:v0.0.26` | +| Helm operator | `docker pull vdaas/vald-helm-operator:v0.0.26` | + +### Documents + +- [GoDoc](https://pkg.go.dev/github.com/vdaas/vald@v0.0.26) +- [Helm Chart Reference](https://github.com/vdaas/vald/blob/v0.0.26/charts/vald/README.md) +- [Helm Operator Chart Reference](https://github.com/vdaas/vald/blob/v0.0.26/charts/operator/helm/README.md) + +### Changes + +- added helm operator +- added telepresence +- improved meta-Cassandra performance +- added doc users/get-started +- fixed some bugs diff --git a/content/docs/v1.8/support/FAQ.md b/content/docs/v1.8/support/FAQ.md new file mode 100644 index 000000000..d1f41229a --- /dev/null +++ b/content/docs/v1.8/support/FAQ.md @@ -0,0 +1,96 @@ +--- +title: "FAQ_v1.8/Support" +date: 2026-09-16T04:40:19Z +draft: false +weight: 0 +description: "" +menu: + support: + parent: Support +--- + +# FAQ + +This page shows the answers to the common questions. + +Please refer to [the troubleshooting page](/docs/v1.8/user-guides/troubleshooting) when you encounter operation problems. + +## Component + +### What is the ingress filter? + +The ingress filter is the user-defined component that connects to the `vald-filter-gateway` component. +For example, you can convert object data (e.g., image binary, text, etc.) to the vector using your ML models as pre-process of each request. + +### What is the egress filter? + +The egress filter is the user-defined component that connects to the vald-filter-gateway component. +You can use it for filtering search results, e.g., when you'd like to get a white T-shirt list, use it to remove other colors of T-shirts from search results that the vald-lb-gateway component returns. + +### Is Vald Index Manager recommended using? + +We recommend using it when you’d like to operate as a cluster. +It helps you to manage indexing timing for each Vald Agent. + +## Custom options + +### What are the pluggable options? + +Vald has three pluggable options: + +1. Backup with the external storage for Vald Agent + - You can connect the external storage, like S3, GCS, etc., to the Vald Agent Sidecar component for backup. +1. Algorithm of the core engine for Vald Agent + - We're going to add another algorithm in the near future. +1. Filtering with filter gateway + - You can filter the search results by your own defined filter component by connecting to the filter gateway before returning the search result. + - You can convert object data to vector by own defined ingress filter component by connection to filter gateway before inserting. + +## Deployment + +### How to deploy the Vald cluster? + +We recommend using [Helm](https://helm.sh/) for deploying the Vald cluster. +You can deploy by following the steps. + +1. Install Helm(v3~) and prepare the Kubernetes cluster +1. Configure `Helm chart` as your demand +1. Deploy by Helm command + +## API + +### Is there any support for bulk inserts? + +Vald provides `MultiInsert` and `StreamInsert` for bulk insert. +Please refer to [the insert API documentation](/docs/v1.8/api/insert) for more detail. + +Vald also provides `MultiXXX` and `StreamXXX` as bulk operations for each service. +For more detail, please refer to [the API document overview](https://vald.vdaas.org/docs/api/). + +## Data + +### Can Vald handle multi-embedding vectors? + +Unfortunately, the current Vald cannot directly handle multi-embedding spaces with a single Vald cluster. +You have to choose one of two options to use the multi-embedding vectors in Vald. + +1. Deploy multiple Vald cluster +1. Covert vector to new vector in the specific space by some methods + +### How to backup index data? + +There are three ways to backup index data: + +1. Using external storage (S3, GCS) +1. Using Persistent Volume +1. Using the external storage and Persistent Volume + + + +Please refer to [the sample configurations](https://github.com/vdaas/vald/tree/main/charts/vald/values). + +--- + +## Related Document + +- [Troubleshooting](/docs/v1.8/user-guides/troubleshooting) diff --git a/content/docs/v1.8/support/_index.md b/content/docs/v1.8/support/_index.md new file mode 100644 index 000000000..2a8da5f4f --- /dev/null +++ b/content/docs/v1.8/support/_index.md @@ -0,0 +1,7 @@ +--- +title: "Support_v1.8/Support" +date: 2026-09-16T04:40:22Z +draft: false +weight: 0 +--- + diff --git a/content/docs/v1.8/support/contacts.md b/content/docs/v1.8/support/contacts.md new file mode 100644 index 000000000..d51697123 --- /dev/null +++ b/content/docs/v1.8/support/contacts.md @@ -0,0 +1,26 @@ +--- +title: "Contacts_v1.8/Support" +date: 2026-09-16T04:40:19Z +draft: false +weight: 100 +description: Please contact us +menu: + support: + parent: Support +--- + +# Contact Us + +Welcome to the Vald project!! + +Vald is an open source project and we would like to hear from you. + +If you are interested in Vald, or you have any questions about Vald, please feel free to contact us. + +We recommend communicating on slack, but using E-mail is also welcome. + +Slack: [Vald Community](https://join.slack.com/t/vald-community/shared_invite/zt-db2ky9o4-R_9p2sVp8xRwztVa8gfnPA) + +E-mail: [vald@vdaas.org](mailto:vald@vdaas.org) + +We are waiting for your contact. diff --git a/content/docs/v1.8/troubleshooting/_index.md b/content/docs/v1.8/troubleshooting/_index.md new file mode 100644 index 000000000..e2e1b0e1d --- /dev/null +++ b/content/docs/v1.8/troubleshooting/_index.md @@ -0,0 +1,23 @@ +--- +title: "Troubleshooting_v1.8/Troubleshooting" +date: 2026-09-16T04:40:18Z +draft: false +weight: 0 +--- + +# Troubleshooting + +When you have any problems with the Vald cluster, e.g., deployment, API, etc., this page will help resolve them. + +## Flow chart + +The [flow chart](/docs/v1.8/troubleshooting/flow-chart) explains the Vald cluster troubleshooting flow for users and operators of the Vald cluster. + +It mainly targets the deployment view, but it will help when users get the error as an API response, and the error is not due to the client-side. + +## Operation troubleshooting + +The [operation troubleshooting](/docs/v1.8/troubleshooting/operation-troubleshooting) shows the popular troubleshooting for users of the Vald cluster. + +It lists the prevalent troubles when getting a response from the Vald cluster. +It would be better to refer to the [API status](/docs/v1.8/api/status) page at the same time to understand the meaning of the error code. diff --git a/content/docs/v1.8/troubleshooting/client-side.md b/content/docs/v1.8/troubleshooting/client-side.md new file mode 100644 index 000000000..344b8201d --- /dev/null +++ b/content/docs/v1.8/troubleshooting/client-side.md @@ -0,0 +1,69 @@ +--- +title: "Client Side_v1.8/Troubleshooting" +date: 2026-09-16T04:40:18Z +draft: false +weight: 100 +description: The popular troubleshooting for client side +menu: + troubleshooting: + parent: Troubleshooting +--- + +# Client Side Troubleshooting + +This page introduces the popular troubleshooting for client side. + +The [flow chart](/docs/v1.8/troubleshooting/provisioning) helps you find the root reason for your problem. + +Additionally, if you encounter some errors when using API, the [API status code](/docs/v1.8/api/status) helps you, too. + +## Insert Operation + +### Vald Agent NGT crashed at the insert process. + +Please check your container limit of memory at first. +Vald Agent requires memory for keeping indexing on memory. + +```bash +kubectl describe statefulset vald-agent-ngt +``` + +If the limit of memory exists, please remove it or update the value to more enormous. + +## Search Operation + +### Vald returns no search result. + +There are two possible reasons. + +1. Indexing has not finished in Vald Agent + + Vald will search the nearest vectors of query from the indexing in Vald Agent. + If the indexing process is running, Vald Agent returns no search result. + + It will resolve when completed indexing instructions, like `CreateIndex`. + +1. Too short timeout for searching + + When the search timeout configuration is too short, Vald LB Gateway stops the searching process before getting the search result from Vald Agent. + + In the sense of search operation, you can modify search timeout by [payload config](/docs/v1.8/api/search). + +
+ +## Others + +### Vald Agent NGT crashed when initContainer. + +Vald Agent NGT requires an AVX2 processor for running. +Please check your CPU information. + +--- + +## Related Document + +- [Provisioning Troubleshooting](/docs/v1.8/troubleshooting/provisioning) +- [API Status](/docs/v1.8/api/status) +- [FAQ](/docs/support/faq) diff --git a/content/docs/v1.8/troubleshooting/mirror-gateway.md b/content/docs/v1.8/troubleshooting/mirror-gateway.md new file mode 100644 index 000000000..217b1b140 --- /dev/null +++ b/content/docs/v1.8/troubleshooting/mirror-gateway.md @@ -0,0 +1,86 @@ +--- +title: "Mirror Gateway_v1.8/Troubleshooting" +date: 2026-09-16T04:40:18Z +draft: false +weight: 0 +description: "" +menu: + troubleshooting: + parent: Troubleshooting +--- + +# Mirror Gateway Troubleshooting + +This page introduces the popular troubleshooting for Mirror Gateway. + +Additionally, if you encounter some errors when using API, the [API status code](/docs/v1.8/api/status) helps you, too. + +## Insert Operation + +Mirror Gateway sends an Update request to its host if some requests are `ALREADY_EXISTS`. + +Therefore, in addition to the [Insert API status code](/docs/v1.8/api/insert#status-code), the [Update API status code](/docs/v1.8/api/update#status-code) may also be returned to the user. + +Here are some common reasons of error. + +| name | common reason | +| :---------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------- | +| CANCELLED | Executed cancel() of rpc from client/server-side or network problems between client and server. | +| INVALID_ARGUMENT | The Dimension of the request vector is NOT the same as Vald Agent's config, the requested vector's ID is empty, or some request payload is invalid. | +| DEADLINE_EXCEEDED | The RPC timeout setting is too short on the client/server side. | +| ALREADY_EXISTS | Request ID is already inserted. This status code is returned when all target hosts return `ALREADY_EXISTS`. | +| NOT_FOUND | Requested ID is NOT inserted. This is the status code of the Update request. | +| INTERNAL | Target Vald cluster or network route has some critical error. | + +`0 (OK)` is also returned when all target hosts return `OK` or `ALREADY_EXISTS`. + +## Update Operation + +Mirror Gateway sends an Update request to its host if some requests are `NOT_FOUND`. + +Therefore, in addition to the [Update API status code](/docs/v1.8/api/update#status-code), the [Insert API status code](/docs/v1.8/api/insert#status-code) may also be returned to the user. + +Here are some common reasons of error. + +| name | common reason | +| :---------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------- | +| CANCELLED | Executed cancel() of rpc from client/server-side or network problems between client and server. | +| INVALID_ARGUMENT | The Dimension of the request vector is NOT the same as Vald Agent's config, the requested vector's ID is empty, or some request payload is invalid. | +| DEADLINE_EXCEEDED | The RPC timeout setting is too short on the client/server side. | +| NOT_FOUND | Requested ID is NOT inserted. This status code is returned when all target hosts return `NOT_FOUND`. | +| ALREADY_EXISTS | Request a pair of ID and vector is already inserted. This status code is returned when all hosts return `ALREADY_EXISTS`. | +| INTERNAL | Target Vald cluster or network route has some critical error. | + +`0 (OK)` is also returned when all target hosts return `OK` or `ALREADY_EXISTS`. + +## Upsert Operation + +The request process may not be completed when the response code is NOT `0 (OK)`. + +Here are some common reasons of error. + +| name | common reason | +| :---------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------- | +| CANCELLED | Executed cancel() of rpc from client/server-side or network problems between client and server. | +| INVALID_ARGUMENT | The Dimension of the request vector is NOT the same as Vald Agent's config, the requested vector's ID is empty, or some request payload is invalid. | +| DEADLINE_EXCEEDED | The RPC timeout setting is too short on the client/server side. | +| ALREADY_EXISTS | Requested pair of ID and vector is already inserted. This status code is returned when all target hosts return `ALREADY_EXISTS`. | +| INTERNAL | Target Vald cluster or network route has some critical error. | + +`0 (OK)` is also returned when all target hosts return `OK` or `ALREADY_EXISTS`. + +## Remove Operation + +The request process may not be completed when the response code is NOT `0 (OK)`. + +Here are some common reasons of error. + +| name | common reason | +| :---------------- | :--------------------------------------------------------------------------------------------------- | +| CANCELLED | Executed cancel() of rpc from client/server-side or network problems between client and server. | +| INVALID_ARGUMENT | The Requested vector's ID is empty, or some request payload is invalid. | +| DEADLINE_EXCEEDED | The RPC timeout setting is too short on the client/server side. | +| NOT_FOUND | Requested ID is NOT inserted. This status code is returned when all target hosts return `NOT_FOUND`. | +| INTERNAL | Target Vald cluster or network route has some critical error. | + +`0 (OK)` is also returned when all target hosts return `OK` or `NOT_FOUND`. diff --git a/content/docs/v1.8/troubleshooting/provisioning.md b/content/docs/v1.8/troubleshooting/provisioning.md new file mode 100644 index 000000000..62179ae50 --- /dev/null +++ b/content/docs/v1.8/troubleshooting/provisioning.md @@ -0,0 +1,26 @@ +--- +title: "Provisioning_v1.8/Troubleshooting" +date: 2026-09-16T04:40:18Z +draft: false +weight: 200 +description: Shows how to resolve trouble with provisioning Vald cluster +menu: + troubleshooting: + parent: Troubleshooting +--- + +# Provisioning Troubleshooting + +This page shows the troubleshooting for provisioning the Vald cluster. + +If you encounter trouble, please confirm the below chart and try to fix it. + + + +If any components output the `INTERNAL` error log, let us know the detail. + +--- + +## Related Document + +- [Client Side Troubleshooting](/docs/v1.8/troubleshooting/client-side) diff --git a/content/docs/v1.8/tutorial/_index.md b/content/docs/v1.8/tutorial/_index.md new file mode 100644 index 000000000..155cdc810 --- /dev/null +++ b/content/docs/v1.8/tutorial/_index.md @@ -0,0 +1,7 @@ +--- +title: "Tutorial_v1.8/Tutorial" +date: 2026-09-16T04:40:22Z +draft: false +weight: 0 +--- + diff --git a/content/docs/v1.8/tutorial/get-started-with-faiss-agent.md b/content/docs/v1.8/tutorial/get-started-with-faiss-agent.md new file mode 100644 index 000000000..b47c48396 --- /dev/null +++ b/content/docs/v1.8/tutorial/get-started-with-faiss-agent.md @@ -0,0 +1,398 @@ +--- +title: "Get Started With Faiss Agent_v1.8/Tutorial" +date: 2026-09-16T04:40:19Z +draft: false +weight: 200 +description: Running Vald cluster with faiss Agent on Kubernetes and execute client codes +menu: + tutorial: + parent: Tutorial +--- + +# Get Started + +This tutorial is for those who have already completed [Get Started](/docs/v1.8/tutorial/get-started). +Please refer to Prepare the Kubernetes Cluster and others there. + +## Deploy Vald on Kubernetes Cluster + +This chapter shows how to deploy Vald using Helm and run it on your Kubernetes cluster.
+In this tutorial, you will deploy the basic configuration of Vald that is consisted of vald-agent-faiss, vald-lb-gateway, vald-discoverer, and vald-manager-index.
+ +1. Clone the repository + + ```bash + git clone https://github.com/vdaas/vald.git && \ + cd vald + ``` + +1. Confirm which cluster to deploy + + ```bash + kubectl cluster-info + ``` + +1. Edit Configurations + + Set the parameters for connecting to the vald-lb-gateway through Kubernetes ingress from the external network. + Please set these parameters. + + ```bash + vim example/helm/values.yaml + === + ## vald-lb-gateway settings + gateway: + lb: + ... + ingress: + enabled: true + # TODO: Set your ingress host. + host: localhost + # TODO: Set annotations which you have to set for your k8s cluster. + annotations: + ... + ## vald-agent-faiss settings + agent: + algorithm: faiss + image: + repository: vdaas/vald-agent-faiss + tag: latest + faiss: + auto_index_check_duration: 1m + auto_index_duration_limit: 24h + auto_index_length: 10 + auto_save_index_duration: 35m + dimension: 784 + enable_copy_on_write: false + enable_in_memory_mode: true + enable_proactive_gc: true + index_path: "" + initial_delay_max_duration: 3m + load_index_timeout_factor: 1ms + m: 8 # dimension % m == 0, train size >= 2^m(or nlist) * minPointsPerCentroid + max_load_index_timeout: 10m + method_type: "ivfpq" + metric_type: "innerproduct" + min_load_index_timeout: 3m + nbits_per_idx: 8 + nlist: 100 + ... + ``` + + Note:
+ If you decided to use port-forward instead of ingress, please set `gateway.lb.ingress.enabled` to `false`. + +1. Deploy Vald using Helm + + Add vald repo into the helm repo. + + ```bash + helm repo add vald https://vald.vdaas.org/charts + ``` + + Deploy vald on your Kubernetes cluster. + + ```bash + helm install vald vald/vald --values example/helm/values.yaml + ``` + +1. Verify + + When finish deploying Vald, you can check the Vald's pods status following command. + + ```bash + kubectl get pods + ``` + +
Example output
+ If the deployment is successful, all Vald components should be running. + + ```bash + NAME READY STATUS RESTARTS AGE + vald-agent-faiss-0 1/1 Running 0 7m12s + vald-agent-faiss-1 1/1 Running 0 7m12s + vald-agent-faiss-2 1/1 Running 0 7m12s + vald-agent-faiss-3 1/1 Running 0 7m12s + vald-agent-faiss-4 1/1 Running 0 7m12s + vald-discoverer-7f9f697dbb-q44qh 1/1 Running 0 7m11s + vald-lb-gateway-6b7b9f6948-4z5md 1/1 Running 0 7m12s + vald-lb-gateway-6b7b9f6948-68g94 1/1 Running 0 6m56s + vald-lb-gateway-6b7b9f6948-cvspq 1/1 Running 0 6m56s + vald-manager-index-74c7b5ddd6-jrnlw 1/1 Running 0 7m12s + ``` + +
+ + ```bash + kubectl get ingress + ``` + +
Example output
+ + ```bash + NAME CLASS HOSTS ADDRESS PORTS AGE + vald-lb-gateway-ingress localhost 192.168.16.2 80 7m43s + ``` + +
+ + ```bash + kubectl get svc + ``` + +
Example output
+ + ```bash + NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE + kubernetes ClusterIP 10.43.0.1 443/TCP 9m29s + vald-agent-faiss ClusterIP None 8081/TCP,3001/TCP 8m48s + vald-discoverer ClusterIP None 8081/TCP,3001/TCP 8m48s + vald-manager-index ClusterIP None 8081/TCP,3001/TCP 8m48s + vald-lb-gateway ClusterIP None 8081/TCP,3001/TCP 8m48s + ``` + +
+ +## Run Example Code + +In this chapter, you will execute insert, search, and delete vectors to your Vald cluster using the example code.
+The [Fashion-MNIST](https://github.com/zalandoresearch/fashion-mnist) is used as a dataset for indexing and search query. + +The example code is implemented in Go and using [vald-client-go](https://github.com/vdaas/vald-client-go), one of the official Vald client libraries, for requesting to Vald cluster. +Vald provides multiple language client libraries such as Go, Java, Node.js, Python, etc. +If you are interested, please refer to [SDKs](/docs/v1.8/user-guides/sdks).
+ +1. Port Forward(option) + + If you do not use Kubernetes Ingress, port-forward is required to make requests from your local environment. + + ```bash + kubectl port-forward deployment/vald-lb-gateway 8081:8081 + ``` + +1. Download dataset + + Download [Fashion-MNIST](https://github.com/zalandoresearch/fashion-mnist) that is used as a dataset for indexing and search query. + + Move to the working directory + + ```bash + cd example/client + ``` + + Download Fashion-MNIST testing dataset + + ```bash + wget http://ann-benchmarks.com/fashion-mnist-784-euclidean.hdf5 + ``` + +1. Run Example + + We use [`example/client/main.go`](https://github.com/vdaas/vald/blob/main/example/client/main.go) to run the example.
+ This example will insert and index 400 vectors into the Vald from the Fashion-MNIST dataset via [gRPC](https://grpc.io/). + And then after waiting for indexing, it will request for searching the nearest vector 10 times. + You will get the 10 nearest neighbor vectors for each search query.
+ Run example codes by executing the below command. + + ```bash + go run main.go + ``` + +
The detailed explanation of example code is here
+ This will execute 6 steps. + 1. init + - Import packages +
example code
+ + ```go + package main + + import ( + "context" + "encoding/json" + "flag" + "time" + + "github.com/kpango/fuid" + "github.com/kpango/glg" + "github.com/vdaas/vald-client-go/v1/payload" + "github.com/vdaas/vald-client-go/v1/vald" + + "gonum.org/v1/hdf5" + "google.golang.org/grpc" + ) + ``` + +
+ + - Set variables + - The constant number of training datasets and test datasets. +
example code
+ + ```go + const ( + insertCount = 400 + testCount = 20 + ) + ``` + +
+ + - The variables for configuration. +
example code
+ + ```go + const ( + datasetPath string + grpcServerAddr string + indexingWaitSeconds uint + ) + ``` + +
+ + - Recognition parameters. +
example code
+ + ```go + func init() { + flag.StringVar(&datasetPath, "path", "fashion-mnist-784-euclidean.hdf5", "set dataset path") + flag.StringVar(&grpcServerAddr, "addr", "127.0.0.1:8081", "set gRPC server address") + flag.UintVar(&indexingWaitSeconds, "wait", 60, "set indexing wait seconds") + flag.Parse() + } + ``` + +
+ + 1. load + - Loading from Fashion-MNIST dataset and set id for each vector that is loaded. This step will return the training dataset, test dataset, and ids list of ids when loading is completed with success. +
example code
+ + ```go + ids, train, test, err := load(datasetPath) + if err != nil { + glg.Fatal(err) + } + ``` + +
+ + 1. Create the gRPC connection and Vald client with gRPC connection. + +
example code
+ + ```go + ctx := context.Background() + + conn, err := grpc.NewClient(grpcServerAddr, grpc.WithInsecure()) + if err != nil { + glg.Fatal(err) + } + + client := vald.NewValdClient(conn) + ``` + +
+ + 1. Insert and Index + - Insert and Indexing 400 training datasets to the Vald agent. +
example code
+ + ```go + for i := range ids [:insertCount] { + _, err := client.Insert(ctx, &payload.Insert_Request{ + Vector: &payload.Object_Vector{ + Id: ids[i], + Vector: train[i], + }, + Config: &payload.Insert_Config{ + SkipStrictExistCheck: true, + }, + }) + if err != nil { + glg.Fatal(err) + } + if i%10 == 0 { + glg.Infof("Inserted %d", i) + } + } + ``` + +
+ + - Wait until indexing finish. +
example code
+ + ```go + wt := time.Duration(indexingWaitSeconds) * time.Second + glg.Infof("Wait %s for indexing to finish", wt) + time.Sleep(wt) + ``` + +
+ + 1. Search + - Search 10 neighbor vectors for each 20 test datasets and return a list of the neighbor vectors. + + - When getting approximate vectors, the Vald client sends search config and vector to the server via gRPC. +
example code
+ + ```go + glg.Infof("Start search %d times", testCount) + for i, vec := range test[:testCount] { + res, err := client.Search(ctx, &payload.Search_Request){ + Vector: vec, + Config: &payload.Search_Config{ + Num: 10, + Radius: -1, + Epsilon: 0.1, + Timeout: 100000000, + } + } + if err != nil { + glg.Fatal(err) + } + + b, _ := json.MarshalIndent(res.GetResults(), "", " ") + glg.Infof("%d - Results : %s\n\n", i+1, string(b)) + time.Sleep(1 * time.Second) + } + ``` + +
+ + 1. Remove + - Remove 400 indexed training datasets from the Vald agent. +
example code
+ + ```go + for i := range ids [:insertCount] { + _, err := client.Remove(ctx, &payload.Remove_Request{ + Id: &payload.Object_ID{ + Id: ids[i], + }, + }) + if err != nil { + glg.Fatal(err) + } + if i%10 == 0 { + glg.Infof("Removed %d", i) + } + } + ``` + +
+ +## Cleanup + +In the last, you can remove the deployed Vald Cluster by executing the below command. + +```bash +helm uninstall vald +``` + +## References + +- [Get Started with NGT agent by default](https://github.com/vdaas/vald/blob/main/docs/tutorial/get-started) +- [Faiss](https://github.com/facebookresearch/faiss) diff --git a/content/docs/v1.8/tutorial/get-started.md b/content/docs/v1.8/tutorial/get-started.md new file mode 100644 index 000000000..0ac190c4a --- /dev/null +++ b/content/docs/v1.8/tutorial/get-started.md @@ -0,0 +1,475 @@ +--- +title: "Get Started_v1.8/Tutorial" +date: 2026-09-16T04:40:19Z +draft: false +weight: 100 +description: Running Vald cluster with NGT Agent on Kubernetes and execute client codes +menu: + tutorial: + parent: Tutorial +--- + +# Get Started + +Vald is a highly scalable distributed fast approximate nearest neighbor dense vector search engine.
+Vald is designed and implemented based on Cloud-Native architecture. + +This tutorial shows how to deploy and run the Vald components on your Kubernetes cluster. +And, Fashion-MNIST is used as an example of a dataset. + +## Overview + +The below image shows the architecture image about the deployment result of `Get Started`.
+The 4 kinds of components, `Vald LB Gateway`, `Vald Discoverer`, `Vald Agent`, and `Vald Index Manager` will be deployed to the Kubernetes. +For more information about Vald's architecture, please refer to [Architecture](/docs/v1.8/overview/architecture). + + + +The 5 steps to Get Started with Vald: + +1. [Check and Satisfy the Requirements](#Requirements) +1. [Prepare Kubernetes Cluster](#Prepare-the-Kubernetes-Cluster) +1. [Deploy Vald on Kubernetes Cluster](#Deploy-Vald-on-Kubernetes-Cluster) +1. [Run Example Code](#Run-Example-Code) +1. [Cleanup](#Cleanup) + +## Requirements + +- Kubernetes: v1.19 ~ +- Go: v1.15 ~ +- Helm: v3 ~ +- libhdf5 (_only required for get started_) + +Helm is used to deploying Vald on your Kubernetes and HDF5 is used to decode the sample data file to run the example.
+If Helm or HDF5 is not installed, please install [Helm](https://helm.sh/docs/intro/install) and [HDF5](https://www.hdfgroup.org/). + +
Installation command for Helm
+ +```bash +curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash +``` + +
+ +
Installation command for HDF5
+ +```bash +# yum +yum install -y hdf5-devel + +# apt +apt-get install libhdf5-serial-dev + +# homebrew +brew install hdf5 +``` + +
+ +## Prepare the Kubernetes Cluster + +This tutorial requires the Kubernetes cluster.
+Vald runs on public Cloud Kubernetes Services such as GKE, EKS. +In the sense of trying to `Get Started`, [k3d](https://k3d.io/) or [kind](https://kind.sigs.k8s.io/) are easy Kubernetes tools to use. + +This tutorial uses Kubernetes Ingress and [Kubernetes Metrics Server](https://github.com/kubernetes-sigs/metrics-server) for running Vald.
+Please make sure these functions are available.
+ +The configuration of Kubernetes Ingress is depended on your Kubernetes cluster's provider. +Please refer to on yourself. + +In the following example, we create the Kubernetes cluster using [k3d](https://k3d.io/), that the internal port 80 (where the traefik ingress controller is listening on) is exposed on the host system. + +```bash +k3d cluster create -p 8081:80@loadbalancer +``` + +The way to deploy Kubernetes Metrics Service is here: + +```bash +kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml && \ +kubectl wait -n kube-system --for=condition=ready pod -l k8s-app=metrics-server --timeout=600s +``` + +## Deploy Vald on Kubernetes Cluster + +This chapter shows how to deploy Vald using Helm and run it on your Kubernetes cluster.
+In this tutorial, you will deploy the basic configuration of Vald that is consisted of vald-agent-ngt, vald-lb-gateway, vald-discoverer, and vald-manager-index.
+ +
+For vald-discoverer to work correctly, if you deploy multiple Vald clusters in the same Kubernetes cluster, please deploy one Vald cluster in one namespace. +
+ +1. Clone the repository + + ```bash + git clone https://github.com/vdaas/vald.git && \ + cd vald + ``` + +1. Confirm which cluster to deploy + + ```bash + kubectl cluster-info + ``` + +1. Edit Configurations + + Set the parameters for connecting to the vald-lb-gateway through Kubernetes ingress from the external network. + Please set these parameters. + + ```bash + vim example/helm/values.yaml + === + ## vald-lb-gateway settings + gateway: + lb: + ... + ingress: + enabled: true + # TODO: Set your ingress host. + host: localhost + # TODO: Set annotations which you have to set for your Ingress resource. + annotations: + ... + ``` + + Note:
+ If you decided to use port-forward instead of ingress, please set `gateway.lb.ingress.enabled` to `false`. + +1. Deploy Vald using Helm + + Add vald repo into the helm repo. + + ```bash + helm repo add vald https://vald.vdaas.org/charts + ``` + + Deploy vald on your Kubernetes cluster. + + ```bash + helm install vald vald/vald --values example/helm/values.yaml + ``` + +1. Verify + + When finish deploying Vald, you can check the Vald's pods status following command. + + ```bash + kubectl get pods + ``` + +
Example output
+ If the deployment is successful, all Vald components should be running. + + ```bash + NAME READY STATUS RESTARTS AGE + vald-agent-ngt-0 1/1 Running 0 7m12s + vald-agent-ngt-1 1/1 Running 0 7m12s + vald-agent-ngt-2 1/1 Running 0 7m12s + vald-agent-ngt-3 1/1 Running 0 7m12s + vald-agent-ngt-4 1/1 Running 0 7m12s + vald-discoverer-7f9f697dbb-q44qh 1/1 Running 0 7m11s + vald-lb-gateway-6b7b9f6948-4z5md 1/1 Running 0 7m12s + vald-lb-gateway-6b7b9f6948-68g94 1/1 Running 0 6m56s + vald-lb-gateway-6b7b9f6948-cvspq 1/1 Running 0 6m56s + vald-manager-index-74c7b5ddd6-jrnlw 1/1 Running 0 7m12s + ``` + +
+ + ```bash + kubectl get ingress + ``` + +
Example output
+ + ```bash + NAME CLASS HOSTS ADDRESS PORTS AGE + vald-lb-gateway-ingress traefik localhost 192.168.16.2 80 7m43s + ``` + +
+ + ```bash + kubectl get svc + ``` + +
Example output
+ + ```bash + NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE + kubernetes ClusterIP 10.43.0.1 443/TCP 9m29s + vald-agent-ngt ClusterIP None 8081/TCP,3001/TCP 8m48s + vald-discoverer ClusterIP None 8081/TCP,3001/TCP 8m48s + vald-manager-index ClusterIP None 8081/TCP,3001/TCP 8m48s + vald-lb-gateway ClusterIP None 8081/TCP,3001/TCP 8m48s + ``` + +
+ +## Run Example Code + +In this chapter, you will execute insert, search, and delete vectors to your Vald cluster using the example code.
+The [Fashion-MNIST](https://github.com/zalandoresearch/fashion-mnist) is used as a dataset for indexing and search query. + +The example code is implemented in Go and using [vald-client-go](https://github.com/vdaas/vald-client-go), one of the official Vald client libraries, for requesting to Vald cluster. +Vald provides multiple language client libraries such as Go, Java, Node.js, Python, etc. +If you are interested, please refer to [SDKs](/docs/v1.8/user-guides/sdks).
+ +1. Port Forward(option) + + If you do not use Kubernetes Ingress, port-forward is required to make requests from your local environment. + + ```bash + kubectl port-forward deployment/vald-lb-gateway 8081:8081 + ``` + +1. Download dataset + + Download [Fashion-MNIST](https://github.com/zalandoresearch/fashion-mnist) that is used as a dataset for indexing and search query. + + Move to the working directory + + ```bash + cd example/client + ``` + + Download Fashion-MNIST testing dataset + + ```bash + wget http://ann-benchmarks.com/fashion-mnist-784-euclidean.hdf5 + ``` + +1. Run Example + + We use [`example/client/main.go`](https://github.com/vdaas/vald/blob/main/example/client/main.go) to run the example.
+ This example will insert and index 400 vectors into the Vald from the Fashion-MNIST dataset via [gRPC](https://grpc.io/). + And then after waiting for indexing, it will request for searching the nearest vector 10 times. + You will get the 10 nearest neighbor vectors for each search query.
+ Run example codes by executing the below command. + + ```bash + go run main.go + ``` + +
The detailed explanation of example code is here
+ This will execute 6 steps. + 1. init + - Import packages +
example code
+ + ```go + package main + + import ( + "context" + "encoding/json" + "flag" + "time" + + "github.com/kpango/fuid" + "github.com/kpango/glg" + "github.com/vdaas/vald-client-go/v1/payload" + "github.com/vdaas/vald-client-go/v1/vald" + "gonum.org/v1/hdf5" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + ) + ``` + +
+ + - Set variables + - The constant number of training datasets and test datasets. +
example code
+ + ```go + const ( + insertCount = 400 + testCount = 20 + ) + ``` + +
+ + - The variables for configuration. +
example code
+ + ```go + const ( + datasetPath string + grpcServerAddr string + indexingWaitSeconds uint + ) + ``` + +
+ + - Recognition parameters. +
example code
+ + ```go + func init() { + flag.StringVar(&datasetPath, "path", "fashion-mnist-784-euclidean.hdf5", "set dataset path") + flag.StringVar(&grpcServerAddr, "addr", "127.0.0.1:8081", "set gRPC server address") + flag.UintVar(&indexingWaitSeconds, "wait", 60, "set indexing wait seconds") + flag.Parse() + } + ``` + +
+ + 1. load + - Loading from Fashion-MNIST dataset and set id for each vector that is loaded. This step will return the training dataset, test dataset, and ids list of ids when loading is completed with success. +
example code
+ + ```go + ids, train, test, err := load(datasetPath) + if err != nil { + glg.Fatal(err) + } + ``` + +
+ + 1. Create the gRPC connection and Vald client with gRPC connection. + +
example code
+ + ```go + ctx := context.Background() + + conn, err := grpc.NewClient(grpcServerAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + glg.Fatal(err) + } + + client := vald.NewValdClient(conn) + ``` + +
+ + 1. Insert and Index + - Insert and Indexing 400 training datasets to the Vald agent. +
example code
+ + ```go + for i := range ids [:insertCount] { + _, err := client.Insert(ctx, &payload.Insert_Request{ + Vector: &payload.Object_Vector{ + Id: ids[i], + Vector: train[i], + }, + Config: &payload.Insert_Config{ + SkipStrictExistCheck: true, + }, + }) + if err != nil { + glg.Fatal(err) + } + if i%10 == 0 { + glg.Infof("Inserted %d", i) + } + } + ``` + +
+ + - Wait until indexing finish. +
example code
+ + ```go + wt := time.Duration(indexingWaitSeconds) * time.Second + glg.Infof("Wait %s for indexing to finish", wt) + time.Sleep(wt) + ``` + +
+ + 1. Search + - Search 10 neighbor vectors for each 20 test datasets and return a list of the neighbor vectors. + + - When getting approximate vectors, the Vald client sends search config and vector to the server via gRPC. +
example code
+ + ```go + glg.Infof("Start search %d times", testCount) + for i, vec := range test[:testCount] { + res, err := client.Search(ctx, &payload.Search_Request){ + Vector: vec, + Config: &payload.Search_Config{ + Num: 10, + Radius: -1, + Epsilon: 0.1, + Timeout: 100000000, + } + } + if err != nil { + glg.Fatal(err) + } + + b, _ := json.MarshalIndent(res.GetResults(), "", " ") + glg.Infof("%d - Results : %s\n\n", i+1, string(b)) + time.Sleep(1 * time.Second) + } + ``` + +
+ + 1. Remove + - Remove 200 indexed training datasets from the Vald agent. +
example code
+ + ```go + for i := range ids [:removeCount] { + _, err := client.Remove(ctx, &payload.Remove_Request{ + Id: &payload.Object_ID{ + Id: ids[i], + }, + }) + if err != nil { + glg.Fatal(err) + } + if i%10 == 0 { + glg.Infof("Removed %d", i) + } + } + ``` + +
+ + 1. Flush + - Remove all remaining training datasets from the Vald agent. +
example code
+ + ```go + _, err := client.Flush(ctx, &payload.Flush_Request{}) + if err != nil { + glg.Fatal(err) + } + ``` + +
+ +## Cleanup + +In the last, you can remove the deployed Vald Cluster by executing the below command. + +```bash +helm uninstall vald +``` + +## Next Steps + +Congratulation! You completely entered the Vald World! + +If you want, you can try other tutorials such as: + +- [Vald Agent Standalone on k8s](/docs/v1.8/tutorial/vald-agent-standalone-on-k8s) +- [Vald Agent on Docker](/docs/v1.8/tutorial/vald-agent-standalone-on-docker) + +For more information, we recommend you to check: + +- [Configuration](/docs/v1.8/user-guides/configuration) +- [Operations](/docs/v1.8/user-guides/operations) diff --git a/content/docs/v1.8/tutorial/vald-agent-standalone-on-docker.md b/content/docs/v1.8/tutorial/vald-agent-standalone-on-docker.md new file mode 100644 index 000000000..b76be44de --- /dev/null +++ b/content/docs/v1.8/tutorial/vald-agent-standalone-on-docker.md @@ -0,0 +1,222 @@ +--- +title: "Vald Agent Standalone on Docker_v1.8/Tutorial" +date: 2026-09-16T04:40:19Z +draft: false +weight: 400 +description: Running Vald Agent on Docker and execute client codes +menu: + tutorial: + parent: Tutorial +--- + +# Vald Agent Standalone on Docker + +Vald is designed and implemented based on Cloud-Native architecture. +However, there may be cases that want to use only Vald Agent without Kubernetes. + +This article will show you how to deploy and run the Vald Agent on Docker. +Fashion-MNIST is used as an example dataset, same as [Get Started](/docs/v1.8/tutorial/get-started). + +## Requirements + +- Docker: v19.0 ~ +- Go: v1.15 ~ +- libhdf5 (_only required for this tutorial._) + +HDF5 is required for this tutorial. If hdf5 is not installed, please install [HDF5](https://www.hdfgroup.org/). + +
[Optional] Install HDF5
+ +```bash +# yum +yum install -y hdf5-devel + +# apt +apt-get install libhdf5-serial-dev + +# homebrew +brew install hdf5 +``` + +
+ +## Deploy + +This chapter will show you how to deploy Vald Agent on docker.
+This chapter will use NGT for the core engine of Vald Agent. + +1. Clone the vdaas/vald repository + + ```bash + git clone https://github.com/vdaas/vald.git + ``` + +1. Create a directory for setting deploy vald-agent-ngt + + ```bash + cd vald && mkdir -p tutorial && cd tutorial + ``` + +1. Create the `config.yaml` + + The configuration of Vald agent for docker is set using `config.yaml`
+ You can also check [the sample configuration](https://github.com/vdaas/vald/blob/main/cmd/agent/core/ngt/sample.yaml). + + ```bash + cat << EOF > config.yaml + --- + version: v0.0.0 + time_zone: JST + logging: + logger: glg + level: debug + format: raw + server_config: + servers: + - name: agent-grpc + host: 0.0.0.0 + port: 8081 + mode: GRPC + probe_wait_time: "3s" + http: + shutdown_duration: "5s" + handler_timeout: "" + idle_timeout: "" + read_header_timeout: "" + read_timeout: "" + write_timeout: "" + startup_strategy: + - agent-grpc + shutdown_strategy: + - agent-grpc + full_shutdown_duration: 600s + tls: + enabled: false + # cert: /path/to/cert + # key: /path/to/key + # ca: /path/to/ca + ngt: + # path to index data + index_path: "/etc/server/backup" + # vector dimension + dimension: 784 + # bulk insert chunk size + bulk_insert_chunk_size: 10 + # distance_type, which should be "l1", "l2" "angle", "hamming", "cosine", "normalizedangle", "normalizedcosine" or "jaccard" + distance_type: l2 + # object_type, which should be "float" or "uint8" + object_type: float + # creation edge size + creation_edge_size: 20 + # search edge size + search_edge_size: 10 + # The limit duration of automatic indexing + # auto_index_duration_limit should be 30m-6h for production use. Below setting is a just example + auto_index_duration_limit: 1m + # Check duration of automatic indexing. + # auto_index_check_duration be 10m-1h for production use. Below setting is a just example + auto_index_check_duration: 10s + # The number of cache to trigger automatic indexing + auto_index_length: 100 + # The limit duration of auto saving indexing + # auto_save_index_duration should be 30m-60m for production use. The below setting is a just example. + auto_save_index_duration: 90s + # The maximum limit duration for an initial delay + # initial_delay_max_duration should be 3m-5m for production use. The below setting is a just example. + initial_delay_max_duration: 60s + # The default create index batch pool size. + # When it is too large comparing to machine resource, the docker container will be crash. + default_pool_size: 500 + EOF + ``` + +1. Create a backup directory + + To avoid removing the indexing data due to any trouble after finishing indexing, we should prepare the path for auto backup. + + ```bash + mkdir -p backup + ``` + +1. Deploy Vald Agent on Docker + + To deploy Vald agent on docker with `config.yaml`, you can run the below command. + Note: + - Please check whether there is `config.yaml` file and `backup` directory in your current directory. + + ```bash + docker run -v $(pwd):/etc/server -u "$(id -u $USER):$(id -g $USER)" -v /etc/passwd:/etc/passwd:ro -v /etc/group:/etc/group:ro -p 8081:8081 --rm -it vdaas/vald-agent-ngt + ``` + +1. Verify + + If the deployment success, you can confirm the output will be similar to below. + + ```bash + 2020-07-01 03:02:41 [INFO]: maxprocs: Leaving GOMAXPROCS=4: CPU quota undefined + 2020-07-01 03:02:41 [INFO]: service agent ngt v0.0.0 starting... + 2020-07-01 03:02:41 [INFO]: executing daemon pre-start function + 2020-07-01 03:02:41 [INFO]: executing daemon start function + 2020-07-01 03:02:41 [INFO]: server agent-grpc executing preStartFunc + 2020-07-01 12:02:41 [INFO]: gRPC server agent-grpc starting on 0.0.0.0:8081 + ``` + +## Run using example code + +1. Download dataset + + This tutorial uses [Fashion-MNIST](https://github.com/zalandoresearch/fashion-mnist) as a dataset for indexing and search query. + + ```bash + # move to the working directory + cd example/client/agent + ``` + + ```bash + # download Fashion-MNIST testing dataset + wget https://ann-benchmarks.com/fashion-mnist-784-euclidean.hdf5 + ``` + +1. Running example + + Vald provides multiple language client libraries such as Go, Java, Node.js, Python, etc.
+ This example will insert and index 400 vectors into the Vald from the Fashion-MNIST dataset via gRPC. + And then, after waiting for indexing, it will request to search the nearest vector 10 times. + You will get the 10 nearest neighbor vectors for each search query.
+ + We use [`example/client/agent/main.go`](https://github.com/vdaas/vald/blob/main/example/client/agent/main.go) to run the example. + The example code is the same as running an example-only Vald agent on Kubernetes. + If you want to learn the detail of running an example, please refer to the tutorial of [Vald Agent Standalone on kubernetes](/docs/v1.8/tutorial/get-started/#run-using-example-code). + + ```bash + # run example + go run main.go + ``` + +
+ It would be best to run CreateIndex() after Insert() without waiting for auto-indexing in your client code, even if you can wait for the finishing auto createIndex function, which sometimes takes a long time. + The backup files (e.g., ngt-meta.kvsdb) will be in your mount directory when vald-agent-ngt finishes indexing. +
+ +
+ If you use Go(v1.16~) and catch the error like `missing go.sum entry to add it` when running `go run main.go`, please run `go mod tidy` and retry. + This error comes from Go Command Changes of Go 1.16 Release Notes.(Please refer to https://golang.org/doc/go1.16#go-command for more details). +
+ +1. Clean Up + + Stop the Vald Agent docker container via `Ctrl+C`. + +## Recommended Documents + +Congratulation! You achieved this tutorial! + +If you want, you can try other tutorials such as: + +- [Vald Agent Standalone on Kubernetes](/docs/v1.8/tutorial/vald-agent-standalone-on-k8s) + +For more information, we recommend you to check: + +- [Architecture](/docs/v1.8/overview/architecture) +- [Configuration](/docs/v1.8/user-guides/configuration) +- [Operations](/docs/v1.8/user-guides/operations) diff --git a/content/docs/v1.8/tutorial/vald-agent-standalone-on-k8s.md b/content/docs/v1.8/tutorial/vald-agent-standalone-on-k8s.md new file mode 100644 index 000000000..191ebf0e1 --- /dev/null +++ b/content/docs/v1.8/tutorial/vald-agent-standalone-on-k8s.md @@ -0,0 +1,405 @@ +--- +title: "Vald Agent Standalone on K8s_v1.8/Tutorial" +date: 2026-09-16T04:40:19Z +draft: false +weight: 300 +description: Running only Vald Agent on Kubernetes and execute client codes +menu: + tutorial: + parent: Tutorial +--- + +# Vald Agent Standalone on Kubernetes + +This article will show you how to deploy a standalone Vald Agent using Helm and run it on your Kubernetes cluster. + +## Overview + +Vald is made up of multiple microservices. +In [Get Started](/docs/v1.8/tutorial/get-started), you may use 4 kinds of components to deploy Vald. +In this case, you use only 1 component, `Vald Agent` that is the core component for Vald named `vald-agent-ngt`, to deploy. +The below image shows the architecture image of this case. + + + +
+Using only Vald Agent, the auto indexing function is not in use. +
+ +The 5 steps to Vald Agent Standalone on Kubernetes with Vald: + +1. [Check and Satisfy the Requirements](#Requirements) +1. [Prepare Kubernetes Cluster](#Prepare-the-Kubernetes-Cluster) +1. [Deploy Vald Agent Standalone on Kubernetes cluster](#Deploy-Vald-Agent-Standalone-on-Kubernetes-Cluster) +1. [Run Example Code](#Run-Example-Code) +1. [Cleanup](#Cleanup) + +## Requirements + +- Kubernetes: v1.19 ~ +- Go: v1.15 ~ +- Helm: v3 ~ +- libhdf5 (_only required for tutorial_) + +Helm is used to deploying Vald on your Kubernetes, and Hdf5 decodes the sample data.
+If Helm or HDF5 is not installed, please install [Helm](https://helm.sh/docs/intro/install) and [HDF5](https://www.hdfgroup.org/). + +
Installation command for Helm
+ +```bash +curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash +``` + +
+ +
Installation command for HDF5
+ +```bash +# yum +yum install -y hdf5-devel + +# apt +apt-get install libhdf5-serial-dev + +# homebrew +brew install hdf5 +``` + +
+ +## Prepare the Kubernetes Cluster + +1. Prepare Kubernetes cluster + + To complete get started, the Kubernetes cluster is required.
+ Vald will run on Cloud Services such as GKE, AWS. + In the sense of trying to "Get-Started", [k3d](https://k3d.io/) or [kind](https://kind.sigs.k8s.io/) are easy Kubernetes tools to use. + +## Deploy Vald Agent Standalone on Kubernetes Cluster + +This chapter will show you how to deploy a standalone Vald Agent using Helm and run it on your Kubernetes cluster.
+This chapter uses [NGT](https://github.com/NGT-labs/NGT) as Vald Agent to perform vector insertion operation, indexing, and searching operation.
+ +1. Clone the repository + + To use the `deployment yaml` for deployment, let's clone [`vdaas/vald`](https://github.com/vdaas/vald.git) repository. + + ```bash + git clone https://github.com/vdaas/vald.git && \ + cd vald + ``` + +1. Deploy Vald Agent Standalone using Helm + + There is the [values.yaml](https://github.com/vdaas/vald/blob/main/example/helm/values-standalone-agent-ngt.yaml) to deploy standalone Vald Agent. + Each component can be disabled by setting the value `false` to the `[component].enabled` field. + This is useful for deploying standalone Vald Agent NGT pods. + + ```bash + helm repo add vald https://vald.vdaas.org/charts && \ + helm install vald-agent-ngt vald/vald --values example/helm/values-standalone-agent-ngt.yaml + ``` + +1. Verify + + ```bash + kubectl get pods + ``` + +
Example output
+ If the deployment is successful, Vald Agent component should be running. + + ```bash + NAME READY STATUS RESTARTS AGE + vald-agent-0 1/1 Running 0 20m + vald-agent-1 1/1 Running 0 20m + vald-agent-2 1/1 Running 0 20m + vald-agent-3 1/1 Running 0 20m + ``` + +
+ +## Run Example Code + +1. Port Forward + + At first, port-forward the vald-lb-gateway is required to make request from your local environment possible. + + ```bash + kubectl port-forward service/vald-agent 8081:8081 + ``` + +1. Download dataset + + Download [Fashion-MNIST](https://github.com/zalandoresearch/fashion-mnist) that is used as a dataset for indexing and search query. + + ```bash + # move to the work directory + cd example/client/agent + ``` + + ```bash + # download Fashion-MNIST testing dataset + wget https://ann-benchmarks.com/fashion-mnist-784-euclidean.hdf5 + ``` + +1. Run Example + + We use [`example/client/agent/main.go`](https://github.com/vdaas/vald/blob/main/example/client/agent/main.go) to run the example.
+ This example will insert and index 400 vectors into the Vald from the Fashion-MNIST dataset via gRPC. + And then after waiting for indexing, it will request for searching the nearest vector 10 times. + You will get the 10 nearest neighbor vectors for each search query.
+ Run example codes by executing the below command. + + ```bash + # run example + go run main.go + ``` + +
The detailed explanation of example code is here
+ This will execute 6 steps. + 1. init + - Import packages +
example code
+ + ```go + package main + + import ( + "context" + "encoding/json" + "flag" + "time" + + "github.com/kpango/fuid" + "github.com/kpango/glg" + agent "github.com/vdaas/vald-client-go/v1/agent/core" + "github.com/vdaas/vald-client-go/v1/payload" + "github.com/vdaas/vald-client-go/v1/vald" + "gonum.org/v1/hdf5" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + ) + ``` + +
+ + - Set variables + - The constant number of training datasets and test datasets. +
example code
+ + ```go + const ( + insertCount = 400 + testCount = 20 + ) + ``` + +
+ + - The variables for configuration. +
example code
+ + ```go + const ( + datasetPath string + grpcServerAddr string + indexingWaitSeconds uint + ) + ``` + +
+ + - Recognition parameters. +
example code
+ + ```go + func init() { + flag.StringVar(&datasetPath, "path", "fashion-mnist-784-euclidean.hdf5", "set dataset path") + flag.StringVar(&grpcServerAddr, "addr", "127.0.0.1:8081", "set gRPC server address") + flag.UintVar(&indexingWaitSeconds, "wait", 60, "set indexing wait seconds") + flag.Parse() + } + ``` + +
+ + 1. load + - Loading from Fashion-MNIST dataset and set id for each vector that is loaded. This step will return the training dataset, test dataset, and ids list of ids when loading is completed with success. +
example code
+ + ```go + ids, train, test, err := load(datasetPath) + if err != nil { + glg.Fatal(err) + } + ``` + +
+ + 1. Create the gRPC connection and Vald client with gRPC connection. +
example code
+ + ```go + ctx := context.Background() + + conn, err := grpc.NewClient(grpcServerAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + glg.Fatal(err) + } + + client := agent.NewAgentClient(conn) + ``` + +
+ + 1. Insert and Index + - Insert and Indexing 400 training datasets to the Vald agent. +
example code
+ + ```go + for i := range ids [:insertCount] { + if i%10 == 0 { + glg.Infof("Inserted %d", i) + } + _, err := client.Insert(ctx, &payload.Insert_Request{ + Vector: &payload.Object_Vector{ + Id: ids[i], + Vector: train[i], + }, + Config: &payload.Insert_Config{ + SkipStrictExistCheck: true, + }, + }) + if err != nil { + glg.Fatal(err) + } + } + ``` + +
+ + - Wait until indexing finish. +
example code
+ + ```go + wt := time.Duration(indexingWaitSeconds) * time.Second + glg.Infof("Wait %s for indexing to finish", wt) + time.Sleep(wt) + ``` + +
+ + - [Optional] Indexing manually instead of waiting for auto indexing. + + You can set Agent NGT configuration `auto_index_duration_limit` and `auto_index_check_duration` for auto indexing. + In this example, you can create index manually using `CreateAndSaveIndex()` method in the client library + +
example code
+ + ```go + _, err = client.CreateAndSaveIndex(ctx, &payload.Control_CreateIndexRequest{ + PoolSize: uint32(insertCount), + }) + if err != nil { + glg.Fatal(err) + } + ``` + +
+ + 1. Search + - Search 10 neighbor vectors for each 20 test datasets and return a list of neighbor vectors. + + - When getting approximate vectors, the Vald client sends search config and vector to the server via gRPC. +
example code
+ + ```go + glg.Infof("Start search %d times", testCount) + for i, vec := range test[:testCount] { + res, err := client.Search(ctx, &payload.Search_Request){ + Vector: vec, + Config: &payload.Search_Config{ + Num: 10, + Radius: -1, + Epsilon: 0.1, + Timeout: 100000000, + } + } + if err != nil { + glg.Fatal(err) + } + + b, _ := json.MarshalIndent(res.GetResults(), "", " ") + glg.Infof("%d - Results : %s\n\n", i+1, string(b)) + time.Sleep(1 * time.Second) + } + ``` + +
+ + 1. Remove + - Remove indexed 400 training datasets from the Vald agent. +
example code
+ + ```go + for i := range ids [:insertCount] { + _, err := client.Remove(ctx, &payload.Remove_Request{ + Id: &payload.Object_ID{ + Id: ids[i], + }, + }) + if err != nil { + glg.Fatal(err) + } + if i%10 == 0 { + glg.Infof("Removed %d", i) + } + } + ``` + +
+ + - Remove from the index manually instead of waiting for auto indexing. + + The removed vectors still exist in the NGT graph index before the SaveIndex (or CreateAndSaveIndex) API is called. + If you run the below code, the indexes will be removed completely from the Vald Agent NGT graph and the Backup file. + +
example code
+ + ```go + _, err = client.SaveIndex(ctx, &payload.Empty{}) + if err != nil { + glg.Fatal(err) + } + ``` + +
+ +
+ It would be best to run CreateIndex() after Insert() without waiting for auto-indexing in your client code, even you can wait for the finishing auto createIndex function, which sometimes takes a long time. + The backup files (e.g., ngt-meta.kvsdb) will be in your mount directory when vald-agent-ngt finishes indexing. +
+ +
+ If you use Go(v1.16~) and catch the error like `missing go.sum entry to add it` when running `go run main.go`, please run `go mod tidy` and retry. + This error comes from Go Command Changes of Go 1.16 Release Notes.(Please refer to https://golang.org/doc/go1.16#go-command for more details). +
+ +## Cleanup + +In the last, you can remove all deployed Vald pods by executing the below command. + +```bash +helm uninstall vald-agent-ngt +``` + +## Recommended Documents + +Congratulation! You achieved this tutorial! + +For more information, we recommend you to check: + +- [Architecture](/docs/v1.8/overview/architecture) +- [Configuration](/docs/v1.8/user-guides/configuration) +- [Operations](/docs/v1.8/user-guides/operations) diff --git a/content/docs/v1.8/tutorial/vald-multicluster-on-k8s.md b/content/docs/v1.8/tutorial/vald-multicluster-on-k8s.md new file mode 100644 index 000000000..d3bfa395e --- /dev/null +++ b/content/docs/v1.8/tutorial/vald-multicluster-on-k8s.md @@ -0,0 +1,314 @@ +--- +title: "Vald Multicluster on K8s_v1.8/Tutorial" +date: 2026-09-16T04:40:19Z +draft: false +weight: 500 +description: Running Multi Vald Clusters with Mirror Gateway on Kubernetes and execute client codes +menu: + tutorial: + parent: Tutorial +--- + +# Multiple Vald Clusters using Vald Mirror Gateway + +This article shows how to deploy multiple Vald clusters on your Kubernetes cluster. + +## Overview + +Vald cluster consists of multiple microservices. + +In [Get Started](https://vald.vdaas.org/docs/tutorial/get-started), you may use 4 kinds of components to deploy the Vald cluster. + +In this tutorial, you will deploy multiple Vald clusters with Vald Mirror Gateway, which connects another Vald cluster. + +The below image shows the architecture image of this case. + + + +## Requirements + +- Vald: v1.8 ~ +- Kubernetes: v1.27 ~ +- Go: v1.20 ~ +- Helm: v3 ~ +- libhdf5 (_only required to get started_) + +Helm is used to deploy Vald cluster on your Kubernetes and HDF5 is used to decode the sample data file to run the example code. + +If Helm or HDF5 is not installed, please install [Helm](https://helm.sh/docs/intro/install) and [HDF5](https://www.hdfgroup.org/). + +
Installation command for Helm
+ +```bash +curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash +``` + +
+ +
Installation command for HDF5
+ +```bash +# yum +yum install -y hdf5-devel + +# apt +apt-get install libhdf5-serial-dev + +# homebrew +brew install hdf5 +``` + +
+ +## Prepare the Kubernetes Cluster + +This tutorial requires the Kubernetes cluster. + +Vald cluster runs on public Cloud Kubernetes Services such as GKE, EKS. +In the sense of trying to `Get Started`, [k3d](https://k3d.io/) or [kind](https://kind.sigs.k8s.io/) are easy Kubernetes tools to use. + +This tutorial uses [Kubernetes Metrics Server](https://github.com/kubernetes-sigs/metrics-server) for running the Vald cluster. + +Please make sure these functions are available. + +The way to deploy Kubernetes Metrics Service is here: + +```bash +kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml && \ +kubectl wait -n kube-system --for=condition=ready pod -l k8s-app=metrics-server --timeout=600s +``` + +Please prepare three Namespaces on the Kubernetes cluster. + +```bash +kubectl create namespace vald-01 && \ +kubectl create namespace vald-02 && \ +kubectl create namespace vald-03 +``` + +## Deploy Vald Clusters on each Kubernetes Namespace + +This chapter shows how to deploy the multiple Vald clusters using Helm and run it on your Kubernetes cluster. +In this section, you will deploy three Vald clusters consisting of `vald-agent-ngt`, `vald-lb-gateway`, `vald-discoverer`, `vald-manager-index`, and `vald-mirror-gateway` using the basic configuration. + +1. Clone the repository + + To use the `deployment` YAML for deployment, let’s clone `[vdaas/vald](https://github.com/vdaas/vald.git)` repository. + + ```bash + git clone https://github.com/vdaas/vald.git && cd vald + ``` + +2. Deploy on the `vald-01` Namespace using [dev-vald-01.yaml](https://github.com/vdaas/vald/blob/main/charts/vald/values/multi-vald/dev-vald-01.yaml) and [values.yaml](https://github.com/vdaas/vald/blob/main/example/helm/values.yaml) + + ```bash + helm install vald-cluster-01 charts/vald \ + -f ./example/helm/values.yaml \ + -f ./charts/vald/values/multi-vald/dev-vald-01.yaml \ + -n vald-01 + ``` + +3. Deploy on the `vald-02` Namespace using [dev-vald-02.yaml](https://github.com/vdaas/vald/blob/main/charts/vald/values/multi-vald/dev-vald-02.yaml) and [values.yaml](https://github.com/vdaas/vald/blob/main/example/helm/values.yaml) + + ```bash + helm install vald-cluster-02 charts/vald \ + -f ./example/helm/values.yaml \ + -f ./charts/vald/values/multi-vald/dev-vald-02.yaml \ + -n vald-02 + ``` + +4. Deploy on the `vald-03` Namespace using [dev-vald-03.yaml](https://github.com/vdaas/vald/blob/main/charts/vald/values/multi-vald/dev-vald-03.yaml) and [values.yaml](https://github.com/vdaas/vald/blob/main/example/helm/values.yaml) + + ```bash + helm install vald-cluster-03 charts/vald \ + -f ./example/helm/values.yaml \ + -f ./charts/vald/values/multi-vald/dev-vald-03.yaml \ + -n vald-03 + ``` + +5. Verify + + If success deployment, the Vald cluster’s components should run on each Kubernetes Namespace. + +
vald-01 Namespace
+ + ```bash + kubectl get pods -n vald-01 + NAME READY STATUS RESTARTS AGE + vald-agent-ngt-0 1/1 Running 0 2m41s + vald-agent-ngt-2 1/1 Running 0 2m41s + vald-agent-ngt-3 1/1 Running 0 2m41s + vald-agent-ngt-4 1/1 Running 0 2m41s + vald-agent-ngt-5 1/1 Running 0 2m41s + vald-agent-ngt-1 1/1 Running 0 2m41s + vald-discoverer-77967c9697-brbsp 1/1 Running 0 2m41s + vald-lb-gateway-587879d598-xmws7 1/1 Running 0 2m41s + vald-lb-gateway-587879d598-dzn9c 1/1 Running 0 2m41s + vald-manager-index-56d474c848-wkh6b 1/1 Running 0 2m41s + vald-lb-gateway-587879d598-9wb5q 1/1 Running 0 2m41s + vald-mirror-gateway-6df75cf7cf-gzcr4 1/1 Running 0 2m26s + vald-mirror-gateway-6df75cf7cf-vjbqx 1/1 Running 0 2m26s + vald-mirror-gateway-6df75cf7cf-c2g7t 1/1 Running 0 2m41s + ``` + +
+ +
vald-02 Namespace
+ + ```bash + kubectl get pods -n vald-02 + NAME READY STATUS RESTARTS AGE + vald-agent-ngt-0 1/1 Running 0 2m52s + vald-agent-ngt-1 1/1 Running 0 2m52s + vald-agent-ngt-2 1/1 Running 0 2m52s + vald-agent-ngt-4 1/1 Running 0 2m52s + vald-agent-ngt-5 1/1 Running 0 2m52s + vald-agent-ngt-3 1/1 Running 0 2m52s + vald-discoverer-8cfcff76-vlmpg 1/1 Running 0 2m52s + vald-lb-gateway-54896f9f49-wtlcv 1/1 Running 0 2m52s + vald-lb-gateway-54896f9f49-hbklj 1/1 Running 0 2m52s + vald-manager-index-676855f8d7-bb4wb 1/1 Running 0 2m52s + vald-lb-gateway-54896f9f49-kgrdf 1/1 Running 0 2m52s + vald-mirror-gateway-6598cf957-t2nz4 1/1 Running 0 2m37s + vald-mirror-gateway-6598cf957-wr448 1/1 Running 0 2m52s + vald-mirror-gateway-6598cf957-jdd6q 1/1 Running 0 2m37s + ``` + +
+ +
vald-03 Namespace
+ + ```bash + kubectl get pods -n vald-03 + NAME READY STATUS RESTARTS AGE + vald-agent-ngt-0 1/1 Running 0 2m46s + vald-agent-ngt-1 1/1 Running 0 2m46s + vald-agent-ngt-2 1/1 Running 0 2m46s + vald-agent-ngt-3 1/1 Running 0 2m46s + vald-agent-ngt-4 1/1 Running 0 2m46s + vald-agent-ngt-5 1/1 Running 0 2m46s + vald-discoverer-879867b44-8m59h 1/1 Running 0 2m46s + vald-lb-gateway-6c8c6b468d-ghlpx 1/1 Running 0 2m46s + vald-lb-gateway-6c8c6b468d-rt688 1/1 Running 0 2m46s + vald-lb-gateway-6c8c6b468d-jq7pl 1/1 Running 0 2m46s + vald-manager-index-5596f89644-xfv4t 1/1 Running 0 2m46s + vald-mirror-gateway-7b95956f8b-l57jz 1/1 Running 0 2m31s + vald-mirror-gateway-7b95956f8b-xd9n5 1/1 Running 0 2m46s + vald-mirror-gateway-7b95956f8b-dnxbb 1/1 Running 0 2m31s + ``` + +
+ +## Deploy ValdMirrorTarget Resource (Custom Resource) + +It requires applying the `ValdMirrorTarget` Custom Resource to the one Namespace. + +When applied successfully, the destination information is automatically created on other Namespaces when interconnected with each `vald-mirror-gateway`. + +This tutorial will deploy the [ValdMirrorTarget](https://github.com/vdaas/vald/blob/main/charts/vald/values/multi-vald/mirror-target.yaml) Custom Resource to the `vald-03` Namespace with the following command. + +```bash +kubectl apply -f ./charts/vald/values/multi-vald/mirror-target.yaml -n vald-03 +``` + +The current connection status can be checked with the following command. + +
Example output
+ +```bash +kubectl get vmt -A -o wide +NAMESPACE NAME HOST PORT STATUS LAST TRANSITION TIME AGE +vald-03 mirror-target-01 vald-mirror-gateway.vald-01.svc.cluster.local 8081 Connected 2023-05-22T02:07:51Z 19m +vald-03 mirror-target-02 vald-mirror-gateway.vald-02.svc.cluster.local 8081 Connected 2023-05-22T02:07:51Z 19m +vald-02 mirror-target-3296010438411762394 vald-mirror-gateway.vald-01.svc.cluster.local 8081 Connected 2023-05-22T02:07:53Z 19m +vald-02 mirror-target-12697587923462644654 vald-mirror-gateway.vald-03.svc.cluster.local 8081 Connected 2023-05-22T02:07:53Z 19m +vald-01 mirror-target-13698925675968803691 vald-mirror-gateway.vald-02.svc.cluster.local 8081 Connected 2023-05-22T02:07:53Z 19m +vald-01 mirror-target-17825710563723389324 vald-mirror-gateway.vald-03.svc.cluster.local 8081 Connected 2023-05-22T02:07:53Z 19m +``` + +
+ +## Run Example Code + +In this chapter, you will execute insert, search, get, and delete vectors to your Vald clusters using the example code. + +The [Fashion-MNIST](https://github.com/zalandoresearch/fashion-mnist) is used as a dataset for indexing and search query. + +The example code is implemented in Go and uses [vald-client-go](https://github.com/vdaas/vald-client-go), one of the official Vald client libraries, for requesting to Vald cluster. + +Vald provides multiple language client libraries such as Go, Java, Node.js, Python, etc. + +If you are interested, please refer to [SDKs](https://vald.vdaas.org/docs/user-guides/sdks). + +1. Port Forward(option) + + If you do NOT use Kubernetes Ingress, port forwarding is required to make requests from your local environment. + + ```bash + kubectl port-forward svc/vald-mirror-gateway 8080:8081 -n vald-01 & \ + kubectl port-forward svc/vald-mirror-gateway 8081:8081 -n vald-02 & \ + kubectl port-forward svc/vald-mirror-gateway 8082:8081 -n vald-03 + ``` + +2. Download dataset + + Move to the working directory. + + ```bash + cd example/client/mirror + ``` + + Download [Fashion-MNIST](https://github.com/zalandoresearch/fashion-mnist), which is used as a dataset for indexing and search query. + + ```bash + wget https://ann-benchmarks.com/fashion-mnist-784-euclidean.hdf5 + ``` + +3. Run Example + + We use [example/client/mirror/main.go](https://github.com/vdaas/vald/blob/main/example/client/mirror/main.go) to run the example. + + This example will insert and index 400 vectors into the Vald cluster from the Fashion-MNIST dataset via [gRPC](https://grpc.io/). + And then, after waiting for indexing, it will request to search the nearest vector 10 times to all Vald clusters. You will get the 10 nearest neighbor vectors for each search query. + And it will request to get vectors using inserted vector ID. + + Run example codes by executing the below command. + + ```bash + go run main.go + ``` + + See [GetStarted](https://vald.vdaas.org/docs/tutorial/get-started/) for an explanation of the example code. + +## Cleanup + +Last, you can remove the deployed Vald cluster by executing the below command. + +```bash +helm uninstall vald-cluster-01 -n vald-01 && \ +helm uninstall vald-cluster-02 -n vald-02 && \ +helm uninstall vald-cluster-03 -n vald-03 +``` + +You can remove all `ValdMirrorTarget` resources (Custom Resource) with the below command. + +```bash +kubectl delete --all ValdMirrorTargets -A +``` + +You can remove all created Namespaces with the below command. + +```bash +kubectl delete namespaces vald-01 vald-02 vald-03 +``` + +## Next Steps + +Congratulation! +You completely entered the World of multiple Vald clusters! + +For more information, we recommend you check the following: + +- [Configuration](https://vald.vdaas.org/docs/user-guides/configuration/) +- [Mirror Configuration](https://vald.vdaas.org/docs/user-guides/mirroring-configuration/) +- [Network Policy](https://vald.vdaas.org/docs/user-guides/network-policy/) diff --git a/content/docs/v1.8/usecase/_index.md b/content/docs/v1.8/usecase/_index.md new file mode 100644 index 000000000..37b1a1476 --- /dev/null +++ b/content/docs/v1.8/usecase/_index.md @@ -0,0 +1,7 @@ +--- +title: "Usecase_v1.8/Usecase" +date: 2026-09-16T04:40:22Z +draft: false +weight: 0 +--- + diff --git a/content/docs/v1.8/usecase/usage-example.md b/content/docs/v1.8/usecase/usage-example.md new file mode 100644 index 000000000..3472b1070 --- /dev/null +++ b/content/docs/v1.8/usecase/usage-example.md @@ -0,0 +1,98 @@ +--- +title: "Usage Example_v1.8/Usecase" +date: 2026-09-16T04:40:21Z +draft: false +weight: 0 +description: "" +menu: + usecase: + parent: Usecase +--- + +# Use cases + +This document will introduce you to the example of what Vald can do. +Vald is a highly scalable distributed fast approximate nearest neighbor dense vector search engine, which uses [NGT](https://github.com/NGT-labs/NGT) as the core engine of Vald, and Vald manages to integrate with Kubernetes. + +You cannot generally search your unstructured data using the inverted index, like images and videos. +Applying a model like BERT or VGG can convert your unstructured data into vectors. +After converting them into vectors, you can insert them into the Vald cluster and process them in the Vald cluster. + +Here are some general use cases of Vald or vector search engines. + +## Image and video processing + + + +You can use Vald as the image/video processing engine to search the similar images/videos or analyze the image/video for your use case. + +Vald is capable of processing a huge number of images at the same time, so its case fits with your use case. + +Here are some examples of what you can do with images and videos using Vald. + +- Search by image +- Face recognition +- Product recommendation based on images +- Image/Video analysis +- Image/Video deduplication + +## Audio processing + + + +Audio processing is important for personal assistant implementation. + +Vald can act as a brain of the personal assistant function, conversation interpreter, and natural language generation. + +Here are some examples of what you can process using Vald. + +- Personal assistant +- Speech recognition +- Natural language understanding and generation + +## Text processing + + + +Using a text vectorizing model like BERT, you can process your text data in Vald. + +Here are some examples of the use case of text processing using Vald. + +- Search by text +- Product recommendation based on text +- Grammar checker +- Real-time translator + +## Data analysis + + + +Vald can process the vector data, you can analyze every data you can vectorize. + +Here are some examples of the use case of data analysis. + +- AI malware detection + + To detect the malware using Vald, you need to vectorize the binary file and insert it into Vald first. + You can analyze your binary by searching for a similar binary in Vald. + If your binary closely resembles the malware binary, you can trigger the alert for users. + +- Price optimization + + By applying the price optimization technique using Vald, you can find the most optimized price for your business. + You can apply models like GLMs to achieve it and use Vald as a machine learning engine for your business. + +- Social analysis + + To analyze the social relationship of users, you can suggest to them their related friends, page recommendations, or other use cases. + You can apply different models to analyze social data and use Vald as a recommendation engine for your business. + +## Advanced use cases + +Besides the general use case of Vald or vector search engine, Vald supports a user-defined filter that the user can customize the filter to filter the specific result. + +For example when the user chose a man's t-shirt and the recommended product is going to be searched in Vald. + +Without the filtering functionality, the women's t-shirt may be searched in Vald and displayed because women's t-shirt is similar to the men's t-shirt and it is very hard to differentiate the image of men's and women's t-shirt. + +By implementing the custom filter, you can filter only the man's t-shirt based on your criteria and needs. diff --git a/content/docs/v1.8/user-guides/_index.md b/content/docs/v1.8/user-guides/_index.md new file mode 100644 index 000000000..92ce8212b --- /dev/null +++ b/content/docs/v1.8/user-guides/_index.md @@ -0,0 +1,7 @@ +--- +title: "User Guides_v1.8/User Guides" +date: 2026-09-16T04:40:22Z +draft: false +weight: 0 +--- + diff --git a/content/docs/v1.8/user-guides/backup-configuration.md b/content/docs/v1.8/user-guides/backup-configuration.md new file mode 100644 index 000000000..c00514b9d --- /dev/null +++ b/content/docs/v1.8/user-guides/backup-configuration.md @@ -0,0 +1,322 @@ +--- +title: "Backup Configuration_v1.8/User Guides" +date: 2026-09-16T04:40:20Z +draft: false +weight: 200 +description: Applies backup feature for saving and restoring indexes +menu: + userguides: + parent: User Guides +--- + +# Backup configuration + +There are three types of options for the Vald cluster: backup, filtering, and core algorithms. + +This page describes how to enable the backup feature on your Vald cluster. + +## What is the backup + +Vald's backup function is to save the index data in each Vald Agent pod as a data file to the Persistent Volume or S3. +When the Vald Agent pod is restarted for some reason, the index state is restored from the saved index data. + +## Backup methods + +You can choose one of three types of backup methods. + +- PV (recommended) +- S3 +- PV + S3 + +Please refer to the following tables and decide which method fit for your case. + +| | PV | S3 | PV+S3 | +| :-------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------- | +| usecase | Want to use backup with low cost
Would not like to use some external storage for backup
Want to backup with highly compatible storage with Kubernetes | Want to use the same backup file with several Vald clusters
Want to access the backup files easily | Want to use backup with PV basically and access the backup files easily
Want to prevent backup file failure due to Kubernetes cluster failure | +| pros :+1: | Easy to use
Highly compatible with Kubernetes
Low latency using in-cluster network
Safety backup using Copy on Write option | Easy to access backup files
It can be shared and used by multiple clusters | The safest of these methods | +| cons :-1: | A bit hard to check backup files
Can not share backup files for several Vald clusters | Need to communicate with external network | Need to operate both storages
The most expensive way | + +## Backup configuration + +This section shows the best practice for configuring backup features with PV, S3, or PV + S3. + +Each sample configuration yaml is published on [here](https://github.com/vdaas/vald/tree/main/charts/vald/values). +Please refer it for more details. + +### General + +Regardless of the backup destination, the following Vald Agent settings must be set to enable backup. + +- `agent.ngt.index_path` is the location where those files are stored. +- `agent.ngt.in-memory-mode=false` means storing index files in the local volume. + +In addition, `agent.terminationGracePeriodSeconds` value should be long enough to ensure the backup speed. + +```yaml +agent: + ... + # We recommend setting this value long enough to ensure the backup speed of PV, since the Index is backed up at the end of the pod. + terminationGracePeriodSeconds: 3600 + ngt: + ... + index_path: "/var/ngt/index" + enable_in_memory_mode: false + ... +``` + +### Persistent Volume + +#### requirement + +You must prepare PV before deployment when using Kubernetes Persistent Volume (PV) for backup storage. +Please refer to the setup guide of the usage environment for the provisioning PV. + +For example: + +- [GKE setup PV document](https://cloud.google.com/kubernetes-engine/docs/concepts/persistent-volumes) +- [EKS storage document](https://docs.aws.amazon.com/eks/latest/userguide/storage.html) + +#### configuration + +After provisioning PV, the following parameters are needed to be set. +It shows the example for using GKE. + +```yaml +agent: + ... + persistentVolume: + # use PV flag + enabled: true + # accessMode for PV (please verify your environment). + accessMode: ReadWriteOnce + # storage class for PV (please verify your environment) + storageClass: standard + # set enough size for backup + size: 2Gi + ... + terminationGracePeriodSeconds: 3600 + ... + ngt: + ... + index_path: "/var/ngt/index" + enable_in_memory_mode: false + # copy on write function flag + enable_copy_on_write: true + ... +``` + +Each PV will be mounted on each Vald Agent Pod's `index_path`. + +You can choose `copy_on_write` (CoW) function. + +The CoW is an option to update the backup file safely. + +The backup file may be corrupted, and the Vald Agent pod may not restore from backup files when the Vald Agent pod terminates during saveIndex without CoW is not be enabled. +On the other hand, if CoW is enabled, the Vald Agent pod can restore the data from one generation ago. + +
+When CoW is enabled, PV temporarily has two backup files; new and old versions.
+So, A double storage capacity is required if CoW is enabled, e.g., when set 2Gi as size without CoW, the size should be more than 4Gi with CoW. +
+ +### S3 + +#### requirement + +Before deployment, you must provision the S3 object storage. +You can use any S3-compatible object storage. + +For example: + +- [AWS S3](https://aws.amazon.com/s3/) +- [Google Cloud Storage](https://cloud.google.com/storage/docs/) + +#### configuration + +After provisioning the object storage, the following parameters are needed to be set. +To enable the backup function with S3, the Vald Agent Sidecar should be enabled. + +```yaml +agent: + ... + terminationGracePeriodSeconds: 3600 + ... + ngt: + ... + index_path: "/var/ngt/index" + enable_in_memory_mode: false + ... + sidecar: + enabled: true + # run sidecar with initContainerMode or not + initContainerEnabled: true + # This is the Amazon S3 settings. + # Please change it according to your environment. + config: + blob_storage: + # storage type (default: s3) + storage_type: "s3" + # your bucket name + bucket: "vald" + s3: + region: "us-central1" + # If you enable sidecar, the following environment variables will be created automatically by default values. + # So, please create the 'aws-secret' resource before deploying. + # env: + # - name: AWS_ACCESS_KEY + # valueFrom: + # secretKeyRef: + # name: aws-secret + # key: access-key + # - name: AWS_SECRET_ACCESS_KEY + # valueFrom: + # secretKeyRef: + # name: aws-secret + # key: secret-access-key +``` + +The Vald Agent Sidecar needs an access key and a secret access key to communicate with your object storage. +Before applying the helm chart, register each value in Kubernetes secrets with the following commands. + +```bash +kubectl create secret -n aws-secret --access-key= --secret-access-key= +``` + +### Persistent Volume and S3 + +You can use both PV and S3 at the same time. +Please refer to the before sections for provisioning storages. + +```yaml +agent: + ... + terminationGracePeriodSeconds: 3600 + ... + persistentVolume: + # use PV flag + enabled: true + # accessMode for PV (please verify your environment) + accessMode: ReadWriteOnce + # storage class for PV (please verify your environment) + storageClass: standard + # set enough size for backup + size: 2Gi + ngt: + ... + enable_in_memory_mode: false + # copy on write function flag + enable_copy_on_write: true + ... + sidecar: + enabled: true + # run sidecar with initContainerMode or not. If true, it allows restoration. + initContainerEnabled: true + # This is the Amazon S3 settings. + # Please change it according to your environment. + config: + blob_storage: + # storage type (default: s3) + storage_type: "s3" + # your bucket name + bucket: "vald" + s3: + region: "us-central1" + # If you enable sidecar, the following environment variables will be created automatically by default values. + # So, please create the 'aws-secret' resource before deploying. + # env: + # - name: AWS_ACCESS_KEY + # valueFrom: + # secretKeyRef: + # name: aws-secret + # key: access-key + # - name: AWS_SECRET_ACCESS_KEY + # valueFrom: + # secretKeyRef: + # name: aws-secret + # key: secret-access-key + ... +``` + +## Restore + +Restoring from the backup file runs on start Pod when the config is set correctly, and the backup file exists. + +### PV + +In using the PV case, restoration starts when Pod starts. + +If the configuration is correct, the backup file will be automatically mounted, loaded, and indexed when the Pod starts. + +### S3 + +In using the S3 case, restoration runs only `initContainerMode`. +To enable restoration, you have to set `sidecar.initContainerMode` as `true`. + +Agent Sidecar tries to get the backup file from S3, unpacks it, and starts indexing. + +### PV + S3 + +In using both the PV and S3 case, the backup file used for restoration will prioritize the file on PV. +If the backup file does not exist on the PV, the backup file will be retrieved from S3 via the Vald Agent Sidecar and restored. + +## Broken index backup + +If a backup file of an index is corrupted for some reason, Vald agent fails to load the index file, and the index file is then identified as a broken index. + +> Causes of broken index could be agent crash during save index operation, partial storage corruption, etc. + +When an index is broken, the default behavior is to discard it and continue running the Pod. This is useful for saving storage space, but sometimes you may need to inspect the contents of a broken index at a later time. By enabling the `broken index backup` feature, a backup is created without deleting the broken index before running the Pod. This feature can help you investigate the cause of index corruption at a later time. + +### Settings + +To enable this feature, set the `agent.ngt.broken_index_history_limit` setting to at least 1 (default: 0). The system stores backups of broken indexes up to the number of generations specified by this variable. If a backup of a broken index is needed that goes beyond this value, the system will delete the oldest backup. + +``` +agent: + ngt: + ... + broken_index_history_limit: 3 + ... +``` + +### Backup location + +The backup is stored under `${index_path}/broken`. Each directory name represents the Unix nanosecond when an attempt was made to read the broken index. + +``` +${index_path}/ + origin/ + ngt-meta.kvsdb + ngt-timestamp.kvsdb + metadata.json + prf + grp + tre + obj + broken/ + 1611271735938403848/ + ngt-meta.kvsdb + ... + 1611271749583028942/ + ngt-meta.kvsdb + ... + 1611271759849304593/ + ngt-meta.kvsdb + ... +``` + +### Restore + +#### CoW: disabled + +If an index file exists under `${index_path}/origin`, restore is attempted based on that index file. If the restore fails, the index file is backed up as a broken index. The agent starts in its initial state. + +#### CoW: enabled + +If an index file exists under `${index_path}/origin`, restore is attempted based on that index file. If the restore fails, `${index_path}/origin` is backed up as a broken index at that point. Then, restore is attempted based on the index file in `${index_path}/backup` (one generation older index file). If the restore fails again, the agent starts in its initial state. + +### Metrics + +The number of generations of broken indexes currently stored can be obtained as a metric `agent_core_ngt_broken_index_store_count`. + +Reference: [vald/k8s/metrics/grafana/dashboards/01-vald-agent.yaml](https://github.com/vdaas/vald/blob/main/k8s/metrics/grafana/dashboards/01-vald-agent.yaml) diff --git a/content/docs/v1.8/user-guides/capacity-planning.md b/content/docs/v1.8/user-guides/capacity-planning.md new file mode 100644 index 000000000..aa60ee29c --- /dev/null +++ b/content/docs/v1.8/user-guides/capacity-planning.md @@ -0,0 +1,200 @@ +--- +title: "Capacity Planning_v1.8/User Guides" +date: 2026-09-16T04:40:21Z +draft: false +weight: 900 +description: Estimates how Kubernetes resources will be required +menu: + userguides: + parent: User Guides +--- + +# Capacity Planning + +## What is capacity planning for the Vald cluster? + +Capacity planning is essential before deploying the Vald cluster to the cloud service. +There are three viewpoints: Vald cluster view, Kubernetes view, and Component view. +Let's see each view. + +
+When introducing production, we recommend that you actually measure how many resources are required for verification. +
+ +## Vald cluster view + +The essential point at the Vald cluster view is the hardware specification, especially RAM. +The Vald cluster, especially Vald Agent components, requires much RAM capacity because the vector index is stored in memory. + +The minimum required memory for each vector (bit) is: + +```bash +// minimum required bits of vector +{ oid (64bit) + timestamp (64bit) + uuid (user defined) } * 2 + { dimension * 64 } + { the creation edge size + the search edge size } * 8 +``` + +Considering the `index size` and `index_replica`, it is easy to figure out the minimum required RAM capacity by the following formula. + +```bash +{ minimum required bits of vector } * { the index size } * { index_replica } +``` + +For example, you want to insert 1 million vectors with 900 dimensions with 32 byte (256 bit) UUID, the index replica is 3, `creation edge size` is 20, and `search edge size` is 10, the minimum required RAM capacity is: + +```bash +{(64 + 64 + 256) × 2 + (900 × 64) + (20 + 10) × 8 } × 1,000,000 × 3 = 175,824,000,000 (bit) = 21.978 (GB) +``` + +It is just the minimum required RAM for indexing. +Considering the margin of RAM capacity, the minimum RAM capacity should be less than 60% of the actual RAM capacity. +Therefore, the actual minimum RAM capacity will be: + +```bash +175,824,000,000 (bit) / 0.6 = 293,040,000,000 (bit) = 36.63 (GB) +``` + +
+In the production usage, memory usage may be not enough in the minimum required RAM.
+Because for example, there are a noisy problem, high memory usage for createIndex (indexing on memory), high traffic needs more memory, etc. +
+ +## Kubernetes cluster view + +### Pod priority & QoS + +When the Node capacity (e.g., RAM, CPU) reaches the limit, Kubernetes will decide to kill some Pods according to QoS and Pod priority. +Kubernetes performs pod scheduling with pods Priority Class as the priority and QoS as the second priority. + +#### Pod priority + +Pod priority has the integer value, and the higher value, the higher priority. + +Each Vald component has the default priority value: + +- Agent: 1000000000 +- Discoverer: 1000000 +- Filter Gateway: 1000000 +- LB Gateway: 1000000 +- Index Manager: 1000000 + +Therefore, the order of priority is as follows: + +```bash +Agent > Discoverer = Filter Gateway = LB Gateway = Index Manger +``` + +Those values will be helpful when the Pods other than the Vald component are in the same Node. + +It is easy to change by editing your `values.yaml`. + +```yaml +# e.g. LB Gateway podPriority settings. +... + gateway: + lb: + ... + podPriority: + enabled: true + value: {new values} + ... +``` + +#### QoS + +QoS value can be either Guaranteed, Burstable, or BestEffort. +And, QoS priority is higher in the order of Guaranteed, Burstable, BestEffort, and Kubernetes will kill Pods in ascending order of importance. + +Resource request and limit determine QoS. + +The below table shows the condition for each QoS. + +| QoS | request CPU | request Memory | limit CPU | request Memory | Sup. | +| :--------: | :-------------: | :-------------: | :-------------: | :-------------: | :---------------------------------- | +| Guaranteed | :o: | :o: | :o: | :o: | All settings are required. | +| Burstable | :o: (:warning:) | :o: (:warning:) | :o: (:warning:) | :o: (:warning:) | One to three settings are required. | +| BestEffort | :x: | :x: | :x: | :x: | No setting is required. | + +Vald requires many RAM resources because of on-memory indexing, so we highly recommend that you do not specify a limit, especially for the Vald Agent. +In this case, QoS will be Burstable. + +**Throttling** + +The CPU throttling affects the pod performance. + +If it occurs, the Vald cluster operator must consider each component's CPU resource request and limit. +It is easy to change by editing the `values.yaml` file and applying it. + +```yaml +# e.g. LB Gateway resources settings. +... + gateway: + lb: + ... + resources: + requests: + cpu: 200m + memory: 150Mi + limits: + cpu: 2000m + memory: 700Mi + ... +``` + +
+Please take care of pod priority and QoS. +
+ +### Node & Pod affinity + +Kubernetes scheduler often applies Pods on Node based on resource availability. + +In production usage, other components sometimes work on the Kubernetes cluster where the Vald cluster runs. +Depending on the situation, you may want to deploy to a different Node: e.g., when running a machine learning component that requires high memory on an independent Node. + +In this situation, we recommend you to set the affinity/anti-affinity configuration for each Vald component. +It is easy to change by editing each component setting on your `values.yaml`. + +
+The affinity setting for Vald Agent is significant for the Vald cluster.
+Please DO NOT remove the default settings. +
+ +```yaml +# e.g. Agent's affinity settings +... + agent: + ... + affinity: + nodeAffinity: + preferredDuringSchedulingIgnoredDuringExecution: [] + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: [] + podAffinity: + preferredDuringSchedulingIgnoredDuringExecution: [] + requiredDuringSchedulingIgnoredDuringExecution: [] + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 900 + podAffinityTerm: + topologyKey: kubernetes.io/hostname + labelSelector: + matchExpressions: + - key: app + operator: In + values: + - vald-agent-ngt + requiredDuringSchedulingIgnoredDuringExecution: [] + ... +``` + +For more information about Kubernetes affinity, please refer to [here](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) + +## Component view + +Depending on the customization of each component for each user, there are some points to be aware of. + +### Index Manager + +If the `saveIndex` is executed frequently, the backup data per unit time will increase, which consumes bandwidth. + +Similarly, as the saveIndex concurrency increases, the backup data per unit time increases. diff --git a/content/docs/v1.8/user-guides/client-api-config.md b/content/docs/v1.8/user-guides/client-api-config.md new file mode 100644 index 000000000..db2ec9b62 --- /dev/null +++ b/content/docs/v1.8/user-guides/client-api-config.md @@ -0,0 +1,708 @@ +--- +title: "Client Api Config_v1.8/User Guides" +date: 2026-09-16T04:40:20Z +draft: false +weight: 1000 +description: Shows more detailed explanation about APIs configuration +menu: + userguides: + parent: User Guides +--- + +# Client API Config + +Vald provides client libraries to make it easier to access different API provided by Vald, including indexing vectors, searching approximate nearest neighbor vectors, updating vectors, and removing indexed vectors. +Each request allows setting request configuration for user demands. +Please select the section you need. + +
+Please read the API documentation for more API service details. +
+ +## Insert Service + +The `Insert` service allows users to insert new vector(s) into the Vald cluster. +It requires the vector, its ID (specific ID for the vector), and optional configuration. + +### Configuration + +```rpc +// Represent insert configuration. +message Config { + // Check whether or not the same set of vector and ID is already inserted. + bool skip_strict_exist_check = 1; + // Configuration for filters if your Vald cluster uses filters. + Filter.Config filters = 2; + // The timestamp when the vector was inserted. + int64 timestamp = 3; +} +``` + +
Insert Configuration Sample (Go)
+ +```go +package main + +import ( + "context" + "time" + + "github.com/vdaas/vald-client-go/v1/payload" + "github.com/vdaas/vald-client-go/v1/vald" + "google.golang.org/grpc" +) + +func main() { + // Create connection + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + target := "localhost:8080" + conn, err := grpc.NewClient(target) + if err != nil { + panic(err) + } + // Init Vald client + client := vald.NewValdClient(conn) + + // Insert sample + location, err := client.Insert(ctx, &payload.Insert_Request{ + // Vector information (mandatory) + Vector: &payload.Object_Vector{ + // Set the vector + Vector: []float32{0, 1, 2}, + // Set the specific ID for the vector + // The ID must not be indexed. + Id: "sample", + }, + // Insert configuration (optional) + Config: &payload.Insert_Config{ + SkipStrictExistCheck: false, + Filters: &payload.Filter_Config{ + Targets: []*payload.Filter_Target{ + { + Host: "vald-ingress-filter", + Port: 8081, + }, + }, + }, + Timestamp: time.Now().UnixMilli(), + }, + }) + if err != nil { + panic(err) + } + ... +} +``` + +
+ +#### skip_strict_exist_check + +`skip_strict_exist_check` is a flag to skip checking whether the same set of the vector and ID is already inserted or not. +If it is set as `true`, the checking function will be skipped.
+The default value is `false`. + +#### filters + +`filters` is the configuration when using filter functions. +In the `Insert` section, it is common to use ingress filtering when applying the filter's configuration. + +The detailed configuration is following. + +```rpc +// Filter related messages. +message Filter { + + // Represent the target filter server. + message Target { + // The target filter component hostname. + string host = 1; + // The target filter component port. + uint32 port = 2; + } + + // Represent filter configuration. + message Config { + // Represent the filter target configuration. + repeated Target targets = 1; + } +} +``` + +#### timestamp + +`timestamp` is the timestamp when the vector is inserted. +When `timestamp` is not set, the current time will be used. + +## Update Service + +The `Update` service allows users to update vector(s) that already exists in the Vald cluster. +It requires the new vector, its ID (the target ID already indexed), and optional configuration. + +### Configuration + +```rpc +// Represent update configuration. +message Config { + // Check whether or not the same set of vector and ID is already inserted. + bool skip_strict_exist_check = 1; + // Configuration for filters if your Vald cluster uses filters. + Filter.Config filters = 2; + // The timestamp when the vector was inserted. + int64 timestamp = 3; +} +``` + +
Update Configuration Sample (Go)
+ +```go +package main + +import ( + "context" + "time" + + "github.com/vdaas/vald-client-go/v1/payload" + "github.com/vdaas/vald-client-go/v1/vald" + "google.golang.org/grpc" +) + +func example() { + // Create connection + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + target := "localhost:8080" + conn, err := grpc.NewClient(target) + if err != nil { + panic(err) + } + // Init Vald client + client := vald.NewValdClient(conn) + + // Update sample + location, err := client.Update(ctx, &payload.Update_Request{ + // Vector information (mandatory) + Vector: &payload.Object_Vector{ + // Set the vector + Vector: []float32{0, 1, 2}, + // Set the specific ID for the vector + // The ID must be already indexed. + Id: "sample", + }, + // Update configuration (optional) + Config: &payload.Update_Config{ + SkipStrictExistCheck: false, + Filters: &payload.Filter_Config{ + Targets: []*payload.Filter_Target{ + { + Host: "vald-ingress-filter", + Port: 8081, + }, + }, + }, + Timestamp: time.Now().UnixMilli(), + }, + }) + if err != nil { + panic(err) + } + ... +} +``` + +
+ +#### skip_strict_exist_check + +`skip_strict_exist_check` (default value is `false`) is a flag to skip checking whether the same set of the vector and ID is already inserted or not. +If it is set as `true`, the checking function will be skipped.
+ +When `skip_strict_exist_check` is `false`, the following checking steps will run in the update process: + +1. Check whether the set of ID and vector is already inserted. + If there is no data, the update process ends with returning the `NOT_FOUND` error. +1. After passing the step.1, check whether the request vector is the same as the indexed vector. + If it is the same, the update process ends with returning the `ALREADY_EXIST` error. + +The update process will continue if all of the above steps have been passed. + +#### filters + +`filters` is the configuration when using filter functions. +In the `Update` section, it is common to use ingress filtering when applying the filter's configuration.``` + +The detailed configuration is following. + +```rpc +// Filter related messages. +message Filter { + + // Represent the target filter server. + message Target { + // The target filter component hostname. + string host = 1; + // The target filter component port. + uint32 port = 2; + } + + // Represent filter configuration. + message Config { + // Represent the filter target configuration. + repeated Target targets = 1; + } +} +``` + +#### timestamp + +`timestamp` is the timestamp when the vector is updated. +When `timestamp` is not set, the current time will be used. + +## Upsert Service + +The `Upsert` service allows the user to update existing vectors in the Vald cluster or insert new vector(s) if the request vector is not indexed. +It requires the vector, its ID (specific ID for the vector), and optional configuration. + +### Configuration + +```rpc +// Represent upsert configuration. +message Config { + // Check whether or not the same set of vector and ID is already inserted. + bool skip_strict_exist_check = 1; + // Configuration for filters if your Vald cluster uses filters. + Filter.Config filters = 2; + // The timestamp when the vector was inserted. + int64 timestamp = 3; +} +``` + +
Upsert Configuration Sample (Go)
+ +```go +package main + +import ( + "context" + "time" + + "github.com/vdaas/vald-client-go/v1/payload" + "github.com/vdaas/vald-client-go/v1/vald" + "google.golang.org/grpc" +) + +func example() { + // Create connection + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + target := "localhost:8080" + conn, err := grpc.NewClient(target) + if err != nil { + panic(err) + } + // Init Vald client + client := vald.NewValdClient(conn) + + // Upsert sample + location, err := client.Upsert(ctx, &payload.Upsert_Request{ + // Vector information (mandatory) + Vector: &payload.Object_Vector{ + // Set the vector + Vector: []float32{0, 1, 2}, + // Set the specific ID for the vector + Id: "sample", + }, + // Upsert configuration (optional) + Config: &payload.Upsert_Config{ + SkipStrictExistCheck: false, + Filters: &payload.Filter_Config{ + Targets: []*payload.Filter_Target{ + { + Host: "vald-ingress-filter", + Port: 8081, + }, + }, + }, + Timestamp: time.Now().UnixMilli(), + }, + }) + if err != nil { + panic(err) + } + ... +} +``` + +
+ +#### skip_strict_exist_check + +`skip_strict_exist_check` (default value is `false`) is a flag to skip checking whether the same set of the vector and ID is already inserted or not. +If it is set as `false`, the checking function will be skipped.
+ +When `skip_strict_exist_check` is `false`, the following checking steps will run in the upsert process: + +1. Check whether the set of (ID and vector) is already inserted or not. + The request ID and vector will be inserted if there is no data. +1. After passing the step.1, check whether the request vector is the same as the indexed vector. + If it is the same, the upsert process ends with returning the `ALREADY_EXIST` error. + +The upsert process will continue if all of the above steps have been passed. + +#### filters + +`filters` is the configuration when using filter functions. +In the `Upsert` section, it is common to use ingress filtering when applying the filter's configuration. + +The detailed configuration is following. + +```rpc +// Filter related messages. +message Filter { + + // Represent the target filter server. + message Target { + // The target filter component hostname. + string host = 1; + // The target filter component port. + uint32 port = 2; + } + + // Represent filter configuration. + message Config { + // Represent the filter target configuration. + repeated Target targets = 1; + } +} +``` + +#### timestamp + +`timestamp` is the timestamp when the vector is inserted or updated. +When `timestamp` is not set, the current time will be used. + +## Search Service + +Vald provides four types of search services. + +1. Search + - `Search` is the `ANN(Approximate Nearest Neighbor)` search with query vector. + It is a fast search even though large dimension vector. + The search duration is quick but less accurate than `LinearSearch`. + The search algorithm depends on each core algorithm. + +1. SearchById + - `SearchById` is the `ANN(Approximate Nearest Neighbor)` search with the stored vector's ID. + The ID should already exist in the NGT indexes before the search process. + The search algorithm is the same as `Search`. + +1. LinearSearch + - `LinearSearch` is the most general search algorithm with a query vector. + It searches all indexed vectors and calculates the distance between the query. + It returns accurate results but requires more processing time than `Search` (ANN search) as it needs to calculate from all indexed vectors instead of only a subset of indexed vectors. + +1. LinearSearchById + - `LinearSearchById` is the primary search algorithm with the vector's ID. + The ID should already exist in the NGT indexes before the search process. + The search algorithm is the same as `LinearSearch`. + +
+Linear Search service is available from Vald v1.4 or later. +
+ +For more details, please refer to [the Search API document](/docs/v1.8/api/search). + +### Configuration + +```rpc +// Represent search configuration. +message Config { + // Unique request ID. + string request_id = 1; + // Maximum number of results to be returned. + uint32 num = 2 [ (validate.rules).uint32.gte = 1 ]; + // Search radius. + float radius = 3; + // Search coefficient. + float epsilon = 4; + // Search timeout in nanoseconds. + int64 timeout = 5; + // Ingress filter configurations. + Filter.Config ingress_filters = 6; + // Egress filter configurations. + Filter.Config egress_filters = 7; + // Minimum number of results to be returned. + uint32 min_num = 8 [ (validate.rules).uint32.gte = 0 ]; + // Aggregation Algorithm + AggregationAlgorithm aggregation_algorithm = 9; +} + +// AggregationAlgorithm is enum of each aggregation algorithms +enum AggregationAlgorithm { + Unknown = 0; + ConcurrentQueue = 1; + SortSlice = 2; + SortPoolSlice = 3; + PairingHeap = 4; +} +``` + +`AggregationAlgorithm` is available from `v1.7.6`. +You can select 1 algorithm from 4 algorithms (Unknown is same as `ConcurrentQueue`) based on desired search performance. +This table shows the description and recommended situation for each algorithm. + +| Algorithm | Description | Recommended Situation | +| :---------------- | :---------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------ | +| `ConcurrentQueue` | Traditional Vald proprietary aggregation algorithm | Good performance when asynchronous aggregation from a large number of Agents and a huge number of threads. | +| `SortSlice` | Aggregate and rerank all agent results together to eliminate duplicates | Good performance when there are small top-K and small number of Agents. | +| `SortPoolSlice` | Memory reduce version of `SortSlice` | Same as `SortSlice`. | +| `PairingHeap` | Aggregation method using PairingHeap that supports Multi threads. | Faster under the medium scale top-K and the medium number of Agents, which is difficult to achive performance with `SortSlice`. | + +
Search Configuration Sample (Go)
+ +```go +package main + +import ( + "context" + "time" + + "github.com/vdaas/vald-client-go/v1/payload" + "github.com/vdaas/vald-client-go/v1/vald" + "google.golang.org/grpc" +) + +func main() { + // Create connection + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + target := "localhost:8080" + conn, err := grpc.NewClient(target) + if err != nil { + panic(err) + } + // Init Vald client + client := vald.NewValdClient(conn) + + // Search sample + res, err := client.Search(ctx, &payload.Search_Request{ + // Query vector + Vector: []float32{0, 1, 2}, + // Search configuration + Config: &payload.Search_Config{ + RequestId: "unique-request-id", + // The number of the search result to be returned. + Num: 10, + // The minimum number of the search result to be returned. + // It prevents the timeout error when the number of results does NOT satisfy Num. + MinNum: 5, + // The space of search candidate radius for NN vectors. + Radius: -1, + // Epsilon determines how much to expand from the search candidate radius. + Epsilon: 0.1, + // Search timeout setting. + Timeout: 100000000, + IngressFilters: &payload.Filter_Config{ + Targets: []*payload.Filter_Target{ + { + Host: "vald-ingress-filter", + Port: 8081, + }, + }, + }, + EgressFilters: &payload.Filter_Config{ + Targets: []*payload.Filter_Target{ + { + Host: "vald-egress-filter", + Port: 8081, + }, + }, + }, + AggregationAlgorithm: payload.Search_PairingHeap, + }, + }) + + if err != nil { + panic(err) + } + ... +} +``` + +
+ +#### request_id + +`request_id` is a unique request ID. +It is **NOT** indexed vector's ID. +Users can use it for, e.g., the error handling process. + +#### num + +`num` is the maximum number of search results you'd like to get. +`num` should be a positive integer. + +#### radius + +`radius`, the specific parameter for NGT, specifies the search range centered on the query vector in terms of the radius of a sphere. +The number of search target vectors increases along with the radius being large. +There is a trade-off between accuracy and search speed. +It is hard to set it depending on the dataset in many cases. + +The default value is infinity. +When setting a negative number as `radius`, `NGT` applies the radius as infinity. + +
+NGT will self-update the radius during the search process. +
+ +#### epsilon + +`epsilon`, the specific parameter for NGT, specifies the search range's magnification coefficient (epsilon). +NGT will use `radius*(1+epsilon)` as the search range. +The number of search target vectors increases along with the epsilon being large. + +The default value is 0.1, and it may work in most cases. +However, the appropriate value may vary depending on the dataset. +While it is desirable to adjust this value within 0 - 0.3, it can also set a negative value (over -1). + +#### ingress_filters + +`ingress_filters` is required when using the ingress filter component. +It requires the ingress filter component's hostname and port. + +```rpc +// Filter related messages. +message Filter { + + // Represent the target filter server. + message Target { + // The target filter component hostname. + string host = 1; + // The target filter component port. + uint32 port = 2; + } + + // Represent filter configuration. + message Config { + // Represent the filter target configuration. + repeated Target targets = 1; + } +} +``` + +#### egress_filters + +`egress_filters` is required when using the egress filter component. +It requires the egress filter component's hostname and port. + +```rpc +// Filter related messages. +message Filter { + + // Represent the target filter server. + message Target { + // The target filter component hostname. + string host = 1; + // The target filter component port. + uint32 port = 2; + } + + // Represent filter configuration. + message Config { + // Represent the filter target configuration. + repeated Target targets = 1; + } +} +``` + +#### min_num + +`min_num` is the minimum number of search results you'd like to get at least. +It helps you avoid the timeout error when the search process requires more time. +`min_num` should be a positive integer smaller than `num`. + +## Remove Service + +The `Remove` service allows the user to delete indexed vectors from the Vald cluster. +Removing the vector requires the vector's ID and optional configuration. + +For more details, please refer to [the Remove API document](/docs/v1.8/api/remove). + +### Configuration + +```rpc +// Represent remove configuration. +message Config { + // Check whether or not the same set of ID and vector is already inserted. + bool skip_strict_exist_check = 1; + // The timestamp when the vector was removed. + int64 timestamp = 3; +} +``` + +
Remove Configuration Sample (Go)
+ +```go +package main + +import ( + "context" + "time" + + "github.com/vdaas/vald-client-go/v1/payload" + "github.com/vdaas/vald-client-go/v1/vald" + "google.golang.org/grpc" +) + +func example() { + // Create connection + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + target := "localhost:8080" + conn, err := grpc.NewClient(target) + if err != nil { + panic(err) + } + // Init Vald client + client := vald.NewValdClient(conn) + + // Remove sample + location, err := client.Remove(ctx, &payload.Remove_Request{ + // Vector ID (mandatory) + // The ID must be already indexed. + Id: &payload.Object_ID{ + Id: "sample", + }, + // Remove configuration (optional) + Config: &payload.Remove_Config{ + SkipStrictExistCheck: false, + Timestamp: time.Now().UnixMilli(), + }, + }) + if err != nil { + panic(err) + } + ... +} +``` + +
+ +### skip_strict_exist_check + +`skip_strict_exist_check` (default value is `false`) is a flag to skip checking whether the same set of the vector and ID is already inserted or not. +If it is set as `true`, the checking function will be skipped.
+ +When `skip_strict_exist_check` is `false`, the following checking step will run in the removing process: + +1. Check whether the set of (ID and vector) is already inserted. + If there is no data, the removal process ends with returning the `NOT_FOUND` error. + +The removal process will continue if the above step has been passed. + +### timestamp + +`timestamp` is the timestamp when the vector is removed. +When `timestamp` is not set, the current time will be used. diff --git a/content/docs/v1.8/user-guides/cluster-role-binding.md b/content/docs/v1.8/user-guides/cluster-role-binding.md new file mode 100644 index 000000000..25913b49f --- /dev/null +++ b/content/docs/v1.8/user-guides/cluster-role-binding.md @@ -0,0 +1,261 @@ +--- +title: "Cluster Role Binding_v1.8/User Guides" +date: 2026-09-16T04:40:21Z +draft: false +weight: 500 +description: Configures cluster role for Vald cluster +menu: + userguides: + parent: User Guides +--- + +# Cluster Role Configuration + +The [cluster role](https://kubernetes.io/docs/reference/access-authn-authz/rbac/) feature provided by Kubernetes contains rules that represent a set of permission to grant access to a specific target depending on the binding rule. + +This page describes why we need a cluster role for the Vald cluster and how to configure it. + +## What are cluster role and cluster role binding for the Vald cluster? + +Vald applies the distributed index system across the Kubernetes cluster depending on the resource usage of the Kubernetes Node, it requires configuration to grant permission to a specific role to retrieve cluster information on Kubernetes. + +By default, the cluster role configurations are deployed automatically when using Helm. + +The following manifest will be deployed by default. + +- [clusterrole.yaml](https://github.com/vdaas/vald/blob/main/k8s/discoverer/clusterrole.yaml) +- [clusterrolebinding.yaml](https://github.com/vdaas/vald/blob/main/k8s/discoverer/clusterrolebinding.yaml) + +These configurations allow the service account `discoverer`, which is for the Vald Discoverer components, to access different resources in the Kubernetes cluster. + +This service account allows [Vald Discoverer](/docs/v1.8/overview/component/discoverer) to retrieve Node and Pod resource usage from [kube-apiserver](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/), and share it to other components in the Vald cluster. + +For example, [Vald LB Gateway](/docs/v1.8/overview/component/lb-gateway) will control which Vald Agent to insert based on the Node and Pod resource usage retrieved by Vald Discoverer. + +If you are interested, please refer to the [insert data flow](/docs/v1.8/overview/data-flow#insert) for more detail. + +## Configuration for Vald Discoverer + +As described in the above section, Vald Discoverer requires configuration on cluster role and cluster role binding to retrieve Node and Pod information from the Kubernetes Cluster. + +In this section, we will describe how to configure it and how to customize these configurations. + +### Cluster role configuration for Vald Discoverer + +By looking at the [cluster role configuration](https://github.com/vdaas/vald/blob/main/k8s/discoverer/clusterrole.yaml), the access right of the following resources are granted to the cluster role `discoverer`. + +```yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: discoverer +--- +rules: + - apiGroups: + - apps + resources: + - replicasets + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - nodes + - nodes/proxy + - services + - endpoints + - pods + verbs: + - get + - list + - watch + - nonResourceURLs: + - /metrics + verbs: + - get + - apiGroups: + - "metrics.k8s.io" + resources: + - nodes + - pods + verbs: + - get + - list +``` + +All of these rules are required to retrieve Node and Pod resource usage from [kube-apiserver](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/) and also used to discover new Vald Agent Pods or Nodes created on the cluster. + +### Cluster role binding configuration for Vald Discoverer + +The cluster role binding configuration binds the cluster role `discoverer` described in the previous section to the service account `vald` according to the [configuration file](https://github.com/vdaas/vald/blob/main/k8s/discoverer/clusterrolebinding.yaml). + +```yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: discoverer + ... +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: discoverer +subjects: + - kind: ServiceAccount + name: vald + namespace: default +``` + +When the role binds to the service account, the access right of the role will be granted to the service account. +In this case, all the access rights of the role `discoverer` will be granted to the service account `vald`. + +The service account `vald` is required for [Vald Discoverer](https://github.com/vdaas/vald/blob/main/k8s/discoverer/deployment.yaml#L155) to retrieve the required information to operate the Vald cluster. + +For more information about Vald Discoverer, please refer [here](/docs/v1.8/overview/component/discoverer). + +## Customize cluster role and cluster role binding configuration on Helm chart for Vald Discoverer + +To customize the cluster role configuration on the Helm chart for Vald Discoverer, you may need to change the `discoverer.clusterRole` configuration on the Helm chart file. The cluster role configurations are enabled by default. + +```yaml +discoverer: +--- +clusterRole: + # discoverer.clusterRole.enabled -- creates clusterRole resource + enabled: true + # discoverer.clusterRole.name -- name of clusterRole + name: discoverer +clusterRoleBinding: + # discoverer.clusterRoleBinding.enabled -- creates clusterRoleBinding resource + enabled: true + # discoverer.clusterRoleBinding.name -- name of clusterRoleBinding + name: discoverer +serviceAccount: + # discoverer.serviceAccount.enabled -- creates service account + enabled: true + # discoverer.serviceAccount.name -- name of service account + name: vald +``` + +
+If you disable these configurations, the Vald Discoverer will not work, and the Vald cluster will not be functional. +
+ +If you want to modify or disable these configurations, you need to grant the [cluster role configuration](https://github.com/vdaas/vald/blob/main/k8s/discoverer/clusterrole.yaml) and bind it to the Vald Discoverer to retrieve required information to operate the Vald cluster. + +## Configuration for Vald Mirror Gateway + +The Vald Mirror Gateway requires configuration on cluster role and cluster role binding to create/update/retrive [ValdMirrorTarget](https://vald.vdaas.org/docs/user-guides/mirroring-configuration/) resource from the Kubernetes Cluster. + +In this section, we will describe how to configure it and how to customize these configurations. + +### Cluster role configuration for Vald Mirror Gateway + +By looking at the [cluster role configuration](https://github.com/vdaas/vald/blob/main/k8s/gateway/mirror/clusterrole.yaml), the access right of the following resources are granted to the cluster role `gateway-mirror`. + +```yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: gateway-mirror +--- +rules: + - apiGroups: + - vald.vdaas.org + resources: + - valdmirrortargets + verbs: + - create + - update + - delete + - get + - list + - watch + - patch + - apiGroups: + - vald.vdaas.org + resources: + - valdmirrortargets/status + verbs: + - create + - update + - get + - list + - patch + - apiGroups: + - vald.vdaas.org + resources: + - valdmirrortargets/finalizers + verbs: + - update +``` + +### Cluster role binding configuration for Vald Mirror Gateway + +The cluster role binding configuration binds the cluster role `gateway-mirror` described in the previous section to the service account `gateway-mirror` according to the [configuration file](https://github.com/vdaas/vald/blob/main/k8s/gateway/mirror/clusterrolebinding.yaml). + +```yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: gateway-mirror + ... +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: gateway-mirror +subjects: + - kind: ServiceAccount + name: gateway-mirror + namespace: default +``` + +When the role binds to the service account, the access right of the role will be granted to the service account. +In this case, all the access rights of the role `gateway-mirror` will be granted to the service account `gateway-mirror`. + +### Customize cluster role and cluster role binding configuration on Helm chart for Vald Mirror Gateway + +To customize the cluster role configuration on the Helm chart for Vald Mirror Gateway, you may need to change the `gateway.mirror.clusterRole` configuration on the Helm chart file. The cluster role configurations are enabled by default. + +```yaml +gateway: + mirror: + --- + clusterRole: + # gateway.mirror.clusterRole.enabled -- creates clusterRole resource + enabled: true + # gateway.mirror.clusterRole.name -- name of clusterRole + name: gateway-mirror + clusterRoleBinding: + # gateway.mirror.clusterRoleBinding.enabled -- creates clusterRoleBinding resource + enabled: true + # gateway.mirror..clusterRoleBinding.name -- name of clusterRoleBinding + name: gateway-mirror + serviceAccount: + # gateway.mirror.serviceAccount.enabled -- creates service account + enabled: true + # gateway.mirror.serviceAccount.name -- name of service account + name: gateway-mirror +``` + +
+If you disable these configurations, the Vald Mirror Gateway will not work properly. +
+ +## Customize cluster role configuration on Cloud Providers + +Please refer to the official guidelines to configure cluster role configuration for your cloud provider, and configure the service account name for Vald Discoverer. + +For example: + +- [Amazon EKS](https://docs.aws.amazon.com/eks/latest/userguide/add-user-role.html) +- [GKE](https://cloud.google.com/kubernetes-engine/docs/how-to/role-based-access-control) + +For other cloud providers, you may need to find the related document on their official website, or you can enable the cluster role and the cluster role binding configurations on the Helm chart. + +## Related Documents + +- [Vald Discoverer](/docs/v1.8/overview/component/discoverer) +- [Data Flow](/docs/v1.8/overview/data-flow) +- [Using RBAC Authorization (External Site)](https://kubernetes.io/docs/reference/access-authn-authz/rbac/) diff --git a/content/docs/v1.8/user-guides/configuration.md b/content/docs/v1.8/user-guides/configuration.md new file mode 100644 index 000000000..569590485 --- /dev/null +++ b/content/docs/v1.8/user-guides/configuration.md @@ -0,0 +1,422 @@ +--- +title: "Configuration_v1.8/User Guides" +date: 2026-09-16T04:40:21Z +draft: false +weight: 100 +description: Applies basic configuration for running Vald cluster +menu: + userguides: + parent: User Guides +--- + +# Configurations + +This page introduces best practices for setting up values for the Vald Helm Chart. + +Before reading, please read the overview of Vald Helm Chart in [its README][vald-helm-chart]. + +
+This page shows the notable fields in Vald Helm Chart.
+It is highly recommended to verify before deployment. +
+ +## General + +### Specify image tag + +It is highly recommended to specify the Vald version. +You can specify the image version by setting `image.tag` field in each component (`[component].image.tag`) or `defaults` section. + +```yaml +defaults: + image: + tag: v1.5.6 +``` + +or you can use the older image only for a target component, e.g., the agent, + +```yaml +agent: + image: + tag: v1.5.5 +``` + +### Specify appropriate logging level and format + +The default logging levels and formats are configured in `defaults.logging.level` and `defaults.logging.format`. +You can also specify them in each component section (`[component].logging`). + +```yaml +defaults: + logging: + level: info + format: raw +``` + +You can specify log level `debug` and JSON format for lb-gateway by the followings: + +```yaml +gateway: + lb: + logging: + level: debug + format: json +``` + +The logging level is defined in [the Coding Style Guide](/docs/v1.8/contributing/coding-style#logging). + +### Servers + +Each Vald component has several types of servers. +They can be configured by specifying the values in `defaults.server_config`. + +Examples: + +```yaml +defaults: + server_config: + servers: + grpc: + enabled: true + host: 0.0.0.0 + port: 8081 + servicePort: 8081 + server: + mode: GRPC + ... +``` + +In addition, they can be overwritten by setting each `[component].server_config`, e.g., `gateway.lb.server_config` is following. + +```yaml +gateway: + lb: + server_config: + servers: + rest: + enabled: true + host: 0.0.0.0 + port: 8080 + servicePort: 8080 + server: + mode: REST + ... +``` + +#### gRPC server + +gRPC server should be enabled because all Vald components use gRPC to communicate with others. +The API specs are placed in [Vald APIs](/docs/v1.8/api). + +#### REST server + +REST server is optional. +The swagger specs are placed in [Vald APIs Swagger][vald-swagger-specs]. + +#### Health check servers + +There are two built-in health check servers: liveness and readiness. +They are used as servers for [Kubernetes liveness and readiness probe][kubernetes-liveness-readiness]. +The liveness health server is disabled by default due to the liveness probe may accidentally kill the Vald Agent component. + +```yaml +agent: + server_config: + healths: + liveness: + enabled: false +``` + +### Metrics servers + +The metrics server enables easier debugging and monitoring of Vald components. +There are two types of metrics servers: pprof and Prometheus. + +pprof server is implemented using Go's `net/http/pprof` package. +You can use [google's pprof][google-pprof] to analyze the exported profile result. + +Prometheus server is a [Prometheus][prometheus-io] exporter. +It is required to set the `observability` section on each Vald component to enable the monitoring using Prometheus. +Please refer to the next section. + +### Observability + +The observability features are useful for monitoring Vald components. +These settings can be enabled by setting the `defaults.observability.enabled` field to the value `true` or by overriding it in each component (`[component].observability.enabled`). +And also, enable each feature by setting the value `true` on its `enabled` field. + +If observability features are enabled, the metrics will be collected periodically. +The duration can be set on `observability.collector.duration`. +Please refer to [the Vald operation guide](/docs/v1.8/user-guides/configuration) for more detail. + +## Component basic configuration + +### Agents + +#### NGT + +Vald Agent NGT uses [NGT-labs/NGT][yj-ngt] as a core library for searching vectors. +The behaviors of NGT can be configured by setting `agent.ngt` field object. + +The important parameters are the followings: + +- `agent.ngt.dimension` +- `agent.ngt.distance_type` +- `agent.ngt.object_type` + +Users should configure these parameters first to fit their use case. +For further details, please read [the NGT wiki][yj-ngt-wiki]. + +Vald Agent NGT has a feature to start indexing automatically. +The behavior of this feature can be configured with these parameters: + +- `agent.ngt.auto_index_duration_limit` +- `agent.ngt.auto_index_check_duration` +- `agent.ngt.auto_index_length` + +
+While the Vald Agent NGT is in the process of creating indexes, it will ignore all search requests to the target pods. +
+ +
+When deploying Vald Index Manager, the above parameters should be set much longer than the Vald Index Manager settings (Please refer to the Vald Index Manager section).
+E.g., set agent.ngt.auto_index_duration_limit to "720h" and agent.ngt.auto_index_check_duration to "24h".
+This is because the Vald Index Manager accurately grasps the index information of each Vald Agent NGT and controls the execution timing of indexing.

+When the setting parameter of Vald Agent NGT is shorter than the setting value of Vald Index Manager, Vald Agent NGT may start indexing by itself without the execution command from Vald Index Manager. +If this happens, the Index Manager may not function properly. +
+ +#### Faiss + +Vald Agent Faiss uses [facebookresearch/faiss][faiss] as a core library for searching vectors. +The behaviors of Faiss can be configured by setting `agent.faiss` field object. + +The important parameters are the followings: + +- `agent.faiss.dimension` +- `agent.faiss.distance_type` +- `agent.faiss.m` +- `agent.faiss.metric_type` +- `agent.faiss.nbits_per_idx` +- `agent.faiss.nlist` + +Users should configure these parameters first to fit their use case. +For further details, please read [the Faiss wiki][faiss-wiki]. + +Vald Agent Faiss has a feature to start indexing automatically. +The behavior of this feature can be configured with these parameters: + +- `agent.faiss.auto_index_duration_limit` +- `agent.faiss.auto_index_check_duration` +- `agent.faiss.auto_index_length` + +
+While the Vald Agent Faiss is in the process of creating indexes, it will ignore all search requests to the target pods. +
+ +
+When deploying Vald Index Manager, the above parameters should be set much longer than the Vald Index Manager settings (Please refer to the Vald Index Manager section) or minus value.
+E.g., set agent.faiss.auto_index_duration_limit to "720h" or "-1h" and agent.faiss.auto_index_check_duration to "24h" or "-1h".
+This is because the Vald Index Manager accurately grasps the index information of each Vald Agent Faiss and controls the execution timing of indexing.

+When the setting parameter of Vald Agent Faiss is shorter than the setting value of Vald Index Manager, Vald Agent Faiss may start indexing by itself without the execution command from Vald Index Manager. +If this happens, the Index Manager may not function properly. +
+ +#### Resource requests and limits, Pod priorities + +Because the Vald Agent pod places indexes on memory, termination of agent pods causes loss of indexes. +It is important to set the resource requests and limits appropriately to avoid terminating the Vald Agent pods. + +Requesting 40% of cluster memory for agent pods is highly recommended. +Also, it is highly recommended not to set the resource limits for the Vald Agent pods. + +Pod priorities are also useful for saving agent pods from eviction. +By default, very high priority is set to agent pods in the Chart. + +[The capacity planning page](/docs/v1.8/user-guides/capacity-planning) helps to estimate the resources. + +#### Pod scheduling + +It is recommended to schedule agent pods on different nodes as much as possible. + +
+The affinity setting for Vald Agent is significant for the Vald cluster.
+Please DO NOT remove the default settings. +
+ +To achieve this, the following [podAntiAffinity][kubernetes-affinity-antiaffinity] is set by default. + +```yaml +agent: + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + topologyKey: kubernetes.io/hostname + labelSelector: + matchExpressions: + - key: app + operator: In + values: + - vald-agent-ngt +``` + +It can also be achieved by using [pod topology spread constraints][kubernetes-topology-spread-constraints]. + +```yaml +agent: + topologySpreadConstraints: + - topologyKey: node + maxSkew: 1 + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: vald-agent-ngt + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: [] # to disable default settings +``` + +### Gateway LB + +#### Ingress + +Ingress for gateways can be configured by `gateway.{filter,lb}.ingress` field object. +It is important to set your host to `gateway.{filter,lb}.ingress.host` field. +`gateway.{filter,lb}.ingress.servicePort` should be `grpc` or `rest`. + +```yaml +gateway: + lb: + ingress: + enabled: true + host: vald.vdaas.org # Set correct hostname here + servicePort: grpc +``` + +#### Index replica + +`gateway.lb.gateway_config.index_replica` represents how many Vald Agent pods a vector will be inserted into. +The maximum value of the index replica should be 30% of the Vald Agent pods deployed. + +```yaml +gateway: + lb: + gateway_config: + index_replica: 3 // By setting the index replica to 3, the number of Vald Agent pods deployed should be more than 9 (3 / 0.3). +``` + +#### Resource requests and limits + +The gateway's resource requests and limits depend on the request traffic and available resources. +If the request traffic varies largely, enabling HPA for the gateway and adjusting the resource requests is recommended. + +#### Discoverer request duration + +`gateway.lb.gateway_config.discoverer.duration` represents the frequency of sending requests to the discoverer. +If the discoverer's CPU utilization is too high, make this value longer or reduce the number of LB gateway pods. + +```yaml +gateway: + lb: + gateway_config: + discoverer: + duration: 2s +``` + +### Discoverer + +#### Cluster Role + +Vald Discoverer gets the Node and Pod metrics from [kube-apiserver](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/) as described in [Vald Discoverer](/docs/v1.8/overview/component/discoverer). +Vald's Helm deployment supports RBAC as default, and the default configuration is the following. + +```yaml +discoverer: + clusterRole: + # if true, the clusterRole configuration will be created. + enabled: true + name: discoverer + clusterRoleBinding: + # if true, the clusterRoleBinding configuration will be created. + enabled: true + name: discoverer + serviceAccount: + # if true, the serviceAccount configuration will be created. + enabled: true + name: vald +``` + +When `RBAC` is unavailable in your environment, or you would like to put some restrictions, please modify it and grant the permissions to the user executing the discoverer. +Each configuration file is the following: + +- [Cluster role](https://github.com/vdaas/vald/blob/main/k8s/discoverer/clusterrole.yaml) +- [Cluster role binding](https://github.com/vdaas/vald/blob/main/k8s/discoverer/clusterrolebinding.yaml) +- [Service account](https://github.com/vdaas/vald/blob/main/k8s/discoverer/serviceaccount.yaml) + +#### Resource requests and limits + +The number of discoverer pods and resource limits can be estimated by the configurations of your LB gateways and index managers because its APIs are called by them. +Discoverer CPU loads almost depend on API request traffic. + +```bash +# The API traffic formula +(the number of LB gateways x its request frequency) + (the number of index managers x its request frequency). +``` + +### Index Manager + +#### Execution index command to Vald Agent + +Vald Index Manager controls the indexing timing for all Vald Agent pods in the Vald cluster. +These parameters are related to the control process. + +```yaml +manager: + index: + indexer: + # namespace of agent pods to manage + agent_namespace: vald # namespace of agent pods to manage + # check duration of automatic indexing + auto_index_check_duration: "1m" + # limit duration of automatic indexing + auto_index_duration_limit: "30m" + # number of caches to trigger automatic indexing + auto_index_length: 100 + # limit duration of automatic index saving + auto_save_index_duration_limit: "3h" + # duration of automatic index saving wait duration for next saving + auto_save_index_wait_duration: "10m" + # the number of Agent Pods indexing at the same time + concurrency: 1 + # number of pool size of creating index processing + creation_pool_size: 10000 +``` + +#### Discoverer request duration + +Same as LB gateway, `manager.index.indexer.discoverer.duration` represents the frequency of sending requests to the discoverer. + +## References + +For further details, there are references to the Helm values in the Vald GitHub repository. + +- [README of Vald Helm Chart][vald-helm-chart] +- [README of Vald-Helm-Operator Chart][vald-helm-operator-chart] + + + +[vald-helm-chart]: https://github.com/vdaas/vald/tree/main/charts/vald +[vald-helm-operator-chart]: https://github.com/vdaas/vald/tree/main/charts/operator/helm +[vald-apis-docs]: https://github.com/vdaas/vald/tree/main/apis/docs +[vald-swagger-specs]: https://github.com/vdaas/vald/tree/main/apis/swagger +[google-pprof]: https://github.com/google/pprof +[prometheus-io]: https://prometheus.io/ +[kubernetes-liveness-readiness]: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/ +[kubernetes-affinity-antiaffinity]: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity +[kubernetes-topology-spread-constraints]: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ +[yj-ngt]: https://github.com/NGT-labs/NGT +[yj-ngt-wiki]: https://github.com/NGT-labs/NGT/wiki +[faiss]: https://github.com/facebookresearch/faiss +[faiss-wiki]: https://github.com/facebookresearch/faiss/wiki diff --git a/content/docs/v1.8/user-guides/deployment.md b/content/docs/v1.8/user-guides/deployment.md new file mode 100644 index 000000000..a4ab3a8e2 --- /dev/null +++ b/content/docs/v1.8/user-guides/deployment.md @@ -0,0 +1,325 @@ +--- +title: "Deployment_v1.8/User Guides" +date: 2026-09-16T04:40:21Z +draft: false +weight: 600 +description: How to launch Vald cluster on your Kubernetes cluster +menu: + userguides: + parent: User Guides +--- + +# Deployment + +There are two major ways for the deployment of the Vald cluster ways: Using the Helm command with `values.yaml` or without Helm command using operator called `vald-helm-operator`. + +- Using Helm command with `values.yaml` + - Easy to deploy + - Allow editing Vald configuration values when the user executes Helm command with inlining. + - Need Helm command when applying configuration + +- Using `vald-helm-operator` + - Monitoring the Vald deployments and Vald custom resource by `vald-helm-operator` + - Use `kubectl` command to set or update of the Vald cluster configuration + - Automate manage the Vald cluster based on CRD (Custom Resource Definitions) + +
+The benefit of using vald-helm-operator, it eliminates the need to check the status of the Vald cluster from the Vald cluster operator. +It manages the Vald cluster based on the designed configuration instead of you. +
+ +## Requirement + +- Helm: v3 ~ + +If Helm is not installed, please install [Helm](https://helm.sh/docs/intro/install). + +
Installation command for Helm
+ +```bash +curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash +``` + +
+ +## Before Deployment + +Both ways need the configuration file when you need to overwrite the default configurations before the deployment. + +Please refer to [the basic configuration document](/docs/v1.8/user-guides/configuration). + +In addition, as you need, please refer to the followings: + +- [Backup Configuration](/docs/v1.8/user-guides/backup-configuration) +- [Filter Configuration](/docs/v1.8/user-guides/filtering-configuration) + +Moreover, we publish the example configuration files for each use case. +Please also refer to [here](https://github.com/vdaas/vald/tree/main/charts/vald/values). + +
+For vald-discoverer to work correctly, if you deploy multiple Vald clusters in the same Kubernetes cluster, please deploy one Vald cluster in one namespace. +
+ +## Use Helm command + +### Deployment + +When deploying with Helm command, it requires `value.yaml` to override the default values. + +
Sample values YAML
+ +```yaml +defaults: + logging: + level: debug + image: + # Please set the specified version, e.g., v1.5.6, instead of latest + tag: "latest" + server_config: + healths: + liveness: + livenessProbe: + initialDelaySeconds: 60 + readiness: + readinessProbe: + initialDelaySeconds: 60 + + ## vald-lb-gateway settings + gateway: + lb: + minReplicas: 2 + maxReplicas: 2 + gateway_config: + index_replica: 2 + + ## vald-agent settings + agent: + minReplicas: 6 + maxReplicas: 6 + podManagementPolicy: Parallel + ngt: + dimension: 784 + distance_type: l2 + object_type: float + # When auto_index_check_duration_limit is minus value, the agent auto indexing is effectively disabled. + auto_index_check_duration_limit: "-1s" + # When auto_index_duration_limit is minus value, the agent auto indexing is effectively disabled. + auto_index_duration_limit: "-1s" + auto_create_index_pool_size: 10000 + default_pool_size: 10000 + + ## vald-discoverer settings + discoverer: + resources: + requests: + cpu: 150m + memory: 50Mi + + ## vald-manager settings + manager: + index: + resources: + requests: + cpu: 150m + memory: 30Mi + indexer: + auto_index_duration_limit: 1m + auto_index_check_duration: 40s +``` + +
+ +After create `values.yaml`, you can deploy by the following steps. + +1. Add vald repo into the helm repo. + + ```bash + helm repo add vald https://vdaas.vald.org/charts + ``` + +1. Deploy with the `values.yaml` + + ```bash + helm install vald vald/vald --values + ``` + +### Update Configuration + +When you need to update the configuration, you can update by following command with your new `values.yaml`. + +```bash +helm upgrade vald vald/vald --values +``` + +### Cleanup + +The Vald cluster can be removed by the following command. + +```bash +helm uninstall vald +``` + +
+If using PV for the backup, PV won't delete automatically. +
+ +## Using with vald-helm-operator + +### Deployment + +When deploying with vald-helm-operator, it requires `vr.yaml` file for applying `ValdRelease`. +`vald-helm-operator` manages the Vald cluster based on configuration of `vr.yaml`. + +
Sample ValdRelease YAML
+ +```yaml +apiVersion: vald.vdaas.org/v1 +kind: ValdRelease +metadata: + name: vald-cluster +# the values of Helm chart for Vald can be placed under the `spec` field. +spec: + defaults: + logging: + level: debug + image: + # Please set the specified version, e.g., v1.5.6, instead of latest + tag: "latest" + server_config: + healths: + liveness: + livenessProbe: + initialDelaySeconds: 60 + readiness: + readinessProbe: + initialDelaySeconds: 60 + + ## vald-lb-gateway settings + gateway: + lb: + minReplicas: 2 + maxReplicas: 2 + gateway_config: + index_replica: 2 + + ## vald-agent settings + agent: + minReplicas: 6 + maxReplicas: 6 + podManagementPolicy: Parallel + ngt: + dimension: 784 + distance_type: l2 + object_type: float + # When auto_index_check_duration_limit is minus value, the agent auto indexing is effectively disabled. + auto_index_check_duration_limit: "-1s" + # When auto_index_duration_limit is minus value, the agent auto indexing is effectively disabled. + auto_index_duration_limit: "-1s" + auto_create_index_pool_size: 10000 + default_pool_size: 10000 + + ## vald-discoverer settings + discoverer: + resources: + requests: + cpu: 150m + memory: 50Mi + + ## vald-manager settings + manager: + index: + resources: + requests: + cpu: 150m + memory: 30Mi + indexer: + auto_index_duration_limit: 1m + auto_index_check_duration: 40s +``` + +
+ +In addition, you can use the operator for the `vald-helm-operator` by applying `vhor.yaml` for `ValdHelmOperatorRelease`. +It is possible to auto-managing vald-helm-operator. + +
Sample ValdHelmOperatorRelease YAML
+ +```yaml +apiVersion: vald.vdaas.org/v1 +kind: ValdHelmOperatorRelease +metadata: + name: vald-helm-operator-release +# the values of Helm chart for vald-helm-operator can be placed under the `spec` field. +spec: + watchNamespaces: "default" +``` + +
+ +For more details of the configuration of `vald-helm-operator-release`, please refer to [here](https://github.com/vdaas/vald/tree/main/charts/operator/helm#configuration). + +After setting `vr.yaml` (and `vhor.yaml`), you can deploy by the following steps. + +
+If you need to deploy ValdHelmOperatorRelease, you should apply vhor.yaml before applying vr.yaml. +
+ +1. Add vald repo into the helm repo + + ```bash + helm repo add vald https://vdaas.vald.org + ``` + +1. Deploy `vald-helm-operator` + + ```bash + helm install vald-helm-operator-release vald/vald-helm-operator + ``` + +1. Apply `vhor.yaml` (optional) + + ```bash + kubectl apply -f vhor.yaml + ``` + + After deployment success, the Kubernetes cluster runs the rolling update for vald-helm-operator components. + +1. Apply `vr.yaml` + + ```bash + kubectl apply -f vr.yaml + ``` + +### Update Configuration + +When you need to update the configuration, you can update by following command with your new `vr.yaml` or `vhor.yaml`. + +```bash +kubectl apply -f +``` + +### Cleanup + +The Vald cluster can be removed by the following steps. + +1. Delete the `ValdRelease` + + ```bash + kubectl delete -f vr.yaml + ``` + +1. Delete the `ValdHelmOperatorReleases` + + ```bash + kubectl delete -f vhor.yaml + ``` + +1. Delete crd + + ```bash + kubectl patch crd/valdhelmopratorreleases.vald.vdaas.org -p '{"metadata":{"finalizers":[]}}' -type=merge + ``` + +
+If using PV for the backup, PV won't delete automatically. +
diff --git a/content/docs/v1.8/user-guides/filtering-configuration.md b/content/docs/v1.8/user-guides/filtering-configuration.md new file mode 100644 index 000000000..f0dfe49b7 --- /dev/null +++ b/content/docs/v1.8/user-guides/filtering-configuration.md @@ -0,0 +1,203 @@ +--- +title: "Filtering Configuration_v1.8/User Guides" +date: 2026-09-16T04:40:21Z +draft: false +weight: 300 +description: Applies filtering feature to the Vald cluster +menu: + userguides: + parent: User Guides +--- + +# Filter Configuration + +This page describes how to enable filtering features on the Vald cluster. + +Before using the filtering functions, please check [the Vald Filter Gateway document](/docs/v1.8/overview/component/filter-gateway) first for what you can do. + +## Requirement + +To use any filtering functions with the Vald cluster, you must deploy the ingress and/or egress filter component before deploying the Vald cluster. + +The ingress filter can be used for, e.g., converting the object data to the vector, some filtering query vector, as pre-processing. + +The egress filter can be used for, e.g., filtering search result from `vald-lb-gateway` by the distance, categories, or any other condition, as post-processing. + +The filter component can be deployed anywhere, but it must be able to communicate with the Vald Filter gateway. +Every filter component should meet Vald's filter gRPC interface. + +- The ingress RPC definition + + ```rpc + service Filter { + // Represent the RPC to generate the vector. + rpc GenVector(payload.v1.Object.Blob) returns (payload.v1.Object.Vector) { + option (google.api.http) = { + post : "/filter/ingress/object" + body : "*" + }; + } + // Represent the RPC to filter the vector. + rpc FilterVector(payload.v1.Object.Vector) + returns (payload.v1.Object.Vector) { + option (google.api.http) = { + post : "/filter/ingress/vector" + body : "*" + }; + } + } + ``` + +- The egress RPC definition + + ```rpc + service Filter { + // Represent the RPC to filter the distance. + rpc FilterDistance(payload.v1.Object.Distance) + returns (payload.v1.Object.Distance) { + option (google.api.http) = { + post : "/filter/egress/distance" + body : "*" + }; + } + // Represent the RPC to filter the vector. + rpc FilterVector(payload.v1.Object.Vector) + returns (payload.v1.Object.Vector) { + option (google.api.http) = { + post : "/filter/egress/vector" + body : "*" + }; + } + } + ``` + +For more details, please refer to [the Vald Filter Gateway document](/docs/v1.8/overview/component/filter-gateway) + +### Official filter components + +Vald provides the two types of ingress filter components. +These will help you to implement your original filter component. + +Please refer to: + +- [Vald ONNX Ingress Filter](https://github.com/vdaas/vald-onnx-ingress-filter) +- [Vald Tensorflow Ingress Filter](https://github.com/vdaas/vald-tensorflow-ingress-filter) + +## Configuration + +It is easy to enable the filtering feature. + +```yaml +--- +gateway: +--- +filter: + enabled: true +``` + +The Vald Filter gateway connects to the filter component specified in the users' request. +So, you can use the filtering function only by setting `gateway.filter.enabled=true` in your Helm chart. + +If you want to make more detailed settings, please set the following parameters. + +```yaml +gateway: + filter: + ... + gateway_config: + ... + # gRPC client config for ingress filter + ingress_filter: + # gRPC client for ingress filter (overrides defaults.grpc.client) + client: {} + # object ingress vectorize filter targets + vectorizer: "" + # search ingress vector filter targets + search_filters: [] + # insert ingress vector filter targets + insert_filters: [] + # update ingress vector filter targets + update_filters: [] + # upsert ingress vector filter targets + upsert_filters: [] + # gRPC client config for egress filter + egress_filter: + # gRPC client config for egress filter (overrides defaults.grpc.client) + client: {} + # object egress vector filter targets + object_filters: [] + # distance egress vector filter targets + distance_filters: [] +``` + +Those parameters help the Vald filter gateway connect the filter component before getting the users' requests. + +## Client configuration + +To use the filter function, you must make settings on the client side because the Vald Filter gateway does not automatically create a connection with the filter component. + +These sample code describes how to use in client-side when you send the request with the blob data and want to convert it to a vector using the filter component. + +```go +package main + +import ( + "context" + + "github.com/vdaas/vald-client-go/v1/payload" + "github.com/vdaas/vald-client-go/v1/vald" + "github.com/vdaas/vald/internal/log" + "google.golang.org/grpc" +) + +func main() { + // address of the Vald cluster ingress. + grpcServerAddr := "vald-ingress-host" + + // ingress filter host and port. + const ingressHost = "vald-onnx-ingress-filter" + const ingressPort = 8081 + + // object data and its id. + const id = "object-id" + var object []byte + + // connect to the Vald cluster + ctx := context.Background() + conn, err := grpc.NewClient(grpcServerAddr, grpc.WithInsecure()) + if err != nil { + log.Error(err) + return + } + + // create client + client := vald.NewFilterClient(conn) + // config for insert request + icfg := &payload.Insert_ObjectRequest{ + // object data you'd like to insert into the Vald cluster + Object: &payload.Object_Blob{ + Id: id, + Object: object, + }, + // insert config + Config: &payload.Insert_Config{ + SkipStrictExistCheck: false, + }, + // specify vectorizer component location + Vectorizer: &payload.Filter_Target{ + Host: ingressHost, + Port: ingressPort, + }, + } + + // send insertObject request + res, err := client.InsertObject(ctx, icfg) + if err != nil { + log.Error(err) + return + } + log.Infof("location: %#v", res.Ips) +} +``` + +If you are the Vald cluster operator and not a client user, please share this information with client users. diff --git a/content/docs/v1.8/user-guides/index-correction.md b/content/docs/v1.8/user-guides/index-correction.md new file mode 100644 index 000000000..4c041a322 --- /dev/null +++ b/content/docs/v1.8/user-guides/index-correction.md @@ -0,0 +1,50 @@ +--- +title: "Index Correction_v1.8/User Guides" +date: 2026-09-16T04:40:20Z +draft: false +weight: 1300 +description: Batch processing to make the index state correct +menu: + userguides: + parent: User Guides +--- + +# Index Correction + +In the Vald cluster, the same Index is replicated to multiple agents due to the `index_replica` setting. However, inconsistencies between replicas may occur due to pod eviction or the occurrence of OOM killer during vector insertions. For example, + +1. The timestamp of the index differs between agents (some agents have an old index saved and it has not been updated). +2. The number of replicas does not meet the value set in `index_replica`. + +To resolve these inconsistencies, you can use the `Index Correction` feature. + +`Index Correction` is implemented as a [`CronJob`](https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/), checking the consistency between replicas regularly and resolving any inconsistencies. + +## Settings + +- enabled + Turns the index correction feature on/off. +- schedule + Sets the interval for the job start in cron notation (the default value is `3 6 * * *`, which means 3:06 AM every day). +- suspend + [Temporary suspension setting](https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/#schedule-suspension) for CronJob. + +```yaml +manager: + index: + corrector: + enabled: true + schedule: "3 6 * * *" + suspend: false +``` + +## Important Notes + +- Processing time + Under conditions of 10 million identical vectors(not including `index_replica`) and 10 agent replicas, the processing takes about 30~40 minutes (this is only a reference, and the actual execution time may vary depending on the infrastructure). Time complexity of the process is `O(MN)` where M is the number of identical vector items and N is the number of agent replicas. `index_replica` does not matter for the processing time. + +- concurrencyPolicy + `Forbid` is set internally, so a new job will not be created while an existing job is running. In other words, if the process does not finish within the interval specified by the schedule, the next job will not be scheduled. + +- Index operations during correction + Vector operations performed after the start of the index correction job are not considered in that job. diff --git a/content/docs/v1.8/user-guides/mirroring-configuration.md b/content/docs/v1.8/user-guides/mirroring-configuration.md new file mode 100644 index 000000000..863f0ecd8 --- /dev/null +++ b/content/docs/v1.8/user-guides/mirroring-configuration.md @@ -0,0 +1,100 @@ +--- +title: "Mirroring Configuration_v1.8/User Guides" +date: 2026-09-16T04:40:21Z +draft: false +weight: 400 +description: Applies mirror gateway for running multi Vald clusters +menu: + userguides: + parent: User Guides +--- + +# Mirror Configuration + +This page describes how to enable mirroring features on the Vald cluster. + +Before you use the mirroring functions, please look at [the Vald Mirror Gateway document](/docs/v1.8/overview/component/mirror-gateway) for what you can do. + +## Requirement + +- Vald version: v1.8 +- The number of Vald clusters: 2~ + +## Configuration + +This chapter shows how to configure values.yaml to enable Vald Mirror Gateway and how to interconnect Vald Mirror Gateways. + +The setting points are the followings: + +1. Enable the Vald Mirror Gateway using Helm values configuration +2. Interconnect Vald Mirror Gateways using the Custom Resource configuration + +### Helm Values Configuration + +The Helm values configuration is required for each Vald cluster to be deployed. + +It is easy to enable the mirroring feature. + +```yaml +--- +gateway: + mirror: + enabled: true +``` + +If you want to make more detailed settings, please set the following parameters. + +```yaml +gateway: + mirror: + ... + gateway_config: + ... + # gRPC client configuration (overrides defaults.grpc.client) + client: {} + # The duration to register other Mirror Gateways. + register_duration: "1s" + # The target namespace to discover ValdMirrorTarget (CR) resource. + # The default value is its own namespace. + namespace: "vald" + # The group name of the Mirror Gateways (optional). + # It is used to discover ValdMirrorTarget resources (CR) with the same group name. + # The default value is empty. + group: "group1" + # The duration to discover other mirror gateways in the same group. + discovery_duration: 1s + # The colocation name of the data center (optional). + colocation: "dc1" +``` + +The cluster role configuration is required when you deploy Vald clusters with Vald Mirror Gateway on multiple Namespaces in the Kubernetes cluster. + +Please refer to [Cluster Role Configuration](/docs/v1.8/user-guides/cluster-role-binding) about cluster role settings for Mirror Gateway. + +### Custom Resource Configuration + +The Mirror Gateway is not connected to other mirror gateways when deployed. + +The Vald Mirror Gateway connects to another Mirror Gateway component specified in the `ValdMirrorTarget` resource (Custom Resource). + +Based on this resource, if the connection succeeds, the Mirror Gateway will interconnect with another. + +```yaml +apiVersion: vald.vdaas.org/v1 +kind: ValdMirrorTarget +metadata: + name: mirror-target-01 + namespace: vald-03 + labels: + # The group name of the Mirror Gateways. + group: mirror-group-01 +spec: + # Colocation name. (optional) + colocation: dc1 + # The connection target to another mirror gateway. + target: + # The hostname. + host: vald-mirror-gateway.vald-01.svc.cluster.local + # The port number + port: 8081 +``` diff --git a/content/docs/v1.8/user-guides/network-policy.md b/content/docs/v1.8/user-guides/network-policy.md new file mode 100644 index 000000000..aa12d325f --- /dev/null +++ b/content/docs/v1.8/user-guides/network-policy.md @@ -0,0 +1,66 @@ +--- +title: "Network Policy_v1.8/User Guides" +date: 2026-09-16T04:40:21Z +draft: false +weight: 1200 +description: Enables to Kubernetes NetworkPolicy to the Vald cluster +menu: + userguides: + parent: User Guides +--- + +# Network Policy + +[Network Policy](https://kubernetes.io/docs/concepts/services-networking/network-policies/) is a Kubernetes feature that controls ingress and egress network traffic for pods. In Vald, you can set network policies as follows. + +> Please note that [prerequisites](https://kubernetes.io/docs/concepts/services-networking/network-policies/#prerequisites) are required for using network policies. Even if you configure the following settings in a cluster that does not meet the prerequisites, network policies will not be effective. + +# Network Policy in Vald + +To enable network policies in a Vald cluster, set `defaults.networkPolicy.enabled` to `true` as follows: + +```yaml +defaults: + networkPolicy: + enabled: true +``` + +This sets the following ingress/egress rules between Vald components (these are the minimum required rules for a Vald cluster to work). + +| from / to | agent | discoverer | filter gateway | lb gateway | index manager | kube-system | +| :------------- | :---: | :--------: | :------------: | :--------: | :-----------: | :---------: | +| agent | N/A | ⛔ | ⛔ | ⛔ | ⛔ | ✅ | +| discoverer | ⛔ | N/A | ⛔ | ⛔ | ⛔ | ✅ | +| filter gateway | ⛔ | ⛔ | N/A | ✅ | ⛔ | ✅ | +| lb gateway | ✅ | ✅ | ⛔ | N/A | ⛔ | ✅ | +| index manager | ✅ | ✅ | ⛔ | ⛔ | N/A | ✅ | + +# Add a user custom Network Policy + +There may be cases where you want to connect a Vald cluster to external components. Specifically, for the following cases: + +- Enable egress to `OpenTelemetryCollector` to use [observability features](https://vald.vdaas.org/docs/user-guides/observability-configuration/) +- Enable egress to an external filter component to use [filtering features](https://vald.vdaas.org/docs/user-guides/filtering-configuration/). + +To handle such cases, Vald allows you to set user custom network policies using the `defaults.networkPolicy.custom` field as follows: + +```yaml +defaults: + networkPolicy: + enabled: true + custom: + ingress: + - from: + - podSelector: + matchLabels: + app.kubernetes.io/name: pyroscope + egress: + - to: + - podSelector: + matchLabels: + app.kubernetes.io/name: opentelemetry-collector-collector +``` + +Please write down the same notation as the `ingress/egress` field of [NetworkPolicy resource](https://kubernetes.io/docs/concepts/services-networking/network-policies/#networkpolicy-resource) in our `custom` field. + +> Currently, these custom network policies are applied to all Vald components. diff --git a/content/docs/v1.8/user-guides/observability-configuration.md b/content/docs/v1.8/user-guides/observability-configuration.md new file mode 100644 index 000000000..8e606c93d --- /dev/null +++ b/content/docs/v1.8/user-guides/observability-configuration.md @@ -0,0 +1,290 @@ +--- +title: "Observability Configuration_v1.8/User Guides" +date: 2026-09-16T04:40:21Z +draft: false +weight: 1100 +description: Monitors performance on metrics dashboards +menu: + userguides: + parent: User Guides +--- + +# Observability Configuration + +Observability for the Vald cluster helps to check each Vald component status by monitoring metrics, logs, and traces. +By enabling observability, you can monitor and visualize the number of indexes, the timing of creating indexes, the number of RPC, CPU resource usage, Memory resource usage, events, etc. + +This page shows the best practice for applying observing features by constructing an observability environment and setting the Vald Helm chart. + +## Architecture + +Vald conforms [OpenTelemetry Protocol](https://opentelemetry.io/docs/reference/specification/protocol/otlp/) and does NOT depend on any commercial data format. + + + +OpenTelemetry Collector handles receiving / processing / exporting the telemetry data, which does NOT depend on the vendors' format. +All Vald components can send OTLP-compliant telemetry data, like metrics, traces, or logs, to the OpenTelemetry Collector. +The telemetry data is used to monitor or visualize with the observing tools. + +## Build an observing environment + +The recommended observability environment consists of the following: + +- Cert-Manager +- Jaeger Operator and Jaeger +- Prometheus Operator and Prometheus +- Grafana +- OpenTelemetry Operator and OpenTelemetry Collector + +Vald provides [the default manifests](https://github.com/vdaas/vald/tree/main/k8s/metrics) and [the make commands](https://github.com/vdaas/vald/blob/main/Makefile.d/k8s.mk) for deploying those components. +Please refer to the following sections to deploy each component. + +
+You can change parameters such as the export host by editing the manifest for your demands. +
+ +### Cert-Manager + +A `Cert-Manager` is required to enable to use of operators on the Kubernetes Cluster. + +```bash +make k8s/external/cert-manager/deploy +``` + +### Jaeger Operator and Jaeger + +Jaeger is helpful for monitoring trace data. It can be available by running the following: + +```bash +make k8s/metrics/jaeger/deploy +``` + +### Prometheus Operator and Prometheus + +Vald recommends using Prometheus as a backend service by the following command to monitor the metrics data. + +```bash +make k8s/metrics/prometheus/operator/deploy +``` + +### Grafana + +Vald recommends using Grafana to visualize metrics data. + +```bash +make k8s/metrics/grafana/deploy +``` + +### OpenTelemetry Operator and OpenTelemetry Collector + +Vald uses OpenTelemetry Collector to get the telemetry data and export it to monitor backend components. +The following command deploys OpenTelemetry Collector via OpenTelemetry Collector. + +
+Before executing the following command, please ensure the Prometheus operator runs healthy. +
+ +```bash +make k8s/otel/operator/deploy k8s/otel/collector/deploy +``` + +### Deploy Observability components with a simple command + +If you would like to deploy observability components with a simple command, please use the following command. + +```bash +make k8s/external/cert-manager/deploy k8s/monitoring/deploy +``` + +## Configure Helm chart + +This section shows how to set `values.yaml` to enable each Vald component to send its own telemetry data. + +The setting points are the following: + +1. Enable observability feature +1. Enable sending system metrics +1. Enable sending trace data +1. Set OpenTelemetry parameters + +
+The general settings are described for convenience, but individual settings are possible for each component by editing `[component].observability`. +
+ +### Enable observability feature + +To enable the observability feature, you must set `defaults.observability.enabled` as `true`. + +```yaml +defaults: + observability: + # enable observability + enabled: true +``` + +### Enable sending system metrics + +Each Vald component sends the system metrics by editing `defaults.observability.metrics`. +All metrics are enabled by default. +Vald recommends using default values unless there is a specific reason. + +```yaml +defaults: + observability: + # enable version info metrics + metrics: + enable_version_info: true + # If the enable_version_info setting is true, this information will be added to the keys of the version info metrics. + version_info_labels: + - "vald_version" + - "server_name" + - "git_commit" + - "build_time" + - "go_version" + - "go_os" + - "go_arch" + - "algorithm_info" + # enable memory metrics + enable_memory: true + # enable goroutine metrics + enable_goroutine: true + # enable cgo metrics + enable_cgo: true +``` + +### Enable sending trace data + +Please set `defaults.observability.trace.enabled` as `true` to enable sending trace data. +The default value is `false`. + +```yaml +defaults: + observability: + trace: + # enable to send trace data + enabled: true +``` + +### OpenTelemetry settings + +This section shows the detailed settings for sending telemetry data. + +#### Specify OpenTelemetry Collector endpoint + +To send the telemetry data, you must set the OpenTelemetry Collector's endpoint to `defaults.observability.otlp.collector_endpoint`. +The default value is not set. + +
+It is required to send telemetry data. +
+ +```yaml +defaults: + observability: + otlp: + collector_endpoint: "opentelemetry-collector-collector.default.svc.cluster.local:4317" +``` + +#### Specify the Telemetry attribute + +You can add the component information to the attribute of telemetry data by editing `defaults.observability.otlp.attribute`. +E.g., when setting `vald-agent-ngt-0` as `agent.observability.otlp.attribute.pod_name`, `target_pod: vald-agent-ngt-0` will be added to the attribute. +These attributes are set auto by the environment values, so Vald recommends using default values unless there is a specific reason. + +```yaml +defaults: + observability: + otlp: + attribute: + # deployed namespace + namespace: vald + # pod name + pod_name: vald-agent-ngt-0 + # deployed node name + node_name: kube-worker01 + # service name + service_name: vald-agent-ngt +``` + +#### Customize send configuration + +You can modify the sending telemetry data behavior by changing the `default.observability.otlp` parameters. + +```yaml +defaults: + observability: + otlp: + # Maximum duration for constructing a batch from the queue. The Processor forcefully sends available spans when timeout is reached. + trace_batch_timeout: "1s" + # Maximum duration for exporting trace spans + trace_export_timeout: "1m" + # Maximum batch size of trace spans. + trace_max_export_batch_size: 1024 + # Maximum queue size to buffer trace spans for delayed processing. + trace_max_queue_size: 256 + # Export interval for metrics + metrics_export_interval: "1s" + # Maximum duration for exporting metrics + metrics_export_timeout: "1m" +``` + +#### gRPC Configuration + +The interceptor configuration is required to send the metrics and trace data related to gRPC. +You can add the interceptor to the server-side and client-side by editing `defaults.server_config.servers.grpc.server.grpc.interceptors` parameters. + +```yaml +defaults: + server_config: + servers: + grpc: + server: + grpc: + # gRPC Server interceptor. + interceptors: + - TraceInterceptor + - MetricInterceptor + grpc: + # gRPC Client interceptor. + client: + dial_option: + interceptors: + - TraceInterceptor +``` + +## Monitoring telemetry data + +Telemetry data can be monitored using Grafana, Jaeger, etc. +Vald publishes [the sample manifest](https://github.com/vdaas/vald/blob/main/charts/vald/values/dev-observability.yaml), which enables Grafana and Jaeger. + +You can apply it after creating an observability environment. + +
+The default manifests don't set ingress host.
+You can monitor the browser by port forwarding or define the ingress host by yourself. +
+ +## Cleanup + +Lastly, the Vald cluster will be removed by executing the following command. + +```bash +helm uninstall vald +``` + +Also, the observability components will be removed by executing the following command. + +```bash +make k8s/monitoring/delete k8s/external/cert-manager/delete +``` + +And the observability components can also be removed by executing the following command. + +```bash +make k8s/otel/collector/delete \ + k8s/otel/operator/delete \ + k8s/metrics/grafana/delete \ + k8s/metrics/jaeger/delete \ + k8s/metrics/prometheus/operator/delete \ + k8s/external/cert-manager/delete +``` diff --git a/content/docs/v1.8/user-guides/operations.md b/content/docs/v1.8/user-guides/operations.md new file mode 100644 index 000000000..2c4be16aa --- /dev/null +++ b/content/docs/v1.8/user-guides/operations.md @@ -0,0 +1,161 @@ +--- +title: "Operations_v1.8/User Guides" +date: 2026-09-16T04:40:21Z +draft: false +weight: 700 +description: Describes the basic operation for the Vald cluster +menu: + userguides: + parent: User Guides +--- + +# Operations + +This page introduces best practices for operating a Vald cluster. + +## Deployment + +### Kubernetes cluster + +Since Vald agents stores vector data on their memory space, unexpected disruption or eviction of agents may cause loss of indices. +Also, disruption or deletion of worker nodes that have Vald agents may cause loss of indices. +If you need to prevent low accuracy effects caused by indices loss, it is better to increase the number of nodes and pods. + +However, to maximize the efficiency of search operations, it is better to have a certain amount of vectors in each NGT vector space. + +We recommend having more than 3 worker nodes with enough memory for the workload. +Deploying 2 or 3 Vald agent pods to each worker node is better. +If you want to store 100 million vectors with 128 dimensions, `8 bytes (64bit float) x 128 (dimension) x 100 million x N replicas`, 100 GB x N memory space is needed. +If the number of index replicas is three, which means N=3, the total amount of memory space for the whole cluster will be 300 GB at least. + +For example: + +- 10 worker nodes with 24 GB RAM and 3 Vald agents on each worker node (total: 240 GB RAM, 30 Vald agents) +- 20 worker nodes with 16 GB RAM and 2 Vald agents on each worker node (total: 320 GB RAM, 40 Vald agents) + +### On multi-tenant cluster + +If you’re going to deploy Vald on the multi-tenant cluster, please take care of the followings. + +- It is recommended to define PriorityClasses for agents not to be evicted. + - For more info, please visit the page [Pod Priority and Preemption][pod-priority-preemption]. + - If you are using [the Vald chart][vald-helm-chart], PriorityClasses are defined by default. +- Defining unique namespaces for each Vald and the other apps are recommended. +- Then, please define ResourceQuotas for the namespace for the other apps to limit their memory usage. + - For more info, please visit this page, [Resource Quotas][resource-quota]. + +## Monitoring + +### Logging + +The logging level of Vald components can be configured by `defaults.logging.level` (or `[component].logging.level`) field in Helm Chart values. +The level must be a one of `debug`, `info`, `warn`, `error`, and `fatal`. +The levels are defined in [the Coding Style document](/docs/v1.8/contributing/coding-style#logging). + +### Observability features of Vald + +The observability features are useful for monitoring Vald components. +Vald has various exporters, such as Prometheus, Jaeger, etc. +Using this feature, you can observe and visualize the internal stats or the events, like the number of NGT indexes, when to createIndex, or the number of RPCs. + +### Enabling observability feature + +By setting `defaults.observability.enabled` (or `[component].observability.enabled`) in the Helm Chart value set to `true`, the observability features become enabled. +If observability features are enabled, the metrics will be collected periodically. +The duration can be set on `observability.collector.duration`. + +If you'd like to use the tracing feature, you should enable it by setting `observability.trace.enabled` set to `true`. The sampling rate can be configured with `observability.trace.sampling_rate` + +### Monitoring Vald cluster using Prometheus and Grafana + +In this section, an example of monitoring the Vald cluster using [Prometheus][prometheus-io] and [Grafana][grafana] will be shown. + +#### Vald configuration + +To use the Prometheus exporter, you should enable it by setting both `observability.prometheus.enabled` and `server_config.metrics.prometheus.enabled` set to `true`. +The exporter port and endpoint are specified in each `server_config.metrics.prometheus.port` and `observability.prometheus.endpoint`. + +Now it's ready to scrape Vald metrics. +Please deploy Prometheus and Grafana to your cluster. + +#### Deploy Prometheus + +Prometheus can be installed using one of the following. + +- [Prometheus Operator][prometheus-operator] +- [Prometheus deployments in Vald repository][vald-prometheus] + +If you use Prometheus Operator, it is required to set configurations properly along with [Prometheus Configuration][prometheus-configuration] page. +It is recommended to use the endpoints role of the service discovery. + +#### Deploy Grafana + +Grafana can be installed using one of the following. + +- [Grafana Operator][grafana-operator] +- [Grafana deployments in Vald repository][vald-grafana] + +It is required to set your Prometheus to a data source. + +#### Example dashboard + +Now you can construct your own Grafana dashboard to monitor Vald metrics. +This is an example of a custom dashboard. It is based on [our standard dashboard settings][vald-grafana-dashboards]. + + + +## Upgrading + +Our versioning strategy is based on [Semantic Versioning][semver]. +Upgrading to a new version, such as minor or major, may require changing your configurations. +Please read the [CHANGELOG][changelog] before upgrading. + +### In case of manual deploy + +In manual deployments, it is generally required to update your ConfigMaps first. +After that, please update the image tags of Vald components in your deployments. + +### In case of using Helm + +In case of using Helm and Vald's chart, please update `defaults.image.tag` field and install it. + +### In case of using Vald-Helm-Operator + +If using Vald-Helm-Operator, please upgrade the CRDs first because Helm doesn’t have support to upgrade CRDs. + +```bash +VERSION=v1.4.1 +``` + +```bash +kubectl replace -f https://raw.githubusercontent.com/vdaas/vald/${VERSION}/charts/operator/helm/crds/valdrelease.yaml && \ +kubectl replace -f https://raw.githubusercontent.com/vdaas/vald/${VERSION}/charts/operator/helm/crds/valdhelmoperatorrelease.yaml +``` + +After upgrading CRDs, please upgrade the operator. +If you're using `valdhelmoperatorrelease` (or `vhor`) resource, please update the `spec.image.tag` field of it. +On the other hand, please update the operator's deployment manually. + +After that, please update `image.tag` field in your valdrelease (or `vr`) resource. +The operator will automatically detect the changes and update the deployed Vald cluster. + +### References + +- [vald-helm-chart][vald-helm-chart] +- [vald-helm-operator-chart][vald-helm-operator-chart] +- [CHANGELOG][changelog] + +[vald-helm-chart]: https://github.com/vdaas/vald/tree/main/charts/vald +[vald-helm-operator-chart]: https://github.com/vdaas/vald/tree/main/charts/operator/helm +[changelog]: https://vald.vdaas.org/docs/release/changelog/ +[pod-priority-preemption]: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/ +[resource-quota]: https://kubernetes.io/docs/concepts/policy/resource-quotas/ +[prometheus-io]: https://prometheus.io/ +[grafana]: https://grafana.com +[vald-prometheus]: https://github.com/vdaas/vald/tree/main/k8s/metrics/prometheus +[prometheus-operator]: https://github.com/coreos/prometheus-operator +[prometheus-configuration]: https://prometheus.io/docs/prometheus/latest/configuration/configuration/ +[vald-grafana]: https://github.com/vdaas/vald/tree/main/k8s/metrics/grafana +[vald-grafana-dashboards]: https://github.com/vdaas/vald/tree/main/k8s/metrics/grafana/dashboards +[grafana-operator]: https://operatorhub.io/operator/grafana-operator +[semver]: https://semver.org/ diff --git a/content/docs/v1.8/user-guides/read-replica-and-rotator.md b/content/docs/v1.8/user-guides/read-replica-and-rotator.md new file mode 100644 index 000000000..66c13ee8a --- /dev/null +++ b/content/docs/v1.8/user-guides/read-replica-and-rotator.md @@ -0,0 +1,116 @@ +--- +title: "Read Replica and Rotator_v1.8/User Guides" +date: 2026-09-16T04:40:21Z +draft: false +weight: 1500 +description: How to improve search request speed +menu: + userguides: + parent: User Guides +--- + +# Read Replica and Rotator + +Read replica enhances the search QPS (Queries Per Second) of the Vald cluster by deploying read-only agents in addition to the regular agents and distributing the requests among them. Read replica is deployed as Kubernetes deployments, and depending on the number of replicas (N), QPS increases by approximately 1.7 to 1.8 times \* N. + +
+The increase in QPS is possible with sufficient infrastructure (see Important notes). +
+ +## How to deploy read replica + +The read replica is managed with a separate chart from the Vald cluster and is deployed as an addon to the Vald cluster. The Vald cluster should be deployed first, followed by the deployment of the read replica. + +> The reason Vald and Vald-readreplica are in separate charts is to avoid conflicts between the read replica's restart and the Helm operator's processes when Vald is managed by a helm operator. Therefore, the read replica will always be deployed using Helm commands. + +### When you deploy Vald with Helm command + +1. Edit `values.yaml` like below (Please refer to [deployment](/docs/v1.8/user-guides/deployment) for other fields.) + + ```yaml + agent: + ngt: + export_index_info_to_k8s: true + readreplica: + enabled: true + minReplicas: 1 # if you don't use hpa, this will be the replicas of the Deployment + maxReplicas: 3 + hpa: + enabled: true # if you prefer to use hpa + targetCPUUtilizationPercentage: 80 + manager: + index: + operator: + enabled: true + rotation_job_concurrency: 2 + ``` + +1. Deploy Vald cluster + + ```bash + helm install vald vald/vald --values values.yaml + ``` + +1. Deploy `vald-readreplica` with the same `values.yaml` + + ```bash + helm install vald-readreplica vald/vald-readreplica --values values.yaml + ``` + +### When you deploy Vald cluster with `vald-helm-operator` + +1. Edit `valdrelease.yaml` with the same fields as above + +1. Deploy Vald cluster + + ```bash + helm install vald-helm-operator-release vald/vald-helm-operator + kubectl apply -f valdrelease.yaml + ``` + +1. Deploy `vald-readreplica` + + ```bash + helm install vald-readreplica vald/vald-readreplica --values + ``` + +## Architecture + +Read replica mainly consists of the following four parts. + +Read Replica Architecture + +### Read replica deployment + +The deployment that generates Pods where the actual processing of read replica takes place. Read replica accepts read requests (search) and reads the index from the read replica PVC. + +### Read replica PVC + +The PVC for read replica Pods is used to read the index. It is generated based on the latest snapshot from the PVC of the regular agent. Unlike the agent PVC, it is generated as ROX, allowing it to be read from multiple Pods. + +### Index operator + +The operator handles the following processes: + +1. Monitoring the time when the agent saved the index to the PVC and when the read replica performed index rotation +1. Generating [Read replica rotator](#read-replica-rotator) job when an index save occurs after the most recent rotation + +> The Index operator also manages the timing of index create/save operations other than those mentioned above. Please refer to another document for details. + +### Read replica rotator + +The Kubernetes job handles the following processes: + +1. Creating a snapshot from the agent's PVC +1. Generating a PVC for read replica from the snapshot +1. Rolling update of the read replica deployment to launch a group of read replica pods with the latest index + +## Important notes + +Result consistency is guaranteed + +There is a time lag between index insertion, agent save, and the completion of read replica rotation. During this time, there may be inconsistencies between the index in the agent itself and the index in the read replica. + +- Sufficient infrastructure is required for QPS scaling + + Even if read replicas are deployed, QPS will not scale if sufficient resources are not available in the Kubernetes cluster. Specifically, agent resources and read replica resources should be deployed on separate nodes. Vald sets `podAntiAffinity` to ensure that agent resources and read replica resources are deployed on separate nodes as much as possible. diff --git a/content/docs/v1.8/user-guides/sdks.md b/content/docs/v1.8/user-guides/sdks.md new file mode 100644 index 000000000..297b56fc2 --- /dev/null +++ b/content/docs/v1.8/user-guides/sdks.md @@ -0,0 +1,41 @@ +--- +title: "Sdks_v1.8/User Guides" +date: 2026-09-16T04:40:20Z +draft: false +weight: 1400 +description: Sends request and receives response from the Vald cluster +menu: + userguides: + parent: User Guides +--- + +# SDKs + +This page shows the introduction of the Vald SDKs. + +## Client Libraries + +Vald provides the official SDKs as client libraries. +You can use Vald SDKs to request something to Vald, e.g., `Insert`, `Update`, `Upsert`, `Search`, `Remove`, and so on. +Also, Vald publishes [the gRPC protobuf](https://github.com/vdaas/vald/tree/main/apis/proto/v1); you can use any languages you want, even if Vald does not provide an SDK. + +## Official SDKs + +Here is the list of Official SDKs. + +- [Go](https://github.com/vdaas/vald-client-go) +- [Java](https://github.com/vdaas/vald-client-java) +- [Python](https://github.com/vdaas/vald-client-python) +- [Node.js](https://github.com/vdaas/vald-client-node) + +## How to use SDKs + +If you use the client libraries provided by Vald, you can try it quickly. +There are only a few steps to use like the followings: + +1. Import package +1. Create the client +1. Set configurations, then send a request +1. Something you'd like to do + +For more information about the SDKs, please refer to each SDK's page. diff --git a/content/docs/v1.8/user-guides/upgrade-cluster.md b/content/docs/v1.8/user-guides/upgrade-cluster.md new file mode 100644 index 000000000..6b35bba7c --- /dev/null +++ b/content/docs/v1.8/user-guides/upgrade-cluster.md @@ -0,0 +1,160 @@ +--- +title: "Upgrade Cluster_v1.8/User Guides" +date: 2026-09-16T04:40:21Z +draft: false +weight: 800 +description: Describes hot to upgrade Vald version for the running Vald cluster +menu: + userguides: + parent: User Guides +--- + +# Upgrade Vald cluster + +Before this document, we recommend reading [the configuration document](/docs/v1.8/user-guides/configuration) first. + +## Update Configuration + +When you'd like to update the configuration for the Vald cluster, you need to edit your configuration file and apply it according to your deployment method. +Please refer to one of the sections below according to your deployment method. + +
+If each Vald Agent already has the indexes and uses the backup feature, please DO NOT edit the dimension and the distance type to avoid initContainer error.
+(Each Vald Agent will restore from backup data when performing a rolling update) +
+ +
+The indexes will be clear when performing a rolling update if each Vald Agent already has the indexes and the backup feature disabled. +
+ +### Using Helm for deployment + +The configuration file is `values.yaml`. ([Helm Values](https://helm.sh/docs/chart_template_guide/values_files/)) + +You can edit your `values.yaml` and update your Vald cluster by following steps: + +1. Edit `values.yaml`. +1. Update with Helm command. + + ```bash + helm upgrade vald/vald --values + # e.g., helm upgrade vald vald/vald --values values.yaml + ``` + +### Using vald-helm-operator + +There are two configuration files: + +- `vhor.yaml` for vald-helm-operator +- `vr.yaml` for the Vald cluster + +If you don't need to update the `vald-helm-operator`, please skip step.1 and step.2. +The Vald cluster will be updated by the following steps: + +1. Edit `vhor.yaml`. +1. Apply `vhor.yaml`. + + ```bash + kubectl apply -f + # e.g., kubectl apply -f vhor.yaml + ``` + +1. Edit `vr.yaml`. +1. Apply `vr.yaml`. + + ```bash + kubectl apply -f + # e.g., kubectl apply -f vr.yaml + ``` + +## Upgrade Vald version + +We recommend using the latest Vald version. + +If your Vald cluster uses an old version and you'd like to upgrade the Vald version, you can upgrade by the following sections. +Please refer to one of the sections below according to your deployment method. + +
+When the upgrade request is applied, Kubernetes will perform a rolling update. +If your Vald cluster does not apply backup, the index will be cleared by a rolling update.
+In addition, if each Vald Agent already has the indexes and the backup feature enabled, please DO NOT edit the dimension and the distance type to avoid initContainer error. +(Each Vald Agent will restore from backup data when backup when performing a rolling update) +
+ +### Using Helm for deployment + +Using the `Helm` command for the deployment of the Vald cluster, the upgrading steps are below. + +
+Before upgrading, please confirm your Vald cluster version.
+If there is major or minor upgrading, the chart structure may have changed. +
+ +1. Verify vald repo in your helm repo + + ```bash + helm repo list + ``` + +1. Update Helm repo + + ```bash + helm repo update + # e.g., helm repo update vald + ``` + +1. Edit `values.yaml` + - We recommend setting a specific version as an image tag. + + ```yaml + defaults: + --- + image: + tag: v1.5.6 # set a new version + ``` + +1. Apply `values.yaml` + + ```bash + helm upgrade vald/vald --values + # e.g., helm upgrade vald vald/vald --values values.yaml + ``` + +### Using vald-helm-operator + +The upgrading steps are below if you use `vald-helm-operator` for the deployment. + +1. Upgrade CRDs + + ```bash + kubectl replace -f https://raw.githubusercontent.com/vdaas/vald//charts/operator/helm/crds/valdrelease.yaml + kubectl replace -f https://raw.githubusercontent.com/vdaas/vald//charts/operator/helm/crds/valdhelmoperatorrelease.yaml + ``` + +1. Update `vhor` + + ```bash + kubectl patch vhor vhor-release -p '{"spec":{"image":{"tag":""}}}' + ``` + + - Also, you can upgrade manually by editing `vhor.yaml` and applying it. + +1. Edit `vr.yaml` + - We recommend setting a specific version as an image tag. + + ```yaml + ... + spec: + defaults: + ... + image: + tag: v1.5.6 # set a new version + ... + ``` + +1. Apply `vr.yaml` + + ```bash + kubectl apply -f + # e.g., kubectl apply -f vr.yaml + ``` diff --git a/content/docs/v1.8/user-guides/vald-operator/_index.md b/content/docs/v1.8/user-guides/vald-operator/_index.md new file mode 100644 index 000000000..cb00fbf64 --- /dev/null +++ b/content/docs/v1.8/user-guides/vald-operator/_index.md @@ -0,0 +1,384 @@ +--- +title: "Vald Operator_v1.8/User Guides/Vald Operator" +date: 2026-09-16T04:40:20Z +draft: false +weight: 0 +--- + +# Vald Operator + +The Vald Operator is a Kubernetes controller that manages the lifecycle of Vald clusters through a single custom resource. +It watches `ValdOperatorRelease` (short name `vor`, API group `vald.vdaas.org/v1`) resources and generates +[`ValdRelease`](https://github.com/vdaas/vald-helm-operator) (VRS) manifests consumed by the Vald Helm Operator (VHO). + +``` +ValdOperatorRelease (vor) ──reconcile──▶ ValdRelease (VRS) × (active infra × clusters) + minimal input consumed by vald-helm-operator +``` + +The controller _generates_ VRS definitions. +It does not run Vald itself; a VHO running in the target cluster turns each VRS into Vald pods. +This separation lets a management cluster emit VRS definitions and distribute them to other clusters. + +## Prerequisites + +- Go 1.23+ +- Docker 17.03+ +- kubectl 1.11.3+ and access to a Kubernetes 1.11.3+ cluster +- A working [Vald Helm Operator (VHO)](https://vald.vdaas.org/docs/operator/vald-helm-operator/) in the target cluster + +## Deployment + +### Build and push the controller image + +```sh +make docker/build/operator/vald +``` + +### Install CRDs and deploy the controller + +```sh +make k8s/operator/vald/deploy +``` + +### Apply a sample ValdOperatorRelease + +```sh +kubectl apply -f cmd/operator/vald/sample.yaml +``` + +### Uninstall + +```sh +kubectl delete -f cmd/operator/vald/sample.yaml +make k8s/operator/vald/delete +``` + +## CRD: ValdOperatorRelease (`vor`) + +The goal of the `ValdOperatorRelease` resource is to collapse the large `ValdRelease` configuration surface into what a user must supply. +There are two input groups: + +1. **Infrastructure / node-pool information** — `spec.infrastructure[]` +2. **Minimal Vald settings** — `spec.vectorEngine.vald` + +### Spec example + +```yaml +apiVersion: vald.vdaas.org/v1 +kind: ValdOperatorRelease +metadata: + name: my-vor + namespace: vald +spec: + infrastructure: + - role: green # arbitrary role label (e.g. green/blue, hot/cold) + type: kind # cluster type hint (informational) + active: true + clusters: + - id: "abc-123" # cluster ID (populated by external system) + name: "cluster-a" # human-readable cluster name + nodePools: + general: # gateway / discoverer / manager + name: general-pool + replicas: 3 + machineResource: + cpu: "4" + memory: "16Gi" + storage: "100Gi" + agent: # optional dedicated agent pool + name: agent-pool + replicas: 6 + machineResource: + cpu: "8" + memory: "32Gi" + vectorEngine: + name: my-vald + vald: + defaults: + logLevel: warn + agent: + ngt: + dimension: 768 + distanceType: l2 + objectType: float + creationEdgeSize: 20 + searchEdgeSize: 10 + persistentVolume: + enabled: true + storageClass: standard + accessMode: ReadWriteOnce + indexer: + indexSchedule: "@every 1m" + concurrency: 2 + manager: true + indexDuration: 1h + saveDuration: 1h + gateway: + indexReplica: 3 + serviceType: LoadBalancer + ingress: + enabled: true + host: vald.example.com + discoverer: + kind: DaemonSet # DaemonSet | Deployment + overlay: {} # arbitrary JSON merged onto the generated VRS +``` + +### `spec.infrastructure[]` + +| Field | Notes | +| ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `role` | Free-form role label (e.g. `hot`, `standby`, `blue`, `green`). Copied to VRS labels. | +| `type` | Cluster type label. | +| `active` | When `false`, the entry is skipped during VRS generation. | +| `clusters[]` | `{ id, name }`. The `id` field is typically filled by an external system; `name` must be set. | +| `nodePools` | Map keyed by pool type: `general` (required) and `agent` (optional). Each pool carries `name`, `replicas`, and `machineResource{ cpu, memory, storage }`. | + +#### Node pool types + +| Key | Hosts | +| --------- | ------------------------------------- | +| `general` | gateway-lb, discoverer, manager-index | +| `agent` | `vald-agent` (NGT) | + +When no `agent` pool is defined, the `general` pool is used for agent resource sizing and replica count as well. + +### `spec.vectorEngine.vald` + +| Field | Description | +| ------------------------ | --------------------------------------------------------------------------------------------------------------- | +| `defaults.logLevel` | Log level for all Vald components. | +| `agent.ngt` | NGT settings: `dimension` (required, >= 2), `creationEdgeSize`, `searchEdgeSize`, `distanceType`, `objectType`. | +| `agent.persistentVolume` | Optional. `{ enabled, storageClass, accessMode }`. Falls back to environment defaults when omitted. | +| `indexer` | `indexSchedule`, `saveSchedule`, `concurrency`, `manager`, suspend flags, durations. | +| `gateway` | `indexReplica`, `serviceType`, `ingress{ enabled, host }`. | +| `discoverer.kind` | `DaemonSet` or `Deployment`. | +| `overlay` | Arbitrary JSON patch merged onto the generated VRS (Helm-style patch). | + +### Status + +```yaml +status: + phase: WaitingCreateVrs + progress: + total: 3 + completed: 1 + conditions: + - type: WaitingClusterCreate + status: "True" + reason: Succeeded + - type: WaitingCreateVrs + status: Unknown + reason: Progressing +``` + +| Field | Description | +| -------------------- | --------------------------------------------------------------- | +| `phase` | The condition type currently being evaluated. | +| `progress.total` | Total lifecycle phase count. | +| `progress.completed` | Phases that have reached `True`. | +| `conditions` | Accumulated `metav1.Condition` array. Phases are never removed. | + +## Lifecycle + +Reconciliation is modeled as an ordered flow of phases. +Each phase carries a `Condition` plus an optional `Builder` (creates/updates resources) and `Checker` (reports readiness). + +| Phase | Builder | Checker | Purpose | +| ---------------------- | ------- | ------- | -------------------------------------------------------------------- | +| `WaitingClusterCreate` | -- | yes | Validate infra config; wait until every cluster has `id` and `name`. | +| `WaitingCreateVrs` | yes | yes | Build VRS objects and wait until they are ready. | +| `Completed` | -- | -- | Terminal phase. | + +### Accumulating, self-healing conditions + +`status.conditions` **accumulates** as phases progress (conditions are never removed), and every reconcile re-evaluates **all** conditions. +If a `True` condition later breaks (e.g. a generated VRS is deleted), the controller detects it and restarts work from that phase. + +``` +Reconcile N: [WaitingClusterCreate=True] + → WaitingCreateVrs seeded as Progressing + +Reconcile N+1: [WaitingClusterCreate=True, WaitingCreateVrs=Unknown] + → VRS created, but Get returns NotFound → stay Progressing + +Reconcile N+2: [WaitingClusterCreate=True, WaitingCreateVrs=True] + → Completed seeded as Succeeded + +--- ValdRelease deleted externally --- + +Reconcile N+3: [WaitingClusterCreate=True, WaitingCreateVrs=False] + → controller detects break → VRS is recreated → recovers +``` + +### Readiness states + +| Result | Condition Status | Meaning | +| ------------- | ---------------- | ----------------------------------------------------------- | +| `Progressing` | Unknown | The controller is actively working. | +| `Pending` | Unknown | Waiting for an external event (e.g. cluster ID assignment). | +| `Succeeded` | True | Phase is complete. | +| `Failed` | False | Misconfiguration or unrecoverable error. | + +## VRS Generation + +`VrsBuilder.Build` is a pure function of `(CR, Config, NodePoolCapability)` — it makes no Kubernetes API calls. + +1. **Validate** the CR (`infrastructure` non-empty, each cluster has a `name`). +2. **Iterate `infrastructure[]`**, skipping inactive entries (and, when node-pool matching is enabled, entries with no matching general pool). +3. **Assemble VRS spec**: defaults, gateway, agent (NGT), manager, discoverer. +4. **Resolve resources from node pools**: derive replicas and per-component CPU/memory from the agent node pool. +5. **Apply optional settings**: persistent volume, node affinities. +6. **Per cluster**: for each `infra.clusters[]`, set name, apply labels, then merge the overlay. + One VRS is produced for each cluster, so a single VOR can yield one VRS per target cluster. + +### Overlay + +`spec.vectorEngine.vald.overlay` is a JSON patch merged onto the generated VRS, layered on top of the default VRS template loaded at startup (`DEFAULT_VRS_PATH`). +This provides an escape hatch for any VRS field not directly exposed in the VOR spec. + +### Resource management + +All generated resources have a controller owner reference pointing to the `ValdOperatorRelease` CR, enabling garbage collection when the CR is deleted. +Every resource is labelled `managed-generation: `. +On each reconcile, resources owned by the CR but absent from the current build output are pruned (deleted). + +## Resource Allocation and Topology + +### Agent resource calculation + +Agent pods request a fraction of the node's resources so that `AgentPodsPerNode` (default 2) pods fit on one node: + +``` +CPU request = nodeCPU * 0.6 / AgentPodsPerNode +RAM request = nodeRAM * 0.6 / AgentPodsPerNode +CPU limit = nodeCPU * 0.6 +RAM limit = (not set) +``` + +The 0.6 ratio reserves 60% of the node for agent pods; the remaining 40% covers OS, DaemonSets, and system processes. +Memory limit is intentionally absent because the NGT index uses memory-mapped files and a hard limit would trigger OOM kills. + +### Agent pod count + +``` +agentPodCount = agentNodeCount * AgentPodsPerNode +``` + +`MinReplicas = MaxReplicas = agentPodCount` (agent uses a StatefulSet with no HPA). + +### Gateway replica scaling + +``` +minReplicas = max(agentPodCount / 2, 1) +maxReplicas = max(agentPodCount * 2, 1) +``` + +### Gateway / discoverer / manager resources + +These components use fixed defaults: + +| Component | CPU req | RAM req | CPU lim | RAM lim | +| ------------- | ------- | ------- | ------- | ------- | +| gateway-lb | 200m | 150Mi | 2000m | 700Mi | +| discoverer | 200m | 65Mi | 600m | 200Mi | +| manager-index | 200m | 80Mi | 1000m | 500Mi | + +### Topology spread constraints + +All components receive a `topologySpreadConstraints` entry to spread pods across nodes: + +```yaml +topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: DoNotSchedule + labelSelector: + matchLabels: + app.kubernetes.io/component: +``` + +## Multi-Cluster Distribution + +The same VOR can be distributed across clusters and generates a VRS where a matching node pool exists. + +### Node-pool matching + +When `REQUIRE_NODEPOOL_MATCH=true`, the controller lists Nodes and resolves a `NodePoolCapability`. +A VRS is generated when a `general` pool is present. +Nodes are matched by labels: + +``` +/namespace = +/type = general | agent +``` + +`prefix` defaults to empty and can be configured via `NODEPOOL_LABEL_PREFIX`. + +When `REQUIRE_NODEPOOL_MATCH=false` (default), the controller skips node listing and treats every infra entry as schedulable. + +### Use cases + +- **Single cluster**: Deploy VOR once; VRS generates in that cluster. +- **Multi-cluster same config**: Deploy VOR to all clusters; generate VRS where node-pool labels match. +- **Blue/green deployments**: Use separate VOR objects with different roles and endpoints. +- **VRS generation**: Generate VRS manifests in a management cluster and distribute them to workload clusters. + +## Configuration + +All configuration is loaded once at startup from a YAML config file, following the same convention as every other Vald component. +See [`cmd/operator/vald/sample.yaml`](https://github.com/vdaas/vald/blob/main/cmd/operator/vald/sample.yaml) for a complete example; when deploying via the Helm chart, every key is exposed through `charts/operator/vald/values.yaml`. + +The reconciler-facing settings live under the `operator` key: + +| Config Key | Default | Description | +| ---------------------------------------------------------- | ------------------------------------------ | ----------------------------------------------------------------- | +| `operator.vrs.default_vrs_path` | `/opt/valdoperatorrelease/config/vrs.yaml` | Default VRS template merged with the overlay. | +| `operator.vrs.log_level` | `warn` | Log level passed through to the generated VRS. | +| `operator.node_pool.require_match` | `false` | Only generate VRS where matching node pools exist. | +| `operator.node_pool.label_prefix` | `""` | Prefix for the `namespace`/`type`/`role` node labels. | +| `operator.node_pool.agent_pods_per_node` | `2` | Agent pods packed per node when computing replicas. | +| `operator.persistent_volume.default_storage_class` | `standard` | PV storage class fallback. | +| `operator.persistent_volume.default_access_mode` | `ReadWriteOnce` | PV access mode fallback. | +| `operator.persistent_volume.buffer_ratio` | `1.5` | PV size = `max(memoryRequest * ratio, min)`. | +| `operator.persistent_volume.min_size_bytes` | `1073741824` | Minimum PV size in bytes. | +| `operator.networking.enable_ingress` | `true` | Enable gateway ingress generation. | +| `operator.networking.gateway_ingress_annotations` | `{}` | Annotations applied to the gateway ingress. | +| `operator.networking.gateway_service_type` | `NodePort` | Gateway service type (`NodePort` / `ClusterIP` / `LoadBalancer`). | +| `operator.networking.discoverer_daemonset_max_surge` | `30%` | Discoverer DaemonSet rolling-update `maxSurge`. | +| `operator.networking.discoverer_daemonset_max_unavailable` | `0%` | Discoverer DaemonSet rolling-update `maxUnavailable`. | + +Controller-level settings (leader election, `requeue` intervals, reconcile concurrency, watched namespaces) live under `operator.controller`, and server/observability settings (`server_config`, `observability`) follow the standard Vald component layout. + +## Development + +### Common tasks + +```sh +# Run unit tests +make test/operator/vald + +# Regenerate k8s manifests after changing charts/operator/vald +make k8s/manifest/operator/vald/update + +# Lint +make lint + +# Build and push the controller image +make docker/build/operator/vald + +# Deploy the controller to the current cluster +make k8s/operator/vald/deploy +``` + +Run `make help` for the full list of targets. + +## Further Reading + +| Document | Description | +| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------- | +| [Design Overview](design) | Controller architecture, CRD spec, reconcile lifecycle, and environment-variable reference. | +| [Controller Specification](controller-spec) | Detailed reconcile model, desired-state model, and resource-management rules. | +| [Resource and Topology Strategy](resource-and-topology) | Agent resource calculation, replica scaling formulas, and topology spread constraints. | +| [Test Strategy](test-strategy) | Test layers, golden file tests, and coverage philosophy. | diff --git a/content/docs/v1.8/user-guides/vald-operator/controller-spec.md b/content/docs/v1.8/user-guides/vald-operator/controller-spec.md new file mode 100644 index 000000000..e7aeacd29 --- /dev/null +++ b/content/docs/v1.8/user-guides/vald-operator/controller-spec.md @@ -0,0 +1,346 @@ +--- +title: "Controller Spec_v1.8/User Guides/Vald Operator" +date: 2026-09-16T04:40:20Z +draft: false +weight: 0 +description: "" +menu: + valdoperator: + parent: Vald Operator +--- + +# ValdOperatorRelease Controller Specification + +## Overview + +The ValdOperatorRelease controller manages the lifecycle of [ValdRelease](https://vald.vdaas.org) resources +on a Kubernetes cluster. Users describe their desired Vald configuration via a single `ValdOperatorRelease` +CR; the controller generates and maintains the corresponding `ValdRelease` objects, adjusting them +whenever the CR changes. + +```text +User applies ValdOperatorRelease CR + │ + ▼ +┌───────────────────────┐ +│ ValdOperatorRelease │ +│ Controller │ +│ │ +│ 1. Check infrastructure│ +│ 2. Build ValdRelease │ +│ 3. Create / update │ +│ 4. Prune stale VRS │ +└───────────────────────┘ + │ + ▼ + ValdRelease CR(s) + (consumed by VHO) +``` + +ValdRelease objects are consumed by the [Vald Helm Operator (VHO)](https://vald.vdaas.org/docs/operator/vald-helm-operator/), +which deploys the actual Vald components. + +--- + +## CRD: ValdOperatorRelease (`vor`) + +### Spec + +```yaml +spec: + infrastructure: + - role: green # arbitrary role label (e.g. green/blue, hot/cold) + type: kind # cluster type hint (informational) + active: true # whether this infra entry is currently active + clusters: + - id: "abc-123" # cluster ID (populated by external system) + name: "cluster-a" # human-readable cluster name + nodePools: + general: # NodePoolTypeGeneral — gateway/discoverer/manager + name: general-pool + replicas: 3 + machineResource: + cpu: "4" + memory: "16Gi" + storage: "100Gi" + agent: # NodePoolTypeValdAgent — optional dedicated agent pool + name: agent-pool + replicas: 6 + machineResource: + cpu: "8" + memory: "32Gi" + vectorEngine: + name: my-vald + vald: + defaults: + logLevel: warn + agent: + ngt: + dimension: 768 + distanceType: l2 + objectType: float + creationEdgeSize: 20 + searchEdgeSize: 10 + persistentVolume: # optional + enabled: true + storageClass: standard # falls back to DEFAULT_STORAGE_CLASS + accessMode: ReadWriteOnce # falls back to DEFAULT_ACCESS_MODE + indexer: + indexSchedule: "@every 1m" + concurrency: 2 + manager: true + indexDuration: 1h + saveDuration: 1h + gateway: + indexReplica: 3 + serviceType: LoadBalancer + ingress: # optional + enabled: true + host: vald.example.com + discoverer: + kind: DaemonSet # DaemonSet | Deployment + overlay: {} # arbitrary JSON merged last (Helm-style patch) +``` + +#### `infrastructure[].clusters` + +| Field | Required | Description | +| ------ | ---------------------------- | ----------------------------------------------------------------- | +| `id` | populated by external system | Cluster identifier. Empty while the cluster is being provisioned. | +| `name` | yes | Human-readable name used to derive the ValdRelease object name. | + +#### `infrastructure[].nodePools` + +Two pool types are recognized: + +| Key | Constant | Hosts | +| --------- | ----------------------- | ------------------------------------- | +| `general` | `NodePoolTypeGeneral` | gateway-lb, discoverer, manager-index | +| `agent` | `NodePoolTypeValdAgent` | vald-agent (NGT) | + +When no `agent` pool is defined, the `general` pool is used for agent resource +sizing and replica count as well. See [Resource Allocation and Topology](resource-and-topology) +for the sizing formulas. + +### Status + +```yaml +status: + phase: WaitingCreateVrs # type of the condition currently being processed + progress: + total: 3 # total number of lifecycle phases + completed: 1 # number of phases that have reached True + conditions: + - type: WaitingClusterCreate + status: "True" + reason: Succeeded + message: "Waiting for Cluster Creation." + lastTransitionTime: "2025-01-01T00:00:00Z" + - type: WaitingCreateVrs + status: Unknown + reason: Progressing + message: "Waiting for VRS creation." + lastTransitionTime: "2025-01-01T00:00:01Z" +``` + +| Field | Description | +| -------------------- | --------------------------------------------------------------------------------------------------------- | +| `phase` | The `type` of the condition currently being evaluated. Updated on every reconcile. | +| `progress.total` | Total lifecycle phase count. Set on every reconcile from the lifecycle definition. | +| `progress.completed` | Count of phases that have transitioned to `True`. Updated when a new phase is seeded. | +| `conditions` | Accumulated standard `metav1.Condition` array. Phases are never removed; each phase appears at most once. | + +#### kubectl columns + +```text +NAME PHASE PROGRESS STATUS AGE +my-vor WaitingCreateVrs 3 True 5m +``` + +> **Note**: The `STATUS` column shows `conditions[0].status`, which is always the +> first phase (`WaitingClusterCreate`). This reflects whether cluster provisioning +> has completed, not the overall readiness. Use `PHASE` for current processing state. + +--- + +## Lifecycle Phases + +The controller defines three phases in order: + +| Order | Condition Type | Desired | Description | +| ----- | ---------------------- | ------------------ | -------------------------------------------------------------------------------------- | +| 0 | `WaitingClusterCreate` | `desired.Prop` | Validates that all clusters in `spec.infrastructure` have non-empty `id` and `name`. | +| 1 | `WaitingCreateVrs` | `desired.Resource` | Builds and creates `ValdRelease` objects; waits for them to exist in the API server. | +| 2 | `Completed` | `nil` | Terminal phase. No desired-state check; automatically marked `Succeeded` when reached. | + +--- + +## Reconciliation Model + +### Conditions Accumulation + +Conditions are **never removed** from `Status.Conditions`. As phases progress, entries +are appended to the array. Every reconcile loop re-evaluates **all** accumulated conditions. + +This provides self-healing: if a previously `True` condition later breaks (e.g. a +`ValdRelease` is deleted, or `spec.infrastructure` is modified), the controller detects +it on the next reconcile and halts at that phase until it recovers. + +```text +Reconcile N: [WaitingClusterCreate=True] + → WaitingCreateVrs seeded as Progressing + +Reconcile N+1: [WaitingClusterCreate=True, WaitingCreateVrs=Unknown] + → VRS created, but Get returns NotFound → stay Progressing + +Reconcile N+2: [WaitingClusterCreate=True, WaitingCreateVrs=True] + → Completed seeded as Succeeded + +--- ValdRelease deleted externally --- + +Reconcile N+3: [WaitingClusterCreate=True, WaitingCreateVrs=False, Completed=True] + → WaitingCreateVrs fails → controller stops, returns error → requeued + → VRS is recreated, WaitingCreateVrs recovers to True on next pass +``` + +### Reconcile Loop (simplified) + +```go +// 1. First reconcile: no conditions yet → seed the first phase +if len(status.Conditions) == 0 { + seed(phases[0], Progressing) + return +} + +// 2. Re-evaluate every accumulated condition +for _, cond := range status.Conditions { + phase := phases.GetByType(cond.Type) + if phase.Desired == nil { continue } + + createOrUpdateResources(phase) // idempotent + result := phase.Desired.IsReady(ctx) // returns desired.Result + updateCondition(cond.Type, result) + + if result != Succeeded { + return error // halt; controller-runtime requeues + } +} + +// 3. If all known phases passed, seed the next one +if next := phases.GetNext(); next exists { + seed(next, Progressing or Succeeded) +} +``` + +### Phase Seeding + +When a new phase is seeded: + +- `Desired == nil` (e.g. `Completed`): seeded with `Succeeded` immediately. +- `Desired != nil`: seeded with `Progressing`; evaluated on the next reconcile. + +--- + +## Desired State Model + +Each lifecycle phase optionally holds a `Desired` implementation that answers two questions: + +1. **`Build(ctx)`** — what Kubernetes resources should exist? +2. **`IsReady(ctx)`** — are those resources in the expected state? + +### `desired.Result` + +`IsReady` returns a `desired.Result` which maps directly to a `metav1.Condition`: + +| Constructor | `Status` | `Reason` | Meaning | +| ------------------ | --------- | ------------- | -------------------------------------------------------------------------- | +| `Progressing(msg)` | `Unknown` | `Progressing` | Controller is actively working (e.g. building resources). | +| `Pending(msg)` | `Unknown` | `Pending` | Waiting for an external event (e.g. external system setting `cluster.id`). | +| `Succeeded()` | `True` | `Succeeded` | Phase is complete. | +| `Failed(err)` | `False` | `Failed` | Unrecoverable error (e.g. missing required configuration). | + +**`Pending` vs `Failed`** + +`Pending` indicates an expected transient state — the system is waiting for something +outside its control. `Failed` indicates a configuration error that requires human intervention. + +Example — `WaitingClusterCreate`: + +| Condition | Result | +| --------------------------------- | ---------------------------------------------------------------------- | +| `spec.infrastructure` is empty | `Failed` — required field, user must fix | +| `infra.clusters` is empty | `Failed` — required field, user must fix | +| `cluster.id == ""` | `Pending` — external system has started but has not assigned an ID yet | +| `cluster.name == ""` | `Pending` — same reason | +| all clusters have `id` and `name` | `Succeeded` | + +### `desired.Prop` + +Used for pure in-memory checks (no Kubernetes API calls). Holds a `Check func() Result`. +`Build` is a no-op. + +### `desired.Resource` + +Used when checking Kubernetes resources via the API server. Holds a `Builder` (which produces +the list of expected objects) and a `Client`. `Build` calls the builder and stores the list; +`IsReady` iterates the list and calls `client.Get` for each object. + +| Get result | `IsReady` returns | +| ------------------------------- | --------------------------------------------- | +| `NotFound` | `Progressing("")` — resources not created yet | +| other error | `Failed(err)` | +| found, `Check` returns non-True | returned as-is | +| all found and passing | `Succeeded()` | + +--- + +## Resource Management + +### NodePool Match (optional) + +When the environment variable `REQUIRE_NODEPOOL_MATCH=true` is set, the controller +only generates `ValdRelease` objects for infra entries that have matching Kubernetes +`Node` objects. Nodes are matched by labels: + +```text +/namespace = +/type = general | agent +``` + +`prefix` defaults to empty (bare `namespace`/`type` keys) and can be configured via +`NODEPOOL_LABEL_PREFIX`. + +### Generation Tracking + +Every created/updated resource is labelled `managed-generation: `. +On each reconcile, resources owned by the `ValdOperatorRelease` CR but absent from the current +build output are **pruned** (deleted). + +### Owner References + +All generated resources have a controller owner reference pointing to the `ValdOperatorRelease` CR, +enabling garbage collection when the CR is deleted. + +--- + +## Configuration + +| Environment Variable | Default | Description | +| ------------------------ | ------- | -------------------------------------------------- | +| `REQUIRE_NODEPOOL_MATCH` | `false` | Only generate VRS when matching Node labels exist. | +| `NODEPOOL_LABEL_PREFIX` | `""` | Prefix for node pool label keys. | + +See [design](design#configuration-environment-variables) for the full +environment-variable reference (PV sizing, ingress, gateway service type, discoverer +DaemonSet strategy, etc.). + +--- + +## Open Items + +- **Requeue behavior**: Pending/Progressing conditions currently return an `error`, causing + exponential back-off and noisy logs. These should return `ctrl.Result{RequeueAfter: duration}` + instead. The duration should be configurable. + +- **`STATUS` printcolumn**: Shows `conditions[0].status` (always the first phase). + Should be changed to reflect the current overall state more meaningfully. diff --git a/content/docs/v1.8/user-guides/vald-operator/design.md b/content/docs/v1.8/user-guides/vald-operator/design.md new file mode 100644 index 000000000..56322a10e --- /dev/null +++ b/content/docs/v1.8/user-guides/vald-operator/design.md @@ -0,0 +1,204 @@ +--- +title: "Design_v1.8/User Guides/Vald Operator" +date: 2026-09-16T04:40:20Z +draft: false +weight: 0 +description: "" +menu: + valdoperator: + parent: Vald Operator +--- + +# Design Overview + +This document explains what the `valdoperatorrelease` controller does and how it works. +It is grounded in the current code; see the referenced files for the source of truth. + +## What it is + +A Kubernetes controller that manages the lifecycle of [Vald](https://vald.vdaas.org/) +clusters through a single custom resource, `ValdOperatorRelease` (short name `vor`, +API group `vald.vdaas.org/v1`). + +From a small, high-level input the controller generates one or more +[`ValdRelease`](https://github.com/vdaas/vald-helm-operator) (VRS) resources — the +manifests that the Vald Helm Operator (VHO) reconciles into a running Vald cluster. + +``` +ValdOperatorRelease (vor) ──reconcile──▶ ValdRelease (VRS) × (active infra × clusters) + minimal input consumed by vald-helm-operator +``` + +The controller only _generates_ VRS definitions. It does not run Vald itself; a VHO +running in the target cluster turns each VRS into Vald pods. This separation lets a +management cluster emit VRS definitions and distribute them to other clusters (for +example via an external workflow engine). + +## The `ValdOperatorRelease` (MVRS) resource + +The goal of MVRS is to collapse the large `ValdRelease` configuration surface into the +minimum a user must supply. There are two input groups. + +1. **Infrastructure / node-pool information** — `spec.infrastructure[]` +2. **Minimal Vald settings** — `spec.vectorEngine.vald` + +Source of truth: `internal/k8s/vald/operator/api/v1/valdoperatorrelease_types.go`. + +### `spec.infrastructure[]` + +A list of infrastructure entries. Each entry produces VRS objects for its clusters. + +| Field | Notes | +| ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `role` | Free-form role label (e.g. `hot`, `standby`, `blue`, `green`). Copied to VRS labels. | +| `type` | Cluster type label. | +| `active` | When `false`, the entry is skipped during VRS generation. | +| `clusters[]` | `{ id, name }`. The `id` field is typically filled in by an external system once the target cluster exists; `name` must be set. | +| `nodePools` | Map keyed by pool type: `general` (required) and `agent` (optional). Each pool carries `name`, `replicas`, and `machineResource{ cpu, memory, storage }`. | + +The `machineResource` values drive per-component resource and replica calculations for +the generated VRS. + +### `spec.vectorEngine.vald` + +The minimal Vald configuration reflected into each VRS: + +- `defaults.logLevel` +- `agent.ngt` — `dimension` (required, ≥2), `creationEdgeSize`, `searchEdgeSize`, `distanceType`, `objectType` +- `agent.persistentVolume` — `{ enabled, storageClass, accessMode }` (storageClass/accessMode fall back to env defaults when omitted) +- `indexer` — `indexSchedule`, `saveSchedule`, `concurrency`, `manager`, suspend flags, durations +- `gateway` — `indexReplica`, `serviceType`, `ingress{ enabled, host }` +- `discoverer.kind` — `DaemonSet` or `Deployment` +- `overlay` — an arbitrary JSON patch merged onto the generated VRS (see _Overlay_ below) + +## Lifecycle + +Reconciliation is modeled as an ordered flow of phases. Each phase carries a `Condition` +plus an optional `Builder` (creates/updates resources) and `Checker` (reports readiness). + +Phases (`pkg/operator/vald/service/phase.go`): + +| Phase (`status.phase`) | Builder | Checker | Purpose | +| ---------------------- | ------- | ------- | -------------------------------------------------------------------- | +| `WaitingClusterCreate` | – | yes | Validate infra config; wait until every cluster has `id` and `name`. | +| `WaitingCreateVrs` | yes | yes | Build the VRS objects and wait until they are ready. | +| `Completed` | – | – | Terminal phase. | + +### Accumulating, self-healing conditions + +`status.conditions` **accumulates** as phases progress (conditions are never removed), +and every reconcile re-evaluates **all** conditions. This is intentional: if a +previously-`True` condition later breaks (e.g. a generated VRS is deleted, or the spec +changes), the controller detects it and restarts work from that phase. `status.phase` +tracks the condition currently being processed. + +See `pkg/operator/vald/service/reconciler.go` (`reconcileValdOperatorRelease` / +`reconcilePhase`) and `pkg/operator/vald/service/phase.go`. + +### Readiness result states + +A phase check returns one of four `result` states +(`pkg/operator/vald/service/phase.go`): + +| Result | Condition status | Meaning | +| ----------------- | --------------------- | ---------------------------------------------------------------------------- | +| `Progressing("")` | Unknown / Progressing | The controller is actively working. | +| `Pending("msg")` | Unknown / Pending | Waiting on something external (e.g. external system assigning a cluster ID). | +| `Succeeded()` | True / Succeeded | Done. | +| `Failed(err)` | False / Failed | Misconfiguration or unrecoverable error. | + +For the cluster-create check: a missing `cluster.id`/`cluster.name` yields **Pending** +(external system not done yet), while empty `clusters` or empty `infrastructure` yields +**Failed** (misconfiguration). See `pkg/operator/vald/service/reconciler.go`. + +## VRS generation flow + +Implemented in `pkg/operator/vald/service/builder.go`. `vrsBuilder.Build` is a pure +function of `(CR, Config, Capability)` — it makes no Kubernetes API calls. + +1. **Validate** the CR (`infrastructure` non-empty, each cluster has a `name`). +2. **Iterate `infrastructure[]`**, skipping entries where `active == false` (and, when + node-pool matching is enabled, entries with no matching general pool). +3. **Assemble the VRS spec** from the CR (all in `pkg/operator/vald/service/builder.go`): + - `buildDefaults` — log level + - `buildGateway` — `indexReplica`, service type, ingress + - `buildAgent` — NGT settings + - `buildManager` — Manager mode when `indexer.manager == true`, otherwise Creator/Saver mode + - `buildDiscoverer` — kind + namespaced RBAC names +4. **Resolve resources from node pools** — `SetScaledResources` derives replicas and + per-component CPU/memory from the resolved agent node pool. The general-pool fallback + rule lives in `resolveAgentNodePool` (`pkg/operator/vald/service/rules.go`). +5. **Optional settings** — persistent volume and node affinities (both in `builder.go`). +6. **Per cluster** — for each `infra.clusters[]`, set `name = -` + (truncated to 63 chars), apply labels (`namespace`, `type`, `role`), then merge the + overlay. One VRS is produced per cluster, so a single MVRS can yield many VRS objects. + +### Applying generated resources (`ResourceSyncer`) + +The controller delegates the write side to `resource.Syncer` +(`internal/k8s/resource/syncer.go`): + +- `Build` the desired objects, then `CreateOrUpdate` each with an owner reference and a + `managed-generation` label set to the owner's `.metadata.generation`. +- **Prune**: list owned resources of the same kind and delete any that the current + `Build` no longer produces (stale generations). + +### Overlay + +`spec.vectorEngine.vald.overlay` is merged onto the generated VRS on top of the default +VRS template loaded at startup (`DEFAULT_VRS_PATH`). The merge uses `internal/config.Merge` +(reflection-based deep merge, later elements override earlier). See `mergeOverlay` in +`pkg/operator/vald/service/builder.go`. + +## Multi-cluster distribution and node-pool matching + +The same MVRS can be distributed to multiple clusters, generating a VRS only where a +matching node pool exists. + +- With `REQUIRE_NODEPOOL_MATCH=true`, the controller lists Nodes and resolves a + `NodePoolCapability`; a VRS is generated only when a `general` pool is present + (`pkg/operator/vald/service/capability.go`). +- Node match is by labels `namespace=` and `type=general` (required); + `type=agent` is optional — when present, agent pods are scheduled onto `type=agent`, + otherwise they fall back to `type=general`. +- Label keys default to `namespace` / `type`; set `NODEPOOL_LABEL_PREFIX` + (e.g. `vald.vdaas.org`) to namespace them. +- With `REQUIRE_NODEPOOL_MATCH=false` (default) the controller skips node listing and + treats every infra entry as schedulable (`AlwaysAvailable`). + +## Configuration (environment variables) + +Loaded once at startup into `config.Config` (`pkg/operator/vald/config/config.go`). + +| Env var | Default | Purpose | +| ------------------------------- | ------------------------------------------ | ----------------------------------------------------------------- | +| `DEFAULT_VRS_PATH` | `/opt/valdoperatorrelease/config/vrs.yaml` | Default VRS template merged with the overlay. | +| `REQUIRE_NODEPOOL_MATCH` | `false` | Only generate VRS where matching node pools exist. | +| `NODEPOOL_LABEL_PREFIX` | `""` | Prefix for the `namespace`/`type`/`role` node labels. | +| `AGENT_PODS_PER_NODE` | `2` | Agent pods packed per node when computing replicas. | +| `DEFAULT_STORAGE_CLASS` | `standard` | PV storage class fallback. | +| `DEFAULT_ACCESS_MODE` | `ReadWriteOnce` | PV access mode fallback. | +| `PV_BUFFER_RATIO` | `1.5` | PV size = `max(memoryRequest * ratio, min)`. | +| `PV_MIN_SIZE_BYTES` | `1073741824` | Minimum PV size in bytes. | +| `ENABLE_INGRESS` | `true` | Enable gateway ingress generation. | +| `GATEWAY_INGRESS_ANNOTATIONS` | `""` | YAML map of annotations applied to the gateway ingress. | +| `GATEWAY_SERVICE_TYPE` | `NodePort` | Gateway service type (`NodePort` / `ClusterIP` / `LoadBalancer`). | +| `VRS_LOG_LEVEL` | `warn` | Log level passed through to the generated VRS. | +| `DISCOVERER_DS_MAX_SURGE` | `30%` | Discoverer DaemonSet rolling-update `maxSurge`. | +| `DISCOVERER_DS_MAX_UNAVAILABLE` | `0%` | Discoverer DaemonSet rolling-update `maxUnavailable`. | + +## Source map + +| Concern | Location | +| ---------------------- | --------------------------------------------- | +| CRD types | `internal/k8s/vald/operator/api/v1/` | +| Reconciler | `pkg/operator/vald/service/reconciler.go` | +| Resource apply / prune | `internal/k8s/resource/syncer.go` | +| Phase / conditions | `pkg/operator/vald/service/phase.go` | +| Node-pool rules | `pkg/operator/vald/service/rules.go` | +| VRS builder | `pkg/operator/vald/service/builder.go` | +| Node-pool capability | `pkg/operator/vald/service/capability.go` | +| VRS API model | `internal/k8s/vald/operator/api/valdrelease/` | +| Config / env | `pkg/operator/vald/config/config.go` | +| Entry point | `cmd/operator/vald/main.go` | +| Manifests / CRDs | `k8s/operator/vald/` | diff --git a/content/docs/v1.8/user-guides/vald-operator/resource-and-topology.md b/content/docs/v1.8/user-guides/vald-operator/resource-and-topology.md new file mode 100644 index 000000000..79fd0f063 --- /dev/null +++ b/content/docs/v1.8/user-guides/vald-operator/resource-and-topology.md @@ -0,0 +1,110 @@ +--- +title: "Resource and Topology_v1.8/User Guides/Vald Operator" +date: 2026-09-16T04:40:20Z +draft: false +weight: 0 +description: "" +menu: + valdoperator: + parent: Vald Operator +--- + +# Resource Allocation and Topology Strategy + +## Background + +Each `ValdOperatorRelease` CR describes one or more node pools. A "general" pool hosts the +gateway, discoverer, and manager components. An optional "agent" (ValdAgent) pool +hosts the NGT vector index pods. + +When there is no dedicated agent pool the controller falls back to the general pool +for agent placement (via `effectiveNodePoolType`). Before this fix that fallback was +not applied to resource and replica sizing, producing zero-replica agent deployments +in no-agent-pool configurations. + +## Node Pool Fallback for Agent Resources + +The agent node-pool selection rule lives in +`pkg/operator/vald/service/rules.go` (`resolveAgentNodePool`) and is called directly by +the builder. This keeps `vrsBuilder.Build` a pure function of `(CR, Config, Capability)`: + +```go +// pkg/operator/vald/service/builder.go +agentPool := resolveAgentNodePool(infra) +row.SetScaledResources(agentPool.NodeCount, agentPool.MachineResource, resourceParams) +``` + +`ResolveAgentNodePool` returns the dedicated `agent` pool when present, and otherwise +falls back to the `general` pool for both replica count and machine resources — so a +config with no agent pool no longer produces zero-replica agent deployments. + +`effectiveNodePoolType` (`pkg/operator/vald/service/builder.go`) applies the same fallback +to NodeSelector/Tolerations, so placement and resource sizing stay consistent. +`resourceParams` carries `AgentPodsPerNode` and the discoverer DaemonSet rolling-update +values from `Config`. + +## Agent Resource Calculation + +Agent pods request a fraction of the node's resources so that +`AgentPodsPerNode` (default 2) pods fit on one node: + +``` +CPU request = nodeCPU × AgentResourceRatio / AgentPodsPerNode +RAM request = nodeRAM × AgentResourceRatio / AgentPodsPerNode +CPU limit = nodeCPU × AgentResourceRatio (all pods on this node share the limit) +RAM limit = (not set) +``` + +`AgentResourceRatio = 0.6` — 60% of the node is reserved for agent pods combined; the +remaining 40% covers OS, DaemonSets, and other system processes. + +Memory limit is intentionally absent: the NGT index grows on disk after startup and +is then mmap'd into address space. A hard memory limit would trigger OOM kills as the +index expands. + +## Agent Pod Count + +``` +agentPodCount = agentNodeCount × AgentPodsPerNode +``` + +`MinReplicas = MaxReplicas = agentPodCount` (agent uses a StatefulSet with no HPA). + +## Gateway Replica Scaling + +Gateway replicas track agent replica count with an HPA range: + +``` +minReplicas = max(agentPodCount / 2, 1) +maxReplicas = max(agentPodCount × 2, 1) +``` + +## Gateway / Discoverer / Manager Resources + +These components are not node-resource-dependent; they run against vald upstream +defaults and are unaffected by node pool sizing: + +| Component | CPU req | RAM req | CPU lim | RAM lim | +| ------------- | ------- | ------- | ------- | ------- | +| gateway-lb | 200m | 150Mi | 2000m | 700Mi | +| discoverer | 200m | 65Mi | 600m | 200Mi | +| manager-index | 200m | 80Mi | 1000m | 500Mi | + +## Topology Spread Constraints + +All four components receive a `topologySpreadConstraints` entry so Kubernetes +spreads pods across nodes rather than relying on implicit scheduler behaviour: + +```yaml +topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: DoNotSchedule + labelSelector: + matchLabels: + app.kubernetes.io/component: +``` + +`maxSkew: 1` with `DoNotSchedule` ensures at most one extra pod per node compared +to the least-loaded node. This replaces the previous approach of inflating resource +requests to crowd out extra pods, which was fragile and hard to reason about. diff --git a/content/docs/v1.8/user-guides/vald-operator/test-strategy.md b/content/docs/v1.8/user-guides/vald-operator/test-strategy.md new file mode 100644 index 000000000..b3ac117c4 --- /dev/null +++ b/content/docs/v1.8/user-guides/vald-operator/test-strategy.md @@ -0,0 +1,60 @@ +--- +title: "Test Strategy_v1.8/User Guides/Vald Operator" +date: 2026-09-16T04:40:20Z +draft: false +weight: 0 +description: "" +menu: + valdoperator: + parent: Vald Operator +--- + +# Test Strategy + +## Background + +The controller uses a single test style: plain `testing` + testify unit tests. +All behavior is covered at the unit level — there are no integration tests or envtest +dependencies. + +## Test layers + +### Unit tests (`testing` + testify) + +Cover pure Go logic — anything that does not depend on a live Kubernetes API server. + +| Package | Under test | +| ---------------------------------------- | ------------------------------------------------------------------------------------ | +| `pkg/operator/vald/service` (builder) | `validate()`, component builders, `Build()`, `mergeOverlay` (golden file) | +| `pkg/operator/vald/service` (capability) | `AlwaysAvailable`, `ResolveNodePoolCapability` | +| `pkg/operator/vald/service` (rules) | `resolveAgentNodePool` fallback logic | +| `pkg/operator/vald/service` (reconciler) | Reconcile loop, phase transitions, status updates (client faked via interface mocks) | +| `pkg/operator/vald/service` (operator) | `New`, `Operator.Start` cancellation | +| `pkg/operator/vald/config` | Config loading and defaults | + +## What is intentionally not unit-tested, and why + +- **`zz_generated.deepcopy.go`** — generated code; not hand-written, so out of scope. + It lowers the raw `go test -cover` number without being a real gap. +- **`reconcilePhase` Kubernetes side-effects** — `CreateOrUpdate` / `Prune` against a real + API server are not exercised in unit tests. The reconciler tests fake the client, so + the unit suite validates the phase logic, not the wire protocol. E2E tests cover the + full reconcile against a real cluster. + +## Golden file test + +`TestVrsBuilder_Build` compares the first item of the generated `ValdRelease` list +against `testdata/vrs.golden.yaml`. The golden file is updated when the test is run with +`-update`, so intentional changes must be reviewed as part of the diff. + +## Coverage philosophy + +We prioritize unit coverage of hand-written logic, excluding generated code. The overall +`go test -cover` number looks low because generated code drags it down; per-function, the +main paths of the hand-written code are covered. + +## Framework choice + +| Use | Framework | Reason | +| ---------- | ------------------- | ------------------------------------------------ | +| Unit tests | `testing` + testify | Standard, minimal deps, easy table-driven tests. | diff --git a/prod b/prod index 296fc00da..655c518ce 160000 --- a/prod +++ b/prod @@ -1 +1 @@ -Subproject commit 296fc00da2b1f4967e5943edbbcbdce25a66704f +Subproject commit 655c518cee9e6103dd67db89cac3d09c6f47bc45 diff --git a/static/images/v1.8/guides/observability-configuration/architecture.png b/static/images/v1.8/guides/observability-configuration/architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..16827b8b64d360d591b4405fd305c5fc6717f469 GIT binary patch literal 44073 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

mSV|86{q+==X2`Y&td5=aPDKO)D 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

l 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 ABLDyZ literal 0 HcmV?d00001 diff --git a/static/images/v1.8/guides/read-replica-and-rotator/architecture.png b/static/images/v1.8/guides/read-replica-and-rotator/architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..70008d0195900d56a399ae72079c1a201dad179a GIT binary patch literal 224800 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≪A`EW8>gK+W5r6uN;bK#7y z2M=Dm&eXX%a$f|QRZ>Lj{?>P?O zSnQx!Z1b;vnd%P`T%15%DSdxJNa|t`?n>6J>Y9TTS$PU(sp^flsDI@n%;@co%sxQx zu_9^dCvse14G@?LUFS)d%9fX;%Ik*7509=9ebT@m|h-1G80$o@8nT9H%?Yk#nkjpYB$<1{< zN-}Frn=s_@eCCZ;6de6~0+D%6t8ZS>!l=w({-XOcATUa6_u43|e2d{&WFhnF?E+ql z{#V*jYO6{oSY$ihXSfxRJ#F)G0FFVyjM_UE9;I)j@cEKgprNY63cnwlm(cOHw`8u0<80`=n=clS#W%=p+B;9yEECCjbmLUEE`n)KO8AsC~7$&6; zrs9SM56SrGn$VKyypQv7A|R;1hw7f=nQEDG_3MJSllRQj4<2x5yFJ(&9GnRg&GvXS z8nO3@||pJR@^*M%itjh|JMNzGx$VY0nRbUiUh{E z6{J856`z-+YA6(hW5u@K+qwNMPh?#GBl+Gz~Z|B(|YoCC#seH6Jk{m)Oo%ey>1 zB{dYH&GM4J*mY?pxQ5jH3d@0Sw}rC#Z98!9Kc&Dwv^FB`gHn_{gMeM%=1Q7t#X=;3 zK<2dvO(1NHFS6%og)*jK&Ny+rE9bAAIUDNxfT*RC3FiOmA?Tq(B-@3Pa~H_jHJk5YZ#N*jc5xEnc$M8x)|i))7I9{P;8eeQ4(-8&Pd0E4Bb1fdQD@ zE`ymjL+(6g3;+UMurNZ-Z5LiP!|RCZ|AJ9m@6s+Qz7PQoOPQY=8$=$3&-!?=hAz|U z+L__HiIlRm3wm-&ekuuhqTih0fF8+G&Pb(}wBs)+_}9fgOqEpJS-mX!wvPt?X*XOd z?=U&=`ZX8sW{r7+VWr)mH1Y8eB{?mY=ai;MH$qe0o%wo&Y(+_SPp8g-gx@U8HGy;( zDgJOFk3jrktFZ~RPlus!4yS;Vv*hPv%pH97-WVVQTv8zDm$NLf$^Xl8|E-G_?!jd* z0>)6yALUQK=PQIKUefvq8U4IfY5MCFSP=RhPEi@tAiA|TPfH@81*v>9uy^ZrGmJiA z|3(1lxWo|B9_sH6MRcd=oE}mQu)SD%=ZuAxNvz@sy#(M|2GDFo73~3upBL`BD}Bl3 zAc+j75DugY6nl*l{rX)sgo%Kt)uwTq-~57X5**szEm*mT7yecGB$4&6zK3S)CgHU5 zRtQE8NDn`%FN_rG0;-K5->S|a%`R7HJB{8!f_u0-?)tk`UrV-&2s1vi#u1tKvd)^Bzg zH|dq6jN2oW%vYK}Z8c@z{lwStyZ%@oi7$}MH&yp+nIWEzyF#8L9UY<9%cX$jibHfV ze!E<#*QMtFGef$4Tc~| zKQc_DMZ3BWfUW}&{kX-pwq-y{i--||tK|)S;{3g~8fM6{y80B8d>4AHD(t2!4{);q zXt+Q`F)s|rRVz@f++>Ywxv3pFoyYf}PnqI8K+7!HtEl|_iuk~%!l$|hAp*oCkoI44 z;{sA&-!9*);?aw+LNXwYy6HL}!Wd~e{H)@4#{%A}hN@)Lzyro`34%*DHvm1Il%WP% z27KDLl-h?(-22Lt?9IXVFJF=B+F}E3qf6I5lV4v@RB%BVjpMemf4gu~G0xnDW|3y0 z&Wj%Ayo(78&TEZmM1#r{q`Fu#X$79{0ut<>M3eV^BiP{Qg?)j#vYgTEd=d^lO$dD! znRw|v+fI5aUv37_irC?2J^C9p`?s3FU5x{xg(W!W195|13j^TiT2OofUWM?$ z#OjtkXWsdVDsAmEK*j}SWJG>Jxix?@)ac(|@oI7LUMS?g^R7yH3#f$fgY#yzSt{xLuuCp9~;{2^@q`r&44ppI%S~@CyU&SK%%Ir&4tna zJRJM~d;gALNt_mYM}^FiZJjdH`^9>BkvMdHg=!qS*M>l{(1WvZ zR687c7jyQL)olmC>M!kKWZPIFR@{80kklzjvWpIL{mM4|M>Nc(;r7l!eyCTb#4Ca< z!{je#ic1H`iBNrNdFy01tUco#6g(F_ycrDoalVyX34T{A7B`pOuqp*KmQ4@9HK)vn zmHf(d|0xmB?Sn@R=ZE*VODJ@tgrHTN7=PMQu23Z5m{u@;Tb-?X?}PYlA<@)8!UtWj zl)$KYUXxUCJB+Eo38H5kk^XD9|JVU!1Xno;z^CM(3d)RH5iPvf*UAmN>JU9w4p{>L zae@i&%QYKC_iDCv>Wh*BXPt(X9hdu(-!zEV^gXMFc7+(go-?T>%l~|)e?GXcAudy= zzXgFSpBNb5?htPLr;y5~G{rdx*0Xwjvv|^{&yB&E7q|niA#KFKwF8m_>9q1!(+bz_ z+P^O%kpKeF#qXU^sq3s$OBB?8p|8Og&Oc$>eIl9SPMSkor^*a9jupNF)aEo>o!*2o{;oPWm ztxjD1R;K`mb!;-k zC_;UG2Icp%?g>HMJFV|((N31*By4Bu{!mbd*u%RtK^qR{&K;9F;3Q+{!NZr`vuOR@ zo?t@mr1fYKd1UT*s3EQ0oGsPsD(N6_1UcjbcYj%@6^}Oge>2tv_OxU|HCpe_^7$k!^^_r zi8zCNC11V^eDkORvTTrV|I3j(T?YiC+O?53pt%S92f>U5MM2t>VsM}k5PBq@0wGHW z5er} zH#h7QI`UKR33F{`BxvF)%$OzWD;V5rR!U0(jKj*KZFL(($!d^ceGhe-ev|uh{2SQ? z3jWJuP_ftlFlo}lDjB%q&>DzQ&3y~@hvSQzL=9ii3QFdSu`0qJ(!WJK5}1|Ve4ASI zLeNlaoKsjrB3HKsz1G|r+IU)J$=cl*3i02CK}`iXf*=uIGTifhf#y-b9Yo^%-MvoA z%t=RJEEg63yphDXO@_h*lS%fSsIx~1wI!H&V*m#oFeH=<0}e=X(Mz{_`VPp3Bw+*3 zQ?*NSQu)uz_K&OYejl!u1HIfK1O-MCU_o^czX*Ng!RP+#FGe?wVM|s*6kK4Wc4}d( zkcg*Fr)Hm>gn2c{nAw6&sZrR{HFnc z2w*dmV4x$w_<7;8U#Nj0`Ui`4X*qz*w91UrE}_AGK{irb=<;w_WVGB~^!5z`mkoCX z+mKOt=*j=#*8JC4`hGxNj1{?Gh%J7HN8^v*X7B^@_BBVb6cg*HW@d-wV^*2(sO6)tIhi7dfZLgA1B)=f`vE!Ay zG+aG3<@Uf#z%JipHVNA=Nu}FmMd*UPA5A;Z$3*%0Jp!+#?i?8IY5=c3Rajrt{qQcN zwX637?0mb{mgQ^K^=3Up-E5kyAX)kRo2`RxPDe1rGUaG%r}DR^|8W=YatTC)9jCR; znL_zOSA#-zv^EW-I%QVF49+h0TCVr~j@*KXNqY_xAf>#=0J7DtQN{KD$yWbTP_Zg- z2ba*KZjCQ_FLRB5nd2YQS+_Ur@;NFN#$r^h$f0?;VDvbxQfs#$;B^6xjqRwu%a#Tq z+v8tX2N64F)Hzx%R)V=TATYhADT}02XWgqNlocNh+cU zTnnII)Am*a>;HSqf86T7H`PKeB68KQgNeHAHi1je4H*|neo&0+goB8MH2j{y)M z{Wu`blI%!*Uh_dH(FHnNB{tu5hKdzuy=&d_kIQiVrx7j+74G8mr+68^f@3OQcx)yI zLHtJpn4Bs@_Adt57k9xV4ho>WPkHVN0?s#s+CV_7Gv|Yd*~C|8DhgQo_KLm0u=`Jn z5~6$U%8G*4A3X2Mc{=*7c{sje-Z@@OZ0bwBE8?Nv75D!U_LX5(ZC%^2rBS*>Qk0eu zkcKTSse&RXAqWymcWR=0bfAp5uFc=ZBX^*lW)<=a?h! zagSr3FJyQN&uqBUGUQ7Hg6CHFo$XC@sm#@METZ0HtyM2ur;|TBi-Z5XQD>XnDGmcX z^Q|fGx&P4$U+~}oLx9i<5MS7w{Fs-1V<6KwM*D&=(98vdd%P`^A%%cckP-bJh>agy zvYEd(N^?O{ecBX%Qb2VWZK#nmJ!|~tGnqKu>P!ZWi2EwvbHg?wXezuHM9>MW(cde5 z>a96M>DT$o6#<8(ACH9X2gOCJcdlxbWOA^tNq%c*;mLI!!R!A%_@6R{fFzc^ zK{qYYK3K`)?DDi;J)1uQJO@DQ`FVHPSdNQt^>8lQbj-Bs7|NtSdhRy0&A0?Z!Npn`{nb)b(K%B>Nxqys9eN4pyrZ zkUi!-c&j&{TlL>%RT~gS)LEBwp*>$d+oA+h6u$LCFz>lOh#hzU%Us$qag5Z%+%a;T7wmwvV^Vv5mKEtj6%BOFm=D z(aT>QQ7(}mxmmE48g(BwEltx=Kw-j9*mBYLPJ^CLBwN_Kx3;KZQ-xC^8q|wDls9|+ zV#mO${VyUv94`p8qD&5KZ5+A_Dc2-85@i|1&YR5M`}rmdC`Nk_6FOjL|Bxs}Cyg`D zy1v3n`6e@#LJ^vz;zw||0m4gBTefmu3f)~I`50G6dJuw2Qki2X*FO9$o&Si zEB?N(e0bik<^#PYymFQH6!jd_nM;JYmNNG=KdoM9Ci=9_*pY< zi3`Wk*M%lUX$mrou9_2C*`#RGE7*U&EclSikFZ@yd}Fqs>Y_Yb*r(}~S% zJw$SLyGJ&Li#+ZfoS}+2x~Df~`M#lLF4L7;6DUHu8VggeRciZc(2ryu#r|cTNw*VN zDFgcN_ZkEhhyIe$d>S`l-b@aLXVRu0v8SqzzOH%UNSZ%e1Hw;sTi9=Wrpk1DgGO;=b&A@-^6(HBv1 zx3|vfWqgAmFu!7dND6Ya)Q=(k`ESY8R>6GM5%17P)pxIG^w+vX2>Ad#o1cbVN*rDt z;ARc{PzlCTpgmp`LWo%Jq`ae6n5QK!q z%iuI#1PHBONwm!a%TfiR-Tpi+*Mr40(w+*ZHiL{b4x{;C`gvhCFGt=dlk+>*V-}`C}5j+0kb2z38gwYA;-W zQqwpOv{ULBNC*UJk2~+Ry;zGJnqGzMXDUIH7!!WWHcs$xVnCs(=x0*Yhd%xS5 z5=_?@)f!8sS5ssM!q61|jNw-8}iP_~{=o@{ZV;g%i`# zCnTpbx4YziI$(@bvtJLBXIVT-F2##f-tVnp`8t{Mdn02;;v;66LRuAIC-^?k;1e0=^v*mZNc@FPmg-)`ch*XA;B}-{ zk41S>+f5*L!U&W2?c`XcKrXEf#jY;&^bxJ&fbu5!N-#w(iDueMDF5^C?Fg1hu#o4& z@NMh4R`8+Y_rk0sk?xd7_&_M}dMS#Md7_?n`IyIb@)Z0KWy?G})7!5br2(u&pL{6- zY6CWG^N;B1Fadt@=C$>8kFm!+>tQ65QLk-x?nc~!5$qEqbZ7$DG*xR`a%sl6s$@s~ zg4FHU8P)#PQjEOf)&}ioIVsMiKLYnSsUvfiaOk;Q+^b23SZ>{yQbg9 zruH3{rBM`Yl^Xz`qKCPm7J5E82GNC$X5y8OV=jPca?DjL^jx{lz9ARRcAbi4 z9n9=7$DWLNTuY}-IBrjz{G`GjGgKW2CZNHaM|BF&b6kM*koWKn}z%thF1#COgL z3I7rtpeK6=v_M8hFf%s)m!brjIu_(wLlN_Y{d>}s9&dR$L3D+AxHk83niP<5p$jkE zR&wYL#;cf?-yo>RqPVc35Pi#o+FFO?hPr#Don^jxNlF^UjhfeiKoSy4P<*Sl%dGj> z>ox6VRk7DrgVdt_BENJ}3A0hA{9jCJ9ds;z-*z&5SXD@EFr!Q7ZS+;(;WSRZf62R@ zq=qAn&Hmh%)SNPul0T;A3RY_g^A(h0j1Du%0XwGYypOZ?KFz9l-Nipj?{9KI4#m&p z)6ZCbeUYwgg7*}Vt}FfDC>Yz*2=9UWQG?A6!B_KfIzvLDD`6;}0dP6~7}8;oFL^%pumd}uUtI1lwm|zDW|2^Au9tMrn`=Z@Zv48w z)!78_dI%?X6;D(Js-^G|Rg5uOSqTjVMTD4#%Ut|2-xlqaW`#u?M^|rd;j-}FODZZ1 zarX5;DbNn26HcnQaB`zQK`(rADf0SX93YNflyz9tK!9OZ%V%+6?VcADuUou0WA#OX zfBmxOD>!}~7|7#Bp((PX|K5qvXR~cQ(J7p}Hdwfu8u-ypv|-Qi)i7C~lyWqb3UZvK z055vNXGIM#CL1TYvJVEvJh@m~R(>m<^S-;b3Wkb*yunAY{d9i~hSgT=gWNe=u~zP% zcQOVgsHi1PjmXsGC?s_p4)rq?+^NO>?nrl<^% zv+~lUK0RMeG{-);bPu)j0%^R^^eKms5^<@9@?e$*p*%^M1 z^^MEJ>PnA}^Xe}D%xDpial+Jy*2M6iKPL<5f4uvtz$;sioDLGUi=IfQ>@BCt(LZwg zDU|^D>KLG8(GS_RGTD z#b-?12hH*Pr_o$BSF$TD3)esz7I*{2M_-hGjK%y|toWpPmP1LEbKCm{@SCKc!dTUA z22*bdO5XmnT>V=vCGa~*oWwlKXUk6Aw?|FRE>U$U=zq{Evtp`z42rUXuh(S9{mcSh z#Z9wFO%kuw^8YL}y{4_svlV%Y9}vAAxzg1oeV{GX_SZLbLqWSu^RP`;FFWZ{;=|=% zcjjUYs8N9CnxV;!$x9~P=wmi4Pzk}yx4m;c4bdd5Y(?xs4su+A^Y$F`~P?agFA5AGanW*1KLLzt&v>w z>tAuD=b7d+yAy?11I`s`L(cLj4$JWxVuR+xsNBKz(aNOWnuh7i zA4vcH6;=1t`qxY<sFf;X106%);hKr#-IXG01 z?hhK53srqM-`2xcVfiriuLuqTG9#Eq-rGyoc`ssqS|e=%P^AFe?*f1f5!kbhaHW=! zeF~Gyrpe&v2-<6%J1lZHoG_$&=+6)ef2twyHdesZm-+~19-7E<2L5_vEhdc@`=bv? z#4&I$jSaa%@w-rKDVmhq-u>BmXVM4Dbd}f!KdXzsF5W+n1ts>OoGA?M0s3DS%t1Ec zHb!2HoQvUZNTml@gfR4?J0%ajhyg(gM)R}39Ed*|a+sVV7zT%LT+|kei~t0F#f6tc zj2`|{9b^Ox>JP}pNo;_7M`jEP4!Xh>Hvb|W088S?Knapm&3=>*5-C7x%Z3Fbl@RLt z&jQLT&tfkG8wQ2rO1{R-O+RR}ZWAngud+6pd`&_3w{z9T1DUaVUy@QO0%U-?{q@Sg z_Xh*vp6VQ$Ko!kNqW01HKE0tij^ zt%l~`&coS*5pnnnCWIY|!dc_5{;Fp2@X|3Riwr=GK)jPJy3-bBFj;DM<1-;~&0)Yd zth0}3`(y0u8v3L4!=H2v?@L+YVghOk=DVU~=)w#)a^x{PgSJul&1 znCaX!FlIm12lP09JmeD=)gPy$R#)-@w-*VxDt`*gKfeKfSzH$K=5wAcN|;;8NVn+! z7C8=Xlo38YZt}Gbq^ZkrrgEeyL^7T zf6Gk~W)b+@utveo|77XcUUKLHiRO|rK>qg+z{*;wkSmiqs2q)dCl9nKFaQ%8g;bXa zSkOzb%o1eXqCdCp?0v+QCP0a-OYopG@ZF_<=@w8h>8*M0RbKozr_&OMR;U=%%m@%V z|6URBV*q@>a%AM75^Ii8h{<(!J#btnF#u|w_rc$)T+gSho zXz;5SDUk!pR4}N^?&aV9*N^o<5o3QPzeNAMjQ%LlRy1&slWqy5HIM) z?8R-Dgff`^^|P}NgGz2SUJS1BRf9?KK8|w)Us4`$+s*Fu?RFcmM}&%?CWPsVwDS2s z8?8Nq$><(%hQ7@X%mI5<8QzJA2kn7upR-IJlb8~Hz!j${O4Su3l6H`YxpvLf4;;2 z?iDM-7f)ocIY;3S{kTAfh&vhvfz~vJ+TY|CT*6Zfm4^-7SZmZqf27W;vkB9WPS%oz zjV5F7-hA_)BZ1x%P@IFJ$1Z+>^ZA`8V}+`~n;&oeZC{}G0E%5aP`2PFF6{Ru*1iaB z=VZe0^}l_L0~3W=ga8tH%*u~}=f5oo?gcK3Ad242zkE&~Z01Ug2g#$^4+QH&Wc_dR zHO_yQj0C8JuQv%kLa3rYP66yU7J~SjD5x(8trqn#msEtnC9(HDm{DW?@ihrz0KoBI z?Fc>1^A*y8t#>S#SVrZ0Ysi}xDz(?X*6levG3|8BTK3%cCMj=#;L!)dmBti}CWH4T z26g{<{MjaV62dX^7RH6u&@Ugje5*FZ1N--t`+oGFRAUiB;9Y`w=XU>#CE*xB394if z%zc$6bv0M)da?J;byX zOc{H>rSEL>0AdBaVg{XQ)o&fZc*S=AHy6M^`zwBjhMG{5+-@bm!Dv%3M3*P}4WRh? z9v0E|<%7xj?*)kmz@AQd`P+Z%AK-0wbZff!h-(nl_57~Aa zd~kkUKXn=hVKrk#w@^irZT{drrrMS}G!tKC zW?dB>k}7+Z`t;{EKXHG*Hx|@gV;vs8L;Y>?|Ii2~n7}kYwjhkc^+E=7g(_y=$!$|` zyjOA?{cXk|`GKPY-i0-!cXxh2^W1JMFd)Llx(?Oe>jh#WgB-?Xney|l4B`aBJ>#m% zh5osL&?*wRU=Gr2{=pgleyp|wxQ}TKExrHVcJQg~AS998oq#d%IFkRI>1_Zex_b^& z|G$rGccOIGusomwJ|#d^{FbeM-a-RfBrHnp-wOdRzi-Whr*K0cJQeenA9p!0d~U&>GbJJ z;O@1doicayf`s{tvbBgg=SIRpr8HbNax$9a}E zM*E#!BHJf>RaL#mU_EOfoC~xOS~XO{j-)VETEX;wj0Wb(g9Gw<&icaVKT2Jj0Mh_l z#3#ul{@gZ8A*#V#%fv{2innM{m`4zIJAm}5b_0AE8__P2IM@Df4Cz1Tced4UkeGMApi@7|bnOHaMF5j$2DJkJOpy4=`01f*$_ya& zcD-%2CVhiWeJ%$7Wl7t43IDHff}@Y%N5;vHw5JV&&RQD-(|}aO{k2Z;Uxeh(m0aVf zKxr4+YSr|y1Av&gj)TrvXXrqC0Xubx%W{c&|KQUZs969EISsAFwQm&v4kU+g z3|O@BlhyI=A&3L*e{}b}zb{MLz(C z6Z<6aqo*Ok#1z+E9WI8bxEDr2nq&?1C&b@EdIrY)-M}{VhGu$TUnZEKI|iixD7w8j zIj8pln~-1b22?ZC-lqbiE;=LTh35H-s`SFVlZ$_H>U%Xz*t*QyF0j6qDYPf}Zk^{S z*=&ef=131V@$~Zk#oPAu^X;*IdPhJV95zaeYzCez1TtS5{P^<9g=W0tM)go*Xv&4nX zE6w?%#DDDHPwnSX4KNb*vYg8kWFA(}Nw41dZQ{`6k%kTdD#xldQZ2; z6R2YfpAETIuB`RMjY7#*1PH|EY05Rb{=&GOu#yVBy;0i^TTIiD(hbl{Y~tfI+!I&N z*l961_l!IB$!9=EHWOmRF#_Qn~>_Rp(Lx3msJ12P1HV!^KJJU%kWv3|(jr z94Nb>2`DDP{qpBvfMQ!aJz3kjar0_5p=H0?b56X*yasAlC&5c1&$Hdr4&@WMt9Eis zp|Qr6{8sMADA(tJs)d+n+y$sG0o#lgG%3lug@8ux;<4y)k7(#iyxE7g$Ef&@BFx1V zlb1{xIY)AYW{`vq7Lu^PHcDn)_0;6ZwoZOgH-X8jHmI;U@J-bI-F*upz88p$lFEyZ z{a*gcBSAL(oW`q0A%J5%*N86GKG}?f!)(-k{+MG4!$IG16U$rXWmUk zUN;?I=6H{FSjB{=cukT|exP-^0&R)N_?stM!;wk;p?uTOP~toRjkk==wrb3Cr7yMKKt(cp_Ikj|ivk{{EuSKgp4^NNcPhW@mOB=+fG-&i{jl32GjQwxOVBTMV zS>GybShNV&pQ&iO`I7AeKj_D_d<9VOIMZt}XpRO)7IeGwQUvuV6#2oA`OV$)zXotz zXH_r4KwnI+_@u0L&>aqc<71z^BH$*X;8^pbEaWRYFB;5vheL`X4(&t91f+Q*k_T>N zHhB@6qNu+?#SurL z@{)|ehtP$Oun};H_*}!o>3-fl4u*~7ybnwFTN<=kF9@&nhdY(}+1ds$Oz_%_86qCF zf5fC$9TljUsY{Gi3gJ2ej`;tL)nC8jJs$Z=%t3!y3k7noTy<;%vjZ^1CzSP%xBO#r2 zc8#u@4$!eWf>x1H1>70Q!x?|G(H4 zGa+0bH?AoOZm3);O5X#M1#vmehiHRI@lmmPygDYz^-M5 zQc6cM)?-~9hu=LGU5}9o8F6`F;dDRn^pUK1s$R4h|Es77NM_WcoI&I9Ywu`lwIVY# zPEQ}URtaqOmlLKf&kLsfFdnK<-rR-8j)8$*Q-Ik2jo9FwbuebbU5>mpasb0km?to=@{g3wzlks<-Zd4gR4**ixO4}{N#sK|{Vp{`4|ei>?S$bID$-eieHTapI5gw zMxa~Eel`@p9#w#{RgAC1jzH|f+`9@evgZx!D5|N9kFsc?bBAHiwF|q zi0o^BG`H#=QoED%H^OhAcR27-_=baaBt%&IGV~N{gZ6TQL+9tg;Ih@n4@iEeg5cDc z6Yt2UhkeWLI1fGMvl&#=^6yW$lVs)<;#UzKNF|MTDZbCUTrP~&$Dbc>w?SeIG*t@p5aitoZ~hHj z6blS~1BFmp{cuu2*xNPoFgO?D#SMw(?<6%*S?jsKgSun-b>PX{Lw0Q7)$47F9~0=J zd|F^UN3kbKKzsNa6)?>|3JAsu^jaRM9=q;-7k2SWJUMl3Rzo3{@OT$2i$UOnqJ<|y z#fZ1@8h%+4;ejp@Ld1hBGlkiKj#qg*!P^t-Ax)}#Vnz@O%-7;B5el~o%6*CP&wVp{ z{>S|BhBkT5TJy$x!woxL?a+TC&-472ohxnhwR*T3W1UQu^B$6Sa13!MD* zMlfYn90?f(5B|>|G`TSK_F+u-ECv90d-~*1yqu0^BKvK-Gci6HVU!yZ41>lEUJuKl zVt(PZtg;B~GsXQK(1zJIRxTFomxSy-^2Z6zgkryEyz`>yNd z;GR-*-Qf1?PuT}e29oSMKKk@0DV{R*Qai09;*_2Zq!!UX`qrW)=vbV=@HTod&DqGc zX`Q0!8+NkoLMLsqlU?w3t(8Y5XxXR)n0PL4n^{_(v?`W9o(!%iSu0CV6H<=o6Zffu zB|pPx@|e1VNz|o0a$WKlAiT4Pad&>2_qe2e$oQ6W+W;@>)leD|s*K45PVho!Z}t0+ zkk@XacHpfJ#yi!gDlhT`1S@hVxVVa&gPAs$LZEo9Sh18u z4mZ$AP}&fh`IXkxE2Lf7nkxF#>*BFvPsbh9&F|%F!=bHl!r#U&Dxr%A9fE;|JS*>Uk2X~t$*af93%%}%3h)vGb`s0OX;GFDi%8980|lCJ;z#b|3e9uL}2PFu+Usag@n+M_D`SfJqJ>mauBF1G$s?|(>7msM{Ttn zxz-FF?^~pXx`OuN6YA(%i!r*2HR%9r*R(Bo09-A>v0{eEx+D!`YD9$s`HFR8rz?jx>>y!raX7e%Wl zgij$CtJt7R%s8%OY|CM-at{U+p%TiT60hyBwmy>Jj~~NDhbaO*==Ub8nI;5nRxnJX zx8)H}t{&TLtLVP}P-c%;njw1aeS_EGg-LSq8}w_J66f-a$%tc2dQ#tT_0#t0=eK;O zvRNH|pm%9k0co+7ZjYH&%oS=_AlFKXI8H{w9Bhb^)d>gER!*Dr<@ch0NgFDWS!z7V zy1Zj1+vXzHC}rQ87G)(sz}c_REBUb1&`7NlfqcZr$0>fzW7ppN`j8sNg7E{Ys${(` zq(eLp{-t8yBus=26@DUh_q_V^>K5bH~Jd{@v_rmylzW6|jhVF>1N~REr;hjX4^|!Gr@7Tm#0i5m=;Ni%- zw9AP8Q5H9v_Xj{em)>a*xXmim|5bV#|Gd6IgUzASvp>BmkfFmrDN!s!qz7^ciA4RX8+TG}6Tb zWSBN}^j^_aLsY17O_pz+XG6_({ox>AMZ$1#og#3UygU>Dr$QfVSfi%+gyl_|uSZrJ zAUMoSY!s*i-o#dT)sf`1-YGHD>18hc-YnIQakM(CO98H?niea@n{Rl>Fsg#JK^OR3 zGC5ymtG-<##&PlM1JlZ#hjxxDSMZ4>aM>xsD{E_}Z{9B=Jg<>;XeK0$D;nboKW&tJ zZibzRH{M1=LZTg({b7B~5F zpaP&abOVL^+w*%sbl(AV_R4GwAOzB__rIs^7iPR+sk{OPe@kl9JV8AHnbv3>8!+_k-_RB2a8^m)QlhKsQ9cvk@yHjZpANv9#ALUE-f~CzNM(TI$V6 zl6r#`!{7|mRa9`4e)Q$j;ZUPD4x@e?0PAP@0zu&Lto+p%ePpEKTMt;Dbm*J{lk+0# z{UL{ZS+fNGK~_W7w>G=&E@|r>qm%{+7s| zFxXm+xKR8e>LhEx^{V#izVvpq0yaK8VINkeD#h-6cY$!aT{v&51i^Lnx3R=0?X-H( zBX<092P7{d6@t^#)7SM8j{LqKY}Up`<(BaWY^r%UCUShb(qb%pDOb689DShq>Nf4` zlV6Xq%~_fqcluPn)nSG74=Dup`a@mFAQ-c1Pl1=#%&=D-6*QfKysK8Q4S|yIPRLY( zV(Kk`9!JhmGT96i{LUBL&c^v-kfX<Cw`IlCtf=36$iCmcBUMYeg85=J)4zDc1 zI|j2ZW1W5J6`~A&pafffds%{dV)Nd%0R-;4zL&_k8|{tUhKS2iwrX^FpK+6&zp%2b z2~}5!`>~FeIUlsNKD!$%^WgrE%Y790l_qjuJ*2Xex{2?d4HsCG&fBnenqKG;w{|$) zne*dn`P9r(JAJdRRcxEy3k7nsfEb4$;3X!b2iuEDOaq!)V0Nj6@Ze!~VS9JMn7Ym5 zd+UIagt@1+=K*KV(_D|X%1vKBBe6ovx6ID~-mMKvf{%SI1P{nGgNA4>)qW%%eApHh z`u#!T#JD#;9Vqsgm|9NOcsP77eYFjmbRU?h-g`6~102h-F|kusTnts$wi_@kU6yy5 zOcUy;8nN9Y1~13#rr+2|s1e%t^#*xr02}YHU98jO8)zg|5Qm=&-v^AS1~~T^dHa%| zDyKHro*b^_<&^@uqe|8LocmA$tgK@K41^{~qI!IC2=e^k7kGCJJ{LbkUBC699%as& z)4+QtR!zC6SXGHc_4!U)=V!EvB%+36KuY#+BW2@e-W~{RZLu@b8y>9H|XN zf1S)vr|~WdGV0fDRohTnPRW7oO)(x>qW4eD`6WZGB)@yd?LC~~3foxZ?i*UvkZ~%3 zBU-Okj8+&Aezpji))N@*uu>LX_wd$JfBYkKuYZi=c(}+g_)hRIp{Jyk;^n5r^KIl^T(<)dz0=2 z(?|Z8Md-=v%cRL)W`ECTJO0@@Rz6m4xg`2?bfv@1G7>pkok^olp7kM|n4=}{A6b7s z$Py%5DZg4#xG;w+Y&j}74%m55b}qW-F&zecY&L#X`u!`1!UHkARw5f~SI`i=J64mj zGZ1#{^LCUP)(U{`NcXtTpIeBJ+sOe|;H%~5bd}p#Z%!W5JF~%g6FmuwcZu98WF8#2 zRwzFjh~9XTOhUBGJ;oEf)V$&1d#v;V7IHbVW56>R2)d`<^OhP^l*dnYOZ4B_PD~oToAZu_mo^*ZIP!ee?0Fn{ z5>b{|Axk`O~R5_FRCho?AegN-kT zALW}JN|Zgho{FE(bv)^`+DEzunACdNNcOhJ%-4`srT;J{Sj-80@h3zVFPbo2OZ`Du z#;JDtAf5h!dv-!wk_Cz^+r*O}v*Z|g-D=b@2m5%;gGWKHHE|T}u48#Q;u7JqhgDbV zYztN5enY|P)cfhh=-fysZdN#4Eov$vj6%*X&7+(y+GEZcoB=8N6k?##ENu+8d|7f! z0iO=c*8aV+u=Eq27*N&my>y7XXF>FC;ML6BL!qW(;Tu*U}G=1 zO0;#WSf~^b;~w`C_4kf-B}p3_?SO7M{hP;_#D{?GKD}xCM5DQD;D&W3$f2K9+F-91 z$qu)lkbT%S5=JVlFR#)c-A)*qQ6Cx`+5v+jWRwVTW8`w)IZTzu(d90-M{VXl_#CU- zGg2g|lXd}DbS^@-HVgIW!k$AU7L~D7X-QalRmA6JBbO@C@s=hz*ObZDncaBB@S>^N zGPmRM3X0Kt??3IRSKMZ)7SvmbIoMeibfo*-M81emO%vsH-qpEIc=zb>`;2PF(vuK9 z61yfePeN%W_!`mLJbxt*5pICy1-bF7_!;=LqB;T!L`fX6^0$6_;s_zE!IGmT&qDw2 zlP$Vj_aCHO&Wftdp64{yvDMQ{nd-snWirXEewHaN|G7W*VbBgRC*7IDzKu)OfQiU> z)-uQVW%J5#v0R5t{|$dE4=T`7;V@h@w-!~YDW;6z5p)8egQ)QAM+L(PvH>Co27+u0 z6!%LN6eKi(p@nNY?>v~KD40)quE>J16e#Z4_F+IqwGhy5B{>OisN=4m%!{7>0EQw9 zXGeo?_;G2NE2ykk^k)t`RDYTqUuHuYMatY-%AcZT=u2Q%FOu0Lm%hQp=9I%YmL!d9 z%H+5P&!IgXHqP^r#2tHxoHO*fL*&rCH*%Wo{i5OWlD8%~j>7YStgX0kFhHo0iQ5QZ zPgJ6JwvA}hzt~JxY^&yUGg-Y{W_5T~T6;9$X=6yE>3VEkS+#v1xlKV;x6FE(WS{NB z6}6)xm!SML*)ER|$t&I-&k^;s=fl0bCon8E`+S!2tOI))fhk1=dB{D90Kz?;-7)U`TsjqY*u* z{_(g)koVqtVgNFOUm6l(-Krjhe|e{=ghYGI9JhriA2t7eZYu2HD?^VsV<3>8TE%LO zbzvLIgg+xwI29^4<%p(ROO_fU9V7R0M1!U>F${x93qEO9*&1A@%me=7*#0n6XNK;# zNBHwy_faEAzw^?fpZAFfm?yMIV;3hdD~T3v!6b?9N{k3b&*`HocHB>`nPjd<*|>^> zop#&F1sjugAWiMgS1f{x(jjFp|y*9k6fTvV=%a`_# zm6($@wpf;WdoOgKJ&4ngS1t~5_>GjXYL;W8v(0WCvw3E6qYE-Mv1Y=_<({rlBF zA}~C7H9MSj#8eFD{DxnXqv61d3oj20o|Q4hhtnv*Xz|T!Chc#glAljo$haeQU@O&f z^xXO5U$0N#x*NaIflX$VHcBHQ5-}OCj-|+v3NwxHCz0X|3==DgM4wyoAeh_hu+W>K zTp4o#SEzGLk{mIE!ND%XudAT+`wBz*T}_N}(Y=tntflrA@Ev8~d$R^jR;Q!a;auQndrIf+t_=h390{6nSZ&SL4O!=ZehGsHj}RF)$$6PE$_L(! z%8mMD`2imm0K}kAnl-os0*G^mQ;knU^4;x&&v}h>2mkK1rtg_z3auC=w`J(u;8rNx zixPZ70$*>-o&PKi8~Ez~gua_h6Rm02@Zyx7EQui@U>J-P-(96jzb|$^+88x5RlPcA0k#j_E1O1p%~0o2rnGVzXCp*sW{l zd4e=CpIp!UeXa%)Y4|-e%f#wO*+X)Pe6SPT{A%J}zn+L!agxG)a%t;%xLTcC_xXP= z0u%p3)!br#pZj){$yGOa1&YOKawb(CPLKMZYmr7~Aj!g$vq7u_LQvDg-BnFp9Kn?j z0@h12gB+)mQTL7ymh&8c>@4*ap2D_C+XbEjmPgT}N!&sap`;5*HoUu_B@m!vR;=Hf zPuBzJS!=rB+Z!()iZlvKED0%8W6Mi@oW?Ya5GmOHqKErNq(3A>l^-*rM-Z@Dg_Pz3hdi_^uW98a#&rD6?#wRE;_dZ zRDxKHn6;6{Hmr~0*a#Q~UH3HkNP=Uuv>`FeuU-W0sPX!jG&u#y7H2txdFiTai2KpQ z34h#rtyvmO@=3q0s_nAnCx3h z>Z-wmEfDyNd{VvB>S&@w`cQSnO-D*tdArNLYGFNljk0N>mq<~%+P?I4yqg+iNN9`{9PC~gMs^I1*U7*dnP3wy%$jit_J0r%MV74Pj zNlT*1XJjM=eF38?vUAfZaQQ_+zNfjlxmrZvbQmp+7SfT77m#%(^*Fq(kk9t+*B_ZA z>q=O82N5%&=^EuWn*s`f8pxGuF;}FCd}hY*!}vit~0uT*Ytpa^zZqu|N7!*?XM-zlEI{qQN&7`(TX z@NOl^1CIs3ap!!vS^{V_RY4vFxF^*1GjF9S%q7wkW1(8-6(vz-&Sd;4CKZ(@%jLsagfj}gbL?zncWRl>A3 zR_O>Tb6UND`Q#+N)rGN=a(-ymTcK8-!s6 z2UA4-bKw6(_9re}|7bF5vh^AR)=AZho|I;zG-3_EA} z5xcZQj(Tyy-owat0}Pn)x6jUp!^Ort+~m?b>7Z{3ScvpgiM^GfLX*+5c3OyIZ>vKz zuN9CgMrc@Uxq~F;S#HiREY-R-$GBCk_E*A6Oi=@%#{>s zwGZcv0uZ3)$JLSZMtQqJpzmxsx7-Ba?GP}mq@_o7%57sC(+z-`Y*oMKPK?d4As7Ia z6`OHDtfmst-wuY+g*nYMz7^qvev2kJtuZc5L+|E*(Fweu%H445A62f zHy)?`b-*LnO5`}{L5hY;weoUzZG0SMRURSlRZX#QeOq-En(m_CO2#n$f;j+WMdrat z_RJVGFLt=>auqIKBU;nS*YOCxoTMQ9ebi(X+^>~3=AQOw$=TICxlk|Vgln+f1Qt9I zK&SVPn=X){*fH6NtI~gKkRbD?vqK{V8!KBlu*osqJaH8UW+z<~%c>)NfNs=2m4aP^ zYMfe!`#v`@GDJ~#gffE=FcwqtaaD`J0XD znT$j>-y@o~Zw82w8eI6P^y*00`z4`tJD5jy)wNW{SPMjC&(&Kq_AgEQ1B(qeMD_Lv z?~gA$?>@pgQC1p*B4;uuHb7~@FkaI*5R8^wj3UVK>9zTs*jV_yW7h&ahFXDSpoJ}= zC_H1fSmERWC5%yL74m=bZk4-rYo?wUvj;(Q1fj&Y z@6b1oJ{JYv63dW>y4;T6o@HjxKF=4_!D({5T29N+*4bhIuGe9cUK;ZYBrP@p-Q<3N znlpS%V0XAzw@dsWmv|-iYj9!H(gG)?@pF8~Q@;8lEzLq7j|PW*10iS!;N~bq7z2cU z^#PQEy7d=NFB4;|4dy55BMwic)=B9h_n46jIgV6Q|AMUTd81TKY?ewEH3|x*L3eD| z#x$@oZy9t}&oDY0mvv?t6emHISh!%|Eba&-9+UuC)>dpsF!@5oVS$ zw+V+XAq=~LYr4vRT8sheb>O#Z&xHuel}8uW zvOGOl<`GPyNj#0!>9`dTE)IK~_2nkr!V|Ro9+~2I(klXDuYbC=jIZ4KCG*nLJLz0n zFVw^aFNq8*#tHy0MBa6iuY3@)fbVFkE7q_1-*i4_4r#hz?L>D>VJTu=HU>X{vzLEi zaLCpbifl9oLR5KrLgFfCub)g$9^w6hf-*?q&EE>LI_ zgsE%8#aT>Ao#j+)Cb#Yk0&OR-MpXFfm4?67Gt`)j$$T6y!8dDw$nM#YA=%ICl0tl! z=1I$Yr8pt`?S(Esk{cG}NmYDSh1n+L$;h zQ{-q}JXnz-|LIpWL3P*ZH@pJNxsIWXXr}C_rvUqzrfiSjJi~_)g=|WmVLvwJZW=s& zo%@2OE$sp++*CQZu}#7??OL=V%@Z2M7sQDR*n=%d`{>*Ut4e)hZYBJl%NuK0?V-2ZZ%b?>WrFkFm&EgscR+7Rwl-18elamRNWFf@+*q2 zgs@%;2X&-cz5YgpYBdsU;t7v0tvrus8Kk!3eeIAoUye&9G-$O2@HAYD1@e4;a?#i# z6@@3!%WWETljsG!_hGxM*e9HCRFuWZr$?$KXoPBgHO|6~J0$7Z*}bBBleicIT36|$ z13;WIv+Vwv&dm+Q+r#qmA6_0mHeWEE!lM*Wz@pDN0XU~UcS6NdKzS15(aDqlsIaV3 zHPgVD`7?xj6yK{TA}OnCg%Y+Oid^D1*JQb#PuIUmO{PLN0N9Z**MaGJg%gklQYdrx zdE!}(IbUKtz?Mu0Djl~RcQ9;%*;*uQ@b6ZVu9RHJN^001{(yc@qw%|rxB7UjqGXPJ z(a*>nF@?R5d-sMJ4`3q=vhLae^LFv+Y6c|P1M{%z_U3_##uG#=*kn|o(Dvl$2k=nxfl;@T3SLBcr{J-N^IHjkb;EG48K&Jq7IWdfb#Td9J9GJj@{KeuA=6WnPfw^~lb~gu>&G z+eF$KD50Ks1V%c>_7K*oQ4eq3cLA}JE2yhqB;$Y>o~(US3NywtiTt{C7C%XUK28H2 zmBmd@is$uieI@j5^CD#&9?SNs#xcz*-nH!cdWAanvsX#sHOE!rk8<5eic#fmXyy~u zh))B`HRSTFI%6YRb z;qicmPrZx-$ZVI@rE=s;o>#){OM@#$3-8&+16hxRZ=RDY;Xk>#U2c z?$t+e1Obvnia7}dgsK5zSvOx^2scmF2|3IEGRh&FYFHh+P(W<9)doRopifseu7 zg6*bA>jq~7N%#)QK;84X3YuH6cGzO-s6#}NCWy1_fe!|VA^496sM#I_P@NnD+OZ;o zt{MumxIrLWphyv$NzQ9>1X>`JLMa>;-skL90C0OHH5dju`2h8M-`9_;PHkEcd&Eji zkQE@O5Z~1=VR7=nSTWIgN(-mw}Vv6sf!{d{UgZ~h{u){6zz15_BU*3 zo?z$1gQ9_YQ76A_nx#eeNN*uhAm5ZX@3>PBi>OX5#_>t$T)qe6ZkIIm?4y@b01gyF zgZ>=Zjp8xE_*eT*3#SIQz#9XJQ}iTM4bq;*$haC!2S}=SN2(&Esotk!oh=aY2*VB+ zBmUOQVgJiqN0;<9tg0~iF=+1U!{+j$vkxU%g^UIMj41gq`8bJD-vb|H)f#R-3z~6S zk^@J!1h!ha#_i`R>mrKf%Sz;o(1BUp<NA~}~&o9XhhhPl5(J-JZ98(Wd@r5slU?{i=Ld0KeLYsiz}!e0?~C=$iXjTQvq+jxno zmh-1JIhq*ui?q65*ii7i%aQWeO9N^CsKFq#kq^-+8TgVftoYP{I=Y1!@%Ta5v7Mo? zm+4w%&%qU0QH>#D$f+FTUIfwgP)~z`OOy|Uu6PGj4z>?E@Ir=I2VDTvG{q<|qfL=-Ef`Kv*Ppkb^Q~HVCruSj}LYZa9n|1SiUo8vy)1R5%}7NgK_N^971fm{qB% z9TsT8Cy9xP0i1?2nw;qQm_L|g)N7+`d6Q6wn$Ju-bglT=ltkLVF4pBPL*g5d>V^bU z1%~CmEHaR<+Ymr4I!28#P_|IExC*9wl5-pUKf>Mv9P7UQAI}w8Wsj1$jO?2)}g_V}G2b>EMk=lehY$I;R8+|PX<*XQ$j zk8`}v*ZDftGZ+9DhX3vj7?~c1v!i&bQai-K%V%gq=bB%B0XXq`WOK=| zR;n_Lg1M}mN`3K4MbC`po2z)%{7G}wZ)tB7zHB4;a(=Of&D6s|J`0gz*HV*eH&QUX zGHFw7r4oM$y8Q4=wWv#fHQB1%=w$o+o-=-$56HIh+==RQN}Tb@Mp4{@8M_cpYZSDE zHy?hgd~;}bNqYAtnXZg<36xzg<%6X8nGFjdoGjE_8t%44}8zPNf-+J8c ztMTIJT)tc*{@NpG)JW*;h`{xb51+YKNz(rTo*ttIvjcLxN`HNkAB1(x*BY>10>e$| zTE&g5H5ZX~hz#35x#?a62lWGEJTsnTuj;)HG(bV6VGeuQ63-(kz-3baR~P`uhg^C~ zJ^%C!2e)y^_S^e2CqK}7GKt&2TH$HQDkOXsR;AbR`Kp;|~=Cz^4 z*?RR#ORQYZCoL(Nelf|fu{)OML$a@+d&%BY-~0CRo>2zR#Kd@tkM@Hx-Ww4C&u9~E`H%ys;rxQnke{Z$@U5?6zEVM zzP3^)>+QG^#>B+=GMA+|;Ou0&vK9)ZeD}&SyaMW_@x-lc>EQbkJ&I%pFA z4x?a?Zy|%&|G( z?iLcv9~GHKyDhz$eHz4YR?}YWR?IJ2=v{185h0*cA3Ofa5cQ810C1WLvr0_r5&We_ zO&{6@aXMN>jnl~>mT=yr$`3m2B$J;CHHgY;)j}vS$~b**>cvBj0smFE_09S6U2w`U zs#8v@Ue&<9Ssr?FjA=SWq8)v{LJXJMbvx~Sy}={QSwp#%Ubsv9KY`yW?h5|BzS%^Vx*A1b*dlJ4Uz~-kniJU2R zf9z?EYGPiV=C0AS#rch|vu%mjZvHy?mS{R@$>-w}GXQv5xvuXX zI~7Rc8}!*$CZ&CaG|7HTmCl7cB-{TzcsbB+w6ORIP1Cx~#{4)$)sG2sHkQ+-S5nfj zmT!wZD_Sm-#xldmD~GAJT(#VLJ?DH{27n_E7HuMuuR5@hVgs}ZbxR%ZX(=CZ9*xi~ zpi7deMp&fJPIfjLnZBlq;X|b5<1c%COqYrwzc>G@r8_f!;>C|UAq^=$9~=Z|b%@F! z;NMxiI`S&SRdWpn0}21!0Md2y)A@Vd1-Z<366E9iS>7GS=|1#pTZO44Zl73K9gNlP zbrR`x@uWXiCH;|^j9O=qGdysGi8dY8|FTpiEY7k&x^~vsp%ubXn}%Up#|B!XgKhyF z*U39TYAk=J>y`PDF}dHDg`6&{M%J+A+vQ{HLEe3(=g^%_$Wh*>6>kv9FM z5Oh90&s->Xfv-svf6p!a`HPjmqEY+_6BC2(0ldx0Um5-D{Ow(_edO_uciWe-nBo_H z`jt$tXD&+KkxG*K{k-sKz)Cv%a#~YWmedG!)o6)2@5SryDm89!tAAO4nf|-v;zIN0 z=FrvMbs!M9FjD@3A^&(JMWP9_6Qw7V{R+-1Mn1>h|0r={S@3Z}wL~RZR?J5J&dJI) z3>2pHW0FKUhxz`oqN!j_khtM0y`k}U5(c8xqA*lO67g zItIsA=}B<+$Q3sA15lvKp(qsd_n>|sX)%AcT866Mn`NAaM-45B&rec1LnyoED@ekh z3>9DGlCo=6T%$MK6<3QBwS{>!WDimw7TxVyjJw}(OvK7y$kf}7(_u$_nDn6#9Riuq z;Ln7l3K*8$>P(*MI`LU!|7&0|w+LpA`^G+SX)O2R?5C5 zSqV3t1U0`h>wymtrda}Z-lnhWhB#79;_qH9V~ddhL1_k|$L0T75#-;Q5Z&6wq?-r_ z-$#e$W4GlFXhlmu&1NQRfm*kr{iXt;7C+8L_5y1;s>HvTIbpJ2=e_w4tZrUEW}go@NGXqhVzeQ$q-0ar$h|9N3iM}n4x z;xyj)gH=@jPJ46fVeibh6_{dAC$!A5+$XP-#8%Yg8{{>spPcNz$1qv$dby2N_4mGD znDEOSnyrh69t8-L)DU||-Z8W<)ot+jH1_;v>0t}x?X*!iCr`8HqhtTW>7Y1{CxNDk zz1XUb-$LDVSHCpTsZb)CC()<1)rD=%EX*xpTF;cIt08%bH}QGj?3ze<<#=RdH!K$( z{v|GoJb@C_5JXPX1La;8o${+*@@>*pJM~lyc6XrEm51FP~T&=TbYuxu<2I+C2VXDWZzPNo8tjB ztCtEPWPgA-@#!pZi_cc@hIYIhXOFiXy^MUXH)kQF#8|tmxMi`^K-YD zx|*()13!=U&1=W@7g0qVN5vwp93#>_xzXI3{x#DZHc$Cn0J>t_kup?_0P(SVz|023 zI!Iv(Xyt32^3b8?=u372N?{#<96vb6_U-F0!PnbT>K5{ScL;D4 z;50FE7I0nb5XYk?ovGx94eCG4f?mGv;3=ky5PP|C6L%IEDnwAT(Au2Gx19NJXK z;maKSQ>15QOT?@}hZq9P19c9VAVvHM_Z!3@BMVSO8{BPR`xNo1ZK1|MMHn9^NBZ5X zQrpGXshDP|@{`B+zaD>@?v3n)({qPZ6wrzl{^+Sl)aDn|360UI36|-k@wIER>Tb2N zXuXPeR_|6GMAz)y6~D6IfVN()Ni~F*s7`sfX6sEaWC@Qj(4^mxNMS{TkR4LZgy`RUqJvpj@uG(3B`jDjI1U zS1Qx9mpD-ICt`|Cag7O55_Ho^J+h-AK9;X{aAfsGgZe#B^NLU7?(C%P7giPygre5o0$8^Zg!$B|i+>SYSZ)zr}CUs}qVMo40$Ucss zpRGkZ1^{3s3Y(qoBkyV@RE{LjCvebI@B7~0S^HzGN270dvhgX0abJJ7)R>-ZW3gX# zri1(|;*J0mrapSL9OlTbjO9IbFzx~HYE*~6X zvKy?gtEl!)lH@7?1N;?Ju`^QJsnP>n*@vVL1@@!Yh|5*1*m1!0k!{Vk*l>}|c_06l zjt)DgNTca-Zhhq~8r^PpH(<$dDFlPE~^f^CzvRvID^6EWMERB?I1k z7wxD@U+`FYkLIo>uh3SeX}E-Z4}7{Ks)11B!(8}nBj=eM{6ApspL7MHcmurgPP_}r z8#s)#@f}so>%|(aU!V??aWE|4a$^ARpg|lilYyp4K5oyO9KiA57YJ;?+GluGq7Dv0 zy3rzSrfcYMr3^J>^!(s1O9y{cLwV!Z`m2G=#eF zvsXB=c%1?A!i3OrlG{^Emv*98>OvnLK&{ihIXILd7UESvMcO#8sBwIMLW1lAbclJP z7oOL?i|rRkWIp}tAxipoH%Ibit~W42lPV!z>vsV)^a_8qxmk9jCQU|bbx46<*@sA>2< z4ohJf-0DvF63uK87svYhSl&XbU%mS7H}ba5Op)WzEa5OE{QG2^so!J|NR460@ydJO zHT2}}*&v{AIg_sGo~_%wWGX2r*&j)iy`MFVqrl^AqSAb7NK_?L5lv%d^=#EM(S9X{ z$>MRyv!88VDrEk7^nN>O#>?0AR_KXO>{a>{6)@9DUW>JyA<{c*6U{HSGa7pd&5JL*Bb5XVS>lD@X+8N#WbPi{f{(cSL!r$#l8 zQyy)ZkH88$qWaSw-@nVUBXHfQ_+kAXkoE6bhAq{gv0=)|Qct4GY~#IxHjrjrxOtm~ z@aZ`Zw(Fkbi&ouh9I?B!q+OwDGpP5c9wp+;I8vV}M0<*7o)o8asO+p@oP6aO47m z7t7=Y(id-wPfJC9>1{P#R6>4yl4xSnYmieJhZ?#AacS!@`v0OTKxOG5UNy~G`nbu# z(?>H-M5`N>WeYNa+z$IWNyvVOir^NES!j1n1)Te{(uw{&>nRUhopjM4 zY2?=YhINti3e>bYLllXxZzGxRX%0WmU$Zjhl#03)^#p=&Mvj1CEO{v}_E#(xkI5)6 zH0h*dh_k`LoIOI4Q}p}8L^K0OaSLc42K1#XsO1YyZ1Tpm}=|Ow*n=t9@bivR_~YH2Nt59k?_uqZtITv`w^Nv z^~bo7IzOlCSGW-I{HTu+eP1f{rvGtV`=Wca9jbpmB=u_bR(nNUxLg^&X7)*n*G5f$ zNRA}@i4M((rnN;2`#8Wn^zR`@dhhy+LCo$Jq`{H0Vm$U59VIa6HRiq*bwCOc>( zT)8kh%_4Yh##NfXjcT!LiP<(HP)@6OAf?uQnH9*mvY%+^=L)y{W@V8~_S3JCj#Ui^R zA}=Z5lUE(FG@mgwGc-%}(l_;BEiFtA08W0{8Yn0J^|>`h6`Mn{AK9-^F0v`sk*FFUpv40D>o=^ie8eSi#q|op_o-+VDxNP4*JF1UM27XdU!K&+hz{5a8TNq9&e1)|j=o`C#6WI{}yVOV3*o1Q`T`gwqS%+Q9xfs37q82^Hd499Rt~bPqLAmZH<0EorPZ-8@W3Mz4 zDhx#drQ^06f2H}&w2EdL_JXd-m=iaWC&<|O1ER32>Nc-69^dE9<>X^JciW)ao->F; zfSR(g^SU^0!TrkRKalJn>w)ouGA#w8>{3t@4N9gWd))QK|CM7WqTV=^;u8r~D&$^4pRm z-oX>3VA5*{ctDLJuVSIbxkJN>)q~;mskS~I7NHug6Y?MU6VHy%s$@%BTR;7~OhCc{ zG!-B96xF}%5}F?e1XGR-p7W79{4|gtxD(Cz`y0RVAH?0T9-U@~UtMbU-fE-#c6CNI zQ*V@2`k@LxoIeaBK2l188ZXFv@*)Il7zDTj+7p|8Y{9@qhQ_X$mtz2`yo1VyKhUH= zTjpcBc!9^}N+IybjoxoSwI5P5OXzq&L}~i2wgbrn7$)KhNCyZ~&pQK>p(c*NwY~#t z1>J4_!O}at*)-MNbLU@zXB{r>_$%-#S;@d{Ks2oM$pg~JkJzf?E5L; zKtwZl9LD+L7tOscadZa?T4SZb^lcj!Lm)5kVQm;v;{^K_C6Z^E7;t|ujp&9~ZC|OA z^xEJ<7P7@?DcddR@mC`jT#yW!R8=e*@Wis-ML><_%J)lw%p zk@!%Vj2PK?Tpn0NqZL_Y!u{?5g4GTFo>;>>Maq_vREGJ`@uY5t=ktm7!cW1W#5m{o zH!kmV4J)Gr#(F!R8p}6ZW9g0B%P&=3eJQP1a7qhrKI0tyJF?Ze`>X;9>9^=c2b_N# zk)VLaU}M{AT9D2&fc%!`xeog#q(M#(dUx`b&B%!WuYU?mt0s;tIX1|;B;4H5Q%+EFdb(Asa2hem<>>0-I z@`mSux-c>BDd|Meq*Oqgh5xSM49)dcx>Gn3!LSgC<7wI!M;^~jof0$)ywuLq5P*3> zusoTq3&I2TKP{gm$UGOQ{RN*8l)4EFn$EykU}$LIY;NDNa|A_#f()0{xjvO+Uc*2x z@1&aM*oC1J(ZUu_qgxCifd9jz?bQ0L5)F+FisbYnvFyJCkkA85mG2Jk4@RJWj$}<& z!OUVgiR=KUELs+izzWoTlPml^;!wV>fVw@SS=N2JQ<(PDb@F{;^ssx>=E?i@E>PSDjo7PNwTNyCzoC^~ z_;;8*3rIFS6&aF^sNW)u3*gu~A_yxR$b-*I(}16`h<( zgUPy-R7tzQ>NT``#`R#bR}_heCd(0Ws<7G$?FxPUlZbUi>(c)@5o) zJQlq=X+XPisx!dPIi}2lPK&QM93Uz)$mGAJ#+!E+q%$pU!)^T7u-sw{O|>sW&fi%C z1?5ZDs_$w~`y-rvkWz{{GdC)^^8IF2bsrg0gMpj{@+H2YtT3- z4C74m#s3`3Ahr6bi`0biMQHpTDVY$-&N=hIvKJba zOSZmg6 zcu215L^$I)=&UynCW~IrKI4k-e4WpLG&z(MKbb7yG?l-L-(wZG2ZKkf)zTBM(Qok){U}jdk-0M3CdYk7WCTdR=S zXmWKz6jh6SPh4(l+WzQ`f*)@?Pg##kUslGF;?*V0{MFq{lNzxMMwK@De$32HETRN` zqvc2H;u<#1xaMxCw#L_Rw9u=T-?}An{O6;|hl+yppR2Sl*jft?Ein4pL6tD|5r9%) zT5T6T5^f4b-1IFf6ocx1g~?7mxU1ghN~%wxrPv)@FSr6X!0kYEX;1^1BK9?$8!AEV z_a~xHB#hKO{J<%9ecpRH%xZtwcK~&ge!# z28Uw7*%;QA1U($IM7<^{DdR+YzF4|pl|MlW!`!~LJa4jQzO0V|D?hq2+jB%7R!q73 zc~k3$?dY$zV?Kn>i0ik2&v$UCRyh%y`p^p|YYT#F23_xSw!?DNMD#v$(MZka2cV6R zC7)D(B)|sY;+PNTy{bt;2-^RRqrxv%M0+M1U-!p^o3e}YqTUg6`3+Z<)1LM3Zd>Rr zscF8=8ajnHw9%;$d3yIyMD3~8pt}TRe#Pf2;;qe7&%b|IY+Pl2EBuwl%SVPg`Hkm$ zymq%|871BW&Csni8JQLurejip#al5|>=rwf%wGES8rnv@xb-$cor8yXG*a_khh9Lz z^3y2_`(6u8A&)c9(PxzB?)Juv5YSx6)xEST^Fxk~P1>sVPGY4SE??i_h28YUJOKjN z<2;Ft_h;2;$;Nfk)XUz)E1Ym>gFd!0DhQ@7WcWVFta!$M4aIw>ImxG1df1Omm<~y3 zhEO|c4>#+OQZ>|A5N+uYCaFEHGVToZ^`PZ5$JI*!@&3VB*VYo`nASg-g^)O zX5eq#u(onc{~EY0Ppf`i@RZd^c7Rb^s?8aER>8#Aw;-S;F*sv5FrevE%QlcrCGy5) zX)Ki>J*m$K2XciU!wabOQ~U3JSRac;`tpEX4h_25sQ$~g>RxzuPuUVD2f+Zm-`&|n z0uGp$$QD4j zN!Hs_V4)@91@oqW>iz*DJF?BOvsS&LW`eQAlteD##gwmr_mdNT-eaQ|Tqvk{KdR-_ zz{it{@j#SAAhMEP9|>cP3)VtkCw+6VB>MuxUwJT>A67^@#9Zz|csdP^m1M$jhA+&h zD(pTFekFdKm#wnKvx_1$>=Z;aO@wZMpUpHnzK*gJwv}9H7Ifwl144VWwQRTZkivR5yg#3;v>j#7cZuhw{A+j z02kZ$@Ucq38_EyS5Iq-n$T)w*KDG|3y~}gw?Hl2c=vP}GHd9UnfBs)RxJU_NP??h? z&Oy8FkS)O4HCHiOcYoz&+V)Sb2(%EGo@tk z`pTUKP;pD#1au&HLGwX^9U4M;f@f*2h7UB*Etk#){6_~s_WXC=^s3LPb`K}aJIB8B9^@J7#-Q9&s6 zo@vl>6dB{U{Yf6KnB)z{)j%QGk!@iBrGH7Jow0XUpoR`5PeoM5B8#)x>EmuWU=w3U zxllROU|{pU9F-Orc{y!qfLuNg`YOOT8@!%UliqF%4o-79uX3bFwy$-yOnAK%=6(S- z;K+0AU#_FeT(OA1fiFyitmLjWEyD+&f`s_GuRG*_&g3^9I%lLtXbrM|(4Xs|MWC^O zNgBR2btoL#V9hm)JxU&uiTe?>+@okApap@63ohx5e_fol4v_iPfCzt)+J>WhE?Rgk zWO159>Tv)jLqf>D^`b!uMuO5W7<+yw-nV;!<$3kQ#GwZu8sp3ODry-BdACWp>?~T0 zl7NuzTJc7w=CMQ!d2;sJNF#4 z={N9g&UDEoVjvwN#lKwmA5#hct72F1x?slSR?cHefHU0+>iL| zam&Ih9ls#)!O{w`dt^k0ORlIjf+*@XRI?=G@_jopBd{Pp^9gTTEhmmw(Q7BCyl3CgMl;ya5w2Og@koPxQn zsnWjjN<$?|DL_Yrrvw-}ncFB6=&?zq9#qKR7v5sfOejaRBolvX1RxY|v8KI?OlsEx z9uUwdp5=~fZzlCqZ6~H^lBhA_He_CN6~kM^KQ+=VU;TnpIVw>f>EeNs!kxt;lYT-b z=R~HFta^CyXzJt`_6%8Mj(2qfoZrl|xrP09pi`3?$orum2-P=(#Xh%DM2Uj3SK*@- zV3en%(PUzu-98W9A*jnwkJ;j>5Oxv>0ZITQWIMo|b~>h#TH4!TA6R4`K?B|=V{F_o zX+4kzfhy48@~b(c=uH?f(tQxWAbRb0XZ*iG{D+hQ<$DAj8HHyR*(jXwI9ZbjSw+Qc z=h3qv0hmGiD>|(Hbqmc6H6#;khVyKjM%3>+o$okz{3wLpXA82T`)g3vx@$*+7P0V> zd15E^&_HlEhv&YqguznXuZ%o)f+X&W){ig61g^Y&tTc3mN+VGe&PTwH`m29uLAyj0 zoIk`diGfhyHU3(hX5lz1EyT6^lbkir!tSE zYYEmbv49#CI{cAs8BbPIrd~$MAPa}Md1@G@G0`&-ghq-%tJ;Zo^0xP zQY$ej$pjoGw3`=M$G+h%@b&~B;tE-0n%Z+vJo{d%7-@h}6aN10ep=sQDef}x^i`Gj zs9WSIzLye>57PW~nJ^{A6mZdzUM`i%A@AlfjZ~V5n+Qy z5hYVKjhAg}G)f52hA)Dptvey%=nm3~1M3Y=AdnNzC}bKaV`8dBEG4l83crXeR~~~F ztqfhWv4o%KpZwVYJ1_(Y1|d^}EP(?kY=U)ad-VjB3EBkWATmJYtuUKJY=Aa|dFW1a z%6yV{9$s0Q9Wsv0V>S`@+aE3My}u7tlbi_k#94otc)s($#4{b;TJe9hZrPc0lO{)g z;%f1&A^NWMY5kSjSw0ePLC_U|ZPmP+2N@~CM7g4CI7;h@Xr!9yM*E&{{Eo=-LBp$% zWiM|NL?q`6H9v9)8Ml6f=&2pFO74IbB@U%bNPf#-dkmP!Z|`;u3#UV{j$e6#8u(oR z!4uWQ{Z-}p=NRC&NJWcIICEadEpN$c-OMAHaT$85F4|kGbgr?8re9~o4iurFO$r&< z1Lzw^C^C}=I&!>S8N}7Lv^Mb66VUojFZZCHLI|-`#ZC~*kq^KXIKqGL> zHOX5wp-fV)QOZ%*jUeCF_eipLyJDhz_WfL^KNEHXk0IE7rNkV-GP*a(H;IoODk6ilO} zY&M!}Ah{VUY(X_@+WOWGaYpZg`L_Q8^jk>ZJT{&Df~7;NU&oUW*BsQUE_Mwk5pmv$ zV(j*5O&wz=6|GAaI@?=%6zDEs-_ziQq1k+N2BHU~mj;<-hH zJC8-1>EsxsTMUC^?O-81VIRu0lyxi#X$fyp`j9hGO4(#@BX!pk%sSjPatP=y-4QPq z$WD-Qo0>X9_|z})Kv*T4C>OCcRqlef@d110{g?kSC&oR=xg(Ak_>G){y@#q4jE5do zzF1MkcOI%p2X)mz?0bQd)Q6x_PLp$W5OfETt^+2#x!XFR>fM6$~jDXmwiw zaR?FT1d>^amrULKbC|*c0u(l6WAQ_82!HVf{5lC zHWZUvEyfHKCZh`CrbRJ>VqWB3sva-<9881dn|P@#xRl!Z^stawgEF6Ak-)}p{?1E+ z7AapK9SB?ZyH7N|czEc@rL4mz8nw|NVg=Ci-S&)jEZ)H=vIK7H^slc0DTW%xN|%df zarT9hHN-euHyqpV1^)y0_+cv@2+|`jQDQ2nPF=9&735YjGqiWO7GeWZxBKXj$(W|b zI5^E@{fQS2N~wC*gtADUkTjcTy+Top5f7^jNN%*ygo2M6o-AlR{U`lIhw9=zpmrHe zK7*gcT5+vMnyP_1D>`+ClgFR|3UKPhUy)6?3$SF^iW&xbWk|e5EIaD2()T@L4Z<3u zwL9!v( z{o%6@ga9c59Vs@JgZmE^1sC=}b0R8H^CUEBs1Cx&uY#T`h3DBC-op6{0oZzOiUoXo zp-VL9hs)k|>lc-QX#lL0r)dIMrk(!wCGgL=0jX_hTw66g~KZ@a=?m3TY1n70Y-|)Ydn222lO@d20^JwMud6e@pauLIT z80_v5y{B|w#7ezD6d58Dx0VE51o7wz)P~+fydfZrGoHPpjZpO6F5cnQMQGm8f(M;X z{eY&ynC}HAt?Ah%dmHJ?fp%p|Longpzt2=^4zmA-|DX36Ix|E74eQ6wnLF6Mj%elTn2u(BBXZ97D`H5tlgC zlP;XbDF-gSywa$LR39;hr%M~(QE)xLZsX?NdufMe8n|hQjKP^ej#;*a@V8*eZN84= z9V-@ag-)A--k;EnUg9*LaCww5mJx!r0SM|qQ(7xKpuv72@7+;I9t!~wDV2b5nkK9g z!ub>`*eD*Czq$6Kf)!gei_AA&Vf$X7QgS;?kn zWf_3tKCw5coX}g#_Kb+q!@wZ6QanQS%1*raE7E8g_6V}+7Em*68WuS9=T5%AzIP`J zkUKfu*tP#o?irj!cl-hdxu*v!R0ra}EglhrLvK?c^cu?s{FodRiG&n19|DAP=0uGL&b~ulB#PaRQt`fc zu~pv$uqKb0#XYHeCwfjCG3~ZG3TB$l_7{DhDPr|RBk}$+aq3(+zpSLIujVumPD}{c_cWzrub&kKDK;2vN&m>Ux75uSe9XqC*u2$qVj7DMtn>YbUG5+x8pV zJ=9NK2i1!wOH`&!Z?9xxmomjSE|1QI=1vH|?t&)zTTC!%avH2BU{!$2o)9(<^#Bkf znFh56%hwvzXIbPpiQPZfhMP`2hoCYTb_7Apaf?)BMoj4h$u6h zyQ?=xj&4l4!bm0Yzs9J@Sl0r4U-#R45)fz2|7P2K)xw?)tGjP7Nek9SGG8p*H=clI zu$vbxaD{Cl2tzzz4xK6OuaJgV=>LSSA2lF!GKnJ@D<#{-TiQJ$r?#YRQT_G{a8#rK z#ep^)p%aqCopOW83%G-X=YZx$+?N(|8f+cY%a2Lv`;TI1?t!I|GaNq-{JRb~E@J$g z;GBtH;E*!S+|mX#t;Iq8R{FeILmUK(E`)a$bAXZG^x_~nk(%+D+LO^_2_wx;8y~>r zn>pA+TqSq+q8!9ka)%z6?u#(~g}Q(Le}g3Ei=$x4XAB75AeA9A;h>&tw>^fd{GlQC z%$4*6m`wO5>45Z=|)zCJY&JcM2x;`)5DMSv9XCcR#L5U3Vt`%}uD3|dZ!#{I1_Lj4me$Zv8nVC)h zUzdN2U)|eHy{xvkA=xH7*b}lpgorm;AZcT0f8tejJ20QCN+u?cYF?a9vW6T0pf=E_ zb{cH`SXhC9G!N1n$KUW8Dwmz;52)a%21IAe(BN>OhA18(4EndX?B4|HD5(v#av-7L zBb81$8-aI6gO95O7h#?ZIJq*;L+eP~m2ZXOB_^t{li5$tw3kf$hmh8qp8Pdm9qPNl z83eI(DWnXOa7Wx3kT$oK8JEw;%<$>Z`CIr=G)A5`_4}ouL$CkG<@tY}ynpHU{=Z?) zM8%b{Z?OPZ+$){5$O&#JnK;KVIrAUv4X!f8^dja>gHbnw-$9WXLgN#5${{A2&dP)W zx!*YG628Ak`?3GL{&k8dX`AZn6qsBP)@G{5??j|+0tGjCg(nPT41X0i z_P+Bp^&nkb)RCj#kj5WY!NKZ$dOp4DMFn8F*e(W6^uI5Sw@CZcDL)Ub=Fp`<^^)o5B43u*o zQ|AL9w8)?M>TL&ePF9vMEpI~h=(j?q!dhD@=#7G5PB&oUJIe+8G@f%|+|Z9D0Z0f? z6&CFmZW$ezTy+JVJ#e16b(XaxsG)O2lSJTLLIx9?*!O=p7a`s=6#-w3WO7L6BPu}c ziCmB(=fNf5Xj#c*Kn)4D0TYEyHAFNWlu5|Fl$|pinltzG81r|wHz5&;I=i_-1Vw-{ zfFTgcK!~)q*-M^*0s|m!+h>UDf2xsmq9&C|)CMCcN(EppQD12D_5ClghkE&iVjp~@ zyra?_J8b{Ft5%*FPZg|(JnckFO2E>)$bnYNN2^~Wcva8_5x$ZflHhaTHj#sPBGgIN z?=cldL}MZKTpX>lE}2Y)9_TfjIAPhVIWaGTI}+#C zk06^V<*~606}gT9mEUyG3rBB{?D`+T~$}1ia#3;U2t{6h}Ij{`(&I zg&iO#4QVxR2JWrjQ|y=v(q0F(^c9APsnbKyqnmOe;P$nQI5K2F2E;%hqJW(o%w-&W zaMl%y1^~ecw=X3lU8)<%L!>Vj{O*q3|0GtP&{ZWI<*eCb(>{b+$%GnutFRP_|Bn{H zAq#jdAm3+8Z$bI&Zg)Yg1kb|hp@@hGs2mlJ2fO(I<&|x!icS?MS^o*I0LEgcdmOw; zutl0I#NGQ^PW2XKSqhHk=6^Ke_FGR1PPn4B4E)`YY-mt5-JSmv;);C(Gx)}uS`cEAP5 zDPEu{N#W;nk8&{lCFlH;T5;`e^V*T4{J@v4C!iBFid(kkOfzx$=% z<%^+4@20e4EiI`gJ|Ft=ql}}!lCT8qtPC%Y=CYgPpXK4<$gMJ;8Ox1wf+hIA_2_z*1#6 z+r?*mAi@vu1O(d9_(!L?JO*DbkrBTs+Nxxd!;B_4eiiByc5s2CHJk;?Csfa@UVhd= zUuCU|Yd+NjnqDwn%j@S4ya>|r>6Ejt^!-_}#>=D& z_)UC-fwcMKfpTEwYPY4J#n5L3{}Jj?x`Zt;O>*SP|Me%42n93KyMS^v#B=>X0sI=B z##Q+Mb_&0b@*S#9KPSEyqPY8nGWr9sL^Mk1jiY>dxYv(g%I>Z=dYzk}07#;`p@(?r zRa?~3*kV9GRM%hxSQqGsX8eRixnc-9SKw8V}7{+G(j!>VmuAjb^K%>POsoL*?sE{ zdw}Dks0e$boy4b{{27g6ee(>1Mom%{(FN|%BmY*0cyP-d>fkWUDkKw|zM4}yH$|5_ zx)>1b`3^{kfNS5V-jG+=-GLIY1znx>vNgLSzBAvW?TH^b$HCJyKpc_mLoXiS&-aDL z7xd7O(W8;Z_R$YH?fRd7PlxDxNCWFY+wl(iG%pj0FkDK0fGt?iRt^*AMzO*R{m&nN z4m7`HbL-0ne=HGu$bn(u)bFbUUghb{T*{F0om_NcDxUyzFA5W$g2l$7P=vRr){uk@ z)F+Y2Jgy*6JQLV_v8fv}G?uh88fUnHl`;tMc#Np6NtUraYT|mQ1WY@U=JR-b=v9tR zSOw?~0E%m2R&}8yurlZol?4mD0l!d+=VL69-~ax>JRlmJW4-V`y)K6$QR+>@>&u53 z3W$;;DnL_X4VkXxWFJ@w!Qnf2BV?RZ;D)?eDE>nK%@8S))brVT)1?DJF%R=QUO-R=*83QA-9RaJI`-Mc0a3hhvz1hF-9FCppm6?>N`VFKpm%Sl$gB@9Be|GF^y9f5tu+uqCeoe^ys8x`E z-3nt1B~0y^h=gJ4yD+reBj8lOUeNflP(j>Hy0|)0*8&37+@GJ1i<~^ zZYj5bz*HY}UO3Dab|Sjl(wkk4z0JtofNGkX`qMsg(8(@nRh11PC;REL@#wf!1?xBv zzwN)est>}&cZGq424g_tfbs#tRs=%$w9X7rHIolot%9K7BvcnpxB|2LSmJC+RlMc7 z7KAKuOvXmLlvVu<4nx{_@r{iE7MVp1#jx$w@?8)77f**?B_X$Hgpg*lIv9enSYe4qo1)dC)8W+|-**uzam1>={V{q3UBY7H^QTc+MR9F6m+Z z+dK%W5!sshk6OanN)tSFE*|p}u#}a_ugFs8~nUWQw8fNP~#I4jNxZ;7=Po{=@QK#)|g zUR@D9U*2m4zq(U0ofE(%AoR0dIp+{7>MSQV@S7loC;wXBYTnt{0g-X~?Vf=hN3^!q ziPI8JKa)UBkAXp~_918!&*-GD=I4Pitmb(jY|NO%Iqpxj|@`VL;05823qXk02 zT$3z9!BFUCs8;uaBx9XFhu;}bz;QJL8;6(iZRdbCviSZrVu>59XnF6OnX5M5yLv#i z6If6K=J{W_>2VcToZrRpTT9rV3C)S^KYt{c;Vg;Mu&{j6%&f?JUzNKbEe;D;#!uH* zfk?7qN8t=OVwJmB+aY|Jias#CWFR>&N#Xp1y2a-@FIX*y>NvP%(UAHKc^+Cl_aQtX zjhjr2*CwoD6A)7n@Th2#^dc3H0j|s=h&Ygd<2G4BMOW>`Na(l!Y(jcGV9&5yu*1;` z%1=EHq0WCf#w|j;UWVBAvn0Y}I#C9jC*MSWwqA$s1bS-!pPZAHLu>qA0QO{)XuL-{ zMT$xr#Ha6q4TFHk2&z?a-|tpGsDja+=Fl?P^LF&$$zAT5_81|shRoO3^@{dMn8!SruI!yT)%pVvyxTPO{SQd-RUVErYE|+H5OHrUjmkF8O)oT=O35Uxbjqn6=Z9=1I$;2QOqR&kdi5KYLFx z>fOb0drMF2IQPX`q9Hh}1)m5{9B3GGo^~eb>8|W3e zb~^Bl3Q>_Vm7X&*D1#v)+|Mm6%Ct68#e|P$@=|0yQ5+IgbfP^i()iK`X@B7G?(uIr zw=f#aI%oGx!0?W5I0cS}+Q!Z~LQVf&>kZ!8_yBU^y8;jM6Qyik|LoN(wi$w>fAY&D z9`meDPzB3wkq0W-Ka)}XiYasgD#cOh z!+J&)LTP%uHu@DiJ<(WMT4_35z_0I1JVNsLablu51jq!xfpRM%g9DGr5|X-G&QHkeYcBbj5z>=rNUfs!pCq9(TdFh}69a&3ppN zbvf5aPsohHZM|xKTVaV4lzOGt_C_^(b*7xg-)#=@y!9i9?hPZml$E$a0tIz4T>EFw zbMd9IwlZOtiN-lkd)9@(u&^;7=0nI0#057+Yt3N$J~t9~%+}&d-qZ~~9$f!XNG!XS zQM--H{q_5mr*sMRwVJO<;L?|2)Or5Pa;l$00Z*eMZ2$(?$pvg_++&tfk^q#@wM6+m z>BaV+;g`nT{RNI+I7`EVr3@5f3Sr!0Sfm{o)`)-TH>mS8q~Y1vQ`y7$eQ4Z7u-rC~ zV!3O~=wGKjdk6kVPdp~E2nxOgvsfwwty(83kzIjB;76u47dAgmDVpLGgH6T z;cILmk4*huwfW{5LMJgM^_t23Lef_GB;9c@CJy66_{4vIK5qBcR&CI-;xQ_MOJh*e ziY`>z05_#OL?)e`M3v5g{V8%r=*7pY4j(&jy)B0XcwIqGiMOJNY2a?dBLBy;$1;63 zhoo&^#MK?@E0nF*wrtWJ<`0w03UJWH{$ll1N#^)lA?fSu^QMZqG;$Ie;NqK}K0 zY#(q1fAwPx4zDH+@T%)!F62PHBSKire2hRPD)ofukFSl$fGY$@yjOHeXWz#1hLGRu zO@q--nO3Xd4ZpO%yY}#?@X?vY;HCS)qQgX9k-`K?GNL+@aWUk(O?xyHgPuQW~Wj0SW1lmZ3qARHUU#LPDhm z_^xq3@3Z%QzV}_rUwiEZ-1mLOc^>Bx242o@`9{SPwIOGwJvtIF7?~Im{Ox#79@heJ zE^m(MDjgZMSlsikmrkpOkA$I_as-~dzM{5Dew!sf+}X_TtM&r8A~Qcy2dz>azjy3g z9q6f&JxECR_BMAqnCtUSCV=x3f!4lHUo-b#tv%fW^fWZ0W$9-KlCe`bTrJ{k$TdlF zM1}W6ZW!cRp>iBzGtyeCqRvLUY^*H31fi;*lCf6O5N&Z^L;t{|5J>zycHsJZWP_2-fvCiD1YippFFwrIEAyV=W%e_RT}@Bv+fZ9R$4#z2H*RvJe=-QrfnnW&y@t8U(8_m@ z*Rg;X;1$rxx#?={>St9LI-*0G43a05EA0dutQ-W@x_qpc$Ssn?5oMkmumiSkiX$-j zt6L`&4>S^fs)UAaJRjM_1gwo&BYJg*hp?hZX@DmZ=|s{ZOKZx*duFnng%pUOw>7t@ zf$8)QrSykbG9z`6+1u`TGSv22+S1dg_2G1qq^WvnENa-UbU-As-?Ykk46JkU>kt0Z zryakQ(7mnu2{?h@I7e9|D|SE}=yik9Gt!&RR5#PRPs{^7d5K3c)X&~CbgU0DTJ^jn zz;uc zY=XE|AH$9 zSkMb?3-Q)mnV^o;x0$dBqA;$_C!JcFUJ$*f`c$jL@>+@Kgfiy)`kI-ToUO;&ojPF! zykY@~U*}%Qm0WOCOAc&?RmU)uB{iD$-gkt14=~{3I$)(ykXZOcb2U^ew~HHy3c`); z_$8~Ci+j$e!(59NKc9|q%&%VVz4?#NRM}wknF6j`%T`BPts6s=? zoIxH*G|5ug##u!Rp$mw+TFNUmBmZa*1}a;FpcTWyTL7Id2msj1i7-l`Ua(ujbvW{ zsAkANr%pNac1@;Ztzu51!BgFvta|}Z{qNB&=;PU315k9KWYKG%#j$f7QLzF{Rkzdj zibOkq=;a9dwH36EANnpgLNm7)2eQOnEg?DIXLu@(ovlcRMu6vx1vfFWNDnu{hiqRW zJ>)_!$ypRVSb13?V$m2$s_cMt?tC7hB=o`UJsm^13jA=}l{Dq%A zDRefxZ8_RudtVY0{|-=IMhzWmd(>@sHf$iHuRUb%wwXd%roJoyNa>#1+SBMv)7{>^ zFaG6-l_-vY!)Vh}n>7=xP*-Py^68pey~8)7b^UE{OU9do6xhAF2%b;ZyG9+Vq(~(P ztfu_dM`b;>e+!4PFtj9l!c;q-+<^K@<^~*p^GmNe}FXsr-1*W8G;b} zsE8uwo3C(1{-?I+TEbEEVRxmOaxd`A^sMiv3d?@HERKHsI#c(F57_) zQ~0J0Gv;z*bJ{kpdr_c0c9Ss0X<7}^?sJg{{)|=4&h+qc>r7D?Jocyp}I{ zT!_b)z?IxU9m|5eh!cATI?MH^Aye-J4@%F<4=6m3E~-3MTC{~no1*p7bPO_y5(&lT=ykfSx3Cc#_fyP z{^$Dg%cL?D)@90cSL9EV#om1W|1~>=Qj9+PfrNuxpVfqGva(7{R9Qr!UwtdZ7YoK| zvTWCIHmSU3n=htFxerV!EWP{-8eU^G6q#Gm2N@T=cu*{jWJ1{?*Kl3Xf4GFSlhHT? zT7(_i#LZ;wox?mOK6e54d7M+b#U}AogVj{s78yC)lYnWp=%pv@6aJoloWH4`v5$hm8k8^r z^d>6H;!~yZYCId<^EylU(cCMg&Kx&55ed*IWY}z7tlg3zf5<&@MZCy1_N^dRhw{(B z>ytoY>}*%cuq*~jE8nf<+$B^v)6QV*5KZ&x?I74yiw}SgjUHM@-gG`IsAV`FGKd8$ zKSi4ag$o*aaSurqd7UvoA7KqeACWU+k5TODUm27V0Xv%E6D`hyPXa}fJ8#ARk|m2#(P?`TU(vt|Ja zgWeXuIrA<+|p z9pPEb6qN&RmOM07l{NAQg9;)ltU;&4Uw?Xk3C3cI1t3o0@XPc6!Ar-m(20lcl1~oR zkn(Y5;jg5rG>w+BWF5ar!EaCM48l+3<*RaBD|f&O=`%9X?^w% zuuiD)OgNx!JdfZE6;B`dp61$X??UzKy$}LpqMhKS#|A1EMH+psmmooDAQV%Xso?5u zMKw`6YtYj0>dUQ|k_BMu$SAl5G_H`Eo);JLES_g@seS((Aq`F;Ll23bn|9= zQV8={YSY=`rhG=C%U*Xpq0&IvNKSKq4%B+M=4xV}PTs5U7mhdwF@6l>QmOH=0^EW0 z;a*<#Opp0~m@q%%&r=|mntyII;? zDSzE=LF6<26cb?)Gqvayw{w|wu1x&g9Ec}5Z?Ets;QTyqyLl9Eu(5P@wpD^ct@yLS zE-m8I!ug*M2V)L`39kroYtM|?9dt&EN50q8AQHCdrlQim@dmP7Y)L=w6Q8bbQo2WW zKc?cxBf^^6JB0Qq%>oCR3W33Na9qI*ovuZY?RSdx@w;a!*+WdPAi~}o4AP(7V^{Of z;>7l`Wcu7+ax52RI=fq)Hcqqk>eO`f01zJ8RE_cVNsT>`)U^EBDrB|LI+xm$ZfqJ^ z(ERSy>hYt3r0mZ0T%-HE z=gx69q^ZY7V7n|naT)QBmJI!R^Q$#5-#RwBp6<#Zi_^aEbS{7P^^qNnjNo7;kNes@ zeCRc`iPWRl@N{9GyE%LSe<85wQEqD+?@`+EW41pli%xr-18)zH3Q0jlwivx^?LP$* z@0AlKSNb@$0Hob&cFs<2Kd;gqz3#Qt_#%#Fk=(g8Ko#oglt2(~!8j-ZoXY8JBPLcU zn^h7scVqJ`C}*w~ZJutkS)$?<3yYppkcA(D@cQlc(fyv2A5AFCUk3-Ds?g%)46n&#~w+rWd~^4apcJk!%lP3w?E+r9Nge1Jyup~vT9Si@XoL7 zHX^;flGe$wc!`Rw$ao$5T7g8b>wf+nbzIC5EG}zd8Zk>Km#09TQI8M5U2rP##aI0+ zjcmZ$wEPwF ztnh7t8~-i=%!t>l(cbwRDx^udK5{YUm8egw>4e&9tpvfw9$N(9BHGM(!Dx5^hkm89 zFcC^J>cSHDi|?Gfq;5?(`=GHQ>SY=As-yqzMbf>L-gLoHd*Vo#&x*sccq|y|X_UEl z-x5l5aITb3p;*cdN*hvjY%rJ3-_1EDd{*PrNPF{V=)nh0I73p^2TnZ;LJoG!7iB%% zL+Eb3qaV75hD?6cxO9Z*iS%S|b8Rg8)OT?3+C~@zsCuHu25Q+6 zmUuvTF{_Soq`JY_zk~(dZh5k>!sCBRFRdsv?y;Imi(^E$`H1L|jc!N2UO6v}> zH8vGF_6$%TDEstVakr3rZNpfqz3m-t87i_S8mj6O1#s%Aeh+3~Y{3jqwh~FDuoFcO zl?t9msV39HEojNKdp=rj%U2S+kr3V&j`F^}M$)Pz?s6TxEYo$tPpP{LlJFwtJW?`c zE0hXsI=^3*f%y3LNQ$UoHW@EhUHP&mDw;NLO^evU+b>?6gIP>{)bta>jL;to)Y~J0 zcTanbW=|f)8w>Xh0#ogdOqm?IesKS3>e)KNl7U)}@vWjAy(gc6lZxPplFty{B9 z)+^#tM{K1HdYvvzaJxmF+rRh;x+=QGWA|PS&CMSvE^U4DzTYKCKhLXZ<{6yLGbr0jL;RVq40#Sm9?}F-wo7@lvbz-nH9fFNn zN&h|k1c}l=U7(JF^=ck)n&emJqdgH8KUoDo{kk{1T2{l8s~!gN?~}XLh2!wk!pAXl zu$&(LSye0+|D^pY0+)adUGF6-Kk(F3j-;m2T+qg@@NUfV2#`Q8c2`h)!H{bOdrxt; zybEa0VGPkV+aUO9RhOSqTJl&3ai+q(^8}#>vy<%+Cu?NpFJUKT4ho7Qyhmx%Sr_(0 zY&JVp`B;cx52lbE=ygp{G;y^LRCaSfks%r3S-wBQyqDwK=%50LZ0++TAnER~H6o*O3?_oV%!wOARk=_Yu$jNGR{Xy;X5gWSDEPT-=Zy27_S*OFm(NNmU7l@vQ z(cvx#=SfK<*;1UvVeW@3*F>86t60%T#VRaA25~Pqr!|;m^KxJZUs>#LY7iBH`OkXC z(Or{H-!!vB0wL@AAossWM^Rw@)23(6&hzgfrMd;g8ZyFa4Rx&lgmNs9BHv=07k(#q zEnl(5NBrB+R>YRQekuO(#*##=Rw3cROY&$|wBQkWu}_@D-K67qnnOz5%{?>r3em6X zK<;+F$MwDsqMNqrOv#6tkR0@B;oI~rt&_17!V|>U>S=+)T-C`wk;~l?pS6I-BG}(s zXx5f-Xw9PJZAhpK#+I&IZ&MfH>>YQ%Uu}8#vlUQuSX^6C3y1s7=8!)fFwJ z(g~t~m-gaf9GZU#Je|3yBJmhq0$B9#FG`CN!vnFzEMeE#dR+o8du;R&edZ+{vE?93 zz$CkxP!=-zh7i98V6(xh9-}g++Q;&%D9!;q3c$=J8g2GQ{{vczL&p#`w>>h`gH}CT z2w?L+U1T4(+x}>_z8Jh&=XVSUsRf5-=}$i-i8Q@=WE`w^4C0Q^-MmpY5oC&a6Cy&} zj~E|4D_L@u1p=GrU;<(Kd{4k=%Ka7rQuy}WsgG_Bg6+BtUr>H~FVRbeSmg6n^+`=x ztCfkSmsB$aZz-w!(^;epZmLe*>5dN#qp+t!P2o8FtELL)>8?Bl)nvEnCxH~rGH4kgSi8+s7TPs7=3 zrigul!QzaSf_3aTU)ZGoTaCQ1CLERhWyErSs5w4M*Bke1g(X3UIS9mUc*ykZ_pbhF zay)WrO65@o=Bpn*qVylwnowV9vMMBzl zQq3Q1UhL^*?95C^GmH{Tu<555KU=AaJDOYj&^>`(??-k`I7KHZ%I=P{It%<9G_V~8 z9H)E|0;ZovV4P%d^tJX4lJCFndxRFk;x%!@ScIc{oTlU;L&XRktRkg1YtS zNXy}W>Q*a8pc1`i|F)Yke9f72ZRwA};#yl3D7J_gsm*FCoVGlzcp5iTVJOt|Mat-7 zV2Y6f>_tY3S!Hv0LZAPxQ9)5PRluddcJfs(D^}2Dx>YYV|Q*rgf_mA`94wyIm`>N`+j$8(L5jGoop>SOP$VdlvG$dFv(lQ5w=_?>%y z1^90?e{WJ0;u3dF$QjaryO66ga;n4M!GkifIXB#wkvC(*ow(REB%xUR4&SG&QjNa?zk=zS3$lo&tjW}K zy~l&)B5;<<-cG0Yut}pe?`%w&Q=l+WafArzL6we6cKfZ1J^|r=*sW(tA3>!}I*jX{ zu%e99&q>PV4@5RP;e7>M#5(i!u078=s4@?_DeL^93;TfmvR+n+)9Fk~TuG)YnZrZ1d@ntY7{(tTd1nP_l9&Sjk2uFuXj!1 zL?uRkT`O`Qjy$EFieQ7ITcH@!K$lB6>VJ3yNRqLGla*HA1x26i z%W6G2NhcM^C>d=J@oevqH$2GBuiMy0B}&%E4AGC>9JRa%ADTnQ9}RG|#VJ({CXXtU zo4vc9LPuNLt$5KcrP{LnPQ(WNZf9nJfp4W7GZ!t_qW^;7Vl|?l5U1U6x|*pw7t`U3 zKjwnY1iswzz}y?#F4K84g&UOnxV|HeZ;~I>0B4tJYK7G%MIe1*gMnMXLEZ6%@)-J8 zl5>$B9X9r#Xwpd)qU^rBVXixGh#1D$&~=izOxv^MXh3cpdHkWR-=|JLhFes^uD~gB zykL;<&pT!#2W9p;L#g-wP>Q2if}nAiJ}{Q0X$yL@x~EwrO;Vd>&m1M^o3WO6FXU21 zd^yX`KgptXxR1wD^K1b)aW#@P#}*sgl&^-L8B=5M6qPTTq9X&Pf;*Oj4Dni(k~0~s zdRO@HAcj!v(aKK{szzlcEcH_SHe-0Pp6GI?H6p+5t>&?0@7 z$i24#G1rhK8x_3hD()Z>5Nxf2rH~ryJ&9kskDGe=J%FNjR?dcwuw;X6Mf}M=;yS>T z8H+XmxIU5X=oe8SLDbY0K)ql_5bMB5qbOcRSqt-Usmi8N?~&g%P)0prU#GIbSG((6ot0 z4!Hvsw0FsFFr5^C5K?@in*zl^8pj}1aD)Q4NrZu3_tHFN<3mTb1JMr+0V>u&)P+eN-3_!)yp zZl5f><=ggkj=eq!akq8=T%C&vDeKt>40+w4_bh1_3b|-ClfVSn@7k{xFp2ufl+3IJ zQ>2&)9G|s*DG?D?3;0$S$^?3kXEyo|KqvVKwZykGcz)^ptL}NCNaqTV%Qb8iuwUx6 z&E6J-|E$KtRqnufy8NkgoE~_H(Ujk)7_`oG`dVcwDTgUU_t;gD;4JMup*3ri`X&yV z1Vj5Sip>-dH^Gp=44DpJsr3RZb!T;ySxmsyaqjB=G%e%1LKm9HemN5)Ql#=x4eN}+ z8G)YmvWDMF0oV=)=L{PVw~iZSwvmhlS?nns*2D}t({~vAt$BmN_LW`+o)I82T4xQ* zuuVLwmKFAHSh!tr^}7-AE6lb21B?B#w>Q-BXB#m1Jg!n)VsSCN&kASUw7^rX(pw^> zZk@^C%iq#)U|)ubZ(cn3S#?J;x8B%AT4LuLC#L=-pt2pPT<;OUTE8QeQ=RiSk+vas zMz(>;eZ!oy1IO(&@_8)j?yPT-mp@uDB(z;zfZrw;rm%q<-S6UNi8KOU){)e z69(M?(%TqHp^cuhZm8T4;p}qGnrUtGNR_*2a%=fT2mmOQByHZE>3s5zR^w7-Q zde`ced1n+Q1&0#S&>AKDiuhD}L-`uz_pBRZcL_e?3o!dwuVJy7=|9`d{XrRql7%0& zz0R$RV@Z&LZ!FEr0=hzWS_gfJk`-&mC=GI~I%)^Z*xejr^uF~UyMkEy-sqb(*1>$W zNAZ(zZVxaWwxB1RngmnXu4l4Fy-NnyfKuQ^Z-|fjqO?KOqyIm43R}L=Zh~p>dSOCGD*xFyoneU z71py}hb|VLHWkDRc)8yv;r)c$+P5CeN`MB@rl$%3@u}xZ(O?K!1r#ucUkzQ}xA?QK z6IblLmRjgJJ_BwOw%^h|$_lPBuaJA#No?FS5U#W;=tWZPo0~Ah-m)e`%m*w6aPoi&&*Yz^>fARZf)(sg_KPU*upGCVtghfVN#O5fBmyjSh+bD{OW(XL z;$E1}YuL_gzap|YlEZP|<(v3gf{}tlTKrkN-E#Yg^!)kOlVn>-6dCJtmq-*o%=xMK z{ho7ovtA~jGQ`o%%kW?PLV{XQ>c@5p2W zKKfX8h`#gUONAQ4ATGy*tHnWLF0$tu=OsHb{f92SlO%-*Mbq4}z;OP@!_|IF?df$A z&agHRD}Ce}7y0!`92SWZsLN`4v`qf>S!LE?h5PQa><;TswFMH!yb{M$9Di;r(qzj`u<*9vi;mzcslGr(Tv=$w0RS-GA>M;N2i8JWniR+r}lay|h~=b>HkfuW`W)ExE* zC%!jd`Co(x0(c)RFR)6l^|0~TGRx9!8%CwNr{sDvSO@HsUynx`?*CPPB%xDPej{WdhM!1;FH zCbL{@`ST_i@`3~KGL~R=V6*+nme3Zc{?5)m`5E@GK0vCn@_qity%W03Tw;|~HHPmZbOy>I3<(4PuT~q7 zWvZam6R8Uw`av2E_q{sqzf04#XQhCyvnT03#yg(BR`nGQMWIWm*s~74i4yESs+?o|N5>aJP_otvMZ0E zgiS$U;4jvrj0zU+u^T^savk7pVzWv4qCIZ?Q>zN2)4cJ&y^g-4yL{|Gf3-~5K>5p8 zvHlTN=uHWkOAmqj!~Hr?UqSQRsbeIfm@EUp+;(FTINeRoT(75g+^<>j;9Gn-1Mm|(OvmEEGc!o(n|jrMc{jtXblPrU^J%qPhjm_do|gC*SiD|LLW zw2OhX5^gl)aHRExp0EUlo*psglssV8nx;yk^konDNDBsRhP!v8t5~9hx8lW1TbSj3)^KTqKv~H zGO{>OkXNmxM#bC6XLd=#Lt4ipw?9fv)ULw z|M`X0n#EM&Y6BdDJXR968ISfVDBrg9YQqpdzS4!Z5sUOwERE3w74hmW43=Qc?uw2m zInp`vXs!d9AC!x<&$iyky_b3_zE*HZ*GND-RTyW>sK-DQjb7JlW;_0RkPse9Acd7H z87R?0o!W>9?)Qp6#VZeEph6pc3ncoig`?l)C$W&D*U0#697%p*;lttfSMW&2kY$0+ zHlzFETc=(ib1c5&rNOAVTDqq~e*FAZZiw4g!Har0S<6Ai&=O_P32b z(AJ4ACzB*LM%1SYEiL-@r_zK%h!3DGQa(-Ds-re;!J0>`Es~i2gdORIlaSDhVjTn2 z@yPqI<2o)qUAh-9lpK5L-9AviAmJUx#^9^qTJY7pR=Bn9g#^O3>U=yt;Z2t8yf6Mo z4fs34UpM-1>X`e!>TM_ozHHJCFVXueW)je=wJv~{%PU8X)a+s%Y##mEo_DST69M^_ zqi4G1Gsq(I&up}($anyhj+3g>HBCqu*1bu`(3m{_vQ984ef9fTmj}uA)E0*)t9VTs zlR7k6gaY?9Jvt87ooEYs9d_4S9oWX9+IUAVGcoh{ep49XnSX)d4pUV)Xio7d z_+L&k0XNKgf9CI@Xn~0SZ z@%FnxlsO;j%JDF`&TI}ilW3(Xa>Z0v1R-J=Q-4s+e;;c%Z0kk*tUE~KK^7fhTmTiv zB;XuS0_jQ<;5s@rlX8><{I1KTPc%da0}4(+(oDrDp!f^C_)kX--z9LJa0jKx*}EYj zI)>O9*=)UPQn}Snoi!g{zSr!xGla+abBuqSM*QY*V|f2Z(@V+>hU3$$+CM!79Q}bc zC5pgGX3(tMv2EzibYNy;KTy%3NsOPv6}Sw-0@7dxIj_@<<>gocI_TaSR)`-ijenUE z9^o1HXwN=fKl$)~ya4jopwuNCHcOm7OPKVOL)4GMi=r6RJs{;>%W6zXN{&u1sK>pO zHs5};-0XLazrL8O3O7ypBmSa6Q_`71bP{W0a>=WnluZKY+Gw)i<(ig zJmc<~{o^x+glKRn7+EI$duHP$8#LOFh34F?SIkE2n$pr*k;Vg-|GuNK=Um^_OV9B7rd%>2fi)-zilLaqf1$W9eGbm_(ju;C(-@n-F;E>PJB<2axUZ_OFQ_W|FAzwLYkk2+J zkN(h@myF^Ce5yJ+AZxg+cx4;>8YRxrqzXM{JAI$En3pBgYPWo~l~1i1hm|A`3R1gy zEv0`Iq)*h)E;A(&Gp^cESAf#qC_sUcC6(sOWnob&b}j_(nGbJPo6Hkl{RLQtPg7nM zcTb1uRw#+SLLuvx$sK?tyNw^K-hbuX@cHiR4j57HWb-sY5I;eWbrI*<U55N*t{qh1&c4rbxCXhqsJXuLZ7mSDpiIdv-L{Oj{qZMy$NpUmw05gyr-<3$~ znLY)78TG`~z$O%_kA0jNwQJN}pjVea0)_bb#evgu6rPETrs=G&zL7I&V-!quSVyn$R?Fh&e8?+aPHEE`*YD3jQA) z?Q}y3@lp9{`vzrVGep;glX%UvS`t{-pv1wsgsa_-z zuY*;d0Ve1rFgR|L{53c(T%(V`wICM6wwU%}HSj_VKu_|NPCb~W49diriO39suZkXo zQmReB5yE#Uvo8pC3JzFO!vJ8%(@*80yi5PrVu1t2+9@!ZI*?s|{=s|FPsMY8-Vb0F z;;DL(hGv=UDSi+n^0|x?h#szC zD9Z(4u+OrU_Q4-Vl90J^+8l>tly!Ek?rfpQ%ZcubfH1e$5DPmDoIq zMjaKo&%T!`?sz0M{AUzzuY@+!3TSw@n~qC$uSOlj$eFXUtGMNBE)K>m3| z@*BU#o%)7z-Q*#Yv&NiEGe7?OxVB42E7A}m;ZieI*2xNkYG%stDp@e z?rNB}ghRIy*K@@-_a3?$08VTpK0ZzJ*!a|m%kr3ri>W zdI%kSBz59-=jpq<|Jf-e5}?V^&Gpk6Z32T5l>36SCrXt6D*voNKva(*P@e@}Uvu2B zAVc(Wu_&y73ZGWO|FeovtWM9wIna$X`s*YZX7tjgu$aWWy@cLe2MX<%xtdzf0G@I* zvCOnFoQqB!1o`2#9y1Xgan-r9kr7vC@6S0AP-U~WP~f{ zPLr0Z6YD^n z?$ruffG+R@9U_c^Cx0_4#P#He4UEk(FPUrvt-F5a4|9(=IG$HJts60Z@HiN1XTr5a1Geg;#mfz*Y{g)~ zB2aB|bJzqkWnWMm+(Ylj5hd)b*|bhg%uw*mcmQr*!0fQ!BX-yYptMh`=Qg+oxKuGdED6JXdcY=xR-^!eGbxi98;kowdUYvTaOP!S|mBP6#(<+m;#W-hYz&KK@Y_y zY=4`*`2>E~79WhU&EVWmf zKaD~dOyiP{JI3h=2XN6Bw$sxe-;zbuXXY>@5NOog0~qVgYZx>f{e+vm3S97-8kc~5tu=N3$c(Z( z=lF!+Br)?a5Vqu8WUg%9$N)0}k%2FjSV>`^KCU&>V*KZ>Wf)_Elpr{xy;1H>*f9_q zV6@O|?Ew760KmLZ`0Y!}5!fYzroT2QvVU#+t3N8${Q2#}^9x#KhghA>f8S~?C{(v4 z=jt6UP3CSKwb+r%Ro(Jj+uASw>A+PM>VoeIv@gC$DrsQ>Kgqo&x7FZ;HO_+m=VyRo zfNTLIpw>c-_oXkDyoVGdP@YPT>!{yTL37;O^Au9o#{x?qq5 zUP}PzOk^?ke-_z)TXX*SCANBIJ|k}U zgE8cdzzLKA-{NVX5N|=S(mfn&{XZz!e~)f;Y7`+A&nCMU3#i&R!RST5&G&xvH&o0a zN=o5IaY>0-LHQk-2dsP&$jtq0dtTj*M=9(|M@zN=y5{G<9VK`j-&@bw6=xH?yH7j% z84|Fb1I!nI*q$c(04({;UIxz?n%}@Lbri31rH|4@`GEI)^zK)n48{KWeR614_LqV) zaK`~oBo{w`^L&*0x-3DwJrdMcC>hn}Xf8hX`9}o5lBQ!*pao%@KuIW;)=Q~&u{Scs zT6O>Y@~HA102iV@aFc;u#^3mNcncF=-(_dC*}j{>>7@;wP?d^ynUDzij`(wofz3Za z)n>#rsS1ORh0pv3Co9o+GCd5TjUEyUj_Nj3$AVZvnKSESz7YQ4XIHi7-)Y=J{i>T**$+|w^@$9)MIW;~ULg5z&HLwZo2Asg zWtAxW^}(zd?|E*i&&m+yig;|C9*edjLYJeLOVM3}D%OQjC$t@4iX#NaFgFXc_}0>It2zvC{DT;g4cxO%pO?=GB4 zV|r5-_NTmnTcUWoyqj_#{*VS|QKUh`E78_}UT&gTNoEilKsoE11?~|XqU*3!dxAuE zQGbCymCrguf_5yz$p7^i-bnf{1H~@c5`6Gvige%a=V3 zE#S{#JftB5{hk00V$m$mTl)We9y-X@BF9%}P=mA2L)(C99xAIBc$9+>2H>SCY1VbU@0n%Zo0`0~$Pa|nfyZ@E-1ka6k}m_AkER;rkKCbg){J$4*4@#hy3 zz_kdl01ErF8BgpEr#hDR4#CTC@LSa*Pz69lVkP}1{k^AMp#WU81qNxA%E0InDd^9F%7)K89tX2WQ!|P3Xa%kTUFig>C*gS1vp}6+_)GJW z5dIyw2%5au5H0xU=VZWwvMjm7b6)CyG-CZ3#_yr|4n49XkN}I{V&UjoT0!hTZ$$kL zYANIDzRz%m+z#V0Y8vYc(l`ZW@l2ba$Di+)0={2M_Oo06pqPG7lCnenbd%y#C6RFI zG;2_yJVTxyuG{K4fqIE4PRhFe08qxxT>;*?_k%r~0FvJPji^g=0kyz!z(G)KRrF=l zo+v@XSHNz^sFV>93%oRo@cwD=L!yFq`Mh`539O2+v>;*#a>tvLJ~U+0=Uth$9ckumYZbCzQ5kUwj@o*1n7SsV4sDk}6lVIX|eU<3_ptUGD4x1|5E z5b1J)K&=Z9;&H^HQ7e~THC_A93?rrk5RTXY8Vqo^0P`H@#r8{}#4HTlq)+L12&eWQ ze*W^o+bZ_6?Tix@YVb(5*zm2rtXmh`TTK}%L6M1XeJ5>qWWigWMD;M=`KIoa zDWXiXKz8&dhb+uwxIli{xW)7Cn{4|y-Ay7mco}3SDzl=g|NM%*urwS`q7f((Ob?v3 zu79g^z)IKi?G+(ar;d~Tu>8T$wW#wnzgXT2XzjZ@n5!V~MCeB`7URDWY@}cu|Vu;o@DtFYm-YvAmg|5V6uif2lZ2=8ILVS?(p1-H3 zGU^>qsL_pe%}gdDChFKd4jvQhTqx27MI3`|gY|$?qzzz_a(2sGTd0&yV6E#!2dq~- zb}qOSU?|zhMAr@yGIsDc?|`ls`U#3erQ6!t0&}3J^ncd(%3U>^sRGkSAyp;YR4LE6 zAPfiZ%WvY?e29~5opD9($FuQmFFECh>7=LYqiWX6pGc&bEROAlJB0*>~EP2GaO*^BUULfaPDDFy!4^A&DzXw-ST zG5LxQEVr947ZcArc&R{T>1g3wu~f#Vud4!r<1ro%eQF|#X%jcbVW||4eMkD|`XdB; zU<#{E)$j%ceih4q1wb{Y`f+&-Z!6G_^J4QPBu7nhf%WPo_KTwF?-fxP36`r5_ZaNT zxCw?bw+1GL9N?b$Y-v+d1%MDThvUmvZ}c9S9KqSRAf=21lhk!?nhO2NEu|U}f38J$ zC}i|bvY5t$#u$-PV8I1i<6lc5_`qFCK&l2bXdpo_A3Qw>RNGlZ1fneV00|AaamXVZ za>VS~q`0VnO`C6`M$@>a%>QlR44);Ix;CltHmtWLG(lkdz)>?eMz{q;0-WhHfl~q8 z4pSka!ZESzmQ6Y|k^%mE9b2EkE>U47@JbkYms%uf@b&y{>8vy=fHe8NrhR!ZG`P8J zDg6szOLrJRU}vSx4PyGIfhhNG;EXQrcLa*-{8c+(j{tnqfv=;O@4*r{|K)Mm6rdB2 z=&Ese?6IH}{aPLG*nlrCfWG~~+Im6p_@8IZmo%v*$mQ^R%7&%pXAPY8zBTh;f09h~*=VJ%;(HHAKfHPg%6RvQ9BiINEVey;4ouepxwylP`(b zdZ6HPmM(!Mc9z^vl?9O_5f5dn%n&K(Las}LTHo#dMeIOS>;So4LnGD zm+bTNECaj}`-f0_DZ1};*{Vz`$v<=N$^DA$;+7ZYvXoz{>ePNxplP419ow-}|4XJL zu)gK~j>t?>vjPU7CW!2hSrmk5?RCQ0F;#ICC2(R-G_xe8=V zfv$n0fjXJ||91bwDgL`JG&KI)ovR;}&bAA3IUR)aN~pr}H~5Pwem^b-1vFcV)E72! z3n5+{pD3&RBN%-`Nu!BOS_viCCLfuCNI7djesTuC+e(kEr|YLqScrh_SYBrcCWojm zuZ{W#c1CztcK)2A6^Sz&uvn8pO&UU95B`l~0Hj?7@FcG`X1NeJKZn0Mqs$+!M%N)9 zZ1>+Ki0>!|swQ;5cmt8+vLMQk)O#yOmk2eF?IPx;Q6OKjbhrQRs$Ro4WXK|jz&o=Y zU{Lpl0QwE*^GvVlJU+}^;J*#*Km|ZxK@gaw+@PBs7KhX6`NCI|HGKb+?NnejwGjQ? zJOD!|#XLX)N7Q(0VjZ}TJbuxb%%)A}nmF=lfWNJsM=e(31$4rkgu9dF#gtC^vrk`? zaCnH71Q;Ro=z($tVnuUYmS3jZKXpp;7t;m%!Q`+Y+{?M3qL(6a5n(p8MD>OAvssgkop@ z;S&7yTT$E8AIN}o%NIbl;+GQG=}k(+%bDM z?E-L1!Uk1sawyZJ z>)f3K;Un&o@(|GTSpf$`gL>O>V21^A@9=NmlREd|HW~y5>bG}in2voBAbw1WMqeI zzw@bHyR-4#HLincpaF2j%M@|?3i`Bkf?Xgb4aRuz z%RFG<0NUY4qpU$5jWGccXh!Ay$p?7L9Wy?0YCe_#^-MabH^8XG@j0)Y#Bpx==Kn`x z$#pRcO+EdJP=xrw@E6l7V^qnfg;GFnP_?mM_i#qn%u|WnsdNo`N}N7<(9UlBA*?|n6ZcnvEA81+d3^Q&3}*B$EC z^n?-j%=;|ziUj8}imiLj;X`N@v94AZxwrw^10aa&_LbVC8ZZoTI1r;ng;JM!d1|G; zU(5NoI6+dyGh^?nvGVH)gq}AZX%~8>EUs3yQ=a_!QPbOAOAL+xD=Bv%3--)4 z)N?E1OT=emX)GUth9NuMY(zzzSVru|2CJUymUW`(Vm*FJ2*HPhK}Be$&Et& zdM`8RlDVi{bZ*|KEmr>{1oo%==D;oK-pelDaCMQ$A(-fFV@`YETntzp@4D-q#JCeU zC^}ALtkY=i|9c%}$)c16k)T6fRiv+tdGzjN)Sb(RLM|y>*DZ2Ro@PZLi8!Kh*Fgro z*!jNQJ6uNmxPIN!;SI4>$DT~wD9pz6#9)^UpWaZ7^|6M80_=;B{sy>u%IKzxdICW6 zNALz&QY_>)vZN;Cp_$*h)N>ra`cRv-N0LTW1Vc-qwdYq(l$bR%-sFVH=aZae8UG*3 z?Hug*7O1`P5C-s4vjpR-jXrO{+OAB9eSp}(NU>z-7?*hh71$OTPZZnX$F8|Ks+T@~ ze*zGIs4cY$G$~mX#pVS?yzr&BQmWZ?+A8U!V<{2F#rz~C4+i?q7h(Oc$99%k=H73Y zg`rWBTsa^iWM3Xn%Y`yAsHI;8;|GG$XNN?HHIIQiVru?rT>7=PfHD(@;UO^ufUdau; z5Ty!qlOeQoasbN(XWcW`F~5Z9Bri4ly0N~HEWRrJHTXyFzWx=<`d%I~g+utpM4dHP zRCQCuKpTVz{-Gd>*U%%!4azpCQ@$A}`Mn3q`rf<3c$=k57QxBg}zl7vS ze`5bhlr!%1A-QXAml!v1YM-a#b@W`yr4miMxz$hmtg9HK6@~YU@t2V!dv&)HJM#sd zg&s3o(eiyW+w00~F?bG=<P$D3?=|BUB6-(4LCVoNDAkTHXZSj`7P0 zAy1-^xCCd<$djEOEon!@o{%4FnEU+k_*EPI;t|}EzVnS%J$3U%&p#yu7-+n`M**B2qxx1Tj7yNBU(9WG zuwrrSb%410+%$tW(7&MLtIS7~NNS97)c(i{IZSez4vFGF$wd|~hSCi6>UZVvx!lfR zb zV*03ZQUGF8hlr=Wk08`?+!(WkyR!{kNn(jI-V+=9s$g)?nL;1ynrcn008h>O+XR>4 zK^87)R=2^@|D(?Ed&4Tw;IA%H*;K$H)PYy>xw_RZsLjFlh*X^X02cXrGkkK>C&_a3 zm1NYjM}8klH6CMF1L4Vr+}eE~OkU6HGQ*ZMvs_NFxvVHIw>F@&A2G?Cw&Ok|)KFik zXSwQa!rYaASAh6+2T^@?_D$85DJ{tea+CM!GduAl#Yca(FqKssHv z($hT4G(}0jdmAQkz%q(*) z?Fm!ihnvkhlLFjy*G+O_gVX!>rCKTtwM@uubtY|EP&a)_IObd8&Yp=Si9( zX_NGJWXALS{;tS!bApt9ZX0&pyX(6)?YX-xz0xT~WGhx7B$%DlJiXZC1T2$$8u||1 zN?9+K%?nOEXfq=n0TP3PXq0jH ziU#!qWjs;#!XvCTE&H7{C96yCDV=n0L@srPHtjMf+py=LQ#@Z^c<$}}aOKIEaf?6R z+`0RlhR+DUOCCDab(=FGcgk`EXbL2c@0ohzzeG@&kg*E=zc!aP{kA&M65*A~y z$u!LYZ~~EXO1|F^PVcEEMXH<|bi}8tl8!q>{D!KtCoQ{{Z!tf9Mi}I{QcJg=G)oq2 z&Vc;@q=9qLApejO08P~=055=?v9{|{N_(6bjDw-8-t^t2BP$eEebm5_R__RLKhdV? z`9+cY#E9Z7;doURXZ`E|D0m zw){|VTFHq;7s$jE`0UFoB?Oclns2UHozdgf3`tSeFlVOO9YZbY22`mqEjkfDW6O!2 z4Z^U-VOK{U=B%AC!*f~s*LmhG=J;bG7srdVzh=5EKr!iWP75xIe=IZVU+!EUuL@iO zp^2b)?laG~Nw|1WZb%~|{pyS0zny&`wETn9H#__{yj}EXxebn^2#=?$p5j8utXeGQ z$>!KTgMHWi7|o~nm1Lst+irfm_cciAn;c1fL!%r)GViliRwH?#1*P!^1{dXbv%aut zMe7t;I^^WJh^7g88(mY;iC^R(84Y{RF?hY6x%GsbynM~Wv_R6a6ONz9W&B7tn=0s7 zMGB1&c(7LOu6*{P#!D~nEYSWpIPelqQPzP0)}bTEPD%NAA^;YEOY9xscR$5ar5w%|QF2XZMsG&m%-M>P36j)?b)2<_ro)yn3O}9#p17=?{~O94pmx9ffu=wEmR# zqjDczCF4lL8@(WK>obMTDvdVtkEw3E@kE(wMIG0}++RGaMe^z@%mj)0wC}Hnzus!s z+M9Rq`}U67@O0C7txTBy`5SgzOemT&4bk~};g0qR{R~FQvNA(o|K^?_^5g2S93i1) z&6`e6^CjV8LT~JSJ6ajePt?v97=4m2TR!@9DMqzvqLA)?6fj||nvr^XjlY}LVg-X% zxtXKkv{F6v==7Dh*5p#!CvZLFOC&VmU7)|4oxxO4hjw-!vhu{8THsB}=8aEd?*J|i z8h?RoU&i`YAp_G;asasMAl5u7Vs3?Gj)WG+Mj+F<@g|teQv`Vpq_|RwP$8nlu3X~I zzLDxM2WyZoEj61Rd z0tzUrtT4Q`yW7PymBOkj$raJSYaBz~j;a1FTCeM@A?|-moH2^sa zaYgjq0>hugAJrP-%xa7{C>s2L_jqR<6tuswNLo{>BttZm53(a1my(3~7?~NZYT`JU zbomWGC^8|=X-k~r-Kx@TlpBcU1k9c<$>7crLlqh(Y_atVNPnu7OOk!IJ9ar7W9m9U zcaJ4piXWv8+Zalw0bol&T@DuFD1~FAMevdQhw)hdqPvk7vi2`NqId2Wk=4)kBs9@x zn@2l_oi4gtW*@tqeOgELE2S@pUzNPtngy?@P_olkIlhlo+KhnhO7Kw{BuGX=O#Fdr!i!)FoKlVU3`C(2c-YFmLDNwLk} zt}FtqjDc)E0%6(Qeop+M^AypA>m*HH;AAjl)lvVq`{|smYrq^6fL$D^+g087(J?6s ziB1*u}O&rpw@$PaOQ5F{eE*i+CABr5Q!UihhwBv8(1M2|v!14_PaKrsm{Zp^E|J-%}_ z(D5#<0_ImTK-LH^wq5S>=HFeztVV{XdH?`DFnPCotfsn zK@LtPR6NVG??rolIevqj8$;KRXNuU>Dnc63Lx_LcLuKx)S1q}W(&fa2FxXdpeg<^o zVN^9Qn0ZKh+P%*-9KTrXvTtz%o1?OmeJcdUOwNN(tEk*@UOeb{+Yu(IZ@{D==%QIu zE(9(CnA@(~YDNYLl>YyM$=FRo+tQC`WZiB)AYNU0qaDtyb`t;9Oj*=O^9p{Pa%?nG zlgcvYqER*PZ!*|O5bolr4TejKT*EL+DyEl?MbL&0a-?yz)f-ZI5|3RM7B39MJc`1L zuGt|MVY#_{q~h{yu#RmAG2fBJ-<~WJly!RLpxvX)j(xV|G2#{CPk{Tj+Q4B5Ax^z0 z$QycVwIk++-+p(=Aojm49g*n04wHQZTrPHEAGGoloe*|*B_3HF+=sl}m%4cq+CKt(eyP`f- zb1z-gQ?=L1%#l0xZ*G0^Y4IsIK8h6JfpZS@ z1|q=(Z;PEM0y3CwN2sV#2;+q`F6>{L0QMew&!$!9rkOT`47e<%oSFq|ub%CN+7Js( zPp-#9*Vu3BX!H_}MA$yb#}wxp24Iy}3FHDV5uaC0;n9^_*$t}-n>_c8venQnU}r~} zAn2vRS=^4xe1*AgXeKaA1mmY-UXH20P@>NEF>oVK3#!SAaoK#?mGu6->%XV=yeLWV zGsy49ldIs_fRYwRbh@Dv>bVCEfXVAV(IYz<1oX9B5aItE36bS`aVL0uL7ajpL~vJw z@1FwkPnk$bF~-fFy5Fkxc$_ms#mxvUJ7#nJXJn(2%RF6Zcx0hYcE%MHBn!-xP5}+v zy?#aduRse_ZKIajxU{)rLs^fVT*QRfDQu)bGY@=*k18)fp zq9wZm?Vc^Z#$_1=wE+A>hxL*zi4oz5{}Vcp00~76T3=+;;(6c$HZ!A7b%!}o$t+{y z)h3RiVr)n6>wj+3Z3$ZWvX;>Cs_;>$Kj+ugDy+aVWu%G`6~4TGUfON_&+Xri{pk98 zw;{L+;KLG@9gL!KeUi+mdu(Ik3i22RsInS^h@V!FBR*ctF_~~H@A$ZC{Zn224rz=m z=#8y=d#&c*UKg%#FUzk)lN}r-5_AmLuKcx+Hr}3F>WQBfM0`0&a&#HE6&j2V@3d?? zwG#i_A4#H;C`vg2R13%Y<&D0J!@6@?_iRr7&YDt$h$*^Y-45@*;q->cQtdt;CTayB ze?ZO`gqlr@f7cJWS-ey}l)eM|SoH?N@sj$zYA-d91EH6?$pnM%Rx%;5W{l6;VCI06 z^gr>75(n9BCBqL;%l@b$J|9=V0AS~acp5*nm{}d&_Zs7+wqS0uH62ka`raO_n5i-k zy(!j6yzA?&n691_wkGph~*^ToZzBjjiO{&u~L1(@vJ(_7#N*qfU{6U=17u;!R7 zpPwK5_pQ&W0;kas`og8k{znoW$l6R^MzIeH6nEak;?R#6s4-lF?ukAxuygziPat!oZv^A_Lq~a|Kb3QtV=-Vt3G?SX`u1Vl`nqa3djr7YkfJ8 zXiowsPDx|2hZ+K&GdcFyafw`(bAYXik`?j5mxxG1iN@QGI})hmp7~v#YFuKh*K`f; z{0&eyb9!5wYtd8TgwWNlGj~a)paFyoCjbK_D(q(9k6BV&7v_n9zR-W%$Z(elmu{tJ z&K38A73~*GFDj(S$IX&9_;8(n{PqjKW14Y##`IZaw?bgbP=9raIO7(~6CS%^)4#gG z74z^Q@M707CR|(JQypMnGyUxMsLMx?gG@-GV(574Z1gG7QV>lTV96r8hb<%oZaQ+o zw+Mk4{}}~LJAwQrvl=D-KoE71QqbS3JB)Gc$i(UMCRj2dZp{}^WFhuJHn$%r3m1I4 z(o+bVK@9WOTQTnk?s7)d>-X@p8j&+5l0IlH`hrM7Wq%GeNQL>W4y(r;}wv9eDMq- zO4Re)U4zfCNR%nt3Wx7eFrFfLMwhR+(v+W`v#0fAHCXp8MpDf9(UcE~`*Y0lxYM)W z^gB3^CmQ2MMT*INIEA<`1M1SqY*;>4ZHZj*U=cj^YX|(F#lYAD8K{xgguc-KUTT!` zc;{5XpY&Og3$QJ}y)UcWf^rNVI-Z@BP9&{c@mPt(?R4O+WI3TQBcVgMxk%CxPv!XE z&(p+#e^uc(Iv`6qj=1-noJ73vpfv?Rm|as0)9&`KPKfeL#OZMlTkV0oS=-WgB$XAxEwQ#rh2ap_BWNe~*G+ zOf6F#@OBYKi^DKjDL#hj7LI1r`JyGBnJ~LWh-{SI)N;eIhLf|LLc#5tdWYd7i7hdsRpn_AZ z|B~xQt3171528wTIBkjR9_(FuzMF5kOKM}!z5zvdui1h;pylVHkE$jenSrrQhkD?q zQW#6Z6b7)`YX|LXs)}mBCLYmtKR*@2bP0yCTiNtwH(=E7*R{9SqSu)H8yxc5D&c2* zbn`&6%?ZSWFe~Q;Z$Fb>YB+?5rtm*%@`5oLg!e;5UOrqWbh(VZORFO+y>!<7`=@00 z*@&}K9Qn$5@UTKE{`)=ZuL;u$OO~;Ah0Dc-hv2JJ7a3`(({ZT)IH7AK(7N2{xZmJK zJQgd{$$cY1=sgju|DjJ%~E?H;Br{7Q7m%tQ7lD1 zZ`MW3GyQb7MHnb#Eb8()ef+XAhtS;yvU`yhFuf&xy(Zv@k1H4I-PJJ|DYy>(>8r=t z0zWi#0t*9~9DU))S@s7_GgZO!n1>Mz&c{1!iG~+XOCS2`1ja_bgvmvr0~>rl&(BT0 zy?p45Wo%}ss({MWp~FWTn8Y2Q-GP1NnXoP(PS-w{uSh?qKw0v##v4MYyw4QX{LCnv+s}|xhL-{fb_3DJ3V)7q5SYDxM_ow>>PAU&lqy@M!+wT zQ^L!0TzLGs*eLZ$2zld!`|8ygQ!JyCW6x184YI%Iijt6#2hg-UV9D8jYdt(tOzF|^ zT|v2dp=L2^tMT-BzpZ7dWhe7p+_p^v5YW$1pB|#6zhIxXs1{Y|+a@}-f$O;6?xpaU zOLqc#RqSI`R3ESn?y05*%+>rCMiKwFJ0o)w%1Iu=UFAi5hi^v?YIi4ql@g<%XQKUJ zzCn5Z;pkM6w=o=xooZ;R~VaF~2#U|P<=mkNhPVa|}Lj)#|w4(m`oo0e)y z+dV}f_mq$Rhu0vnJbI#)BJ-0t$b6I1O`dHBz9KRK0SCPNkB)D%?9dYvDCzfGpf&P zlJKKs(A-&887FU4vgGRRjYSx+tdFmx|86pKFrYRrBgR$vEqgJZ9d!%UKh6e8AR=~q z&Aev7I=3FF73H4?!q4Sur8EgI{`+w$f>t+k`yUM2Hs-)ghnM^8QNuTjHH%VHxynTN zy@)ls{>wcI*7safT!U89s~wjx>&0hNykS!T8UKnftsj5|hBr&=Lw`LkD0^K+GU0#d z*fVB@m6pVF>`AG)ve(DHUTZ7A@=9LZw|uAW_Ag;^Yu1TVtqIE2xDy7P$-?$=M>aRYa8 zIf4j{)qMsCk}K&|&=fYmSUEWWgKfU8BVC6MU9bsvZ`+%6e+jg@w(j9v$VJ8732%Xb z%w9oR0MDD<{F%Ab)2)jdu}&%p(($WlpVYZXIHMsGi=@E%FdKVo{k*(3j#%Dkah5Dg z6(P|#R{_r`xr8!mI;_FesdM+n;bQ}xiaVdGN;hd&i-@+yuE+jvJjC$g>zGgwM;`f^ zlr;@p8Nl{K?`G$<=xoYmIqoMCyf9C#V|hw#>@VDA#{SU)h;^jbcx&T+x2GBtCx|;qHRPpVbCRkziZ3QxGwzN0 z=qC9$1-^Q>fW38|%a$_EcUC^KpFJII?96HSQVGKJ(ycs$YJvWkImDuXe7C!Pciv$kj<@%z zL0^5K;2o&Zs^XyP{lex~=6JCVndxq89UkRJC>a8Er<8{e+sBKr1iQotJne?vpwGhv zHwLmB!oM~U@;{v0bzomE^nKfzjS?+#<`EF1z_g?NTSAu3|^7$Kg)8Es*Reh7k-t?wSuo0L&W%rhAJeO~>P7n~E zcmioo(}}b#rq5no$BMV|_RfsnsV=j!AF1_fTL21B%tNp*&c6^O~{B1hIhghau;$#@KfJ6*|L5uLKQ&{DXlLP0;t7H4L z+L2jh?0wSE#0Dew&PM`juL0+nj{UT6o-^6}o=QA*(R) zxRJM9UO!&Za)ojkbh81izwpY$aSgQnR$nebvk*nLZYhJJ zibYmt2tAP;wHzjr?z$4Y&23bxV6Hq&ilb#3L!0k`H*>0MmVRBbkRVNXGfmS}GM^0Q zmz*ot!$ATY$ug;;I3(qu6u?h-<_D6}67z_dQfKJ23c)BiW_qo#olhNl9qz@|djXOZ zgnb3K@{h1N&}c`HdH8_W>+kp)N4(AHUF1I_Kr}$svK1>uKe#hvWY8{0seK zKKdJqA6En@C`-!M7%!GsF>k&l8!4dlg>csPS6K+q`%4D_C*YZcHgi{<(ZzP`ITb z=+^Zdu^n%Anu{VDe@n-1BBIoE&#SFWkozj#1^Zf>p>!^=26?8^hIlGKL zQU|ewQ4aO^?#~!;`yml&;nX4-nNCgC0$XOaY&1+2ghNT+z8Un5vW?^h6dAd96Q^wp z2f>3Bltx+;y@5A?03X5VyCn5s+oo6%E7fk?=|b&JmIX-?G-KVU^yv9?OtJ4 zX~)r~&08fwu$>C)qvy~YbFe;HOvw?Zds*v2Cq_dU4IF8&>*#Vnz_v-Vik3X%pJIEe z{xkA^9gC+v&v!1DG0F^OwO@vASJsfof&ECPU}ob;Aa}0HN;^c3kq0G`?>gOa1CVBD zo7=&#?3b4|>`W3o{>^8hLWHxyCr0&$>4zI4HSx0%G;#4hqwiUJBFdm#=+pXO+d6%G zztJ|#g50nxFcbI&)k8(_*-o0!jx)~fnbM=bMvgi28djcHc-{?Cg5s%CV-fZ4o+x)W17=uV-ez z3iaCwL0%A}i?Xje>gO^|aC-iH{5Ch;29Y`wXpcb5uW!VM0wRXF*B>2nPTXPYF@4Q=bQ(TWC)8zs!yjLSD4; zecgX1K$jR$93P+rC>PHZv?90f-mh98Juwg^xvrNO>}_XYnRCM6J*WXl4eY*HG_u=F ze-1_NKzI+NxJflG4{`(2kXkP0tIF~cj2)DgJs?o<&~=(aqF(wgkTCQuD*^KuWn1YB zSpwb40q#TPnx#~D2~~KP*CAA>9E6ISjHLOpJ}THq2^O4oJOzg`8Y^%#Tw?`9v=ZuwVHwuYh1#Uzc~F6% z$i|>QJpJl%&?uCp7HLihJe%z*Y}@a+yVhqQX53@`Gs4ats7;^NNlrDBW$aRpz8~-g zmKd)Y+=%(#39md>12TXUIotVVFljh5r$vP|)Qi-Gun-Bif~7;MzKjDq@)*+@(@67f*-|kZ2}v;ybI}w9>3{*% zqz?>HX)u=ZJE*y#zR*gz=`{aC#2`S*DZCgYx*jMJXkEgi*$4x7mz8F2qgP<3b`!{& zmHre2>7HX7E-qp|8;;?jD&?-bLw5SDcS!z4>KUgREDt!Z1A z^G+n+Rm%T~C1z)9I^iqVavrXcQ>HDwoT+yWv@Ms=VaPVTCp-#sgk5EsEJcQY&T%9~ z=-%6j8l@lyzEKv9-FKJ#RdusmrV1RKiP|ajv=K6wF#3g%=fKt6!@nhj)Kn0Oux?xH z1E87_#ddWJ)SG&tb^eJyIc5gt$vD2f{@@LRg0CBvGS>rgUE(Dh7c0LVo4VFEb4dG$ zhFJ91k3BY}O!^c>J22Wf^Z6-V9^^fXAUaCGnpX}gfFY||$I?1^3`&gaKB<_y&UCR= zQc@ABz2b?hZW)4~%+flhDl^|F3^Tkc_ZuwQ(q_8K(pc)959T!g>_~1@<-2Wt5pKo+ zs*vJ`j3P3%0(EJPE<)mk@{lrKJizm`zdsR@UhltUV?9+uluS(W&vXI`cK0Dhu|GXp zTndxFfiU04+lRirQyxr0Hv;Tq!2Ta+TH?3!ID!8zCF@W{ zE~BU4_I!oAVv?(%$*FE0HfvHTU!Vm7TRqrs1|TrkywDFInNs1$`7pBG8`#+SABvr= z%LPm>%DNKQsbqK01=v=J$$5|7ik5^?_tYnifW=&Usraw(0nQd9OHFy^No!xr$C#U( zx>OZ@&P1vT+V>8QWEhbODS!E43%g%57HLd3;cG#xq(l3UdR>Lhx~=m~l5g4X-{hrw za@$$~y&?~>d9T0F{13#kv2Wd1Y)9!cNbY;c>*T1tM48v%bY5!YCke-~2G9kn8kwLV2L* zZlP&7b{#GJ1ya%Vm-&bPqEDyD8+5nfW`Z#VVFOo?46xoV%mFB>X`=GXhYPE3j!Rz! z&z0yUC=Sb{>VU%zHo=Uj)Os3Wx)=b&!_TPseQYT12M}IPZ62`uk3Rvo>N;Y2tGWYR zISw6{=y@*XQ@B9+?A1SW^Qp{&cau!L?qLWR7 z%Xda(?3R{vaPr>P3hW9MzpyelqV?{53oH~%F%AoTAS;{@O0p=KP zl*%>J+mu`pg%MTOa%-Vje2nAb?AtXzn{WF+r3oz6m9;2w)+2ekl*A&^TzY?A z*P%hZwcE~U^8h@}##ClapEdTIei%YtI0o3t^YX(b`O&iDM_Qt#juiLq@c8_&R!6VM zWX)dM&1@H_`NoCVam1djDmeXfC-J3bPYrcA_i?G$n)J6+GJ!IsL=|nypBVq& z|2Gu7Nzvf;80sJ4HtV{#sN5l<=D+`OCLY%Y2&Mdl8Pex$12FReY~2R!>@0%}*eK6y zPCV}*EPk9d9V~ab7$ZVe`(^Z3PpXnTdyFEaikrmFs#o_9V76n!X&T_~}8pLR{#OpVJe zLI#_@@jTDi?yBT~7I|;RE!wcExMvwjCKm~Re&}guyKwbQOM%k8b7s1^p>#wvGe`l* z%QyLNl=}PX;V-za%%n~IxDyLoVY(#e1RvB@zU53ix|e@b%SAfX_U|6?Ar0LV%60&rRA?OZ*Ygq{iEobYun*wL{wx*u77b-%EGv?|vdvCN-KFo7H zpZn?Id1_-Tf#T)g+lw*^Pdx1D_4+-eEdd9YPs0j9(9K((D_<)bq*xE?fU@lL+k6NLpWLW$<9HHHm1DZny%?pYLViq3<%RCY_*q&2~77H=YEa z{Wd%0yGeRu;W9&iT+_LK6CK#~=XdFDflV)rJS7279}3=tv)5v6m!?MzwCF+a-|KGF2$~xv_Np4&y%s2kyowX8Yz(3!`+7qJ1GgylZ!C-}7 z@ticYR*!N$Os%Ya)8bJ5^8)i`&{XtOYd|~@NY@3`G%~794~dbI`hIYz3%)~7ey8xD zZ8ywN1Aqf{iRP9^vY(Ei#6OD;FG2vnTz7(?dbA11ExX+(4OGVY9RDs8(IrMICivYq zzF&L_z?r>AXZu#%SxcUV$b-wDevQ$23e# zg)Z+u=Ef0=?2S6(w|_fY@E;@edlq$MQ}B zu_w-4srBC##>$anjC1lfRMb?)sWxwPX8qprVWD`98R6?`PgI?cs9wH26Muz~_Bi3O zFH*Jq(nmR?^zV#w-qjF3SS(x^3uz!WbNi?IQ({l(R}$i0eLR!;40$ploUs!FJ&E4G z;U~<$0k-0`r`J8lv^CLnI@JH{s#1&srsT=yXG<3ue*;6|LqrKU>+L1OES^EJ32NhV z%_7`y5}D~`EK`TTx5pZPmgHbzgp?Q^?_Xg{Doks%ac>5A7kNHx5C!H=O^%>O#0oi2 zJ|)t8BHZ7euD?3ae%t&|+;va$A+6uckE|ycQwkJ4^%Bwr2O>6nY@JL(lFR>o?B;ZA z4`*6`?4*=sq$;F?PcS>6@(5;}_vtj?CXY zM$ntf=8K|J(`*@LBFukV`mRUib*8+^2n0^WgT!%-@AuiyS3}W+U`MlO9cw@@BRxRg zzWh*$S`=;hpB?|`vcK7Of!R*I;mmi{q7~*(pNi6Kz2nW6ZBujxvf0$;{j$e)#c$s~ zF|XHiz)w?9FoDVtY4Oy&fD$W`UR?M_c_S-y(8_@P?`B6rD`*KblR;LxPvdy;Pvo*~ zKTjYvuI?_(ppM1c*Q^X6`Ru_Q@{QiO$v*$-|3Rgi*l*)O^M zenUMRIMb35^2cS%j#n)xPTc!gq9k{b(T$)En$!iqMB}YBKR)mdZ=S(Ck`wZ@QXp|A z>N|3PnZZ^dy6n&J)kHmI^hKORX0R==LmKGMs^;t+DiBxx@m}Qd9V4#euw*T_P74wJ z-h3iJ_}&1~d&mq`HcEk5`};M>@r@9VjFG_hS}mBOlAU{jkiq z>!wFSC4lRZK3OpJY235O7Z)FPk>UE4o!(PXxhC3t%zmZMD5Chb_KZ}<2~t^vSPzaL zBWfAV8u;O7r#2Y|!x+M?;oTschY$1=eA4va5kHO)huy!tZVVJ3V9*ry*6)%|-`?@z zKzk?qoE1EZeB^v9luFLml8?QwthKxAG`E@64AF(Vqw03ShLP+K)2>dQdH;avT87~o zQ^lGredh#ahCyQ?$Wk0!L^m;x$0>Gs+&8bK>*!yg;(OD>M#gazf3Jl@!~2F>qCGdr zT6!$>xcpulG@246iBHM)=e>~P@3ZN?maU|Qy(Q@&pGJ*?| z;=9YkuW~v&cZ-DjVWRoZLqnV1n|SZzU431EDs&i zd}(&oB!`4f*wj#u;LnwrbrOT-9V-aI{xWKyLe-sotl^97a~wZo!%yKHte^8`ng|7C z)UGMm|8|bi`RAgvJ40?6zx;W}tOWy%Yb*rnxwui2ieq4Uog!?PLI-&EJj^xXl(kX; z$?^lL4UkQ0K+{ajb(-|(J_0hs4JWm;^ad(_9^+b1@IDluf6;Axd*1CWnf$Xwy%KW#zIzIh;Z~g6sCaD+}&Alg|i1c-fbPk zy|v(yMdTpiU(G?f`q~6B=ei9oc(|R;Vn-1_4jTkRSV6v#ZTgdC+jnyh(DPZq=ONZ`jb{ZY^{kdHEbN zD2~y#eGPI?!nO?1Tq0mr!!t@N$ug;trK8gqIfu?UH%iTGXz68oa22qeH*1cAaV+s| zfF(p=(V$9QM>HfeVOi^etnVK7`d2%!acW=!O}cXqI8n2OMKG_Ne?NEQJ+MP?{(NAC zE!|nsS?*9ZnXt9?qefN>ktiE(C+!CXUH7dzCHYUMe{)pGP0PArJanZ;Qxyi(ln~c! zCD+6A2tTduGHTSmW}dfl48?w@<}F}@>0aNHWjMv<9)k4NguIO3)^vBU5 z7}G0^-Z?+){mY){yle5tZu~Gle5s4{_gQI9@}C(!2@PY_VS+RZbB|{W2E1ctJA|Db z$oI3B(6M!&6f4**wTeRpY#DBn&udg)teIELu(!Cmk~bKVRMl5rZ$?p}UptHWml|N;i@-%MdL5vB=&D}y&szlVI?}u;Q~LL+ z-Y~!u+WL_{22tfjO#e8hJFh4E?R;aXR71MpeDAF2{anMoah`s_GoA|+1##;6KwTqd z-%8-AC-NGFC38Z1S0?7gi&Y0{^ww!<2Izm>^19iT|9g>$R2Xkj4P9|k zIhxZ~!0l2n<$AuW^}1uAwHi7s1#dqfd;eY<^Mc?Cm#+i`Zpe=?!$3&v%Z*D{9d_NX z^x1d>umIl)TxV*VlwX5g>;!^7+BCGL$UD|P?@Z#M-+oJ>>H`i8Ri^h&A~k)G0g0j-KZtl8KPA$^pDJU(mu@vw`Ft{t2?e@eVtl}cZeqee{XAgX#e>!* zB<;;-ZEZfJ?)y}h^9l)tSUHNZ`)e(Q0rI0rHb(FTnO`q8y&^%G5k@C$t1$Kw@_=CF zcb>1<>{C}4JuR!be&=u}@PCQWD=01tRa!W(-}xabkr7`UhP_YWxF58;-{iwCVkI$L zF}Pq8Snne`p+gqU|7dTzBH?M%z3;kV$Q>k0n8gejVoN=D zX&p1gRpXsw0dv!DWXUSNz-pY)n2RH+-~BpQ=L>c2m(OE!m_4wqFa)Y2ue9?9@cCL* zNGt8zlLbO#<7`)IiUgb8zv+|-ypN=_uomM?gkFc}RNA-OcQoH1@}`&!r^VZZKU+OV8&8LxiSR3Mo=el;#BxzI~*nvF(o*|dq2JQ+RVtK zD-<5HTBa76&dr>b^KI^aWKY|{;Qp;GDyfE2Sdm!vn6zKdnbK-DW7v^X=^h#BVu!T1 zLF)FFw6ljIb}jSH7GeEQ##F!NJL5hxQ^Rss==dN zJp(eU@z*&iw_`u~TdAq)3z@2Y7Cx?2#KdpWp;N6)>9?n4E?ONKBVC+Cdy{ul2sDz( z@|{pJm{mV{jv}!Ml<_ys9HiwAe>Tu+u^L`(lM5HwaNE-WA<2w>U#FGW&fC~f zHIFjn{JQ*U^YO|yP3CX5n+dSsUO{sVxkc~@jK2i(dE9$Tcp$=x`Q`F8vQ={rMxu?F zbK9i%eJr;1J^2Z?D!(C?#q`4^Rr-U`uMp}9>!(rh5)@l(eERbjB#He%Hlg^=djI?k zu*s}SixjDGM=@DlGxDN`?75gxgoKWXE#sqibcZ8agd~`r5-C36;~DZju^FB)=f}$( zS{I!1<*q-9Wj5Jvz~_)?TDOmM@@#rdyCLMi*q3OykWNrFa>}8dxx-u5 zfEa6$hwH5O^XqP8fSSF#(RV)Wyl$sWPF#3vy{n&_wh)@pKh4(F6Ki2&gvuM0Ote- z++FK&iM^_jYU@$vcm~VJ{3OC9+>7={+o57^Y4xYx4Y{l-qZOmDDi^aTUVozhLQp#) zg*W>QdAV`S`1X`laxVtoO_NsMibJ1%mrV)RGmF}q>E_}&qF=Sw=5ITeI>ktVnWk3cf&ku~N1+Klm#YT#@!Rz5$}Y z4|qZk?{n>{0V^6w@72(csuvJTY-o?XpIAP(26J>EoN_13maHiQuYO}dH=7ose*|{* z@`dkCBZ?n698d({i0SL6H4-OO9m~?do)BRFv+iDtXNEC0?$inBV7oVv7JmICI0_@U~U z_x~q;029uH+i8W=*B4^dGD~v3nwuk`pS^Lj8+1bfX&P{7YyJGaX#{k_j4K_-zrG8X zhROwqAs#D#rSa%iiJ;?>vD zD@8#+m%naHZhVTD)FgS9hFO}KZ2A$q^%k4%faxKyR?KdY$-YIJZFih;Y;lFqs9tVyp5#RJ`j2yqAPQDowJb zsFfy@i6(4AN=&O0cES zJ7^hb8;5_;&1mkWyqBw?eKjNPMdQwU!TSrjb2SsHb-pGV*J97fNeq9~*dt;kt@Lk) z-pkhN49rLqX2j%}VthY8yYc37B<->Z$kY6ThcI>IWc@*o)lQ;@_})v1wiK0~FU!)A z&6*?JF*shLLgsN@-n8Z3Xp*qv+3n-;hdH2Q;P2t~z7;Spn3v-kgNuhA_9G}t#XFAL zlQTRO3(bPBCCa_T=xrd!3 z4J}mQkm-EejGuk~yApA<(?iFO&HiP%%!QDWFFU>F9dY6Q`%HX;!|qw`Z$dmsP7%sf zX>WS_#*jvlb0=W4{8(-y5`hht1=9eul=MeO9zIY+l~Z9%Cgr87ZDLm$rod^iHgt0w zIiAX=^kTToI>EUz!&GVc+e4{vEFVSw_4-$%l7ZIyf+J^+rDfs@+rYOOdI~=fLrWk# z+7A)>R>I{m1KEn9Td($=fs@<7;fWv`_e`c(^M#vc1jL5*!XeuJfXjIivS>oHjU?bC z0+A|aF36>NjRO>Zv0UhwJ3ITXuhJErCOHLAReg<{3T(GsCi<7jCivO~oA?hEj>ie& zOC^8aH+5j%2rPR@CR*l0a)aa}$ZfF2T*J5?{$bc)gmIOqpF|mFGvK)$XwWt%VM{;I zn6gT~i(9U>v1C>&Ll1Rxw5OgTyd7J8DgzwNj`Ro45Dsc&BLVBt#Rj_#J5k_MG(!me ztRiq88av9aSE8V61DQu3(pHG$)P(1lTAcL-u6QP-=OAZxCHGGA*6HyLzv}=PL+ZK+ zrWbMWAY?cnijE(XJ`_5;@mKw8Z1y{)1|;3tSPJu>Pw5dyM~An$P-jX@`fB&EKXEjuGKbzbit5DAbZS85_EtSDBLK zPi|_Scryo@wrV42Pn4Pe#{{z)rJdHXI#D^p$w?A5L;;fHJpQ`Kn-5=x3aEMXDNBOB z<=RP3O!}8moN*@HUmVf?0Bk-4s6<>62Vm&yQGU?iNNC(Jw9`&|P27jKr!Q&l106kK zA|=Q*5AZm1v}aeM*fn#Plr2Z+f*@V)z#qHnvlKaLfOvd23!GSDPG)h-!9(-A;D6XR zVCo<~FK+!?dKnO9&+L0d<6SY%3VPa*mTDPt0g`;aJ=dnH#)%{bnz}F0_$w z5rmBAA^ROH7QwY5_repdQu^C-&lhH4o%g`8$^=FwvSDhT7x6XofrfU@JM~so(MigS zkINob)@;GJ?(qWB9T zF2;FWXN^#z-HqRZ93{Qu7vyCnL-2mFp)J4WUUF=SZMMPSQ^J-r*}Cw4WwJ%U5#J(6 zMBITB?S1bKUXLB}YVxrt!c5CmMonBM8l;1*)37g{J>&c7f4R4tjo~L0uAYAOo?VL` z(10`k8+Q5|VAbRCp2ngFXenYNfFmGTZi2m{JHUxN_r1aHS2f91X_=&6e{}t)>^xFR zCx9LmeVOl}533fLzMoa^{{}16PNOSH)(PnE22H=OlrR?#P@lL;?2&EKczSwQ{o+~0 zvpgstCGvj%cCTj=nUuUvai1M~49FA|7*k27o$*yewGLuAQpI5}dKP?&+v0>Fg$ACc z^oM-n>FXCpl+XxRan!z?P|=Pw|Q)Xh9j4JDtd2qKNTx(6>u z|10ovX67KkC6(~AYpLykuD{4sn#ymi%;SFO_%$CXA#Yf5=rzR7pC21IB!MZ;o#! z$PGo(3dIt8zI+k~#0{iUP9kKbYy>luRUjlFRHA876}h<}t5n0oy5Z|_k>`i+q^^W>-5^*zQTY@75|`UL61>PB5?wQ01CrJy|EhiH;Q(kIQj1a4M&| zK=<(!+a4sfgOIiwytEGDuLFp)ZmE%&_YL&W2E5ZQ++O?9$36)hxEx)dMEzEs_FAu> zz^<~$E1;{Td;ST|3)=*e{wtZR!=#r%^#M9Zic4WOk-i|*i~rIf04;pe$~d8w-N$8O zHjXrd&4B8h3aT@atoBjNDdV&UxyEcP{(E&>Q!K-z(-Ie|Z$bNhU#|Cc&M;7Rlb-P8 z{>~(TSW011;90Hut~Nw+IxFCq#5L-uV-yk* zviaY?#`8Sy`+u*itE;P_<2SzdeShxHpz=SkHy)_oB8EuJ)*q3GIteU*qh6*;_>Ul0 zijxs_PUrj0Nn^ulX8M3z{+VRrDVbfW657r9^oAx+6+)f@vhHgC_3fWJMXF)P&lUu) zJI{^I-9hLY$xm<$(k_}_{n0kQc6p+xdL2(!lPjX47Qb-WOuMCO%fy=JaS9ZzIGVH< zRVHaEVPSpX$Ro(U!>Jq={DI7kR4^ck{~aq}#Q#|rQKDq2TTU%#WnnDhfcp~!8sb_A z!?`;qq<=DBHck-_(=fYq9>aG~GYx?Lp`ETuCHV{ijV<@8(!h<&Kb;I=QHE7Ofo8AF zeY{L*oW-3{LIM3w{ipcx@mUZ!{WzndU7JyYQIi7%6nvMwfNIj4CqU@n%$5a6>R#^= z(bv$Q0kKrzR!A(24`<-02sm_1dzLtYK|<)$#o2C~dSEnc6^*_W_uHtsM%Q6w$H6px zc?C04?1a4YzkeJ)$1-w!4##-;&5VGO_JoR}%0&u%mLeJJVSY1`t#-G3&Fi|ufqy_^ zDhJCuzc+SjsaUd{9>F0va=^@c>m-~${V)N9_|hqTboWO5<>bLHzNs=&@eS9u7Ij)kO&_6>UNED3;k84y(+Hdeg+kIb>wM@4F1vX;hLHAvRwRnC3@fcE&) zSp#%?B$g~&+w$3GiKZ;ECt->iBIkC7h)=xFInSPU(EI!Nt1J36N_^;BSMD5uYWAx$ za>q2vcIgfR0DEW1`o8k~^zIiho0H#~Wmm{NBqSbkG?ZKSm0EZ<7-}t_%s2?9E!H#| zZ;rK1yYjyiSW7bePo!4}Y}+m-*Wl7g6=}RG0)YnG<3|w~U1PypL!OdGM=i z2P=nsR%Hp+Z=vP8ZwA}fCl7p-;FfYrC&^rR&QH6oD^2)y*AMG}q`#NFb5bn`GGX)zN$g9hd4H{WpQ_abjbA#U6O}H=yjg)h2gBa) zt)H?sP|muPKPmqHy6yL{UrfAK=iZ&|z5A?q)AcgTAO)U~=g?O9=K&dFMOO9#$P92T z8%X|V2Td_9us7~Yyn4o0ZW_hZt@TGl2p4+w`n^L*2CrNdabNC02*SjOklVCcLh$z5 zY;Pu#U-u3{Jh5mu%>z=^j-)^Rm=vCmXDzuVkCYia`gMLGM;NT8U7~N8Ryx9Y9!$}5 z!Yx+a?wM_VkTI8FxCt7CI{#sbB|ux5#Tfl7@8!TtKp=CB_MaU*j7!VAsC(x3cWI}{ z;(ECI)GKPaNm@7q;7t!ayx5xYEFI!X5Sgy9F1Q^e%iI*$zKn#C!S>;V)cOA56XUTwVU@0sQAz`}&b z@f%l{eG+hej%L4zhjvt1^6Au?K*Z0A_Qdm#B9Hh_i?528coFad5S;L74=z85!Od|6?W*#pRt7L-haGJ|l(>4VWU_&;*-YY74@-akW5$h3 z=EPSS3_9t+me_!cVDFg5iGq&j-9EKTvw391?I&TstwJsL6~BMC%rS4NTe0hYx)!nf zIH4J9&kZ@T++V8}r3`oA9t2foAEz=3RA(?K-fiFypqm|sPyHwrygPP)-x!ygybOF| zWE^UnJf7KlltKJkD|_Ut9eb8?d2pd!Tm?#>lKkFUfgfa{@xHTC0o5k39wR{baUoPh zZ&;A=;x6nAR39H}|9G!adDNB^ge8-GiQX*@9b5ebnk&T1bFh^E%i?@b8CaX$~vKgrJGVmw!TX+p5i!! zhi<~^WgQw;qPaz?le-Xlqtp>$f$V0U4`=D5O2<9tko6TdTJ0xNeu$qUH}KtAZUHkZ zHZ`Er8E9qzv8gvz##=j!~zUobC* z3FT(M_7}VVy|2MAKk2HSc;T#~vHe<3EO9IW!AlO_0QFO4tPpz+Wd||Lb?9nb+1Byf z>bFZU7q@MVVsNgQfKEF2Cc1^t2fQWf_^-euI&)$e42!%UcZ}@xYs-+ja5pIkmR0`J zOTr857>ETbz8$kGdi`pmykCvqS0{?z${5VV^u(Zu-YN{`53=1}Rb*ulOtGfw5_2C* zYp+N|gswlbFS8U)EvB3s^@u~p60kdHG9LLl3ZJDXa$yzJ#xT=u7$1{e!Uis}94o+I zjE#bS&#iYxSX}NsG-FB%tfQZ;sT;sqgl^!Qc4c-0-5E#$VY-JzvxrxKA7sH~hm?8hS6jsTu_yJOOG zz5t4}MAD}te=xz-uoip_p4<{Y`vFb@x7t(U3UVh@?AH4q?m$G{)zQ*jrSWIJ`&16@ zWQ1ebXg|Qp84q*C`s#7CPElNX(!Tw&l?La3pukcRB}k|(rFKks{+Ck?1%+z_9&Kh# zM@Oy`g5C&@zXARn=ZXX z2Qaava#dEhZfuAiFV0_XGw$@eO@>U^LOKy@RQJGkuf)%`dPQP)qOJcJE~2|!W_mNV zwoR||_j%7273R+z&nP9{>e(swVdEs+>SgFSI-Q3(e&o~jQboGPq=YnuN4MADeIM3w zMM(78fUOO4g7z=~y%F?c?ZxDt+t9w0_8$+BAseuiKX~@?(nwkP$Y>1N$6c7bu6yg& z0B5BaiF8ga?)(XYs=w|w)_T5R;S^Vmk$#p^MjuceQ)J@7v0D-|N({vW^}$ufgXkuqk`^L1yU6{3VA0l`%7<<7HI ztFRSKp1Q-=h}&Vg)F3F0YyL*wjs5lWTsBA*s$S|orTb& z_%PA=ab}`zRB`c3D4t#>l)j_WuG*JgxiYq&&%EAIOp_|`pz+=N0fA-cC~a@YLhg-8 znk~`M{+dgoGMnqjXKpaF~>Tow400P=p2jf>a1qmH0!-jq~DgU-d%eR!1zy{kSb?K>BS$2mE3pHV`s{w zW(2DWtBhLDVb@?u-|jL{e)6bw6+veDH_>kTyU+l+-kfox;2OI!;d>UiyoxBF){Ep6 z=+}k&*-}nG1&5slDK0Pqznm#TT@78rDgUW;0l8Xa0ftOxNOwa{(W*Cc!`we@`R|;VS4X06N6HC zl{qz7n#pA+f>Y34uO(>S6v^rEdMRjVP~qG$m)lHUH^%<*FmzL9QRJ81YD!&7)y}{5 z{FYKiD~98Uj`8#zqxHjz{u20Va?kr_btG@ieraov3x9+FMLaUdDA%y`Y$&&h<#L%! zDvUiL{ZGlZM!LU@pk=v#NSWGHDLwII2Efvn$e~14GU8&dyB=V;V+EG-Kk088Lq1*l z=|}0U&Y!=W*1FZfH5|yO`d0k>W{i$p(JkGFoz38EG~oOs$dpL7VL70@zpethfJ}z0 z%trn8EpwnGu=g?rqAm3evVXFXyb?{(Nw+PNt^vAAf^P+EcJo{Fqav2UFiAPxtl6u?eyUA9-`p0AE~L+Ssxl4;l&)uf1hi}cZPUq_^%g$T38y_ zh{~Dd{;ZC)Ga~{Mo-S$Y@rfNkdpB>;1cHHjV!jf2&Pf>6Tx;fm7G9M6H4?FtU#h!k|xixkb;ahQw?$k}$E?tW5Uj)MRdba#-8?_JEWt<~fPi@eVoCuut#J@L^ zOUdp1CiNoZRTx0Utk%AMc#H_oc?5}H@-Uv-J%0#F+YeQiu?w@Swg@NPl+5TI;A#ji zw({Nd#|NFk-$8x9`L${+5++{GmK=jDM}N}-+lXx$QK^T$9YSeo3lhEYw(L4Kw~MEq zDP(XfzI6Y4HxJ>kjP2I`tJHi(8`}>9V)3?#3zX7AHeN8(~mA3rjWI=ogCzz3y>mHwu0vjG2LLkM$Yn;1#& zs8u$q9zMz7rIRe$mRkJ$CsZf91Yr@fG06Une(?>)>s`&!`y1KkZ^LFg3EX!zb;=Yg z;}m{KjEpCaau$zCmfivG*JA8>xHl7E`BcQV0DGO@Cqbz+&2?+nU>Q;#cNke5vfgLZ zOMEsMN=y~)-|&<+y6htbj-De5f)iXi$y4Gy;kj%9#P+1+5`Bu2n0Xs|^=n16gXJYb zCR8jDXGk}mlcg(8Txjxd^2;6#aMNkmC6G^L8&2jI2U9qP-2+g*b)zcC}Qb*sJI zK9M>CQiV=tkVy>g#edqX`U1M7mVhsW>p;OUW3n*iKEf>r=BXF=QBncUyr!f#!kL;FgY-Xo@x z!ZG@0Q*Wvbo*~Zx$L}Tu2g4M`dcL0`nYe#$)^UO}pFk=CrIlI>*x!7&w#2-Ce`Ggq z$iv&IXST^lJ*AAI8n##G zE;W%{**Mz^QO&18*k=2Z(vRtEul))@6?Qit*(SvapqfuOPdtvA-|Eafvh-xizK8A&Vt>N-nE%wl+T0QOEBH)60bU5 z|3~)_%Zx+fxxw3Cb)xLC$G4$}*jdj|oPM$27Pt%(E{y63J#w7{%d14E+9fE3ejwKY zGDYcK`<>nIeuyK%f+#iV3wYJznWrG@8rXLtJ5(j0v0Z~ZG<*dtCOgG1fw>7}lu$|- z2-ra~mM4a_`kA|3`XE5zot>WqeV0k*6J7A*E((&Y<5zRv`X&Hv=_~q)b)UXqUJBf% zCKZ-T7_$g`lX%?MNC+T%pOeH7uvrEXW>)?ZX+uH%vl|&f**~JQh*Wc)A$(yL7Q9(q z0$6JLY`WQghmF)EM{MYx))_TCTJG9F4taqpB0~e5^C#Mnc-6n~m#`YSazn(^t|hJ( zI#VFF971lm;4zVDL=grtM0-P(4i&Rc9yr(TdM0?mONF!n-%=K4>g@{_fmQsGVwu>b zCj*yC)C^G(NXt}Ln^KhwVE9mXC0yjKY|H|nF)6U&hE;)^F|zxHC~Cm^WOfOUcJ{=PIuBmR^gq1#@y*YVSDC%=I9Qb*8p8**A~b*yoL_C$ zOnY5zEkveN=4yd1?*U|j6lJfJNzbq;$>>e6nnVJuqZ>S@z~P#= zzO9ozsS&uHjd5BV>!4MXg&kCW#bhX~unM!q#1Oz7IpqX95QC`30`&{BYO(+y?m?XF z>-U=@&~{FyWHM(?#;m|4zXj8{z?)UQ+S?%iKFnLx58YQ_+R$^6p|Ty=T_2$!@oX=f zhrQ>SI`aoTZ@KJ!!7OLg_v^V%fM4&h0(EDzr(rjI`bvZj$yIXQ&tu$=t*`^Sl@!6g zC7a{7KTA7?Yl1r#wmAZSs(>mA!@BGK5#K&N!N)^Oa z9Z}mxI++d}u3UPe=T4-nFkV&LHkHMJRi`p?Wo+E~6Oh}a>$D}DA}CKxwLWA|J(o73 zEe@#|yia>AA*?q=_#(SXSfM)|TNEk5rw+CKE~1R~Q0xT8_7u*gbkGPkKc8S!uJV}Y z!c;F&X45FM97Jt}cO6I${WZnq6HjAFx($sDz$FUEjS$G=zw%@SWU5BQt0}sMoPREr z?rZd2TAgpXkKPuMb?*R8eN`eW!YJaLFCcC)oeAcdypn|1E1N}ROaihHr7lcYrTsa$ z#oI^>dH*FnGP{Sq2mKZU$1+`<9x~}uCoEk&21CHcg_d>JThtP_4E)~HZ6r|SdstmO zcx2b9vKVyzJa+@GZ@unzJDADOt&R z|3|67!i?6d)?rWd}{G1oXN2g9jx>PCMZKEWu0mNlT zi{|rQm`Il-Sna^d)_N^*f<4^Gd3dL0Q`wBDp^N=hrV%|OACDsXhmyaGe9-bcN>k9_ z(~)6OmH^=a4S&oNPsy##f!5@exm&eRM@@3(;1{5C{_`S0WP5%jj(9rA-$HttaNf^2 z8Q^ZmNjpwc$7MYV?#HlwgEKESVc;-sZ#>N7Z+87>6&4j51rke0a@`~9=;{GG2|VoQ zAOISPSl@g9T>t+1N5(@Z+;zdiFhkRc5?M1MA|#CqKU*GnC_G(+A_mD8=gL+@P25Tq zGw%V~bseS`z^BOXOkw4}*oQ2hfb$ws8K;zedSU{zk2nIpej6UUu|XJCrL4sh)IJY- zhNT=4e@1b0?pZFo97ASCE;K75(&9%|ef^IM%P(Dg5ak-@pBL4Mmzw08;azJycHam- z>A~c^$6xURv6XpcCU#^vDomqKZPtZ=Vn9EpL;P=`XKp$`*+gn0q?77BteXqduZJtA zv+~68&ocwKP$2f`t0gEo`Z{LRKd$$lRtCjOB8ithYY#xHt%TLb?e%{tUJ#}TiVTly zGI>7CO*`#5t#?%P{Jb6!5wAx^7e2g2HD{4ho1fv${=n6FIfwROgH z>?1`t$lkrF)2jM1TL5j>prn&8!kMDtGM$v^aNmSkd%+^ z3KgQs@|f*8Th#NwJ09_4dg#Be7Hvb0*etakBG`A9VqVMZn&gN&$X_ja=$QqA`cOi0 z(4ags=q14QQKgx+XZS$h2{!`4Y@J}pPyGG72%%SBFdXEbNkHO?Jko$@?WA}q8(H=q zK8GFmB_zuK0ujBfdcQOr;JT9sUQg<%58VG4EW_dqNh#4jol5~%Wt5UwA;UQ6>&|+* zLD_+93D|yZpFAJ__vG0^Lr8$xRshc@@41qE)GR0`webSWT9oKh zF8KeSpi|H_Hcf|pNx>sM`ZcYj;bu|K@R1-=RZozWT&C3cv;Kjb0hhoZZk;Q?XqSH# zau#y1N7)g(`$S3(t)FM@i6J-ot3U-AJN`<22W znZQ{Ir20JXSg-2-`OH@6s5%IBtMc8wRdzrXg`^b1`m68bQL~~R(r3RJCci(zA!Pp8 zoQ?lVh128C2p{fCu6F`KU6ff}|C5)0ZBmdrrHDevc)-Bjt1OcB70hn`fQ3nj^5P5S zi-`~{Ks(mIR<534`(VTH40_{#jv;Ez1CdM6WjwXHU+|~#kL5%r?aM=+c<5iRK}M?e z{S(g?>fZfOd~y~Z*x&yFNN=x9G)W15jU$ewCJ4d`=FJUQ{iz1{0AjKLGUB}QQ+fX?#y`u3o{{?Xqv1qEx=8+86cou+N>pcXlg5DLy= z^HN7c-c)B04bzx9Jv=pou+jNVY>iJl@uc@TjrNDGSiLYUkC+vU z;nFfGw5kuK<^!&RUz9bRU+gB$;JDrgS{^ViqmX6i$?CsLegUk0faz_1;~eL0Z>kqp zfF-}l1#zI`frfrT6uJhlzbR(iNN6sC5J+H&1nmj-75*({J_sB%u`g7Z}@(K~C$JrAx8^Bb~G{wOb1}aDI|A`7V6eVT;yd1VU4VFjW#;+ie4&myD70wa?6dB%(rG)eHveuK`Zy^ zl`Dm%@8)N)uW!n4J`VkSu3sr^Yr!-Ww#|qDQ3YDG=1688KO|WS+WuzS{wz1n(~u|+ zt^R1ZGq~kXvK!>H4^|vT;>cL9fZ09R!9wc!w>RL{zIa~m1EO&i_`&T5q+>Z|@^CO% z{B>_aBmKW0IPddDKl0z#QR4t6<;{61*Sj2Kw@fVX?gm9YB+{mi2v{S9Lll7^X$ZF9 zE^Y!WUJPnk9XEsEwVGuJ;=5`*9i@156VjNjHl@P{rz%|nPDH(iwbJ$yppwc#t{f{s zbXZ~n{L<`cyr5-`*YYH65VlKut)CQR4zFmZ~h(y8}U2Osx)sN=v62WoBMcH5O^Ej8MaL^+n6R0Q=y5$U;~Z`xnn zt0PR=e(bK8u??T*y5Fxz}fRvMg;2D?xXQU%ZB^N8=*!JP=wSdik zhW#&1HP8W{24!oW65K3!{gu}yvLbg=>ov@r{nzZlP-#c}sa|N*Nw7tdxJ`?IA?fi3 zp*%Wv6j3BzcK?aavpa`fjQ(rSLXNM>w;!)G)q(Ym5Lv4Qs>P0w(;l!$RTOwislqYv z>V%h2$BSR?ZZtR$(h-__gG*EX0H`CPQqCV9J!vsE#HGM)Xpk4Q{ZT3oQlCe@A6pg& zKO?;yA5eV0Psuu@0*AmdVRm}_9y}cs4P)ANNX5e1qunxtR${Z(HIlUa`AV(d67gyG zP?W4#FU9;+8N~T~0;flW`3k$$ZdJwH5-LpMUUej8=~ zJ_Y~&tdWZSNddMCe?~v#keEC+Wypr99+0R0T1#voiG!TvZ8V|{LJY&GelF|{7Wf8P z;I+YhZ~+RV_?5oK?(ZWT3EKpH%ZnuL&Oi5mu2{OYah@gG&vluS`}CV{TGR}+m$VMO!4Hc3+LThwln){xt+S~#1xtPtCT3{!E9^{Nx$zppa?L;7QzQR{X zaGX7M0$=yxVXf^0DX%v=hoGAe08Nk&FjCH42SNCl_yXL(TydhBJi4*a14UqaHa^NT zD}MV;{}w30o4$r7z8?e2XO?)N??pMkFsq~(^mpz*CHsZ11Ul5BMqw25zFodt;dlvFwCV2N^~rpgeiq%1Wb3JW%^!C&F#1KRRh| z;XZbzrQ-zDEWEeDjenY)e}4fkQ*Jysc28hF-~n-uL0e2P^O9O3&_&@*UYnQluaM>K zE7v{*X|$?4ARpjz2+ZhVfw|A?_Qi7V2d(B=>=|f=zlnZgd9n@q68q~n05_yam%p_J z<#yvO^B!67GSBdAv4BW4??_hWJ0d&yZ5vv}w4>cAQk1nnLC{e}m2d-8>Rk*>hTG4x z(=@oA>rR6s3*xJEcZ=M{?C{yUkF}0$=CF|RtaPdRG66yfedywcx9^!QOFxZ@xfSg# zMe%tL&W3VM!h9m_)DpP{*Gl$IY?3v(#U3JIU&RrOc!SV?Sl`P)pEu-?K;_~pZ6HLVnWn# zh%GOi&HAFd&1mfMu||DaYa$AyONKLBYS>V=eTeylQt;$iCfUxV!pQFjl$Si3O}BtC z5NM$rkXkG{fe2oK-?iGSikM>}XWZBHP6f2>7nWMT!&xP{M;rIF#MMs$N%al{)vz&c z5Z+sc8{%;i5qu=p`VI{F1a z)3Lps8Tpb^t-@rx0WKDkCKp1(Qpk83-W)u)-`x7?7wzqb)cLjsuP5HeKJ zQvf1)4)v{=t~xFM+);XuN{ZGvw04h4{5Ivfp#}c)*7;t9bIVd^j_YRaY3LOL*neYx zcYNQ&+jYN*U-I$1$W{(qzXhoteaz~Dgn`%u*5CL(mKy!4lV)BW>fnU0ui4d zPjcs*XLxaZNe3g`eQH~A>MOK)&k4fox=*VJq10!$M-yn4dBqiiwFN5jM`maJS`cXjYVDAT=5NVxl;~ST!+QSOU486L~ ztTW!4eOx9c`EUf_X@VQ%O2%A6Uf?`?%5T7*g4JVeZ<0#YzS;Owtcm+)tI7k}@GnvZ z-`3#OrhJdPnyrxMnrFBbzX9t?1BO)MJc5JOUM?e_7QO}e0%<8@vPxEMDy-o{$ue#E zB+@udG4HDx7;ByleJ0+>lHdkDpXB3mH3tJDW-b*D8kwFCeABUe1Cad<0Gjf31b{?NW#^@vm!!;LQ;^;Wv)Qr`JrR;G-<2!;sfpc!hO z)u>`2F$X`h@sK{I$Xm>;{^)$NS#n+tgbw}x{oqkYMb~M%eH;MqRUAF1zgLK$;MV0r zzcNA63x}4exKqMj<|w|`$_auxxSk{Zg2(}wBp-DUp8>yQ`H_>)^Y@^q&VuMTUsJW0 z6~vPdeBj}!S_Q}P#>*2xR%3C1Dr9CK`W;wpQLCkXj!WwrKszrh9eS?MZF>p0ObmhS z+6dA9%i{=1(gv1CsZg2|UiM#4ETb~(xXyV~dqIHw48zM7DV-I?IldOB2QY?Ax13Mw znFkeYB%h|lkg%9NrL*ITIZud3WZ4@qM=_$Vpzm25mK+`4QEukis7z(sc{{2tgtX5# zUsj4e#kC>MtDX|GgHB6vhYQljm^@6KK-td;3Z6k-;KP9qe0k6 z<*Wz))~x?LpPX2KiEW^^QrEawOp;ry!D-AS;cQ0sOw9MVv!E-XLS*(+#QQ!=^MHh< ztbgXN>YOOI{k|himgKi~sY)V$?M!-)67y@jI#q?9P4)G6A7>vfURJ&)u065r+!8Nx z`|XE@JG(;#Nd(D(h)cDQ^@E1=yN5U+6 zIZ80ll=R%|D-#vqvrE+T?G=y(yGyv0#{@jKe2LxPTU5SHAK&OI;Xf?2!XW0@C|m^SDG(fF1^#1b_@8I7 zRf510l`d@84r=`mFHIA0=m<)thX`c<>`mRu8M|B{3c=2d#Uy)3S}(tr+(ValXpSy@ z4BK@C)}2JMIXL}EUsIbxzRIkbw@X>`pyJPbOzz%W$^%i+GtJeNL&HdhnXlYk zrJJFQ&hjd!27jzD08YK&I&h*vSUM{!^ z_bQ!=%vWZis9B>_y_P3_8k||~vNWY4iwGUnJ2w}EzeAH(tO@OX?lA5w{+8?yEfNR5 z^Edj4?Vyaaj5Rynk^Xa76A}h@rjLY{#nca z<5fjwU>;nGCVoTiiRU*Uk`tb|M-N4r4EKb?zdBS?Aiu5cR?EDNY#30#yb~tiC?|d8 zDvy=k`-~yHWNFb9!gtYBN~dM~V?oS;DYZ38A<7s=htpNBVA_Snui6(09bfsUmEQvw z1U8KW+8>6F-JAU4&1_4o4H*E8PdBn*+RVN(0Uc{nRpa+m=Fe%~ND9%Cu7mdOhdrMI zHgX5^c_#_b?XOFjo&tYpdpIT=@CqXi$BlB8S^C{lb&dYHq!X#m<}z+19-M0$&sJgngh&_O^Epc2R=`@q#w(V7!O;paWSba zN0iLo{t0uFeeGtTLI&-wY+k_sc@QO$MWNF$VsQv=iQDHw{7~UZ{94;DEdTZaeYVAl zmoL;r2$P=8B|$yqzOl2m+i-|}c($sAjG*9WczjdmjI)0fEX-+WQDON%q{BH+x z1N84TI}cC8HvElwIQ_S_6I$nl2zMD4V0~_>E#z11v=@E%GSl5Rrjx)EaQZ3K!WeMG zebCVOVyFmN6Uf{pmF*nH-FLnWD{{2jXVam}*PHeXs=vGTipGT&_cf5#g!#bYCGA+Q znAyy9Ta12~cbD=031CEnhUkN9>uzv?svBMGXz!sPDML?dZ+uaL1#Ua}#p>5NMnkYn@LIs1^Hi0JKn82~cA|N8uK!|lscl;{)zvA9Oo+7B-nU;c1dpJLsR`|D zFR12?e_Ip&=d1;dJX_SRz9WXz$eiC7g+t3htY*%<#SRY{|56OQJOB6DU#n7Cri5Qy zR~XcnQpEXv7Dp?6j45x+5=TYEr@V068Q9fyx|*utK<>hZ~~b1!~?465}>Ps^(x zAQNf%_O;VSn2&*UBx3PxeR2s9pRxY6y>SFjQ$XwkBX&6EzO&X-8@$(*VTjeZ>M8to z+|rCfMOZ_ln;Lq}O16v9YlTbkQ=kU7-Ci092#=lC)qLEKXo4OEFY!oPHw3z`xx`l& znRHhAYwimK4I454v&wSx$7+`qKF9gyssA~Ec`iapt0n`e@QpW46D2s@X3zqEoi_*z zcX=ng9Ow>_ufH8qQ%zq4Usz$rjiBvM-L=rXf$BW=_(h4F`{@|XHpwUa+JlCsmu@o2 zdMzPhW+10OKBQkl`1x4~V%G+$$|Kc7FX!AzeZL$BjpXs>?G(~<>L~&f z#n3f3q@G|KZXKr|xqqXF?fL+G6%Ve5S(tb_sTAd7z+ni;vb{7^^TS}WMN{Vjw_dPd z_tZeb=|>cZx(doqs4e;6TOIQ4Gw2gGk)%wr8=q!msOkr6Z^5EuCH&m%5&83;6PUs{ zV)>|zyT&7;d*g=WJ;2g4F=+o!!}IUqqk9>}g~nm} zF}H~Qo8NP%lCS1N2iXB}e5XbrK*cR9h`rpj^mw47%W!d4x{dhx@y-Y#5G&JW1_@p$ ztZVn2>uVa`*>D~ztRFtt1C|T6;sSGwm-m%~9mGk6Kdl()#3L}&%X`SJS#6ZInzJXP z{NVJS)OF}eP5Az1`hMF@%G_FFBrUc-$6eXU22Wd25H5YqK9Zz@< zOiSSu1NVan*&k~C4diyi9zkAHM)GF|I<+q&iHFaEVkVCHZM=muRUd5g0+aZc_Z_~E z9V}1o+uP?>addye$jjQosBv;fVLEo@B+-=akx)iqk*;Q|(_EqC){U*#TdnV1XPQ3w zww!`jR_ZE+-o0}VJC^QrI_%bbc;WE<&iC$`?(I+8Z}xK_c;MI=~vj>h(3kcJ&SjtIX}U zXJus!xHA59BsY+hTqPK2;_9Bg9!3!DEQQesk#5<3X=&e` z4a?oH#-6!K3mB)0iF-~Jm(s_e;$Ekil=k1#3ZuL!Njn?3g@1FFRn$@b?CZ?GOSr$w zvR?pXqQ4++Dlp#52P_ZYvdNkWbEy{9UBb`)QeO`w9(d5r^Fj^3SR=pLrt{(;Vmi+H z_(Qu>VLegs_$l`)M%fr-YT4juS$iFa;W*9ST-W_At}@iC=u7fdqEKp(mp_j&hv>-! z*M|(A+KY!bdP{d-84B`Gm}ZrO|E(^24Ut(+*t?e4$+z>5*b?xg1&9XrD$fHV#N01r$R>muZIMNOgZM z`~Uz-0-G%|ru?mXII)>l)>&^O-;GyttNeylLv3HJ$eDfml>dD#4E+6vi3~IN#aK~A zuse3f)p6ol`udcS$N2JTRG_sO|ERIWL+a=-TNtyS1?i`e>LI=+K&iQZ`=jd`Kvb#F z8S0!_i#1ocE)RQ`g;&F9hTL=2+2x=^nqQiD`?j=V6`T$(APJ}Z=JqxalvhmSDq90} z3n&#Aj$-Z(A=@!H7~iDR#n>Mt@caZCG4Hw)XS#;2Z(XIPE1mm{E%9-7M^a^(Ke?S+ z21iB{`h&|FS0-MmyZ?l`SJ@1@z$+bsK>I~kC1{=|tRO8sH@yNhr4eoQjkLk*gv?xX zv_>Xq3dClc%s~9X0(8*Nf~H8>9|bt&oM*kiqJ@>E{v?#nu^RpsrE3AEUZ-@pP2_tf+NBP_Dx% z`3aNUAR;HvjfbQv_TnS4hv8+m^*=Dbaom2w?fr{7J~kYC&c;=Jx&v9PzZVU}8Ella zviSZ-FEtV7U4zh*1{=&m%ayFjt6Hi)B(2RguNWUr$$U)v0ex*aATwkhrM+9SG@}71O7ydXrQ5(hzp}wA)jDHM zIHpm2DMK}q$#V<9=OuvOL~h@jb}gqRIGNh%(E8OYvb=$#wcv%H+pDjZJCn2%8{zywP))cY_4Llo^_0nx!~it;{Me_vSZ@GQT$t!k@~BRDyyD+ZM^4Fgbm0acC}{>kv0=f9TgtW3#5Elc;S3S!!c{t zrZZ;3Df$C14Z)H+<0JYUswLDJ%)eoBtmIg76fxfZRmz!AV03Ww^2CK%n!z)Sxl=n+ zz8us8Rh%e%(zGTwMBhcAG&-$YX+Cxh92S2LLje+WgWh5~c${;tor!aMp- zH!*i^fylD)%9>_;x{4(?%?3RQ?GAur#vlw^9#(-8q>uAa4J4tX%3T{7(GtR&I?_BNrl5=sz&UGx5YYgqNw4RUss zS1#k9y4O9H>w0!R}ndC!cht0|CLZo}~fL+G+?HjxdbZkM2MhX9H3x0Jno#+&w;63x?h|8s?9)79M zbzvh@WcfiC(Q!ny!6qoCL){gf|7ArKQ|=Spo5PL)J!5soRICT+nrV#1iZvuDZ{$NY zD7@!fRQH+q%sF)_%d;8A`Ze0fuS4SXEsA8t_G=nLQE>S z(|!6LD<0x-jWz!?8kNIGLHZiV2Nf!QfJGvlM@?78fc{el>)-r@qwU=rvW>St_!ZSI zS4O;)Rej8QV;%fq!cC`WPZWFuy^X~Uz;7*XUmI-#qtGp*c`)Yd}iSehW56SQ@<#!id>nE?p1$?q8iIBk|G(sdS z0f_55_q_{(=k$JPPVkqt&F9)qAveWkv^RH#%<4uzbjKSq4eQ|q6MXkw>7gaUpkEE> zp+pV;%k%O-@bGH~+6)W#NrTIOks-?0e7cGL1y#bRgCP}8l#Eyn&w2aoO$y1YtFRVA zyxTZ&+174@zw*@8juD8O7+7;3F)lUEX>BB)*#HiY*&qY8BV&nEgo3fTT&}pP4_!V0 zFXG>b-wc~j;Wg<9(%97}c;o7I(=ySPsC@ZgBSyookCHnXU`BiX==zhBg_b9+%Ypmo zEdB$1caBE!pH%$XKfK>kG=bK=%5npKfutD78 z(xi2usS5EpJpK)v?f<=C(EH?2;C>h`6QsHc*>Gx3!0#~c zptYO~r+YJP)0}#iw7!Clfin85W=1itZh1R&nC_Vi3Gsk?Gr0^Xn!zcHuwE!(R2tw0sN%}_{k(0g9)`S=yT(yJgEz^!5tE=1&&(@u^9jJ z19yZn$DbzOF!4uvBrp&vU#Y;hdX6{$Ri6gN6u43%zIlmazOTTkMO=R4EMXchu)NE( z2yC%uuC>w^{O3brWpOq^uAfGvyo#pBH{v}E(Qe8ab1}>5$Aa`LV&*4fXQJ5B@*KF* z14z**L4DMB@Lqwyrmdd~d>GmE$QYHs*6Q%ClWJh4FY|IayZ4{hHdYZm@%dH8>;F;N zSWtuP{u=jx#*g|_KR@UUDk5*I-+OpNQ|nXT^%m@we5v` z_#s$Q8avJO)?x@E7!=g;O;7Z_|6Y9L_PZm`Vwd;%0wug3`Gp#vP1y7z)+NB0+$$iF z{_mrxc7`Cfj6QHsq~N)I{Lw67j_4Nm9G_&)6C|FnIHCVv-c!?lL3T@8rU=i z4$cpk%z!C5FW-`roDhB7uWtw&iWvoJH6Z#~0Zv+1$6kAc-pp`^k#(+2WU| zI&ka++rMR~G{6SDspO3Wi%~5z$`R2Q|Gg;KO?1kd!{!3j$XyZb@PZ(%-0#y;HH8RL zM5SCu+eEUdJ@fr7oU#uX1z0P2%6QFaQPk^qYoH+6kRbgOXqBHq%bLXa_=BB^2jb>% zgtBKu`5t$9u_7G93%ny4CID)?qIold=5;EWxX5#OanSbOUrR<#)hbzVUqYNtLM7e2 z0PE!pHIA#M|M%U({lF}-(H7LN+&=tRyEeC7^(Bo`#zmCRd&!|RJvH){AjO~4u6yV? z!E+_uDvDG&v9#_)48-V~cLG(f90xThycA`LzCQi?-6HqD2>B$mw>9HvaVcau18#5O z?ZYuHX7GcpOhX`sXA&G}s2R%bxYA-A)ZhMzsD#l;7d3lrS!F`_kV{XJVL3xGwwkTW zL0qJ(kKtDGfH%ADNx@ms<56{=3CCEVcYz08otUh)7^W$8joh%`w=5UtJDoxI91%9M zvsS4d?}z>Nce%l1(r?e_CY>^)RLZNQkayQ_p(XA9p@9Zt-(VU^CWb}iUrM-)S)xN( zbm6-W{dn;{ZEt*NC^idY@Sa3^uNzSjA%1tM(qN!#OyQN4t+{}ZzV9CJOG2z#fVUDX zpUd~6P(h&i#o18o7h;2UAE6so#qumyCWg~5g%|c>UO;&SiGT?(=nzE^fUkYb5y&Tc z9jOfK#J{JF+ChTpSBcJQ#F4m6O*UN-6wl-+A70qp&8u@`UWEX8JQh*7^>4#B05^_9 zGiAJlrvhd!Z9G`k$$%=WPy=CIK|}opv`A&nW76IpZ7yvWpZ~f|1_1z+I_ereVq3C| zyVEo=GY=2ZPsDa5E5YLMTQOy`i2k>(vHxM%da#l)bD1f4nR6*iP~+54UD%XPJ~h0N zzo@F6-}{SG8nKE8N`W-jq(%;6#1`R#lxKbVAJp-PVFbLU7C<9}g5e2$3qbW2m^1Dc zJ*jAI12ICBAntMQuaU$5fP$nD-|}!<`LP6KngZMB8XkVxo{T?oEk|Ti8;+azWYc9M z@NHj&wr}TSI!M^1G}lL>r`*%;_5FMD=~|+MKJ{`2ez@uAeiQ0m-JzJsmo6-evRRr( zBO471&M5^0-4*;A7J%s3{9zq5y1f-{l3VNWIs^aDnPLNsJB^a}I^EGIfG`U_u;*^W z>uMW$K&XMd0dC$ln8AriPX@tKvI@(wE?`oNCt^6O!HHW|5&Zx{;IpMx=sp4^7b&e{!4>~A~r6X({oo1 zFS{?6PBh9I9tGHzN&`0;R<&u@o9VjL+hEvmshA+zi4~P0`-a&qe_;PE?>B^oB?P3j zfaRPRC#sao%6`%<#p3Gf>-4|pCiW7A6=Cj2nR*g_H*2a-i5&x1$y-klOuJTRjudX{ zQf^?GtqrbEGWLv5wF1&QF`xMxv{ix)MdFw{cvkY4~ zahvW`OZT$>$JTp5WBLF8;~B+G_9`Kp?2)~0LW+!JuMj1&S0&sZ4-l^Cs za(1~A$SU7V9R-6HU_K~|VS3{$QnQN5F78ml zt$aB;iEXVLm;SiA{6_A7Tu>4M&G|zTdCwAd>I4pX`Zp1DGI?!3Gomd(Pd>;o8^X4g zZT|jdML|d`RW;?XKxt_pJXJnhs0kFwPLbA4MO0Zi@{zkA1d`J1u2t>*@Tv|*on2TB zb`_G*TpUyJ&8Sf6CM}=ePq{YW(!~?3$l1{!g>5F2N(9;6H3?pt{feN%5)h1T_^vT%69{#yl` z1}zEAZVl1Nh?7c#=2>!@ix3*uon=&ZP!^&SVU+R&rQ4&%+Cw-dtM4wywe~GDPkYYx>%x3$F z^pv;P$LsH^;afq6Id=G?u`}O(Xqg0uF0a-)93H@U@jW~F>t?}6k}4x8y*|MTcziBB zU~kv%efkU(&ZfTiw3EwHuH#qqy~qo&=Z3A#A&K$#zL*o)GFin5@xKKBD7`|Pnz+$} z28$3U#y2l$dcgRXsU)=fH&HS3ysiHo)^cv%Bqdu=$8-fuwe8R$FOo; zUhVom9r_awB@MS9IN-qqN}|M7s0*f>i34)CHkkEE51xY^HLo7&nv|%6nP;L$`b-}O zr3kY|`=JX|!k^q^F@0bUUiWJiO5Loh0yANM-*>RwPJ{c`rF3@Y891k1p02gpN$0Hk zE!`CHDpOO93hk9D@SpsrXr=q{(ubGrIV5S3l(E`?jj zYtFZC+u+4Rd7QfftaJSn7C@+SnaZ+|l4a1=XiYDd0J+#`8hoznIGJY|*5PPw6zxyx zJ8=eviIO)=0jfqqSwY;ZLDfE{|Li}d&0`%_F=OA2&pC%-fyq*L6nYup$`9d?jvbi$ zBSXY&e#6xmq(p#4zq5V)MG9*D?qSk+Aqzg?sUFtXlE}D`b%&6>HYg3oJmBV*))2+) zIQXRmbYcKyH?zy{(B!ZnD-0cotU)L^^=>~WF!tiZCsVF&ts7;|E@EG}-lbds(rQvQ zKY$W`fz>YzBpGcI{9uGX` zJJ<$j)zVc3lZls04}16&s_ID@T2nPiVvZ~&T@CkjZ=8lG=Bci1iWV9u5aJSj-=|%P z)c#93Wo5%Gw6n`-=!9z5tFn$08h=<-8h#puGJ3^wQ?WTAmMro&V|!e7@F!yLI4 zB78F<=h!*8T5}yO&I@+oXaG|_xoyG+RI>X-yN$1>8P#>dr?clEHw^i{Inhb4RC`kv;%%B9szFL+-}FDD!KkWKeF{d`FRRe;2@iyWUrw9nDdXX5 zeN5T>kA3x$5sdZ#6;Br;%*nnS96;|t;AW9!4CQM$4ROd(ihx0W|E|}Pp_Gb_Uj@3eGeX9fR`Q zLSre0U_$U--@eU^%bxiP%L8M_oSKc+&N=d`4R&f;&1%}kv8XN!UN`eBXg>;y4M6^J zaDlA130JmwZ+qlWjmHNnw$UZtx|+NL#XAvxP?xldFQ^&RBaRh7&}ID1x8X^FB@SKp z=c$s6VkmCD*>-KUm;H;d=UCm@$$aV6R7M zV5?~tp#sCd^Bk1D){i4g5}z|=>m-Qaop6)U7M|PC6Z}`-MNmci#uuP^T-~(;b&B^M zwChYJYhiI5dhJP4h?fLnBC>r5cd2n`7Iv2hjw|j#t~?l;9xtf-iBHgp;!RG9sQ9+0 zDu@z3CQNmGZUA~6sQE(C(pu{{6Gl_s9Gf+Mt(sc<)ud z4hxk9!**anyw3ma4S_(eLyIQ%Lnw-}61EI5$@w)~)ILA>@izAB>tp+M|24oo@;jV^ zV>yY9wF6oS{lJ+3Nq0jrYq|^Xmmct)g)vPcI9MUfu(pecvv2#Jy_#!yICx=`+yp_Z z0@0EJ$0XvoD0wYNDbBX?;Z^}INo1>zq6`p7G^ZCu&fc95FjD*T?9qf+Lb3PaV)E$m zQ1lz(D@$t@H|(P?=fcA!+fqC0JO4GWA3POIUpK$2NdIO(;>mzKm?ja7iZvF3-!57a zH_B~-apj`bMJ`$~qdaP334VM@T_@F&HLF+wuK3p z^)?R_@al`stB)cT&;Bn6EcK_{sQvbk18Jl>rU+NUgshKH<^9)g2n{o&5D)xWA3(w^ z4H9f2y``edcOdKT5I`;pTJ1V|p&VN(%S;j6t&D5l-k0{@@*L%KPeJp5(p=G#*Nc=< z+p8s2H#$4J>z2{C zz1&W_OL9DQ!ohIpx)!TZZdz7oQMNk>8!F_e^KO7d1#bfIpF8iS(b7X8L(r{5s3;)6 zeDC;8NcUp)!Ma1f!YyV$SN6L| zP!}AOAvezsfoDH3+OptY*(0w6D;Q98Vt1l(v)RKP&voKi$RoMf@FK7THCP(M){CN( z(#$ot0SZ!HN|nF41VslnT>6Y9$V4G9Qn!xi=rP2u0J4FhuQaqDF}Zc@H2vR|eDIW6 zFn~xFO?}}V%`tLGiBPa@IO;SD^$ZPjg>E1ebuN#yHFuCwA9I8Yjxod_6*!xuTEtdv z^>hFyT!?Puixf>yZK}vWpA2hI8h>MDBDe1$r*1qw%Qd}#asssHQ!{+bx2M~#)l{;U z9Wwdb&R_qX7OSSuGhqAkXQ7VV_s#sb3>ah3&H>|GX#$fBDOPs@4Ky|;{sl>fp;R2u ze;HLN-&Ec&>5)6FYk-dv!GThY$s6Ze>bb_HwGNpyh}^r!c|rXPy8LHOxJo3_@Sr~v zKh6wZ4lxTTca$_J)4Preip2KaOZz_fHOm&ooNdd|rh>8tulIPSmIK;Gf9&0(N+3P0zfEhTieSkh5zc!$P0B(U1e{FXTPCYaL|ACngH@#U? zd>$@7h_d*B93L{$lHQ1(3*?(0u6<$O?lM%x=XvCK0DqSs0FF7(7*3C6Aie`g50nDS z9gfMZ)j2RgCTHNT065S%=$kt6TRAc>uqiuJeW_*ps0l|To9-KW?XF!zZWiHgWe1FC zo!2Q%LhRs|A3W@uwy%ecTis->u~smZ1glkHlfo;o_AzEnZnqAF{@*L_L|2y(YMcp} z86g4$O))OTsOi?w0sv>kP+R&{Kb{ysmhNd|*X zaIH)=w7h9YgT>stKyGC$kx%SHL)B}+(~nJ7N@Mi@jc`<0`qiC2;G0SZRLacOjI@`u zIH)o~3NR;+D*)@kq{|B-;&G!iXeuSpwu0GebrNr(RO;dccvBhHy|O{zg?WNY{|*|c zomaFIc4aP7z)tjuZMY%@$#DV^Bp%P?)K_CCD7*LfmBTP_XmmzYtB!f^;w#dhh+F0C zcaa~sD9LI3Aqtg}d%z^)JlyPw9q6)mv@^x*^c3SGInYJmWp(^FLmQItdbL_uTj3u zCZyHz<)A|t+W-3t6Ha+~PGITkWi>{Zbak8U=i(%L!pI(0TQQ%;| zxJ(`wMUI%M@9;wjibVO$C-Pi53GVpvgR`rl+nS$cCIz`l5~u1hir0W|(+?+8WTM@f4i+(m_1h9ByYqbrcPSF0V%1B@Zd4mx6a8QHi4 zRD~F+EfHTdC_q18iE%+FM1ch)*xH`lryRA|oDqXj_Cr7u*C59`C0-aICVnnm%vAc? zJbxZc8C0Or1{eP; zJq|YC^W2m!&lvuJdFu=z{#ug4(&RV9wv90;yIH-K{LGQog|Na(uCXi zcCt%DtXaG^s*CQtyo|!1504aV9C(74d#Ea%VK3Dsz~?1``X^G9NH~Y0lED)&Vv>}7 zqygME*x<6X<6!^Y-rf4;Gx!C3Z7^!SAK}H?U3OFEN1!Q0qTgaJeR*YJ#E!n~>90Mn(FWF%kj! zZ<-O$LJH5rZzUj$A8W#r#tyo%6H1)oqK1W1SEZARw<3ZCS_i7 z$AcE*c*>tP7_JXpZDmVMxxe5s&xtS7?!uzB0uH%a4++D3$Fo|ff4@VZu@Iy(C)iuv zo|9;;GcvKG9>y4}JkUe-n3JSuAQ>+nUW3|NIXgHRh&67!`FKHC9~Y!}!r>OQzBf9~Y6Yl?SJn z!XV-~!zaJ5jn6%0QJh9K(^WwWPcJ-A4CC=h9?UZFb@Nv`&miTz|8daT#Bf^May#2LvdbG-W51mD`w~UxK;G;nX$8zi2f{0~ zmW(B>-*PJG$qO}PlKoOu$ag>K2XZXCN>n$nSN9)j=h@@_CE2gI!pYNU5c4Q2l*cVO zDy;gzKR!=)gbwpo{a;B&Ld847T&T;jUw~lFQ@!k_Fj$!er&yD=K#oF+uhyl<0<;#bR5zX!Q~v)o&=gP(iw4j^CA8I-I|Ye>5a zJe451S^2~*|CU<2nMY?LmWfz=1I=jiT}JA{K#u)NQ_6FHmIh%?RK!6jih&X+C|(s3 zm&^xWsGWO+5XZPET}Nz7pKlw4B#sGYo0^u6P7(gfXKFLpd!|bM3xu7C+6-@VWGxjQ zM9#c3pFPw3@S{!cX|ur#x<&P$gmtmA3S=TMw2i; z$Ohs(g8GcEod10HpQ+9fcHN~Dm-TGNf-7G7bU$Y_vPM?SOD+st-45XRtG}i=UFD-~ zg(31%XW49C)h)K+T)tdlc>Ji;#iSL8T7ymw!dN7Wz4m*^mmKg+7SKAzN132#*;T>c zaF8ZTxPQ{S*IJBBr?%oHMWhDn#ZXUz7(HJO?Q3>h<`#m3)s!p#mE9ug?{P z(|Wuslfmh`CG(O8b2mDr!vH7@{3_6B8g;4BV#}~WM>YOotLHoO zG`*iCy*8v(A^u6{4<7lI`%n&+DxI##0Q+yw6cy>6E3f*#T`&%syd?R0_b8tr;>EgJ z;!k%c-H-9Z1tSYmpDTNLAyTJCwk0NcNqh>%A6z#NR%2zudo_$w?kPy{NRAq>LEibR zE3nN%U^34|s>aY{|EHrXfO9sfS47cd|KHc`Mf{xUo5MIYLeXDgr47R0lZ8}P_o zd)VEkmu{Q;y72t3EL-5io}ZFVdDI)q5dsDIp{)N~*1BsSQrlS1%DBsM$`G*mqIOlf?k?v?W=`m?fv%$Wke>d-tU!M!t_wne@ohbDB)f!z z0mcW5-R#Sa~MG#`L`H zw!_aqKKe^A&8|Mk)U$U z&r1$F8m%V!lYT+nrGF^mepmH~CX|Av5)v+=0CN8Ju=&L6+vn(WYcaLcabpW*I+3!` z_V}VI(rL^NXou8t#hEE>#45p@t z{;vf3yIB8iu-!4}B5Q18#PO~V9+R@G`Nd|owVzT=1I+SX*J^USGL>Nqq?`}1+}7g= zYT+FqdR|=Ys({QAK5zuG`!}(L08u*drApHv%*a#4uWCoIM;USDKrJ)RLQD1!$^F|n z{yCvo?FenKL2vW1B>&1~{jWb&VSGiNP3|sTpxCT)$tqZtRf>=^Xwp81UtHG#7l(Uk z2>+ciDv*>*yF_%i;nb}N_BMm?X180l7Ybzmx>myw>6+B7&$8(!{tc@3PZ1X6jq_1i z2S?2lyjs4#pYSoYr>b2df!EK#VYH!1F?vy(9JLS@>K56?qw(g8$NHtya$|H)1)uGk zl||pcZ*$uO6`&$Oosetm1x)X0a6j{Hg1?3kncE<6R{(2>n5;3J>Hp={9lUf0UwPBe zT7PCUgHtNfq0x)%dDWtnXQ}qUx z@6-PI4LWrHLmL`HpYR7YdTB{77F{NzTggG^rryLw9$^E%VeM5+Cdb}4MBCy!UzO4E zYNhu`^-yHE_1$ATfmXrO0jI{Hk4)Gqs5IuK?*P-PH(m1iP}&L{Kr z8|N-1-6MNKEx|g>qTxJ7t-bobi<8+^=e1K!Z2rYW4g5|lM*;8tCnKR2QDbVH1`*qB zWNrfqVrX7BK8|oXWMm*5dXeG(Jn{ejn2wMD5L+W_w&am@J1CKiCj_wsmpkxflAC_K z=AECB{r#n`34O0V^{bw$luT8xVy+(>e%3h#zG(TXixkGgQIDzt3N;l2&MH@iJ}84Q z<}|KRU2wjYK?$9&?6ox33~m%*dUZFIhc4eELy(MM>h|9r88!b~yq*@o@Z~K^2vB&L z|7qNFw1*)>8i^7{=o<+A6q5uK#xlfrpfx#}Jw*H>Px!L`#TKK-ri1(D|cq^K8}9_CTM?(F5BR2$f+bf#|1jGA0;*68cl$?h(jSoKh~EEI#Ib_1at zwCf5Y%d2`%r&>n)>~aIM?4IOL1nK=f^>^7BqVgAB3@uM54glAPtekxlWWY%mB>OlO zm~)+UTu)xVEh^_Z)lqrpwqpRW+aOaZSk3~FF=ONRntxi%gKu>u?2yu#M|;$w`b+4k zYb`!k@Uu;@ao-C#(^OBWpbYqq1%C`GH*1fh#9T-CH8-%=!JdKD2X^7M{LgNiKz&8> zA@6`hT#>YH2}Q#_Y?7jjV0f_z$ny8z^J!iNSk;Z}4X_Ad1exPBI_)qAA!5?-YFh*x z0g4T%Gt7n%1C%ftp0X|&Sa^enQpx{&tz+?l(@)|xqS)10DR_UP`Xd}ftVv_)&mS|r zA}3UIWwEsIN9go3By%kY7)uo5dQnJadg*?kj5bod^S`YSyR+#jqy6@%$-BSiuE-fd zsW}Y7+_kq`+5X>!Gm7`i*}s(nCl4jZe1rM zq5rAaUTSFuV7|B;*fwA@rY4nR{%4Q=-vbbA_E5BTcZ@Gt|I~A^k13VS5TIvt=Q zDqvmk_>i8|zgq@JeeuHmBrz{Uw8Mr9V$Oydz7b#cdoBYYniz8K#QF$m&y3Q^6lNNLs6~fsfTa;0u$%eS@po>yi1C#y_m-Ey1s27+k ztzFc@J!cIgTAkwWk^FhXp!r4Yd?Mw1AdEZ|eTG`3aaxrm4Td-1w{dJ*dIvh9#-cKn zFsU)7+^&BoEqk;WJ_t0Hl!=(fWRZZx?AJas@EFkE#L_=jW^&)!YdCc17M+nC;t0DL zZzIHY#lEw&d1lPm9;<$7f5M8~0B97z0_&!Cw-jtYt*{x&H@L}4j!ccS4i6y?^Dsi0 z1-i0Ro%oj4wafqga9s)fA4yR;5^wLrVs*bzz4B|xpxNA`d-$hnb*h=B@mbxv6!aix z)LuY*S}q|A8L|YKyb|?aVc=GrX2IAnXJ>@|D(=z-eAWT{ z&rY}$&lLPW?+9|s(D#Xv*dn+-7^y$Xc$!%5MU$yN zw?Y6MMS#1J<&-$}6~cEvrqYFx_-b4$Z0NKJ(Rl)o^DM)_cIGCqK()$mmKH&jflEreQB z58aY0Vd!JXQ>3MA`)34z(V|2vtuk1iu+y6ALwsU>&LM>|aB9u{7VAO%Qu2EbNhvDU z%+HdysS9ma+-hwP00HstdZUshcn>000m1>YyzCpM~Rd|cYlYkAa+#dP@wyh3SF2)+&w(nWO)2eS(A^P&hQ9!YxlU_ zmi7MlDGPlUBX>p{0q8l1Da zQow1-u+0RKnL5_TmxP~3vrbs^dN!-v3c;h#p%yw9l_b4(IodlrhKobof=-cCzJH+P zrP*1@ms)?fT%huVT3&cLBiK{Azf{pjA>OUa*0h7`@4o=ZI;|Qo&fDJ|vK3$y6Z{lw zx=%fC7P=JZcQhDbQbOJ+m;2%^2nB)6lxtdELTnU6uC-*y+!4}m=;CX?C{P?a6HH4Nj?1{eIQJd*=-M{eS5f3FfY%CT<#id)q!dDLMvZhW zcBnia^>g}}mk^{}y_hW9 zmXZ2`>XWqQBGaa}PSe@?gLhe#l?&NwudsKMAvcDYT14D!$W7K8SK}`GZ+=UO;iC*? z`0xP-tXq$WsvSAo+Q{D`&tC~2i3MSBEokGy0&lw(uLjbI@tvxyK`i(m25jF`mwvqn z_l+1u&*9SI7FN_I%P$br73Gr6A|gvs9KF3#l@yh*WUc8;VoMw)dZ*ftNts!ATxs*m zLy~tBq`2bh&;`qRf*K;ZBR>I)n+oTDWBf8^Y~xtR5ZH?N-xT-;)=EEuNf|1^i*eV9 z{Dfr0993z02{v@9lCn*_1A+pg-ExGO12uKv4h|JrI&t?4<IoM3@;$dA9z3-S>V+fi!7n?kFHLoS&$v#Ia1f2b(|Vi9&7_V;8dbhD zjir6go={-L+GcYuG|lmUefa{ShSmhDV*K?iFR1PQwAy3rz@qXa-xUmTZ|;B;whWj; zRL_&pKT2;l()S7LzB;Yg0M-`EVEu67%eQyuif0i^g|CW_L{iv#Kb)ZAs8`$t1CL^S z&^oi}6_4&DdoVCn(75;a+=4tNp|YvxV9iphzdkIcpsrhTyPgN+rh-<)S6lEMH{ZMA zwcOSYSSug;pdLMTDuTw>C;W`>`%}hS5yTDyMxPi2j036UpAA?XU*!^!*cC9dKx&nX znt@8|z|kc*DzeNXC~b&&m@l=kANTL8*fDY33J7j=)40 z&PRZCBHWD~uK`mlgPcRT%A`}`TZXji57?h*E?uO8nt_DoGII#s$=%=m`lU9&g zm?SA}^|fnRzS9~r_IvaTwEaRvAv54G^8*f=%ZFdPxdVSgtKvH0Z6CUKbaL+U?ljl7 zVf7a9jq|u_MqTxd4bQ8AqNckx)rjOv&7VwgIh z?g0ul`jFuVbH)t#1+qv_n!ZbL)DEsHT>iq6bv5#ZjJ4HWhxr$q-1u}nln^yW?OQLC zTXULbL2c3gb6pA$ujN0}K%pb6l=>G4LAm--^mtT{sr%*?fq`(Dn%V_bB4$JgIpak z*lVkCJy^G(s7l8YS5(8uiJH9$sj9_bSN@34vg)1h=YD}-%9#lIHhX+4VxP$TkB5FP zL*Pwcimb#sRPrlD1*J{>XXF4(430gkWFu3KcF*hC(xRBOdtZ!tw z7k-G4K5=#evuUW$Z`=q%CdI&Nm>1aZ$ ztk|dI@*GQT)&0zKl>WgS%@a!7sKupb%a1UY>ZGka(wE-+=ECmvX2A>3Omr)e<8BNC zk5Rt0u;E{p4=es>n$Y5{Q^*(YND{ z`{O@Yg@9eUj2lWtW2d#haS%P&^Kgt+{!nk;vPeU8(l6fuSJ$~a7OTTvJH!OsOFT0_ zN5a|_u!?CLaoxIJ9Ct3&m7o=XJ zCgNO&?$~6;8M1qT%$1|2%6#Ni?QLaWn)~lERQ-t;8rfzUQe4jLKAI%DtImfXL->?% zae~fGQQ2WDukM@ZuKTuv>ru*&#}^X>9V=FlfFqbDIDsT_teVtrDW{>O8a9MUhIEtu z-=U67tN7~9?`y@Q`#Bp+C}QyXCNw;cJsCBvO^N95ZZi9X!~ni?G};sLfBIIhjA}z_ z29%PScPt$d>*}iTKWeZIqzh5<$otbG?DrU_@#5u7wif7O&@%Cg9B(f^c=V{2pw#m_ zCE)-E^WIoF=JW}x3J?m!oQ%FdOxblV!iA)7ku}(~9&&?CWs?`)+Po@)adQ#t5af7c z#|1+kLBT9E*d)jkJ)ARslv(z)`p16g=J_&%<&?k7J0jz&>ds!YnQSuJku#^vq(?kl zLohjn&-|NvwBQBOHJ2NQA3=GkpeRYkf}j7^T@YxlGeVfuPkK|{FW(EL`*JI>?!H6< z-Vr(fI+J@7oBW10?Nz0aON8y^$mEhohn}-LIA58&EOOOBv%^C*6h`}r8>JQ( z*^LO-TaN^}S{~N>?OrY}U{v(BhMO~5m1c586yOS#lni+!M}o*58d9Udn{DhHu$DQ<_Y4- zcp8oLw?!N@&1oHi?*#>P)&KA#k0XH;wXwO+{)5xe%Y@jL6r8N6*!{5u5ux!H$%pve z_~sma%(f~0NQ95MOkh`p z$_Jdj?+kJB>JFxC=Dqly7)Sj?!jk9{lD@Z9AfPq;2`(7s6}|;2_h};9#0p4D ziqp{QzjX*4A|47h4$)jrY^G6EuSjUPRCiurP&dMN;o7!)SF{`oO08nH4 zVnmCl;srmpuOIvUIFLX$f2rQ3Qdq6eNtWy9s*a}9+p6s#N0_iV?`NX`-y~MKQ{_It zqN+Tt5+^>AhoFH7Be-IyjtQiU;$W(>UBQ#_TVKFIN9LNY)cfui^g>1mj0yzC$TPR4J zkY4o5p^G+M`kF6-{XA_&#(W3}nDli9ce*R3X(TZkagyQvX zUM0Ws011j|IA2+Eoxd)+#cT5#o+#9>n>)ROdVar1>E7I_>0ddwj1};yvAK$3M za+h{V5`A-jAnD4TM#Wnz*QyBW{A#kv1C}i&B94o@uGq=-E^r~{D9Gpck83L4I)3em zYc&j7L~_^b8LS3C(r0Ah6-T!|E*ub+bX+b{E1!7*TtUucpM5iNhr+|V>^hZ)>ve2= zd0_Sm;;!dHQuWibHj?}avKaa3aqpX{t>#tL*O(bLyy6Z92Tgnc?U%}5kU!o_pGj&#pomQ%S27GjgBF21s{nRS@^ztpODlv>- zi0~uV+bjB~C_OlQP-|OfnY|u&Q9PY{PjclE8clpP;#PUfn5v_099MPJImJ`ktl_6A zCnFzk%c{pcm2muu*o>X2gh*%6FYooKiE9bBdcizifF^fdWf$}MX#cdUzjL-KAH!=QBvsMS38q7k_7$B{ zNY_3z#@Gvn>Fe;kkS3X&MZUL^UM7y-`4B>nq69rTsQ+TazKS3?vU=ce33DTv3^J!@ z18-^l!j}fyAJWgt+&|5Xvo7j+VkKZKTwvOWe+}%eT@A{hab6tFi+&Y5)C3Ld7PxEW ziJ7AyU1iS%sNv`mjEer-;~;YDN)1yMB<^Xs+|VJpY`9*gbs zCn<%4mS1_Y>}j|%l#DC6yESg=t{JfW45OCe8IG^@y|&|3AeLDA@JwRlm8@H6QM((W z8y&5p+>Fw*TxP>)K>pYv+x_OfvJy9V=ixoQ6XnVs)# zeln_7A+rH$$*4d^lSjd$#}wS5SU1cCdS<+kWI^Y7{tW{X)UtmfdJ`1Wq@+0Mvv%_d zsufAp7nmBCTD^KR9MWH@D~&6US9c%1CvFq7^gKzG{A#>RXc}YH78{C5m&kHLxO5ml zH#EGO=fR|)$h2W#8&OU_7uOP`h<~g$$_`rCZmm=|Rm2<%n67xTl>2EXpKnjS{{8S4 z%cLA*(l1xipLr!Li|;`;qwa&cI`R2BZvA(GcSjO|N~wh`wCis1yFd&sXHs1|CYUTq z@YbMv=0|zSFX0~nq&C4bk2Hk}ZiBp!omwno3$gFMy9w3jO_*+Q#G0x7fn&vVpjn*A zHYR`xd=odGp~lS?A6wfsq1`tQ4+659@j-x zzB_q)R&M<>V{lF}$A5KiX3E zVd+C;q%<0$cOL#+wy`o1t?aSS5- zY=gTj(`#ca8$H}Z9zv59+*lVQGgsX8)!gGV+6ys+*3=2&C=es$E%8cerb7qwMCA?6x$cV+eiU<=2v|SN<%x`Vm+py_yX4SQN^nX`+8kz4mt0J0@+B zFkI`W38%tl?ytz4!)3ZD=nE^ANnoqWFU>a<0i*&2BodD}Pj21jr>6+-eJ;0GH44$b znO}j@l_wOYv@@;aiy|S#nco5oV&_q!4oAqcLFgQFk|b8=osyrQ#18;J;QK1YyZT{9 z1|1-OzQTp4;M7__G~J*d^$i#;@4OK#P-O?x{SWNAQ;pqKbW3~70oNbijda|-0OF6~ zv`vTioFo1N%s@w76b|n4FRGw_o)OYw92KBnIl7>G?a4MFdFPJ4(&zH$DVr|$cXLTy zW=l;AY;c5a#De0xJzv64?Q0m>qmSbklD4;Ax{jotZg4FOT{zpi?iP-Km`6zR??!N_ z{;4?ppCc!3b>R=t1GaNjaju#Pq>g;N4s}}H)g@t^8>x&RP)b(CO{Pyku2c#f?+rG4FuITTeZFZo>M3U+QSm^YhOr z?phVRqzJYWrEoCh>^n569&SMS)asd@{w$ml-bOdqL~>cJ)YYVsZsH1Q=~P53k4G(; zRB>OU9C?f|fs3$Vj7V(!n~PYVaBWttiU|6$FGzoAnkP8A^r>#}t}70@XYu4|_k|S? zpfUFMh^<}k@JOPK|3xYzZBck7?BSQ=8Kb`%%WBw5g>%gmQQn&C_h)xAk7m2c>{b&{ zY|j`~vFA`7S^IpwzryJZL3=?6lRL5Ih97T^^{G_rVMfzLLM+QY&Y&k@mwJ}90w3y( zQE*wBZ{*~O9=GxCWp-2V1ap5mGR_4L{U!l9-A7$8Wia6;*CshU>}t1exoqT5nSbX- z|M##Dx1WWI?9QWCV>fm?^Gr0?TQ1DLFZTPhS1zGpuVk?|u|@VuDm<;+#h2#@o#{hw z^fFj@#$=uw&SWzAej~x|3=(1-u2y>7k7hJlBLq)r60rY|@2Vft+nm{)>mt5Cy{_S7 zXFRB8(?}3!z=mnp+Vh0bz+|#VRM+T--}fjKeF0_PJdAKCULL_{>`*WV|9)!IL;I;H zyymWUb$FAPnY*th`xUlS|<~9t7#y)@0U3azLg8&@jrZMXJ<<;mH9u?oeBp{YU zjKPP5IsHPhRgZi*#zXxqUJZ8meJM*W<-@R~DeFo8^#a&*#ux{Zn3JV>&kyv`r@TDz zDwyafA2u?`BRPZdNqc`xm5GRXZ@cx?X8}0}^HKB!s_E>lzh@gMp7%PE!uI+d8H}p{ z?Gd#~Id)^&qlGuWSk`?^W0@(KPTDbDG>B`xm}%mXK?ka-lc(Mw_d7=TM)LM2{R_YCAB=xEWHrv+Rk#$o$d9{( zkEX=NH19N>@;6t1ZJ)Od+qRLW8UEIE+V&Vv<>7}G?x=zDTh5({5~Q3CT;LJcN{^Py zv&2svdJ>Jd1a_MDT^em$@2j-0V=eiEfkr1Ye7ve@9q`=20v$#B`u9jHt>?SPs$JvGRu1AKk~=A+=MD_H#5*= z!{lm8Fd)!}99pyU14ypSfG94VoiI6c%H@IEXLjaiwDC7d>2HxpU`=(_v+M@7dzRI! z6+WW4T!$CzR$TBZrWdLb9(jy<)aa*F_$g|#|DaO%-Vmjq2rWF*Tah;1gn$AOq^}n) zpB3X5_g%Pb{;l^Eb$Vu4?Olo!o{mpyHJjj{M&u^tQ_i3LVqdcYqo!bqyA1Py!#ufz z2{Pl3<bE|b7SN`Sfg#;JCAm_14fb%` zD|=upzYtCIy4geJV(n{cOrUflfl20xS0+?VbeN{KbAwJY*PEZgB-gtfzL=D?2364s zR~#1_C_QN) z#ABTsLJiJ8iy7%mHgJe{w!mCXz1a{^0!}5~xL}4dzoCg!9ghXAiIx_3)*+VHnA?%x zx|YEw?y&xl@uwWbB0!fM&(O^8cm30x^x=?^(5e*;M+>nSkMpsddAY!(3p8UFxGsFS z-I#4F$!1zj$eg}kgXI&AUho~?)rr7`h|QMjZzfln_kPqqL$CeLv=P%I0&1^mte49T zSxV9`y0sHO8e*T&e6@s2;#CC&>w}LqT$0Vq) zuv-d3X3eT1=o$4>6vTt(BbUvc!j6euA;v${mMF5szj8f=4}V50gVJ>k-VYY zHgXM>*#E2+t|SG{Vv7{27*k-CS`x!7p4a{5t`4)mbE4x)6Bb;4?~NnEoOt^%GM>!& zsh->?!?m~ol43)MerTc22DzBgA@ti^5d5#cA;ArlX2SMmn3w0Y8 z((fG2+WBqegDBmWB6A1~r0W|LlBXw`AW`@;`GD}rwB64x{xGnftb2mv%gL+qdzh4# z`=*Rfq)7e{9vi}fc)dl$*f=5|9lN1Ff`5oId#AF>6CZ^;#k{+o&xOH$*8B}*q|awg zxSVP``wRNyy1r_#T(NHJ_eD#$%j=NkUnIn5i`;4V&2)a|t;$+L$R6Ozb+bN?RrI}? ziDShz83x7pQJ(e92Gj4%pTj@-7r9h!*!yjh51pfUONNmms-mdR0+`F$VB0{TWrfD6 z^ve2t=&yV$%|D=(6J@l8ujGVY2DJQ5a(G&<>{J!t^cUZL{37?sz&WIc8i-4 zRRxDev&l)luTd~nmUOx%jjfwXCwY$w8u3jD@E3}IqJ`WP=;w0lY6RB^=?y+GbaG#` zi*wGgKk+osj7;DYVe(?(#a}G%*ST&p3cnw3;%d3iZSFLA=j>B|*+L{#$+kbz&ik1H zp>(e5n^ECIT(yF;!1sWlTGhK`Q$ue!ZA3c7gpaH*b21`O4zn|cX?5e-6WerzpBF-! z-y3cms^cB~NgO^bw<*M?qLaVQ-NV@rg@p2*|m_n^67<20wFyO(sEr$tjQOlEMap0qJn z+^T~5`UC&TLRYBu*I)i?|N9Rz1yb>*)@G*HawnA5dx;dneB zc(uJ!jjxMZJ*r(^HII|G+zbUux2Hdoo0(XuiUlYh?kn%1`OuZi{Mez1Xx5Bt0U13norV(Z*;i?eOau; zO&Z?<0kq0eCt%aF@`mrgF`-O%mz|pQ4EXG51X`widFg9C#ag*9$7e5CuQe z)yE~P z#pyyWN%Gi3fAB#vqp3DW{a3Z-30C^sdUPiG&fX@+1=BKK+KZbXTPs>`J}Kud&=%kN z@q;+!Nl;d~OP8PQrN$9+($-seJwC(}el;QWzTqi+%;yIZG?RQi0f13}Pg!Gl?U1KR z+Rfu{#)Yhz=9oZ>8bx<&0p{wimq#;39YYVF^f+33n|bMj6{}dJC6~dGDX*NzvNU!MHl%zp4 zZ#Y4_l8))+8qss4^WJ~`IxhtU>X%YcB4h_C zvU&r*IGt^&IQ{PYsDh>m9AWsnW03}i@+&si?MZ^ez_l5+*q zBt!D0d^h1EzYYci!N#rMZd`hgQTJ>Lsu9-|Zy#nTJ*Ba@7E-HwzDF6w?IRLj=#&1C zJ=gLzmJ_-MN)IBXIogS&d8D!b{Wc(ypV6?osm6lL8Hbt}F>~IA*h`(x!cWL{1+I|) zLpx-}SH{=qUcDERMtCfpP?J-dg4oDe$4#1;#{UAxiJyZX%g470Y6ZJ=N>7UTMX!VuC+#ti>_0pm<6WG3hT9iPl2nvjWWIVooT}Ue{t@xWJ%)5#ija zrr#y(cptWU2S{kGiQ*x4x3aHtuteYUrIe&BG241BojP}F8)>oB-1reg@6k+pxhiOD z2-M7T#IAK;-14bUuKl*KeCE*qN7YvVRkeTN5=RLI4<(WwX_UBxw1P-Uw~7*? zAnlXg>>n{Jb{Tfj-e_ELi2PwdqvpHW&__A%HPi=?TFp8s}tcdkzlyk%Ok za4|SkHsr5cAf^D9@O8xgc-)IdOb93lA z|Ad5S|0l!rn|_*flBbMWq>VI_pPQ(*8k?@?Z5d42$U?FWgm=jpP6|a$e`fZb<|Ao} zUlClfm*>OEyxlRR;92IXyme=koVtPClD}0oAr?azGRRi(3cUS&zbgYEXu2R>^l;N@I4hZsMdE*=%k`v-7jlfJqG zndMhuX8E6VCL2l9axO5)p9W*@Yua1vKF9`w#?2beA=3>YH7o;ckwBw~Zwk6M4zqSV z=i^p>5hY&4{GV=ZAmN%Bl^5F-`M47i*}9Wh6xFm$Y|%fhE{uhY-8H$45HsOcUP7^h zzwGzVF2nm-Ji1???kLfVq+jls+g0UVpS}lN#3xk_)>zsl80jJhEu*ezg{iI>FW0&W@DJU zs8o+lu#_WiuQ;)P2?X4&C(Y zwR(r9p_l5ItA{rgM$*6d^mBQS9`IKYQKA7P4$pzP~f8P22SMSkH0ejS!vsAgP{`k2I#SGb=6_gc^ zjW7DHNj4<5w=H~k0Z$QuAELUJ#)e6~)sn?Tf@Qo=Eg!z8GyZhKVyqNLhag675;`eS z5z?<;C}r?=1=u3+QW@!eg(sN!>1;x-da_ zccqo^ydH{;Od_tuaxb%N^-R{o>oymZ+`UCgd!RXTCz5D&B++y+=gXa9>Kjzz_G9iA z-+?l%^F;OUqy)#8VP&7`Zp{HX^z_fc-5?sx|27vCc<$HoQD*F>)CcXA{Yx!rNFBYrsbzVY3iKC74bL-K0}`r8|h6E+W3m#`<>w@X4#-6JpV z4H=UZuBJ;Lav91)@XTx~E6ll3ir&1ZE+fr9-*u{9d-}DIdKgkkR@qyA{t4ddx%J}4 zV|7*@$oBzzIFaTz^`BfKMQP*;n!^~r{dlt9uhXa|hCZlRTjv^ZHh*o7zXUwG;%xWi zw%iBLIQ=3mZMDy*z0^hmw*3A>xxPDA)c@j3;UOs+UkOPC~VqNgrir- zoI3ce27PF9C-0oweHEw47FJ~XcpYdc)es`|H%Z?MMvm&+v(%Jy;cGL}`rK=fxm;>A z6wK~R^4ILTF;V!Zn?D_!DR96AIg)* zOrlchEkFUm!Qp4+4K!>;PX)K*Q~t$MWGSvqR;lhKgla2(44yg^`PeKFAC1q8?GILOmM06aR)-RfcYQ8#LY_6=ajp^w^)F($AY@49rCyQNKvHl zb3)J!V>}$8B$j$PWMmwhk=+%#P#Iz)kN>SkdA>#Fu)Aoi(-wr%y@UGTxRNn2VrqF! zX-)4&UGyqi@Da)uxhAK3n=8j4P?g^wUyye>FfWbI^+@>whKe)nmk>impc{cZyAB7) zO>TYBzB!)!-<<}qB_N&EP>|cV2XDW`*LQ)LYjQu$)!gWF`VK<0$AzAa7HJwTE z-5Q=?01e3twhg<{5IYS15RmBdj3)&A8JgbTeW4NL+z?j_)V=tZyw$>Q-+wiN(V+lg zM;f_inZF}dnRbB_;bSQ}|B%xSW9yQE#>dUec<(QS$cL*C;aV=2-jIJHJ$_H7^vCl~ z{FJ5o9pgj);TcqKKEg7FJbSb5RLQM-tAQTuq0aG3f|q(p9A=zqAo!w=Gi}!2&nsyL zs`utfn#m}gziwmqudu-GLk?(eIH0#v-J(D`hRH#ppY>Hs{?{NtSRVq}O--O|5UFtt zaxT0jVb{d}x*mW5O68vjLIdU*RNuD6Ul$96gV#v0A9Wa4toE)N)7dthb;=XbP41WI z@j}TDOj)mfiuSw*Dl8isBe#V|3^YiQK5`2rOAS9uObH}Y2cEDiD|zZ|s)6GIvL|Bi z6M1=Ki+O1OT+}5x{l{_-*w(9VRwZBbo+PNvKh8g@q{HOOpd}HHR5^Ck1uiQ2nHt`R zE&EdeY2WscA_pg42JK3vMun0Q9j6G|P z$`k2gH;z>S1ROy7$I~FppN|lv0OoyS%v_^wI3$L2#HPz73@+NW#BIpedk#r1Kqrz= zeB8pTAH#YHPa8av!y)6yvjN_3Q{{^=_1aqP5L_{D>b_oW2$OJ4)OF!+dO{kA@sGky zPte+d&v^kN0or=;-8_;Xc8->pRnwQFgu~nugv|em?r7f1j(!G<{0hlx^LkfWgs>jcAdK9yA7I%FYVqnQnxScKM*TiD&mRbNvB=QB2d)Hfh|j< zD|`6sC0RNe%H(T0$DVNmN%;(FasTB*&1b+VIDj6&ENV{gDkb+m)sC{qHLh}<_rxdn zKw2&h-2nJOEo|bTP%C2&xbX7*?j*^v>!}F(BzC^{06{T)i+jg=3f~=-qG|JE_%p^Y!`3Lv|jCY>~BvDU%%w+&U#31eA4i%gs z&@ptQ#fVX{pKq$KkWjN0`Wh^xqE&c;%Iq6{<&xgLHYESSoJ`V{zD;$$3|WMt%!Kdm z?3ZqU4GW|L5Z3QN(ImSy8OW`fiqOcmG;J?jZstyRC;Ds~_lbR^7RXTGZ2tJY+l;}C zmmLYQy!#7|$}I*6t^=kdYITg4{}i*tf4ZbO1pb3PGHdGk8LgG=qC|gZg=wLRv?u-Y z^J+_C15jQTg0M<|k(>NhdEv~tyXM^!bFia^jOwD!`VK;gXwaeaKjBor5{!7iTPhry z_!3fWog)0Yg@B6x5h~Me(yc(mqzdi9DdB8kRZJm8+0&<-9oB6)25%=d=7dL+DI507 zmyZb*M0AyFn;+;OYHJsj>w-_UXr0`z`)txEShF=HyT1~nLTg1gIM4oP6hsorcIC}N z4N3{)Ah-h2omCPCx@FRYFl&rMpTlh_0x;3;(g#gan1dNttr{R z1+Q|>KY+DBcj&t7wZlFM^8nS`aFgY3bHLj#}v)+@`>=_AsQn6xb0#162 z4-=M;r*pQ+K8SI!p4PDHWSOQP8%uns`qR$_A)w0rC2I^irA*nPl=?!R?rak@Q%tcX zKrJ^ke{?I>$vnvv3Rf=qteQ~(>#v5KTzRy*GjTAk+1CeimhS-i4Laeg)Lc#W`9q%r z81OMW?I~u4-*7r1P<#VS!0ud#sWr_Ep`maCm;F20qwPmgB_8Z}I68LvUE4mp$$!{3 z%3%q@J`{d-*1LK}ATRS@A;H0cslo@n_$T3fK?y&$1dmz&0-h1wnP)Co++lK@!A_{( z7MpT?&OKI|jB;WNk`5}RESk;wa>kyUw&Ct>W|bqCU5=-=dEsDzo8Eg+AjDoE+EN4B znSBHk3IEBy%y z7O!)uUPdDzO!I2;2587I9px1Ld+lzWA7PBlYAam$b3_ySP!GTxJLnisYSGY1wcT;P z(nuN>L8VAFs*Sz+h1d}n#&-OoYHt?dD76mzqGX7Q=m)_w6>Bv5)Zbp@HhRZgh~U{} zJAa-$7c&`PD;gj~&gFGIH;&uFRja4^x82yK&0RvR>({?I8FW>PxPDvvGM>9&T z-uJB0df;`_Ws^^h;7@N*gnf4C9a4g#jy?tIUZ*AYF3rY)GRYI`W4OLOMuoZ^ped7A=>zi97`5tk$gT!pw+(Z;SLDK!?2_`uV z*L!T#T&4vW>Blzw``vfGl*#6UY8GmVp|H5}70p;3YHdqbYwkHD;B~;`&8rSE^WT0;3>PdkoT( zaNO+(q1L~WCUttR-^PtY{LIqKrl*zGaMS=M24m#_D{ebLGVYt_n)y zq;2iYN@h}=jN-|0jL^r**)jeSkFE>1wGK}}9incX6oT?b^#*@wbS*S*hVO!APM)9_ zcz1HF%1vDQD+~z(vuij&X!kz}AzJA# z>vi>x5HE95OoVg1gpGLgq=c_}#^q1ilVF0&9(n=mc~Y4(JH#&t%S7IW3t>xCvKXsp z$+U8D5s!2t(@s0Anf(ZykSG)`xTUwIW8lH4TEnqb?dJ_e^VbLzy&$`tnWkH3Dwrsz zEREr;BT){`ZEq+K6WL7K2(@q0v0={15b#RM5Ijxk0DE@-1M#`Y>9@vTe#?mK4}$cL zkZN)ZOZ4~w$ZQgCJCT`&Q?x$t-5CM50wh6dYwf0a+{xW?eqXS??TWtGJGQqDNex3k zfV|WiT6WfP%ps`+l15uB@iF=Q=jRF`+|uyH;fuGDJ3K~;?$8W*!BC5TOfYqejqfE& zULqJ7I#4$-VWsjqkfJKq618i!KmS!!P0`Z}F!Y0Pif1nLHPxcKa*L}<4o)H02 z|1QZDCwIPtB`)C}ox;e)0jr^t-f=`R%E5RksbHGtm#Xf*N&sh*duareQ(oU##2r#y zQJg8K#=-QV5N2ixLmXL9aV6B|%2u51WInKe^aq6MtWqvkcnQkURuM4a;hL3HW?Y%l zH_~LTSnPbkQ$|+e@UYNd#M7>l`Vd&YLR!qf6o1O;^BL;CbFWdIURjpOlN@D?H8A8F<7xMtIV*j zxFt8Ao~nBFpnTXeVGeY+_+V_NR2m>(DR!md7Fea14<9amX>-!0baD@Hlh}LTDQr$g zJC=BQCm#7d!dEbi7?#B4{;PrbEe-LpnG-lEE*YBXw@ODpf|d=G9cYH^f5vy|5Y?l2 z_~@Od9yUv(FQA>`E#p$nMY><8tZ|0Y6UGOR42DH6|7GE4xcI4ACBV%Vdvm%uR|*c^ z^Hi9bt4cqvq81>EGk&^l?!s5_xYyv4ab48!@C$Q53<#|M+zDk7^IV_5aVMlsGoedd z>fzG7sZ-E4bY?$t5GUT+U#3UrcnF){(>=h5ZzfVTsBNBbYYCFw_@W2PccKu*LgT=5 z@2K0m_XNHIUv%>K!>g067X>M2Dq}KrwcQ#rj1ptf)ZAisq5T#c0t&qNXfc^9PXTHC z6*+nLa6Ct7BI)_y;pa035s}MMDf#pByfZ~y$kA4Jd1q+=$T&_CKm{1tP>N!-c+wMS zzOsn4&HR1uumK4)yQBS3v@>4_io?M*R%Tb@?^fj-FVQ6dboITHmcGzs!ca5PxE=9; zbO)dYp@QX4H~xiLXQ}1J*AHyt=BLk^gVCtXNco$Kld$WyI!yPc1mL9HgD9x){0#3o z@L-g(b^n^E0qHj(+T$q9~ogbKz94%?@2-wZA1Ym02YpFh4JG?}C&kZISNbfvZl$JpC&X3*E-< zdXDUdUZ+yqpPofl3G9oi;H;NF>Tf{s-=10-B9WZ%9_>%758ajiT|K>o`1k|dp;*{_ z(0ldNcI6}OFagCEo+z-Ao+Q6dPwMb~upqaFlV*p|e(=5j=7-t~E08)?G4|ixmgq}h z`?EOmrMWPz=hi#yL_t*}!9uEFbcUQh@>`=MpFngKoM7Ph^la@5!^Y8sLk<-|jg62v z$3Uefq2~n|c&o;BlAd^|Y-i^tKt}Qqp!j0y`%L4J356%wdi9A)7+=a~ao?RuV+yK` zZhC$@Z29YB0{IQ!y>GsBZ{9MW8#VRGW*fhvSfF?O=tg->jj`+R@%bb2Xb}po>EA`h z$txfU1PYYn!)-)lMU+wmuSE zrln`rPBh&r6M4(+9O&w@U9|~;veTEwIS$WH-!E7~@F(`!7U@PiGP6G^4 z9w@G4-e1iML6#%<<~;E`k*aB@L?i>!`a+1CtYOn^Lb9cb!bt8677Pi~js1r*r@Kvt z%YIxtgV*I5ycCmy^Vepvr(^X|-u{pEv*W$RI%tsDb4IU@Ck`49QLk~z;QBRe4q5^m| z9IZ9bq8~;`?~js>)Y!7OOLY~?zgACb_&BZL>hw&&r20$@9>M}z@)uAKGQ=OSQp`F` zzH&I$4qq1S&y1d&?w$zKHurN$XB0ATgpfZkA}OxXoO4I+Dwrx1Ejyg~Imnq0Zy{dI z?}tpo%eB(1#tG1uBe{oOV7Z{Fz5yB-Oa|QY9~(DzYe^-N@TS)fHo<0>K&2r&PIGT^ z2=hZ2%b>+`r6OMy^Dso&)~;Oo!-eyBi5$`R4J&@te|0Y@VP-OW+?K$%+P{NEJRX3M z(!Xf((Iz8^R+q9lcg2?kObC)T~y*V0~p#6EELeSy*_pIR6@i1OOs2)%M5jp{42&H7^ z*^RUjP^`wyDrZYGU!YMXeQRy z2(&YvZi%UI$w!ils%HG~pe#n;WT6`sR-mZvw6+)B0}P1c2|)uyh1lHFNiE9Kkx-?A zoKW;f91vxkez<@)`7V)xydIe-yPTA~gwBctZan6*Kl<(6h7@ws?W0Oa7cqIT300yv zGIPnjNJuZiVv2{mEZ!GUh+sr3KZb*0aPcBv^RfCt+cB%b#EVc$T#zt-`uPHB8JnWe zC!P98fuiY8T|>_nKL6_Y1^-dl>%0Q=B3h^jA6ojw!rvTY;Mrv*rryEGZUa) zIQfTlA+7iB_S&#myT^DnWF`(GHk9F>h@A(bcD!lvOrG<_`?H?&FX0ZL4=sW`x5T1h zo{82ul>7wr@(4*}6Ehb;6T1MX@M|HS;nunM--#e-?SLEYd5>@PXR@WB|6;r}iFtws z?9(z$-?6`y3!l1tV;GLPB(6FkoR6Oi$FG^s4Q(;X?wFvfaxFpkJsERXjt)W{xaqRg zkLqdoHwPSM{sTGcNx~pbfi0y>oD&!Nd~vs+ZXsDzk*x82FnN?o{H?x|Pd`Vp$$qfnoolLfU8(!Ld;pGhl z7n8l6O|90u6}>^&=v@G9u>tQCCSrM(2Xf21Dyb)DzWH;H)tzFX>Qf1{NJ zC9SoPKqM;;+0*k&-~*=e1!ZzY64nPl6Lby9jhrd+?spI26j*}+oe$dtXz0mP_Srn1 z?jurGs1kN(ElAp+HF>`m@eRIp%fTQ0@ZI({v?2Kz#gr)v zr^$eSn~?e}B?Uom7J2CjY#c0MuLcJu$ z&qo#{yPEwFt4CGI{yj!vMT+u*r`QVKm{?Q#*E@Dt^Qq3=2vzLOHGc_ZDN{)w0NY9C z1`ygf09rlZ=-~#`gj6_QAmOrtnkbOqc%q5t0*#goE+?y@GH?!2D$-~BHil8D)5*pR ztNaFuXA|I#*1VRzTN!&x_mLZfFFWt}U(Qd?T2$kcujvFSiHe;vm5(DNROap|KYNeV zdO^KWFP&1sYP`G{Im+$hZkv{mc_{4*POVf4M*#kO#E8TY{7`^fKdwpt;ELTAHppM? z6$9eS6L9i2V)&C)SgnC!rcL_;#)4LI;Rz+Jm z3r<7))zT-khP5a2pRXWA)@3xZ2jR~)27Rz0aMoK-5IG-^7F-v##s5q5(!06^Db;FS zJEoV+39b^iMdtHl8i+9#CJi*LFUp-bLYX2K8A_a`jf@qiGL<7WoFioE21YgC10eij z81-% z!~0|C!(uwPf0A(`N!2h!D!ul-FV$)nLh$71F3&!{o9V_MY*`jpCqPVWkvwnp?Y1)o z>qz~ZSz|DP2~JNVA&X(RJH;wX$z~I6;-vZzq-CYojK|Sp*|co=UNV$E^qv%}X0!a?zTSH5s0hF0Ap| ze}Bx-6$S0;XgLT^lSYepPhO6Gp&9QaK6m%TdN5Y?g>qQdWwd$mbn!ma`vwY=jsEEa zg?kB;AIBl_R_pTi3`{yUmE(9Q?P4&fPZmlFo59%GI&eNO1LBlW4XQIk$~FH72h(&@ zGpU+2nw>N@oiC^iiv+lBYtCq!$EPr|jO9(D#_tm=LqIZFwJ$6YXN+Rtf7iv2j*3!w zCaYh-qGIBCg}5F?0#bHZnlNQ&%!bZMQO=y$@l2VH2cxzJg~%02RCno`kaVAch586g@Y?6 z6AWBm3kH>0`2VK~-cLFS9TR)YZg0^X9>O%Jabs$9x;?q!&*q}URN`!{7kr;Mz#t$ZCPl}Nb*ys^}T+X!|Hk;DpTdE@1E6c&_^`9cAt zVJJKQBJfo(J=^PlHHs%3;wFNt`PYwzDr=~x#5rXJX%m^%F7fWO%9S5kV$tKboh&ZV zUkGJ4QmNoMQLy=+T7Td3gYmrs5vE2KneUEjgz=VXeSl+1vcGV1>pm`ONPA$@bmINR z@-hW8uC##`5(>tkb``SuFrI5+A9k5`RFY{1{>$EkQdCXgBS&m+SoE^AK-^q@!P|zF zFZBv@pR=D|eGeh)*DBQ-bCM-esr z%}=-0W03TZfQZcnpP5dQ1`T9ZXK1;7{7zf=$p^Y6bQ0jlx0dl*$c`4@r1pmfO_A=q z(&ZWxo8-5cnrY98YKHJU9l5>z%&KX9T(4Z=L!7e`7~z$mt?zQJ}heGG1#Ju1hAT`}d+z4}u=W;?8StC;>Wc_-^h`hD0` zRd0n}l=$GhUei%GqyY5mOfvTgZMC?s(9U_!RrTltWT8qok6p@oF=%&m=rGe@9j5ru zlUjbh1Q*zSZo8D{uG!L<5>gu7jq}0wx<{9Yx_{#c`sPTaaOt<2qxsvrfg<`BX>Q58 zFS#okpiX1&I-f__Sy`y{*WL4V7xRWyzBiII6T8FB&nOoQ1-TxvXEmd)i!fL2fVQ#iU7m7M!w|i?9N?}cOLqXUKy0_ynX3fdQ#}A+J zF|(I3+`DB7Ip%{wCXLguqMIF7*1D>ru5q+ikl_+YSiXH*;{U`PC^xUen^?1@iSLv$ zC9PQ8(ujzAXN;t5TPjnC(I{|Bj-2rN?L6ne>RrB0%htIgG*kcM1kRB1s8t0%M$&g2CjU7o|3NfOPuMqxvByb9)ditQ)8#xb3# z8U4w1yOTD~w|LK0f1El0=k^zA=40adPp%i%H1p)f^B+@bI9QWCprH$43hZ_BTnN?M z@1`(@`t)9$-@)tU!Gh~z(k~4YRS9%b=|4*9G3POrkO;28T$}F-t~)8mJ@3`|_*4Fp ze>dJfgsxd^a+<;FmUwv^LsoW9sIRe|$sZK4bpLxy3fx_Ps(-p*xh?F+Fv7-xHO#pC;G#U+*ycA6?<%UwqRSC- zHDsvYR=>8|HnBUOoHHdzI`z~Uq8=64W5%6$N8jJts#Y@2{k04xU0~hhyk9~0lj+21KA2RI ze5dY`!_DfuuI1;@ml(OPcC*T}KR>;E-Tr;N!H^g#zL_ikr@P7 z2s6+f5M3kyAVmWI+Gyn7R6Q==h14SetI-jjb}2Vqk*8PwqeSHkR(3UDnw*;=!GNdY ze(edJ`CjYd$A&(Yv~Y49Ut)(wxqx2XLaBd1hXs4&`!1%i`NC*^Ws)G}l48t-dcFun zUba|8qNu{6dyduT$1Q2Em?&5_YcNxh(&H^7r)&$|aaV7yD2e6O>1!-NgkZB6tEoL$ zGAVXqyFZn8+zaGyW#(j^xzcu(dUnRz<>r+8x#jK**;*?Od)danz~G{dUodTs=<*5a z|0TbV@Qhy5t%4;Q;w<}+;o^K6=M=p|`2%#?c#)-Q7!s2{$TncSivgq-LFQ z6DkTiS-sP{HOyTfV;k}`QC5qZwR43(Xc26(pWl+1a>1XQ4#H2`J@AW-V`2TUz-=`Y z9DRJwagH*2-3PF}>4A*94T~VrMLNi@U}@d+mo$Ix=mf~)@=Mu5TtX+iJdF6xV*i>I zvUQ8uoHiWVudP4Mq(Y^&5ymAZaGpU*Qf%eiSRtD~RIu$u0LxO5T<`k<1oY3!d+NJQ01`tQlc+N z6}J-G%F{&bhtqxjT5EoTms_44Z$61#iu|y9L(M&3*9fzdPd3MTi%PyGJBwtkwBsJ} z=H}4J;K`~^Fe*&C^rHihys0=-Ah4zpNtuhW z;&jfXRz|SWnDFpO^|Z58djw|Z95oD!@|gVs1LNdO)uYv>fGI-Vo^+|E5gT*S+mE71 z?S9$e7-uK9*Z@9*@(8e z>gHa$Y8!ocX?2g?ke0hxMDTZm;5TtoeV)|P>;LDDk47bXbnqID%%a}NRE1JU89U?%Kk84azpjn zz1PQTCObtn%5-~qV@O{m`TeHVlqMs3GW#{(euK@AXW7NEAt!|=S~x=eSucl21a}=7 zcXMp@$T3drZ12o373+k=x!W|M9Iz8z1kj9N0{aW)9Xo^OhD1G>F0 zY`xpWni9AmD8X~I@2aYE6M3CWN~QJ^w;eIjHgEG0dxs6Oksf4v4CwirjB~?E(HpX% z!!NMFV_OENSDl=PE)z4Ex~^{EQdJ_&oAO;{D#I7Td6s!;9W;NJWB0uFF+n@iR>R~h zG~MhXmYsDmsdtYJoyUnQUFq{?EdAH~D8eCL-mLZy5^g;O&CgA~NZ4NeL-D+NH}cNi z_aw@39^XbrxTo&=aOC6|xTc<_Zum)Bd(EJ%KZs*t=MTI;BK_OzJaJsv3ZGxCN$6iF z?HzuVzynMOaMa)OmEh5PFsJI@&yXo`o^MJo`X;AaR92zRr7k)q z&WkrswD6CRzgsJR!o*MY4F@EqVDIJx^Jah~AJE;*6p78yn3=6!gm`Qch9xJkqOrW* zHUzzu#tERu_&D*G$OS0#@LxHxS)elSu5G!*xxe{3m^)AkD3NyeaezaH{@wxHd`}o| zCll2*z0TRh_rnzC_e)e9(^JF&e~ak6C1tpd+P;uEv|^3eX!)}Zu|;!)A5=2 zST6nGN$!&|-Q}_p`RibJZmSG|k{+J;_{C1v>LGT#{71bneLbRq>&G(yP7ls*t5SQrNF^8Vv`_b=q zfh5b`ku*I`!Z9-&kOS7`>W35cpL#K?UjXR4-y;@XfAao*)qW=E^-|(d=a_S_zL)WN zrC=4hdH};&ixV}HPxKgTCLiyB#3A_S#~^%jlK$dhu;|QFI9qR17DydUiDL~QLFm-k z%c)I*0h8MT-yoD+_@{zn=Jw!+f?d1W&EoeFTvXO*vMB6w@aY_z(T4B(EofB3%Tx>7 zt2Mhuz^p&{#6wjElRDU++shRHy5oid`ZUAg+%C*JaPEcaqP#unWc=d{H$BA_Xb)Ii zi>ET(4Y}4}HU9ph-ArnXaQ+qFooGHWd@6}K(NGO6diJ_31p|f=hlOEFJk2cBa1cUX znvFV+cNDEqs9AGvE;p8%hVx4TXu<)FHn-W6`QlIt-=5T{t%{6J0*OrPvx@WC>F)eZF%<%t{e8M(a$~;_tbuW_iFk5e@SX! zA@|3iGmx4ja+oef4|p-BIV0qf$rKJo?<-yovO#~p)RoFqY)?nkv{D$gn7ADnPJHP} zNueq8pv(NrQ0k}clH_ZG%szk0GK{hH#}e)XscfHc=!3$@8(^8u!(2nD;vsM9bobYw zv*7m#wVnAd-rIMTf$Wf|fE$Nl#TIT2v?j41To!b1BlazIz%CeH)$fsewSH+52~l-v zM|?||KUlN+qjn##0Fm-qZ-e#ui4`*y4&QHf0oyd>>84qRTnnVxKin39y;~t4=3JK$ z3~cqs6XhH}Z~->{9S1aqkGNI}@g5T(d-h<#C3b5OIwmY{^$Ezuj<~u84m<#s`le)#EO(5Xo2svi{`UC~FXR3(<6McP1Z94* z=l(gGAkQSot?Ep3q(SZmPX1}?DSp)g2pKoZLyYUy8Y4&}6jxlG%9hfh!1`Y@0 zUe6=k;1{bD7Kp+BjaFH+aSPzgvW_XU8nW`~>1(qS?UdW{Hwc+IYwN*6ahYZ}?}t%? zPAj39;sD2B7VUWo=bCX zV(C-WVab|UE=`&k%hsFZRI@9OmDgSOa+%Ea#$WfW}VRli5xqKr)DzhH$yh)0piHR;5* zvTFS0&l@%$n#*Zqab2$fl5%Jd z+E6;CFn2Zi{FjT>cUSiLT*S2&K3E{PD(d_xDLT3asz9AE;xRb9zkLGzP{4i19m*i= zx@<+!Hz&69K;3K<(1{-#Kc*JW=%u=qW3DWsfBu;1HjL0O@+xTyx}O|r%Yq~$HM=35z;{3O zdy_N-{G*L_1bi4(0oi7z^pAGWI%B8s^qmmw_!S_RUs!&Ab3KCxC&XT_Fxv^+4VVL(v3F9{} z0bDK^J|d7{*c8y~l-d;=OKT8{dl1F%p^|A(FjO=%+TQS-Jt?n#GsSy^+M77|)P%$M zbDzuypJ%?+?q6&Xv#(ru?Nf=&7JmZ)j3sepo?jOoD5=c6y88|%x;!Mqkx7}ExKls0 zm%1w~&o>8l%-LYY6Bcz7gTQeD1rFUVqBxoW>$2|Z1vVuWRhJ; z1rIZC8X3bU+p(F12hIy_G9Q|ZE87_GL84}@nN3d{u)TKrza|B0y%9l*rfigzLuGBbt?W*~@pqk_x^4;2DDG{hKp;%ItlpX1jn0_pGEQj!9#4$w@Gf9p9 zvLiTvE!K$W0%wr1s(sere}{`3h}T#myX}?~|5i)8(OJy9T>N8f4QQsQ2a1mS52Q(d z@Vz1NzASYngEu{J8!$T?Zwv(zmB>tk0`Q{~;yfoBg{i(I>;8>ZpnL z3A7by3>ITna5LMok3%xg-H7^5Q6xA1c_IJk_Tu>D&~en+D@nG#b~%jyIesm?x#f+3 zrS2Zo65b#cZYiVDNmwhc7;%tB^;wFn%z4=F^NCgE?Fa6XdP2)XMs)zNIrW`Kdvt;U zmgnx$XRmIaV^h^?% ztJ$0JS;@nknr!%&ZJnXndkRK%H*5@7h>UDI`nhAY@@FL?E%X;M88g-y8MU)3eskYO zL0cEr-@_?1Gi=}&^GLt*ty#=ar^c_k#@eI=5$2`qJo!zU8JE*?&afX3Fil%JffIjF z+~==zoV5Nk!)<*Nv3ULB;UBa=YJcYl;pdfn)5SG2$tyo*3vp(IlW#Vgxt+Zu)6VbVvb zs+jnspPHmxR{Hs#3&LD!g%vQtC@I%PTKhr3rnADWHJ_YLpY5|&8cF4;0Rz-{UjJKz z`)BjM@7{cuZ}BBfPX9#Fqd3M_g!?-?wGEdu)&P;S8|Ec zv@~Y&i1bcXx1*lXN$$n=BNgXf57#UIW|O6w1Z_#~3m^QPN9iNBkRTyoZX{!V#XHIW zxdbs*h#B;)SrBd-Q7ERSZ9yYNZ9;mQB`rT(k7S(LJyeaf=kLg?_&?GQWY;L&P8fJZ zpjr7o(V6RZKfV1@m`RMqnlN1~&x=w(SITWq0U0q{WBh`c?L)@j0DUP>h_F%3gsw1N z@Hicxh9dCWzC0&r@fo>>(~s(LTcC)Qc3&ur{|BUq!*`goPsO3re~X`K zuMiA^2iSb1Z?&cEx%%=i{h}GmK->>J2tUHTSd^6wBK@eaXSW_MFn#BkL7oRWLtra| zOv5>@;bqnF4wmX=;+%i@_m?Mi?XHEl;D0&i@CAd(?5igI3YjjkROaOtMYsC?uA9}voZo(2A;Y=Wn@p%uad{83Ut!66 zAgz{{*6h5Y(I34{30VnsD1RV~K&8MLz5;~8BZK+~#StgGrGe7*} z=4hJ|`5x>{Bnq0Ecrt0%gW7BmdZiT@%rQzj-GkMn>T4+ZKXmXcYKA^N#!>%&d;<&z zsPDWQ{~xhuM>!n0J7n%T9Tp6WfL$@i_fbM)=mwG<8397%eS zbo7@DoQ3M8Qy-o;@ytK27G*6;M3#~8cVNOQ?#doQpYva*v9j@VIi{#}@I3$D+~y(% zQ;TPmGXxs_(MuO6;u!$08u(qtX`82}Y=tm6%vUrY)07230`aE>Ttk+$V0n&jtlQts zy;wNfG%v1HHS|U5FlQZ>q}?r`!%V6wJmGAr^I7|xpO$0Zud^sRY{@gufh2-&2Gi}1 zYwY3HGN~%^2}ios;qINcy*b%Icv{M&$AQQMZ4jm-CXvW-Gjj10qSk{vHd4U^J zygsN6u4gyPI{nd_pZtZ5$`W!jJ*ni*DIHr0ZYy*T9nf@P+1F6wj^a+z+E_mAioU#!tI{@3*Br%8A`&%;XHcjUkoQ4v316%rYJoAs;0YNZ=3fesuL1iKL}LUpw7ypJ{?0uAus2HqY&<0eEn}*@1_uFjwrKw zaHLNm@nwkIJZkwYr{-mPi9PVIW%s~SwOpxo{rR;EOCvbLQsc7F4-oKb$L@r^YG~7{ z0{X(UV=JPz84Gm@BZdmb?qlmJIXwc7efBz4y|Mo#(apR-r!wY>rOs@6(j?7d*NF9sWVrfil^-_-#3>VR6!0v5Ph@}_UqIShhKJ?*N#@P1*JFf zs!rm+J^ca=_*!r)kMX}-#z~XlC1W1bFjEo1zV~d>{eaI68dQSZSkFRcpAP;CAjgt$ zr()&vXRu#ebZGW;dP46%pI{%>;Pm5ZXq>b-#inJRqnh%{<6ZlMSMR@yi)MH;@|Gl- zRiCLrDW<-DO6b_V?P88sy|y!Wl1fF^pIY|ZLfB=F0oi1mJI_dI_sDg!Jq^HGK~9g1 z1Etc^l#x{rr{KN3Nsa9M-J=~Jg}5BEKQ8_Izhs_cWT2ZGnV*sfXgNRiDz1LCF~u|9 zU7wQs#@|=h;&?<1oIV8M1Ik{NJeX8r`EWZ&l~Vbt>#u8PIA7_Zu9M}Gg)Kg^4aV$i zDKcNojKxrmnu}^M=BzM|{*8WNqs2tHdD$r=DtKJaC&hz=*D~ak!b5iSb1{1(b{9Qw zT7Ma;8#P|r5HxuiuHYff5?}A3;ya*-!STQh{*aaRD%h6tXbcK}@5T{Cyg!k;OzYVF zywGfNeZ2$ckM1s&Sd6y}0e>s?5VMe13Y3C6q>0UO;YoHGk`52)R%zY5$zC}oS`kSm z4jQ(o_0fy?8%GsN`$u#dh@b2GlPo1Nma!A@?&UJaAi>d{=SA}zq`(NcGoqZCWeS)X zn8;mOGXBRKh6hnO@s0VYql3S<#G&1gRIM)c9~j^Z&5}G;FxO9caEbfgn^?Luw-2YX zYMdlQ5{{$RzCK7Vqgxek=h)-bq@A14h1@6U&d^M~h91FND8>Z8xVi z_VN7AH*QH~-Y89(noYNrRm)h8{bqLFvck>gBdLCj-%en6Ezs&RPw#mbvDzS7)4CPi zUG4M1S3v-_vdkYJhZ)*!D6K-~jNnGNRGM4=o6qOQM>~F_cPf<0QTb9Ht1|%B`~jDC zBoMt8#4sRKL5R-(;#jv6P46q(>W3InPjZWB;vksRRbIm2=X1Q#>UHCzO*f1P8~z`q zKTIx)^f}vQi}rX15lcj|*Q+zNOUu0mL004MZNJG!xC8MtT6W((dAS3JBoE$t@JZ40 zV1Lh+E0mpAvesle*RMZBUWt#8J!V8SAO}ZrM|Idws@LDn_{Pl zso0)GIepVpD^zRwdFzpJWua<;>WfCL>m6LfRr#M^HozJqr}_YJcv0nIzPwxDIxwK@ z-VYjj|6OO0amVKu_}sVu1GBNqaJ%szgi*0;%B(0-+y^ zoI7*9bI&^MUsDG;ugbv^F0ibXvt_pefZCD@h!n~P{WyIjBlw6 zx%5PtOF-D4ll5fY??|Q~o)VwWCq9G~OO;e?Xf3@}Ho9tLl%3_oWyq{t0R->vFYv`!6(fR_M6)6u#M(+7S$BZ*w30+i7V9-_`D61ZSihzOu>}qU zW>7V)kczr*z}{Y2@K|tbJ1h4} z7Eb_VE26TW_97Av5JpxM8>Z&~Vx783D7k^)f1$5+db%MNTnNVz2W+qbKpf5+1Rn4` z7f;{&MxZ(eZAsd*$4XoxC8y~5ZXK3eQdD(F&EjI)#q%dXz(b`u4skjZ9|2k5V`L(Z zyMXf^EfW;8btNCSjj9o^%}NZ^uHjH>dqCrjBSnZFdT(kPwStrDlFGpQ>}?wpVHed` zDMlJ4b&RfWKH18q>eN}b2hm3hqeuy%AhqelpWH*_c3YnOO=MTVs`|!rYfCDl@Rp6N{TKAXW6?i~CrTx*e7g7_*zU0f+ zVd*s#bwcq}i|KAzKYch~OC2LBlcUYnI$T&iEd;k>GL1w08Gv4;DhYw4+8W2V0FKmQ zOe6l|EcwE4ow3wm$_8{xmXwP%`xgen{>w$8PS3% z+WThC#=rL%E;HB4{s4vhq6DT*_KQJT)63Vl<#;QCOb6RKo|0+TmhB@7SSSnm~E`wMME5kKIlhiZ(}c;Z9Q#JP51fyJgX`rgn_|o z)SpusSZhahukoW}`eBIQZ~PnG`$eQ>FTqH9V6m$++PNj-pMH) zei0^WbzT<{0;>pmc2{25z;Jj$Ko ziL~(C!zvW&5qY2+!8K+&oDo$y%}?fF+z&BBv%p|FE4m>_PMfS0X<)-NsGQyR^e6G( z1F+fP#uk+qTm5#e*_03pQC*mop2Q}8hTFpG6>S9o9Y`FU)KKg4R3)R(-3LOvx){eV zUR4Itl(OE4_O>>m2QyzPu(Yxi?b--7reLD`->Vjf{AAZ@4=5)~?7KtPFm+rZIPaFC z$t_L$;bV8s3f~vfzk$HoBTqHI_EU1;(%LWWW%Nq1^uDF*B>JVEmN@vyg4oiHS-T`-zGow6)tNcETvSM3! zi`qD!$@|(l;Fe)Fgw8;P*Do06uCRN=DZgt(RG>a#J3R!Ivp%?AoC456n}7#&8hB_% z+_K}CQ3Y~J!sh=Z+3>;JIN{6e zy)?7>`09-nt8-N1f`j`$1R03z#iSUJ(RLeCc-wxM?f!ZIP&Ja~+_V3jcwSg`2QQ69 z*x|CrB7rRoggJ*-K0KtvKKvu339X@L`EZFMKKqD&ANPIt@o{?d2B^OqDO5{eS|0(; zf@Bo}!d=k8l+2bP2Uw~7?^93(K!T#Z1zD67G*Qi$5oxei3i@ zc(&m&IW1(Pgk*#nx03dtFS<3t7s#J$P7Trglh^)UbGG1Z(-xJX+J?Tjs{B@AWCHf! zWV*av1fE8Eo}1&cWOlyt?r`VKvbD8b#B**aV!8buh3=ad^f`e&*CeTegV^m3-CtA- zF$R+%ozkDL)tMD9nhDw&7s(qBFMvGayR!mUdv4=del)m@k*i{CpHCF>N>RZ6bZeEc zGRZ*|A(iZiku@|m2ryYERDuuh60=djSlIkE{2hu z*`cY$c6s)fLo{FWn1##&79z@-%cW14? zhk$&}$=m!QhF^(@-ho^|Ku}@@j5hH!w~sQ**O*FGSaKm}_7Ko0bVWOBh}(hxWdpqY z(A`kYuG*}{Gx$`A0%8^$8f0H?1cqo6xl4O(3_nS|?E<+3z1ZdrwHYVX`n41=(W1K#|jlu~TqPURbHmJo$R;pgXY(}oPjd~Gdj z=fkhz6hq0St7v#t=EEW&=p|$7Z z+V=nn%$hEae;XpuH}m1}l@ijzL}LL&gn+4<58fpXSI!O2_t;(czAuznJOA}M(eaOg zTf@z7tSqGwl|2gpUhFUsBua|VZV zTTM?kE^htGy*9w+K_3n2)a@5Q_q?SwOTO7x zWh5WUsu1uuUOuNAIxWrTY+E5)bh020pjqq)f_TTVm+?wvODDcgSGqjO2ourQraY+( zzXIn?07mx0-MHHX=PEEzFf_84WN6nP1dj6$*9_0$|0@X?6b(SA;7ZSfwvDkG>@Dk1 zNz&p*KSAp{Wuu>gXPuz279kIUK{)LD*01)RyMzVE6xz~cxeOay&1vfFM~!C}EL}Wn zg616;2FM2q`|?4YK$fb`DO_3|J4x{|0A-LNS4`vw&4L@5Y^AF_%VW{n#v77yWzpZ2 zPC0P8ek-~xUNVxx*J-R|cJMPv)%-)ad|iQy9E12 z$&{Bg{j-={KOpCI!`>|vv4eP$&MY(>eEcX12 zeqUS71wAF-)sPFD@84(E8?6nwOwP9ioz9USax&WM^^Q{W%x{aTMYf?^@{Aa{E)YfB zuUFJkdoO^S;YazHnI>z7%dT4d{p==>s9hYs^FQA;m<|jYP>PgI_%j#?B&Z)xtO!(< zyM^|9lp?EBKg|nO;IZk$4)%cP zYI&{d5R5s{PSz63h_QM_3^YduE4brwr!xGusS{Xy4wQ=igo=7T1cFGRzovND4GP4i z!ucJ+tY@BO9e|l+sl#j+;2Frwrc~!6zs!%Jvc;d0la~^Ybz_ z%ROfm@%VK6rpKKw>L}aOtg+pG&ma)iFKserLUDC}yJA0R%iP()i0X+$4&?q6^(8Za zH`xVR$jt;no*zQ-#YoEtF2UyW4cEiHL8m!%jVE>==Dm(BU-y_UwTv*odHxMj#o`BH0Twk0$h=UISb?x;Y1ji()*xJvw4QYZsZG;KV?bj-`vTcXE2HDor1vb_O6GGuAA4vAuH=UE?) z1kgWS`StdnM-l?)z4syhKMrics4y;44Pj5dEtVd4$Umt!QAK+ZBd<@KzpJlejC%>B zOwV;cfy(B#tMZ9AZDCR&aV`Mj;HT0ou&Dh7@>OQf&(>MfFo-VKU;gMXlL*~4TL%6J zUJvU<4%T=-UN-HlwJ_>095vu|8=AR+sJY-Hh-e@TeQ*7S`bcS0xsdzbD(Ue7mkimy zvdOE-Q&~gMfLj9;k-(_T{7{In7e8tT43Hf#H%vW2sFQ;aa$bpO@O-QG+W1TfB#7zxd60D! zgMkwaL-B*#(g8zk1~IGfc06<{b)FF?x*wx%bef3uf`n!GF$H0CTccLyVwrV8{nUk% z%@gE!X;Ugk?b>VJx{nJ?o0(`C1P$p2J072>)QdP#eZNDfONAB##$8GGhTXZ$#WOS@f`Q;DiANT0@zr)S&5Yme+My%hE?pK9jG*&LK7yOYRI4EtzHQ;(|{d)aX z!ci^mg&|$Dyo`tWb~x)9vYM~#yys|VK~5_E<@{|<(8|)sDV(4_uIJb$4H;63{6kpQ zTnv}30O?f&?>9NaCpjG^ZZ;x%BhRc(J^d&xhdIGfM`l#$?&JMCUdftKS)t$Bq%mCe z9}GDz8fctCE}!Zrll9647NCa7xPfd0&?{KK#%Cv^BFTr9F)nM5uw4D46fs}<%FccV zLZS_CEnDgAi2he=d^BWVy)8p@B3XyxHLx5Le68Z9$+e>YwzskWF~fHY?-~uIs9CwL zCD<#hc|2a^W9r<4_Cn7aK##q!szt0B3l^Zm2>Y=^H(&K7DBU6*fZ*9$(PDA+vqpUj4e83Blxq;($2x8b6{;#TtXg@p&lJP(`O^c>rS#A=JW<6PhJcq4eW zA1}R9l+tU8_&(98-FnVhgYg>Chw;@Zv^2%#PvAn$;g>-=R<0{(1ai&9-4RhD>wR4H z;WhFZ3e?nPH1vK27zoF@Z?>y`1UI|+aK}J{>`{skQM^$_FX;vlpzC4+&J3hTW5;_m z_$W><*abK1o2KlW6g8!+x>%uRfr+4jcej1lCeB?xECgk;$ze;jfQa-Bk$U#o)!`L3 zsHD`w1QrLhXT)}qt}naEU#Nlq|F>Vkd-d`VJB2bsiV@D(;{HzSKDRiF%{fx;ddZB^ zRp`dqK(l3HQi0~~)+`>E))NtESy*)0{69#C23&geaarAg&;j#Hq5y41CoCP!NPf zbQ6I=KRWV_2P;7h$FI{*Mz1?UH(_sYy~6o3mr?E6PCH3D>*DbkC^2g_<7ipE#t<-W z-powqGX>)oQ2B0Qi5F$! z^i`kK$X+py_l*i|Z12_o?k1%(umuQ6%&L8{7()Hrs~Ar^+xptRZvtZk&7UM?QKrnd zUSKT}?SeK5>h|@%Wl8;MpLm8rpwQ6`ud^nE6tGnj46ZQx8r3YZ>%TLof|(OPcuF-s zM&H_G_u7;flm7VcA%;8Z?75g1Oh*kxB@PVp5Hd3BK)QjeuAgTEO!yuB@pJR0 z<4^CYG6=e+yUGfbHQivQT;XCK&2~C^3j&y^z!qg5clGPPPiCG`TZC5Si=JudsAn&< z4Y~hf$ZT}|$cuS_?sxI>+CXm0VE?box*xvmG>)q8J}++)<@W5b;ZY-YQhC>+@a+-m zz)_Vm0+_B8LtmTkG=eEGv%-RMsJSwB|*D6iIc`-fAW!__4SNEFbY#!g<*B#5StV zLl<_bP3HjuP}KJu$np(5_$z*Z@*xO*^sWYQ~V z4&Qx+sM8RYBS2;ltaQ>&k+`!87vCd-h;KCn6x6m`jG6J06TZTKffNuHpCbccg3oD( z0*CN+)q;+dmzDcSKMWU46O=OKxP4B!F(w5$or+AE$-KBR{H$-hFqTd=!t9(FMR z-{FW+AA-L0gp&VL+jQ3c{=z=B^mf&Wvo}8Y*=j0VDmjiVR_gcNradA*NF|@bTs(M~>!jZt^i9k$p(%OkZd8}- z-=ju@nIe%15|a|&uNGos4PWn6Uqh`QSJix{>r?vlvxn4CJ)LZdX?t29=2VDWJ|xj} z*+VcQu@53K(k}#_v}>-`J;BpC0Ob{cnLwKm9O$BrO>k19=iFUPp|r;ud1Nx_8UTU zJie0f<9M8$!y^#xgmimFB`uPU;+i-s78TzXGhzL3d8P}iPD;s)A{CP_7vBu6uMlZd zt9cHF)sL)gHRV4eX)1NhjZ)nAvJoyPs1!aqO0)@(?;wLyKI=QxZ9~L_yTDfwfN5F& z2+rG!?VXG-51}*%>BvP86BWMZlb&&S%mLc@NSw-PMs4D@_$_^MM#5_HE2|sOnb{KJ zy)OHEs`Hhq+Rk-K%@5ZP_dci0(1O>#5YtAMa;oiVFPO~X->v`wUQmjgdxXsfcx$c_ z$LXixTvml3*B0}U?v==4BJ^iN(Y1fg#{b6ju_xj~uFL|$V%KzT-$I?jo%*`J zJGf8Y6xc5JT7!-BTI8el)Gd#2i9aH&e?BCgz;=EJw$?%beIh>;dn$cZb-@=Sqm!qW zPqKT^F&ttu6r%tx=lk!7nwAI>y95kDTAT5ho>97>5z4`9s)zs7s(;!fwpZXND$Dx9KuwCqLK+A>giTWQg<3GEPA=X>yqG0&v z8)NF$ZLXpuyH&sX;j-x`(o86*~~sM$ITZa_qRN~FJd_M0Zbe+?|8 zm)8y%ujY%auZImC=+t8$H@4Zi?@^&cEVx7xOO_amhqE2QO5OK?LLmM^=3d6RcG@RT zu4Vs{!@d67Sgu?rV2fE*W<|6zZe7jfc8&^k2raHx7W*KiFc{1x04G$kIco@sARFKg z(*g4*-}vW6(b27oH!IbSnyY`hO9$o!GE#GY@3iZ9=&V83lvG2dOmknR?x-x>#AuK6 zS&k5xjZL?(dqE12k7rr+so1rtXfFY)AKVp5d#qx*PLU{XbqSl_y`L1`)GtU2v~G>p z6N4@#Lq9ng5i;IHw1YoBN;z$mR_Jw1Di#^Yx}N`jq3HF&V=y;H;@HF0F6X0Tj~>BH zu`g1q9qG)Exm+q)6d_Y=X1aCvEVuGG#mg5m^n^Zlf1J~}Y8?7?=Lxv8%~ypmS9Q^5Wub0T`;0&dcnZ?@S@AA`3#D`rgQE=vRyBc zlB%r6ISV#);H_$WY^g$r*PCJ=mi9NoCV2H*dAN_05PCp80xYiyxa*^u02@GzDNV^~ zd_AMTYFBi*mT_rrLX7sG#vi;@h}8eGl#v|dJX$L{BEvS>#e+2MHnS8GfHHc!)KUnd zd8|o##7Yxhe^W9mx9J3rqFdNRcQ%7bO@T1P)`M(Yhl+g88*JwBL zv*r7yJuUe@;XS474dP#O#J{W+abiV{Z*tzfYU^Ha-pftklP;6t>%CptR580}$2Pjv zC*SZb?cK%3SoZx<%KfQFDeSr>@0GBXs|3i&oEQH#flRm)3IledheJ}uIWLmS^8EW` zsGdXeY%E%GK#~0uZ1(A>1a<~l@@>XdP|lwpq2AjLORn1J4AJ%9kb3IZF4ksCb>&D4 zz#)zm9rvr^qJ&uj_k}Alp3_MZdRwY(@q6!s2NbT~`|X2eGNZwRB7A{vco4lo*pVFd z+@N=3YSf0$FILOb{_+DpKFmcF!s>WN1oufJ)Om_9`0feg|C2G>oE07{Md4S|N zu5KX6ByM0&ZB$LhZ_{cveBgLF8PEc;Ek`@Kuf3(~vL zPmL%}UcR)M?r(NK=lb!=M7{gndAo&1Ru0lJD;P|VN}0L9fWiG_Q{_lv*W83 zDrYn)@XX0D+`li4xS$3Y8&vqbavyd~-*Osl3ZYvgbY6sZErbg+sAArYRY5$rR$abx zm5R`6FZlgn2Q}d#uU*CA?@gWds-F*^N3uJFu12#g{J+nKG~(F(`6`RjvBlL^A1&&5 zyQadT3kli5gz`I2AI%6EX`eSCeCl_aF0o0^Q{!1sWDHj5;oINP(erX%tk3~$H? zDr;~}-&51+a%2s<=Rm3GblRE8#qr$>ow!5-v*M&%@tNmX64WQJ@Wx7t@g z@neF(7)*O~(br*gO zl-W|Dov-cbu7Dg=o~TjAulM&l(#p1OzBVg3OS-uJ%icjH-?vrjBT6mM+hb^jcZ`TI zTdePR_~VjUq;zRX(A524*IlfxZHeg#^*Vk`;FXJo%q#LccIlK>LuX1M&ct6qWs4(($t>)vfn_a^^Iq&K!m)}`>r9E_}IqZypdmVuX)RM zPAQ!xTnwYi*>cXKX%IJC_FyM7f>f38_=&WML%s$`c|GH``jE(~|t#b3)0gv4~A=OfOA zBHW_r2~Jt{|NCVt$Uv8ZPvnTW04ra@Oxa4&=o-DD&c&mF%9P{|v(Sk9ln-Ak`VJ!$ z=StE$xpQVOd;M`K%+E)^Rz^~D?f~)C$nf*xH%Jr=vzoN&hqnDXNCq&<@&_0{=wqr# z8bGhXQ+{==^+*UDGDU>eYQ>*T{5QKfMT1-VnvK5TMYFeLd#dkZ54FFdW_}(zH zp?uD(fV_bRpp_S>yeXah8gt{y**zJC>oV%->_tL+)YxK=m>#3$M?gF=(pPrh^D+y- zNXv9D>lFU;`RFnPnc5wpUhy|L&fK7S%`Rw{muO`XTmrougNQ;7m#EIhDaQw4H_k9& z=^JMB>|)|q6Fi^JKy^Ox4vL-ce$y(R^mw+9V5rf1P`13Nb`NW1r5=Fc402RG8tvJd zxhY``N6k!s@fr}7%H>xfI$|Thv)#Xf+eHGNEN-UPQ8v7HGwu%&Ob0$C?6gc1z92;1 z_7mgN%Xp?$gynD&>pD&@)lH&k0gNUuJK7xr@81DG^#Z~*Cn=d_=(tB?1)qklNWCO})nqKhS zhpGlzRy47|(dh#TlM<)75`ams5M@OXBV>n2It5p-942AWdgrnQ58Q5dB>&j%yaM|%YtdB~ZS088YZE-AShX;+;Bwzs zL_o8xE0bdTpc5ZL3MNIeZ%QnxqO)WvJqUlmO!~Kcat{nL&{3dfYXuEFU{|(-SL&?U z;vBL%XiKkws`doE;I`Y~_5eV75$7x!8^#(ut-N2OVxSOQvi+S&3f`LnE$(bUC!T=9 z$*iL)TuB0>rm#cf9VgL02lU)^Z}qh-0&z8KOL5NVnS9?xxd_K`!XM(5X(3(C%sOt? za@fS;({*+eH(p=fq#OF3r%f4gYHiw#`^-g9mP3Vh)4A1|3c<@djYH;N=seqx zV4?vCi^ioGlOmx6@qWPKkfoK%%guLW-U98D$FPuGM4|zTXM*hVbpRhh;OSAiD%wEE;Zj`y83g$eHCr)pEb550a z<7nk`SBZKWk0$k0L5atp-dWLZoE__6w~c95@p?U|u~Qr-@>}bRY4#a%`KyV*}~k%AM2~|^LnV}xDT{5z?jVxR*B#XZ-dVJ zhI9iCN{U|W;%c{8a+*BebvoSLhbiYG8ovAux~~zm!w0PF15U`F>vV%2GUKjir`w|G zH07yVCoJAm=bF^EXbEPYTq+AK+JYA}L@>m46*X3wnUo~)5f4=KwW*8-0WKI%f(RL9 zzb%SSAYkQ zM22~+1Ef^xO`vkKG_3620kBu9Rw?sk^sz`WS52?oKYHqUn1)`7U&`MYQfeL<$N7u% zAg|$+di0U}BD@9;Ov4)Klk#EI>bF&9kh z^NmEw%$a{&+KOQQvokdXwt3nB1l&E_1CYup=z9=cnc<9q7bUpg(4fD6)8=u|Tn@f8 z2yz>Gb=~IwfPoSQ(pPD{Oh9E^S=R|ASaX3w+%Z{LbpbMa-^8mew;4T(u_AnXJ@upfCMQw?Sr`2nHmH z2FgFl(jpgZbFMqo=5jV#7-j5+Ud=a9Ha(0lLl5SOY5y1OKojl-&);wsLvBx-`_q#= zN&W%(Ga&;Yl_Ode9C9x9>&ny5C;XKYa>CR)V-%ajP(~vtw zwlBN705M-+6aqXWtt~KAzxUAw5mN!}w<3s6h)M{7BL7`*M+C$L4=trr&)xE&IWhb% z@`C^<*(pWO6;cqj|0 zItnR?pQ4qx{$@3#q2f7|1!If855uW^>4npEqG>8$@V}6y!Y;0k_>9eZQsedSoG5}R z!$1>cg4q5NpYYvq50zsBWRB5-3}}NJU-dsG;6`QRtp-2n))B z=MIgwdS=oY67&xOaqO}2?x2lHecqx^22~iBzm zNY5|`zD9ZD>mktW>P0ZjU=W5V1J-6R=iwgz=yRoUSeQY&3|Az5GR*tckYD-o4OHu3 z@=+ZiK=sZ7XDgZQZ?5Gp?u3=hl0tPVh%xO7Lul`uWq}SGby@G!>m{nlNc9D+-k&$5 zq)D2y+g@`=T`Y^Z6myNVR?-R=J%fiDnC{AEAfPS0*HQc9Ih9eVu;*2Wox-_Z$Q4|m zVN*5AZ);OJ_Yr1HbQ2G|iE`mFSBly7&7>b1o8j@N)=elIidRa@`tO!5OpXa1V~^GL zI_|-l?;!4d%Tv#He|S)`|L!QE{!{FJ^^(7tJ~>uBds*nB`Vn9hR1qqkyXL4bVDhcfdp9vUmV&5!{o%BnjCv7uE@+eYX61Bjz)} zQLZ(zq&i}hVe`|c6Z*LU$F?<6b%XQUyTQ|Pr?cay=_}3fDjHol3b8#lgor>*YfDy@ z^+5K|Tnp*YI!$%i{oOBwc;RGv=2x{qsDrVnQ6+~s4ukGP#m$({+8j9^k?k5rxS9f1 z&8lv2N=QR0!N&G{%h<#R02j)wAsnf^6re}lcgo>_m?FO}y$V(X?d|6w@d&ocKpgEg#7@+nXw zyIPJm{2g6O3U&6A2KguH8j%U^d*vbfg98b&3MyUeC!8+u`b^!ufvYBiy4*;0#?XsltB`mglg@~QXl{#NO-d9c2+x8%AF?w^H;uji}QJJvPU=7a?%@1|j*b|}%;`Q@l( zSt7T+Zr`j9LRBN&Rw8B%cK~z+D+%a*=bRCKAG-e((gC-U2bT&;#MX;j)88;+he9%uAu@#tpi7dLmEd6+?MHkpY=I;mX%+1cp@hes|m!hR!5 zNgtMyoR%Va8F9_S#2NQPw?D3X)zwyDFWn=sIlW((CPLD79R+6N-r{`6Td^1DQWK9I z$H0WYxoV9bVP{Mi!>b)UH@d)*UQr$9X1rooW?LUwV~C1i-*W44zZ3GdZq}tE2gqU*(}=Xof`5}fS^9!~C# z>+hN9MShJio@|{#B+mjB($zen(79PU=7-b{Nur51f%ZFQ?XX+))I4WYlU%`N>C@iU zuh<`78gt#pnIF#UStV!OLJ#b3Isdvcz+dB*xpBU%$fD-6)Wpt$uVJorKk?e``&YlO z1`OVRl_;&t6C(OEy>_3S%TRM#BS6ybMA{3_D?zDsItE|vt~#(-{dpM2&?T70!w+uE zJUM(CNEdwNL`lO%ikOx3JGh(Evoy5R0 zZkgdz=aBA#tETAmj+KquPqEz&G3mmG1slATurHo>GlbFL+5~Pe?nSeEgcY<}j}A7E zB(~$OGZ{4?YfN@_Mo*G#%fqcmgAm zR>@U$a$!AyKSCI#4{UYF-E-edZBC6M&V`98lm72VPiz7S3t=;9ukH-OI1*OOm%^Fe zuS}`^K+rnJzkH=dC^MGh;Vzie1k^MIy%2(lD~RVIo@eqa#g}`^!9k`&FCwSzYI`FD z9qvRS&+%wY;?p6~KaFHI5bE@0g{u0-{cQe@vW2u2sY$>T| ziQxXrx&@csj}oNk56-~1u*ydezt=kyFC9Dq?_+4YGmgRM&4zDXeY9vdVETbF8K zbjeF3SS+@Co9~Oum4|{FVa~DxEI;b>xjOWCh?jNvmc99VNrk$Jj$^E_DQ#VuUV7f~ z@nx+G;=c%pk?adSP>dm_9t+@meSFA^Wc9uc(0bvr zLp5w15<9>m)-T@%Dh@^413iK#J%&N7a$)#kUsY`E{J{evO-lp8@p&}J>v1am@c0JC z!BBN?+C|Hf`0qaoVGp|5w=OLsW+!s*A@R{7v><9H7n3D6;!k*j$|RY@n%lriVwp*umY`@h zRQBr=%_%#NNXmxZ*m(Q`dP##C<@5D*$VgT>&me4i7Pmb=cK2cZy>WdN4>FnOu9I|*!R8vf4PR;P6#p$Ao%Qy=~uO;l9@)*xNs2N_O z2+*P}u3=TvY<<d%egnlk!9&veNb;|XKUUGJr$8(NALx7f++l+q4t1hwgOw? zme7Qsi_0bud`Pl$>MEojdMN9GmMzTsmV?cNFI(LP_c2xNmsn^N$#{@L7J_|4aT4 z7U8vyQry<=)8lN;zFphlj=l9YB)l(xOd$b`WFBay`k3z^pnp>^8R$e zzqJqyaI`iNj+BDX-UMI8=D>P62{u~lDf!aFI_uHl5e}gpNHs$ZL`POGh&$$k&%i{0 z=Zq5iXrXW(r-{S;Zmq99a6&FEaqmiQlw&Z_{hI`n{aHr114;%03-m%lC#GBmGdgma zBSWc#FnFgjO zJuB+Fn(+DU4x@m0<4M8VflofQF-gjzWhy1OaWCYVS4NrmSiivoJsomZsCv|0&iMzo z67_t{#*I>`C#mbtT~(1=7QbH1A|-#7lP7K+fi&DEzjiGQE0^N>xx-iWX9NDAqr$fN zNyI^M^nIt%p?M>OUtmk(khlfD791$t+^mV3FFL*ag8HjnlzXoG3m3gXbAj_iI&tAV zOvw%yn73(gnIko2IaiIUvvptnvmEbAW32jW`d_XWSr5MRYAFWZhI;I|q$&O~Qr|kR zwCqX#(pYzgIpKeiOkNe}P>ckHL&Op~H`EH+^C6;i zWv)^@2k+qE?U)C6vy{=2R9`)~QIBIdV1J<;$yQ#PQXc6$ZJjJ#M^(mMRfzsPV^4vJ ztcYKx;)%SoqT{n$oh@NqN&;eiQkbUa1g2t+p&t@%%-MLkfgk4`(CO>qKviecSsZ;X zeCad1KS|>vJUD%Tl7p^8x2=d{A2ZzxvBk<0Z4c|-Fku}=M`V@X zJdbO;Z0APc;U7dy%&!<6LS-*@6uy>xd$hPb9{;?%YnxYW4HW}`6>i5ewcDK3ThF7M zr9vSeIEJRi45UI735wy^ye^I1^LhCr*{B&d#1=60YXg7nZzkQWpGnB;x1i2mFbjFo#F&tml+puUp;A z;t&!h7N(r%4gIvdBQ$_l5y!gcH5n9(Gl_g^hWHRbiKd8s&)YdgJd!Q)>`>tI7&<{& z1ew;TgB!?{_it&##clWtaJj$Xq&`Go$=`2^{Le3v7bh0gUk;T&cn7!_)RSFg>=}20 z6rVKW$&xEjZfkFYM=jY*Zj6 z6%tTSbTqT($FafOXOX**7!;2NFH&pKoU8zQ)fP}dLnIKitVK+pIR7#ORfiuyheH5~ zz!rZ1I^0wL_s1#2j|g(+9AR8Qg!zC7ASJSeFR%@-&N0Yg#kvd2cj-=#56bxn$6 zSIb^Y*!YyaZZ#b>!u!_KE2T4Ii!XyF$wm&W63P1c|Fw_fpsnZL7x@}O| zby!&(oS1L;uzYzo-@8Z4x4>n1gF`NYLrU}gs()RH{|zI%DHR)2-&pZ77EGEXy$=@Z z3sHjBnKOplBF841y4_}lzGOU1q89w}Y);a<(jQ}Gt(KjF08tT(s;vOy2y#F^IGA?T zI4%NGL`-ExfJ8dX-2+$@(X{L zUf_S|-14kou%pBJ^~n)Y0T5DfOET;;u!~!>R<#USm)u$HP(f1jn}L;p^>+NOZ5C}f zJHj#i$;`LTav8)l7GY0@Nw-bL<4};ZY3E-#cs~=rA|=h@|BI!lrVKb0yt$9{ZmQQHkPl;OyuBmP7kX`h z=;3d$sT+sf=0oSTrDuy2+AcRw!*_#$XCa88t^wnaX)dvXCBvs;f+tsCb1afIFtL&0 zZ~TT=@Dk&`;#ASOxTEwY`H6U*itD{C%PanQ@;r14ZMXL01Wye|e673Q)I67@+=x5n zvL0i+Rq=fEaqA$et7}_c#0}mbTJ&V?&O;Spx*ZT80yp>j5FfKaoq|GQ1Jn>nf>QL| zmuk|lp6No*I6j9z78Y(Ct}elSC%c_D6^=A8|%-ueWsH(JGm*}9B3?}F5?s}JNilwN!P51r{I}5 zgB8H1SPn!IVOyKa#`jzWrhrXl6qZUk9dBc(P<|M$v0~ zLY+^K-YfD2Rs`XGg>!)y4ogk(WK;Cxk;UtjLZvjLER~Y0v3=fT^3q*Fy`43&J?jBh)PhUS`&w3yep{;t&hYLuMlOU#$K+)W7M#1y3ZF0q}q>vjaI&1S~o{J9x4)> zzhZvTCh&bK1}ytF%{LBISRRZLod9EN;M?0agh;*8pA7p%b25zlJpYjQVP^Ggh3v8k z`wv3D4c(?x|G_1KPc^rEe}Ol_vjXOGf74UsMyY@sWpmBh@o*KDMJH)CvlZWlNI(b0 zEpsSO^GZ<47d!px01>^!yO}y{)@4OOF>Ja&I7;3o-siyyqe$TtY}mfF?vNNEYtzQ! znfaCB%PnPXkr*UMtrDBhpGf;@R6RO6-`nc>8>Bb-@+9ioK*sC^M|9aw$3;Rn*&@BD z%X53+9jVmBi3FWg+zJpFpPwqr8ic{dIzTB%$`}2hx1wBk`8C|>e-Eg*8)i<#K;r$o zR}MD{dI?KP<=N>cn`l+G3tl6wzp}?s>_GEE^+a80 z#9-9?Ojhu=fSCDGS4bHhY4C zdjoCkm`APJtpEG-0%PIu9+F$9hic}Lga&ap&Uo9dwg8VH@DpOBOnjd17SFv{i@R{Y zh(My1xDr39#`iy!o_IE96Shs)_|*$XuizOb&E??dMR*JvCl)Le9^72Y+@~?gu~xa3 z94Fq#i-)giG+X9j84|lBWc`l85xXYn&BJBgS^PQq=!cWi0*@p3f-Z?VO*!` zrhxX`v&)gr&?4W3US@+^5Y+u@$39f_UVe+9YY;^=!$2pyP(0td{IOh@nm-oGNJZi7N-puh@#*N7nJKs#bt2sB z76wWug(k+Qc=d?Gun;E%z<$(2M$3qFcK>deV`wM1uzo(&4Gui~yQ>;#&qp^Ls_I;C zm`)h%_PL#u=|gwb1!{J@GAEu^D`bC|HuU8|H2hB3{(>Exj}u8@$%h8}ZK%_`i<_?&sh$XGsEMAI-fB3Vu!cWOAiKzCok> z9@UnLSj|fhXJe{sAz1JAleZMZIJ%$tk%VDUu4)f`D3abw@HE_AdA4G;=gj)<|F!p( zVNq^T+a4Sg3__4lN>IXsgoG$VC@mmJC@Li!1yN8CaE2aHK`9ZG?k*9rz#$GPEgcfl zq0|gW!?zyKQ6K!yb-h2{zc0V$dYIYIjQyB#+UNLAMbA0YVhn(^(e;f)ax4;ja8tP5tp^FV(lzi|5D@z z1yvh38I0B6vP(Z4lAiGKau+9&N88N*JGv)_N<+`Tr4w4Ux!UP)CG-kYrOInIkCA94sE z-R?xPLZLXwxK#GUg>G;G0vXKYETY3yD#E%}5gQXA4V`epQOP8y`_HJ#5@&V_6JIoP zVZobtx2o?*-qeF1HXiJ%Vit&Jn9Dm-{k#0g!aQJBZ5A5cRi05f_AFNAe52IB%%WOu zRf27BLULC?c-7u_a69h;EUek;c6R%ZXI0oG@9)E)~L+PE6K?OLaGqT;8-b z4(G44fH2c>tPM2Y)0jlf9pLHC^heAQaq%Y-oGYSB?++OFpzMv0c3=V32tV$p|6zd9 zam{Q{KGMeS`7+^I>?Lw|U}j7#<{Zb~O!eY?*2TSX^Ramj z*Q(}J3w`FB$hWnc%gEJ)YWh~%7GrY?%++&TZAYZQ$n?6zUSRMIsUCR|ZiH-uqtHT@ z9472G>w|=~_FNU*%0(p{hC}JG;;&So#-!W3!hQ`p^f19J9JeZT6}J#{8OfD+W#vQ^ zbkq`0e!#bbk^-}DuXk(Xzix&6h6F~jB4!~&mixFl=eLz^MWZs)u28Ip?&kK%P?Ed02k-F%tuN86ly}+9{y9aQb`|(D`C97 zlS7)jA#mt+iZXibtA*CshZN_I9Pwr3)Up{hNc;??MkayVPy}@*1i^p(E~(yJ#@EJ6 zTMMl{)0k(7zmKGrT9IG2duw@63;$WC2}Q(=kii(wr=OP@WNk2jN*LyaZfO^4DNSDf zRRtv-ZUege3Uw0PNqp`{4e5+0+SF0fPE{N3f$z_CERPcYHI57P-ip{ZIZmP0=(@Ci z(1yUxb7x(sg!oheurXEo0ZPh1B?Ls=D^AxpIJMWV-?f(p9IiD{E zSSc={GPufn&iYN~$nKlCQpjx9={c0my2d?HP^WtQ`8<4kEltaH0+}7yBl?jJqU=8~ zZ4PiY>#6QHqtvB`yaEOLI_)h5m8O4v(f>d&URBdEkXcT7Spkc+zyOznr<%=PiRQ?j z8G>?O4@2Lirfo|Frm!q*%mAqY?+NN?DExJn)kMXcgZo0ZB)^pqB!e$@ove=VsfLR? z)};HE&IBVaA>d;o_8{ckjoU^7=TlJEii-XMh{QR_raPNYR z_34d5OSa(;p2+nLzFDch-7?cwmXE1ONTiC%NJ(nB`g|b?{I&J8jJ)NpuN#&3uMNtr zo7e8C50JK4(!A-Uo9|DLQ+a9Jb?9yf;M3J7>Oxgte6%-^vcT8p+TIqKX!M9mBL$uWHuV%OX>;7HOA@GQIgX*P9`DxEBub94#BdWh;Ax!T|=QbJ>60ESt{C!3?U z(NF6xmHm+VrKp$mh>S|NGn9u~TOF0tx^zC%fWF4Fl6x|ZcSjK%W;9^tP!Y-1{FNdk zS7#MslB+Y(5JK<)SqYwr_2!Z;hrN_BNjW0Uwe1ZJb5J1HXH;Vv0$*vvAMd~O22bO4 zQjfW;tDq)l%@*XYd*AZW=HyT=jto4~`WXpZTNR>rTE+#TvOD$zBeb_@DPx8|73ZOR zKp!7N)Z~Bx`lJ-uplBVg-u|f6{NnALwuRN0st%RMPug*ocd%*80g#sH<7L3S^*CI} zLE^$*-OCA)UeUJ_Zz^8)(+5@z3{lG&yN!s&JX9N6Qj3I2K;u{LWs5d0HLQPa(9ujR z3cOsIIjhGIkRWuWA1OyF`@leRErD~*a+gFjJxA3XkT;pGJEi2RuK5~wfRFu7=amre z$w9UL@yq=Qp26FgcxUau8SqO-pXP{w8}#B9WlvsIk+I^w=dTjFVBlWZa`|ltea2`6 zg!UH7+qs%7zwWf-_AulF=Ha=F70!aGiw3y$+}WHV<*+SgDJdOdO)t zC+q~H*tK2=-JMDG9nT76a$jzaIWw6+%8gmOaScCd;3+dd)3CAHfT!b_r{7YUIOqvycqfgHe}*x5Kb9Vt!A|(c z{^`a&X9PYQ`bU**P}IMkP1CU?hs&{YJbg)HAk@n!e8$M04sUg^TQn~Z$NIPR`j)SN zsl<7Gm?O+sXKbL}F!A=jdIS#W`IjJuu}l4(zsOonO+ln>XL|vVnsMAtvkMLhWT-5y z@mZCgwy=Lpe9NygQ21BX6P+tW-=(ox{#iXv6Dif<{vDhv9hFV~wgTq$cbvj@Xu+J5 z=lhH1vu^H{EwgTuPfq~Q3M%PVkTq@81u0NFd&HJ5y>;2Ln))P%_;J|?RJ-Z=FDk%s+qCjN#S zNYwGQ>k+Ob8)s}L#G0UjdyH^b<*{8HZj1c@1~Xnc#|WECtRRdN34hND2c(f2lXa3k z`(V{@d2*w$nH$v=)Q~{YeY^)-oLb)%1+qj~A$o`P#E~Q*HwcF$NP@<2K4CP6&5^bG@H4IV9;6_MJ@j65OA=kNk!lzFvc|J@Z zEbC2I`k<|ukezXi@XM%n=3(SE%`_6IO7&qP#8YV%U$DzHlA*TMV8@}0BVS@h{e0}M z70e+Bs#P-WDgYnN;j=ryneI^8bA5YN$W%6mEfB+eStqJS5oXB83%IWl%K_Unkb*Dc zSJLQoH{RC~g)h?B(yM#TY~C|e;s+p}eo8+EC2<;0d3GCb~^IWSdWl2fZ27O@rSDK9rbRLly`*xI9Aw`pZSEZKIg(EFb@2gN5O=7LA zFapcdq5?!qy4+XIT_gDD9$TEu;lYUQ*S;Vpk{(B@$ea?}82>($nn(N`{a})n=5_k~ zIM(42HAAS_Xmsw$7(Q&z&a9B+lMC1O)SV_Qz^%4>b7!0v-hlmg=f-2nPbAQH^395pPsgG#m%^pe2Z)8F`@-Wg)@dt z)t)9Lq%6Db0F3QuCCO)Q6}E%NxU3}raYO#V0r0`{j673sWL7i^3UG;7Qv~X#=w1X# zhDsEn#@|d90ar(F??=4)2uyRGnn!N^^mj#yBDSi?1rB}-WnYI~%`IUQuf0=M_65$~ zY5nEJ`gTdqLpvM(1`|+UMteG(Uf689-Wr9m5U}-{>O;x(%PrLCeqBAq)er}DqDByS zUMKN>eWMzJ^C?*Ab7bRpw=Q3vn+DuS{RKhCDzcMr1A&g z=+#GuH^t9jnogPe@t$bqLwAx&BS{`xDlL}{G^$HWH9AWO9f@;ex&bQLUAp0?R6`cO z14r){mU}9LHA3$*TQ1MqfoWUqWefI+;|->#^sVjy8OGndezxp#u(6+xE9o+(9!N%~siBdLP&Ixc8w07;^X<`rzwa^;%SnqRRs=NM(6m_S! zlP3xRf-#Ou&7aeJwrt@lP*Xgq*ODUZ0QxXF1mFLs${70Bvn8mhpUH2?}G_3>MK^SDj_P2t$uA-){^qOK&pa3 z(q-Cg638eDh<2*-IhOA|?*g`$rm${5*t&89-aJh;kg<^cK|A3ply&wGxu#oLEPc+z zy*0yl?=>f@bBBpvjHauv_kMfxc9@n+ShBpj7ZIu6%s7%aSL<1DsoA!sJV2^tB3Fjo z4uZhl+x%&HiD^iI(?QOgIYgH?)x^_<-C;@gthoQ@0QsVj*|-5ZsEeqXE->F;2TlC) z5P)Pi3%-DLCz76KYgn-0C$`JZ5D*Ks+^`=)2waG7-29BA6*-R9m7hrxNOq6L5Ny!J z7`TKaXEanRshz0h_P#LqqWVPI6=qUsTR}fUK$Iu)Fmno#; z<{T8;@fZ&FM|_{ z-IjZ`;Ff5%ZH|2k6?|3@lZY8N2|qM!Y_a{r^Fj2Cxu;pgnhm5o{GmZ*f;O&6GNLUq z#pDPZC%8?T6K*@f=c*Q(SKFP<1%HJJ+`X{GkWGG;#+F;Fo0kWRvQ#`E@R{CWRHD@` zQ@scb^>bgmQaeFGA%O-VW8%AoLfd!9VGX5YH0rGKywi?>y9zz!{{d>`O-UGt$VZ8f zy-LKV3+&~VDFBv{nVn;rY=Lj^GO(YP;O_Pw(N4ed4+uPvws}-NCK=7cA;SjQY>@ne zpUBno#w-JG@XNQ3uPpuTh2LJg?4E&wjqh6&rbF;6#BSSc`ja{_kY}Lq*DCm6%)zQf zqSBqE*5%u$BzUs@*v${jg|Z;0 zda~Q}8G= zo6ga8u@$Nb5k~5iCa^V7(MJATvhGe_M66@g%p9C!RJTOD<5h{_z-;u z|JA~qj9lx0+<~#sJ4>yFD~VhKe~cVBO`Bj66b z$l`G7r>o?j54DdyLh%oT-sqwZcvCabiww@=yCQ$#ukU9li;u^UvtTU^x)DM+d|ASC z!vDAL{&HRaE*9uFlV#! z7`L5@J^9D<+%{W>AA~jJRfKR%TV@((4|-9M5tdRBzg`iR1`og^8D?Q)tOoNpR9NKu z92|z3+3|1(T8tq=t4z?8>>L7Xww#4_OdGwdfa!T51S-oA(y4|rR6a&}Bt419Z57)D zHxLWJ5(e|p-VyEH`;W;YJcB6NC8x`~P^kC!roQ+00nmDBH7)+lJox^ISIrczObHS< zG(RLC+e5)H8#q8sGgEJ^`_VOUVw$Vddmw%2s5pw)F_K2?fg1N zh2{MmHKmMRT=^Afcd|%XS@nBYP^*ak7QEFHxV78KP2vAT2fBp8BH55QdTR56{3AuE z)>$;5pP?ab=%pDu@&c~5?A;9<7tc!s3S|5<5*CC%$sOB+{%&^h-t&f{>wjOs;#WufRb#IanpX1X zUXCYx`6qwmhnPM9stfJtRpI|(td`ke7Zye2{NGdjbJ>FUVXn(EuGap1^3R82A~kMb39(UH1l{^JdYG{I zB(zVQ;m=zDF~!9l(+m_|=Gw`r)KG|O{?$eK(>)V4=C{Cw#`Up#!EGN) zrVx61K4!vvUoa^^iO$D82!X{mplE7gn~TF@aP;hx^{z0 zG7b=^%2yHD3Xxg@N~_^H*zk5K(>mzJ|`@JAmN8U^&&C(5O4p*utM( zj~+)6Eb?0W2P)JvAhtD*ci~6up;Bm@m{|e1q-Y4M&E}w8Ct38o&T3d*?d#XjLj`8# zV`*UJ*&i+cz8{nIxM)iT8Og$huO@xTac}UE*0;req%9zWPS%I1%{t$PN3}L1O~CH1 z){XCSAf?6I#wrKsuY7h;^^m6}tWHL{C(d;w?T+BJOtt=ebQ@~slXak_ry;Fs=$1Ap zN!};YNg5f(JUO}r(M~)cIS4NOvyw&45i2g&8(xd-LA5}&3p5_5w>kuNf9_vijaWv* z;jnWB7CI*$t2}70$v8$q<`)rQB`5mc<`7O&KmbY*-VfA=poDp+c=+C7TO3txEIX7_juASf zmHa^KgN#`y#)&%8zDaFqq)Fv;;kDkHBnROxYzzMFQM5o^RfHMgVeY#2OEf}_2s&IX zhHSELw$@F(wjaS%2tD3j>jjSaj(};8U&BUvLWG{+q@92Lm`k3Eng2?EPFh~KjNEew z%j~8GS^>2a^Qc0W8=Hh;BHA}g^$L#A-jNLR8>m)WxNdb^_8Kk_&$q`5xLeH`EU5`F zaEWXqGSU5y9%F^j^yM0WfSar#Tsu z^f{NuOfxk}*^3%&jNfvO9%n&m1To0siOJbHWsll=mUhOtv2$Bc^MAW0;Iro@?N zDK&VyUbjm;sPo68xC0?=$}+ z3d>)vYGj?}v^>H&8LG|p;HQkjhXChD!EPgzS}C5}sYf2y`KT{^Qo{RD>C=*Qr-0OB z6c{T;eph_ba8Su$8QJUpTlZHI#sWqtS(T4{3;N`3Ko)Az=~xV6_Z6rx`z23rrER3a zP}$Rab79@zD!(#Zb5sv88qMlDZ$0AIOF+*9(o9c#ANNdJ?r-i0bU;Oewh-Rz1nQOBecymzVb<&*2pBe}1*og#^WB3Dqm`YE584sQMIRl?kbLT41K;SUOZP;#w`@WP*?YZlNB z7g~D%u>ZVaV|Us!n)7UyK%lUmi>-_DkEYfrkiX_IC)MXaJ#g-LaLLLy+IRX++klPw zqlMxY=vm789_f?6n#APq7$OV2m)z&5ps{;eN=27VHZye%4l3>4CBRAS-WG6Pw|&XW zz-6SRCBp_aKU^Y*K>rBjJwD_4^95T7$0^glJd=|YBX*|svp1)CmyWV!Z3AmbGZu2W zm@k|t#8*lty3y!`Gxo0kv2a^w^Wm*bpoBpB^Bn2Ti(E%R?-pV=rW&v>YVCrIS%34K z?I)MLzJ5R`OjnoCC^Ii9y#?iI@`lp&Ge`*06?NpB8ozV?H3M4+VN90G<2^k`r@47< zE{}=1UW>vK@-Qa~ck>;h64u-?y{PygZ_5onv~UI^v$jehErXghJA!KRAYb-FI{}@; z*6eFn*mBG@^oMR~f^Ts43!zwVN7<#Dc0l`2QCUx%Nh8Xi`N6~c-f+TB10Gj<1sPO7 zMJ`h$-M?=>XKrvw>TZTj&H^~J+e8IpM9!5S0+p=vn5{^CFe;d}U-T<}iSAEZJQ@>2 z8W04k*RTI|4>k4{LG9hq>a_IPOSk}scAgpkQ0$W|8#O$GXCoh>laZgvJoU_WT0iiv z9qUbG_*-W!Gu16wv+N+y6rqt&_YzORXX*e4$=m!|f*eKfXnLX^Y512d% z+lUJ56q-5yS@g3%#6SZ^)v+?Q)+k)>A%7j(%qkGyh632~xfJZ4Nwb;3#*rP73}rr7 z%f)B;RSvj?D>X_J)ELI(R?k>4O(#%aWGrBDtZc~W_)70ueLw?fF_(UlK80hca zA%<^CY!XRF3Qn|Q_il7LJVO}KXo`}QMxTS>v0=ijGG)_~hJEGtdK)oc$&Vp-ArT*_dwP%f&S*SeO_hges=wU z!TQ1ED!<~LuNKAmWtq|++?`2(h)x2b3WkH|5S=hed50b+_}k(%4)~>BD-`6;^(BhA zRW`Wz{cTIF5idXM;I(Mvl5KXQ&&{$VU4@QgR6@9~{n~)|CYzEl0EMYJE@E4iMU!=#EdQuIvCtR}|SF&^w&VpYn6mM`&8O{*lg zq=cX*fG$)5tiwzr+tD9shyYpTEeKGyM*rBWgEZMRH)6xF%vxbijS7VV!n7?#6&O2$ zKdbfYm-!rSuQhuozXlIRvH0lE;p8r|yE@uzv-meD98n#RXZ3Q1{*TEszr?;cm`ahv zm|L2`C&q4zn0GB|SVWlP-ht3UnO@Ls+CIt;}|K=_7sATB0Y}j*+oG-P)B6Z6c1H9)|MLFW#w+U)C22CuIhR`n^;NAa!bmy z!v$#EvxUGxF>-&QIOid%Ypi?6AUv+f9r(n%VJxcu6?F^WM{6P6?CcYF9zH8P%ddvg>a?#Ug zDCw+?m!GB{IKWLGQ~s&sIK$YbSk-rfL5ceum|g85MJ)nKaD80Fjju@UL!)vJd4Sr+ zOtYJuN{0x?2WKof1NAW%m6G+ldSFynwPPEouz#hq;$5CFaterzjRd143DPD3b17?} zPkNC$inW6Sw(I5C`p}QZ>zL~Owh)9UZYzJzF;VPMhtleKc2^`Q1G17m@5JeE@kE&l`x@~ZliS(V zUWMGHC0u1jA(#I$JE`pVdK+S&9C7u>1;mLn5EHg zf5P&$W>+G*|G>2ufG$Kg1tIIyu=`-7`pLP8y&i7H;ZtBhJeMZbY zU)u`vX1{D&cc=ydhP6aU(?b+ogj2+X!gb_JYvFS3bcXbsH_4YMdd!PU#yO-UQMw1d zmI2B&E1wN*2mI1ysQHti$~>#UnUB)TzfKkO53J#we>-l)vJ>@Nlur5C#};reWho#^(Q@7!_!-5>XcjIqMX%3O24^?9H9t`H41`70N1Up#f{)D=Yq8Ld;N z&P<*<^;g#U)8LE(oBbv@{B8S0^~tGIrID8oo}UH(-?dcGQayFbhvn3%mv2s;+5@Lv zE}S~${_xbPrDvy3i6)*pMdgxGqbUx)IA@_OFLUbnnl-1f$`-*YOpor;aNjq=TWed~<4Dyu5>oJ@sZgoU1GRD1hll9r77 zs)p6t6~*>R-(tf4Odpp2^0nxa`n4gF^i#pe4p!D)k>Ka&z->>x)O+~~aq{8RDG2$m zg97Z=Axq@f;V$N{lj8^6um4{1lbswN!_WNsxBt$s!~bu7NGprLhO}5nrkh_mnb@~a zyMFZsoP+pnx<{Dza+w+rVwJLY?M_s4s_>Nx%Z>AY%Vm}}WuvrmZ(aAlxw>R=hMv2fxNnyM62Tx?l3k{}xC8o&Ognpy?3j z@{wha+7^q(-Cxqp^6tt)VQgTcseaj9nWF^)qIbxH2kcuO{C#r&EL5ZcCuQw~nx>9V zoVQWRG6pp+H1E34Jq@k@{Ny9jmnG(c`iX5$9!BA~(s4zak}qoC$e{53o)@GRDng4Z zL{im~F>f=*dla&@UOm=0kr-Xb zt5E8IefEPTubld|>j6y3VhgeMI*Fg7Y`L!)q^hwhQT-CQ0xXL}CwwPjCTZ{OCEsTs zT#lZ!%rR!R?hegb*7s8;6dWGztlq>M?{QB%ydQ34{AINta0&#mj0g5#t7?diyzDAB z6AHL)8LaKL)tXpc7R}Cs@^){i)J-_cFEjo!0!Rcu)Gl5`R@0dSHP4`Pr}ukp(OoE7 zR6n~{>a&aRop+|c9Of=23b$Q0;&}gb{@8dNkYGYsxRJJYzsbjnHKiBogudsDp`WhS z{_?TMa3&1i8aXx-UFp2ff@t#j>CsVi(a7TQlu2c|}#U~hLeK6mcIYqflCDjL({XQ#6dW#W&B!Zaei zpC4*|W4k5s$e$ql9zS5l%_}HAA%6dU5O9Jd74l&1eI$o|5%@giPn@7*=*v=!NmQ|U z^5luX?Jx?)8q*=}x4TYPyi~hZ&*ZuIJy)A+Gg#7XK?A&Wb#MKvgcWg2hYSqqB%}zh zWxr)$N{rw2P2pVFnhFS49^8x*L@J}Qy_;p-o3NNG=bAn~d7Y?&D>AJe56B+E?{DMV ztav@wCRfV&dDz)4CIziKV<}0}y@`)rcUVnUzc};TJP00XIKBM1;tJG?YPJU)yQ?&| zp&}9D%F9sqN(x~NS084{SmmD(=j06UunIUnBswDyIXTfXl;{7JR3?1*aFr!S@n>R( zl`G2RKRTXNg4Ir#`9|WdWT6~jQyaaD@wi#WD-4veW||m_Qu|&T(jjrGBXVS9WUR_5 zS@^}zch{IspE(mrFTs92F4KX*>8ftMs07kw6x5$zPIE} zDlILo?DZgR&Y@uQ5wt?i19`fNii$$e$;;&AJ-xk%UJXsnQu~3t7vORlKAXADxS?Wm zL}0dF{(Mhr?TZYhZ+d%$Rxtd)7YxZB zm)FA|`R?7bBRW*oTkJt+9LLu89n)HmM@ij}EL*En5jz4g7er6PsflzI=LPsGkBkDC?{i4#g{DXga_-%pE z4l8=8fRuyyrs)r#RTUL;Q*l7JTOKLrUOμ54sKN$Gci_re9jI;>VFYKI&JhrWOR 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

gJDWV#!55&0EU+fjuI_2}ey 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 Tiyp1)00000NkvXXu0mjfc`MSV literal 0 HcmV?d00001 diff --git a/static/images/v1.8/overview/component/agent/sidecar_restore.png b/static/images/v1.8/overview/component/agent/sidecar_restore.png new file mode 100644 index 0000000000000000000000000000000000000000..57b6f5d2e169e6836f395cae25a9d4c79597ee89 GIT binary patch literal 64460 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≷7TRC75VxykdK4J=pVzZ>yHq-$b6(u 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 z zMh$bMvF{pXj|RE>VK;qziomX2v0f;Fl^t!Zy>Mdnh61|RlggJlANJt1*+Qm*v8{5U zK5%3?@C=a|>JJ0t@c@vG!Up#p<~<|%%g`+$3jquA-};3ZQsZusJ#^Jieq5|`mZ`6# zMvC&j`GI87qs6)Nd%6v0Fm!%8 z*^pZi3XY1#i&`3x5_VwG6g6B3whZTDdH#ww>4@sj8Q;K^Ft&wl2|RQ zA1$XWN&d0)X)L4mTb6=nlktj7;s^o_VH6K=wFJ!-Af|FUKhpdOIPaCP_Rt%ywdxUj zXzqeHI|tFpc>HWufE5<6jES2et0o&I$cjTiQ5ED}PGnABuXeo;BEDjS>X22oatXSR zLC8~+p$&i3GS{JzGIsE-{J;Uq-b+_^7Htte_Q{tYl^bB3bf<1CA5BawhRGqLmYMQK zR@Or9pN)uEmXNx-udLdm++a5$F21oG$!5zsq9O@@@g~W)HkgTJ$wbMGL8Fq<(}Q|{ z%qIzT#my=#c@GSZr%Rf9KwQgYuHkD22E<4a zh{qaAJ|FPac z|8&30Gg3=ABVY9=v+a1wMjpSw#Vj7X90XSldyIvz3?(n`A7^xP>KIsH?yER61@xJqdB)C>*26=-{8p;uzzM=Y`S+>fv7xV({3)@Oa@`8>id- zM;k{4$NfgTJ-aNUG)e&dnYqZ)C%3}Lso#DI(H;z#(ZTR4kWIai1o-^%uh}wnh#oqe={;R#jOKNrIIn35fwyqbdbPHtmSAk_A^P3O1^gn{b8AV zq4~pvd}JIpeeN=6in6JOsPst#vK94=WV`uaI1K<^>0jwM*Z>9C5VC;e0DdwB0_(f) zH{Ty{-BLcV3Q}xI|J_@9G zh(TjTmoNtDdYJutDVeu?tNBLgW(%ihQ4~WM$9hqEXHxlgd{agpejrT)dqjwOf^K70 z5BIR1T_pl3{M?$xGy;#nI&{uF!tV915p!_lKh|{tIKk73*ruS3 zkG%3DLDvY<3YP-T4E%g9icr^NRiOQMC=lC)@g@p+MxsMvT1(C(6|LmoC<;hg@$;3% z@*$67{)x<-((FM*>lXRpyqwiu{#Kc6(YDa;T7)O=)ZTg?+|HL)mu{ZSAgOICZDdai^k66x30qB4Jb8c3fH9d=3Wz8K% z7EOlvc_Rs`M^J+imD#z4p69Kr-xJ^jv^G(=^YPWs9(imJ`YFiaK1&Rj)ezMyILRRNmf^P5?u9 zHbCZ z?cfSDjxt5@Y`!}gPme))`|lVnY^+0yVu|_e@r|M1-xS%l=Uhq|m(nzf^Fgu?DzB3D zW2Y9@ii4Cg^rd<52An?iWSR&<_#-WJe(#g_wyI7#YC~&$DoB26G5g*5R^>g1fPlIn zrzo5`4nIE)8X(|4!K2PhA|(Y9)w5=Nit!LtszFdx4=TX;@{I(>c7Swl`~ zYZiq^FxQfTeu7|28&n#&)sw!5sAUHut{50 zh$N|(cxz*$cLycp?-*969)77P(Kt!bOgH;F#CT=wMm=Z<#ekJ~2lw3`%Q*R__?pbw zYQTSSfDA+HF6=RCa%$F|4NLhe&)YX-XNMu-l27x_d(_xZ>$Pkl*0g;S*mIr}<5XY80zQ_NoxMpnp!f5qOeAzYMPIkpk^%SDZ-bu${xO5m&{u!z z;~jVtEnPD7TbSp*h~ts^!HAGKlcA}|6j$I@Af4;Fhk2R_okWBIub3HUcC`^qrp)M{Cg@qbkcGqahbKwF-5$esa z;Sg`nA}tfN2{J;OjI3F_3d1b*M_wM>1c^~$;Cb@<)Qj&pxvJG&o#>BNriD{S&lQWD zy0cLn(Ws4nns_rtY(%|m%webw5OjHUIu;++X-%2h(Wc7Sj(bmUAPn{0>semHm3P%#ad~${y0lrq>D40GuPHgrZY45hH zYBNR&G5mGl1^sOx!5vjz*w^ccuhDAy=}#8R9UbH5@S5nMO}?qlV zNq{P55aha%N5{zJr`5Tcnrx920E*K2rFRxN+Iw074jQ|$?e~;u?K`4Kmi)9_B0AiL zXD^xOXlAF^p3{Xi8Gk6hk%K_{Iz27RhHC4_r zQd>!rLFaL0ad^EW^Cp+&IqO@I-1Y6}G9!-r_H9X=>t0J5RjIZMnjwA@J(w8}%)1+RC5qJB;Dr(+sy37EW~#7ciI!)O*8t#}ilibv2kdHhq4)J}hXdWj{QIZ8;O zB@-`VcPEv{v;DD7%oaBb>QEZPuf^!pwvCBF+#rqIm?n*!14eFtwkkVU>2yY((g zOZoO91lye)`%A+ufksq3yS?vJ!_yIB)5O%84ya;9f*D_`8_hpffru20m95Lwj<>+~ zSL%r=EJ;+)xpZFd1Z#Hh=4!^LQ(H0lAsw$$>{dw}sC~)HvA%ojqc?Q&T;@bbYxehYpNR?!n)@fB2bf z{|z!wX~H-S4-yq^ESHU2r$(-nG!flX4L9SX5aSPO!Fjxi84w3c`)dKKoY+XwnqK=i zJDb1cW+DBL?yL!=2YSbSzS$#jokJ{*}@8ZknUIhzs{shPBH?r**_;J{s*_@1Ghay9V+S5jW23W&W*_=`F@ALu@F&gFD-60@kB(?e5rfIK&+aOE1>&-UAsG+&>xw8tEs{U zW^1Hj9SzKDmK2B|;;s)wxUowg%WBD|qjt1t5(Q^!Ls5 zjLcGF;-THi2B{qJ;-^x=T#g|nZ6*uw?s5#zKz=O3rWiIYT*>Ac{~KI)Tp=tH&-=2! zr&sYvPq_pVHJJnDU)}QGm2fJW)4K%B6V#W#ne2{liL?yT%Ufbk41yx#d*T_HKa(Ov za>&H0ya>d_{v06tAiTSYXakLokAKs(*%-JpKj`pH%j1hV_yfk8GF}p5jsN-KxcoQO zv9LFoMHz=WYOQJoUM~j(LDC@q*b0Hd8oIB4Ft+|qy-gc=i_Lnist?G2-`(1f2VWgnS_dFV(BZJ z#?`n$zEkBI{Jab5$OyN%xAp4}It!*|+jv+Z`RNhK-Q>r8cQG@@Lt zRU*xULz@Qek%kWVZXsl4ip>Mxcrf;(d{IrC!}IQ<_q0X?fi_7+FC6^0abP$ofly7m z9rr$yuoCdxy-RXXw(uJh%VP@NC&)*(}#(KE{%`x1pF1rM+Bryewpf2=5$z}ji9N0B? z$;m;^!rcx)o@prbgQCvy{@f-X1lvD!?Bi&ELQp|@CNsRkJAxN=D)^C zPQ9A;yyA4lPe#S{*M!>k!zg@CKm74YMzTf=2e_d|Q7F>bDS2s19WaTX7y+@FADy>= zncz)OSL+Sy%m>^FGngHfOqZV+`0sWL%7!L>NXBvnJan%*hBp^2tB@r2LsCo{6vxV$!kFQEh~jmgfR;tp0=*a1kEyQ`g|WA?_f> z$+WCrL$MQeD7^c?u^NN5s7}BoNcK@ceq8%i^qDXi;L-om>N*s=9OTdJqMto&E$z!7 z@E`^Tk^YU@AUqt@a;d@2FSJg=*tU@@$kiqxNG1>tS;vZda0dE)T|*d>fE`l(GcYO@ zLaD^)k9ah1JW4jc`iIX3ev8cZ`#EM2C;#3TfcQ&00&M?j4|wqVTK#rkzrYRO=*WoI z-mVhCjb)z7_`O2DC_pPXk+#jBeF*CPP8fYG%9Ri+w`|E`xMLEhAKT32YabIYHv2kt zNZe$zav*{~t`b@~JcSRl8G zdD>~~Wx+j!^`RG``fJ_3!N@JfD2nZI=?tP74i5%S_)LSt7}RzKKert#2^iCk|oGfzsBX>sfi%~25#LmDk`V0A1dLus{K`ijAKP~TV1hZdzi|OQ>FE{mIy2q z0qB6;!QZN?i$Olxj|Gzx{rY5KfXWg0h0T)63V8J8v9!AbE&h45N-+{=xobXs2lDd_kc;E+L9 zX;BsA4ECKB0X zR%05pIT>2J4+Q{2=e3kkpa=B>2QyxjQC1-ti*rVqMjisuA4!)u_9YcaK&`!75BBe9 zt0)~v;D$vXyOFfTXG#lzqzJJYF@CARrN96Qp=OdqCD-sa;NnUPdJZz4$sp&3xOFN! zSNq%!c-~@iS4i>r3dfILr|T38QuNSHPLD3lbq9#SKjJM`{P|POpaJU8NY{jEnNWx* zc;#g~WGfxjj^=CMJXv)5+jn!1n`#47+}^+W&YN1yT#4W_xOUuJ2>uG+K`E;}3V%EI zTO!|rB5?n1w6TS=p{&fJjk_A;{23E@?LFhnL6OJitJs4fpC9v@KOlKqbyiz;Y;n`V z?m+au@RX`lT zwnPX#0;MNCN96-dPrijJveC<83Gl0_@-78yZ~f#-fgz) z7R@f5W)*hfXZF!EdgXOH)-F%cd8at!7b^`h1SSIupP0CWfgZ9#_TJtiF$?A86H2TA;m9K4huk|DG zpkVD16Eay8A%3dOU!tYxuMG{K@#08lvvqd|TDKeXn1i8^2n3v{rQ~B^xR%KMI4Yl?`j*_%bm*XV>&_Q~&Ot*?)q`@$N_^-D@vza1r9Zq=WMkN+kOD z&;8yr>c*=Z`3Gli@9*6-1^oTcd=-d~I_+nk0k(?49-thN`_7UZs>pe~R}K$~|ibR2L``;y#|IfzB z|FchWYw{vX*8ikQ)*bV|ZIl1Ik5c=}48=Ae z#?iTx-w= z^SX+|!j!U3smjFA)8ZIeHDem$(I$$IdY>N6ha4;c4(^Z2!^4+8n~fNvwwAW%nuYA~ z>?K0?3E=prS>mskIjM)~1LA)@B1uE)IL^to${;*SfO{tq3rrQp`?LeU ziqpf*W|%<(@mq~3ju}_n1VN%`u#pL9<-jdFX+3F(?qYp>b7_@$jhO~3G77cg2eKpi z+o5JFySXe(=Z>aqWTtkGeZ2-004Y!%Q4jtTO5O(}v{s7EIS^)!O)r@uJb*95V3ES! zHs7y=clFOU_x>lYyavfAT-42R4LLVL6D1w-pup|i)o8{c6E0RWpZeCB?&=r6U|h}& zyNb&|_x|pttv+2b?dbPIifo3gG#h_s&tuEW9fn;G27HPzvy(IeHN1G_4va&CCy4@`EgBLOd5ISp)$S+Ifp1cFG1st(Zu!3Nytl$;% zbv~`kcu#Jvb&^b6vp+ukLHRzh&`#%1nYTyZB$|j~w+FtG(o3ruy8(}XmhXQ z=P^x}{)U~s-Lc$tA=j&LXgZ4wf;DQca?64?8Xv+COXDz zxbIWFYTn|8U_R+jL?N{8W!d%V4Q=lij*T7!bSVh~dxZ1UAhIRq@%s zidAKY}Cpr6$K;aRoovp6(lmRb`~L`0>C zDJH=4X#%6M&>ycE>sAmblv!*49HJ3^Ft~o^@f0cZJj}fRKu-56#IbjE=Glj38+o3G z+#${ezQN1wg3DHM;xxh{&Z0b(eN6z6d;xy&Ra|<@IOr9&yW1&WNp+eZM2pN9il;nJ z8vI!1j#5@wcqyFnZJZJjt47aI9QQPUNym&B-~M@&#C0hLbeQm-iso%Cl5b4)aP9-` zG!bi<*|s0XBshiMw!xteU!Z(V7?Wdl4|5&MT-)>mDMY$CGvCA4&Clun=L2O2xD8Cl z$UV7xi8v`VR}nTF1{P_kZ&HQkO?nUjQiT%OH}tQ1)BcE6>BE&no9BY3y z)ki{y0;mH#&^=unaJl(boWKX_I<_oS#WQ~gNrc2F7pLrLK|~ey7DVegR~)FMYP)Vn zlB!1$z9ei2KxpWMMW%&3LV%B~Ml-%Gh!ZhEQ;aA!XE$pa0e9m8=Hg5?g<;KL1a-`u zrc)3aDRyzZVm>1sPAUbY)$bNUT}A$A@!Ik7{beCGr9gBIR4~!GXTnzVK^6DSzqCaW zt*F8%JKmS;kTa!(rHizr&4{<4KDS81bV-D3idWBlZK)J=t`ntq!hN1%M@qOcy}ztV zc)jBdmPQ7`JD{!K!cu+0{TIC8mJ)XGKi;V>xOkIj4))90*X{?eq5~@R5abG zaPSJh+S3C|e;Z?Bf4FCb*olP&w7Fbea{t~2A@CFnZvO6BZo3UG&#P(Vv8aMSUr3>v^A^p|^7wm1K}w1x3H=w`&YhT9%8c zn$rp?>3726KS8K<_oEu8KAl+2bvZR_z?*+DLN-2#);wqlw( ztO|uVveHz*t+Hm$YHG|LNIdCnUL>XFw=<7N_tDtLChjvSm!I*AsNP5FG@lu-sP8I& z*-WCGp5MA%G_T_43OUpd50Q>+rwXaC*(}t9X^qb(YbcL;SwS2}uG9wi5z6I?@DfC&=+n;L8;4()IFU6oe>6bS3Fs%ov9{aT*q+ zW7x!@Ps8U$6C^>?58>=Mcf6=+m98Gv)KX(4^S0{r;5W0Z+rU6**x`R!{Lr9`ko@Y6 zz$`ZUupJUX1Zt{Z&H^&2Z-d@*&!qejH3%9Wy~0zj)V05w`M$h+kbR*0&ivb49zC&i zB_%NtP4f6msKWb+A9DwrjCylS;Ywcxli=bn78b>$A$-N}7cfu&Y_DVHBaMhjn+pT@ zrhT5jXXM{HSZh&zbBVx#;)+#^EK6z}zQQmb@y2J02K@A!>sdaMf`5`NHVyQ_bc04!Y=WKIgO{METV zB{~1+G7{ic?xa_^6R(nF)W-HuO?(PKL_*wR7^1$ZyFe=0srL;crfkTr2{}1}`<8~1 zo(3L4hA3{FF)Jc@Z}{#wA1?4rhW3XVSM=2g2*ZCwxsZ_3jX@<9%Vr~i&}H*CCp)|f;G4cFa;NpTlQwq!W> zSFUQlk14hXacNx={{*x)_luFOH}(?MT#9V6r*aBGlacgi*-;2U^l@cY&0!YhebM1~Jz9mo=&?DGG&9L&1&R<+bn9!9! zn#D=w39~Pdaq*w0xt_DDARP%+H&($&-!Ti42Z#M@IwCl}IHok{Luh_VPwSax2AH+4 zZbvQ1^yEY2NpxOr3F4*oCyPx}*jbh7lEORgPNzi;0C+3FG;=K9A8$z}kcCfq5Kmj6 zzd33hc}MBhF^nvoGFWW?`|-}C50`kEUge}?>HXr7_<Ias4GvvQ|BH;@8$R<)|>1E^pLd=d%guHEqFgg4@dGeE=3FA)3 zPXNZwPy3y*UeF{3ha&JYw}gnI)_(7&c&i>O9`F&5>s|qdON%xYaMibC4sO<~s4`ItiI8%Hges*7#@#%tGtJoVr7?g=AP-_0GGE&GNAl<^&_28CwX%-U54-Z3!wgP#nVQ?k`WNN3 z`)=#}gSIEl2+5611~KU$(gezQx8WW;=vWdlH~QJp{=e~;^r6P7ON3(AeS!NB@?iSW$HXDo4PAc}?%7>1 zNP&x_drtj{!vWWpG8?~w@@WoMLsa1Ig$#{&zXr9-Wdhc637rb_A(%UvLMOkOrHCMb z_1BzR_soN9<5{xO7}aEL-~9gcG<(+VP!=uu==2Lp2>!}W{2S1O>|m~<3l;Ff2pbp& zkUCi6C#6fYTt?*?9+CU;x>@^86aVK@h!60Le<5`6CZcXM-@kPy*L?s%@lyZ)q0P0} z_GvBt@7N~xv;sDEOL70lCT$`<{{yxb{EKa(X}+jmsy_{j{+sFW@Ww`>J5?akw7kZ0I=@ z{s=nylc~uG+jj_$1nNs3`jv%=iq@k@$aO}`brUn}P)B^${ol9=`1O2wCHyc%>=jc}D-#dD!`3w=ihssJFb$cvq`Ie#CNGt! zBmwgC2l&ziFh+BKwHM1LCs%VNq9oLJ1QQ8kLcR-;4(QLFe<5rOK_Qx6khN}HO}8%8 zEd8|ObCh(=pMfUx-wildH21yP_dPI2L=d~W;g^M*3oo4rWQhXk^8xJdno}Ctj)kpI ziI^Aj$oGN9fAU#PL6(<^3G&-n3P@w~i$?aQFp>Ru99Y=O!tl4f=p$tLgE_fSzb#Xg z%knw7!x{L$wO%$Uf?75AGw7Xp{qWiYQA`y?F+tCQgbQ0mj~Ch=^xhHmUhZxz?}$q* zOW9`+iV)=CbWj(y1IsIE;5fcoW_lr#P-y*~wjQd97+T!epK@K!1crfm2zKO$`iZ&9 zYmC(A<52q@7r4{1pJ5-`0I2^*b6htA@k>ai`LZ9XK}YweD~O-|HPQ(ID-#&+>%7Y2 z$E+Pjz0?QjTzOw62eIS^a?t@v5jGSkI=>)KTcP};=GwFc$(R1z?$xA(pOI?dX`Iop zvpY5TK3e%f(-^Zg?$Onj>>}v8hIdE7zenQ{{-I%#Jm&Z7YhfUEt$Kb<#o@UA*7m=o zp8TV`!pRhWyM8&BH(=vI8k*Hsm3IeK2pc(7^6cR(T=t{Jggs$a7~=QCWa4OH0LMJA za6oA)=x;>urRGVE03X(aiiRlOVZ3u=rxga_^&6ZH;yuR{CYjQgB->($tOV$@oWHdf2hpxFV|Y3qv1Q$h`C5 z48?%zb4hU4vR`&7e%=c6g=%fk<7y`evh#B-BlV)UXyc%ry)ZibMx)}EEWG>cVd_l` z|B-TFv_HAD)+QEjyL*h1R$1Uys-L$Xd!~4P#^UK38i@V8kLpRPa*qNwFmm*Qxc!;b z>w}FBycQybEEU=I=T9Xq&?vYQt)vS?%w!+JDia)&CV*NBhOQ&S)$@5F)?jQz4xesu z^9D-!s(C@8V=|{dUcLA}KS>1r_SOROaSTRz8!Xg~|8N0*g~vp>@Q1e4$U)=Bs}9H| zpjnW zR+ODGNa}z*WtBrOK`lKuYfnM0@h22M!X~+>{Ry&Q`*cnoWmcR!9~~9@66Kp17#r7@ zi1m;fzgNf^CHsHvOymd3TV9LXCPF(}6xj&s?={0tIud6MNl?h@u)-GB4U3{D(b7l>?$ z8$q3yk@m4Bo)8I|s>^AC9qPZW6cjGg+w`!x49hP?pO~HjRV)1xz zh$y?SW0%jK$13k{R!X+~LKWPhik9;?)eSoVupcj$t+}BKsFQcAOh=2l??b<$(d0!b z*(=Opzh6b*cqLq_YptP2nf~JUZ?jHHmBk)lYeFy;sm-NERFB)PG%rlglEs)MJpNF` z43yajlyXl)bY!+A`&}+XtWqOx%rI-O}?2Va95+6n7AZV0 zrv8==OOTY#i(uxk6|&uyHL~BKJ2%scU`-t6D0IN-=~p}R8b4AzCeYV{|I}gPsW>26 z#C_sqVUn9JUq2Hl#}i$8*>zX7vzU0OY}Ndjh1J0k5A-h+1!}BY^|vE*BA77g`mzVI zE2R`HbyQ0C*^m}B?i^PzX@O9hj1S>g=uCOqH&*bo9?e8_kRkzjvWb^ zB>gmdTnTTEc<-SwH*Ddd@bL6Q_CKq=j5LS7lv<^N%#6=OWW?&^z^O4^#AU5C7{$cl zxNW!@eVtO)b(;CJO}b}Bk|+g2r7BbhfeX~^uH5JOAYl(&8X0*h(n$m(qT_dh(G-3| zdvtvMhN%871~~S9+bx*t7}1lWMkuO=OHC`YYjtU=!C$LG=x^Q=FoYX~li8fiTU-a!3T>VYwMPCW<#aLgmSjEP?>rs$cPpF z7Tm2By-s)a%3t`vQ~t}mg^}{1X0INcWuESOUtQ=K%5z$EDmfqE77g{7o`_>p@k|b& zs5^BTyqif&Iea_0MKfBJLyJ&*O>E?wEh_xkUfc<$^uz6OK>_;6U|0lCI&D_gqdGdf z0iA0puneW;YKAU}bXbHaQcGp};H*VE%QgW(_3d^IDP%^u!aMKGi;k@9OH z;*WyM#L4rPW%TvjYWUXHL_S|!PxXoiYPv#2lZ>0ElXesZylU}lPW~cc_*03Bhv_fq z&;39Dv^GV?pBSx2ZQq}~Vd1U4_TYR_o>@n1!-#1N_>5!j)av!7Ri{jlXAuW>2}VEp zmyny9{{A*{Xg}BYdms<*Qv!UxVCMkPDPSV7D_w@K>he?D;{g~X*s$RH?j>6O zBEy9=cR5FYS)!i5tDNqIFTP^XKxKT6-9y~CY;OI~ma5-}LwzjarS&DFo5 zUxXKk%HiHB90vlqRMJ_LEZV(T9V+4Ix+qRxPkNA!XDoliOv>u=d?TbNY3+}{^C`8m zvSb*N=tT9SUl(viZj%Y!PzgE&ioQ8`BjT$GHvPLJp@fk_MTe%R6PcT#RhE1%JXzL{ zt2*+pk|1iah|f1qSGx_D)Mwey9fFkxUOloBv+9VN#dVdaPod|hPrtW)UUTwUzToSj z=efOmPCBmXZu_}4a!l0EL^)i8iG4OetB_M<+a-jG9Qtt*^lVjYV0Mfn9dDuz zI-%?6{2eFU2zxx#!mtjTVdGs~!Bg0yKrXQSp|I6gp5x%J=9}OSIavUNt4Uj%Pa3aWA5W`9Ysz-%OZZYh}ceue@R4r5^Kq z5KQ?2<=qS-@zZCioa=ZYrq@E74`8c#MOqzw8@fx2k|s=Zfj;Uk8Qs!L1`0`kQ(_B0 z`;jhZ;&7wh^#p6cQz9&BgQXqwK3Kp+OmUXI5gc2FTNx z7dd@{HOKq2oklSB%9p+HPA;H;+o(_HhzWylyjWd|B(Ye`pSI|2zx!S1R|K)qeSD=D z<;08^m?2g!hUN{y9MDV2g_08`TRQ!3e>eCsy8zXENjKc4*A zUT1h^L~Zx6=(8_-o#LApWnyeqi|pW}MXwlL%L+VYq?m%KZt>+Kesp9^QLFq??!YL) zn0q$&)YjK#MHH%CUM-9%rjxOq+v4eDZ6M{Y?#CXi(0B_!{cczxlGk zU>nyEulL_S<3$zN`x0*P$-oE*ew=%9o=%DPS%a;(HG}en`e9iW;rvQyUW$MrD(ndu z4|bJ`M|O$1Db8>V3E2F$+y2vzJ{jvHdN>$^%LT^`))uQ^ES^NnsZL@hGf$imSP~NSYM92Bo!DsWAP!yM) zroYjpEqcb=p(eXeAtk}LvmVbKub6f^i z8+CHRul%0zB}mFIasVgRG(f0nJ=N}z#>edmx_xjAlv$8*wF@s7MMs{YEMl&6+F7!n z<5-2at(IM77+?bn>bP|ek;m$(2^bnE8YEihn53A5o~i(cOnVXYl_YDBLnnIMM|HDp zA83ol*t+w;auv^!!CF@fHRBQnd+ulys5W`9C5n_RO$S(=~Psd{0eX@H4o&)!NQ>xP5v=W z56V=?+p}cDaw_>WY1iBRgPQ#r`1SmM^?d)&)a}bf)U_X>W!V4X&-(NZy!~VQ{=X}n zu4}oi|F(KReO3~xCLR!6`p=VRU@S6=)f_M5q2RhL#k|b9W_s*IrU5BoJ!bp5mbt*k0|0JTYC9)zDI)R>aVzEin3iz_iTT zv8ieSi7IQ4RZsm{^afL2r%sezT#{8LFX43Oqi6`s1e zMcq#oKetgG%}AQfHA#i)bNk<5_6;1R+{`+PsXTXNaY&-4h@5*5qZ0#!ejo_Ji$+`T zb5RCf^^AnO=_vak#gK=egwOoX)SjZ#E!0eOfn@jiG$p^#{sf;RvQ9t7R@gZZojJ|{ zaYD`R5o8W%4c^qF)p_V^s;ZdeXZZ`!H;mz1sO3bv_;LeR*YHVh z5QJ7-Sz{c3M16Ga;q+6uC<4T`or`8dh=z)bN+F@-l~{$J2Q_RI_P(SZRuRFH3q5HE zgE%8%XFcr3No6S>dQ|!lJ*LdkDGZ&pb9av*1Z|*lv)t`2k+{42xoTe$9u32D;VJpd z`1A-`{sA%`9J>&rK8S#mo#@1Z{;F_A?Wxq!U-B>*QFjA;2JlQSK$CGuA_G2MCCLjD6cSni?uBb@|y@>aj(5_=uMoU9(ti%Lq^KYl4yQ4HAEr9AtnQOg!+zEUZHYsH zX5xi;MT4w>AXbpt7mlmQI|GxHucIiS#b0j+Pcr;SvdT3py`VZq#8a_i{N~4_uY{z$ zT4HeTJdp)zekRC70p{qYa`4LQ78F{relJqIA2uR&jtvzdIUuxo|H}`ev)CaX#zxr{ z8RY3G{d~x0^R60+f`n#1e#tJvsMlG@M3u-!P#tO{&z}@~(ntBd2quEW0UV9-E@NFn*zmu_&jNsHvy+wN|f8GKtS}>HKj9 zc6fVKd)(W!W-HcZc>D7{>;X%;Sh+L2!NkaHY4=wWc?a$kLR+yQuqz)I7{ksUXH26= zZ{(aTHXE!~{<>QJAFXQ43DOk8K|D6Vr#Bc~qJz3l3;>G_wTIjHW9OIw2((q%x0GxWm56Df)mSNNu0T zY2lBn(9&A=dCflQRVNkbfh?xw?VCcz5VHmmux4mc-CtR5Pa%M;8TYp1{@QdL2DbW9 zLDpO}zt#$0EEgcCF_>jI^J{~Os!ND|(Ktq&&j}p8R4m4lP`Zhery5v6UtT-Mh22mK z*I!e!muv79kQ4+0lV38&1h)T}t}v?Lw6ii+6t5@$lA}&;hlX1ax#m$Whyab$p_{mcbkbW;wfblATb6Eb3-@(MtYWJnaSp)?{>i83!7^vfz&_+ zk!C&4U)tg?e+di<;_+X$+ce_JQ&4_nLEr1D@~m-HAdg3J?xHTQbZcVTg2(E2V5kER zYo%vV5=)LRR)!;*7v#}H-=eUA>ZM~rzi$Y*l624=-QR!p(mbvY&fo9*>*n_1!&%+} z60oL)zK9U^f%w!oY9BKy@A0VG?Q@Qd#AGm~E6DQBHhND!Y(i9lByQwl0>dx8DkPsL zDIT^&Y}tglccnMIO(uA0l+RF99jpH`PTDDs0FE`HFsJhj@pU~ZE&{hM0gU=(Cml@n zH+lZqFb#cNcnNMy8AbeLFR|3 zL>Z%KC|Q_W9Y#U~Ibu{WqJx_`yVHUVT5I%iB>;yCm?3@l@E%e3Q;ZZSKm6XnO)Pzns=7WtnIR*4X+UjF>kJdlk=iuAxOAU-iC^dQFo`@6+D pO2aTx?0=9%Ab4I?)4y6r_!khu$x29%E!O}5002ovPDHLkV1l%ayO;m~ literal 0 HcmV?d00001 diff --git a/static/images/v1.8/overview/component/filter-gateway/egress_filtering.svg b/static/images/v1.8/overview/component/filter-gateway/egress_filtering.svg new file mode 100644 index 000000000..6de43377d --- /dev/null +++ b/static/images/v1.8/overview/component/filter-gateway/egress_filtering.svg @@ -0,0 +1,4 @@ + + + +

Egress filtering (Search)

Egress filtering (Search)
Vald LB Gateway
Vald LB Gateway
Vald LB Gateway
Vald LB Gateway
Vald LB Gateway
Vald LB Gateway
User
User
Vald Filter Gateway
Vald Filter Gateway
Broadcast
search request
Broadcast...
Return
search result
Return...
4
4
3
3
・・・
・・・
Search request 
Search request 
Return post filtered searching result
Return post filtered...
1
1
10
10
Vec
Vec
B
B
X
X
k = 3
k = 3

A
A
C
C
E
E

A
A
B
B
F
F

B
B
C
C
E
E

X
X
Y
Y
D
D
Aggregate
all searching result
Aggregate...
5
5
Search
request 
Search...
Return aggregated searching result
Return aggregated searchi...
2
2
6
6
B
B
X
X
A
A
Note:
When the result is post filtered, it may have fewer search result than k even the original has k vectors.
Note:...
Filter search result by user-defined filtering logic
Filter search result by user...
     Egress Filter
     Egress Filter
Return the filtered result
Return the filtered resul...
9
9
Forward the aggregated searching result
Forward the aggregated...
7
7
8
8
Out of the Vald cluster
Out of the Vald cluster
Egress filter
component address
Egress filte...

Vald Agent
Vald Agent

Agent 1
Agent 1
A
A
C
C
・・・
・・・

Agent 2
Agent 2
A
A
B
B
・・・
・・・

Agent 3
Agent 3
B
B
C
C
・・・
・・・
・・・
・・・

Agent N
Agent N
X
X
Y
Y
・・・
・・・
Text is not SVG - cannot display
\ No newline at end of file diff --git a/static/images/v1.8/overview/component/filter-gateway/ingress_filtering_blob.svg b/static/images/v1.8/overview/component/filter-gateway/ingress_filtering_blob.svg new file mode 100644 index 000000000..92b1b5865 --- /dev/null +++ b/static/images/v1.8/overview/component/filter-gateway/ingress_filtering_blob.svg @@ -0,0 +1,4 @@ + + + +
 Vald Discoverer
 Vald Discoverer

Ingress filtering (Request with blob data)

Ingress filtering (Request with blob data)
kube-api server
kube-api server
Vald LB Gateway
Vald LB Gateway
Vald LB Gateway
Vald LB Gateway
Vald LB Gateway
Vald LB Gateway
Request with ID, blob data & ingress component address
Request with ID, blob data &...
1
1
Asynchronously fetch
resources data
Asynchronously fetch...
Vald Agent and
node resourse
Vald Agent and...
User
User
Reconcile pod and
node 
resources
Reconcile pod and...
Extract feature vector by user-defined filtering logic
Extract feature vector by u...
Vald Filter Gateway
Vald Filter Gateway
     Ingress Filter
     Ingress Filter
Forward the blob data to given address
Forward the blob data...
2
2
Return vector data
Return vector data
4
4
・・・
・・・
X
X
A
A
B
B
C
C
Y
Y
A
A
B
B
C
C
・・・
・・・
X
X
Y
Y
5
5
Forward the request
(ID & vector)
Forward the request...
6
6
Request to the Vald Agent
Request to the Vald Age...
7
7

Vald Agent
Vald Agent

Agent 1
Agent 1
A
A
C
C
・・・
・・・

Agent 2
Agent 2
A
A
B
B
・・・
・・・

Agent 3
Agent 3
B
B
C
C
・・・
・・・
・・・
・・・

Agent N
Agent N
X
X
Y
Y
・・・
・・・
Out of the Vald cluster
Out of the Vald cluster
Text is not SVG - cannot display
\ No newline at end of file diff --git a/static/images/v1.8/overview/component/filter-gateway/ingress_filtering_vector.svg b/static/images/v1.8/overview/component/filter-gateway/ingress_filtering_vector.svg new file mode 100644 index 000000000..27dc9bf78 --- /dev/null +++ b/static/images/v1.8/overview/component/filter-gateway/ingress_filtering_vector.svg @@ -0,0 +1,4 @@ + + + +

Ingress filtering (Request with vector)

Ingress filtering (Request with vector)
Vald LB Gateway
Vald LB Gateway
Vald LB Gateway
Vald LB Gateway
Vald LB Gateway
Vald LB Gateway
Request with ID, vector, ingress component address 
Request with ID, vector, in...
1
1
User
User
Vald Filter Gateway
Vald Filter Gateway
Forward the request
(ID & modified vector)
Forward the request...
6
6
Request to the Vald Agent
Request to the Vald Age...
7
7
・・・
・・・
X
X
A
A
B
B
C
C
Y
Y
Modify feature vector by user-defined filtering logic
Modify feature vector by us...
     Ingress Filter
     Ingress Filter
Forward the vector to given address
Forward the vector to...
2
2
Return modified vector
Return modified vecto...
4
4
・・・
・・・
X'
X'
A'
A'
B'
B'
C'
C'
Y'
Y'
5
5
Out of the Vald cluster
Out of the Vald cluster
 Vald Discoverer
 Vald Discoverer
kube-api server
kube-api server
Asynchronously fetch
resources data
Asynchronously fetch...
Vald Agent and
node resourse
Vald Agent and...
Reconcile pod and
node 
resources
Reconcile pod and...

Vald Agent
Vald Agent

Agent 1
Agent 1
A'
A'
C'
C'
・・・
・・・

Agent 2
Agent 2
A'
A'
B'
B'
・・・
・・・

Agent 3
Agent 3
B'
B'
C'
C'
・・・
・・・
・・・
・・・

Agent N
Agent N
X'
X'
Y'
Y'
・・・
・・・
Text is not SVG - cannot display
\ No newline at end of file diff --git a/static/images/v1.8/overview/component/mirror-gateway/full-mesh-connection.png b/static/images/v1.8/overview/component/mirror-gateway/full-mesh-connection.png new file mode 100644 index 0000000000000000000000000000000000000000..66ae6b4d40a3f273d34c00326e1325e28ddc729c GIT binary patch literal 26793 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

9*)OEUVt0wjdwdKW7I=GZd>NRp zFTuiIDnSl=1jzw7VQMjddpd) zIVw|QkfIGG5)_#({8jQ%3sJz@hKt9{_7<>5#IZdOUD(Vt0qfEBo}Pr&55ryZprdE6 z)++(&7!oGUu=%k)-!;&mr7AoeH^ICi2zS5p{7g`QgRo$(NcD$9F#L z4)FZpHPUrwENrmqarWLlv-4H%kx z6@Z~Bl4Ak3*|EbIk?qE$;3@xB!C@cfsxeXf#V5cs3%%rsCax|=vysfNk5$V4yagT` z>%-Tcewu5wG0u+=6ZKjz1OjFL56n*52CUB~M)jifR-WXVI*i|rX11r+SzMJ^=pD0Y z$RT!92f#@ii%yr^`1oppg4FWx6(NANIs?3~U^+fP9nWAeyg2#!0yu93S&UpCJ^;}O za4vsP!d->@5!pSQ*a!_$(Q4-!uxMP!qO<@ybR(h5l{x>5|Hvd`+4vGd_Agj6O?jd6 z)s$2_ER8%<;eq!_`6ow>=>tKQn+5rIRT{rzO z#i3~DULCC!(Zu#}dma1Jux^9yu4-RLUzasPFq77Cm=b)zl5qo^bQ+n>gvSv}A=31# zS14iS23nif>c$~Q9{|Z><9VIF>u9WBvUq9iYq$2RbsQ6A+xpFzO#yMzr-(QoVa;mYu)5oy5^WG--pc{1#CM;|mslENLg=V6o zu?r0~J7wjQX2X79iqSeo)B3rjSOvOkezu{MhuLaMri=d)v{3Xe0C3~93$n+9FP0;? z;Tqg*^*0a>$xj`6^~2=kE~WUY@aALtRzLO_Rft?FJe~xc=Cuwb5M6k6#1%0D$->qB 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