├── .github ├── CODEOWNERS ├── dependabot.yml ├── license.yml └── workflows │ ├── docker_publish.yaml │ ├── pr_build_and_test.yaml │ └── trivy.yml ├── .gitignore ├── .golangci.yaml ├── .protolint.yaml ├── Dockerfile ├── LICENSE ├── Makefile ├── NOTICE ├── README.md ├── cmd ├── client │ ├── cmd.go │ ├── cmd_test.go │ ├── common │ │ ├── client.go │ │ ├── client_mock.go │ │ ├── io.go │ │ ├── io_test.go │ │ ├── model.go │ │ └── pointer.go │ ├── del │ │ ├── cmd.go │ │ └── cmd_test.go │ ├── deleterange │ │ ├── cmd.go │ │ └── cmd_test.go │ ├── get │ │ ├── cmd.go │ │ └── cmd_test.go │ ├── list │ │ ├── cmd.go │ │ └── cmd_test.go │ ├── notifications │ │ └── cmd.go │ ├── put │ │ ├── cmd.go │ │ └── cmd_test.go │ ├── rangescan │ │ ├── cmd.go │ │ └── cmd_test.go │ └── sequenceupdates │ │ └── cmd.go ├── coordinator │ ├── cmd.go │ ├── cmd_test.go │ └── viper_configmap.go ├── flag │ └── flag.go ├── health │ ├── cmd.go │ └── cmd_test.go ├── main.go ├── main_test.go ├── pebble │ └── cmd.go ├── perf │ └── cmd.go ├── server │ ├── cmd.go │ └── cmd_test.go ├── standalone │ └── cmd.go └── wal │ ├── cmd.go │ ├── common │ └── option.go │ ├── perf │ └── cmd.go │ ├── scan │ └── cmd.go │ └── truncate │ └── cmd.go ├── common ├── cache │ ├── memoize.go │ └── memoize_test.go ├── channel │ ├── noblock.go │ ├── override_channel.go │ ├── override_channel_test.go │ ├── read.go │ └── read_test.go ├── collection │ ├── map.go │ ├── set.go │ ├── set_test.go │ ├── visible_map.go │ └── visible_map_test.go ├── compare │ ├── compare_with_slash.go │ ├── compare_with_slash_benchmark_test.go │ └── compare_with_slash_test.go ├── concurrent │ ├── batch_once.go │ ├── batch_once_test.go │ ├── callback.go │ ├── condition.go │ ├── condition_test.go │ ├── future.go │ ├── future_test.go │ ├── once.go │ ├── once_test.go │ ├── signal.go │ ├── utils.go │ ├── wait_group.go │ └── wait_group_test.go ├── constant │ ├── constants.go │ ├── grpc_errors.go │ └── keys.go ├── entity │ ├── opt_json.go │ ├── opt_json_test.go │ └── t_error.go ├── hash │ ├── hash.go │ └── hash_test.go ├── logging │ └── logger.go ├── metric │ ├── counter.go │ ├── gauge.go │ ├── histogram.go │ ├── latency_histogram.go │ ├── meters.go │ ├── metrics.go │ ├── metrics_test.go │ └── unit.go ├── object │ ├── ref_count.go │ └── ref_count_test.go ├── process │ ├── pprof.go │ └── run.go ├── rpc │ ├── client_pool.go │ ├── client_pool_test.go │ ├── grpc_server.go │ └── logging_client_rpc.go ├── security │ └── tls.go ├── sharding │ ├── shards.go │ └── shards_test.go └── time │ ├── backoff.go │ └── clock.go ├── coordinator ├── coordinator.go ├── coordinator_rpc_server.go ├── coordinator_test.go ├── impl │ ├── cluster_rebalance.go │ ├── cluster_rebalance_test.go │ ├── cluster_updates.go │ ├── cluster_updates_test.go │ ├── coordinator.go │ ├── coordinator_e2e_test.go │ ├── coordinator_test.go │ ├── k8s_client.go │ ├── k8s_client_test.go │ ├── metadata.go │ ├── metadata_configmap.go │ ├── metadata_file.go │ ├── metadata_memory.go │ ├── metadata_test.go │ ├── mock_test.go │ ├── mock_utils.go │ ├── node_controller.go │ ├── node_controller_test.go │ ├── rpc_provider.go │ ├── shard_controller.go │ ├── shard_controller_test.go │ └── shard_status_test.go ├── model │ ├── cluster_common.go │ ├── cluster_config.go │ ├── cluster_config_test.go │ ├── cluster_status.go │ ├── cluster_status_test.go │ └── shard_status.go ├── policies │ ├── anti_affinity.go │ └── policies.go └── selectors │ ├── ensemble │ ├── context.go │ ├── selector.go │ └── selector_test.go │ ├── selector.go │ ├── single │ ├── anti_affinity_selector.go │ ├── anti_affinity_selector_test.go │ ├── context.go │ ├── idx_selector.go │ ├── idx_selector_test.go │ └── selector.go │ ├── utils.go │ └── utils_test.go ├── deploy ├── chaos-mesh │ ├── chaos-mesh.yaml │ └── oxia-cluster.yaml ├── charts │ └── oxia-cluster │ │ ├── .helmignore │ │ ├── Chart.yaml │ │ ├── templates │ │ ├── _helpers.tpl │ │ ├── coordinator-configmap.yaml │ │ ├── coordinator-deployment.yaml │ │ ├── coordinator-role.yaml │ │ ├── coordinator-rolebinding.yaml │ │ ├── coordinator-service.yaml │ │ ├── coordinator-serviceaccount.yaml │ │ ├── coordinator-servicemonitor.yaml │ │ ├── server-service-public.yaml │ │ ├── server-service.yaml │ │ ├── server-serviceaccount.yaml │ │ ├── server-servicemonitor.yaml │ │ └── server-statefulset.yaml │ │ └── values.yaml └── dashboards │ ├── oxia-containers.json │ ├── oxia-coordinator.json │ ├── oxia-golang.json │ ├── oxia-grpc.json │ ├── oxia-nodes.json │ ├── oxia-overview.json │ ├── oxia-shards.json │ └── values-kube-prometheus-stack.yaml ├── docs ├── README.md ├── architecture │ ├── design-goals.md │ ├── logical-architecture-1.png │ ├── logical-architecture-2.png │ ├── logical-architecture-3.png │ ├── logical-architecture-4.png │ ├── logical-architecture.md │ ├── physical-architecture-1.png │ ├── physical-architecture-2.png │ ├── physical-architecture-3.png │ ├── physical-architecture.md │ ├── replication-architecture.md │ └── storage-architecture.md ├── banner.svg ├── client │ └── go-api.md ├── consensus │ ├── oxia-data-replication.png │ ├── replication-coordinator.md │ ├── replication-protocol.md │ └── replication-storage.md ├── correctness │ ├── maelstrom.md │ └── tla+.md ├── deployment │ ├── bare-metal.md │ ├── kubernetes-oxia-cluster.md │ └── kubernetes.md ├── features │ └── oxia-key-sorting.md ├── getting-started.md └── oxia-logo.svg ├── go.mod ├── go.sum ├── maelstrom ├── coordinator_rpc_client.go ├── dispatcher.go ├── grpc_provider.go ├── main.go ├── messages.go └── replication_rpc_provider.go ├── oxia ├── async_client_impl.go ├── async_client_impl_test.go ├── auth │ ├── authentication.go │ └── token.go ├── batch │ ├── batch.go │ ├── batcher.go │ ├── batcher_factory.go │ └── batcher_test.go ├── cache.go ├── cache_test.go ├── client.go ├── example_test.go ├── internal │ ├── batch │ │ ├── batcher_factory.go │ │ ├── manager.go │ │ ├── manager_test.go │ │ ├── read_batch.go │ │ ├── read_batch_test.go │ │ ├── rpc_errors.go │ │ ├── utils_test.go │ │ ├── write_batch.go │ │ └── write_batch_test.go │ ├── convert.go │ ├── convert_test.go │ ├── executor.go │ ├── metrics │ │ ├── metrics.go │ │ ├── metrics_test.go │ │ ├── timer.go │ │ └── utils.go │ ├── model │ │ └── model.go │ ├── shard_manager.go │ ├── shard_manager_test.go │ ├── shard_strategy.go │ ├── shard_strategy_impl.go │ ├── shard_strategy_impl_test.go │ └── write_stream.go ├── notifications.go ├── notifications_test.go ├── optional.go ├── optional_test.go ├── options_base.go ├── options_client.go ├── options_delete.go ├── options_delete_range.go ├── options_delete_test.go ├── options_get.go ├── options_get_sequence_updates.go ├── options_list.go ├── options_put.go ├── options_range_scan.go ├── oxia.go ├── proto_utils.go ├── results_heap.go ├── sequence_updates.go ├── sessions.go ├── sync_client_impl.go └── sync_client_impl_test.go ├── perf └── perf.go ├── proto ├── client.pb.go ├── client.proto ├── client_grpc.pb.go ├── client_vtproto.pb.go ├── proto_grpc.go ├── proto_test.go ├── replication.pb.go ├── replication.proto ├── replication_grpc.pb.go ├── replication_vtproto.pb.go ├── storage.pb.go ├── storage.proto └── storage_vtproto.pb.go ├── server ├── assignment_dispatcher.go ├── assignment_dispatcher_test.go ├── auth │ ├── authentication.go │ ├── interceptor.go │ └── oidc.go ├── benchmark_test.go ├── constants.go ├── follower_controller.go ├── follower_controller_test.go ├── follower_cursor.go ├── follower_cursor_test.go ├── internal_rpc_server.go ├── internal_rpc_server_test.go ├── kv │ ├── db.go │ ├── db_benchmark_deleterange_test.go │ ├── db_notifications_test.go │ ├── db_sequences.go │ ├── db_sequences_wait_tracker.go │ ├── db_sequences_wait_tracker_test.go │ ├── db_test.go │ ├── kv.go │ ├── kv_pebble.go │ ├── kv_pebble_snapshot.go │ ├── kv_pebble_test.go │ ├── notifications_tracker.go │ ├── notifications_trimmer.go │ ├── notifications_trimmer_test.go │ └── pebble_logger.go ├── leader_controller.go ├── leader_controller_test.go ├── mock_test.go ├── notifications_dispatcher.go ├── public_rpc_server.go ├── quorum_ack_tracker.go ├── quorum_ack_tracker_test.go ├── rpc_provider.go ├── secondary_indexes.go ├── secondary_indexes_test.go ├── server.go ├── server_test.go ├── session.go ├── session_manager.go ├── session_manager_test.go ├── shards_director.go ├── shards_director_test.go ├── standalone.go ├── util │ ├── bitset.go │ ├── bitset_test.go │ ├── crc │ │ └── crc.go │ ├── file.go │ ├── file_test.go │ └── stream_reader.go └── wal │ ├── benchmark_test.go │ ├── codec │ ├── codec.go │ ├── codec_test.go │ ├── v1.go │ ├── v1_test.go │ ├── v2.go │ └── v2_test.go │ ├── fsync_benchmark_test.go │ ├── readonly_segment.go │ ├── readonly_segment_test.go │ ├── readonly_segments_group.go │ ├── readonly_segments_group_test.go │ ├── readwrite_segment.go │ ├── readwrite_segment_test.go │ ├── segment_config.go │ ├── treemap.go │ ├── treemap_test.go │ ├── trimmer.go │ ├── trimmer_test.go │ ├── wal.go │ ├── wal_impl.go │ ├── wal_reader.go │ └── wal_test.go ├── tests ├── security │ ├── auth │ │ └── auth_oidc_test.go │ ├── certs │ │ ├── ca.crt │ │ ├── ca.key │ │ ├── ca.srl │ │ ├── client.crt │ │ ├── client.csr │ │ ├── client.key │ │ ├── generate_command.txt │ │ ├── openssl.conf │ │ ├── peer.crt │ │ ├── peer.csr │ │ └── peer.key │ └── tls │ │ └── tls_encryption_test.go └── sequence │ └── sequence_test.go └── tlaplus ├── .gitignore ├── MessagePassing.tla ├── OxiaReplication.cfg └── OxiaReplication.tla /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @merlimat @mattisonchao @coderzc @RobertIndie 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /.github/license.yml: -------------------------------------------------------------------------------- 1 | header: | 2 | // Copyright {{YEAR}} StreamNative, Inc. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | -------------------------------------------------------------------------------- /.github/workflows/trivy.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | 6 | name: trivy 7 | 8 | on: 9 | push: 10 | branches: [ "main" ] 11 | pull_request: 12 | branches: [] 13 | schedule: 14 | - cron: '24 7 * * 5' 15 | 16 | permissions: 17 | contents: read 18 | 19 | jobs: 20 | build: 21 | permissions: 22 | contents: read # for actions/checkout to fetch code 23 | security-events: write # for github/codeql-action/upload-sarif to upload SARIF results 24 | actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status 25 | name: Build 26 | runs-on: "ubuntu-22.04" 27 | steps: 28 | - name: Checkout code 29 | uses: actions/checkout@v3 30 | 31 | - name: Build an image from Dockerfile 32 | run: | 33 | make docker 34 | 35 | - name: Run Trivy vulnerability scanner 36 | uses: aquasecurity/trivy-action@0.24.0 37 | with: 38 | image-ref: 'oxia:latest' 39 | format: 'template' 40 | template: '@/contrib/sarif.tpl' 41 | output: 'trivy-results.sarif' 42 | severity: 'LOW,MEDIUM,HIGH,CRITICAL' 43 | 44 | - name: Upload Trivy scan results to GitHub Security tab 45 | uses: github/codeql-action/upload-sarif@v2 46 | with: 47 | sarif_file: 'trivy-results.sarif' 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | bin/oxia 3 | bin/oxia-maelstrom 4 | .DS_Store 5 | **/*.toolbox 6 | data 7 | vendor/ 8 | zz_generated.deepcopy.go 9 | pkg/generated/ 10 | -------------------------------------------------------------------------------- /.protolint.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 StreamNative, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | lint: 16 | rules: 17 | all_default: true 18 | remove: 19 | - ENUM_FIELD_NAMES_PREFIX 20 | - ENUM_FIELD_NAMES_ZERO_VALUE_END_WITH 21 | # TODO Enforce below COMMENT rules when APIs stabilize 22 | - FILE_HAS_COMMENT 23 | - SERVICES_HAVE_COMMENT 24 | - RPCS_HAVE_COMMENT 25 | - MESSAGES_HAVE_COMMENT 26 | - FIELDS_HAVE_COMMENT 27 | - ENUMS_HAVE_COMMENT 28 | - ENUM_FIELDS_HAVE_COMMENT 29 | # end 30 | rules_option: 31 | indent: 32 | not_insert_newline: true 33 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2023 StreamNative, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | FROM golang:1.24-alpine AS build 16 | 17 | RUN apk add --no-cache make git build-base bash 18 | 19 | ENV PATH=$PATH:/go/bin 20 | ADD . /src/oxia 21 | 22 | RUN cd /src/oxia \ 23 | && make 24 | 25 | FROM alpine:3.21 26 | 27 | RUN apk add --no-cache bash bash-completion jq 28 | RUN apk upgrade --no-cache 29 | 30 | RUN mkdir /oxia 31 | WORKDIR /oxia 32 | 33 | COPY --from=build /src/oxia/bin/oxia /oxia/bin/oxia 34 | ENV PATH=$PATH:/oxia/bin 35 | 36 | RUN oxia completion bash > ~/.bashrc 37 | 38 | CMD [ "/bin/bash" ] 39 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | StreamNative Oxia 2 | Copyright 2023 StreamNative, Inc. 3 | -------------------------------------------------------------------------------- /cmd/client/common/client.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package common 16 | 17 | import ( 18 | "time" 19 | 20 | "github.com/streamnative/oxia/oxia" 21 | ) 22 | 23 | var ( 24 | Config = ClientConfig{} 25 | MockedClient *MockClient 26 | ) 27 | 28 | type ClientConfig struct { 29 | ServiceAddr string 30 | Namespace string 31 | RequestTimeout time.Duration 32 | } 33 | 34 | func (ClientConfig) NewClient() (oxia.SyncClient, error) { 35 | if MockedClient != nil { 36 | return MockedClient, nil 37 | } 38 | 39 | return oxia.NewSyncClient(Config.ServiceAddr, 40 | oxia.WithRequestTimeout(Config.RequestTimeout), 41 | oxia.WithNamespace(Config.Namespace), 42 | ) 43 | } 44 | -------------------------------------------------------------------------------- /cmd/client/common/io.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package common 16 | 17 | import ( 18 | "encoding/hex" 19 | "encoding/json" 20 | "io" 21 | ) 22 | 23 | func WriteOutput(out io.Writer, value any) { 24 | if sl, ok := value.([]string); ok { 25 | writeStringSlice(out, sl) 26 | return 27 | } else if b, ok := value.([]byte); ok { 28 | writeByteSlice(out, b) 29 | return 30 | } 31 | 32 | b, err := json.Marshal(value) 33 | if err != nil { 34 | panic(err) 35 | } 36 | _, err = out.Write(append(b, "\n"...)) 37 | if err != nil { 38 | panic(err) 39 | } 40 | } 41 | 42 | func writeStringSlice(out io.Writer, sl []string) { 43 | for _, s := range sl { 44 | if _, err := out.Write([]byte(s)); err != nil { 45 | panic(err) 46 | } 47 | if _, err := out.Write([]byte("\n")); err != nil { 48 | panic(err) 49 | } 50 | } 51 | } 52 | 53 | func writeByteSlice(out io.Writer, sl []byte) { 54 | if _, err := out.Write(sl); err != nil { 55 | panic(err) 56 | } 57 | if _, err := out.Write([]byte("\n")); err != nil { 58 | panic(err) 59 | } 60 | } 61 | 62 | func WriteHexDump(out io.Writer, buffer []byte) { 63 | dump := hex.Dumper(out) 64 | if _, err := dump.Write(buffer); err != nil { 65 | panic(err) 66 | } 67 | if err := dump.Close(); err != nil { 68 | panic(err) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /cmd/client/common/io_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package common 16 | 17 | import ( 18 | "bytes" 19 | "testing" 20 | "time" 21 | 22 | "github.com/stretchr/testify/assert" 23 | ) 24 | 25 | func TestWriteOutput(t *testing.T) { 26 | for _, test := range []struct { 27 | name string 28 | result any 29 | expected string 30 | }{ 31 | {"common.OutputError", OutputError{Err: "hello"}, "{\"error\":\"hello\"}\n"}, 32 | {"common.OutputErrorEmpty", OutputError{}, "{}\n"}, 33 | {"common.OutputVersion", OutputVersion{ 34 | Key: "my-key", 35 | VersionId: 1, 36 | CreatedTimestamp: time.UnixMilli(2), 37 | ModifiedTimestamp: time.UnixMilli(3), 38 | ModificationsCount: 1, 39 | SessionId: 5, 40 | }, "{\"key\":\"my-key\",\"version_id\":1,\"created_timestamp\":\"" + time.UnixMilli(2).Format(time.RFC3339Nano) + 41 | "\",\"modified_timestamp\":\"" + time.UnixMilli(3).Format(time.RFC3339Nano) + 42 | "\",\"modifications_count\":1,\"ephemeral\":false,\"session_id\":5,\"client_identity\":\"\"}\n"}, 43 | } { 44 | t.Run(test.name, func(t *testing.T) { 45 | b := bytes.NewBufferString("") 46 | WriteOutput(b, test.result) 47 | assert.Equal(t, test.expected, b.String()) 48 | }) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /cmd/client/common/model.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package common 16 | 17 | import "time" 18 | 19 | type OutputVersion struct { 20 | Key string `json:"key"` 21 | VersionId int64 `json:"version_id"` 22 | CreatedTimestamp time.Time `json:"created_timestamp"` 23 | ModifiedTimestamp time.Time `json:"modified_timestamp"` 24 | ModificationsCount int64 `json:"modifications_count"` 25 | Ephemeral bool `json:"ephemeral"` 26 | SessionId int64 `json:"session_id"` 27 | ClientIdentity string `json:"client_identity"` 28 | } 29 | 30 | type OutputError struct { 31 | Err string `json:"error,omitempty"` 32 | } 33 | -------------------------------------------------------------------------------- /cmd/client/common/pointer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package common 16 | 17 | func PtrBool(b bool) *bool { 18 | return &b 19 | } 20 | 21 | func PtrInt64(i int64) *int64 { 22 | return &i 23 | } 24 | 25 | func PtrString(s string) *string { 26 | return &s 27 | } 28 | -------------------------------------------------------------------------------- /cmd/client/deleterange/cmd_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package deleterange 16 | 17 | import ( 18 | "bytes" 19 | "strings" 20 | "testing" 21 | 22 | "github.com/spf13/cobra" 23 | "github.com/stretchr/testify/assert" 24 | 25 | "github.com/streamnative/oxia/cmd/client/common" 26 | "github.com/streamnative/oxia/oxia" 27 | ) 28 | 29 | func runCmd(cmd *cobra.Command, args string, stdin string) (string, error) { 30 | actual := new(bytes.Buffer) 31 | cmd.SetIn(bytes.NewBufferString(stdin)) 32 | cmd.SetOut(actual) 33 | cmd.SetErr(actual) 34 | cmd.SetArgs(strings.Split(args, " ")) 35 | err := cmd.Execute() 36 | Config.Reset() 37 | return strings.TrimSpace(actual.String()), err 38 | } 39 | 40 | func TestDeleteRange_exec(t *testing.T) { 41 | var emptyOptions []oxia.DeleteRangeOption 42 | 43 | for _, test := range []struct { 44 | name string 45 | args string 46 | expectedParameters []any 47 | }{ 48 | {"range", "--key-min a --key-max c", []any{"a", "c", emptyOptions}}, 49 | {"short", "-s a -e c", []any{"a", "c", emptyOptions}}, 50 | {"partition-key", "-s a -e c -p xyz", []any{"a", "c", []oxia.DeleteRangeOption{oxia.PartitionKey("xyz")}}}, 51 | } { 52 | t.Run(test.name, func(t *testing.T) { 53 | common.MockedClient = common.NewMockClient() 54 | 55 | common.MockedClient.On("DeleteRange", test.expectedParameters...).Return(nil) 56 | _, err := runCmd(Cmd, test.args, "") 57 | assert.NoError(t, err) 58 | 59 | common.MockedClient.AssertExpectations(t) 60 | }) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /cmd/client/notifications/cmd.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package notifications 16 | 17 | import ( 18 | "log/slog" 19 | 20 | "github.com/spf13/cobra" 21 | 22 | "github.com/streamnative/oxia/cmd/client/common" 23 | "github.com/streamnative/oxia/oxia" 24 | ) 25 | 26 | var Cmd = &cobra.Command{ 27 | Use: "notifications", 28 | Short: "Get notifications stream", 29 | Long: `Follow the change notifications stream`, 30 | Args: cobra.NoArgs, 31 | RunE: exec, 32 | } 33 | 34 | func exec(_ *cobra.Command, _ []string) error { 35 | client, err := common.Config.NewClient() 36 | if err != nil { 37 | return err 38 | } 39 | 40 | defer client.Close() 41 | 42 | notifications, err := client.GetNotifications() 43 | if err != nil { 44 | return err 45 | } 46 | 47 | defer notifications.Close() 48 | 49 | for notification := range notifications.Ch() { 50 | args := []any{ 51 | slog.String("type", notification.Type.String()), 52 | slog.String("key", notification.Key), 53 | } 54 | if notification.Type == oxia.KeyCreated || notification.Type == oxia.KeyModified { 55 | args = append(args, slog.Int64("version-id", notification.VersionId)) 56 | } 57 | if notification.Type == oxia.KeyRangeRangeDeleted { 58 | args = append(args, slog.String("key-range-end", notification.KeyRangeEnd)) 59 | } 60 | slog.Info("", args...) 61 | } 62 | 63 | return nil 64 | } 65 | -------------------------------------------------------------------------------- /cmd/client/sequenceupdates/cmd.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package sequenceupdates 16 | 17 | import ( 18 | "context" 19 | "log/slog" 20 | 21 | "github.com/spf13/cobra" 22 | 23 | "github.com/streamnative/oxia/cmd/client/common" 24 | "github.com/streamnative/oxia/oxia" 25 | ) 26 | 27 | var Config = flags{} 28 | 29 | type flags struct { 30 | key string 31 | partitionKey string 32 | } 33 | 34 | func (flags *flags) Reset() { 35 | flags.key = "" 36 | flags.partitionKey = "" 37 | } 38 | 39 | func init() { 40 | Cmd.Flags().StringVarP(&Config.partitionKey, "partition-key", "p", "", "Partition Key to be used in override the shard routing") 41 | _ = Cmd.MarkFlagRequired("partition-key") 42 | } 43 | 44 | var Cmd = &cobra.Command{ 45 | Use: "sequence-updates [flags] KEY --partition-key PARTITION_KEY", 46 | Short: "Get key sequences updates", 47 | Long: `Follow the updates on a sequence of keys`, 48 | Args: cobra.ExactArgs(1), 49 | RunE: exec, 50 | } 51 | 52 | func exec(_ *cobra.Command, args []string) error { 53 | client, err := common.Config.NewClient() 54 | if err != nil { 55 | return err 56 | } 57 | 58 | defer client.Close() 59 | 60 | Config.key = args[0] 61 | updates, err := client.GetSequenceUpdates(context.Background(), Config.key, oxia.PartitionKey(Config.partitionKey)) 62 | if err != nil { 63 | return err 64 | } 65 | 66 | for key := range updates { 67 | slog.Info("", slog.String("key", key)) 68 | } 69 | 70 | return nil 71 | } 72 | -------------------------------------------------------------------------------- /cmd/flag/flag.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package flag 16 | 17 | import ( 18 | "fmt" 19 | 20 | "github.com/spf13/cobra" 21 | 22 | "github.com/streamnative/oxia/common/constant" 23 | ) 24 | 25 | func PublicAddr(cmd *cobra.Command, conf *string) { 26 | cmd.Flags().StringVarP(conf, "public-addr", "p", fmt.Sprintf("0.0.0.0:%d", constant.DefaultPublicPort), "Public service bind address") 27 | } 28 | 29 | func InternalAddr(cmd *cobra.Command, conf *string) { 30 | cmd.Flags().StringVarP(conf, "internal-addr", "i", fmt.Sprintf("0.0.0.0:%d", constant.DefaultInternalPort), "Internal service bind address") 31 | } 32 | 33 | func MetricsAddr(cmd *cobra.Command, conf *string) { 34 | cmd.Flags().StringVarP(conf, "metrics-addr", "m", fmt.Sprintf("0.0.0.0:%d", constant.DefaultMetricsPort), "Metrics service bind address") 35 | } 36 | -------------------------------------------------------------------------------- /cmd/main_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "log/slog" 19 | "testing" 20 | 21 | "github.com/spf13/cobra" 22 | "github.com/stretchr/testify/assert" 23 | 24 | "github.com/streamnative/oxia/common/logging" 25 | ) 26 | 27 | func TestCall_LogLevel_Default(t *testing.T) { 28 | var captured slog.Level 29 | rootCmd.SetArgs([]string{}) 30 | rootCmd.RunE = func(cmd *cobra.Command, args []string) error { 31 | captured = logging.LogLevel 32 | return nil 33 | } 34 | err := rootCmd.Execute() 35 | assert.Equal(t, logging.DefaultLogLevel, captured) 36 | assert.NoError(t, err) 37 | } 38 | 39 | func TestCall_LogLevel(t *testing.T) { 40 | tests := []struct { 41 | name string 42 | level string 43 | expectedErr error 44 | expectedLevel slog.Level 45 | }{ 46 | {"debug", "debug", nil, slog.LevelDebug}, 47 | {"info", "info", nil, slog.LevelInfo}, 48 | {"warn", "warn", nil, slog.LevelWarn}, 49 | {"error", "error", nil, slog.LevelError}, 50 | {"junk", "junk", LogLevelError("junk"), slog.LevelInfo}, 51 | } 52 | for _, test := range tests { 53 | t.Run(test.name, func(t *testing.T) { 54 | invoked := false 55 | rootCmd.SetArgs(append([]string{"-l"}, test.level)) 56 | rootCmd.RunE = func(cmd *cobra.Command, args []string) error { 57 | invoked = true 58 | assert.Equal(t, test.expectedLevel, logging.LogLevel) 59 | return nil 60 | } 61 | err := rootCmd.Execute() 62 | if err == nil { 63 | assert.True(t, invoked) 64 | } else { 65 | assert.False(t, invoked) 66 | } 67 | assert.ErrorIs(t, err, test.expectedErr) 68 | }) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /cmd/pebble/cmd.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package pebble 16 | 17 | import ( 18 | "github.com/cockroachdb/pebble/tool" 19 | "github.com/spf13/cobra" 20 | 21 | "github.com/streamnative/oxia/server/kv" 22 | ) 23 | 24 | var ( 25 | Cmd = &cobra.Command{ 26 | Use: "pebble", 27 | Short: "Pebble DB utils", 28 | Long: `Tools for the Pebble DB`, 29 | } 30 | ) 31 | 32 | func init() { 33 | t := tool.New(tool.DefaultComparer(kv.OxiaSlashSpanComparer)) 34 | 35 | for _, cmd := range t.Commands { 36 | Cmd.AddCommand(cmd) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /cmd/server/cmd_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package server 16 | 17 | import ( 18 | "strings" 19 | "testing" 20 | "time" 21 | 22 | "github.com/spf13/cobra" 23 | "github.com/stretchr/testify/assert" 24 | 25 | "github.com/streamnative/oxia/server" 26 | ) 27 | 28 | func TestServerCmd(t *testing.T) { 29 | for _, test := range []struct { 30 | args []string 31 | expectedConf server.Config 32 | isErr bool 33 | }{ 34 | {[]string{}, server.Config{ 35 | PublicServiceAddr: "0.0.0.0:6648", 36 | InternalServiceAddr: "0.0.0.0:6649", 37 | MetricsServiceAddr: "0.0.0.0:8080", 38 | DataDir: "./data/db", 39 | WalDir: "./data/wal", 40 | WalRetentionTime: 1 * time.Hour, 41 | WalSyncData: true, 42 | NotificationsRetentionTime: 1 * time.Hour, 43 | DbBlockCacheMB: 100, 44 | }, false}, 45 | } { 46 | t.Run(strings.Join(test.args, "_"), func(t *testing.T) { 47 | Cmd.SetArgs(test.args) 48 | Cmd.RunE = func(cmd *cobra.Command, args []string) error { 49 | return nil 50 | } 51 | err := Cmd.Execute() 52 | assert.Equal(t, test.isErr, err != nil) 53 | assert.Equal(t, test.expectedConf, conf) 54 | }) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /cmd/wal/cmd.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package wal 16 | 17 | import ( 18 | "github.com/spf13/cobra" 19 | 20 | "github.com/streamnative/oxia/cmd/wal/scan" 21 | 22 | "github.com/streamnative/oxia/cmd/wal/common" 23 | "github.com/streamnative/oxia/cmd/wal/perf" 24 | "github.com/streamnative/oxia/cmd/wal/truncate" 25 | ) 26 | 27 | var ( 28 | Cmd = &cobra.Command{ 29 | Use: "wal", 30 | Short: "Wal utils", 31 | Long: `Tools for the oxia WAL`, 32 | } 33 | ) 34 | 35 | func init() { 36 | Cmd.PersistentFlags().Int64Var(&common.WalOption.Shard, "shard", 0, "shard id") 37 | Cmd.PersistentFlags().StringVar(&common.WalOption.Namespace, "namespace", "default", "namespace name") 38 | Cmd.PersistentFlags().StringVar(&common.WalOption.WalDir, "wal-dir", "data/wal", "directory path") 39 | Cmd.AddCommand(truncate.Cmd) 40 | Cmd.AddCommand(perf.Cmd) 41 | Cmd.AddCommand(scan.Cmd) 42 | 43 | if err := Cmd.MarkPersistentFlagRequired("shard"); err != nil { 44 | panic(err) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /cmd/wal/common/option.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package common 16 | 17 | type WalOptions struct { 18 | Namespace string 19 | Shard int64 20 | WalDir string 21 | } 22 | 23 | var WalOption WalOptions 24 | -------------------------------------------------------------------------------- /common/cache/memoize.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package cache 16 | 17 | import ( 18 | "sync" 19 | "time" 20 | ) 21 | 22 | type memoize[T any] struct { 23 | sync.RWMutex 24 | 25 | provider func() T 26 | cachedValue T 27 | lastCalled time.Time 28 | cacheTime time.Duration 29 | } 30 | 31 | // Memoize is used to cache the result of the invocation of a function 32 | // for a certain amount of time. 33 | func Memoize[T any](provider func() T, cacheTime time.Duration) func() T { 34 | m := memoize[T]{ 35 | provider: provider, 36 | cacheTime: cacheTime, 37 | } 38 | 39 | return m.Get 40 | } 41 | 42 | func (m *memoize[T]) Get() T { 43 | m.RLock() 44 | 45 | if time.Since(m.lastCalled) < m.cacheTime { 46 | defer m.RUnlock() 47 | return m.cachedValue 48 | } 49 | 50 | m.RUnlock() 51 | 52 | m.Lock() 53 | defer m.Unlock() 54 | 55 | // Since we released the read-lock in between, it's 56 | // better to re-check the last-called time 57 | if time.Since(m.lastCalled) >= m.cacheTime { 58 | m.cachedValue = m.provider() 59 | m.lastCalled = time.Now() 60 | } 61 | return m.cachedValue 62 | } 63 | -------------------------------------------------------------------------------- /common/cache/memoize_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package cache 16 | 17 | import ( 18 | "sync/atomic" 19 | "testing" 20 | "time" 21 | 22 | "github.com/stretchr/testify/assert" 23 | ) 24 | 25 | func TestMemoize(t *testing.T) { 26 | count := atomic.Int64{} 27 | 28 | f := func() int64 { 29 | return count.Add(1) 30 | } 31 | 32 | m := Memoize(f, 1*time.Second) 33 | 34 | for i := 0; i < 10; i++ { 35 | assert.EqualValues(t, 1, m()) 36 | } 37 | 38 | // Let the cached value expire 39 | time.Sleep(1100 * time.Millisecond) 40 | 41 | for i := 0; i < 10; i++ { 42 | assert.EqualValues(t, 2, m()) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /common/channel/noblock.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package channel 16 | 17 | func PushNoBlock[T any](ch chan T, t T) bool { 18 | select { 19 | case ch <- t: 20 | return true 21 | default: 22 | return false 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /common/channel/override_channel.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package channel 16 | 17 | import ( 18 | "context" 19 | "io" 20 | "sync" 21 | ) 22 | 23 | // OverrideChannel is a type of channel that only keeps the latest value, the write operation never blocks. 24 | type OverrideChannel[T any] interface { 25 | io.Closer 26 | 27 | Receive(ctx context.Context) (T, error) 28 | 29 | // 30 | Ch() chan T 31 | 32 | // Update the latest value 33 | WriteLast(value T) 34 | } 35 | 36 | type overrideChannel[T any] struct { 37 | sync.Mutex 38 | ch chan T 39 | } 40 | 41 | func NewOverrideChannel[T any]() OverrideChannel[T] { 42 | return &overrideChannel[T]{ 43 | ch: make(chan T, 1), 44 | } 45 | } 46 | 47 | func (o *overrideChannel[T]) Close() error { 48 | close(o.ch) 49 | return nil 50 | } 51 | 52 | func (o *overrideChannel[T]) Receive(ctx context.Context) (t T, err error) { 53 | select { 54 | case t = <-o.ch: 55 | return t, nil 56 | case <-ctx.Done(): 57 | return t, ctx.Err() 58 | } 59 | } 60 | 61 | func (o *overrideChannel[T]) Ch() chan T { 62 | return o.ch 63 | } 64 | 65 | func (o *overrideChannel[T]) WriteLast(value T) { 66 | o.Lock() 67 | defer o.Unlock() 68 | 69 | for { 70 | select { 71 | case o.ch <- value: 72 | // If we were able to write on the channel, we're good: the receiver will eventually read 73 | // the notification from here 74 | return 75 | 76 | default: 77 | // If the channel buffer is full, empty the channel and retry 78 | select { 79 | case <-o.ch: 80 | continue 81 | default: 82 | continue 83 | } 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /common/channel/override_channel_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package channel 16 | 17 | import ( 18 | "context" 19 | "sync/atomic" 20 | "testing" 21 | "time" 22 | 23 | "github.com/stretchr/testify/assert" 24 | ) 25 | 26 | func TestOverrideChannel(t *testing.T) { 27 | och := NewOverrideChannel[int]() 28 | 29 | och.WriteLast(1) 30 | och.WriteLast(2) 31 | 32 | n, err := och.Receive(context.Background()) 33 | assert.NoError(t, err) 34 | assert.Equal(t, 2, n) 35 | 36 | done := atomic.Bool{} 37 | lastWritten := atomic.Int32{} 38 | go func() { 39 | x, _ := och.Receive(context.Background()) 40 | 41 | done.Store(true) 42 | lastWritten.Store(int32(x)) 43 | }() 44 | 45 | time.Sleep(1 * time.Second) 46 | assert.False(t, done.Load()) 47 | 48 | och.WriteLast(5) 49 | time.Sleep(1 * time.Second) 50 | 51 | assert.True(t, done.Load()) 52 | assert.EqualValues(t, 5, lastWritten.Load()) 53 | } 54 | -------------------------------------------------------------------------------- /common/channel/read.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package channel 16 | 17 | import ( 18 | "context" 19 | 20 | "github.com/streamnative/oxia/common/entity" 21 | ) 22 | 23 | func ReadAll[T any](ctx context.Context, ch chan *entity.TWithError[T]) ([]T, error) { 24 | container := make([]T, 0) 25 | for { 26 | select { 27 | case t, more := <-ch: 28 | if !more { 29 | return container, nil 30 | } 31 | if t.Err != nil { 32 | return nil, t.Err 33 | } 34 | container = append(container, t.T) 35 | case <-ctx.Done(): 36 | return nil, ctx.Err() 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /common/collection/map.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package collection 16 | 17 | type Map[K comparable, V any] interface { 18 | Put(key K, value V) 19 | Get(key K) (value V, found bool) 20 | Remove(key K) 21 | Keys() []K 22 | Empty() bool 23 | Size() int 24 | Clear() 25 | Values() []V 26 | String() string 27 | } 28 | 29 | func NewVisibleMap[K comparable, V any]() Map[K, V] { 30 | return &visibleMap[K, V]{ 31 | container: make(map[K]V), 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /common/collection/set_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package collection 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/stretchr/testify/assert" 21 | ) 22 | 23 | func TestSet(t *testing.T) { 24 | s := NewSet[int]() 25 | assert.True(t, s.IsEmpty()) 26 | assert.Equal(t, 0, s.Count()) 27 | assert.False(t, s.Contains(5)) 28 | 29 | s.Add(5) 30 | assert.False(t, s.IsEmpty()) 31 | assert.Equal(t, 1, s.Count()) 32 | assert.True(t, s.Contains(5)) 33 | 34 | s.Remove(5) 35 | assert.True(t, s.IsEmpty()) 36 | assert.Equal(t, 0, s.Count()) 37 | assert.False(t, s.Contains(5)) 38 | 39 | s.Add(1) 40 | s.Add(1) 41 | assert.False(t, s.IsEmpty()) 42 | assert.Equal(t, 1, s.Count()) 43 | assert.True(t, s.Contains(1)) 44 | 45 | s.Add(3) 46 | s.Add(2) 47 | assert.Equal(t, []int{1, 2, 3}, s.GetSorted()) 48 | 49 | o := NewSetFrom([]int{3, 4, 5}) 50 | assert.Equal(t, []int{1, 2}, s.Complement(o).GetSorted()) 51 | } 52 | -------------------------------------------------------------------------------- /common/compare/compare_with_slash.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package compare 16 | 17 | import ( 18 | "bytes" 19 | "math" 20 | 21 | "github.com/cockroachdb/pebble" 22 | ) 23 | 24 | func CompareWithSlash(a, b []byte) int { //nolint:revive 25 | for len(a) > 0 && len(b) > 0 { 26 | idxA, idxB := bytes.IndexByte(a, '/'), bytes.IndexByte(b, '/') 27 | switch { 28 | case idxA < 0 && idxB < 0: 29 | return bytes.Compare(a, b) 30 | case idxA < 0 && idxB >= 0: 31 | return -1 32 | case idxA >= 0 && idxB < 0: 33 | return +1 34 | } 35 | 36 | // At this point, both slices have '/' 37 | spanA, spanB := a[:idxA], b[:idxB] 38 | spanRes := bytes.Compare(spanA, spanB) 39 | if spanRes != 0 { 40 | return spanRes 41 | } 42 | 43 | a, b = a[idxA+1:], b[idxB+1:] 44 | } 45 | 46 | switch { 47 | case len(a) < len(b): 48 | return -1 49 | case len(a) > len(b): 50 | return +1 51 | } 52 | 53 | return 0 54 | } 55 | 56 | func AbbreviatedKeyDisableSlash(key []byte) uint64 { 57 | slashPosition := bytes.IndexByte(key, '/') 58 | if slashPosition != -1 { 59 | return math.MaxUint64 60 | } 61 | return pebble.DefaultComparer.AbbreviatedKey(key) 62 | } 63 | -------------------------------------------------------------------------------- /common/compare/compare_with_slash_benchmark_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package compare 16 | 17 | import ( 18 | "bytes" 19 | "testing" 20 | ) 21 | 22 | var benchKeyA = []byte("/test/aaaaaaaaaaa/bbbbbbbbbbb/cccccccccccc/dddddddddddddd") 23 | var benchKeyB = []byte("/test/aaaaaaaaaaa/bbbbbbbbbbb/ccccccccccccddddddddddddddd") 24 | 25 | func Benchmark_StandardCompare(b *testing.B) { 26 | for i := 0; i < b.N; i++ { 27 | bytes.Compare(benchKeyA, benchKeyB) 28 | } 29 | } 30 | 31 | func Benchmark_CompareWithSlash(b *testing.B) { 32 | for i := 0; i < b.N; i++ { 33 | CompareWithSlash(benchKeyA, benchKeyB) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /common/concurrent/callback.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package concurrent 16 | 17 | type Callback[T any] interface { 18 | // OnComplete is invoked when the operation completes successfully with the result 't' of type T. 19 | OnComplete(t T) 20 | 21 | // OnCompleteError is invoked when the operation fails, providing an error 'err' indicating the failure reason. 22 | OnCompleteError(err error) 23 | } 24 | 25 | type StreamCallback[T any] interface { 26 | 27 | // OnNext is called whenever a new data item of type T is received from the stream. 28 | // It processes the received data item and returns an error if any issues occur during processing. 29 | // This method allows for custom logic to be applied to each individual data item in the stream. 30 | OnNext(t T) error 31 | 32 | // OnComplete is called when the stream has ended, either successfully or due to an error. 33 | // The 'err' parameter indicates the status of the stream completion. 34 | // If the stream ended successfully, 'err' will be nil. Otherwise, 35 | // it will contain the error that caused the stream to terminate. 36 | OnComplete(err error) 37 | } 38 | -------------------------------------------------------------------------------- /common/concurrent/future.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package concurrent 16 | 17 | import ( 18 | "context" 19 | ) 20 | 21 | type Future[T any] interface { 22 | 23 | // Wait until the future is either completed or failed 24 | // You should only call wait once 25 | Wait(ctx context.Context) (T, error) 26 | 27 | // 28 | Complete(result T) 29 | 30 | // Fail Signal that one party has failed in the operation 31 | Fail(err error) 32 | } 33 | 34 | type value[T any] struct { 35 | t T 36 | err error 37 | } 38 | 39 | type future[T any] struct { 40 | val chan value[T] 41 | } 42 | 43 | func NewFuture[T any]() Future[T] { 44 | return &future[T]{ 45 | val: make(chan value[T], 1), 46 | } 47 | } 48 | 49 | func (f *future[T]) Wait(ctx context.Context) (t T, err error) { 50 | select { 51 | case v := <-f.val: 52 | return v.t, v.err 53 | 54 | case <-ctx.Done(): 55 | return t, ctx.Err() 56 | } 57 | } 58 | 59 | func (f *future[T]) Complete(result T) { 60 | f.val <- value[T]{result, nil} 61 | } 62 | 63 | func (f *future[T]) Fail(err error) { 64 | var t T 65 | f.val <- value[T]{t, err} 66 | } 67 | -------------------------------------------------------------------------------- /common/concurrent/signal.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package concurrent 16 | 17 | import ( 18 | "io" 19 | "log/slog" 20 | "os" 21 | "os/signal" 22 | "syscall" 23 | ) 24 | 25 | func WaitUntilSignal(closers ...io.Closer) { 26 | c := make(chan os.Signal, 1) 27 | signal.Notify(c, os.Interrupt, syscall.SIGTERM) 28 | 29 | sig := <-c 30 | slog.Info( 31 | "Received signal, exiting", 32 | slog.String("signal", sig.String()), 33 | ) 34 | 35 | code := 0 36 | for _, closer := range closers { 37 | if err := closer.Close(); err != nil { 38 | slog.Error( 39 | "Failed when shutting down server", 40 | slog.Any("error", err), 41 | ) 42 | code = 1 43 | } 44 | } 45 | 46 | if code == 0 { 47 | slog.Info("Shutdown Completed") 48 | } 49 | os.Exit(code) 50 | } 51 | -------------------------------------------------------------------------------- /common/concurrent/utils.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package concurrent 16 | 17 | import "github.com/streamnative/oxia/common/entity" 18 | 19 | type streamCallbackChannelAdaptor[T any] struct { 20 | Ch chan *entity.TWithError[T] 21 | } 22 | 23 | func (c *streamCallbackChannelAdaptor[T]) OnNext(t T) error { 24 | c.Ch <- &entity.TWithError[T]{ 25 | T: t, 26 | Err: nil, 27 | } 28 | return nil 29 | } 30 | 31 | func (c *streamCallbackChannelAdaptor[T]) OnComplete(err error) { 32 | if err != nil { 33 | c.Ch <- &entity.TWithError[T]{ 34 | Err: err, 35 | } 36 | } 37 | close(c.Ch) 38 | } 39 | 40 | func ReadFromStreamCallback[T any](ch chan *entity.TWithError[T]) StreamCallback[T] { 41 | adaptor := &streamCallbackChannelAdaptor[T]{Ch: ch} 42 | return NewStreamOnce(adaptor) 43 | } 44 | 45 | type streamCallbackCompleteOnly struct{ onComplete func(err error) } 46 | 47 | func (*streamCallbackCompleteOnly) OnNext(any) error { return nil } 48 | func (c *streamCallbackCompleteOnly) OnComplete(err error) { c.onComplete(err) } 49 | -------------------------------------------------------------------------------- /common/concurrent/wait_group.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package concurrent 16 | 17 | import "context" 18 | 19 | // WaitGroup is similar to sync.WaitGroup but adds 2 capabilities: 20 | // 1. Returning an error if any operation fails 21 | // 2. Accept a context to cancel the Wait 22 | type WaitGroup interface { 23 | 24 | // Wait until all the parties in the group are either done or if there is any failure 25 | // You should only call wait once 26 | Wait(ctx context.Context) error 27 | 28 | // Done Signals that one party in the group is done 29 | Done() 30 | 31 | // Fail Signal that one party has failed in the operation 32 | Fail(err error) 33 | } 34 | 35 | type waitGroup struct { 36 | parties int 37 | responses chan error 38 | } 39 | 40 | func NewWaitGroup(parties int) WaitGroup { 41 | return &waitGroup{ 42 | parties: parties, 43 | responses: make(chan error, parties), 44 | } 45 | } 46 | 47 | func (g *waitGroup) Wait(ctx context.Context) error { 48 | for i := 0; i < g.parties; i++ { 49 | select { 50 | case err := <-g.responses: 51 | if err != nil { 52 | return err 53 | } 54 | 55 | case <-ctx.Done(): 56 | return ctx.Err() 57 | } 58 | } 59 | 60 | // Everyone has completed successfully 61 | return nil 62 | } 63 | 64 | func (g *waitGroup) Done() { 65 | select { 66 | case g.responses <- nil: 67 | default: 68 | } 69 | } 70 | 71 | func (g *waitGroup) Fail(err error) { 72 | g.responses <- err 73 | } 74 | -------------------------------------------------------------------------------- /common/constant/constants.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package constant 16 | 17 | import "time" 18 | 19 | const ( 20 | MetadataTerm = "term" 21 | MetadataNamespace = "namespace" 22 | MetadataShardId = "shard-id" 23 | DefaultNamespace = "default" 24 | 25 | DefaultPublicPort = 6648 26 | DefaultInternalPort = 6649 27 | DefaultMetricsPort = 8080 28 | 29 | MaxSessionTimeout = 5 * time.Minute 30 | MinSessionTimeout = 2 * time.Second 31 | ) 32 | -------------------------------------------------------------------------------- /common/constant/keys.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package constant 16 | 17 | // InternalKeyPrefix is the prefix of keys used by oxia. 18 | const InternalKeyPrefix = "__oxia/" 19 | -------------------------------------------------------------------------------- /common/entity/opt_json.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package entity 16 | 17 | import ( 18 | "encoding/json" 19 | "reflect" 20 | 21 | "github.com/mitchellh/mapstructure" 22 | "github.com/pkg/errors" 23 | ) 24 | 25 | type OptBooleanDefaultTrue struct { 26 | val *bool 27 | } 28 | 29 | func Bool(val bool) OptBooleanDefaultTrue { 30 | return OptBooleanDefaultTrue{&val} 31 | } 32 | 33 | func (o *OptBooleanDefaultTrue) Get() bool { 34 | if o.val != nil { 35 | return *o.val 36 | } 37 | 38 | return true 39 | } 40 | 41 | func (o *OptBooleanDefaultTrue) MarshalJSON() ([]byte, error) { 42 | return json.Marshal(o.val) 43 | } 44 | 45 | func (o OptBooleanDefaultTrue) MarshalYAML() (any, error) { 46 | return o.val, nil 47 | } 48 | 49 | var trueVal = true 50 | var falseVal = false 51 | 52 | func (o *OptBooleanDefaultTrue) UnmarshalJSON(data []byte) error { 53 | s := string(data) 54 | if s == "null" || s == "" || s == `""` { 55 | o.val = &trueVal 56 | return nil 57 | } 58 | 59 | if s == "true" { 60 | o.val = &trueVal 61 | return nil 62 | } 63 | 64 | if s == "false" { 65 | o.val = &falseVal 66 | return nil 67 | } 68 | 69 | return errors.New("invalid boolean value: " + s) 70 | } 71 | 72 | func OptBooleanViperHook() mapstructure.DecodeHookFuncType { 73 | return func(_ reflect.Type, t reflect.Type, data any) (any, error) { 74 | if t == reflect.TypeOf(OptBooleanDefaultTrue{}) { 75 | b, ok := data.(bool) 76 | if !ok { 77 | return nil, errors.Errorf("invalid boolean value: '%v'", data) 78 | } 79 | 80 | return Bool(b), nil 81 | } 82 | 83 | return data, nil 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /common/entity/t_error.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package entity 16 | 17 | type TWithError[T any] struct { 18 | T T 19 | Err error 20 | } 21 | -------------------------------------------------------------------------------- /common/hash/hash.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package hash 16 | 17 | import ( 18 | "github.com/zeebo/xxh3" 19 | ) 20 | 21 | func Xxh332(key string) uint32 { 22 | return uint32(xxh3.HashString(key)) 23 | } 24 | -------------------------------------------------------------------------------- /common/hash/hash_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package hash 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/stretchr/testify/assert" 21 | ) 22 | 23 | func TestXxh332(t *testing.T) { 24 | for _, test := range []struct { 25 | key string 26 | expected uint32 27 | }{ 28 | {"foo", 125730186}, 29 | {"bar", 2687685474}, 30 | {"baz", 862947621}, 31 | } { 32 | t.Run(test.key, func(t *testing.T) { 33 | hash := Xxh332(test.key) 34 | assert.Equal(t, test.expected, hash) 35 | }) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /common/metric/gauge.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package metric 16 | 17 | import ( 18 | "context" 19 | "log/slog" 20 | "os" 21 | 22 | "go.opentelemetry.io/otel/metric" 23 | ) 24 | 25 | type Gauge interface { 26 | Unregister() 27 | } 28 | 29 | type gauge struct { 30 | gauge metric.Int64ObservableGauge 31 | attrs metric.MeasurementOption 32 | callback func() int64 33 | registration metric.Registration 34 | } 35 | 36 | func (g *gauge) Unregister() { 37 | if err := g.registration.Unregister(); err != nil { 38 | slog.Error( 39 | "Failed to unregister gauge", 40 | slog.Any("error", err), 41 | ) 42 | os.Exit(1) 43 | } 44 | } 45 | 46 | func NewGauge(name string, description string, unit Unit, labels map[string]any, callback func() int64) Gauge { 47 | g, err := meter.Int64ObservableGauge(name, 48 | metric.WithUnit(string(unit)), 49 | metric.WithDescription(description), 50 | ) 51 | fatalOnErr(err, name) 52 | 53 | res := &gauge{ 54 | gauge: g, 55 | attrs: getAttrs(labels), 56 | callback: callback, 57 | } 58 | 59 | res.registration, err = meter.RegisterCallback(func(ctx context.Context, obs metric.Observer) error { 60 | if ctx.Err() != nil { 61 | return ctx.Err() 62 | } 63 | obs.ObserveInt64(res.gauge, res.callback(), res.attrs) 64 | return nil 65 | }, g) 66 | 67 | if err != nil { 68 | slog.Error( 69 | "Failed to register gauge", 70 | slog.Any("error", err), 71 | ) 72 | os.Exit(1) 73 | } 74 | return res 75 | } 76 | -------------------------------------------------------------------------------- /common/metric/histogram.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package metric 16 | 17 | import ( 18 | "context" 19 | 20 | "go.opentelemetry.io/otel/metric" 21 | ) 22 | 23 | var sizeBucketsBytes = []float64{ 24 | 0x10, 0x20, 0x40, 0x80, 25 | 0x100, 0x200, 0x400, 0x800, 26 | 0x1000, 0x2000, 0x4000, 0x8000, 27 | 0x10000, 0x20000, 0x40000, 0x80000, 28 | 0x100000, 0x200000, 0x400000, 0x800000, 29 | } 30 | 31 | var sizeBucketsCount = []float64{1, 5, 10, 20, 50, 100, 200, 500, 1000, 10_000, 20_000, 50_000, 100_000, 1_000_000} 32 | 33 | type Histogram interface { 34 | Record(value int) 35 | } 36 | 37 | type histogram struct { 38 | h metric.Int64Histogram 39 | attrs metric.MeasurementOption 40 | } 41 | 42 | func (t *histogram) Record(size int) { 43 | t.h.Record(context.Background(), int64(size), t.attrs) 44 | } 45 | 46 | func NewCountHistogram(name string, description string, labels map[string]any) Histogram { 47 | return newHistogram(name, Dimensionless, description, labels) 48 | } 49 | 50 | func NewBytesHistogram(name string, description string, labels map[string]any) Histogram { 51 | return newHistogram(name, Bytes, description, labels) 52 | } 53 | 54 | func newHistogram(name string, unit Unit, description string, labels map[string]any) Histogram { 55 | h, err := meter.Int64Histogram( 56 | name, 57 | metric.WithUnit(string(unit)), 58 | metric.WithDescription(description), 59 | ) 60 | fatalOnErr(err, name) 61 | 62 | return &histogram{h: h, attrs: getAttrs(labels)} 63 | } 64 | -------------------------------------------------------------------------------- /common/metric/latency_histogram.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package metric 16 | 17 | import ( 18 | "context" 19 | "time" 20 | 21 | "go.opentelemetry.io/otel/metric" 22 | ) 23 | 24 | var latencyBucketsMillis = []float64{ 25 | 0.1, 0.2, 0.5, 1, 2, 5, 10, 20, 50, 100, 200, 500, 1_000, 2_000, 5_000, 10_000, 20_000, 50_000, 26 | } 27 | 28 | type Timer struct { 29 | histo *latencyHistogram 30 | start time.Time 31 | } 32 | 33 | func (tm Timer) Done() { 34 | tm.histo.histo.Record(context.Background(), float64(time.Since(tm.start).Microseconds())/1000.0, tm.histo.attrs) 35 | } 36 | 37 | type LatencyHistogram interface { 38 | Timer() Timer 39 | } 40 | 41 | type latencyHistogram struct { 42 | histo metric.Float64Histogram 43 | attrs metric.MeasurementOption 44 | } 45 | 46 | func (t *latencyHistogram) Timer() Timer { 47 | return Timer{t, time.Now()} 48 | } 49 | 50 | func NewLatencyHistogram(name string, description string, labels map[string]any) LatencyHistogram { 51 | h, err := meter.Float64Histogram( 52 | name, 53 | metric.WithUnit(string(Milliseconds)), 54 | metric.WithDescription(description), 55 | ) 56 | fatalOnErr(err, name) 57 | 58 | return &latencyHistogram{histo: h, attrs: getAttrs(labels)} 59 | } 60 | -------------------------------------------------------------------------------- /common/metric/meters.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package metric 16 | 17 | import ( 18 | "fmt" 19 | "log/slog" 20 | "os" 21 | 22 | "go.opentelemetry.io/otel/attribute" 23 | "go.opentelemetry.io/otel/metric" 24 | ) 25 | 26 | var meter metric.Meter 27 | 28 | func LabelsForShard(namespace string, shard int64) map[string]any { 29 | return map[string]any{ 30 | "shard": shard, 31 | "oxia_namespace": namespace, 32 | } 33 | } 34 | 35 | func fatalOnErr(err error, name string) { 36 | if err != nil { 37 | slog.Error( 38 | "Failed to create metric", 39 | slog.String("metric-name", name), 40 | ) 41 | os.Exit(1) 42 | } 43 | } 44 | 45 | func getAttrs(labels map[string]any) (options metric.MeasurementOption) { 46 | attrs := make([]attribute.KeyValue, 0) 47 | for k, v := range labels { 48 | key := attribute.Key(k) 49 | var attr attribute.KeyValue 50 | switch t := v.(type) { 51 | case uint32: 52 | attr = key.Int64(int64(t)) 53 | case int64: 54 | attr = key.Int64(t) 55 | case int: 56 | attr = key.Int(t) 57 | case float64: 58 | attr = key.Float64(t) 59 | case bool: 60 | attr = key.Bool(t) 61 | case string: 62 | attr = key.String(t) 63 | 64 | default: 65 | slog.Error(fmt.Sprintf("Invalid label type %#v", v)) 66 | os.Exit(1) 67 | } 68 | 69 | attrs = append(attrs, attr) 70 | } 71 | 72 | return metric.WithAttributes(attrs...) 73 | } 74 | -------------------------------------------------------------------------------- /common/metric/metrics_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package metric 16 | 17 | import ( 18 | "fmt" 19 | "io" 20 | "net/http" 21 | "testing" 22 | 23 | "github.com/stretchr/testify/assert" 24 | ) 25 | 26 | func TestPrometheusMetrics(t *testing.T) { 27 | metrics, err := Start("localhost:0") 28 | assert.NoError(t, err) 29 | 30 | url := fmt.Sprintf("http://localhost:%d/metrics", metrics.Port()) 31 | response, err := http.Get(url) 32 | assert.NoError(t, err) 33 | if response != nil && response.Body != nil { 34 | defer response.Body.Close() 35 | } 36 | 37 | assert.Equal(t, 200, response.StatusCode) 38 | 39 | body, err := io.ReadAll(response.Body) 40 | assert.NoError(t, err) 41 | 42 | // Looks like exposition format 43 | assert.Equal(t, "# HELP ", string(body[0:7])) 44 | 45 | err = metrics.Close() 46 | assert.NoError(t, err) 47 | 48 | response2, err := http.Get(url) 49 | assert.ErrorContains(t, err, "connection refused") 50 | assert.Nil(t, response2) 51 | 52 | if response2 != nil && response2.Body != nil { 53 | defer response2.Body.Close() 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /common/metric/unit.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package metric 16 | 17 | // Unit has been deprecated in otel: https://github.com/open-telemetry/opentelemetry-go/pull/3776 18 | type Unit string 19 | 20 | const ( 21 | Dimensionless Unit = "1" 22 | Bytes Unit = "By" 23 | Milliseconds Unit = "ms" 24 | ) 25 | -------------------------------------------------------------------------------- /common/object/ref_count.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package object 16 | 17 | import ( 18 | "io" 19 | "sync/atomic" 20 | ) 21 | 22 | type RefCount[T io.Closer] interface { 23 | io.Closer 24 | 25 | Acquire() RefCount[T] 26 | 27 | RefCnt() int32 28 | 29 | Get() T 30 | } 31 | 32 | func NewRefCount[T io.Closer](t T) RefCount[T] { 33 | res := &refCount[T]{ 34 | rc: &atomic.Int32{}, 35 | t: t, 36 | } 37 | res.rc.Store(1) 38 | return res 39 | } 40 | 41 | func (r refCount[T]) RefCnt() int32 { 42 | return r.rc.Load() 43 | } 44 | 45 | type refCount[T io.Closer] struct { 46 | rc *atomic.Int32 47 | t T 48 | } 49 | 50 | func (r refCount[T]) Close() error { 51 | if count := r.rc.Add(-1); count == 0 { 52 | return r.t.Close() 53 | } 54 | 55 | return nil 56 | } 57 | 58 | func (r refCount[T]) Acquire() RefCount[T] { 59 | r.rc.Add(1) 60 | return &refCount[T]{ 61 | rc: r.rc, 62 | t: r.t, 63 | } 64 | } 65 | 66 | func (r refCount[T]) Get() T { 67 | return r.t 68 | } 69 | -------------------------------------------------------------------------------- /common/object/ref_count_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package object 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/stretchr/testify/assert" 21 | ) 22 | 23 | type testRc struct { 24 | closeFunc func() 25 | } 26 | 27 | func (t *testRc) Close() error { 28 | t.closeFunc() 29 | return nil 30 | } 31 | 32 | func TestRefCount(t *testing.T) { 33 | var done bool 34 | rc := NewRefCount[*testRc](&testRc{ 35 | closeFunc: func() { 36 | done = true 37 | }, 38 | }) 39 | 40 | assert.EqualValues(t, 1, rc.RefCnt()) 41 | 42 | rc2 := rc.Acquire() 43 | assert.EqualValues(t, 2, rc.RefCnt()) 44 | assert.EqualValues(t, 2, rc2.RefCnt()) 45 | 46 | assert.NoError(t, rc.Close()) 47 | assert.EqualValues(t, 1, rc.RefCnt()) 48 | assert.EqualValues(t, 1, rc2.RefCnt()) 49 | assert.False(t, done) 50 | 51 | assert.NoError(t, rc.Close()) 52 | assert.EqualValues(t, 0, rc.RefCnt()) 53 | assert.EqualValues(t, 0, rc2.RefCnt()) 54 | assert.True(t, done) 55 | } 56 | -------------------------------------------------------------------------------- /common/process/run.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package process 16 | 17 | import ( 18 | "io" 19 | "log/slog" 20 | "os" 21 | 22 | "github.com/streamnative/oxia/common/concurrent" 23 | ) 24 | 25 | func RunProcess(startProcess func() (io.Closer, error)) { 26 | profiler := RunProfiling() 27 | process, err := startProcess() 28 | if err != nil { 29 | slog.Error( 30 | "Failed to start the process", 31 | slog.Any("error", err), 32 | ) 33 | os.Exit(1) 34 | } 35 | 36 | concurrent.WaitUntilSignal( 37 | process, 38 | profiler, 39 | ) 40 | } 41 | -------------------------------------------------------------------------------- /common/rpc/client_pool_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package rpc 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/stretchr/testify/assert" 21 | ) 22 | 23 | func TestClientPool_GetActualAddress(t *testing.T) { 24 | pool := NewClientPool(nil, nil) 25 | poolInstance := pool.(*clientPool) 26 | 27 | address := poolInstance.getActualAddress("tls://xxxxaa:6648") 28 | assert.Equal(t, "xxxxaa:6648", address) 29 | 30 | actualAddress := poolInstance.getActualAddress("xxxxaaa:6649") 31 | assert.Equal(t, "xxxxaaa:6649", actualAddress) 32 | } 33 | 34 | func TestClientPool_GetTransportCredential(t *testing.T) { 35 | pool := NewClientPool(nil, nil) 36 | poolInstance := pool.(*clientPool) 37 | 38 | credential := poolInstance.getTransportCredential("tls://xxxxaa:6648") 39 | assert.Equal(t, "tls", credential.Info().SecurityProtocol) 40 | 41 | credential = poolInstance.getTransportCredential("xxxxaaa:6649") 42 | assert.Equal(t, "insecure", credential.Info().SecurityProtocol) 43 | } 44 | -------------------------------------------------------------------------------- /common/sharding/shards.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package sharding 16 | 17 | import ( 18 | "math" 19 | ) 20 | 21 | type Shard struct { 22 | Id int64 23 | Min uint32 24 | Max uint32 25 | } 26 | 27 | func GenerateShards(baseId int64, numShards uint32) []Shard { 28 | shards := make([]Shard, 0) 29 | bucketSize := (math.MaxUint32 / numShards) + 1 30 | for i := uint32(0); i < numShards; i++ { 31 | lowerBound := i * bucketSize 32 | upperBound := lowerBound + bucketSize - 1 33 | if i == numShards-1 { 34 | upperBound = math.MaxUint32 35 | } 36 | shards = append(shards, Shard{ 37 | Id: baseId + int64(i), 38 | Min: lowerBound, 39 | Max: upperBound, 40 | }) 41 | } 42 | return shards 43 | } 44 | -------------------------------------------------------------------------------- /common/sharding/shards_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package sharding 16 | 17 | import ( 18 | "math" 19 | "testing" 20 | 21 | "github.com/stretchr/testify/assert" 22 | ) 23 | 24 | func TestGenerateShards(t *testing.T) { 25 | type args struct { 26 | baseId int64 27 | numShards uint32 28 | } 29 | tests := []struct { 30 | name string 31 | args args 32 | want []Shard 33 | }{ 34 | {"1-shard", 35 | args{0, 1}, 36 | []Shard{ 37 | {0, 0, math.MaxUint32}, 38 | }}, 39 | {"2-shards", 40 | args{0, 2}, 41 | []Shard{ 42 | {0, 0, 2147483647}, 43 | {1, 2147483648, math.MaxUint32}, 44 | }}, 45 | {"3-shards", 46 | args{0, 3}, 47 | []Shard{ 48 | {0, 0, 1431655765}, 49 | {1, 1431655766, 2863311531}, 50 | {2, 2863311532, math.MaxUint32}, 51 | }}, 52 | {"4-shards", 53 | args{0, 4}, 54 | []Shard{ 55 | {0, 0, 1073741823}, 56 | {1, 1073741824, 2147483647}, 57 | {2, 2147483648, 3221225471}, 58 | {3, 3221225472, math.MaxUint32}, 59 | }}, 60 | {"2-shards-different-base-id", 61 | args{5, 2}, 62 | []Shard{ 63 | {5, 0, 2147483647}, 64 | {6, 2147483648, math.MaxUint32}, 65 | }}, 66 | } 67 | for _, tt := range tests { 68 | t.Run(tt.name, func(t *testing.T) { 69 | assert.Equalf(t, tt.want, 70 | GenerateShards(tt.args.baseId, tt.args.numShards), 71 | "GenerateShards(%v)", tt.args.numShards) 72 | }) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /common/time/backoff.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package time 16 | 17 | import ( 18 | "context" 19 | "time" 20 | 21 | "github.com/cenkalti/backoff/v4" 22 | ) 23 | 24 | func NewBackOff(ctx context.Context) backoff.BackOff { 25 | return NewBackOffWithInitialInterval(ctx, 100*time.Millisecond) 26 | } 27 | 28 | func NewBackOffWithInitialInterval(ctx context.Context, initialInterval time.Duration) backoff.BackOff { 29 | return backoff.WithContext(&backoff.ExponentialBackOff{ 30 | InitialInterval: initialInterval, 31 | RandomizationFactor: backoff.DefaultRandomizationFactor, 32 | Multiplier: backoff.DefaultMultiplier, 33 | MaxInterval: backoff.DefaultMaxInterval, 34 | MaxElapsedTime: 0, // Never stop trying 35 | Stop: backoff.Stop, 36 | Clock: backoff.SystemClock, 37 | }, ctx) 38 | } 39 | -------------------------------------------------------------------------------- /common/time/clock.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package time 16 | 17 | import ( 18 | "sync/atomic" 19 | "time" 20 | ) 21 | 22 | type Clock interface { 23 | Now() time.Time 24 | } 25 | 26 | type systemClock struct { 27 | } 28 | 29 | var SystemClock = &systemClock{} 30 | 31 | func (systemClock) Now() time.Time { 32 | return time.Now() 33 | } 34 | 35 | type MockedClock struct { 36 | currentTime atomic.Int64 37 | } 38 | 39 | func (c *MockedClock) Set(currentTime int64) { 40 | c.currentTime.Store(currentTime) 41 | } 42 | 43 | func (c *MockedClock) Now() time.Time { 44 | return time.UnixMilli(c.currentTime.Load()) 45 | } 46 | -------------------------------------------------------------------------------- /coordinator/coordinator_rpc_server.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package coordinator 16 | 17 | import ( 18 | "crypto/tls" 19 | 20 | "google.golang.org/grpc" 21 | "google.golang.org/grpc/health" 22 | "google.golang.org/grpc/health/grpc_health_v1" 23 | 24 | "github.com/streamnative/oxia/common/rpc" 25 | 26 | "github.com/streamnative/oxia/server/auth" 27 | ) 28 | 29 | type rpcServer struct { 30 | grpcServer rpc.GrpcServer 31 | healthServer *health.Server 32 | } 33 | 34 | func newRpcServer(bindAddress string, tlsConf *tls.Config) (*rpcServer, error) { 35 | server := &rpcServer{ 36 | healthServer: health.NewServer(), 37 | } 38 | 39 | var err error 40 | server.grpcServer, err = rpc.Default.StartGrpcServer("coordinator", bindAddress, func(registrar grpc.ServiceRegistrar) { 41 | grpc_health_v1.RegisterHealthServer(registrar, server.healthServer) 42 | }, tlsConf, &auth.Disabled) 43 | if err != nil { 44 | return nil, err 45 | } 46 | 47 | return server, nil 48 | } 49 | 50 | func (s *rpcServer) Close() error { 51 | s.healthServer.Shutdown() 52 | return s.grpcServer.Close() 53 | } 54 | -------------------------------------------------------------------------------- /coordinator/coordinator_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package coordinator 16 | 17 | import ( 18 | "encoding/json" 19 | "testing" 20 | 21 | "github.com/stretchr/testify/assert" 22 | ) 23 | 24 | func TestCoordinator_MarshalingError(t *testing.T) { 25 | config := Config{ 26 | InternalServiceAddr: "123", 27 | InternalSecureServiceAddr: "123", 28 | MetadataProviderImpl: "123", 29 | K8SMetadataConfigMapName: "123", 30 | } 31 | _, err := json.Marshal(config) 32 | assert.Nil(t, err) 33 | } 34 | -------------------------------------------------------------------------------- /coordinator/impl/coordinator_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package impl 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/stretchr/testify/assert" 21 | 22 | "github.com/streamnative/oxia/common/rpc" 23 | "github.com/streamnative/oxia/coordinator/model" 24 | ) 25 | 26 | func TestCoordinatorInitiateLeaderElection(t *testing.T) { 27 | s1, sa1 := newServer(t) 28 | s2, sa2 := newServer(t) 29 | s3, sa3 := newServer(t) 30 | defer s1.Close() 31 | defer s2.Close() 32 | defer s3.Close() 33 | 34 | metadataProvider := NewMetadataProviderMemory() 35 | clusterConfig := model.ClusterConfig{ 36 | Namespaces: []model.NamespaceConfig{{ 37 | Name: "default", 38 | ReplicationFactor: 1, 39 | InitialShardCount: 2, 40 | }}, 41 | Servers: []model.Server{sa1, sa2, sa3}, 42 | } 43 | clientPool := rpc.NewClientPool(nil, nil) 44 | 45 | coordinator, err := NewCoordinator(metadataProvider, func() (model.ClusterConfig, error) { return clusterConfig, nil }, nil, NewRpcProvider(clientPool)) 46 | assert.NoError(t, err) 47 | defer coordinator.Close() 48 | 49 | metadata := model.ShardMetadata{ 50 | Status: model.ShardStatusSteadyState, 51 | Term: 999, 52 | Leader: nil, 53 | Ensemble: []model.Server{}, 54 | RemovedNodes: []model.Server{}, 55 | Int32HashRange: model.Int32HashRange{Min: 2000, Max: 100000}, 56 | } 57 | err = coordinator.InitiateLeaderElection("default", 1, metadata) 58 | assert.NoError(t, err) 59 | 60 | status := coordinator.ClusterStatus() 61 | assert.EqualValues(t, status.Namespaces["default"].Shards[1], metadata) 62 | } 63 | -------------------------------------------------------------------------------- /coordinator/impl/metadata.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package impl 16 | 17 | import ( 18 | "io" 19 | 20 | "github.com/pkg/errors" 21 | 22 | "github.com/streamnative/oxia/coordinator/model" 23 | ) 24 | 25 | type Version string 26 | 27 | var ( 28 | ErrMetadataNotInitialized = errors.New("metadata not initialized") 29 | ErrMetadataBadVersion = errors.New("metadata bad version") 30 | ) 31 | 32 | const MetadataNotExists Version = "-1" 33 | 34 | type MetadataProvider interface { 35 | io.Closer 36 | 37 | Get() (cs *model.ClusterStatus, version Version, err error) 38 | 39 | Store(cs *model.ClusterStatus, expectedVersion Version) (newVersion Version, err error) 40 | } 41 | -------------------------------------------------------------------------------- /coordinator/impl/metadata_memory.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package impl 16 | 17 | import ( 18 | "strconv" 19 | "sync" 20 | 21 | "github.com/streamnative/oxia/coordinator/model" 22 | ) 23 | 24 | // MetadataProviderMemory is a provider that just keeps the cluster status in memory 25 | // Used for unit tests. 26 | type metadataProviderMemory struct { 27 | sync.Mutex 28 | 29 | cs *model.ClusterStatus 30 | version Version 31 | } 32 | 33 | func NewMetadataProviderMemory() MetadataProvider { 34 | return &metadataProviderMemory{ 35 | cs: nil, 36 | version: MetadataNotExists, 37 | } 38 | } 39 | 40 | func (*metadataProviderMemory) Close() error { 41 | return nil 42 | } 43 | 44 | func (m *metadataProviderMemory) Get() (cs *model.ClusterStatus, version Version, err error) { 45 | m.Lock() 46 | defer m.Unlock() 47 | return m.cs, m.version, nil 48 | } 49 | 50 | func (m *metadataProviderMemory) Store(cs *model.ClusterStatus, expectedVersion Version) (newVersion Version, err error) { 51 | m.Lock() 52 | defer m.Unlock() 53 | 54 | if expectedVersion != m.version { 55 | panic(ErrMetadataBadVersion) 56 | } 57 | 58 | m.cs = cs.Clone() 59 | m.version = incrVersion(m.version) 60 | return m.version, nil 61 | } 62 | 63 | func incrVersion(version Version) Version { 64 | i, err := strconv.ParseInt(string(version), 10, 64) 65 | if err != nil { 66 | return "" 67 | } 68 | i++ 69 | return Version(strconv.FormatInt(i, 10)) 70 | } 71 | -------------------------------------------------------------------------------- /coordinator/impl/mock_utils.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package impl 16 | 17 | import "github.com/streamnative/oxia/coordinator/model" 18 | 19 | func SimpleEnsembleSupplier(candidates []model.Server, nc *model.NamespaceConfig, cs *model.ClusterStatus) []model.Server { 20 | n := len(candidates) 21 | res := make([]model.Server, nc.ReplicationFactor) 22 | for i := uint32(0); i < nc.ReplicationFactor; i++ { 23 | res[i] = candidates[int(cs.ServerIdx+i)%n] 24 | } 25 | return res 26 | } 27 | -------------------------------------------------------------------------------- /coordinator/impl/shard_status_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package impl 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/stretchr/testify/assert" 21 | 22 | "github.com/streamnative/oxia/coordinator/model" 23 | ) 24 | 25 | func TestShardStatus_String(t *testing.T) { 26 | assert.Equal(t, "Unknown", model.ShardStatusUnknown.String()) 27 | assert.Equal(t, "SteadyState", model.ShardStatusSteadyState.String()) 28 | assert.Equal(t, "Election", model.ShardStatusElection.String()) 29 | } 30 | 31 | func TestShardStatus_JSON(t *testing.T) { 32 | j, err := model.ShardStatusSteadyState.MarshalJSON() 33 | assert.NoError(t, err) 34 | assert.Equal(t, []byte("\"SteadyState\""), j) 35 | 36 | var s model.ShardStatus 37 | err = s.UnmarshalJSON(j) 38 | assert.NoError(t, err) 39 | assert.Equal(t, model.ShardStatusSteadyState, s) 40 | 41 | err = s.UnmarshalJSON([]byte("xyz")) 42 | assert.Error(t, err) 43 | } 44 | -------------------------------------------------------------------------------- /coordinator/model/cluster_common.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package model 16 | 17 | type Server struct { 18 | // Name is the unique identification for clusters 19 | Name *string `json:"name" yaml:"name"` 20 | 21 | // Public is the endpoint that is advertised to clients 22 | Public string `json:"public" yaml:"public"` 23 | 24 | // Internal is the endpoint for server->server RPCs 25 | Internal string `json:"internal" yaml:"internal"` 26 | } 27 | 28 | type ServerMetadata struct { 29 | // Labels represents a key-value map to store metadata associated with a server. 30 | Labels map[string]string `json:"labels" yaml:"labels"` 31 | } 32 | 33 | func (sv *Server) GetIdentifier() string { 34 | if sv.Name == nil { 35 | return sv.Internal 36 | } 37 | return *sv.Name 38 | } 39 | -------------------------------------------------------------------------------- /coordinator/model/cluster_config.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package model 16 | 17 | import ( 18 | "github.com/streamnative/oxia/common/entity" 19 | "github.com/streamnative/oxia/coordinator/policies" 20 | ) 21 | 22 | type ClusterConfig struct { 23 | Namespaces []NamespaceConfig `json:"namespaces" yaml:"namespaces"` 24 | Servers []Server `json:"servers" yaml:"servers"` 25 | // ServerMetadata is a map associating server names with their corresponding metadata. 26 | ServerMetadata map[string]ServerMetadata `json:"serverMetadata" yaml:"serverMetadata"` 27 | } 28 | 29 | type NamespaceConfig struct { 30 | Name string `json:"name" yaml:"name"` 31 | InitialShardCount uint32 `json:"initialShardCount" yaml:"initialShardCount"` 32 | ReplicationFactor uint32 `json:"replicationFactor" yaml:"replicationFactor"` 33 | NotificationsEnabled entity.OptBooleanDefaultTrue `json:"notificationsEnabled" yaml:"notificationsEnabled"` 34 | // Policies represents additional configuration policies for the namespace, such as anti-affinity rules. 35 | Policies *policies.Policies `json:"policies,omitempty" yaml:"policies,omitempty"` 36 | } 37 | -------------------------------------------------------------------------------- /coordinator/model/cluster_config_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package model 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/stretchr/testify/assert" 21 | ) 22 | 23 | func TestClusterConfig(t *testing.T) { 24 | cc1 := ClusterConfig{ 25 | Namespaces: []NamespaceConfig{{ 26 | Name: "ns1", 27 | InitialShardCount: 2, 28 | ReplicationFactor: 3, 29 | }}, 30 | Servers: []Server{{ 31 | Public: "f1", 32 | Internal: "f1", 33 | }, { 34 | Public: "f2", 35 | Internal: "f2", 36 | }}, 37 | } 38 | 39 | cc2 := ClusterConfig{ 40 | Namespaces: []NamespaceConfig{{ 41 | Name: "ns1", 42 | InitialShardCount: 2, 43 | ReplicationFactor: 3, 44 | }}, 45 | Servers: []Server{{ 46 | Public: "f1", 47 | Internal: "f1", 48 | }, { 49 | Public: "f2", 50 | Internal: "f2", 51 | }}, 52 | } 53 | 54 | assert.Equal(t, cc1, cc2) 55 | assert.NotSame(t, &cc1, &cc2) 56 | } 57 | -------------------------------------------------------------------------------- /coordinator/model/cluster_status_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package model 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/stretchr/testify/assert" 21 | ) 22 | 23 | func TestClusterStatus_Clone(t *testing.T) { 24 | cs1 := &ClusterStatus{ 25 | Namespaces: map[string]NamespaceStatus{ 26 | "test-ns": { 27 | ReplicationFactor: 3, 28 | Shards: map[int64]ShardMetadata{ 29 | 0: { 30 | Status: ShardStatusSteadyState, 31 | Term: 1, 32 | Leader: &Server{ 33 | Public: "l1", 34 | Internal: "l1", 35 | }, 36 | Ensemble: []Server{{ 37 | Public: "f1", 38 | Internal: "f1", 39 | }, { 40 | Public: "f2", 41 | Internal: "f2", 42 | }}, 43 | Int32HashRange: Int32HashRange{}, 44 | RemovedNodes: []Server{{ 45 | Public: "r1", 46 | Internal: "r1", 47 | }}, 48 | }, 49 | }, 50 | }, 51 | }, 52 | ShardIdGenerator: 5, 53 | ServerIdx: 7, 54 | } 55 | 56 | cs2 := cs1.Clone() 57 | 58 | assert.Equal(t, cs1, cs2) 59 | assert.NotSame(t, cs1, cs2) 60 | assert.Equal(t, cs1.Namespaces, cs2.Namespaces) 61 | assert.NotSame(t, &cs1.Namespaces, &cs2.Namespaces) 62 | assert.Equal(t, cs1.Namespaces["test-ns"].Shards, cs2.Namespaces["test-ns"].Shards) 63 | assert.Equal(t, cs1.Namespaces["test-ns"].Shards[0], cs2.Namespaces["test-ns"].Shards[0]) 64 | 65 | assert.Equal(t, cs1.ShardIdGenerator, cs2.ShardIdGenerator) 66 | assert.Equal(t, cs1.ServerIdx, cs2.ServerIdx) 67 | } 68 | -------------------------------------------------------------------------------- /coordinator/model/shard_status.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package model 16 | 17 | import ( 18 | "bytes" 19 | "encoding/json" 20 | ) 21 | 22 | type ShardStatus uint16 23 | 24 | const ( 25 | ShardStatusUnknown ShardStatus = iota 26 | ShardStatusSteadyState 27 | ShardStatusElection 28 | ShardStatusDeleting 29 | ) 30 | 31 | func (s ShardStatus) String() string { 32 | return toString[s] 33 | } 34 | 35 | var toString = map[ShardStatus]string{ 36 | ShardStatusUnknown: "Unknown", 37 | ShardStatusSteadyState: "SteadyState", 38 | ShardStatusElection: "Election", 39 | ShardStatusDeleting: "Deleting", 40 | } 41 | 42 | var toShardStatus = map[string]ShardStatus{ 43 | "Unknown": ShardStatusUnknown, 44 | "SteadyState": ShardStatusSteadyState, 45 | "Election": ShardStatusElection, 46 | "Deleting": ShardStatusDeleting, 47 | } 48 | 49 | // MarshalJSON marshals the enum as a quoted json string. 50 | func (s ShardStatus) MarshalJSON() ([]byte, error) { 51 | buffer := bytes.NewBufferString(`"`) 52 | buffer.WriteString(toString[s]) 53 | buffer.WriteString(`"`) 54 | return buffer.Bytes(), nil 55 | } 56 | 57 | // UnmarshalJSON unmarshals a quoted json string to the enum value. 58 | func (s *ShardStatus) UnmarshalJSON(b []byte) error { 59 | var j string 60 | if err := json.Unmarshal(b, &j); err != nil { 61 | return err 62 | } 63 | // If the string cannot be found then it will be set to the Unknown status value. 64 | *s = toShardStatus[j] 65 | return nil 66 | } 67 | -------------------------------------------------------------------------------- /coordinator/policies/anti_affinity.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package policies 16 | 17 | type AntiAffinityMode string 18 | 19 | const ( 20 | // Strict Enforce anti-affinity rules strictly. Ensemble choosing fails if rules can't be met. 21 | // This mode is for high-availability systems needing strict resource separation. 22 | Strict AntiAffinityMode = "Strict" 23 | 24 | // Relaxed Try to follow anti-affinity rules, but proceed with ensemble choosing if not feasible. 25 | // Ideal for environments where resource utilization is limited. 26 | Relaxed AntiAffinityMode = "Relaxed" 27 | ) 28 | 29 | // AntiAffinity defines rules to prevent co-location of resources based on specified labels and operation mode. 30 | // Labels specifies a set of key-value labels that form the basis of the anti-affinity constraints. 31 | // Mode determines the mode of anti-affinity enforcement, for instance, strict or relaxed. 32 | type AntiAffinity struct { 33 | 34 | // Labels defines a list of label keys used to evaluate anti-affinity constraints for resource placement decisions. 35 | Labels []string `json:"labels,omitempty" yaml:"labels,omitempty"` 36 | 37 | // Mode specifies the enforcement level of anti-affinity constraints, such as Strict or Relaxed behavior. 38 | Mode AntiAffinityMode `json:"mode,omitempty" yaml:"mode,omitempty"` 39 | } 40 | -------------------------------------------------------------------------------- /coordinator/policies/policies.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package policies 16 | 17 | type Policies struct { 18 | // AntiAffinities defines a list of anti-affinity rules for placement policies, ensuring resources don't coexist undesirably. 19 | AntiAffinities []AntiAffinity `json:"antiAffinities,omitempty" yaml:"antiAffinities,omitempty"` 20 | } 21 | -------------------------------------------------------------------------------- /coordinator/selectors/ensemble/context.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package ensemble 16 | 17 | import ( 18 | "github.com/emirpasic/gods/sets/linkedhashset" 19 | 20 | "github.com/streamnative/oxia/coordinator/model" 21 | p "github.com/streamnative/oxia/coordinator/policies" 22 | ) 23 | 24 | type Context struct { 25 | Candidates *linkedhashset.Set 26 | CandidatesMetadata map[string]model.ServerMetadata 27 | Policies *p.Policies 28 | Status *model.ClusterStatus 29 | Replicas int 30 | } 31 | -------------------------------------------------------------------------------- /coordinator/selectors/selector.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package selectors 16 | 17 | import ( 18 | "github.com/pkg/errors" 19 | ) 20 | 21 | var ( 22 | ErrUnsatisfiedEnsembleReplicas = errors.New("selector: unsatisfied ensemble replicas") 23 | ErrUnsatisfiedAntiAffinity = errors.New("selector: unsatisfied anti-affinity") 24 | ErrUnsupportedAntiAffinityMode = errors.New("selector: unsupported anti-affinity mode") 25 | ErrNoFunctioning = errors.New("selector: no functioning selection") 26 | ErrMultipleResult = errors.New("selector: multiple results") 27 | ) 28 | 29 | type Selector[O any, R any] interface { 30 | Select(o O) (R, error) 31 | } 32 | -------------------------------------------------------------------------------- /coordinator/selectors/single/idx_selector.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package single 16 | 17 | import "github.com/streamnative/oxia/coordinator/selectors" 18 | 19 | var _ selectors.Selector[*Context, *string] = &serverIdxSelector{} 20 | 21 | type serverIdxSelector struct { 22 | } 23 | 24 | func (*serverIdxSelector) Select(ssContext *Context) (*string, error) { 25 | startIdx := ssContext.Status.ServerIdx 26 | candidatesArr := ssContext.Candidates.Values() 27 | server, ok := candidatesArr[int(startIdx)%len(candidatesArr)].(string) 28 | if !ok { 29 | panic("unexpected candidate cast") 30 | } 31 | return &server, nil 32 | } 33 | -------------------------------------------------------------------------------- /coordinator/selectors/single/idx_selector_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package single 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/emirpasic/gods/sets/linkedhashset" 21 | "github.com/stretchr/testify/assert" 22 | "k8s.io/utils/ptr" 23 | 24 | "github.com/streamnative/oxia/coordinator/model" 25 | ) 26 | 27 | func TestServerIdxSelectNew(t *testing.T) { 28 | selector := &serverIdxSelector{} 29 | 30 | server1 := model.Server{Name: ptr.To("server1"), Public: "server1", Internal: "server1"} 31 | server2 := model.Server{Name: ptr.To("server2"), Public: "server2", Internal: "server2"} 32 | server3 := model.Server{Name: ptr.To("server3"), Public: "server3", Internal: "server3"} 33 | server4 := model.Server{Name: ptr.To("server4"), Public: "server4", Internal: "server4"} 34 | server5 := model.Server{Name: ptr.To("server5"), Public: "server5", Internal: "server5"} 35 | server6 := model.Server{Name: ptr.To("server6"), Public: "server6", Internal: "server6"} 36 | candidates := []any{server1.GetIdentifier(), server2.GetIdentifier(), server3.GetIdentifier(), server4.GetIdentifier(), server5.GetIdentifier(), server6.GetIdentifier()} 37 | 38 | options := &Context{ 39 | CandidatesMetadata: make(map[string]model.ServerMetadata), 40 | Candidates: linkedhashset.New(candidates...), 41 | Policies: nil, 42 | Status: &model.ClusterStatus{ 43 | ServerIdx: 0, 44 | }, 45 | } 46 | 47 | for i := 0; i < 12; i++ { 48 | options.Status.ServerIdx = uint32(i) 49 | result, err := selector.Select(options) 50 | assert.NoError(t, err) 51 | assert.EqualValues(t, *result, candidates[i%6]) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /coordinator/selectors/single/selector.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package single 16 | 17 | import ( 18 | "github.com/pkg/errors" 19 | 20 | "github.com/streamnative/oxia/coordinator/selectors" 21 | ) 22 | 23 | var _ selectors.Selector[*Context, *string] = &server{} 24 | 25 | type server struct { 26 | selectors []selectors.Selector[*Context, *string] 27 | } 28 | 29 | func (s *server) Select(selectorContext *Context) (*string, error) { 30 | var serverId *string 31 | var err error 32 | for _, selector := range s.selectors { 33 | if serverId, err = selector.Select(selectorContext); err != nil { 34 | if errors.Is(err, selectors.ErrNoFunctioning) || errors.Is(err, selectors.ErrMultipleResult) { 35 | continue 36 | } 37 | return nil, err 38 | } 39 | if serverId != nil { 40 | return serverId, nil 41 | } 42 | } 43 | if serverId == nil { 44 | panic("unexpected behaviour") 45 | } 46 | return serverId, nil 47 | } 48 | 49 | func NewSelector() selectors.Selector[*Context, *string] { 50 | return &server{ 51 | selectors: []selectors.Selector[*Context, *string]{ 52 | &serverAntiAffinitiesSelector{}, 53 | &serverIdxSelector{}, 54 | }, 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /deploy/chaos-mesh/oxia-cluster.yaml: -------------------------------------------------------------------------------- 1 | image: 2 | repository: oxia 3 | tag: latest 4 | pullPolicy: Never 5 | -------------------------------------------------------------------------------- /deploy/charts/oxia-cluster/.helmignore: -------------------------------------------------------------------------------- 1 | # Copyright 2023 StreamNative, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Patterns to ignore when building packages. 16 | # This supports shell glob matching, relative path matching, and 17 | # negation (prefixed with !). Only one pattern per line. 18 | .DS_Store 19 | # Common VCS dirs 20 | .git/ 21 | .gitignore 22 | .bzr/ 23 | .bzrignore 24 | .hg/ 25 | .hgignore 26 | .svn/ 27 | # Common backup files 28 | *.swp 29 | *.bak 30 | *.tmp 31 | *.orig 32 | *~ 33 | # Various IDEs 34 | .project 35 | .idea/ 36 | *.tmproj 37 | .vscode/ 38 | -------------------------------------------------------------------------------- /deploy/charts/oxia-cluster/Chart.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 StreamNative, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | apiVersion: v2 15 | name: oxia-cluster 16 | description: Oxia cluster 17 | type: application 18 | version: 0.0.1 19 | appVersion: "0.0.1" 20 | home: https://streamnative.io 21 | sources: 22 | - https://github.com/streamnative/oxia 23 | icon: https://raw.githubusercontent.com/streamnative/oxia/main/docs/oxia-logo.png 24 | maintainers: 25 | - name: StreamNative Support 26 | email: support@streamnative.io -------------------------------------------------------------------------------- /deploy/charts/oxia-cluster/templates/coordinator-configmap.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 StreamNative, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: v1 16 | kind: ConfigMap 17 | metadata: 18 | labels: 19 | {{- include "oxia-cluster.coordinator.labels" . | nindent 4 }} 20 | name: {{ .Release.Name }}-coordinator 21 | data: 22 | config.yaml: | 23 | namespaces: 24 | - name: default 25 | initialShardCount: {{ .Values.initialShardCount }} 26 | replicationFactor: {{ .Values.replicationFactor }} 27 | servers: 28 | {{- $vars := dict "name" .Release.Name "namespace" .Release.Namespace "public" .Values.server.ports.public "internal" .Values.server.ports.internal }} 29 | {{- range until (int .Values.server.replicas) }} 30 | - public: {{ $vars.name }}-{{ . }}.{{ $vars.name }}-svc.{{ $vars.namespace }}.svc.cluster.local:{{ $vars.public }} 31 | internal: {{ $vars.name }}-{{ . }}.{{ $vars.name }}-svc:{{ $vars.internal }} 32 | {{- end }} 33 | -------------------------------------------------------------------------------- /deploy/charts/oxia-cluster/templates/coordinator-role.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 StreamNative, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: rbac.authorization.k8s.io/v1 16 | kind: Role 17 | metadata: 18 | labels: 19 | {{- include "oxia-cluster.coordinator.labels" . | nindent 4 }} 20 | name: {{ .Release.Name }}-coordinator 21 | rules: 22 | - apiGroups: [ "" ] 23 | resources: [ "configmaps" ] 24 | verbs: [ "*" ] 25 | - apiGroups: [ "oxia.streamnative.io" ] 26 | resources: [ "oxiaclusters" ] 27 | verbs: [ "get", "update" ] 28 | -------------------------------------------------------------------------------- /deploy/charts/oxia-cluster/templates/coordinator-rolebinding.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 StreamNative, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: rbac.authorization.k8s.io/v1 16 | kind: RoleBinding 17 | metadata: 18 | labels: 19 | {{- include "oxia-cluster.coordinator.labels" . | nindent 4 }} 20 | name: {{ .Release.Name }}-coordinator 21 | subjects: 22 | - kind: ServiceAccount 23 | name: {{ .Release.Name }}-coordinator 24 | namespace: {{ .Release.Namespace }} 25 | roleRef: 26 | apiGroup: "" 27 | kind: Role 28 | name: {{ .Release.Name }}-coordinator 29 | -------------------------------------------------------------------------------- /deploy/charts/oxia-cluster/templates/coordinator-service.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 StreamNative, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: v1 16 | kind: Service 17 | metadata: 18 | labels: 19 | oxia_cluster: {{ .Release.Name }} 20 | {{- include "oxia-cluster.coordinator.labels" . | nindent 4 }} 21 | name: {{ .Release.Name }}-coordinator 22 | spec: 23 | ports: 24 | {{- range $key, $value := .Values.coordinator.ports }} 25 | - name: {{ $key }} 26 | port: {{ $value }} 27 | targetPort: {{ $key }} 28 | {{- end}} 29 | selector: 30 | {{- include "oxia-cluster.coordinator.selectorLabels" . | nindent 4 }} 31 | -------------------------------------------------------------------------------- /deploy/charts/oxia-cluster/templates/coordinator-serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 StreamNative, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: v1 16 | kind: ServiceAccount 17 | metadata: 18 | labels: 19 | {{- include "oxia-cluster.coordinator.labels" . | nindent 4 }} 20 | name: {{ .Release.Name }}-coordinator 21 | {{- if .Values.image.pullSecrets }} 22 | imagePullSecrets: 23 | - name: {{ .Values.image.pullSecrets }} 24 | {{- end}} 25 | -------------------------------------------------------------------------------- /deploy/charts/oxia-cluster/templates/coordinator-servicemonitor.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 StreamNative, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | {{- if .Values.monitoringEnabled }} 16 | apiVersion: monitoring.coreos.com/v1 17 | kind: ServiceMonitor 18 | metadata: 19 | labels: 20 | {{- include "oxia-cluster.coordinator.labels" . | nindent 4 }} 21 | name: {{ .Release.Name }}-coordinator 22 | spec: 23 | endpoints: 24 | - port: metrics 25 | selector: 26 | matchLabels: 27 | {{- include "oxia-cluster.coordinator.selectorLabels" . | nindent 6 }} 28 | targetLabels: 29 | - oxia_cluster 30 | {{- end }} 31 | -------------------------------------------------------------------------------- /deploy/charts/oxia-cluster/templates/server-service-public.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 StreamNative, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: v1 16 | kind: Service 17 | metadata: 18 | labels: 19 | oxia_cluster: {{ .Release.Name }} 20 | {{- include "oxia-cluster.server.labels" . | nindent 4 }} 21 | name: {{ .Release.Name }} 22 | spec: 23 | ports: 24 | {{- range $key, $value := .Values.server.ports }} 25 | - name: {{ $key }} 26 | port: {{ $value }} 27 | targetPort: {{ $key }} 28 | {{- end}} 29 | selector: 30 | {{- include "oxia-cluster.server.selectorLabels" . | nindent 4 }} 31 | -------------------------------------------------------------------------------- /deploy/charts/oxia-cluster/templates/server-service.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 StreamNative, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: v1 16 | kind: Service 17 | metadata: 18 | labels: 19 | oxia_cluster: {{ .Release.Name }} 20 | {{- include "oxia-cluster.server.labels" . | nindent 4 }} 21 | name: {{ .Release.Name }}-svc 22 | spec: 23 | clusterIP: None 24 | publishNotReadyAddresses: true 25 | ports: 26 | {{- range $key, $value := .Values.server.ports }} 27 | - name: {{ $key }} 28 | port: {{ $value }} 29 | targetPort: {{ $key }} 30 | {{- end}} 31 | selector: 32 | {{- include "oxia-cluster.server.selectorLabels" . | nindent 4 }} 33 | -------------------------------------------------------------------------------- /deploy/charts/oxia-cluster/templates/server-serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 StreamNative, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: v1 16 | kind: ServiceAccount 17 | metadata: 18 | labels: 19 | {{- include "oxia-cluster.server.labels" . | nindent 4 }} 20 | name: {{ .Release.Name }} 21 | {{- if .Values.image.pullSecrets }} 22 | imagePullSecrets: 23 | - name: {{ .Values.image.pullSecrets }} 24 | {{- end}} 25 | -------------------------------------------------------------------------------- /deploy/charts/oxia-cluster/templates/server-servicemonitor.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 StreamNative, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | {{- if .Values.monitoringEnabled }} 16 | apiVersion: monitoring.coreos.com/v1 17 | kind: ServiceMonitor 18 | metadata: 19 | labels: 20 | {{- include "oxia-cluster.server.labels" . | nindent 4 }} 21 | name: {{ .Release.Name }} 22 | spec: 23 | endpoints: 24 | - port: metrics 25 | selector: 26 | matchLabels: 27 | {{- include "oxia-cluster.server.selectorLabels" . | nindent 6 }} 28 | targetLabels: 29 | - oxia_cluster 30 | {{- end }} 31 | -------------------------------------------------------------------------------- /deploy/charts/oxia-cluster/values.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 StreamNative, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Default values for oxia-controller. 16 | # This is a YAML-formatted file. 17 | # Declare variables to be passed into your templates. 18 | 19 | initialShardCount: 3 20 | replicationFactor: 3 21 | 22 | coordinator: 23 | cpu: 100m 24 | memory: 128Mi 25 | ports: 26 | internal: 6649 27 | metrics: 8080 28 | 29 | server: 30 | replicas: 3 31 | cpu: 1 32 | memory: 1Gi 33 | storage: 8Gi 34 | #storageClassName: xxx 35 | ports: 36 | public: 6648 37 | internal: 6649 38 | metrics: 8080 39 | 40 | image: 41 | repository: streamnative/oxia 42 | tag: main 43 | pullPolicy: Always 44 | #pullSecrets: xxx 45 | 46 | pprofEnabled: false 47 | monitoringEnabled: false 48 | -------------------------------------------------------------------------------- /deploy/dashboards/values-kube-prometheus-stack.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 StreamNative, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | prometheus: 16 | prometheusSpec: 17 | serviceMonitorSelectorNilUsesHelmValues: false 18 | 19 | grafana: 20 | 21 | dashboardProviders: 22 | dashboardproviders.yaml: 23 | apiVersion: 1 24 | providers: 25 | - name: 'oxia' 26 | orgId: 1 27 | folder: 'Oxia' 28 | type: file 29 | disableDeletion: false 30 | editable: true 31 | options: 32 | path: /var/lib/grafana/dashboards/oxia 33 | 34 | dashboards: 35 | oxia: 36 | oxia-overview: 37 | file: dashboards/oxia-overview.json 38 | oxia-containers: 39 | file: dashboards/oxia-containers.json 40 | oxia-coordinator: 41 | file: dashboards/oxia-coordinator.json 42 | oxia-golang: 43 | file: dashboards/oxia-golang.json 44 | oxia-grpc: 45 | file: dashboards/oxia-grpc.json 46 | oxia-nodes: 47 | file: dashboards/oxia-nodes.json 48 | oxia-shards: 49 | file: dashboards/oxia-shards.json 50 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Oxia Documentation 2 | 3 | 4 | ## Table of contents 5 | 6 | - Architecture 7 | - [Design Goal](./architecture/design-goals.md) 8 | - [Physical Architecture](./architecture/physical-architecture.md) 9 | - [Logical Architecture](./architecture/logical-architecture.md) 10 | - [Storage Architecture]() 11 | - [Replication Architecture]() 12 | - Client 13 | - [Golang](./client/go-api.md) 14 | - [JVM]() 15 | - Consensus 16 | - [Leader Election]() 17 | - [Data Replication]() 18 | - Deployment 19 | - [Bare Metal](./deployment/bare-metal.md) 20 | - [Docker]() 21 | - [Kubernetes](./deployment/kubernetes.md) 22 | - [Development]() 23 | - Correctness 24 | - [TLA+](./correctness/tla+.md) 25 | - [Maelstrom](./correctness/maelstrom.md) 26 | - Performance 27 | - [Perf]() 28 | - Features 29 | - [Versioning]() 30 | - [Automatic Session Management]() 31 | - [Notification]() 32 | - [Secondary Index]() 33 | - [PartitionKey]() 34 | - [Sequence Generation]() 35 | - [Sequence Notification]() 36 | - Operations 37 | - Policies 38 | - [Affinity*/Anti-Affinity]() 39 | - [Namespace Management]() 40 | - [Data Node Management]() 41 | - LoadBalancing 42 | - [Shards]() 43 | - [Leader*]() 44 | - Shards Management 45 | - [Auto-Splitting*]() 46 | - [Auto-Merging*]() 47 | 48 | > * denotes that support for this feature is planned for the future. -------------------------------------------------------------------------------- /docs/architecture/logical-architecture-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/streamnative/oxia/782ada84cb15d36d7d915fed074fda0913d2abb7/docs/architecture/logical-architecture-1.png -------------------------------------------------------------------------------- /docs/architecture/logical-architecture-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/streamnative/oxia/782ada84cb15d36d7d915fed074fda0913d2abb7/docs/architecture/logical-architecture-2.png -------------------------------------------------------------------------------- /docs/architecture/logical-architecture-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/streamnative/oxia/782ada84cb15d36d7d915fed074fda0913d2abb7/docs/architecture/logical-architecture-3.png -------------------------------------------------------------------------------- /docs/architecture/logical-architecture-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/streamnative/oxia/782ada84cb15d36d7d915fed074fda0913d2abb7/docs/architecture/logical-architecture-4.png -------------------------------------------------------------------------------- /docs/architecture/physical-architecture-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/streamnative/oxia/782ada84cb15d36d7d915fed074fda0913d2abb7/docs/architecture/physical-architecture-1.png -------------------------------------------------------------------------------- /docs/architecture/physical-architecture-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/streamnative/oxia/782ada84cb15d36d7d915fed074fda0913d2abb7/docs/architecture/physical-architecture-2.png -------------------------------------------------------------------------------- /docs/architecture/physical-architecture-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/streamnative/oxia/782ada84cb15d36d7d915fed074fda0913d2abb7/docs/architecture/physical-architecture-3.png -------------------------------------------------------------------------------- /docs/architecture/physical-architecture.md: -------------------------------------------------------------------------------- 1 | # Oxia Physical Architecture 2 | 3 | ![Physical Architecture](./physical-architecture-1.png) 4 | 5 | As a distributed metadata storage, Oxia can be split into two major components, as shown in the diagram above. 6 | 7 | - Coordinator 8 | - Data Node 9 | 10 | ## Coordinator 11 | 12 | The Oxia Coordinator is a stateless component that is in charge of some main tasks: 13 | 14 | 1. Coordinate data nodes and takes part in the leader election process 15 | 2. Perform error detection and recovery 16 | 3. Perform Shards/Leader load balance 17 | 18 | ![Coordinator Input/Output](./physical-architecture-2.png) 19 | 20 | The coordinator uses the [configuration]() as input and the [status]() as a way to checkpoint the current status of 21 | all the shards. 22 | 23 | - For the Kubernetes environment, it's easy to use `ConfigMap` to store that data and ensure high availability. 24 | - For the Bare Metal environment, you can write it to a local file for testing or shard-nothing metadata storage for high availability. 25 | 26 | > We might consider supporting store status and metadata to the data node for bootstrapping. But need more discussion here. 27 | > 28 | 29 | 30 | ## Data Node 31 | 32 | ![Data Node Architecture](./physical-architecture-3.png) 33 | 34 | The Oxia Data Node is a stateful component that is in charge of some main tasks: 35 | 36 | 1. Provide the public endpoint to the client for interacting with data. 37 | 2. Provide the internal endpoint to the coordinator or other data node for data replication and leader election, etc. 38 | 3. Provide the ability to store data**. -------------------------------------------------------------------------------- /docs/architecture/replication-architecture.md: -------------------------------------------------------------------------------- 1 | # Replication Architecture 2 | 3 | (todo...) -------------------------------------------------------------------------------- /docs/architecture/storage-architecture.md: -------------------------------------------------------------------------------- 1 | # Storage Architecture 2 | 3 | 4 | (todo...) -------------------------------------------------------------------------------- /docs/consensus/oxia-data-replication.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/streamnative/oxia/782ada84cb15d36d7d915fed074fda0913d2abb7/docs/consensus/oxia-data-replication.png -------------------------------------------------------------------------------- /docs/correctness/maelstrom.md: -------------------------------------------------------------------------------- 1 | # Maelstrom 2 | 3 | [Maelstrom](https://github.com/jepsen-io/maelstrom) is a tool that makes it easy to run a [Jepsen](https://jepsen.io/) 4 | simulation to verify the correctness of a system. 5 | 6 | Unlike for TLA+, Maelstrom works by running the real production code, injecting different kinds of failures and verifying 7 | that the external properties are not violated, using the Jepsen library. 8 | 9 | For Oxia this means that we can run a multi-node Oxia cluster as a set of multiple processes running in a single physical 10 | machine. Instead of TCP networking through gRPC, we run Oxia nodes that use stdin/stdout to communicate, using the 11 | JSON based [Maelstrom protocol](https://github.com/jepsen-io/maelstrom/blob/main/doc/protocol.md). 12 | 13 | To build Oxia for Maelstrom: 14 | 15 | ```shell 16 | $ make maelstrom 17 | ``` 18 | 19 | This will produce the binary in `bin/oxia-maelstrom` 20 | 21 | To run the Maelstrom simulation, you need to download Maelstrom and then run: 22 | 23 | ```shell 24 | $ ./maelstrom test -w lin-kv --bin /path/to/oxia-maelstrom \ 25 | --time-limit 30 --concurrency 2n --latency 10 --latency-dist uniform 26 | ``` -------------------------------------------------------------------------------- /docs/correctness/tla+.md: -------------------------------------------------------------------------------- 1 | # TLA + 2 | 3 | 4 | [TLA+](https://lamport.azurewebsites.net/tla/tla.html) is a high-level language for modeling distributed and 5 | concurrent systems. 6 | 7 | The Oxia TLA model [OxiaReplication.tla](../../tlaplus/OxiaReplication.tla), contains a model of data replication for a 8 | single shard. 9 | 10 | The TLA+ tools are able to use this model and explore all the possible states, verifying that the system properties are 11 | not violated (eg: missing entries in the log). 12 | 13 | You can run the TLA+ tools by doing: 14 | 15 | ```shell 16 | $ make tla 17 | ``` 18 | 19 | It will download all the required TLA+ tools and run the validation. -------------------------------------------------------------------------------- /docs/features/oxia-key-sorting.md: -------------------------------------------------------------------------------- 1 | # Oxia Keys Sorting 2 | 3 | Oxia uses a custom comparison operator for sorting the keys of the stored records. 4 | 5 | In Oxia all the keys are independent of each other and there is no explicit tree-like structure 6 | with parent-children relations. 7 | 8 | Having said that, Oxia assigns a special meaning to the `/` character in keys. This is done in 9 | order to ensure efficient traversal of small portions of the key space. 10 | 11 | Specifically, Oxia ensures that keys with a common prefix and the same number of `/` segments 12 | are sorted in such a way that they are stored close to each other in the database. 13 | 14 | For example, if we are considering the following keys and the order in which they will be stored: 15 | * `/xyz/A` 16 | * `/xyz/B` 17 | * `/xyz/C` 18 | * `/xyz/A/1` 19 | * `/xyz/A/2` 20 | * `/xyz/B/1` 21 | * `/xyz/A/1/a` 22 | 23 | This sorting, aware of `/` characters, makes possible to do efficient range queries to 24 | retrieve the list of "first level children" of a given key. 25 | 26 | For example, in Go: 27 | 28 | ```go 29 | client.List(context.Background(), "/xyz/", "/xyz//") 30 | ``` 31 | 32 | This will return a list with `["/xyz/A", "/xyz/B", "/xyz/C"]`, doing the minimum scan in the database. 33 | -------------------------------------------------------------------------------- /oxia/auth/authentication.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package auth 16 | 17 | import "google.golang.org/grpc/credentials" 18 | 19 | type Authentication interface { 20 | credentials.PerRPCCredentials 21 | } 22 | -------------------------------------------------------------------------------- /oxia/auth/token.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package auth 16 | 17 | import ( 18 | "context" 19 | ) 20 | 21 | type tokenAuthentication struct { 22 | requireTransportSecurity bool 23 | tokenGetFunc func() string 24 | } 25 | 26 | func (tokenAuth *tokenAuthentication) GetRequestMetadata(_ context.Context, _ ...string) (map[string]string, error) { 27 | token := tokenAuth.tokenGetFunc() 28 | return map[string]string{ 29 | "authorization": "Bearer" + " " + token, 30 | }, nil 31 | } 32 | 33 | func (tokenAuth *tokenAuthentication) RequireTransportSecurity() bool { 34 | return tokenAuth.requireTransportSecurity 35 | } 36 | 37 | func NewTokenAuthenticationWithFunc(tokenGetFunc func() string, requireTransportSecurity bool) Authentication { 38 | return &tokenAuthentication{tokenGetFunc: tokenGetFunc, requireTransportSecurity: requireTransportSecurity} 39 | } 40 | 41 | func NewTokenAuthenticationWithToken(token string, requireTransportSecurity bool) Authentication { 42 | return NewTokenAuthenticationWithFunc(func() string { 43 | return token 44 | }, requireTransportSecurity) 45 | } 46 | -------------------------------------------------------------------------------- /oxia/batch/batch.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package batch 16 | 17 | type Batch interface { 18 | CanAdd(any) bool 19 | Add(any) 20 | Size() int 21 | Complete() 22 | Fail(error) 23 | } 24 | -------------------------------------------------------------------------------- /oxia/batch/batcher_factory.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package batch 16 | 17 | import ( 18 | "context" 19 | "fmt" 20 | "runtime" 21 | "time" 22 | 23 | "github.com/streamnative/oxia/common/process" 24 | ) 25 | 26 | var batcherChannelBufferSize = runtime.GOMAXPROCS(-1) 27 | 28 | type BatcherFactory struct { 29 | Linger time.Duration 30 | MaxRequestsPerBatch int 31 | } 32 | 33 | func (b *BatcherFactory) NewBatcher(ctx context.Context, shard int64, batcherType string, batchFactory func() Batch) Batcher { 34 | batcher := &batcherImpl{ 35 | batchFactory: batchFactory, 36 | callC: make(chan any, batcherChannelBufferSize), 37 | closeC: make(chan bool), 38 | linger: b.Linger, 39 | maxRequestsPerBatch: b.MaxRequestsPerBatch, 40 | } 41 | 42 | go process.DoWithLabels(ctx, map[string]string{ 43 | "oxia": fmt.Sprintf("batcher-%s", batcherType), 44 | "shard": fmt.Sprintf("%d", shard), 45 | }, batcher.Run) 46 | 47 | return batcher 48 | } 49 | -------------------------------------------------------------------------------- /oxia/internal/batch/manager.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package batch 16 | 17 | import ( 18 | "context" 19 | "sync" 20 | 21 | "go.uber.org/multierr" 22 | 23 | "github.com/streamnative/oxia/oxia/batch" 24 | ) 25 | 26 | func NewManager(ctx context.Context, batcherFactory func(context.Context, *int64) batch.Batcher) *Manager { 27 | return &Manager{ 28 | ctx: ctx, 29 | batcherFactory: batcherFactory, 30 | batchers: make(map[int64]batch.Batcher), 31 | } 32 | } 33 | 34 | type Manager struct { 35 | sync.RWMutex 36 | ctx context.Context 37 | batcherFactory func(context.Context, *int64) batch.Batcher 38 | batchers map[int64]batch.Batcher 39 | } 40 | 41 | func (m *Manager) Get(shardId int64) batch.Batcher { 42 | m.RLock() 43 | batcher, ok := m.batchers[shardId] 44 | m.RUnlock() 45 | 46 | if ok { 47 | return batcher 48 | } 49 | 50 | // Fallback on write-lock 51 | m.Lock() 52 | defer m.Unlock() 53 | 54 | if batcher, ok = m.batchers[shardId]; !ok { 55 | batcher = m.batcherFactory(m.ctx, &shardId) 56 | m.batchers[shardId] = batcher 57 | } 58 | return batcher 59 | } 60 | 61 | func (m *Manager) Close() error { 62 | m.Lock() 63 | defer m.Unlock() 64 | 65 | var err error 66 | for id, batcher := range m.batchers { 67 | delete(m.batchers, id) 68 | err = multierr.Append(err, batcher.Close()) 69 | } 70 | 71 | return err 72 | } 73 | -------------------------------------------------------------------------------- /oxia/internal/batch/manager_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package batch 16 | 17 | import ( 18 | "context" 19 | "errors" 20 | "testing" 21 | 22 | "github.com/stretchr/testify/assert" 23 | 24 | "github.com/streamnative/oxia/oxia/batch" 25 | ) 26 | 27 | var errClose = errors.New("closed") 28 | 29 | type testBatcher struct { 30 | closed bool 31 | } 32 | 33 | func (b *testBatcher) Close() error { 34 | b.closed = true 35 | return errClose 36 | } 37 | 38 | func (b *testBatcher) Add(any) {} 39 | 40 | func (b *testBatcher) Run() {} 41 | 42 | func TestManager(t *testing.T) { 43 | testBatcher := &testBatcher{} 44 | 45 | newBatcherInvocations := 0 46 | batcherFactory := func(context.Context, *int64) batch.Batcher { 47 | newBatcherInvocations++ 48 | return testBatcher 49 | } 50 | 51 | manager := NewManager(context.Background(), batcherFactory) 52 | 53 | batcher := manager.Get(shardId) 54 | assert.Equal(t, testBatcher, batcher) 55 | assert.Equal(t, 1, newBatcherInvocations) 56 | 57 | batcher = manager.Get(shardId) 58 | assert.Equal(t, testBatcher, batcher) 59 | assert.Equal(t, 1, newBatcherInvocations) 60 | 61 | err := manager.Close() 62 | assert.ErrorIs(t, err, errClose) 63 | 64 | assert.True(t, testBatcher.closed) 65 | 66 | _ = manager.Get(shardId) 67 | // proves that the batcher was removed on Close 68 | // as it had to recreate it on Get 69 | assert.Equal(t, 2, newBatcherInvocations) 70 | } 71 | -------------------------------------------------------------------------------- /oxia/internal/batch/rpc_errors.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package batch 16 | 17 | import ( 18 | "google.golang.org/grpc/codes" 19 | "google.golang.org/grpc/status" 20 | 21 | "github.com/streamnative/oxia/common/constant" 22 | ) 23 | 24 | func isRetriable(err error) bool { 25 | code := status.Code(err) 26 | switch code { 27 | case codes.Unavailable: 28 | // Failure to connect is ok to re-attempt 29 | return true 30 | case constant.CodeInvalidStatus: 31 | // Leader has fenced the shard, though we expect a new leader to be elected 32 | return true 33 | case constant.CodeAlreadyClosed: 34 | // Leader is closing, though we expect a new leader to be elected 35 | return true 36 | case constant.CodeNodeIsNotLeader: 37 | // We're making a request to a node that is not leader anymore. 38 | // Retry to make the request to the new leader 39 | return true 40 | } 41 | 42 | return false 43 | } 44 | -------------------------------------------------------------------------------- /oxia/internal/batch/utils_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package batch 16 | 17 | import ( 18 | "github.com/streamnative/oxia/oxia/batch" 19 | ) 20 | 21 | var ( 22 | shardId = int64(1) 23 | one = int64(1) 24 | two = int64(2) 25 | ) 26 | 27 | func add(b batch.Batch, call any) (panicked bool) { 28 | defer func() { 29 | if r := recover(); r != nil { 30 | panicked = true 31 | } 32 | }() 33 | b.Add(call) 34 | 35 | return false 36 | } 37 | -------------------------------------------------------------------------------- /oxia/internal/convert.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package internal 16 | 17 | import ( 18 | "github.com/streamnative/oxia/proto" 19 | ) 20 | 21 | func toShard(assignment *proto.ShardAssignment) Shard { 22 | return Shard{ 23 | Id: assignment.Shard, 24 | Leader: assignment.Leader, 25 | HashRange: toHashRange(assignment), 26 | } 27 | } 28 | 29 | func toHashRange(assignment *proto.ShardAssignment) HashRange { 30 | switch boundaries := assignment.ShardBoundaries.(type) { 31 | case *proto.ShardAssignment_Int32HashRange: 32 | return HashRange{ 33 | MinInclusive: boundaries.Int32HashRange.MinHashInclusive, 34 | MaxInclusive: boundaries.Int32HashRange.MaxHashInclusive, 35 | } 36 | default: 37 | panic("unknown shard boundary") 38 | } 39 | } 40 | 41 | func hashRange(minInclusive uint32, maxInclusive uint32) HashRange { 42 | return HashRange{ 43 | MinInclusive: minInclusive, 44 | MaxInclusive: maxInclusive, 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /oxia/internal/convert_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package internal 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/stretchr/testify/assert" 21 | 22 | "github.com/streamnative/oxia/proto" 23 | ) 24 | 25 | func TestToShard(t *testing.T) { 26 | for _, item := range []struct { 27 | assignment *proto.ShardAssignment 28 | shard Shard 29 | err error 30 | }{ 31 | { 32 | &proto.ShardAssignment{ 33 | Shard: 1, 34 | Leader: "leader:1234", 35 | ShardBoundaries: &proto.ShardAssignment_Int32HashRange{ 36 | Int32HashRange: &proto.Int32HashRange{ 37 | MinHashInclusive: 1, 38 | MaxHashInclusive: 2, 39 | }, 40 | }, 41 | }, Shard{ 42 | Id: 1, 43 | Leader: "leader:1234", 44 | HashRange: hashRange(1, 2), 45 | }, nil}, 46 | } { 47 | result := toShard(item.assignment) 48 | assert.Equal(t, item.shard, result) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /oxia/internal/metrics/timer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package metrics 16 | 17 | import ( 18 | "context" 19 | "time" 20 | 21 | "go.opentelemetry.io/otel/metric" 22 | ) 23 | 24 | type Timer interface { 25 | Record(ctx context.Context, incr time.Duration, attrs metric.MeasurementOption) 26 | } 27 | 28 | type timerImpl struct { 29 | sum metric.Float64Counter 30 | count metric.Int64Counter 31 | } 32 | 33 | func newTimer(meter metric.Meter, name string) Timer { 34 | return &timerImpl{ 35 | sum: newMillisCounter(meter, name), 36 | count: newCounter(meter, name, ""), 37 | } 38 | } 39 | 40 | func (t *timerImpl) Record(ctx context.Context, incr time.Duration, attrs metric.MeasurementOption) { 41 | millis := float64(incr) / float64(time.Millisecond) 42 | t.sum.Add(ctx, millis, attrs) 43 | t.count.Add(ctx, 1, attrs) 44 | } 45 | -------------------------------------------------------------------------------- /oxia/internal/shard_strategy.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package internal 16 | 17 | type ShardStrategy interface { 18 | Get(key string) func(Shard) bool 19 | } 20 | 21 | type Shard struct { 22 | Id int64 23 | Leader string 24 | HashRange HashRange 25 | } 26 | 27 | type HashRange struct { 28 | MinInclusive uint32 29 | MaxInclusive uint32 30 | } 31 | -------------------------------------------------------------------------------- /oxia/internal/shard_strategy_impl.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package internal 16 | 17 | import ( 18 | "github.com/streamnative/oxia/common/hash" 19 | ) 20 | 21 | type shardStrategyImpl struct { 22 | hashFunc func(string) uint32 23 | } 24 | 25 | func NewShardStrategy() ShardStrategy { 26 | return &shardStrategyImpl{ 27 | hashFunc: hash.Xxh332, 28 | } 29 | } 30 | 31 | func (s *shardStrategyImpl) Get(key string) func(Shard) bool { 32 | code := s.hashFunc(key) 33 | return func(shard Shard) bool { 34 | hashRange := shard.HashRange 35 | return hashRange.MinInclusive <= code && code <= hashRange.MaxInclusive 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /oxia/internal/shard_strategy_impl_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package internal 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/stretchr/testify/assert" 21 | ) 22 | 23 | func TestShardStrategy(t *testing.T) { 24 | shardStrategy := &shardStrategyImpl{ 25 | hashFunc: func(key string) uint32 { 26 | return 2 27 | }, 28 | } 29 | predicate := shardStrategy.Get("foo") 30 | 31 | for _, item := range []struct { 32 | minInclusive uint32 33 | maxInclusive uint32 34 | match bool 35 | }{ 36 | {1, 3, true}, 37 | {2, 3, true}, 38 | {1, 2, true}, 39 | {1, 1, false}, 40 | {3, 3, false}, 41 | } { 42 | shard := Shard{ 43 | HashRange: HashRange{ 44 | MinInclusive: item.minInclusive, 45 | MaxInclusive: item.maxInclusive, 46 | }, 47 | } 48 | assert.Equal(t, item.match, predicate(shard)) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /oxia/notifications_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package oxia 16 | 17 | import ( 18 | "context" 19 | "testing" 20 | 21 | "github.com/stretchr/testify/assert" 22 | ) 23 | 24 | func TestNotificationsClose(t *testing.T) { 25 | count := 0 26 | ctx, cancel := context.WithCancel(context.Background()) 27 | 28 | nm := ¬ifications{ 29 | cancel: func() { 30 | count++ 31 | }, 32 | ctxMultiplexChanClosed: ctx, 33 | multiplexCh: make(chan *Notification, 100), 34 | } 35 | nm.multiplexCh <- &Notification{ 36 | Key: "key1", 37 | } 38 | nm.multiplexCh <- &Notification{ 39 | Key: "key2", 40 | } 41 | close(nm.multiplexCh) 42 | 43 | cancel() 44 | err := nm.Close() 45 | assert.NoError(t, err) 46 | assert.Equal(t, 1, count) 47 | 48 | n, ok := <-nm.multiplexCh 49 | assert.Equal(t, false, ok) 50 | assert.Nil(t, n) 51 | } 52 | -------------------------------------------------------------------------------- /oxia/optional.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package oxia 16 | 17 | // Optional represents a wrapper for some value that can be present or not. 18 | type Optional[T any] interface { 19 | // Present is true if the optional value is set 20 | Present() bool 21 | 22 | // Empty is true if the optional value is not set 23 | Empty() bool 24 | 25 | // Get the value and test if it was present 26 | Get() (value T, ok bool) 27 | 28 | // MustGet get the value and panic if it's not present 29 | MustGet() T 30 | } 31 | 32 | type optional[T any] struct { 33 | value T 34 | present bool 35 | } 36 | 37 | func (o *optional[T]) Present() bool { 38 | return o.present 39 | } 40 | 41 | func (o *optional[T]) Empty() bool { 42 | return !o.present 43 | } 44 | 45 | func (o *optional[T]) Get() (value T, ok bool) { 46 | return o.value, o.present 47 | } 48 | 49 | func (o *optional[T]) MustGet() T { 50 | if o.Empty() { 51 | panic("optional empty on MustGet call") 52 | } 53 | return o.value 54 | } 55 | 56 | func optionalOf[T any](t T) Optional[T] { 57 | return &optional[T]{ 58 | present: true, 59 | value: t, 60 | } 61 | } 62 | 63 | func empty[T any]() Optional[T] { 64 | return &optional[T]{present: false} 65 | } 66 | -------------------------------------------------------------------------------- /oxia/optional_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package oxia 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/stretchr/testify/assert" 21 | ) 22 | 23 | func TestOptionalPresent(t *testing.T) { 24 | o := optionalOf(1) 25 | assert.True(t, o.Present()) 26 | assert.False(t, o.Empty()) 27 | v, ok := o.Get() 28 | assert.True(t, ok) 29 | assert.Equal(t, 1, v) 30 | } 31 | 32 | func TestOptionalEmpty(t *testing.T) { 33 | e := empty[string]() 34 | assert.False(t, e.Present()) 35 | assert.True(t, e.Empty()) 36 | es, ok := e.Get() 37 | assert.False(t, ok) 38 | assert.Equal(t, "", es) 39 | } 40 | -------------------------------------------------------------------------------- /oxia/options_delete.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package oxia 16 | 17 | type deleteOptions struct { 18 | baseOptions 19 | expectedVersion *int64 20 | } 21 | 22 | // DeleteOption represents an option for the [SyncClient.Delete] operation. 23 | type DeleteOption interface { 24 | PutOption 25 | applyDelete(opts *deleteOptions) 26 | } 27 | 28 | func newDeleteOptions(opts []DeleteOption) *deleteOptions { 29 | deleteOpts := &deleteOptions{} 30 | for _, opt := range opts { 31 | opt.applyDelete(deleteOpts) 32 | } 33 | return deleteOpts 34 | } 35 | 36 | // ExpectedVersionId Marks that the operation should only be successful 37 | // if the versionId of the record stored in the server matches the expected one. 38 | func ExpectedVersionId(versionId int64) DeleteOption { 39 | return &expectedVersionId{versionId} 40 | } 41 | 42 | type expectedVersionId struct { 43 | versionId int64 44 | } 45 | 46 | func (e *expectedVersionId) applyPut(opts *putOptions) { 47 | opts.expectedVersion = &e.versionId 48 | } 49 | 50 | func (e *expectedVersionId) applyDelete(opts *deleteOptions) { 51 | opts.expectedVersion = &e.versionId 52 | } 53 | -------------------------------------------------------------------------------- /oxia/options_delete_range.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package oxia 16 | 17 | type deleteRangeOptions struct { 18 | baseOptions 19 | } 20 | 21 | // DeleteRangeOption represents an option for the [SyncClient.Delete] operation. 22 | type DeleteRangeOption interface { 23 | applyDeleteRange(opts *deleteRangeOptions) 24 | } 25 | 26 | func newDeleteRangeOptions(opts []DeleteRangeOption) *deleteRangeOptions { 27 | deleteRangeOpts := &deleteRangeOptions{} 28 | for _, opt := range opts { 29 | opt.applyDeleteRange(deleteRangeOpts) 30 | } 31 | return deleteRangeOpts 32 | } 33 | -------------------------------------------------------------------------------- /oxia/options_get_sequence_updates.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package oxia 16 | 17 | type getSequenceUpdatesOptions struct { 18 | baseOptions 19 | } 20 | 21 | // GetSequenceUpdatesOption represents an option for the [SyncClient.GetSequenceUpdates] operation. 22 | type GetSequenceUpdatesOption interface { 23 | applyGetSequenceUpdates(opts *getSequenceUpdatesOptions) 24 | } 25 | 26 | func newGetSequenceUpdatesOptions(opts []GetSequenceUpdatesOption) *getSequenceUpdatesOptions { 27 | getSequenceUpdatesOptions := &getSequenceUpdatesOptions{} 28 | for _, opt := range opts { 29 | opt.applyGetSequenceUpdates(getSequenceUpdatesOptions) 30 | } 31 | return getSequenceUpdatesOptions 32 | } 33 | -------------------------------------------------------------------------------- /oxia/options_list.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package oxia 16 | 17 | type listOptions struct { 18 | baseOptions 19 | 20 | secondaryIndexName *string 21 | } 22 | 23 | // ListOption represents an option for the [SyncClient.List] operation. 24 | type ListOption interface { 25 | applyList(opts *listOptions) 26 | applyRangeScan(opts *rangeScanOptions) 27 | applyGet(opts *getOptions) 28 | } 29 | 30 | func newListOptions(opts []ListOption) *listOptions { 31 | listOpts := &listOptions{} 32 | for _, opt := range opts { 33 | opt.applyList(listOpts) 34 | } 35 | return listOpts 36 | } 37 | 38 | type useIndex struct { 39 | indexName string 40 | } 41 | 42 | func (u *useIndex) applyList(opts *listOptions) { 43 | opts.secondaryIndexName = &u.indexName 44 | } 45 | 46 | func (u *useIndex) applyRangeScan(opts *rangeScanOptions) { 47 | opts.secondaryIndexName = &u.indexName 48 | } 49 | 50 | func (u *useIndex) applyGet(opts *getOptions) { 51 | opts.secondaryIndexName = &u.indexName 52 | } 53 | 54 | // UseIndex let the users specify a different index to follow for the 55 | // Note: The returned list will contain they primary keys of the records. 56 | func UseIndex(indexName string) ListOption { 57 | return &useIndex{indexName} 58 | } 59 | -------------------------------------------------------------------------------- /oxia/options_range_scan.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package oxia 16 | 17 | type rangeScanOptions struct { 18 | listOptions 19 | } 20 | 21 | // RangeScanOption represents an option for the [SyncClient.RangeScan] operation. 22 | type RangeScanOption interface { 23 | applyRangeScan(opts *rangeScanOptions) 24 | } 25 | 26 | func newRangeScanOptions(opts []RangeScanOption) *rangeScanOptions { 27 | rangeScanOpts := &rangeScanOptions{} 28 | for _, opt := range opts { 29 | opt.applyRangeScan(rangeScanOpts) 30 | } 31 | return rangeScanOpts 32 | } 33 | -------------------------------------------------------------------------------- /oxia/oxia.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package oxia provides a Go client library for interacting with Oxia service. 16 | package oxia // import "github.com/streamnative/oxia/oxia" 17 | -------------------------------------------------------------------------------- /oxia/results_heap.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package oxia 16 | 17 | import "github.com/streamnative/oxia/common/compare" 18 | 19 | type ResultAndChannel struct { 20 | gr GetResult 21 | ch chan GetResult 22 | } 23 | 24 | type ResultHeap []*ResultAndChannel 25 | 26 | func (h ResultHeap) Len() int { 27 | return len(h) 28 | } 29 | 30 | func (h ResultHeap) Less(i, j int) bool { 31 | return compare.CompareWithSlash([]byte(h[i].gr.Key), []byte(h[j].gr.Key)) < 0 32 | } 33 | 34 | func (h ResultHeap) Swap(i, j int) { 35 | h[i], h[j] = h[j], h[i] 36 | } 37 | 38 | func (h *ResultHeap) Push(x any) { 39 | *h = append(*h, x.(*ResultAndChannel)) 40 | } 41 | 42 | func (h *ResultHeap) Pop() any { 43 | old := *h 44 | n := len(old) 45 | x := old[n-1] 46 | *h = old[0 : n-1] 47 | return x 48 | } 49 | -------------------------------------------------------------------------------- /proto/proto_grpc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package proto 16 | 17 | import ( 18 | "fmt" 19 | 20 | "github.com/planetscale/vtprotobuf/codec/grpc" 21 | "google.golang.org/grpc/encoding" 22 | _ "google.golang.org/grpc/encoding/proto" 23 | pb "google.golang.org/protobuf/proto" 24 | ) 25 | 26 | type vtprotoCodec struct{} 27 | 28 | type vtprotoMessage interface { 29 | MarshalVT() ([]byte, error) 30 | UnmarshalVT([]byte) error 31 | } 32 | 33 | func (vtprotoCodec) Marshal(v any) ([]byte, error) { 34 | switch v := v.(type) { 35 | case vtprotoMessage: 36 | return v.MarshalVT() 37 | case pb.Message: 38 | return pb.Marshal(v) 39 | default: 40 | return nil, fmt.Errorf("failed to marshal, message is %T, must satisfy the vtprotoMessage interface or want proto.Message", v) 41 | } 42 | } 43 | 44 | func (vtprotoCodec) Unmarshal(data []byte, v any) error { 45 | switch v := v.(type) { 46 | case vtprotoMessage: 47 | return v.UnmarshalVT(data) 48 | case pb.Message: 49 | return pb.Unmarshal(data, v) 50 | default: 51 | return fmt.Errorf("failed to unmarshal, message is %T, must satisfy the vtprotoMessage interface or want proto.Message", v) 52 | } 53 | } 54 | 55 | func (vtprotoCodec) Name() string { 56 | return grpc.Name 57 | } 58 | 59 | func init() { 60 | encoding.RegisterCodec(vtprotoCodec{}) 61 | } 62 | -------------------------------------------------------------------------------- /proto/proto_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package proto 16 | 17 | import ( 18 | "testing" 19 | "time" 20 | 21 | "github.com/stretchr/testify/assert" 22 | "google.golang.org/protobuf/proto" 23 | ) 24 | 25 | var appendRequest = &Append{ 26 | Term: 1, 27 | Entry: &LogEntry{ 28 | Term: 1, 29 | Offset: 128, 30 | Value: make([]byte, 1024), 31 | Timestamp: uint64(time.Now().UnixNano()), 32 | }, 33 | CommitOffset: 128, 34 | } 35 | 36 | func Benchmark_ProtoMarshall(b *testing.B) { 37 | for i := 0; i < b.N; i++ { 38 | _, err := proto.Marshal(appendRequest) 39 | assert.NoError(b, err) 40 | } 41 | } 42 | 43 | func Benchmark_VTProtoMarshall(b *testing.B) { 44 | for i := 0; i < b.N; i++ { 45 | _, err := appendRequest.MarshalVT() 46 | assert.NoError(b, err) 47 | } 48 | } 49 | 50 | func Benchmark_VTProtoMarshall_WithBuffer(b *testing.B) { 51 | buf := make([]byte, appendRequest.SizeVT()) 52 | for i := 0; i < b.N; i++ { 53 | _, err := appendRequest.MarshalToSizedBufferVT(buf) 54 | assert.NoError(b, err) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /proto/storage.proto: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | package proto; 18 | 19 | option go_package = "github.com/streamnative/oxia/proto"; 20 | 21 | import "client.proto"; 22 | import "google/protobuf/descriptor.proto"; 23 | 24 | extend google.protobuf.MessageOptions { 25 | optional bool mempool = 64101; 26 | } 27 | 28 | message StorageEntry { 29 | option (mempool) = true; 30 | 31 | bytes value = 1; 32 | int64 version_id = 2; 33 | int64 modifications_count = 3; 34 | fixed64 creation_timestamp = 4; 35 | fixed64 modification_timestamp = 5; 36 | 37 | optional int64 session_id = 6; 38 | optional string client_identity = 7; 39 | 40 | optional string partition_key = 8; 41 | 42 | repeated io.streamnative.oxia.proto.SecondaryIndex secondary_indexes = 9; 43 | } 44 | 45 | message SessionMetadata { 46 | option (mempool) = true; 47 | 48 | uint32 timeout_ms = 1; 49 | string identity = 2; 50 | } 51 | 52 | message LogEntryValue { 53 | option (mempool) = true; 54 | oneof value { 55 | WriteRequests requests = 1; 56 | } 57 | } 58 | 59 | message WriteRequests { 60 | repeated io.streamnative.oxia.proto.WriteRequest writes = 1; 61 | } 62 | -------------------------------------------------------------------------------- /server/auth/authentication.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package auth 16 | 17 | import ( 18 | "context" 19 | 20 | "github.com/pkg/errors" 21 | ) 22 | 23 | const ( 24 | ProviderOIDC = "oidc" 25 | 26 | ProviderParamTypeToken = "token" 27 | ) 28 | 29 | var ( 30 | ErrUnsupportedProvider = errors.New("unsupported authentication provider") 31 | ErrUnMatchedAuthenticationParamType = errors.New("unmatched authentication parameter type") 32 | ErrEmptyToken = errors.New("empty token") 33 | ErrMalformedToken = errors.New("malformed token") 34 | ) 35 | 36 | var Disabled = Options{} 37 | 38 | type Options struct { 39 | ProviderName string 40 | ProviderParams string 41 | } 42 | 43 | func (op *Options) IsEnabled() bool { 44 | return op != nil && op.ProviderName != "" 45 | } 46 | 47 | // todo: add metrics 48 | type AuthenticationProvider interface { 49 | AcceptParamType() string 50 | Authenticate(ctx context.Context, param any) (string, error) 51 | } 52 | 53 | func NewAuthenticationProvider(ctx context.Context, options Options) (AuthenticationProvider, error) { 54 | switch options.ProviderName { 55 | case ProviderOIDC: 56 | return NewOIDCProvider(ctx, options.ProviderParams) 57 | default: 58 | return nil, ErrUnsupportedProvider 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /server/constants.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package server 16 | 17 | import ( 18 | "github.com/streamnative/oxia/proto" 19 | "github.com/streamnative/oxia/server/wal" 20 | ) 21 | 22 | var InvalidEntryId = &proto.EntryId{ 23 | Term: wal.InvalidTerm, 24 | Offset: wal.InvalidOffset, 25 | } 26 | -------------------------------------------------------------------------------- /server/internal_rpc_server_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package server 16 | 17 | import ( 18 | "context" 19 | "fmt" 20 | "testing" 21 | 22 | "github.com/stretchr/testify/assert" 23 | "google.golang.org/grpc" 24 | "google.golang.org/grpc/credentials/insecure" 25 | "google.golang.org/grpc/health" 26 | "google.golang.org/grpc/health/grpc_health_v1" 27 | 28 | "github.com/streamnative/oxia/common/rpc" 29 | ) 30 | 31 | func TestInternalHealthCheck(t *testing.T) { 32 | healthServer := health.NewServer() 33 | server, err := newInternalRpcServer(rpc.Default, "localhost:0", nil, 34 | NewShardAssignmentDispatcher(healthServer), healthServer, nil) 35 | assert.NoError(t, err) 36 | 37 | target := fmt.Sprintf("localhost:%d", server.grpcServer.Port()) 38 | cnx, err := grpc.NewClient(target, grpc.WithTransportCredentials(insecure.NewCredentials())) 39 | assert.NoError(t, err) 40 | 41 | client := grpc_health_v1.NewHealthClient(cnx) 42 | 43 | request := &grpc_health_v1.HealthCheckRequest{Service: ""} 44 | response, err := client.Check(context.Background(), request) 45 | assert.NoError(t, err) 46 | 47 | assert.Equal(t, grpc_health_v1.HealthCheckResponse_SERVING, response.Status) 48 | } 49 | -------------------------------------------------------------------------------- /server/kv/db_benchmark_deleterange_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package kv 16 | 17 | import ( 18 | "fmt" 19 | "testing" 20 | "time" 21 | 22 | "github.com/stretchr/testify/assert" 23 | "k8s.io/utils/ptr" 24 | 25 | "github.com/streamnative/oxia/common/constant" 26 | time2 "github.com/streamnative/oxia/common/time" 27 | 28 | "github.com/streamnative/oxia/proto" 29 | ) 30 | 31 | func BenchmarkDeleteRange(b *testing.B) { 32 | factory, err := NewPebbleKVFactory(&FactoryOptions{ 33 | InMemory: true, 34 | CacheSizeMB: 1024, 35 | }) 36 | assert.NoError(b, err) 37 | db, err := NewDB(constant.DefaultNamespace, 1, factory, 0, time2.SystemClock) 38 | assert.NoError(b, err) 39 | defer db.Close() 40 | for i := range b.N { 41 | _, err := db.ProcessWrite(&proto.WriteRequest{ 42 | Puts: []*proto.PutRequest{ 43 | { 44 | Key: "00000000000000000001", 45 | PartitionKey: ptr.To("00000000000000000001"), 46 | Value: []byte("00000000000000000000"), 47 | SequenceKeyDelta: []uint64{1}, 48 | }, 49 | }, 50 | DeleteRanges: []*proto.DeleteRangeRequest{ 51 | { 52 | StartInclusive: "00000000000000000001-00000000000000000000", 53 | EndExclusive: fmt.Sprintf("00000000000000000001-%020d", i), 54 | }, 55 | }, 56 | }, int64(i), uint64(time.Now().UnixMilli()), NoOpCallback) 57 | assert.NoError(b, err) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /server/kv/db_sequences_wait_tracker_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package kv 16 | 17 | import ( 18 | "context" 19 | "testing" 20 | 21 | "github.com/stretchr/testify/assert" 22 | ) 23 | 24 | func TestSequencesWaitTracker(t *testing.T) { 25 | swt := NewSequencesWaitTracker() 26 | 27 | w1 := swt.AddSequenceWaiter("key-1") 28 | w2 := swt.AddSequenceWaiter("key-2") 29 | 30 | assert.Empty(t, w1.Ch()) 31 | assert.Empty(t, w2.Ch()) 32 | 33 | swt.SequenceUpdated("non-existing", "non-existing-123") 34 | assert.Empty(t, w1.Ch()) 35 | assert.Empty(t, w2.Ch()) 36 | 37 | swt.SequenceUpdated("key-1", "key-1-123") 38 | x1, err := w1.Receive(context.Background()) 39 | assert.NoError(t, err) 40 | assert.Equal(t, "key-1-123", x1) 41 | assert.Empty(t, w2.Ch()) 42 | 43 | swt.SequenceUpdated("key-2", "key-2-456") 44 | assert.Empty(t, w1.Ch()) 45 | x2, err := w2.Receive(context.Background()) 46 | assert.NoError(t, err) 47 | assert.Equal(t, "key-2-456", x2) 48 | 49 | w3 := swt.AddSequenceWaiter("key-1") 50 | 51 | swt.SequenceUpdated("key-1", "key-1-890") 52 | x3 := <-w1.Ch() 53 | assert.Equal(t, "key-1-890", x3) 54 | x4 := <-w3.Ch() 55 | assert.Equal(t, "key-1-890", x4) 56 | assert.Empty(t, w2.Ch()) 57 | 58 | assert.NoError(t, w1.Close()) 59 | 60 | swt.SequenceUpdated("key-1", "key-1-234") 61 | assert.Empty(t, w1.Ch()) 62 | x5 := <-w3.Ch() 63 | assert.Equal(t, "key-1-234", x5) 64 | assert.Empty(t, w2.Ch()) 65 | 66 | assert.Empty(t, w3.Ch()) 67 | 68 | ch3 := w3.Ch() 69 | _ = w3.Close() 70 | swt.SequenceUpdated("key-1", "key-1-234") 71 | assert.Empty(t, ch3) 72 | } 73 | -------------------------------------------------------------------------------- /server/kv/pebble_logger.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package kv 16 | 17 | import ( 18 | "fmt" 19 | "log/slog" 20 | "os" 21 | ) 22 | 23 | // pebbleLogger is the wrapper of slog to implement pebbel's logger interface. 24 | type pebbleLogger struct { 25 | zl *slog.Logger 26 | } 27 | 28 | func (pl *pebbleLogger) Infof(format string, args ...any) { 29 | pl.zl.Info( 30 | fmt.Sprintf(format, args...), 31 | ) 32 | } 33 | 34 | func (pl *pebbleLogger) Errorf(format string, args ...any) { 35 | pl.zl.Error( 36 | fmt.Sprintf(format, args...), 37 | ) 38 | } 39 | 40 | func (pl *pebbleLogger) Fatalf(format string, args ...any) { 41 | pl.zl.Warn( 42 | fmt.Sprintf(format, args...), 43 | ) 44 | os.Exit(1) 45 | } 46 | -------------------------------------------------------------------------------- /server/server_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package server 16 | 17 | import ( 18 | "fmt" 19 | "io" 20 | "net/http" 21 | "testing" 22 | 23 | "github.com/stretchr/testify/assert" 24 | ) 25 | 26 | func TestNewServer(t *testing.T) { 27 | config := Config{ 28 | InternalServiceAddr: "localhost:0", 29 | PublicServiceAddr: "localhost:0", 30 | MetricsServiceAddr: "localhost:0", 31 | } 32 | 33 | server, err := New(config) 34 | assert.NoError(t, err) 35 | 36 | url := fmt.Sprintf("http://localhost:%d/metrics", server.metrics.Port()) 37 | response, err := http.Get(url) 38 | assert.NoError(t, err) 39 | if response != nil && response.Body != nil { 40 | defer response.Body.Close() 41 | } 42 | 43 | assert.Equal(t, 200, response.StatusCode) 44 | 45 | body, err := io.ReadAll(response.Body) 46 | assert.NoError(t, err) 47 | 48 | // Looks like exposition format 49 | assert.Equal(t, "# HELP ", string(body[0:7])) 50 | } 51 | -------------------------------------------------------------------------------- /server/util/bitset.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package util 16 | 17 | import ( 18 | "fmt" 19 | "math/bits" 20 | ) 21 | 22 | const MaxBitSetSize = 16 23 | 24 | // BitSet 25 | // Simplified and compact bitset. 26 | type BitSet struct { 27 | bits uint16 28 | } 29 | 30 | func (bs *BitSet) Count() int { 31 | return bits.OnesCount16(bs.bits) 32 | } 33 | 34 | func (bs *BitSet) Set(idx int) { 35 | if idx < 0 || idx >= MaxBitSetSize { 36 | panic(fmt.Sprintf("invalid index: %d", idx)) 37 | } 38 | bs.bits |= 1 << idx 39 | } 40 | -------------------------------------------------------------------------------- /server/util/bitset_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package util 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/stretchr/testify/assert" 21 | ) 22 | 23 | func TestBitSet(t *testing.T) { 24 | bs := BitSet{} 25 | 26 | assert.Zero(t, bs.Count()) 27 | 28 | bs.Set(0) 29 | assert.Equal(t, 1, bs.Count()) 30 | 31 | bs.Set(2) 32 | assert.Equal(t, 2, bs.Count()) 33 | 34 | bs.Set(1) 35 | assert.Equal(t, 3, bs.Count()) 36 | 37 | bs.Set(2) 38 | assert.Equal(t, 3, bs.Count()) 39 | } 40 | 41 | func TestBitSetPanic(t *testing.T) { 42 | bs := BitSet{} 43 | 44 | assert.Panics(t, func() { 45 | bs.Set(-2) 46 | }) 47 | 48 | assert.Panics(t, func() { 49 | bs.Set(16) 50 | }) 51 | 52 | assert.Panics(t, func() { 53 | bs.Set(20) 54 | }) 55 | } 56 | -------------------------------------------------------------------------------- /server/util/crc/crc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package crc 16 | 17 | import ( 18 | "hash/crc32" 19 | ) 20 | 21 | const MagicNumber uint32 = 0xa282ead8 22 | 23 | var table = crc32.MakeTable(crc32.Castagnoli) 24 | 25 | type Checksum uint32 26 | 27 | func (c Checksum) Update(b []byte) Checksum { 28 | return Checksum(crc32.Update(uint32(c), table, b)) 29 | } 30 | func (c Checksum) Value() uint32 { 31 | return uint32(c>>15|c<<17) + MagicNumber 32 | } 33 | -------------------------------------------------------------------------------- /server/util/file.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package util 16 | 17 | import ( 18 | "errors" 19 | "os" 20 | ) 21 | 22 | // RemoveFileIfExists removes the file, it will return nil if the file does not exist. 23 | func RemoveFileIfExists(path string) error { 24 | err := os.Remove(path) 25 | if err != nil && !errors.Is(err, os.ErrNotExist) { 26 | return err 27 | } 28 | 29 | return nil 30 | } 31 | -------------------------------------------------------------------------------- /server/util/file_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package util 16 | 17 | import ( 18 | "errors" 19 | "os" 20 | "path" 21 | "testing" 22 | 23 | "github.com/stretchr/testify/assert" 24 | ) 25 | 26 | func TestRemoveFileIfExists(t *testing.T) { 27 | t.Run("file not exists", func(t *testing.T) { 28 | path := path.Join(t.TempDir(), "file_not_exists") 29 | _, err := os.Stat(path) 30 | assert.True(t, errors.Is(err, os.ErrNotExist)) 31 | 32 | err = RemoveFileIfExists(path) 33 | assert.NoError(t, err) 34 | }) 35 | 36 | t.Run("file exists", func(t *testing.T) { 37 | path := path.Join(t.TempDir(), "file_exists") 38 | f, err := os.Create(path) 39 | assert.NoError(t, err) 40 | assert.NoError(t, f.Close()) 41 | 42 | err = RemoveFileIfExists(path) 43 | assert.NoError(t, err) 44 | 45 | _, err = os.Stat(path) 46 | assert.True(t, errors.Is(err, os.ErrNotExist)) 47 | }) 48 | } 49 | -------------------------------------------------------------------------------- /server/wal/codec/codec_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package codec 16 | 17 | import ( 18 | "os" 19 | "path" 20 | "testing" 21 | 22 | "github.com/stretchr/testify/assert" 23 | ) 24 | 25 | func TestCodec_GetOrCreate(t *testing.T) { 26 | baseDir := os.TempDir() 27 | nonExistFileName := "0" 28 | v1FileName := "1" 29 | v2FileName := "2" 30 | _, err := os.Create(path.Join(baseDir, v1FileName+v1.GetTxnExtension())) 31 | assert.NoError(t, err) 32 | _, err = os.Create(path.Join(baseDir, v2FileName+v2.GetTxnExtension())) 33 | assert.NoError(t, err) 34 | 35 | codec, exist, err := GetOrCreate(path.Join(baseDir, nonExistFileName)) 36 | assert.NoError(t, err) 37 | assert.EqualValues(t, v2, codec) 38 | assert.EqualValues(t, false, exist) 39 | 40 | codec, exist, err = GetOrCreate(path.Join(baseDir, v1FileName)) 41 | assert.NoError(t, err) 42 | assert.EqualValues(t, v1, codec) 43 | assert.EqualValues(t, true, exist) 44 | 45 | codec, exist, err = GetOrCreate(path.Join(baseDir, v2FileName)) 46 | assert.NoError(t, err) 47 | assert.EqualValues(t, v2, codec) 48 | assert.EqualValues(t, true, exist) 49 | } 50 | -------------------------------------------------------------------------------- /server/wal/readonly_segments_group_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package wal 16 | 17 | import ( 18 | "fmt" 19 | "os" 20 | "path/filepath" 21 | "testing" 22 | "time" 23 | 24 | "github.com/stretchr/testify/assert" 25 | 26 | "github.com/streamnative/oxia/proto" 27 | ) 28 | 29 | func TestReadOnlySegmentsGroupTrimSegments(t *testing.T) { 30 | t.Run("when txnFile is not exists", func(t *testing.T) { 31 | walFactory := NewWalFactory(&FactoryOptions{ 32 | BaseWalDir: t.TempDir(), 33 | Retention: 1 * time.Hour, 34 | SegmentSize: 128, 35 | SyncData: true, 36 | }) 37 | w, err := walFactory.NewWal("test", 1, nil) 38 | assert.NoError(t, err) 39 | for i := int64(0); i < 1000; i++ { 40 | assert.NoError(t, w.Append(&proto.LogEntry{ 41 | Term: 1, 42 | Offset: i, 43 | Value: fmt.Appendf(nil, "test-%d", i), 44 | })) 45 | } 46 | walBasePath := w.(*wal).walPath 47 | readOnlySegments, err := newReadOnlySegmentsGroup(walBasePath) 48 | assert.NoError(t, err) 49 | 50 | // Ensure newReadOnlySegment will return an NotExists error 51 | err = os.Remove(filepath.Join(walBasePath, "0.txnx")) 52 | assert.NoError(t, err) 53 | 54 | err = readOnlySegments.TrimSegments(6) 55 | assert.NoError(t, err) 56 | }) 57 | } 58 | -------------------------------------------------------------------------------- /server/wal/segment_config.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package wal 16 | 17 | import ( 18 | "github.com/streamnative/oxia/server/wal/codec" 19 | ) 20 | 21 | type segmentConfig struct { 22 | codec codec.Codec 23 | segmentExists bool 24 | txnPath string 25 | idxPath string 26 | baseOffset int64 27 | } 28 | 29 | func newSegmentConfig(basePath string, baseOffset int64) (*segmentConfig, error) { 30 | _codec, segmentExists, err := codec.GetOrCreate(segmentPath(basePath, baseOffset)) 31 | if err != nil { 32 | return nil, err 33 | } 34 | 35 | return &segmentConfig{ 36 | codec: _codec, 37 | segmentExists: segmentExists, 38 | txnPath: segmentPath(basePath, baseOffset) + _codec.GetTxnExtension(), 39 | idxPath: segmentPath(basePath, baseOffset) + _codec.GetIdxExtension(), 40 | baseOffset: baseOffset, 41 | }, nil 42 | } 43 | -------------------------------------------------------------------------------- /tests/security/certs/ca.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDBTCCAe2gAwIBAgIUB5hhGvpFVPjcGY4OYKj3C6Y2oDQwDQYJKoZIhvcNAQEL 3 | BQAwEjEQMA4GA1UEAwwHb3hpYS1jYTAeFw0yNDAzMTcwODE3MjFaFw0zNDAzMTUw 4 | ODE3MjFaMBIxEDAOBgNVBAMMB294aWEtY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IB 5 | DwAwggEKAoIBAQDtSzUYEdMbkcfLAuH5cASbAGs27dquP0DFpWyz2567vOGZkPmU 6 | wBT1pTpPnvax3ru+jejlYqWVXH0WqhAJV+aYznk5se9MKH9YGHvmTO8Sbe4l29Xw 7 | fD+daySo70dXxDQ139SOjq0Or5sknFEDWdEuF/ShAgjKIyqX7Tl+55ShrbstIiFQ 8 | JAehD3FBwRGYJF8XYoS6OaYCJq8N0XXpm0pKnYzoltTpmhIgZCni0zrH5bTKyuv1 9 | 5GvtW734xngwKEDpbE/2RQDBxMXSMHN1f6xx3od6XJKY58P1Wc21FL18UJG7NYZn 10 | NCoCAfFdXS0NFgHgXwz0cmiYB2eMeZ7B+0KzAgMBAAGjUzBRMB0GA1UdDgQWBBRQ 11 | V55Tk2qqaFx7AViwOwxoeD/UBTAfBgNVHSMEGDAWgBRQV55Tk2qqaFx7AViwOwxo 12 | eD/UBTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBCM1i96sd0 13 | XMdIVXZ/gav3qOM42j4QdCZ8NSNa2izuj0H0x9cyx5LYDzt65ZvvTOjBIuyE1nZ3 14 | 73bwO8fewyWJmGTfeC7nLCSNaeS9HagiS2zwmGniFZTcmhdw8zmLXRhzqCGGdYMc 15 | nL+RIn9dqFLkJVC9+jvNTn5+FCWAVF445fAXERtJK/xCOY+YjwrnPQ+n4TolgfDc 16 | Z0BII5XV5M0ftZfUzywtwASMgfND6I+IU0gO3ZZhzeXF79b1K/17yb7kECKIHsAE 17 | w+tDEfK3Ab+Mvv9oH8Fxu6Z5Xd3XQGDBiqEkubzhu5Es3R4tJAcQVhwM/CrFPv5q 18 | 9euuOGFZZZRt 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /tests/security/certs/ca.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEugIBADANBgkqhkiG9w0BAQEFAASCBKQwggSgAgEAAoIBAQDtSzUYEdMbkcfL 3 | AuH5cASbAGs27dquP0DFpWyz2567vOGZkPmUwBT1pTpPnvax3ru+jejlYqWVXH0W 4 | qhAJV+aYznk5se9MKH9YGHvmTO8Sbe4l29XwfD+daySo70dXxDQ139SOjq0Or5sk 5 | nFEDWdEuF/ShAgjKIyqX7Tl+55ShrbstIiFQJAehD3FBwRGYJF8XYoS6OaYCJq8N 6 | 0XXpm0pKnYzoltTpmhIgZCni0zrH5bTKyuv15GvtW734xngwKEDpbE/2RQDBxMXS 7 | MHN1f6xx3od6XJKY58P1Wc21FL18UJG7NYZnNCoCAfFdXS0NFgHgXwz0cmiYB2eM 8 | eZ7B+0KzAgMBAAECgf8v4/3TMxsSyq6H9Qz7n/VN/celo7DUJJqYnnT8glaG4pf6 9 | u5z4vct9HYZR4MM+eArBao1BE6es0qhsP+ZR/GuNwMi2ht1vgWeHYBRMETrZfVY9 10 | oyCKaIkY5twp5st/QG9JGuN14gBgoRBZUzMGehoS3hgru+gKOQ608CuqcRKOOR5e 11 | r0d0BI4qN7RS8aZ7rewxmzb6FlZtVJRec5Kd1QSXot8QQOVmcIU2y9HVa1inK7S+ 12 | 4IVlfmFr2ZF3tBUZzgHLCyF1vp6d/bqTOKd3apQyTqOB/321e6AGewaehKR6it59 13 | BpFoYpCZwAJV2n3c+YZtE0W0vVYqA3pafPT4Y7kCgYEA/KRl/yv1aLMA+fdxYbWv 14 | tQNNXOX56tjNjOhNSc9UvRXPrY3LfDjb0AEk+A42qWAVQjX+XTPhEyvielG6dZ2P 15 | PKgl3AmuQavAlcxUWXg1PSHaaP6SR2LowzLFDQj6v+99qI0WJze5ck06E6EYypox 16 | wOaKgnpV5sqNqJlUqwFJsScCgYEA8HKWO+JMTr9I64XcM4yW/Umd3euG1/5mUDrV 17 | Y+utHHH50fph1+3XbSD7T7303QV1e0D7IgxDNn5Edu5fKy5olfAYlcPqlaeirgsP 18 | 0Ta/eX0skwNe8dMHQfVB6x6rr/Cn5blhKC+2T7nTtaxuiDArADqSUcESYuYqlyTc 19 | ZRgqAZUCgYB02vGWglWjlNBJzkryP+BCoIFjC7h6CMeiejxtfGmcf/8sLl7FWWNK 20 | pYzc824TD68ljeXWZE2h2XWK0EynZDZBfyDr0sp386JDZ1xIedJeMU9dqwor3LL+ 21 | vnfoXJPmvTZpBMEPaLXtCY1oUOYvp/yFVd0RFtauxleZeTpxUkoslwKBgFD/eMZx 22 | hB0e6PSWIkinOpHWg7ynQ8RtKZArUYkYcjdb2bk3bFBuCZz0eFQzfFIcLag4iqC8 23 | msO3o16hDCQivQ6NU+rXlaWWVAHnLgvfMn89gI+BmjFCUnaQpQUMTu+01QpY+Xat 24 | I/AVfFD95jowZ7vq/zFVJdl1CbHis+/GRRhFAoGAJJ2p2ZGJXLlD+t9CM86XMYi5 25 | dHWcHg9F2LHGy/ts1osWOlupK0oFvx6h+J5MjCbDeWaM57oBIftW7RHa+xDcz/GK 26 | Yf/7Beh6MpG/aYzxGTXyS2Eu1487kSEWbSuSotg5roBA4CMp49lXMgIlK80g6PF+ 27 | UXitJmFrZXO7VCqPKIo= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /tests/security/certs/ca.srl: -------------------------------------------------------------------------------- 1 | 3EB08DA81CA84F9A911415D40B25B7B01A36C000 2 | -------------------------------------------------------------------------------- /tests/security/certs/client.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDSzCCAjOgAwIBAgIUPrCNqByoT5qRFBXUCyW3sBo2v/8wDQYJKoZIhvcNAQEL 3 | BQAwEjEQMA4GA1UEAwwHb3hpYS1jYTAeFw0yNDAzMTcwODQ0MTJaFw0zNDAzMTUw 4 | ODQ0MTJaMBYxFDASBgNVBAMMC294aWEtY2xpZW50MIIBIjANBgkqhkiG9w0BAQEF 5 | AAOCAQ8AMIIBCgKCAQEApkfGpV5V2oPyYdRGBtDoh/3mC021yCOWPfaTws24MPDa 6 | NFn28guNcLlLPs1zcQNbnN5O4bBfYcDqnt/W1vAzqLAySOvG3MVyxAJ2gaUy4AJx 7 | n99KOSgi+E5bk05B5y5dvYIAYtGPKZajgqf7kNDxF92Ob7j3XYQf+9ipMNuAjYdO 8 | Or4yagwa8QvozXmvdkfyoFapkMw8TtcNM724qJBv175bQa/Db/b6RxmeRNinXFjR 9 | pHydBYsON2bXQpkIC3IzPyjXFOysgJgvwHGtQ54RHziBMtmlaU+JV+WpAjIkTSQ0 10 | I0N+8B6uxoWk9MWktCy0tSdkiNNHqm8J9J/W1RUOkQIDAQABo4GUMIGRMA4GA1Ud 11 | DwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwIAYDVR0R 12 | BBkwF4IJbG9jYWxob3N0hwR/AAABhwTAqAECMB0GA1UdDgQWBBQYr4BhzghKkp+o 13 | I0kYIX8n4ZPvczAfBgNVHSMEGDAWgBRQV55Tk2qqaFx7AViwOwxoeD/UBTANBgkq 14 | hkiG9w0BAQsFAAOCAQEAJg9jp5MKsW+B5d3sqP2xZw3GyK74noHZVXVj7Wg2meka 15 | LnBd3pAg3tiQh7hdbmmLyY4QeZ1i9ayyLkAm6e0/DMV39ZZWFdO820H/M4Sg4Stj 16 | 8HW6UDlbZywXRk4c4vIoh4Fc8GJQj/ggJbRxl1QhpvNnfxbFk0anmKJyxgcLHaXo 17 | FHYGD9Yx6JL/gQzISlvDuAH3ZjR+A5kmedaEPByVfwDFSpNoPzbw3T1VmBvB+KG9 18 | bGtoNgX6VGudNtYftDqvPqYgXgsnZ0o19q5/Wu61860K+3Uv71YMeR1OoDDPJ6pN 19 | 2ih5l1s3OSC44z+3ynFsKMr9UEf8FN9n2PfGeVG5lA== 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /tests/security/certs/client.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIICvTCCAaUCAQAwFjEUMBIGA1UEAwwLb3hpYS1jbGllbnQwggEiMA0GCSqGSIb3 3 | DQEBAQUAA4IBDwAwggEKAoIBAQCmR8alXlXag/Jh1EYG0OiH/eYLTbXII5Y99pPC 4 | zbgw8No0WfbyC41wuUs+zXNxA1uc3k7hsF9hwOqe39bW8DOosDJI68bcxXLEAnaB 5 | pTLgAnGf30o5KCL4TluTTkHnLl29ggBi0Y8plqOCp/uQ0PEX3Y5vuPddhB/72Kkw 6 | 24CNh046vjJqDBrxC+jNea92R/KgVqmQzDxO1w0zvbiokG/XvltBr8Nv9vpHGZ5E 7 | 2KdcWNGkfJ0Fiw43ZtdCmQgLcjM/KNcU7KyAmC/Aca1DnhEfOIEy2aVpT4lX5akC 8 | MiRNJDQjQ37wHq7GhaT0xaS0LLS1J2SI00eqbwn0n9bVFQ6RAgMBAAGgYjBgBgkq 9 | hkiG9w0BCQ4xUzBRMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcD 10 | AQYIKwYBBQUHAwIwIAYDVR0RBBkwF4IJbG9jYWxob3N0hwR/AAABhwTAqAECMA0G 11 | CSqGSIb3DQEBCwUAA4IBAQCGu0X1DNV+3OAjwaoaFi+8/EzJq3GmiRo4NOYLugyy 12 | 91GjcUY9OOLzJS3FIAE1oBJqQrljyZ9Sv0ANqkDm5mFeItI4YVM4iAxso46VJbHi 13 | m+FugWDQdAdZ/fvlThSMa8XwQYxgfF5zbRCR3+liOJp9xjoJ7mQYC0OYoajjvvwB 14 | 9mY/+zG3AU+AgxjefKwZkpdHFBsR+ZegCMHXBrdye6Ye3EC9YnE3Aw6YNiE2aiS6 15 | hrKFCKK43Gy3rTj2rRKMeHbL70Qm1XKhhOjXtBFXpCzeIWRQn/3ne+rSAzNO3Uj3 16 | 0ooIpUS7Lms6kAhqthQBim4aWA76VRKaZBQgRrRbJ/Gi 17 | -----END CERTIFICATE REQUEST----- 18 | -------------------------------------------------------------------------------- /tests/security/certs/client.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCmR8alXlXag/Jh 3 | 1EYG0OiH/eYLTbXII5Y99pPCzbgw8No0WfbyC41wuUs+zXNxA1uc3k7hsF9hwOqe 4 | 39bW8DOosDJI68bcxXLEAnaBpTLgAnGf30o5KCL4TluTTkHnLl29ggBi0Y8plqOC 5 | p/uQ0PEX3Y5vuPddhB/72Kkw24CNh046vjJqDBrxC+jNea92R/KgVqmQzDxO1w0z 6 | vbiokG/XvltBr8Nv9vpHGZ5E2KdcWNGkfJ0Fiw43ZtdCmQgLcjM/KNcU7KyAmC/A 7 | ca1DnhEfOIEy2aVpT4lX5akCMiRNJDQjQ37wHq7GhaT0xaS0LLS1J2SI00eqbwn0 8 | n9bVFQ6RAgMBAAECggEAAn5fZsR653YvcN3Pl7OTVn1tcUGe68ZI2bC0d5Pr4rlX 9 | SbizAN9BJZh0c0J+gYqKLwM7I0b+BIfG8k9WDU7N34ZA7XddD0YwxJVq3zfcgvUr 10 | wg4wXUS8/YSjT/AOV0q1N4Msa/mg5X/vqiUdxdwJpDt7iDEPGJ/DdlyXRkx3FD2V 11 | 2FVHI/oRKRprrCuqZskfgxhWXQ2FFwl+TYePfUD43Oog7wwbhJsHE9miS9P1OMkk 12 | XDkvr0+2wGltw6wDtnC42XF6w7jYy6e9lxYNrxShwDbRmsfbU4VnPLi3a1Ao78Qs 13 | dphKKYm/B5z5v5EoSRmCswqCvBOCC32EGQACHyiT4QKBgQDhKJMFNmz0I6Vjzlnr 14 | 7D1i8jWZb3SVU+7xlN1OtSJZ3DF3ZxAkcd8n0dn5EagiaNHnDO5lSjkm1ieO1FUs 15 | 2HI5PLhcCkytbr/6633c9LuLhTadaG/TVmDMctLQ2kC5bfUwT6S6LI5gMtm5PiDf 16 | SEIOG2MJKKAtARjkuzWaI8AnmQKBgQC9DpOtBtp1uWjsE3ul5s5bs+YTt/f6iH6d 17 | gMjK+0EUqpbe8i94mrYY/aMbdsJTs786HUCIyEh3j7zfABmPSJe2auO6gCfIwsg1 18 | 1QB2inNYMH+SMZ0P7KB8WirmvBZW4M35E83NtU4WDDmcINqNs1bBX1LDUxzjnZl0 19 | UQwAZL6ZuQKBgH70mtlwk8ShKMt6+db+EKru+Wv3nVvpxKwxDQwpNCJI9xdlZIO4 20 | NWTFfctjulMUmW5XK0CvtkQAsrNlZFVyAg3l/+nd4NhVvygjioeA8xK7XU0qKwuW 21 | A+Sjxdz/g0lLB9pivfY/01PIuF0r++sKWjyMEq6CWraljLzEFnhAOkPZAoGBAJyl 22 | nVr6mLx+mh8xrXwzF70H/JVOE5x0yGR8tuLo+G3FQykLhqXpmwRt9F3nMvDjuIjQ 23 | gA5V1SWr5ZTookUEoyA0xOhdE5nXOsjP6ohOA3CJDsr1bOwq5nbixzVTlzb5IfFF 24 | pXrSxRajnoBtAGn0tOeYshzBmuhGR0YHeWhCEWORAoGBAKXx+r0uM3Jnr//uBdGo 25 | KbQ0GA8zfJYLX+MYeq20uPG7v9DU0WKZLrw3eqYn4IcIEMkmIEQJ9/s07vfDkiw0 26 | 7fnsEApdjdpyAyzHIqVW5eQH1b/pfd8oLwP/AYPHXjiKugTlY79ZDM7APXcXiu9M 27 | 0JzlazgCUVkoYTWSPoNdTQQ5 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /tests/security/certs/generate_command.txt: -------------------------------------------------------------------------------- 1 | # CA 2 | openssl genrsa -out ca.key 2048 3 | openssl req -x509 -new -nodes -key ca.key -subj "/CN=oxia-ca" -days 3650 -out ca.crt 4 | 5 | # client 6 | openssl genrsa -out client.key 2048 7 | openssl req -new -key client.key -subj "/CN=oxia-client" -out client.csr -config openssl.conf 8 | openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 3650 -extensions oxia_req -extfile openssl.conf 9 | 10 | # peer & server 11 | openssl genrsa -out peer.key 2048 12 | openssl req -new -key peer.key -subj "/CN=oxia-peer" -out peer.csr -config openssl.conf 13 | openssl x509 -req -in peer.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out peer.crt -days 3650 -extensions oxia_req -extfile openssl.conf -------------------------------------------------------------------------------- /tests/security/certs/openssl.conf: -------------------------------------------------------------------------------- 1 | req_extensions = oxia_req 2 | distinguished_name = req_distinguished_name 3 | [req_distinguished_name] 4 | [ oxia_req ] 5 | keyUsage = critical, digitalSignature, keyEncipherment 6 | extendedKeyUsage = serverAuth, clientAuth 7 | subjectAltName = @alt_names 8 | [alt_names] 9 | DNS.1 = localhost 10 | IP.1 = 127.0.0.1 11 | IP.2 = 192.168.1.2 -------------------------------------------------------------------------------- /tests/security/certs/peer.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDSTCCAjGgAwIBAgIUPrCNqByoT5qRFBXUCyW3sBo2wAAwDQYJKoZIhvcNAQEL 3 | BQAwEjEQMA4GA1UEAwwHb3hpYS1jYTAeFw0yNDAzMTcwODQ0MThaFw0zNDAzMTUw 4 | ODQ0MThaMBQxEjAQBgNVBAMMCW94aWEtcGVlcjCCASIwDQYJKoZIhvcNAQEBBQAD 5 | ggEPADCCAQoCggEBAJ+tg1qSDcARGs32be7nYVhF+yYvG6yUpnEAog3/y0axcoii 6 | hi/UtfNexdK0GVQMem4mHKPx8oiB+LMmee4AVJnfM4NjdAGXRGdUPQzyOm7t6xVu 7 | SIND0QLmuCCSU8BZ/jf8MLrUV0+sX62xhQiNuferDFi3dpscjmb9H9LFQ9YXIH2/ 8 | Q4+Ze2KxX7ff6l54x0YE1cM1SyV99dnpIk2ahYat1u16HNmtnCGfZHkWZnVPFEog 9 | NP8RLd/rb4hTJ4U8JdBs+PZZ+3lmb6ay02WMp4kxf/G6TD21sFVMqerGZm3310UP 10 | HeZDf2rOzoLaNa3mVRlTNV2nrern2JpJxyOpvHcCAwEAAaOBlDCBkTAOBgNVHQ8B 11 | Af8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMCAGA1UdEQQZ 12 | MBeCCWxvY2FsaG9zdIcEfwAAAYcEwKgBAjAdBgNVHQ4EFgQU6vHHEZfq6gSL+ugO 13 | aNmHPaXxQe0wHwYDVR0jBBgwFoAUUFeeU5NqqmhcewFYsDsMaHg/1AUwDQYJKoZI 14 | hvcNAQELBQADggEBAG2TR14AW96A5DLqAbMQZBEMgG4GeV8WWNighTdaSAo45pal 15 | n1MnzF6DXr6L3TB6ii9nd/uj2JZBlglhARL1iDB7DiH3R5QXT+Csw7cycBQZz3cz 16 | vQ/m3/OzROPITNorzbC65MaoLw1R8JfT98l63U0HajPyNpi/YH7lSVxxjUiG4fIH 17 | P78moOHwUtCmdi8FwuJ7OPE6+q5XaGfGIamzuw/FPYWiwE0ICzYHskGSmbKJdHbf 18 | 3S/DS+hsp/FUhV72+5lb3CHK6GXrjdpdEJBpuVI0zxGl+pT4UD3sNmZJx/OI8Dlf 19 | eHgJzCf2/2MgSlDuqcauVJDi+22ZzfpUzP2xxQY= 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /tests/security/certs/peer.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIICuzCCAaMCAQAwFDESMBAGA1UEAwwJb3hpYS1wZWVyMIIBIjANBgkqhkiG9w0B 3 | AQEFAAOCAQ8AMIIBCgKCAQEAn62DWpINwBEazfZt7udhWEX7Ji8brJSmcQCiDf/L 4 | RrFyiKKGL9S1817F0rQZVAx6biYco/HyiIH4syZ57gBUmd8zg2N0AZdEZ1Q9DPI6 5 | bu3rFW5Ig0PRAua4IJJTwFn+N/wwutRXT6xfrbGFCI2596sMWLd2mxyOZv0f0sVD 6 | 1hcgfb9Dj5l7YrFft9/qXnjHRgTVwzVLJX312ekiTZqFhq3W7Xoc2a2cIZ9keRZm 7 | dU8USiA0/xEt3+tviFMnhTwl0Gz49ln7eWZvprLTZYyniTF/8bpMPbWwVUyp6sZm 8 | bffXRQ8d5kN/as7Ogto1reZVGVM1Xaet6ufYmknHI6m8dwIDAQABoGIwYAYJKoZI 9 | hvcNAQkOMVMwUTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEG 10 | CCsGAQUFBwMCMCAGA1UdEQQZMBeCCWxvY2FsaG9zdIcEfwAAAYcEwKgBAjANBgkq 11 | hkiG9w0BAQsFAAOCAQEARN+pdpaGaSvgO4votNrinUpqXhKV17puNeb5A49dsvVD 12 | AdcjN28b61VjnlZKm2ZCE4a4lyyR0ndOGNXHj/BWntQnuzIVyPYUn2LNAuKKcCq7 13 | R1fd4fyPAudnYr45t9CILNPeaNAWfTYPvpdDuVkTeOEJeIHt9PiEDloiH7+YkIxq 14 | o6y91e5ajE4UuJLHkB+L4ktNZVFkrrRkVVMsoq9EzFJv+v7wm89usUFdvbQyLkWi 15 | y6qmN6gf9epfx3uiQ4zsq1ujlVhBMFU9sbHQdP8iLbhFujsstxZQCA7TnJ3EdYHf 16 | +P6YEdPCYGmzmy4fErFSzt3DXl7VHO8SJbCTV/67FQ== 17 | -----END CERTIFICATE REQUEST----- 18 | -------------------------------------------------------------------------------- /tests/security/certs/peer.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCfrYNakg3AERrN 3 | 9m3u52FYRfsmLxuslKZxAKIN/8tGsXKIooYv1LXzXsXStBlUDHpuJhyj8fKIgfiz 4 | JnnuAFSZ3zODY3QBl0RnVD0M8jpu7esVbkiDQ9EC5rggklPAWf43/DC61FdPrF+t 5 | sYUIjbn3qwxYt3abHI5m/R/SxUPWFyB9v0OPmXtisV+33+peeMdGBNXDNUslffXZ 6 | 6SJNmoWGrdbtehzZrZwhn2R5FmZ1TxRKIDT/ES3f62+IUyeFPCXQbPj2Wft5Zm+m 7 | stNljKeJMX/xukw9tbBVTKnqxmZt99dFDx3mQ39qzs6C2jWt5lUZUzVdp63q59ia 8 | Sccjqbx3AgMBAAECggEAGkEFuWUNRLirPxUXEzJRA5oSBr+1HaWdTjQr8QBPVHLF 9 | oaiTRpcDSZ4aCaBaD8MzeBvlFqGn3oD8OryHTLYaMuiLiD1YbBCKizDDDvde2zFc 10 | NzxYSNjkK0VzHcvhYF2LmnnpKzbRmkR9wMnQ1Uy0OIATuhyEMFG8KjvROS79Ddyi 11 | Ae16ozt+SO0bS8IUraDJaufdI7VCk8z8k1cNGYQk6TQgy+Ir+jxiQ9grEr7go5hW 12 | Bw1hFYRLrjKmQMFCCnNQzfcLyBPV2v0cDasHfQ6nk8DbaScYM+cCxzNCezW2bKpr 13 | +wbvBONWi7MBscD7Gw1IlXRipwZgVF3/qe9pCopkXQKBgQDVehtvy2K4DFL4/jZc 14 | EqEd2++3vnHvjO/XxRmzJiA4CYet4YMw+AXDV3qMlftHK+/lfCxjHuAd5Be8oQ9o 15 | XdT+AKSIwU4jExSVGOtOHRVysOxjECANDgc/AsGb1O+TRTVk9q5BtoqfiguwdnCg 16 | nAaH4m4hepnXbSF/dktyaj2bewKBgQC/fAIVfKEQ5V5KAWnHs5e6n0fr+7/e2u6u 17 | 5lx18oEfc0PVZV15cXzJQjRb5fVBXhbKXzVmQZzPckM65OaG1+zhh66kwY7MfUtO 18 | uM1rsa9FavyQqecYHfD4tkPKL9NLLHIVPKtKo8UBHS0vii5VjZg0con9Dtm6oTad 19 | LxYmBJ3kNQKBgHF1xL9WSdGoOzE7a4jkHd3Q10fBYK0BKzhy+YXN7bHa3tH72c5d 20 | BDMee3PdAwCAybAenzjPYC6C3FrU/2to85tMnCZTF0RtD7Nu2yFZM90RS5IbL+3A 21 | VvmWbXB1bB/J2OcgdN1YN7UzmaLgUFwMkSwHnKEOmFV3GyazqMXW0YehAoGAcT3/ 22 | ZYGtJ3beilnHmyjk5KNqP29FC6DiY2Es8TwXjOf4B+3ImBe7urKCkI6wupXQWKJx 23 | zCWlfTGP/PZ+NuTf3IkHibxGnTzEGqxEIBqXEzCwaUVxu9uJNS1lbd9W0DBaz6Bv 24 | lNbBnWUJ4gGQYVw51VqfrJ7puOu6hr7gFF/KmLkCgYAzXUXkSQ3Ehig7Ni1pGZk5 25 | CY5k/BnO1Ydy9ppA/wqKP25wrdVpSenTuBhcFMrLqLp6UTi9QNX3JObAXfRXqsi1 26 | faqEYgNwQL6+BnbfuXs4ym4OFkUZLg7LYeKO0rM4hStk46Y8fMbhv63sW7i17/kp 27 | mwPW7W6vI0hfvRm2cN+vng== 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /tests/sequence/sequence_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 StreamNative, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package sequence 16 | 17 | import ( 18 | "fmt" 19 | "testing" 20 | 21 | "github.com/stretchr/testify/assert" 22 | 23 | "github.com/streamnative/oxia/oxia" 24 | "github.com/streamnative/oxia/server" 25 | ) 26 | 27 | func TestSequence_WithOtherKeyInBatch(t *testing.T) { 28 | config := server.NewTestConfig(t.TempDir()) 29 | standaloneServer, err := server.NewStandalone(config) 30 | assert.NoError(t, err) 31 | defer standaloneServer.Close() 32 | 33 | client, err := oxia.NewAsyncClient(fmt.Sprintf("localhost:%d", standaloneServer.RpcPort())) 34 | assert.NoError(t, err) 35 | defer client.Close() 36 | 37 | for i := 1; i <= 3; i++ { 38 | if i == 2 { 39 | _ = client.Put("/abc", []byte("0"), 40 | oxia.PartitionKey("x")) 41 | } 42 | 43 | ch := client.Put("x", []byte("0"), oxia.PartitionKey("x"), oxia.SequenceKeysDeltas(1)) 44 | pr := <-ch 45 | assert.NoError(t, pr.Err) 46 | assert.Equal(t, fmt.Sprintf("x-0000000000000000000%d", i), pr.Key) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tlaplus/.gitignore: -------------------------------------------------------------------------------- 1 | .tools 2 | states 3 | *_TTrace_*.tla 4 | -------------------------------------------------------------------------------- /tlaplus/OxiaReplication.cfg: -------------------------------------------------------------------------------- 1 | CONSTANTS 2 | Coordinators = {coordinator1, coordinator2} 3 | Nodes = {node1, node2, node3, node4} 4 | Values = {v0, v1, v2, v3, v4} 5 | RepFactor = 3 6 | 7 | CONSTANTS 8 | MaxTerms = 4 9 | MaxCoordinatorStops = 3 10 | 11 | CONSTANTS 12 | LEADER_ELECTED = LEADER_ELECTED 13 | LEADER = LEADER 14 | PENDING_TRUNCATE = PENDING_TRUNCATE 15 | PENDING_REMOVAL = PENDING_REMOVAL 16 | STEADY_STATE = STEADY_STATE 17 | INVALID_TERM = INVALID_TERM 18 | APPEND = APPEND 19 | ACK = ACK 20 | FENCED = FENCED 21 | NEW_TERM_REQUEST = NEW_TERM_REQUEST 22 | NOT_RUNNING = NOT_RUNNING 23 | NOTIFY_LEADER = NOTIFY_LEADER 24 | ADD_FOLLOWER_REQUEST = ADD_FOLLOWER_REQUEST 25 | ATTACHED = ATTACHED 26 | RUNNING = RUNNING 27 | TRUNCATE_REQUEST = TRUNCATE_REQUEST 28 | BECOME_LEADER_REQUEST = BECOME_LEADER_REQUEST 29 | BECOME_LEADER_RESPONSE = BECOME_LEADER_RESPONSE 30 | NIL = NIL 31 | TRUNCATE_RESPONSE = TRUNCATE_RESPONSE 32 | NOT_MEMBER = NOT_MEMBER 33 | FENCING = FENCING 34 | ELECTION = ELECTION 35 | NEW_TERM_RESPONSE = NEW_TERM_RESPONSE 36 | FOLLOWER = FOLLOWER 37 | INSTANCE = INSTANCE 38 | OK = OK 39 | 40 | SPECIFICATION LivenessSpec 41 | 42 | INVARIANTS 43 | NoLogDivergence 44 | NoLossOfConfirmedWrite 45 | ValidMessages 46 | LegalLeaderAndEnsemble 47 | --------------------------------------------------------------------------------