Repository: scylladb/gocql Branch: master Commit: ebb024992fe2 Files: 375 Total size: 2.8 MB Directory structure: gitextract_l7q0abss/ ├── .git-blame-ignore-revs ├── .github/ │ ├── dependabot.yml │ ├── issue_template.md │ └── workflows/ │ ├── bench-tests.yml │ ├── call_jira_sync.yml │ ├── clean_dockerhub_images.yml │ ├── docs-pages.yml │ ├── docs-pr.yml │ ├── extended-ci-longevity-large-partitions-with-network-nemesis-1h-test.yml │ └── main.yml ├── .gitignore ├── .golangci.yml ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── NOTICE ├── README.md ├── address_translators.go ├── address_translators_test.go ├── batch_test.go ├── callreq_wait.go ├── callreq_wait_race.go ├── cass1batch_test.go ├── cassandra_test.go ├── ci/ │ └── clean-old-temporary-docker-images.py ├── client_routes.go ├── client_routes_test.go ├── client_routes_unit_test.go ├── cloud_cluster_test.go ├── cluster.go ├── cluster_test.go ├── common_test.go ├── compressor.go ├── compressor_test.go ├── conn.go ├── conn_test.go ├── connectionpool.go ├── connectionpool_test.go ├── connpicker.go ├── control.go ├── control_integration_test.go ├── control_test.go ├── cqltypes.go ├── debounce/ │ ├── refresh_deboucer.go │ ├── refresh_debouncer_test.go │ ├── simple_debouncer.go │ └── simple_debouncer_test.go ├── dial.go ├── dialer/ │ ├── recorder/ │ │ └── recorder.go │ ├── replayer/ │ │ └── replayer.go │ └── utils.go ├── dns_test.go ├── doc.go ├── docs/ │ ├── Makefile │ ├── _utils/ │ │ └── redirects.yaml │ ├── pyproject.toml │ └── source/ │ ├── conf.py │ ├── index.rst │ └── sample-page.rst ├── errors.go ├── errors_test.go ├── events/ │ ├── event_converter.go │ ├── event_converter_test.go │ ├── events.go │ └── events_test.go ├── events.go ├── events_test.go ├── events_unit_test.go ├── example_batch_test.go ├── example_dynamic_columns_test.go ├── example_lwt_batch_test.go ├── example_lwt_test.go ├── example_marshaler_test.go ├── example_nulls_test.go ├── example_paging_test.go ├── example_set_test.go ├── example_test.go ├── example_udt_map_test.go ├── example_udt_marshaler_test.go ├── example_udt_struct_test.go ├── example_udt_unmarshaler_test.go ├── exec.go ├── exec_test.go ├── export_test.go ├── filters.go ├── filters_test.go ├── frame.go ├── frame_test.go ├── framer.go ├── framer_bench_test.go ├── go.mod ├── go.sum ├── helpers.go ├── helpers_bench_test.go ├── host_source.go ├── host_source_scylla.go ├── host_source_test.go ├── hostpolicy/ │ ├── hostpool.go │ └── hostpool_test.go ├── install_test_deps.sh ├── integration.sh ├── integration_only.go ├── integration_serialization_scylla_test.go ├── integration_test.go ├── internal/ │ ├── ccm/ │ │ ├── ccm.go │ │ └── ccm_test.go │ ├── debug/ │ │ ├── debug_off.go │ │ └── debug_on.go │ ├── eventbus/ │ │ ├── README.md │ │ ├── eventbus.go │ │ ├── eventbus_test.go │ │ └── example_test.go │ ├── frame/ │ │ └── frames.go │ ├── lru/ │ │ ├── lru.go │ │ └── lru_test.go │ ├── murmur/ │ │ ├── murmur.go │ │ ├── murmur_appengine.go │ │ ├── murmur_test.go │ │ └── murmur_unsafe.go │ ├── streams/ │ │ ├── streams.go │ │ └── streams_test.go │ └── tests/ │ ├── common.go │ ├── err_equal.go │ ├── mock/ │ │ └── mock_framer.go │ ├── rand.go │ └── serialization/ │ ├── mod/ │ │ ├── all.go │ │ ├── custom.go │ │ ├── custom_refs.go │ │ └── refs.go │ ├── pointers.go │ ├── pointers_test.go │ ├── set_negative_marshal.go │ ├── set_negative_unmarshal.go │ ├── set_positive.go │ ├── utils.go │ ├── utils_equal.go │ ├── utils_error.go │ ├── utils_new.go │ ├── utils_str.go │ └── valcases/ │ ├── get.go │ └── simple.go ├── keyspace_table_test.go ├── logger.go ├── lz4/ │ ├── go.mod │ ├── go.sum │ ├── lz4.go │ ├── lz4_bench_test.go │ └── lz4_test.go ├── marshal.go ├── marshal_test.go ├── metadata_scylla.go ├── metadata_scylla_test.go ├── policies.go ├── policies_bench_test.go ├── policies_integration_test.go ├── policies_test.go ├── prepared_cache.go ├── query_error_test.go ├── query_executor.go ├── recreate.go ├── recreate_test.go ├── renovate.json ├── ring_describer.go ├── ring_describer_test.go ├── schema_queries_test.go ├── scylla.go ├── scylla_cdc.go ├── scylla_shard_aware_port_common_test.go ├── scylla_shard_aware_port_integration_test.go ├── scylla_shard_aware_port_mocked_test.go ├── scylla_test.go ├── scylla_tokens_test.go ├── scyllacloud/ │ ├── cluster.go │ ├── config.go │ ├── config_test.go │ ├── hostdialer.go │ └── hostdialer_test.go ├── serialization/ │ ├── ascii/ │ │ ├── marshal.go │ │ ├── marshal_utils.go │ │ ├── unmarshal.go │ │ └── unmarshal_utils.go │ ├── bigint/ │ │ ├── marshal.go │ │ ├── marshal_utils.go │ │ ├── unmarshal.go │ │ └── unmarshal_utils.go │ ├── blob/ │ │ ├── marshal.go │ │ ├── marshal_utils.go │ │ ├── unmarshal.go │ │ ├── unmarshal_utils.go │ │ └── unmarshal_utils_test.go │ ├── boolean/ │ │ ├── marshal.go │ │ ├── marshal_utils.go │ │ ├── unmarshal.go │ │ └── unmarshal_utils.go │ ├── counter/ │ │ ├── marshal.go │ │ ├── marshal_utils.go │ │ ├── unmarshal.go │ │ └── unmarshal_utils.go │ ├── cqlint/ │ │ ├── marshal.go │ │ ├── marshal_utils.go │ │ ├── unmarshal.go │ │ └── unmarshal_utils.go │ ├── cqltime/ │ │ ├── marshal.go │ │ ├── marshal_utils.go │ │ ├── unmarshal.go │ │ └── unmarshal_utils.go │ ├── date/ │ │ ├── marshal.go │ │ ├── marshal_utils.go │ │ ├── unmarshal.go │ │ └── unmarshal_utils.go │ ├── decimal/ │ │ ├── marshal.go │ │ ├── marshal_utils.go │ │ ├── unmarshal.go │ │ ├── unmarshal_ints.go │ │ └── unmarshal_utils.go │ ├── double/ │ │ ├── marshal.go │ │ ├── marshal_utils.go │ │ ├── unmarshal.go │ │ └── unmarshal_utils.go │ ├── duration/ │ │ ├── duration.go │ │ ├── marshal.go │ │ ├── marshal_str.go │ │ ├── marshal_str_test.go │ │ ├── marshal_utils.go │ │ ├── marshal_vint_test.go │ │ ├── unmarshal.go │ │ ├── unmarshal_str.go │ │ ├── unmarshal_str_test.go │ │ ├── unmarshal_utils.go │ │ └── unmarshal_vint_test.go │ ├── float/ │ │ ├── marshal.go │ │ ├── marshal_utils.go │ │ ├── unmarshal.go │ │ └── unmarshal_utils.go │ ├── inet/ │ │ ├── marshal.go │ │ ├── marshal_utils.go │ │ ├── unmarshal.go │ │ └── unmarshal_utils.go │ ├── smallint/ │ │ ├── marshal.go │ │ ├── marshal_utils.go │ │ ├── unmarshal.go │ │ └── unmarshal_utils.go │ ├── text/ │ │ ├── marshal.go │ │ ├── marshal_utils.go │ │ ├── unmarshal.go │ │ └── unmarshal_utils.go │ ├── timestamp/ │ │ ├── marshal.go │ │ ├── marshal_utils.go │ │ ├── unmarshal.go │ │ └── unmarshal_utils.go │ ├── timeuuid/ │ │ ├── marshal.go │ │ ├── marshal_utils.go │ │ ├── unmarshal.go │ │ └── unmarshal_utils.go │ ├── tinyint/ │ │ ├── marshal.go │ │ ├── marshal_utils.go │ │ ├── unmarshal.go │ │ └── unmarshal_utils.go │ ├── uuid/ │ │ ├── marshal.go │ │ ├── marshal_utils.go │ │ ├── unmarshal.go │ │ └── unmarshal_utils.go │ ├── varchar/ │ │ ├── marshal.go │ │ ├── marshal_utils.go │ │ ├── unmarshal.go │ │ └── unmarshal_utils.go │ └── varint/ │ ├── marshal.go │ ├── marshal_bigint_test.go │ ├── marshal_custom.go │ ├── marshal_ints.go │ ├── marshal_uints.go │ ├── marshal_utils.go │ ├── unmarshal.go │ ├── unmarshal_bigint_test.go │ ├── unmarshal_custom.go │ ├── unmarshal_ints.go │ ├── unmarshal_uints.go │ └── unmarshal_utils.go ├── session.go ├── session_connect_test.go ├── session_event_bus_integration_test.go ├── session_event_bus_test.go ├── session_test.go ├── session_unit_test.go ├── stress_test.go ├── tablet_integration_test.go ├── tablets/ │ ├── cow_tablet_list_test.go │ ├── tabets_utils_test.go │ ├── tablet_utils.go │ ├── tablets.go │ ├── tablets_bench_test.go │ └── tablets_test.go ├── testdata/ │ ├── pki/ │ │ ├── ca.cnf │ │ ├── cassandra.cnf │ │ ├── generate_certs.sh │ │ └── gocql.cnf │ └── recreate/ │ ├── aggregates.cql │ ├── aggregates_golden.cql │ ├── index.cql │ ├── index_golden.cql │ ├── keyspace.cql │ ├── keyspace_golden.cql │ ├── materialized_views.cql │ ├── materialized_views_golden.cql │ ├── scylla_encryption_options_golden.json │ ├── secondary_index.cql │ ├── secondary_index_golden.cql │ ├── table.cql │ ├── table_golden.cql │ ├── udt.cql │ └── udt_golden.cql ├── tests/ │ ├── bench/ │ │ ├── bench_marshal_test.go │ │ ├── bench_single_conn_test.go │ │ ├── bench_vector_public_test.go │ │ ├── go.mod │ │ ├── go.sum │ │ ├── rec_insert/ │ │ │ ├── 192.168.100.11:9042-0Reads │ │ │ └── 192.168.100.11:9042-0Writes │ │ └── rec_select/ │ │ ├── 192.168.100.11:9042-0Reads │ │ └── 192.168.100.11:9042-0Writes │ └── serialization/ │ ├── marshal_0_unset_test.go │ ├── marshal_10_decimal_corrupt_test.go │ ├── marshal_10_decimal_test.go │ ├── marshal_11_texts_test.go │ ├── marshal_12_ascii_corrupt_test.go │ ├── marshal_12_ascii_test.go │ ├── marshal_13_uuids_corrupt_test.go │ ├── marshal_13_uuids_test.go │ ├── marshal_14_inet_corrupt_test.go │ ├── marshal_14_inet_test.go │ ├── marshal_15_time_corrupt_test.go │ ├── marshal_15_time_test.go │ ├── marshal_16_timestamp_corrupt_test.go │ ├── marshal_16_timestamp_test.go │ ├── marshal_17_date_corrupt_test.go │ ├── marshal_17_date_test.go │ ├── marshal_18_duration_corrupt_test.go │ ├── marshal_18_duration_test.go │ ├── marshal_19_list_set_v3_corrupt_test.go │ ├── marshal_19_list_set_v3_test.go │ ├── marshal_1_boolean_corrupt_test.go │ ├── marshal_1_boolean_test.go │ ├── marshal_20_map_v3_corrupt_test.go │ ├── marshal_20_map_v3_test.go │ ├── marshal_2_tinyint_corrupt_test.go │ ├── marshal_2_tinyint_test.go │ ├── marshal_3_smallint_corrupt_test.go │ ├── marshal_3_smallint_test.go │ ├── marshal_4_int_corrupt_test.go │ ├── marshal_4_int_test.go │ ├── marshal_5_bigint_corrupt_test.go │ ├── marshal_5_bigint_test.go │ ├── marshal_6_counter_corrupt_test.go │ ├── marshal_6_counter_test.go │ ├── marshal_7_varint_corrupt_test.go │ ├── marshal_7_varint_test.go │ ├── marshal_8_float_corrupt_test.go │ ├── marshal_8_float_test.go │ ├── marshal_9_double_corrupt_test.go │ └── marshal_9_duble_test.go ├── token.go ├── token_test.go ├── topology.go ├── topology_test.go ├── tracer.go ├── tracer_test.go ├── tuple_test.go ├── udt_test.go ├── uuid.go ├── uuid_test.go ├── vector_bench_test.go ├── vector_test.go ├── version.go ├── warning_handler.go └── wiki_test.go ================================================ FILE CONTENTS ================================================ ================================================ FILE: .git-blame-ignore-revs ================================================ # Commit that replaced interface{} with any across the codebase. # Purely mechanical rename with no behavioral change. 3ab64422067e24efb0b6b30eea0396d0e9395aee ================================================ FILE: .github/dependabot.yml ================================================ version: 2 updates: - package-ecosystem: "uv" directory: "/docs" schedule: interval: "daily" allow: - dependency-name: "sphinx-scylladb-theme" - dependency-name: "sphinx-multiversion-scylla" ================================================ FILE: .github/issue_template.md ================================================ Please answer these questions before submitting your issue. Thanks! ### What version of ScyllaDB or Cassandra are you using? ### What version of ScyllaDB Gocql driver are you using? ### What version of Go are you using? ### What did you do? ### What did you expect to see? ### What did you see instead? --- If you are having connectivity related issues please share the following additional information ### Describe your Cassandra cluster please provide the following information - output of `nodetool status` - output of `SELECT peer, rpc_address FROM system.peers` - rebuild your application with the `gocql_debug` tag and post the output ================================================ FILE: .github/workflows/bench-tests.yml ================================================ name: Run benchmark tests on: push: branches: - master pull_request: types: [opened, synchronize, reopened] jobs: bench-tests: if: contains(github.event.pull_request.labels.*.name, 'run-benchmark-tests') name: Run benchmark tests runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version-file: go.mod cache-dependency-path: | lz4/go.sum tests/bench/go.sum go.sum - name: Run benchmark tests run: make test-bench ================================================ FILE: .github/workflows/call_jira_sync.yml ================================================ name: Sync Jira Based on PR Events on: pull_request_target: types: [opened, edited, ready_for_review, review_requested, labeled, unlabeled, closed] permissions: contents: read pull-requests: write issues: write jobs: jira-sync: uses: scylladb/github-automation/.github/workflows/main_pr_events_jira_sync.yml@main with: caller_action: ${{ github.event.action }} secrets: caller_jira_auth: ${{ secrets.USER_AND_KEY_FOR_JIRA_AUTOMATION }} ================================================ FILE: .github/workflows/clean_dockerhub_images.yml ================================================ name: Docker Cleanup on: schedule: - cron: '0 12 * * 1' # Runs every Monday at noon (UTC) workflow_dispatch: jobs: cleanup: runs-on: ubuntu-latest steps: - name: Check out the repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 with: python-version: "3.x" - name: Install dependencies run: | python -m pip install --upgrade pip pip install requests - name: Run Docker image cleanup run: make clean-old-temporary-docker-images env: DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} DELETE_AFTER_DAYS: 30 ================================================ FILE: .github/workflows/docs-pages.yml ================================================ name: "Docs / Publish" # For more information, # see https://sphinx-theme.scylladb.com/stable/deployment/production.html#available-workflows on: push: branches: - master - 'branch-**' paths: - "docs/**" workflow_dispatch: jobs: build: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: ref: ${{ github.event.repository.default_branch }} persist-credentials: false fetch-depth: 0 - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 # actions/setup-python@v6 poetry cache feature requires poetry to be installed beforehand # which makes use of it extremely awkward. with: path: | /home/runner/.cache/pip /home/runner/.cache/uv key: docs-cache-${{ runner.os }}-${{ hashFiles('docs/pyproject.toml', 'docs/Makefile') }} - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 with: python-version: '3.12' - name: Install uv uses: astral-sh/setup-uv@v6 - name: Set up env run: make -C docs setupenv - name: Build docs run: make -C docs multiversion - name: Build redirects run: make -C docs redirects - name: Tar folder run: | tar \ --dereference --hard-dereference \ --directory docs/_build/dirhtml/ \ -cvf ${{ runner.temp }}/artifact.tar \ . - name: Upload artifact uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: name: github-pages path: ${{ runner.temp }}/artifact.tar retention-days: "1" release: # Add a dependency to the build job needs: build # Grant GITHUB_TOKEN the permissions required to make a Pages deployment permissions: pages: write # to deploy to Pages id-token: write # to verify the deployment originates from an appropriate source contents: read # to read private repo # Deploy to the github-pages environment environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} # Specify runner + deployment step runs-on: ubuntu-latest steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5 ================================================ FILE: .github/workflows/docs-pr.yml ================================================ name: "Docs / Build PR" # For more information, # see https://sphinx-theme.scylladb.com/stable/deployment/production.html#available-workflows on: push: paths: - "docs/**" - ".github/workflows/docs-pr.yml" pull_request: types: [opened, synchronize, reopened] paths: - "docs/**" - ".github/workflows/docs-pr.yml" workflow_dispatch: permissions: contents: read jobs: build: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: persist-credentials: false fetch-depth: 0 - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 # actions/setup-python@v6 poetry cache feature requires poetry to be installed beforehand # which makes use of it extremely awkward. with: path: | /home/runner/.cache/pip /home/runner/.cache/uv key: docs-cache-${{ runner.os }}-${{ hashFiles('docs/pyproject.toml', 'docs/Makefile') }} - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 with: python-version: '3.12' - name: Install uv uses: astral-sh/setup-uv@v6 - name: Set up env run: make -C docs setupenv - name: Build docs run: make -C docs test ================================================ FILE: .github/workflows/extended-ci-longevity-large-partitions-with-network-nemesis-1h-test.yml ================================================ name: Build scylla-bench docker image with gocql PR on: pull_request_target: types: [labeled] jobs: trigger-longevity-large-partitions-with-network-nemesis-1h-test: if: contains(github.event.pull_request.labels.*.name, 'extended-ci') runs-on: ubuntu-latest strategy: matrix: scylla-version: [ENTERPRISE-RELEASE, OSS-RELEASE] steps: - name: Login to Docker Hub uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Check out the scylla-bench repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: repository: scylladb/scylla-bench path: scylla-bench - name: Checkout GoCQL PR Repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 with: repository: ${{ github.event.pull_request.head.repo.full_name }} ref: ${{ github.event.pull_request.head.sha }} path: gocql - name: Build and push Scylla-bench Docker Image with gocql from PR run: | cd scylla-bench GOCQL_REPO="github.com/${{ github.event.pull_request.head.repo.full_name }}" GOCQL_VERSION="${{ github.event.pull_request.head.sha }}" make build-with-custom-gocql-version DOCKER_IMAGE_TAG="scylladb/gocql-extended-ci:scylla-bench-${{ github.event.pull_request.head.sha }}" DOCKER_IMAGE_LABELS="com.scylladb.gocql-version=${{ github.event.pull_request.head.sha }}" make build-sct-docker-image docker push "scylladb/gocql-extended-ci:scylla-bench-${{ github.event.pull_request.head.sha }}" - name: Install get-version CLI run: | git clone https://github.com/scylladb-actions/get-version.git cd get-version go mod tidy go build -o get-version - name: Get scylla version id: scylla-version run: | cd get-version if [[ "${{ matrix.scylla-version }}" == "ENTERPRISE-RELEASE" ]]; then echo "value=$(./get-version --source dockerhub-imagetag --repo scylladb/scylla-enterprise -filters "^[0-9]{4}$.^[0-9]+$.^[0-9]+$ and LAST.LAST.LAST" | tr -d '\"')" >> $GITHUB_ENV elif [[ "${{ matrix.scylla-version }}" == "OSS-RELEASE" ]]; then echo "value=$(./get-version --source dockerhub-imagetag --repo scylladb/scylla -filters "^[0-9]$.^[0-9]+$.^[0-9]+$ and LAST.LAST.LAST" | tr -d '\"')" >> $GITHUB_ENV elif echo "${{ matrix.scylla-version }}" | grep -P '^[0-9\.]+'; then # If you want to run specific version do just that echo "value=${{ matrix.scylla-version }}" | tee -a $GITHUB_OUTPUT else echo "Unknown scylla version name `${{ matrix.scylla-version }}`" exit 1 fi - name: Start Jenkins job uses: scylladb-actions/jenkins-client@b947e07e8b588db2a8028313274992d3eda73360 # v0.2.0 with: job_name: scylla-drivers/job/gocql/job/extended-ci/job/longevity-large-partitions-with-network-nemesis-1h-test job_parameters: '{"email_recipients": "scylla-drivers@scylladb.com", "scylla_version": "${{ steps.scylla-version.outputs.value }}", "extra_environment_variables": "SCT_STRESS_IMAGE.scylla-bench=scylladb/gocql-extended-ci:scylla-bench-${{ github.event.pull_request.head.sha }}"}' base_url: https://jenkins.scylladb.com user: ${{ secrets.JENKINS_USERNAME }} password: ${{ secrets.JENKINS_TOKEN }} wait_timeout: 3h polling_interval: 1s ================================================ FILE: .github/workflows/main.yml ================================================ name: Build on: push: branches: - master paths-ignore: - "*.md" - "docs/**" - .github/workflows/docs-* - .github/workflows/bench-tests.yml - .github/workflows/extended-ci-longevity-large-partitions-with-network-nemesis-1h-test.yml pull_request: types: [opened, synchronize, reopened] paths-ignore: - "*.md" - "docs/**" - .github/workflows/docs-* - .github/workflows/bench-tests.yml - .github/workflows/extended-ci-longevity-large-partitions-with-network-nemesis-1h-test.yml jobs: build: name: Build runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 with: path: | /home/runner/.cache/pip /home/runner/.local /home/runner/.ccm/scylla-repository /home/runner/.ccm/repository /home/runner/.sdkman testdata/pki bin/ # CCM, scylla, cassandra and java versions are in Makefile key: pr-check-${{ runner.os }}-${{ hashFiles('Makefile') }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version-file: go.mod cache-dependency-path: | lz4/go.sum tests/bench/go.sum go.sum - name: Run linters run: make check - name: Run unit tests run: make test-unit - run: sudo sh -c "echo 2097152 >> /proc/sys/fs/aio-max-nr" test-integration-scylla: name: Integration Tests On Scylla runs-on: ubuntu-latest needs: build strategy: matrix: scylla-version: [LATEST, PRIOR, LTS-LATEST, LTS-PRIOR] fail-fast: false env: SCYLLA_VERSION: ${{ matrix.scylla-version }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 with: python-version: '3.11' - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 with: path: | /home/runner/.cache/pip /home/runner/.local /home/runner/.sdkman testdata/pki bin/ # CCM, pip and java versions are in Makefile key: pr-check-${{ runner.os }}-${{ hashFiles('Makefile') }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version-file: go.mod cache-dependency-path: | lz4/go.sum tests/bench/go.sum go.sum - name: Get scylla version id: scylla-version run: make resolve-scylla-version - name: Pull CCM image from the cache uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 if: steps.scylla-version.outputs.value != 'NOT-FOUND' id: ccm-cache with: path: ~/.ccm/repository key: ccm-scylla-${{ runner.os }}-${{ steps.scylla-version.outputs.value }} - name: Download ScyllaDB (${{ steps.scylla-version.outputs.value }}) image if: steps.ccm-cache.outputs.cache-hit != 'true' && steps.scylla-version.outputs.value != 'NOT-FOUND' run: make download-scylla - name: Save CCM ScyllaDB image into the cache if: steps.ccm-cache.outputs.cache-hit != 'true' && steps.scylla-version.outputs.value != 'NOT-FOUND' uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 with: path: ~/.ccm/repository key: ccm-scylla-${{ runner.os }}-${{ steps.scylla-version.outputs.value }} - name: Run integration suite with ScyllaDB ${{ matrix.scylla-version }}(${{ steps.scylla-version.outputs.value }}) if: steps.scylla-version.outputs.value != 'NOT-FOUND' run: make test-integration-scylla - name: Run CCM integration suite with ScyllaDB ${{ matrix.scylla-version }}(${{ steps.scylla-version.outputs.value }}) if: steps.scylla-version.outputs.value != 'NOT-FOUND' run: TEST_INTEGRATION_TAGS="ccm gocql_debug" make test-integration-scylla test-integration-cassandra: name: Integration Tests On Cassandra runs-on: ubuntu-latest needs: build strategy: matrix: cassandra-version: [5-LATEST, 4-LATEST] fail-fast: false env: CASSANDRA_VERSION: ${{ matrix.cassandra-version }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 with: python-version: '3.11' - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 with: path: | /home/runner/.cache/pip /home/runner/.local /home/runner/.sdkman testdata/pki bin/ # CCM, python and java versions are in Makefile key: pr-check-${{ runner.os }}-${{ hashFiles('Makefile') }} - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version-file: go.mod cache-dependency-path: | lz4/go.sum tests/bench/go.sum go.sum - name: Get cassandra version id: cassandra-version run: make resolve-cassandra-version - name: Pull CCM image from the cache uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 if: steps.cassandra-version.outputs.value != 'NOT-FOUND' id: ccm-cache with: path: ~/.ccm/repository key: ccm-cassandra-${{ runner.os }}-${{ steps.cassandra-version.outputs.value }} - name: Download Cassandra (${{ steps.cassandra-version.outputs.value }}) image if: steps.ccm-cache.outputs.cache-hit != 'true' && steps.cassandra-version.outputs.value != 'NOT-FOUND' run: make download-cassandra - name: Save CCM Cassandra image into the cache if: steps.ccm-cache.outputs.cache-hit != 'true' && steps.cassandra-version.outputs.value != 'NOT-FOUND' uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5 with: path: ~/.ccm/repository key: ccm-cassandra-${{ runner.os }}-${{ steps.cassandra-version.outputs.value }} - name: Run integration suite with Cassandra ${{ matrix.cassandra-version }}(${{ steps.cassandra-version.outputs.value }}) if: steps.cassandra-version.outputs.value != 'NOT-FOUND' run: make test-integration-cassandra - name: Run CCM integration suite with Cassandra ${{ matrix.cassandra-version }}(${{ steps.cassandra-version.outputs.value }}) if: steps.cassandra-version.outputs.value != 'NOT-FOUND' run: TEST_INTEGRATION_TAGS="ccm gocql_debug" make test-integration-cassandra ================================================ FILE: .gitignore ================================================ gocql-fuzz fuzz-corpus fuzz-work gocql.test .idea bin/ testdata/pki/.keystore testdata/pki/.truststore testdata/pki/*.crt testdata/pki/*.key testdata/pki/*.p12 docs/_build/ docs/source/.doctrees TODO*.md # Codex - AI assistant metadata .codex .codex/ .codex-cache/ .codex-config.json .codex-settings.json codex.log AGENTS.md # Claude - AI assistant metadata .anthropic/ .claude/ claude.log claude_history.json claude_config.json CLAUDE.md ================================================ FILE: .golangci.yml ================================================ version: "2" formatters: enable: - goimports settings: goimports: local-prefixes: - github.com/gocql/gocql - github.com/apache/cassandra-gocql-driver - github.com/apache/cassandra-gocql-driver/v2 golines: max-len: 120 linters: exclusions: rules: - path: '(.+)_test\.go' text: "fieldalignment" linters: - govet default: none enable: - nolintlint - govet settings: govet: enable-all: true disable: - shadow nolintlint: allow-no-explanation: [ golines ] require-explanation: true require-specific: true run: build-tags: - integration - unit ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing to the ScyllaDB GoCQL Driver **TL;DR** - this manifesto sets out the bare minimum requirements for submitting a patch to gocql. This guide outlines the process of landing patches in gocql and the general approach to maintaining the code base. ## Background The goal of the gocql project is to provide a stable and robust CQL driver for Go. This is a community driven project that is coordinated by a small team of core developers. ## Minimum Requirement Checklist The following is a check list of requirements that need to be satisfied in order for us to merge your patch: * You should raise a pull request to scylladb/gocql on Github * The pull request has a title that clearly summarizes the purpose of the patch * The motivation behind the patch is clearly defined in the pull request summary * You agree that your contribution is donated to the Apache Software Foundation (appropriate copyright is on all new files) * The patch will merge cleanly * The test coverage does not fall * The merge commit passes the regression test suite on GitHub Actions * `go fmt` has been applied to the submitted code * A correctly formatted commit message, see below * Notable changes (i.e. new features or changed behavior, bugfixes) are appropriately documented in CHANGELOG.md, functional changes also in godoc If there are any requirements that can't be reasonably satisfied, please state this either on the pull request or as part of discussion on the mailing list. Where appropriate, the core team may apply discretion and make an exception to these requirements. ## Commit Message The commit message format should be: ``` Patch by ; reviewed by for ##### ``` Short description should: * Be a short sentence. * Start with a capital letter. * Be written in the present tense. * Summarize what is changed, not why it is changed. Short description should not: * End with a period. * Use the word Fixes . Most commits fix something. Long description / Reason: * Should describe why the change is needed. What is fixed by the change? Why it it was broken before? What use case does the new feature solve? * Consider adding details of other options that you considered when implementing the change and why you made the design decisions you made. ## Beyond The Checklist In addition to stating the hard requirements, there are a bunch of things that we consider when assessing changes to the library. These soft requirements are helpful pointers of how to get a patch landed quicker and with less fuss. ### General QA Approach The Scylla project needs to consider the ongoing maintainability of the library at all times. Patches that look like they will introduce maintenance issues for the team will not be accepted. Your patch will get merged quicker if you have decent test cases that provide test coverage for the new behavior you wish to introduce. Unit tests are good, integration tests are even better. An example of a unit test is `marshal_test.go` - this tests the serialization code in isolation. `cassandra_test.go` is an integration test suite that is executed against every version of Cassandra that gocql supports as part of the CI process on Travis. That said, the point of writing tests is to provide a safety net to catch regressions, so there is no need to go overboard with tests. Remember that the more tests you write, the more code we will have to maintain. So there's a balance to strike there. ### Sign Off Procedure Generally speaking, a pull request can get merged by any one of the project's committers. If your change is minor, chances are that one team member will just go ahead and merge it there and then. As stated earlier, suitable test coverage will increase the likelihood that a single reviewer will assess and merge your change. If your change has no test coverage, or looks like it may have wider implications for the health and stability of the library, the reviewer may elect to refer the change to another team member to achieve consensus before proceeding. Therefore, the tighter and cleaner your patch is, the quicker it will go through the review process. ### Supported Features gocql is a low level wire driver for Cassandra CQL. By and large, we would like to keep the functional scope of the library as narrow as possible. We think that gocql should be tight and focused, and we will be naturally skeptical of things that could just as easily be implemented in a higher layer. Inevitably you will come across something that could be implemented in a higher layer, save for a minor change to the core API. In this instance, please strike up a conversation in the Cassandra community. Chances are we will understand what you are trying to achieve and will try to accommodate this in a maintainable way. ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: Makefile ================================================ SHELL := bash .ONESHELL: .SHELLFLAGS := -eo pipefail -c MAKEFILE_PATH := $(abspath $(dir $(abspath $(lastword $(MAKEFILE_LIST))))) KEY_PATH = ${MAKEFILE_PATH}/testdata/pki BIN_DIR := "${MAKEFILE_PATH}/bin" CASSANDRA_VERSION ?= LATEST SCYLLA_VERSION ?= LATEST GOLANGCI_VERSION = 2.5.0 TEST_CQL_PROTOCOL ?= 4 TEST_COMPRESSOR ?= snappy TEST_OPTS ?= TEST_INTEGRATION_TAGS ?= integration gocql_debug JVM_EXTRA_OPTS ?= -Dcassandra.test.fail_writes_ks=test -Dcassandra.custom_query_handler_class=org.apache.cassandra.cql3.CustomPayloadMirroringQueryHandler CCM_CASSANDRA_CLUSTER_NAME = gocql_cassandra_integration_test CCM_CASSANDRA_IP_PREFIX = 127.0.1. CCM_CASSANDRA_REPO ?= github.com/apache/cassandra-ccm CCM_CASSANDRA_VERSION ?= trunk CCM_SCYLLA_CLUSTER_NAME = gocql_scylla_integration_test CCM_SCYLLA_IP_PREFIX = 127.0.2. CCM_SCYLLA_REPO ?= github.com/scylladb/scylla-ccm CCM_SCYLLA_VERSION ?= master ifeq (${CCM_CONFIG_DIR},) CCM_CONFIG_DIR = ~/.ccm endif CCM_CONFIG_DIR := $(shell readlink --canonicalize ${CCM_CONFIG_DIR}) CASSANDRA_CONFIG ?= "client_encryption_options.enabled: true" \ "client_encryption_options.keystore: ${KEY_PATH}/.keystore" \ "client_encryption_options.keystore_password: cassandra" \ "client_encryption_options.require_client_auth: true" \ "client_encryption_options.truststore: ${KEY_PATH}/.truststore" \ "client_encryption_options.truststore_password: cassandra" \ "concurrent_reads: 2" \ "concurrent_writes: 2" \ "write_request_timeout_in_ms: 5000" \ "read_request_timeout_in_ms: 5000" ifeq (${CASSANDRA_VERSION},3-LATEST) CASSANDRA_CONFIG += "rpc_server_type: sync" \ "rpc_min_threads: 2" \ "rpc_max_threads: 2" \ "enable_user_defined_functions: true" \ "enable_materialized_views: true" \ else ifeq (${CASSANDRA_VERSION},4-LATEST) CASSANDRA_CONFIG += "enable_user_defined_functions: true" \ "enable_materialized_views: true" else CASSANDRA_CONFIG += "user_defined_functions_enabled: true" \ "materialized_views_enabled: true" endif SCYLLA_CONFIG = "native_transport_port_ssl: 9142" \ "native_transport_port: 9042" \ "native_shard_aware_transport_port: 19042" \ "native_shard_aware_transport_port_ssl: 19142" \ "client_encryption_options.enabled: true" \ "client_encryption_options.certificate: ${KEY_PATH}/cassandra.crt" \ "client_encryption_options.keyfile: ${KEY_PATH}/cassandra.key" \ "client_encryption_options.truststore: ${KEY_PATH}/ca.crt" \ "client_encryption_options.require_client_auth: true" \ "maintenance_socket: workdir" \ "enable_tablets: true" \ "enable_user_defined_functions: true" \ "experimental_features: [udf]" export JVM_EXTRA_OPTS export JAVA11_HOME=${JAVA_HOME_11_X64} export JAVA17_HOME=${JAVA_HOME_17_X64} export JAVA_HOME=${JAVA_HOME_11_X64} export PATH := $(MAKEFILE_PATH)/bin:~/.sdkman/bin:$(PATH) print-config: echo ${CASSANDRA_CONFIG} .prepare-bin: @[[ -d "$(MAKEFILE_PATH)/bin" ]] || mkdir "$(MAKEFILE_PATH)/bin" .prepare-get-version: .prepare-bin @if [[ ! -f "$(MAKEFILE_PATH)/bin/get-version" ]]; then echo "bin/get-version is not found, installing it" curl -sSLo /tmp/get-version.zip https://github.com/scylladb-actions/get-version/releases/download/v0.4.5/get-version_0.4.5_linux_amd64v3.zip unzip /tmp/get-version.zip get-version -d "$(MAKEFILE_PATH)/bin" >/dev/null fi .prepare-environment-update-aio-max-nr: @if (( $$(< /proc/sys/fs/aio-max-nr) < 2097152 )); then echo 2097152 | sudo tee /proc/sys/fs/aio-max-nr >/dev/null fi clean-old-temporary-docker-images: @echo "Running Docker Hub image cleanup script..." python ci/clean-old-temporary-docker-images.py CASSANDRA_VERSION_FILE=/tmp/cassandra-version-${CASSANDRA_VERSION}.resolved resolve-cassandra-version: .prepare-get-version @find "${CASSANDRA_VERSION_FILE}" -mtime +0 -delete 2>/dev/null 1>&1 || true if [[ -f "${CASSANDRA_VERSION_FILE}" ]]; then echo "Resolved Cassandra ${CASSANDRA_VERSION} to $$(cat ${CASSANDRA_VERSION_FILE})" exit 0 fi if [[ "${CASSANDRA_VERSION}" == "LATEST" ]]; then CASSANDRA_VERSION_RESOLVED=`get-version -source github-tag -repo apache/cassandra -prefix "cassandra-" -out-no-prefix -filters "^[0-9]+$$.^[0-9]+$$.^[0-9]+$$ and LAST.LAST.LAST" | tr -d '\"'` elif [[ "${CASSANDRA_VERSION}" == "5-LATEST" ]]; then CASSANDRA_VERSION_RESOLVED=`get-version -source github-tag -repo apache/cassandra -prefix "cassandra-" -out-no-prefix -filters "^[0-9]+$$.^[0-9]+$$.^[0-9]+$$ and 5.LAST.LAST" | tr -d '\"'` elif [[ "${CASSANDRA_VERSION}" == "4-LATEST" ]]; then CASSANDRA_VERSION_RESOLVED=`get-version -source github-tag -repo apache/cassandra -prefix "cassandra-" -out-no-prefix -filters "^[0-9]+$$.^[0-9]+$$.^[0-9]+$$ and 4.LAST.LAST" | tr -d '\"'` elif [[ "${CASSANDRA_VERSION}" == "3-LATEST" ]]; then CASSANDRA_VERSION_RESOLVED=`get-version -source github-tag -repo apache/cassandra -prefix "cassandra-" -out-no-prefix -filters "^[0-9]+$$.^[0-9]+$$.^[0-9]+$$ and 3.LAST.LAST" | tr -d '\"'` elif echo "${CASSANDRA_VERSION}" | grep -P '^[0-9\.]+'; then CASSANDRA_VERSION_RESOLVED=${CASSANDRA_VERSION} else echo "Unknown Cassandra version name '${CASSANDRA_VERSION}'" exit 1 fi if [[ -z "$${CASSANDRA_VERSION_RESOLVED}" ]]; then echo "There is no ${CASSANDRA_VERSION} Cassandra version" if [[ -n "$${GITHUB_ENV}" ]]; then echo "value=NOT-FOUND" >>$${GITHUB_OUTPUT} echo "CASSANDRA_VERSION_RESOLVED=NOT-FOUND" >>$${GITHUB_ENV} exit 0 fi exit 2 fi echo "Resolved Cassandra ${CASSANDRA_VERSION} to $${CASSANDRA_VERSION_RESOLVED}" if [[ -n "$${GITHUB_OUTPUT}" ]]; then echo "value=$${CASSANDRA_VERSION_RESOLVED}" >>$${GITHUB_OUTPUT} fi if [[ -n "$${GITHUB_ENV}" ]]; then echo "CASSANDRA_VERSION_RESOLVED=$${CASSANDRA_VERSION_RESOLVED}" >>$${GITHUB_ENV} fi echo "$${CASSANDRA_VERSION_RESOLVED}" >${CASSANDRA_VERSION_FILE} SCYLLA_VERSION_FILE=/tmp/scylla-version-${SCYLLA_VERSION}.resolved resolve-scylla-version: .prepare-get-version @find "${SCYLLA_VERSION_FILE}" -mtime +0 -delete 2>/dev/null 1>&1 || true if [[ -f "${SCYLLA_VERSION_FILE}" ]]; then echo "Resolved ScyllaDB ${SCYLLA_VERSION} to $$(cat ${SCYLLA_VERSION_FILE})" exit 0 fi if [[ "${SCYLLA_VERSION}" == "LTS-LATEST" ]]; then SCYLLA_VERSION_RESOLVED=`get-version --source dockerhub-imagetag --repo scylladb/scylla -filters "^[0-9]{4}$$.^[0-9]+$$.^[0-9]+$$ and LAST.1.LAST" | tr -d '\"'` elif [[ "${SCYLLA_VERSION}" == "LTS-PRIOR" ]]; then SCYLLA_VERSION_RESOLVED=`get-version --source dockerhub-imagetag --repo scylladb/scylla -filters "^[0-9]{4}$$.^[0-9]+$$.^[0-9]+$$ and LAST-1.1.LAST" | tr -d '\"'` if [[ -z "$${SCYLLA_VERSION_RESOLVED}" ]]; then SCYLLA_VERSION_RESOLVED=`get-version --source dockerhub-imagetag --repo scylladb/scylla-enterprise -filters "^[0-9]{4}$$.^[0-9]+$$.^[0-9]+$$ and LAST-1.1.LAST" | tr -d '\"'` fi elif [[ "${SCYLLA_VERSION}" == "LATEST" ]]; then SCYLLA_VERSION_RESOLVED=`get-version --source dockerhub-imagetag --repo scylladb/scylla -filters "^[0-9]{4}$$.^[0-9]+$$.^[0-9]+$$ and LAST.LAST.LAST" | tr -d '\"'` elif [[ "${SCYLLA_VERSION}" == "PRIOR" ]]; then SCYLLA_VERSION_RESOLVED=`get-version --source dockerhub-imagetag --repo scylladb/scylla -filters "^[0-9]{4}$$.^[0-9]+$$.^[0-9]+$$ and LAST.LAST.LAST-1" | tr -d '\"'` elif echo "${SCYLLA_VERSION}" | grep -P '^[0-9\.]+'; then SCYLLA_VERSION_RESOLVED=${SCYLLA_VERSION} else echo "Unknown ScyllaDB version name '${SCYLLA_VERSION}'" exit 1 fi if [[ -z "$${SCYLLA_VERSION_RESOLVED}" ]]; then echo "There is no ${SCYLLA_VERSION} ScyllaDB version" if [[ -n "$${GITHUB_ENV}" ]]; then echo "value=NOT-FOUND" >>$${GITHUB_OUTPUT} echo "SCYLLA_VERSION_RESOLVED=NOT-FOUND" >>$${GITHUB_ENV} exit 0 fi exit 2 fi echo "Resolved ScyllaDB ${SCYLLA_VERSION} to $${SCYLLA_VERSION_RESOLVED}" if [[ -n "$${GITHUB_OUTPUT}" ]]; then echo "value=$${SCYLLA_VERSION_RESOLVED}" >>$${GITHUB_OUTPUT} fi if [[ -n "$${GITHUB_ENV}" ]]; then echo "SCYLLA_VERSION_RESOLVED=$${SCYLLA_VERSION_RESOLVED}" >>$${GITHUB_ENV} fi echo "$${SCYLLA_VERSION_RESOLVED}" >${SCYLLA_VERSION_FILE} cassandra-start: .prepare-pki .prepare-cassandra-ccm .prepare-java resolve-cassandra-version @if [ -d ${CCM_CONFIG_DIR}/${CCM_CASSANDRA_CLUSTER_NAME} ] && ccm switch ${CCM_CASSANDRA_CLUSTER_NAME} 2>/dev/null 1>&2 && ccm status | grep UP 2>/dev/null 1>&2; then echo "Cassandra cluster is already started" exit 0 fi if [[ -z "$${CASSANDRA_VERSION_RESOLVED}" ]]; then CASSANDRA_VERSION_RESOLVED=$$(cat '${CASSANDRA_VERSION_FILE}') fi if [[ -z "$${CASSANDRA_VERSION_RESOLVED}" ]]; then echo "Cassandra version ${CASSANDRA_VERSION} was not resolved" exit 1 fi source ~/.sdkman/bin/sdkman-init.sh; echo "Start Cassandra ${CASSANDRA_VERSION}($${CASSANDRA_VERSION_RESOLVED}) cluster" ccm stop ${CCM_CASSANDRA_CLUSTER_NAME} 2>/dev/null 1>&2 || true ccm remove ${CCM_CASSANDRA_CLUSTER_NAME} 2>/dev/null 1>&2 || true ccm create ${CCM_CASSANDRA_CLUSTER_NAME} -i ${CCM_CASSANDRA_IP_PREFIX} -v "$${CASSANDRA_VERSION_RESOLVED}" -n3 -d --vnodes --jvm_arg="-Xmx256m -XX:NewSize=100m" ccm updateconf ${CASSANDRA_CONFIG} for conf_dir in ${CCM_CONFIG_DIR}/${CCM_CASSANDRA_CLUSTER_NAME}/node*/conf; do \ sed -i 's/^#MAX_HEAP_SIZE=.*/MAX_HEAP_SIZE="256M"/' "$$conf_dir/cassandra-env.sh"; \ done ccm start --wait-for-binary-proto --wait-other-notice --verbose ccm status ccm node1 nodetool status scylla-start: .prepare-pki .prepare-scylla-ccm .prepare-environment-update-aio-max-nr resolve-scylla-version @if [ -d ${CCM_CONFIG_DIR}/${CCM_SCYLLA_CLUSTER_NAME} ] && ccm switch ${CCM_SCYLLA_CLUSTER_NAME} 2>/dev/null 1>&2 && ccm status | grep UP 2>/dev/null 1>&2; then echo "Scylla cluster is already started"; exit 0; fi if [[ -z "$${SCYLLA_VERSION_RESOLVED}" ]]; then SCYLLA_VERSION_RESOLVED=$$(cat '${SCYLLA_VERSION_FILE}') fi if [[ -z "$${SCYLLA_VERSION_RESOLVED}" ]]; then echo "ScyllaDB version ${SCYLLA_VERSION} was not resolved" exit 1 fi echo "Start scylla $(SCYLLA_VERSION)($${SCYLLA_VERSION_RESOLVED}) cluster" ccm stop ${CCM_SCYLLA_CLUSTER_NAME} 2>/dev/null 1>&2 || true ccm remove ${CCM_SCYLLA_CLUSTER_NAME} 2>/dev/null 1>&2 || true if [[ "$${SCYLLA_VERSION_RESOLVED}" != *:* ]]; then SCYLLA_VERSION_RESOLVED="release:$${SCYLLA_VERSION_RESOLVED}" fi ccm create ${CCM_SCYLLA_CLUSTER_NAME} -i ${CCM_SCYLLA_IP_PREFIX} --scylla -v $${SCYLLA_VERSION_RESOLVED} -n 3 -d --jvm_arg="--smp 2 --memory 1G --experimental-features udf --enable-user-defined-functions true" ccm updateconf ${SCYLLA_CONFIG} ccm start --wait-for-binary-proto --wait-other-notice --verbose ccm status ccm node1 nodetool status sudo chmod 0777 ${CCM_CONFIG_DIR}/${CCM_SCYLLA_CLUSTER_NAME}/*/cql.m || true download-cassandra: .prepare-cassandra-ccm resolve-cassandra-version @if [[ -z "$${CASSANDRA_VERSION_RESOLVED}" ]]; then CASSANDRA_VERSION_RESOLVED=$$(cat '${CASSANDRA_VERSION_FILE}') fi if [[ -z "$${CASSANDRA_VERSION_RESOLVED}" ]]; then echo "Cassandra version ${CASSANDRA_VERSION} was not resolved" exit 1 fi rm -rf /tmp/download.ccm || true mkdir /tmp/download.ccm || true ccm create ccm_1 -i 127.0.254. -n 1:0 -v "$${CASSANDRA_VERSION_RESOLVED}" --config-dir=/tmp/download.ccm rm -rf /tmp/download.ccm download-scylla: .prepare-scylla-ccm resolve-scylla-version @if [[ -z "$${SCYLLA_VERSION_RESOLVED}" ]]; then SCYLLA_VERSION_RESOLVED=$$(cat '${SCYLLA_VERSION_FILE}') fi if [[ -z "$${SCYLLA_VERSION_RESOLVED}" ]]; then echo "ScyllaDB version ${SCYLLA_VERSION} was not resolved" exit 1 fi rm -rf /tmp/download.ccm || true mkdir /tmp/download.ccm || true if [[ "$${SCYLLA_VERSION_RESOLVED}" != *:* ]]; then SCYLLA_VERSION_RESOLVED="release:$${SCYLLA_VERSION_RESOLVED}" fi ccm create ccm_1 -i 127.0.254. -n 1:0 -v "$${SCYLLA_VERSION_RESOLVED}" --scylla --config-dir=/tmp/download.ccm rm -rf /tmp/download.ccm cassandra-stop: .prepare-cassandra-ccm @echo "Stop cassandra cluster" @ccm stop --not-gently ${CCM_CASSANDRA_CLUSTER_NAME} 2>/dev/null 1>&2 || true @ccm remove ${CCM_CASSANDRA_CLUSTER_NAME} 2>/dev/null 1>&2 || true scylla-stop: .prepare-scylla-ccm @echo "Stop scylla cluster" @ccm stop --not-gently ${CCM_SCYLLA_CLUSTER_NAME} 2>/dev/null 1>&2 || true @ccm remove ${CCM_SCYLLA_CLUSTER_NAME} 2>/dev/null 1>&2 || true test-integration-cassandra: cassandra-start @echo "Run integration tests for proto ${TEST_CQL_PROTOCOL} on cassandra ${CASSANDRA_VERSION}" if [[ -z "$${CASSANDRA_VERSION_RESOLVED}" ]]; then CASSANDRA_VERSION_RESOLVED=$$(cat '${CASSANDRA_VERSION_FILE}') fi if [[ -z "$${CASSANDRA_VERSION_RESOLVED}" ]]; then echo "Cassandra version ${CASSANDRA_VERSION} was not resolved" exit 1 fi echo "go test -v ${TEST_OPTS} -tags \"${TEST_INTEGRATION_TAGS}\" -distribution cassandra -timeout=10m -runauth -gocql.timeout=60s -runssl -proto=${TEST_CQL_PROTOCOL} -rf=3 -clusterSize=3 -autowait=2000ms -compressor=${TEST_COMPRESSOR} -gocql.cversion=$${CASSANDRA_VERSION_RESOLVED} -cluster=$$(ccm liveset) ./..." go test -v ${TEST_OPTS} -tags "${TEST_INTEGRATION_TAGS}" -distribution cassandra -timeout=10m -runauth -gocql.timeout=60s -runssl -proto=${TEST_CQL_PROTOCOL} -rf=3 -clusterSize=3 -autowait=2000ms -compressor=${TEST_COMPRESSOR} -gocql.cversion=$$(ccm node1 versionfrombuild) -cluster=$$(ccm liveset) ./... test-integration-scylla: scylla-start @echo "Run integration tests for proto ${TEST_CQL_PROTOCOL} on scylla ${SCYLLA_VERSION}" if [ -S "${CCM_CONFIG_DIR}/${CCM_SCYLLA_CLUSTER_NAME}/node1/cql.m" ]; then CLUSTER_SOCKET="-cluster-socket ${CCM_CONFIG_DIR}/${CCM_SCYLLA_CLUSTER_NAME}/node1/cql.m" else echo "Cluster socket is not found" fi if [[ -z "$${SCYLLA_VERSION_RESOLVED}" ]]; then SCYLLA_VERSION_RESOLVED=$$(cat '${SCYLLA_VERSION_FILE}') fi if [[ -z "$${SCYLLA_VERSION_RESOLVED}" ]]; then echo "ScyllaDB version ${SCYLLA_VERSION} was not resolved" exit 1 fi echo "go test -v ${TEST_OPTS} -tags \"${TEST_INTEGRATION_TAGS}\" -distribution scylla $${CLUSTER_SOCKET} -timeout=5m -gocql.timeout=60s -proto=${TEST_CQL_PROTOCOL} -rf=3 -clusterSize=3 -autowait=2000ms -compressor=${TEST_COMPRESSOR} -gocql.cversion=$${SCYLLA_VERSION_RESOLVED} -cluster=$$(ccm liveset) ./..." go test -v ${TEST_OPTS} -tags "${TEST_INTEGRATION_TAGS}" -distribution scylla $${CLUSTER_SOCKET} -timeout=5m -gocql.timeout=60s -proto=${TEST_CQL_PROTOCOL} -rf=3 -clusterSize=3 -autowait=2000ms -compressor=${TEST_COMPRESSOR} -gocql.cversion=$${SCYLLA_VERSION_RESOLVED} -cluster=$$(ccm liveset) ./... test-unit: .prepare-pki @echo "Run unit tests" go clean -testcache ifeq ($(shell if [[ -n "$${GITHUB_STEP_SUMMARY}" ]]; then echo "running-in-workflow"; else echo "running-in-shell"; fi), running-in-workflow) echo "### Unit Test Results" >>$${GITHUB_STEP_SUMMARY} echo '```' >>$${GITHUB_STEP_SUMMARY} echo go test -tags unit -timeout=5m -race ./... go test -tags unit -timeout=5m -race ./... | tee -a "$${GITHUB_STEP_SUMMARY}"; TEST_STATUS=$${PIPESTATUS[0]}; echo '```' >>"$${GITHUB_STEP_SUMMARY}"; exit "$${TEST_STATUS}" else go test -v -tags unit -timeout=5m -race ./... endif test-bench: @echo "Run benchmark tests" ifeq ($(shell if [[ -n "$${GITHUB_STEP_SUMMARY}" ]]; then echo "running-in-workflow"; else echo "running-in-shell"; fi), running-in-workflow) echo "### Benchmark Results" >>$${GITHUB_STEP_SUMMARY} echo '```' >>"$${GITHUB_STEP_SUMMARY}" echo go test -bench=. -benchmem ./... go test -bench=. -benchmem ./... | tee -a >>"$${GITHUB_STEP_SUMMARY}" echo '```' >>"$${GITHUB_STEP_SUMMARY}" else go test -bench=. -benchmem ./... endif check-go-mod-drift: @echo "Check Go module drift" go mod tidy -diff go mod tidy -C lz4 -diff go mod tidy -C tests/bench -diff check: .prepare-golangci check-go-mod-drift @echo "Build" go build -tags all . echo "Check linting" ${BIN_DIR}/golangci-lint run fix-go-mod-drift: @echo "Fix Go module drift" go mod tidy go mod tidy -C lz4 go mod tidy -C tests/bench fix: .prepare-golangci fix-go-mod-drift @echo "Fix linting" ${BIN_DIR}/golangci-lint run --fix .prepare-java: ifeq ($(shell if [ -f ~/.sdkman/bin/sdkman-init.sh ]; then echo "installed"; else echo "not-installed"; fi), not-installed) @$(MAKE) install-java endif install-java: @echo "Installing SDKMAN..." curl -s "https://get.sdkman.io" | bash echo "sdkman_auto_answer=true" >> ~/.sdkman/etc/config source ~/.sdkman/bin/sdkman-init.sh; echo "Installing Java versions..."; sdk install java 11.0.30-zulu; sdk install java 17.0.18-zulu; sdk default java 11.0.30-zulu; sdk use java 11.0.30-zulu; .prepare-cassandra-ccm: @if command -v ccm >/dev/null 2>&1 && grep CASSANDRA ${CCM_CONFIG_DIR}/ccm-type 2>/dev/null 1>&2 && grep ${CCM_CASSANDRA_VERSION} ${CCM_CONFIG_DIR}/ccm-version 2>/dev//null 1>&2; then echo "Cassandra CCM ${CCM_CASSANDRA_VERSION} is already installed"; exit 0 fi $(MAKE) install-cassandra-ccm install-cassandra-ccm: @echo "Install CCM ${CCM_CASSANDRA_VERSION}" pip install "git+https://${CCM_CASSANDRA_REPO}.git@${CCM_CASSANDRA_VERSION}" mkdir ${CCM_CONFIG_DIR} 2>/dev/null || true echo CASSANDRA > ${CCM_CONFIG_DIR}/ccm-type echo ${CCM_CASSANDRA_VERSION} > ${CCM_CONFIG_DIR}/ccm-version .prepare-scylla-ccm: @if command -v ccm >/dev/null 2>&1 && grep SCYLLA ${CCM_CONFIG_DIR}/ccm-type 2>/dev/null 1>&2 && grep ${CCM_SCYLLA_VERSION} ${CCM_CONFIG_DIR}/ccm-version 2>/dev//null 1>&2; then echo "Scylla CCM ${CCM_SCYLLA_VERSION} is already installed"; exit 0 fi $(MAKE) install-scylla-ccm install-scylla-ccm: @echo "Installing Scylla CCM ${CCM_SCYLLA_VERSION}" pip install "git+https://${CCM_SCYLLA_REPO}.git@${CCM_SCYLLA_VERSION}" mkdir ${CCM_CONFIG_DIR} 2>/dev/null || true echo SCYLLA > ${CCM_CONFIG_DIR}/ccm-type echo ${CCM_SCYLLA_VERSION} > ${CCM_CONFIG_DIR}/ccm-version .prepare-pki: @[ -f "testdata/pki/cassandra.key" ] || (echo "Generating new PKI" && cd testdata/pki/ && bash ./generate_certs.sh) generate-pki: @echo "Generating new PKI" rm -f testdata/pki/.keystore testdata/pki/.truststore testdata/pki/*.p12 testdata/pki/*.key testdata/pki/*.crt || true cd testdata/pki/ && bash ./generate_certs.sh .prepare-golangci: @if ! "${BIN_DIR}/golangci-lint" --version | grep '${GOLANGCI_VERSION}' >/dev/null 2>&1 ; then mkdir -p "${BIN_DIR}" echo "Installing golangci-lint to '${BIN_DIR}'" curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b bin/ v$(GOLANGCI_VERSION) fi ================================================ FILE: NOTICE ================================================ ScyllaDB GoCQL Driver Copyright 2024 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). This product originates, before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40, from software from the Gocql Authors, with copyright and license as follows: Copyright (c) 2016, The Gocql authors All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Where The Gocql Authors for copyright purposes are below. Those marked with asterisk have agreed to donate (copyright assign) their contributions to the Apache Software Foundation, signing CLAs when appropriate. Christoph Hack Jonathan Rudenberg * Thorsten von Eicken * Matt Robenolt Phillip Couto * Niklas Korz Nimi Wariboko Jr Ghais Issa * Sasha Klizhentas Konstantin Cherkasov Ben Hood <0x6e6562@gmail.com> Pete Hopkins Chris Bannister * Maxim Bublis Alex Zorin Kasper Middelboe Petersen Harpreet Sawhney Charlie Andrews * Stanislavs Koikovs Dan Forest Miguel Serrano * Stefan Radomski Josh Wright Jacob Rhoden Ben Frye Fred McCann * Dan Simmons * Muir Manders Sankar P * Julien Da Silva Dan Kennedy * Nick Dhupia Yasuharu Goto * Jeremy Schlatter * Matthias Kadenbach Dean Elbaz Mike Berman Dmitriy Fedorenko * Zach Marcantel James Maloney Ashwin Purohit * Dan Kinder * Oliver Beattie * Justin Corpron * Miles Delahunty Zach Badgett Maciek Sakrejda * Jeff Mitchell Baptiste Fontaine * Matt Heath * Jamie Cuthill Adrian Casajus * John Weldon * Adrien Bustany * Andrey Smirnov * Adam Weiner * Daniel Cannon Johnny Bergström Adriano Orioli * Claudiu Raveica * Artem Chernyshev * Ference Fu LOVOO nikandfor * Anthony Woods * Alexander Inozemtsev * Rob McColl ; * Viktor Tönköl * Ian Lozinski Michael Highstead * Sarah Brown * Caleb Doxsey * Frederic Hemery * Pekka Enberg * Mark M Bartosz Burclaf * Marcus King * Andrew de Andrade Robert Nix Nathan Youngman * Charles Law ; * Nathan Davies * Bo Blanton Vincent Rischmann * Jesse Claven * Derrick Wippler Leigh McCulloch Ron Kuris Raphael Gavache * Yasser Abdolmaleki Krishnanand Thommandra Blake Atkinson Dharmendra Parsaila Nayef Ghattas * Michał Matczuk * Ben Krebsbach * Vivian Mathews * Sascha Steinbiss * Seth Rosenblum * Javier Zunzunegui Luke Hines * Zhixin Wen * Chang Liu Ingo Oeser * Luke Hines Jacob Greenleaf Alex Lourie ; * Marco Cadetg * Karl Matthias * Thomas Meson * Martin Sucha ; * Pavel Buchinchik Rintaro Okamura * Yura Sokolov ; Jorge Bay * Dmitriy Kozlov * Alexey Romanovsky Jaume Marhuenda Beltran Piotr Dulikowski Árni Dagur * Tushar Das * Maxim Vladimirskiy * Bogdan-Ciprian Rusu * Yuto Doi * Krishna Vadali Jens-W. Schicke-Uffmann * Ondrej Polakovič * Sergei Karetnikov * Stefan Miklosovic * Adam Burk * Valerii Ponomarov * Neal Turett * Doug Schaapveld * Steven Seidman Wojciech Przytuła * João Reis * Lauro Ramos Venancio Dmitry Kropachev Oliver Boyle * Jackson Fleming * Sylwia Szunejko * ================================================ FILE: README.md ================================================
![Build Passing](https://github.com/scylladb/gocql/workflows/Build/badge.svg) [![Read the Fork Driver Docs](https://img.shields.io/badge/Read_the_Docs-pkg_go-blue)](https://pkg.go.dev/github.com/scylladb/gocql#section-documentation) [![Protocol Specs](https://img.shields.io/badge/Protocol_Specs-ScyllaDB_Docs-blue)](https://github.com/scylladb/scylladb/blob/master/docs/dev/protocol-extensions.md)

Scylla Shard-Aware Fork of [apache/cassandra-gocql-driver](https://github.com/apache/cassandra-gocql-driver)

This is a fork of [apache/cassandra-gocql-driver](https://github.com/apache/cassandra-gocql-driver) package that we created at Scylla. It contains extensions to tokenAwareHostPolicy supported by the Scylla 2.3 and onwards. It allows driver to select a connection to a particular shard on a host based on the token. This eliminates passing data between shards and significantly reduces latency. There are open pull requests to merge the functionality to the upstream project: * [gocql/gocql#1210](https://github.com/gocql/gocql/pull/1210) * [gocql/gocql#1211](https://github.com/gocql/gocql/pull/1211). It also provides support for shard aware ports, a faster way to connect to all shards, details available in [blogpost](https://www.scylladb.com/2021/04/27/connect-faster-to-scylla-with-a-shard-aware-port/). --- ### Table of Contents - [1. Sunsetting Model](#1-sunsetting-model) - [2. Installation](#2-installation) - [3. Quick Start](#3-quick-start) - [4. Data Types](#4-data-types) - [5. Configuration](#5-configuration) - [5.1 Shard-aware port](#51-shard-aware-port) - [5.2 Client routes (PrivateLink)](#52-client-routes-privatelink) - [5.3 Iterator](#53-iterator) - [6. Contributing](#6-contributing) ## 1. Sunsetting Model > [!WARNING] > In general, the gocql team will focus on supporting the current and previous versions of Go. gocql may still work with older versions of Go, but official support for these versions will have been sunset. ## 2. Installation This is a drop-in replacement to gocql, it reuses the `github.com/gocql/gocql` import path. Add the following line to your project `go.mod` file. ```mod replace github.com/gocql/gocql => github.com/scylladb/gocql latest ``` and run ```sh go mod tidy ``` to evaluate `latest` to a concrete tag. Your project now uses the Scylla driver fork, make sure you are using the `TokenAwareHostPolicy` to enable the shard-awareness, continue reading for details. ## 3. Quick Start Spawn a ScyllaDB Instance using Docker Run command: ```sh docker run --name node1 --network your-network -p "9042:9042" -d scylladb/scylla:6.1.2 \ --overprovisioned 1 \ --smp 1 ``` Then, create a new connection using ScyllaDB GoCQL following the example below: ```go package main import ( "fmt" "github.com/gocql/gocql" ) func main() { var cluster = gocql.NewCluster("localhost:9042") var session, err = cluster.CreateSession() if err != nil { panic("Failed to connect to cluster") } defer session.Close() var query = session.Query("SELECT * FROM system.clients") if rows, err := query.Iter().SliceMap(); err == nil { for _, row := range rows { fmt.Printf("%v\n", row) } } else { panic("Query error: " + err.Error()) } } ``` `SliceMap()` consumes and closes the iterator before it returns. ## 4. Data Types Here's an list of all CQL Types reflected in the GoCQL environment: | ScyllaDB Type | Go Type | | ---------------- | ------------------ | | `ascii` | `string` | | `bigint` | `int64` | | `blob` | `[]byte` | | `boolean` | `bool` | | `date` | `time.Time` | | `decimal` | `inf.Dec` | | `double` | `float64` | | `duration` | `gocql.Duration` | | `float` | `float32` | | `uuid` | `gocql.UUID` | | `int` | `int32` | | `inet` | `string` | | `list` | `[]int32` | | `map` | `map[int32]string` | | `set` | `[]int32` | | `smallint` | `int16` | | `text` | `string` | | `time` | `time.Duration` | | `timestamp` | `time.Time` | | `timeuuid` | `gocql.UUID` | | `tinyint` | `int8` | | `varchar` | `string` | | `varint` | `int64` | ## 5. Configuration In order to make shard-awareness work, token aware host selection policy has to be enabled. Please make sure that the gocql configuration has `PoolConfig.HostSelectionPolicy` properly set like in the example below. __When working with a Scylla cluster, `PoolConfig.NumConns` option has no effect - the driver opens one connection for each shard and completely ignores this option.__ ```go c := gocql.NewCluster(hosts...) // Enable token aware host selection policy, if using multi-dc cluster set a local DC. fallback := gocql.RoundRobinHostPolicy() if localDC != "" { fallback = gocql.DCAwareRoundRobinPolicy(localDC) } c.PoolConfig.HostSelectionPolicy = gocql.TokenAwareHostPolicy(fallback) // If using multi-dc cluster use the "local" consistency levels. if localDC != "" { c.Consistency = gocql.LocalQuorum } // When working with a Scylla cluster the driver always opens one connection per shard, so `NumConns` is ignored. // c.NumConns = 4 ``` ### 5.1 Shard-aware port This version of gocql supports a more robust method of establishing connection for each shard by using _shard aware port_ for native transport. It greatly reduces time and the number of connections needed to establish a connection per shard in some cases - ex. when many clients connect at once, or when there are non-shard-aware clients connected to the same cluster. If you are using a custom Dialer and if your nodes expose the shard-aware port, it is highly recommended to update it so that it uses a specific source port when connecting. * If you are using a custom `net.Dialer`, you can make your dialer honor the source port by wrapping it in a `gocql.ScyllaShardAwareDialer`: ```go oldDialer := net.Dialer{...} clusterConfig.Dialer := &gocql.ScyllaShardAwareDialer{oldDialer} ``` * If you are using a custom type implementing `gocql.Dialer`, you can get the source port by using the `gocql.ScyllaGetSourcePort` function. An example: ```go func (d *myDialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) { sourcePort := gocql.ScyllaGetSourcePort(ctx) localAddr, err := net.ResolveTCPAddr(network, fmt.Sprintf(":%d", sourcePort)) if err != nil { return nil, err } d := &net.Dialer{LocalAddr: localAddr} return d.DialContext(ctx, network, addr) } ``` The source port might be already bound by another connection on your system. In such case, you should return an appropriate error so that the driver can retry with a different port suitable for the shard it tries to connect to. * If you are using `net.Dialer.DialContext`, this function will return an error in case the source port is unavailable, and you can just return that error from your custom `Dialer`. * Otherwise, if you detect that the source port is unavailable, you can either return `gocql.ErrScyllaSourcePortAlreadyInUse` or `syscall.EADDRINUSE`. For this feature to work correctly, you need to make sure the following conditions are met: * Your cluster nodes are configured to listen on the shard-aware port (`native_shard_aware_transport_port` option), * Your cluster nodes are not behind a NAT which changes source ports, * If you have a custom Dialer, it connects from the correct source port (see the guide above). The feature is designed to gracefully fall back to the using the non-shard-aware port when it detects that some of the above conditions are not met. The driver will print a warning about misconfigured address translation if it detects it. Issues with shard-aware port not being reachable are not reported in non-debug mode, because there is no way to detect it without false positives. If you suspect that this feature is causing you problems, you can completely disable it by setting the `ClusterConfig.DisableShardAwarePort` flag to true. ### 5.2 Client routes (PrivateLink) Scylla Cloud exposes a `system.client_routes` table that maps hosts to PrivateLink endpoints. When configured, the driver can resolve and connect to the per-host PrivateLink address instead of using the public host IP. Use `WithClientRoutes` to enable it and pass the connection IDs you receive from Scylla Cloud: ```go cluster := gocql.NewCluster("private-link.dns.name") cluster.WithOptions( gocql.WithClientRoutes( gocql.WithEndpoints( gocql.ClientRoutesEndpoint{ConnectionID: "your-connection-id"}, ), ), ) ``` If you also want to seed the cluster with PrivateLink hostnames, provide `ConnectionAddr` values in the endpoints list. ### 5.3 Iterator Paging is a way to parse large result sets in smaller chunks. The driver provides an iterator to simplify this process. Use `Query.Iter()` to obtain iterator: ```go iter := session.Query("SELECT id, value FROM my_table WHERE id > 100 AND id < 10000").Iter() var results []int var id, value int for !iter.Scan(&id, &value) { if id%2 == 0 { results = append(results, value) } } if err := iter.Close(); err != nil { // handle error } ``` In case of range and `ALLOW FILTERING` queries server can send empty responses for some pages. That is why you should never consider empty response as the end of the result set. Always check `iter.Scan()` result to know if there are more results, or `Iter.LastPage()` to know if the last page was reached. ### 5.3 Compression To control network costs and traffic, you can enable compression. Use `ClusterConfig.Compressor` to enable compression (either Snappy or LZ4): ```go ... import ( ... "github.com/gocql/gocql" "github.com/gocql/gocql/lz4" ... ) config := gocql.NewCluster("10.0.12.83", "10.0.13.04", "10.0.14.12") config.Compressor = &gocql.SnappyCompressor{} //or LZ4 config.Compressor = &lz4.LZ4Compressor{} ... ``` ## 6. Contributing If you have any interest to be contributing in this GoCQL Fork, please read the [CONTRIBUTING.md](CONTRIBUTING.md) before initialize any Issue or Pull Request. ================================================ FILE: address_translators.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import "net" // AddressTranslator provides a way to translate node addresses (and ports) that are // discovered or received as a node event. This can be useful in an ec2 environment, // for instance, to translate public IPs to private IPs. type AddressTranslator interface { // Translate will translate the provided address and/or port to another // address and/or port. If no translation is possible, Translate will return the // address and port provided to it. Translate(addr net.IP, port int) (net.IP, int) } type AddressTranslatorFunc func(addr net.IP, port int) (net.IP, int) func (fn AddressTranslatorFunc) Translate(addr net.IP, port int) (net.IP, int) { return fn(addr, port) } // IdentityTranslator will do nothing but return what it was provided. It is essentially a no-op. func IdentityTranslator() AddressTranslator { return AddressTranslatorFunc(func(addr net.IP, port int) (net.IP, int) { return addr, port }) } type AddressTranslatorHostInfo interface { HostID() string Rack() string DataCenter() string BroadcastAddress() net.IP ListenAddress() net.IP RPCAddress() net.IP PreferredIP() net.IP Peer() net.IP UntranslatedConnectAddress() net.IP Port() int Partitioner() string ClusterName() string ScyllaShardAwarePort() uint16 ScyllaShardAwarePortTLS() uint16 ScyllaShardCount() int } // AddressTranslatorV2 provides a way to translate node addresses (and ports) that are // discovered or received as a node event. This can be useful in an ec2 environment, // for instance, to translate public IPs to private IPs. type AddressTranslatorV2 interface { AddressTranslator TranslateHost(host AddressTranslatorHostInfo, addr AddressPort) (AddressPort, error) } type AddressTranslatorFuncV2 func(hostID string, addr AddressPort) AddressPort func (fn AddressTranslatorFuncV2) Translate(addr net.IP, port int) (net.IP, int) { res := fn("", AddressPort{ Address: addr, Port: uint16(port), }) return res.Address, int(res.Port) } func (fn AddressTranslatorFuncV2) TranslateHost(host AddressTranslatorHostInfo, addr AddressPort) (AddressPort, error) { return fn(host.HostID(), addr), nil } var _ AddressTranslatorV2 = AddressTranslatorFuncV2(nil) ================================================ FILE: address_translators_test.go ================================================ //go:build unit // +build unit /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "net" "testing" "github.com/gocql/gocql/internal/tests" ) func TestIdentityAddressTranslator_NilAddrAndZeroPort(t *testing.T) { t.Parallel() var tr AddressTranslator = IdentityTranslator() hostIP := net.ParseIP("") if hostIP != nil { t.Errorf("expected host ip to be (nil) but was (%+v) instead", hostIP) } addr, port := tr.Translate(hostIP, 0) if addr != nil { t.Errorf("expected translated host to be (nil) but was (%+v) instead", addr) } tests.AssertEqual(t, "translated port", 0, port) } func TestIdentityAddressTranslator_HostProvided(t *testing.T) { t.Parallel() var tr AddressTranslator = IdentityTranslator() hostIP := net.ParseIP("10.1.2.3") if hostIP == nil { t.Error("expected host ip not to be (nil)") } addr, port := tr.Translate(hostIP, 9042) if !hostIP.Equal(addr) { t.Errorf("expected translated addr to be (%+v) but was (%+v) instead", hostIP, addr) } tests.AssertEqual(t, "translated port", 9042, port) } func TestTranslateHostAddresses_NoScyllaPorts(t *testing.T) { t.Parallel() translator := AddressTranslatorFunc(func(addr net.IP, port int) (net.IP, int) { return net.ParseIP("10.10.10.10"), 9142 }) host := HostInfoBuilder{ ConnectAddress: net.ParseIP("10.0.0.1"), Port: 9042, }.Build() translated, err := translateHostAddresses(translator, &host, nil) tests.AssertNil(t, "should return no error", err) tests.AssertTrue(t, "translated CQL address", net.ParseIP("10.10.10.10").Equal(translated.CQL.Address)) tests.AssertEqual(t, "translated CQL port", uint16(9142), translated.CQL.Port) tests.AssertTrue(t, "shard aware empty address", len(translated.ShardAware.Address) == 0) tests.AssertEqual(t, "shard aware empty port", uint16(0), translated.ShardAware.Port) tests.AssertTrue(t, "shard aware tls empty address", len(translated.ShardAwareTLS.Address) == 0) tests.AssertEqual(t, "shard aware tls empty port", uint16(0), translated.ShardAwareTLS.Port) } func TestTranslateHostAddresses_WithScyllaPorts(t *testing.T) { t.Parallel() translatedIP := net.ParseIP("192.0.2.10") translator := AddressTranslatorFuncV2(func(hostID string, addr AddressPort) AddressPort { if hostID != "a0000000-0000-0000-0000-000000000001" { t.Errorf("expected host id %q, got %q", "a0000000-0000-0000-0000-000000000001", hostID) } return AddressPort{ Address: translatedIP, Port: addr.Port + 1, } }) host := HostInfoBuilder{ ConnectAddress: net.ParseIP("10.0.0.1"), Port: 9042, HostId: "a0000000-0000-0000-0000-000000000001", }.Build() host.setScyllaFeatures(ScyllaHostFeatures{ shardAwarePort: 19042, shardAwarePortTLS: 19043, }) translated, err := translateHostAddresses(translator, &host, nil) tests.AssertNil(t, "should return no error", err) tests.AssertTrue(t, "translated CQL address", translatedIP.Equal(translated.CQL.Address)) tests.AssertEqual(t, "translated CQL port", uint16(9043), translated.CQL.Port) tests.AssertTrue(t, "translated shard aware address", translatedIP.Equal(translated.ShardAware.Address)) tests.AssertEqual(t, "translated shard aware port", uint16(19043), translated.ShardAware.Port) tests.AssertTrue(t, "translated shard aware tls address", translatedIP.Equal(translated.ShardAwareTLS.Address)) tests.AssertEqual(t, "translated shard aware tls port", uint16(19044), translated.ShardAwareTLS.Port) } ================================================ FILE: batch_test.go ================================================ //go:build integration // +build integration /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "fmt" "testing" "time" ) func TestBatch_Errors(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s (id int primary key, val inet)`, table)); err != nil { t.Fatal(err) } b := session.Batch(LoggedBatch) b = b.Query(fmt.Sprintf("SELECT * FROM gocql_test.%s WHERE id=2 AND val=?", table), nil) if err := b.Exec(); err == nil { t.Fatal("expected to get error for invalid query in batch") } } func TestBatch_WithTimestamp(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s (id int primary key, val text)`, table)); err != nil { t.Fatal(err) } micros := time.Now().UnixNano()/1e3 - 1000 b := session.Batch(LoggedBatch) b.WithTimestamp(micros) b = b.Query(fmt.Sprintf("INSERT INTO gocql_test.%s (id, val) VALUES (?, ?)", table), 1, "val") b = b.Query(fmt.Sprintf("INSERT INTO gocql_test.%s (id, val) VALUES (?, ?)", table), 2, "val") if err := b.Exec(); err != nil { t.Fatal(err) } var storedTs int64 if err := session.Query(fmt.Sprintf(`SELECT writetime(val) FROM gocql_test.%s WHERE id = ?`, table), 1).Scan(&storedTs); err != nil { t.Fatal(err) } if storedTs != micros { t.Errorf("got ts %d, expected %d", storedTs, micros) } } ================================================ FILE: callreq_wait.go ================================================ //go:build !race package gocql func waitCallReqDone(call *callReq, where string) { call.done.Wait() } ================================================ FILE: callreq_wait_race.go ================================================ //go:build race package gocql import ( "fmt" "time" ) func waitCallReqDone(call *callReq, where string) { done := make(chan struct{}) go func() { call.done.Wait() close(done) }() timer := time.NewTimer(2 * time.Second) defer timer.Stop() select { case <-done: case <-timer.C: panic(fmt.Sprintf("gocql: timed out waiting for exec cleanup in %s (stream=%d)", where, call.streamID)) } } ================================================ FILE: cass1batch_test.go ================================================ //go:build integration // +build integration /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "fmt" "strings" "testing" ) func TestProto1BatchInsert(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf("CREATE TABLE gocql_test.%s (id int primary key)", table)); err != nil { t.Fatal(err) } begin := "BEGIN BATCH" end := "APPLY BATCH" query := fmt.Sprintf("INSERT INTO %s (id) VALUES (?)", table) fullQuery := strings.Join([]string{begin, query, end}, "\n") args := []any{5} if err := session.Query(fullQuery, args...).Consistency(Quorum).Exec(); err != nil { t.Fatal(err) } } func TestShouldPrepareFunction(t *testing.T) { t.Parallel() var shouldPrepareTests = []struct { Stmt string Result bool }{ {` BEGIN BATCH INSERT INTO users (userID, password) VALUES ('smith', 'secret') APPLY BATCH ; `, true}, {`INSERT INTO users (userID, password, name) VALUES ('user2', 'ch@ngem3b', 'second user')`, true}, {`BEGIN COUNTER BATCH UPDATE stats SET views = views + 1 WHERE pageid = 1 APPLY BATCH`, true}, {`delete name from users where userID = 'smith';`, true}, {` UPDATE users SET password = 'secret' WHERE userID = 'smith' `, true}, {`CREATE TABLE users ( user_name varchar PRIMARY KEY, password varchar, gender varchar, session_token varchar, state varchar, birth_year bigint );`, false}, } for _, test := range shouldPrepareTests { q := &Query{stmt: test.Stmt, routingInfo: &queryRoutingInfo{}} if got := q.shouldPrepare(); got != test.Result { t.Fatalf("%q: got %v, expected %v\n", test.Stmt, got, test.Result) } } } ================================================ FILE: cassandra_test.go ================================================ //go:build integration // +build integration /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "bytes" "context" "errors" "fmt" "math" "math/big" "net" "reflect" "sort" "strconv" "strings" "testing" "time" "unicode" frm "github.com/gocql/gocql/internal/frame" "github.com/gocql/gocql/internal/tests" "github.com/stretchr/testify/require" "gopkg.in/inf.v0" ) func TestEmptyHosts(t *testing.T) { t.Parallel() cluster := createCluster() cluster.Hosts = nil if session, err := cluster.CreateSession(); err == nil { session.Close() t.Error("expected err, got nil") } } func TestInvalidPeerEntry(t *testing.T) { t.Parallel() t.Skip("dont mutate system tables, rewrite this to test what we mean to test") session := createSession(t) // rack, release_version, schema_version, tokens are all null query := session.Query("INSERT into system.peers (peer, data_center, host_id, rpc_address) VALUES (?, ?, ?, ?)", "169.254.235.45", "datacenter1", "35c0ec48-5109-40fd-9281-9e9d4add2f1e", "169.254.235.45", ) if err := query.Exec(); err != nil { t.Fatal(err) } session.Close() cluster := createCluster() cluster.PoolConfig.HostSelectionPolicy = TokenAwareHostPolicy(RoundRobinHostPolicy()) session = createSessionFromCluster(cluster, t) defer func() { session.Query("DELETE from system.peers where peer = ?", "169.254.235.45").Exec() session.Close() }() // check we can perform a query iter := session.Query("select peer from system.peers").Iter() var peer string for iter.Scan(&peer) { } if err := iter.Close(); err != nil { t.Fatal(err) } } // TestUseStatementError checks to make sure the correct error is returned when the user tries to execute a use statement. func TestUseStatementError(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() if err := session.Query("USE gocql_test").Exec(); err != nil { if err != ErrUseStmt { t.Fatalf("expected ErrUseStmt, got: %v", err) } } else { t.Fatal("expected err, got nil.") } } // TestInvalidKeyspace checks that an invalid keyspace will return promptly and without a flood of connections func TestInvalidKeyspace(t *testing.T) { t.Parallel() cluster := createCluster() cluster.Keyspace = "invalidKeyspace" session, err := cluster.CreateSession() if err != nil { if err != ErrNoConnectionsStarted { t.Fatalf("Expected ErrNoConnections but got %v", err) } } else { session.Close() //Clean up the session t.Fatal("expected err, got nil.") } } func TestTracing(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s (id int primary key)`, table)); err != nil { t.Fatal("create:", err) } buf := &bytes.Buffer{} trace := &TraceWriter{session: session, w: buf} if err := session.Query(fmt.Sprintf(`INSERT INTO %s (id) VALUES (?)`, table), 42).Trace(trace).Exec(); err != nil { t.Fatal("insert:", err) } else if buf.Len() == 0 { t.Fatal("insert: failed to obtain any tracing") } trace.mu.Lock() buf.Reset() trace.mu.Unlock() var value int if err := session.Query(fmt.Sprintf(`SELECT id FROM %s WHERE id = ?`, table), 42).Trace(trace).Scan(&value); err != nil { t.Fatal("select:", err) } else if value != 42 { t.Fatalf("value: expected %d, got %d", 42, value) } else if buf.Len() == 0 { t.Fatal("select: failed to obtain any tracing") } // also works from session tracer session.SetTrace(trace) trace.mu.Lock() buf.Reset() trace.mu.Unlock() if err := session.Query(fmt.Sprintf(`SELECT id FROM %s WHERE id = ?`, table), 42).Scan(&value); err != nil { t.Fatal("select:", err) } if buf.Len() == 0 { t.Fatal("select: failed to obtain any tracing") } } func TestObserve(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s (id int primary key)`, table)); err != nil { t.Fatal("create:", err) } var ( observedErr error observedKeyspace string observedStmt string ) const keyspace = "gocql_test" resetObserved := func() { observedErr = errors.New("placeholder only") // used to distinguish err=nil cases observedKeyspace = "" observedStmt = "" } observer := funcQueryObserver(func(ctx context.Context, o ObservedQuery) { observedKeyspace = o.Keyspace observedStmt = o.Statement observedErr = o.Err }) // select before inserted, will error but the reporting is err=nil as the query is valid resetObserved() var value int if err := session.Query(fmt.Sprintf(`SELECT id FROM %s WHERE id = ?`, table), 43).Observer(observer).Scan(&value); err == nil { t.Fatal("select: expected error") } else if observedErr != nil { t.Fatalf("select: observed error expected nil, got %q", observedErr) } else if observedKeyspace != keyspace { t.Fatal("select: unexpected observed keyspace", observedKeyspace) } else if observedStmt != fmt.Sprintf(`SELECT id FROM %s WHERE id = ?`, table) { t.Fatal("select: unexpected observed stmt", observedStmt) } resetObserved() if err := session.Query(fmt.Sprintf(`INSERT INTO %s (id) VALUES (?)`, table), 42).Observer(observer).Exec(); err != nil { t.Fatal("insert:", err) } else if observedErr != nil { t.Fatal("insert:", observedErr) } else if observedKeyspace != keyspace { t.Fatal("insert: unexpected observed keyspace", observedKeyspace) } else if observedStmt != fmt.Sprintf(`INSERT INTO %s (id) VALUES (?)`, table) { t.Fatal("insert: unexpected observed stmt", observedStmt) } resetObserved() value = 0 if err := session.Query(fmt.Sprintf(`SELECT id FROM %s WHERE id = ?`, table), 42).Observer(observer).Scan(&value); err != nil { t.Fatal("select:", err) } else if value != 42 { t.Fatalf("value: expected %d, got %d", 42, value) } else if observedErr != nil { t.Fatal("select:", observedErr) } else if observedKeyspace != keyspace { t.Fatal("select: unexpected observed keyspace", observedKeyspace) } else if observedStmt != fmt.Sprintf(`SELECT id FROM %s WHERE id = ?`, table) { t.Fatal("select: unexpected observed stmt", observedStmt) } // also works from session observer resetObserved() oSession := createSession(t, func(config *ClusterConfig) { config.QueryObserver = observer }) if err := oSession.Query(fmt.Sprintf(`SELECT id FROM %s WHERE id = ?`, table), 42).Scan(&value); err != nil { t.Fatal("select:", err) } else if observedErr != nil { t.Fatal("select:", err) } else if observedKeyspace != keyspace { t.Fatal("select: unexpected observed keyspace", observedKeyspace) } else if observedStmt != fmt.Sprintf(`SELECT id FROM %s WHERE id = ?`, table) { t.Fatal("select: unexpected observed stmt", observedStmt) } // reports errors when the query is poorly formed resetObserved() value = 0 if err := session.Query(`SELECT id FROM unknown_table WHERE id = ?`, 42).Observer(observer).Scan(&value); err == nil { t.Fatal("select: expecting error") } else if observedErr == nil { t.Fatal("select: expecting observed error") } else if observedKeyspace != keyspace { t.Fatal("select: unexpected observed keyspace", observedKeyspace) } else if observedStmt != `SELECT id FROM unknown_table WHERE id = ?` { t.Fatal("select: unexpected observed stmt", observedStmt) } } func TestObserve_Pagination(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s (id int, PRIMARY KEY (id))`, table)); err != nil { t.Fatal("create:", err) } var observedRows int resetObserved := func() { observedRows = -1 } observer := funcQueryObserver(func(ctx context.Context, o ObservedQuery) { observedRows = o.Rows }) // insert 100 entries, relevant for pagination for i := 0; i < 50; i++ { if err := session.Query(fmt.Sprintf(`INSERT INTO %s (id) VALUES (?)`, table), i).Exec(); err != nil { t.Fatal("insert:", err) } } resetObserved() // read the 100 entries in paginated entries of size 10. Expecting 5 observations, each with 10 rows scanner := session.Query(fmt.Sprintf(`SELECT id FROM %s LIMIT 100`, table)). Observer(observer). PageSize(10). Iter().Scanner() for i := 0; i < 50; i++ { if !scanner.Next() { t.Fatalf("next: should still be true: %d: %v", i, scanner.Err()) } if i%10 == 0 { if observedRows != 10 { t.Fatalf("next: expecting a paginated query with 10 entries, got: %d (%d)", observedRows, i) } } else if observedRows != -1 { t.Fatalf("next: not expecting paginated query (-1 entries), got: %d", observedRows) } resetObserved() } if scanner.Next() { t.Fatal("next: no more entries where expected") } } func TestPaging(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf("CREATE TABLE gocql_test.%s (id int primary key)", table)); err != nil { t.Fatal("create table:", err) } for i := 0; i < 100; i++ { if err := session.Query(fmt.Sprintf("INSERT INTO %s (id) VALUES (?)", table), i).Exec(); err != nil { t.Fatal("insert:", err) } } iter := session.Query(fmt.Sprintf("SELECT id FROM %s", table)).PageSize(10).Iter() var id int count := 0 for iter.Scan(&id) { count++ } if err := iter.Close(); err != nil { t.Fatal("close:", err) } if count != 100 { t.Fatalf("expected %d, got %d", 100, count) } } func TestPagingWithAllowFiltering(t *testing.T) { t.Parallel() session := createSession(t) table := testTableName(t) t.Cleanup(func() { if err := session.Query(fmt.Sprintf("DROP TABLE gocql_test.%s", table)).Exec(); err != nil { t.Fatal("drop table:", err) } session.Close() }) const ( targetP1 = 50 targetP2 = 50 totalExpectedResults = 30 pageSize = 5 deletedRageStart = 10 deletedRageEnd = 20 // Some record range is being deleted, to test tombstones appearance expectedCount = totalExpectedResults - (deletedRageEnd - deletedRageStart) ) paginatedSelect := fmt.Sprintf("SELECT c1, f1 FROM gocql_test.%s WHERE p1 = %d AND p2 = %d AND f1 < %d ALLOW FILTERING;", table, targetP1, targetP2, totalExpectedResults) validateResult := func(t *testing.T, results []int) { if len(results) != expectedCount { t.Fatalf("expected %d got %d: %d", expectedCount, len(results), results) } sort.Ints(results) expect := make([]int, 0, expectedCount) for i := 0; i < totalExpectedResults; i++ { if i >= deletedRageStart && i < deletedRageEnd { continue } expect = append(expect, i) } if !reflect.DeepEqual(results, expect) { t.Fatalf("expected %v\ngot %v", expect, results) } } t.Run("Prepare", func(t *testing.T) { if err := createTable(session, fmt.Sprintf("CREATE TABLE gocql_test.%s (p1 int, p2 int, c1 int, f1 int, "+ "PRIMARY KEY ((p1, p2), c1)) WITH CLUSTERING ORDER BY (c1 DESC)", table)); err != nil { t.Fatal("create table:", err) } // Insert extra records for i := 0; i < 100; i++ { if err := session.Query(fmt.Sprintf("INSERT INTO gocql_test.%s (p1,p2,c1,f1) VALUES (?,?,?,?)", table), i, i, i, i).Exec(); err != nil { t.Fatal("insert:", err) } } // Insert records to a target partition for i := 0; i < 100; i++ { if err := session.Query(fmt.Sprintf("INSERT INTO gocql_test.%s (p1,p2,c1,f1) VALUES (?,?,?,?)", table), targetP1, targetP2, i, i).Exec(); err != nil { t.Fatal("insert:", err) } } if err := session.Query(fmt.Sprintf("DELETE FROM gocql_test.%s WHERE p1 = ? AND p2 = ? AND c1 >= ? AND c1 < ?", table), targetP1, targetP2, deletedRageStart, deletedRageEnd).Exec(); err != nil { t.Fatal("insert:", err) } }) t.Run("AutoPagination", func(t *testing.T) { for _, c := range []Consistency{One, Quorum} { t.Run(c.String(), func(t *testing.T) { iter := session.Query(paginatedSelect).Consistency(c).PageSize(pageSize).Iter() var c1, f1 int var results []int for iter.Scan(&c1, &f1) { if c1 != f1 { t.Fatalf("expected c1 and f1 values to be the same, but got c1=%d f1=%d", c1, f1) } results = append(results, f1) } if err := iter.Close(); err != nil { t.Fatal("select:", err.Error()) } validateResult(t, results) }) } }) t.Run("ManualPagination", func(t *testing.T) { for _, c := range []Consistency{One, Quorum} { t.Run(c.String(), func(t *testing.T) { var c1, f1 int var results []int var currentPageState []byte qry := session.Query(paginatedSelect).Consistency(c).PageSize(pageSize) for { iter := qry.PageState(currentPageState).Iter() // Here we make sure that all iterator, but last one have some data in it if !iter.LastPage() && iter.NumRows() == 0 { t.Errorf("expected at least one row, but got 0") } for iter.Scan(&c1, &f1) { if c1 != f1 { t.Fatalf("expected c1 and f1 values to be the same, but got c1=%d f1=%d", c1, f1) } results = append(results, f1) } if err := iter.Close(); err != nil { t.Fatal("select:", err.Error()) } if iter.LastPage() { break } newPageState := iter.PageState() if len(currentPageState) == len(newPageState) && bytes.Compare(newPageState, currentPageState) == 0 { t.Fatalf("page state did not change") } currentPageState = newPageState } validateResult(t, results) }) } }) } func TestPagingWithBind(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf("CREATE TABLE gocql_test.%s (id int, val int, primary key(id,val))", table)); err != nil { t.Fatal("create table:", err) } for i := 0; i < 100; i++ { if err := session.Query(fmt.Sprintf("INSERT INTO %s (id,val) VALUES (?,?)", table), 1, i).Exec(); err != nil { t.Fatal("insert:", err) } } q := session.Query(fmt.Sprintf("SELECT val FROM %s WHERE id = ? AND val < ?", table), 1, 50).PageSize(10) iter := q.Iter() var id int count := 0 for iter.Scan(&id) { count++ } if err := iter.Close(); err != nil { t.Fatal("close:", err) } if count != 50 { t.Fatalf("expected %d, got %d", 50, count) } iter = q.Bind(1, 20).Iter() count = 0 for iter.Scan(&id) { count++ } if count != 20 { t.Fatalf("expected %d, got %d", 20, count) } if err := iter.Close(); err != nil { t.Fatal("close:", err) } } func TestCAS(t *testing.T) { t.Parallel() cluster := createCluster() cluster.SerialConsistency = LocalSerial session := createSessionFromClusterTabletsDisabled(cluster, t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf(`CREATE TABLE %s ( title varchar, revid timeuuid, last_modified timestamp, PRIMARY KEY (title, revid) )`, table)); err != nil { t.Fatal("create:", err) } title, revid, modified := "baz", TimeUUID(), time.Now() var titleCAS string var revidCAS UUID var modifiedCAS time.Time if applied, err := session.Query(fmt.Sprintf(`INSERT INTO %s (title, revid, last_modified) VALUES (?, ?, ?) IF NOT EXISTS`, table), title, revid, modified).ScanCAS(&titleCAS, &revidCAS, &modifiedCAS); err != nil { t.Fatal("insert:", err) } else if !applied { t.Fatal("insert should have been applied") } if applied, err := session.Query(fmt.Sprintf(`INSERT INTO %s (title, revid, last_modified) VALUES (?, ?, ?) IF NOT EXISTS`, table), title, revid, modified).ScanCAS(&titleCAS, &revidCAS, &modifiedCAS); err != nil { t.Fatal("insert:", err) } else if applied { t.Fatal("insert should not have been applied") } else if title != titleCAS || revid != revidCAS { t.Fatalf("expected %s/%v/%v but got %s/%v/%v", title, revid, modified, titleCAS, revidCAS, modifiedCAS) } tenSecondsLater := modified.Add(10 * time.Second) if applied, err := session.Query(fmt.Sprintf(`DELETE FROM %s WHERE title = ? and revid = ? IF last_modified = ?`, table), title, revid, tenSecondsLater).ScanCAS(&modifiedCAS); err != nil { t.Fatal("delete:", err) } else if applied { t.Fatal("delete should have not been applied") } if modifiedCAS.Unix() != tenSecondsLater.Add(-10*time.Second).Unix() { t.Fatalf("Was expecting modified CAS to be %v; but was one second later", modifiedCAS.UTC()) } if _, err := session.Query(fmt.Sprintf(`DELETE FROM %s WHERE title = ? and revid = ? IF last_modified = ?`, table), title, revid, tenSecondsLater).ScanCAS(); !strings.HasPrefix(err.Error(), "gocql: not enough columns to scan into") { t.Fatalf("delete: was expecting count mismatch error but got: %q", err.Error()) } if applied, err := session.Query(fmt.Sprintf(`DELETE FROM %s WHERE title = ? and revid = ? IF last_modified = ?`, table), title, revid, modified).ScanCAS(&modifiedCAS); err != nil { t.Fatal("delete:", err) } else if !applied { t.Fatal("delete should have been applied") } if err := session.Query(fmt.Sprintf(`TRUNCATE %s`, table)).Exec(); err != nil { t.Fatal("truncate:", err) } successBatch := session.Batch(LoggedBatch) successBatch.Query(fmt.Sprintf("INSERT INTO %s (title, revid, last_modified) VALUES (?, ?, ?) IF NOT EXISTS", table), title, revid, modified) if applied, _, err := session.ExecuteBatchCAS(successBatch, &titleCAS, &revidCAS, &modifiedCAS); err != nil { t.Fatal("insert:", err) } else if !applied { t.Fatalf("insert should have been applied: title=%v revID=%v modified=%v", titleCAS, revidCAS, modifiedCAS) } successBatch = session.Batch(LoggedBatch) successBatch.Query(fmt.Sprintf("INSERT INTO %s (title, revid, last_modified) VALUES (?, ?, ?) IF NOT EXISTS", table), title+"_foo", revid, modified) casMap := make(map[string]any) if applied, _, err := session.MapExecuteBatchCAS(successBatch, casMap); err != nil { t.Fatal("insert:", err) } else if !applied { t.Fatal("insert should have been applied") } failBatch := session.Batch(LoggedBatch) failBatch.Query(fmt.Sprintf("INSERT INTO %s (title, revid, last_modified) VALUES (?, ?, ?) IF NOT EXISTS", table), title, revid, modified) if applied, _, err := session.ExecuteBatchCAS(successBatch, &titleCAS, &revidCAS, &modifiedCAS); err != nil { t.Fatal("insert:", err) } else if applied { t.Fatalf("insert should have not been applied: title=%v revID=%v modified=%v", titleCAS, revidCAS, modifiedCAS) } insertBatch := session.Batch(LoggedBatch) if *flagDistribution == "cassandra" && flagCassVersion.AtLeast(4, 1, 0) { insertBatch.Query(fmt.Sprintf("INSERT INTO %s (title, revid, last_modified) VALUES ('_foo', 2c3af400-73a4-11e5-9381-29463d90c3f0, toTimestamp(NOW()))", table)) insertBatch.Query(fmt.Sprintf("INSERT INTO %s (title, revid, last_modified) VALUES ('_foo', 3e4ad2f1-73a4-11e5-9381-29463d90c3f0, toTimestamp(NOW()))", table)) } else { insertBatch.Query(fmt.Sprintf("INSERT INTO %s (title, revid, last_modified) VALUES ('_foo', 2c3af400-73a4-11e5-9381-29463d90c3f0, DATEOF(NOW()))", table)) insertBatch.Query(fmt.Sprintf("INSERT INTO %s (title, revid, last_modified) VALUES ('_foo', 3e4ad2f1-73a4-11e5-9381-29463d90c3f0, DATEOF(NOW()))", table)) } if err := session.ExecuteBatch(insertBatch); err != nil { t.Fatal("insert:", err) } failBatch = session.Batch(LoggedBatch) if *flagDistribution == "cassandra" && flagCassVersion.AtLeast(4, 1, 0) { failBatch.Query(fmt.Sprintf("UPDATE %s SET last_modified = toTimestamp(NOW()) WHERE title='_foo' AND revid=2c3af400-73a4-11e5-9381-29463d90c3f0 IF last_modified=toTimestamp(NOW());", table)) failBatch.Query(fmt.Sprintf("UPDATE %s SET last_modified = toTimestamp(NOW()) WHERE title='_foo' AND revid=3e4ad2f1-73a4-11e5-9381-29463d90c3f0 IF last_modified=toTimestamp(NOW());", table)) } else { failBatch.Query(fmt.Sprintf("UPDATE %s SET last_modified = DATEOF(NOW()) WHERE title='_foo' AND revid=2c3af400-73a4-11e5-9381-29463d90c3f0 IF last_modified=DATEOF(NOW());", table)) failBatch.Query(fmt.Sprintf("UPDATE %s SET last_modified = DATEOF(NOW()) WHERE title='_foo' AND revid=3e4ad2f1-73a4-11e5-9381-29463d90c3f0 IF last_modified=DATEOF(NOW());", table)) } if applied, iter, err := session.ExecuteBatchCAS(failBatch, &titleCAS, &revidCAS, &modifiedCAS); err != nil { t.Fatal("insert:", err) } else if applied { t.Fatalf("insert should have not been applied: title=%v revID=%v modified=%v", titleCAS, revidCAS, modifiedCAS) } else { if scan := iter.Scan(&applied, &titleCAS, &revidCAS, &modifiedCAS); scan && applied { t.Fatalf("insert should have been applied: title=%v revID=%v modified=%v", titleCAS, revidCAS, modifiedCAS) } else if !scan { t.Fatal("should have scanned another row") } if err := iter.Close(); err != nil { t.Fatal("scan:", err) } } casMap = make(map[string]any) if applied, err := session.Query(fmt.Sprintf(`SELECT revid FROM %s WHERE title = ?`, table), title+"_foo").MapScanCAS(casMap); err != nil { t.Fatal("select:", err) } else if applied { t.Fatal("select shouldn't have returned applied") } if _, err := session.Query(fmt.Sprintf(`SELECT revid FROM %s WHERE title = ?`, table), title+"_foo").ScanCAS(&revidCAS); err == nil { t.Fatal("select: should have returned an error") } notCASBatch := session.Batch(LoggedBatch) notCASBatch.Query(fmt.Sprintf("INSERT INTO %s (title, revid, last_modified) VALUES (?, ?, ?)", table), title+"_baz", revid, modified) casMap = make(map[string]any) if _, _, err := session.MapExecuteBatchCAS(notCASBatch, casMap); err != ErrNotFound { t.Fatal("insert should have returned not found:", err) } notCASBatch = session.Batch(LoggedBatch) notCASBatch.Query(fmt.Sprintf("INSERT INTO %s (title, revid, last_modified) VALUES (?, ?, ?)", table), title+"_baz", revid, modified) casMap = make(map[string]any) if _, _, err := session.ExecuteBatchCAS(notCASBatch, &revidCAS); err != ErrNotFound { t.Fatal("insert should have returned not found:", err) } failBatch = session.Batch(LoggedBatch) failBatch.Query(fmt.Sprintf("UPDATE %s SET last_modified = TOTIMESTAMP(NOW()) WHERE title='_foo' AND revid=3e4ad2f1-73a4-11e5-9381-29463d90c3f0 IF last_modified = ?", table), modified) if _, _, err := session.ExecuteBatchCAS(failBatch, new(bool)); err == nil { t.Fatal("update should have errored") } // make sure MapScanCAS does not panic when MapScan fails casMap = make(map[string]any) casMap["last_modified"] = false if _, err := session.Query(fmt.Sprintf(`UPDATE %s SET last_modified = TOTIMESTAMP(NOW()) WHERE title='_foo' AND revid=3e4ad2f1-73a4-11e5-9381-29463d90c3f0 IF last_modified = ?`, table), modified).MapScanCAS(casMap); err == nil { t.Fatal("update should hvae errored", err) } // make sure MapExecuteBatchCAS does not panic when MapScan fails failBatch = session.Batch(LoggedBatch) failBatch.Query(fmt.Sprintf("UPDATE %s SET last_modified = TOTIMESTAMP(NOW()) WHERE title='_foo' AND revid=3e4ad2f1-73a4-11e5-9381-29463d90c3f0 IF last_modified = ?", table), modified) casMap = make(map[string]any) casMap["last_modified"] = false if _, _, err := session.MapExecuteBatchCAS(failBatch, casMap); err == nil { t.Fatal("update should have errored") } } func TestConsistencySerial(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) type testStruct struct { name string id int consistency Consistency expectedPanicValue string } testCases := []testStruct{ { name: "Any", consistency: Any, expectedPanicValue: "Serial consistency can only be SERIAL or LOCAL_SERIAL got ANY", }, { name: "One", consistency: One, expectedPanicValue: "Serial consistency can only be SERIAL or LOCAL_SERIAL got ONE", }, { name: "Two", consistency: Two, expectedPanicValue: "Serial consistency can only be SERIAL or LOCAL_SERIAL got TWO", }, { name: "Three", consistency: Three, expectedPanicValue: "Serial consistency can only be SERIAL or LOCAL_SERIAL got THREE", }, { name: "Quorum", consistency: Quorum, expectedPanicValue: "Serial consistency can only be SERIAL or LOCAL_SERIAL got QUORUM", }, { name: "LocalQuorum", consistency: LocalQuorum, expectedPanicValue: "Serial consistency can only be SERIAL or LOCAL_SERIAL got LOCAL_QUORUM", }, { name: "EachQuorum", consistency: EachQuorum, expectedPanicValue: "Serial consistency can only be SERIAL or LOCAL_SERIAL got EACH_QUORUM", }, { name: "Serial", id: 8, consistency: Serial, expectedPanicValue: "", }, { name: "LocalSerial", id: 9, consistency: LocalSerial, expectedPanicValue: "", }, { name: "LocalOne", consistency: LocalOne, expectedPanicValue: "Serial consistency can only be SERIAL or LOCAL_SERIAL got LOCAL_ONE", }, } err := session.Query(fmt.Sprintf("CREATE TABLE IF NOT EXISTS gocql_test.%s (id int PRIMARY KEY)", table)).Exec() if err != nil { t.Fatalf("can't create table:%v", err) } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { if tc.expectedPanicValue == "" { err = session.Query(fmt.Sprintf("INSERT INTO gocql_test.%s (id) VALUES (?)", table), tc.id).SerialConsistency(tc.consistency).Exec() if err != nil { t.Fatal(err) } var receivedID int err = session.Query(fmt.Sprintf("SELECT * FROM gocql_test.%s WHERE id=?", table), tc.id).Scan(&receivedID) if err != nil { t.Fatal(err) } require.Equal(t, tc.id, receivedID) } else { require.PanicsWithValue(t, tc.expectedPanicValue, func() { session.Query(fmt.Sprintf("INSERT INTO gocql_test.%s (id) VALUES (?)", table), tc.id).SerialConsistency(tc.consistency) }) } }) } } func TestDurationType(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() if session.cfg.ProtoVersion < protoVersion5 { t.Skip("Duration type is not supported. Please use protocol version >= 4 and cassandra version >= 3.11") } table := testTableName(t) if err := createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s ( k int primary key, v duration )`, table)); err != nil { t.Fatal("create:", err) } durations := []Duration{ Duration{ Months: 250, Days: 500, Nanoseconds: 300010001, }, Duration{ Months: -250, Days: -500, Nanoseconds: -300010001, }, Duration{ Months: 0, Days: 128, Nanoseconds: 127, }, Duration{ Months: 0x7FFFFFFF, Days: 0x7FFFFFFF, Nanoseconds: 0x7FFFFFFFFFFFFFFF, }, } for _, durationSend := range durations { if err := session.Query(fmt.Sprintf(`INSERT INTO gocql_test.%s (k, v) VALUES (1, ?)`, table), durationSend).Exec(); err != nil { t.Fatal(err) } var id int var duration Duration if err := session.Query(fmt.Sprintf(`SELECT k, v FROM gocql_test.%s`, table)).Scan(&id, &duration); err != nil { t.Fatal(err) } if duration.Months != durationSend.Months || duration.Days != durationSend.Days || duration.Nanoseconds != durationSend.Nanoseconds { t.Fatalf("Unexpeted value returned, expected=%v, received=%v", durationSend, duration) } } } func TestMapScanCAS(t *testing.T) { t.Parallel() session := createSessionFromClusterTabletsDisabled(createCluster(), t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf(`CREATE TABLE %s ( title varchar, revid timeuuid, last_modified timestamp, deleted boolean, PRIMARY KEY (title, revid) )`, table)); err != nil { t.Fatal("create:", err) } title, revid, modified, deleted := "baz", TimeUUID(), time.Now(), false mapCAS := map[string]any{} if applied, err := session.Query(fmt.Sprintf(`INSERT INTO %s (title, revid, last_modified, deleted) VALUES (?, ?, ?, ?) IF NOT EXISTS`, table), title, revid, modified, deleted).MapScanCAS(mapCAS); err != nil { t.Fatal("insert:", err) } else if !applied { t.Fatalf("insert should have been applied: title=%v revID=%v modified=%v", title, revid, modified) } mapCAS = map[string]any{} if applied, err := session.Query(fmt.Sprintf(`INSERT INTO %s (title, revid, last_modified, deleted) VALUES (?, ?, ?, ?) IF NOT EXISTS`, table), title, revid, modified, deleted).MapScanCAS(mapCAS); err != nil { t.Fatal("insert:", err) } else if applied { t.Fatalf("insert should have been applied: title=%v revID=%v modified=%v", title, revid, modified) } else if title != mapCAS["title"] || revid != mapCAS["revid"] || deleted != mapCAS["deleted"] { t.Fatalf("expected %s/%v/%v/%v but got %s/%v/%v%v", title, revid, modified, false, mapCAS["title"], mapCAS["revid"], mapCAS["last_modified"], mapCAS["deleted"]) } } func TestBatch(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s (id int primary key)`, table)); err != nil { t.Fatal("create table:", err) } batch := session.Batch(LoggedBatch) for i := 0; i < 100; i++ { batch.Query(fmt.Sprintf(`INSERT INTO %s (id) VALUES (?)`, table), i) } if err := session.ExecuteBatch(batch); err != nil { t.Fatal("execute batch:", err) } count := 0 if err := session.Query(fmt.Sprintf(`SELECT COUNT(*) FROM %s`, table)).Scan(&count); err != nil { t.Fatal("select count:", err) } else if count != 100 { t.Fatalf("count: expected %d, got %d\n", 100, count) } } func TestUnpreparedBatch(t *testing.T) { t.Parallel() t.Skip("FLAKE skipping") session := createSession(t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s (id int primary key, c counter)`, table)); err != nil { t.Fatal("create table:", err) } batch := session.Batch(UnloggedBatch) for i := 0; i < 100; i++ { batch.Query(fmt.Sprintf(`UPDATE %s SET c = c + 1 WHERE id = 1`, table)) } if err := session.ExecuteBatch(batch); err != nil { t.Fatal("execute batch:", err) } count := 0 if err := session.Query(fmt.Sprintf(`SELECT COUNT(*) FROM %s`, table)).Scan(&count); err != nil { t.Fatal("select count:", err) } else if count != 1 { t.Fatalf("count: expected %d, got %d\n", 100, count) } if err := session.Query(fmt.Sprintf(`SELECT c FROM %s`, table)).Scan(&count); err != nil { t.Fatal("select count:", err) } else if count != 100 { t.Fatalf("count: expected %d, got %d\n", 100, count) } } // TestBatchLimit tests gocql to make sure batch operations larger than the maximum // statement limit are not submitted to a cassandra node. func TestBatchLimit(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s (id int primary key)`, table)); err != nil { t.Fatal("create table:", err) } batch := session.Batch(LoggedBatch) for i := 0; i < 65537; i++ { batch.Query(fmt.Sprintf(`INSERT INTO %s (id) VALUES (?)`, table), i) } if err := session.ExecuteBatch(batch); err != ErrTooManyStmts { t.Fatal("gocql attempted to execute a batch larger than the support limit of statements.") } } func TestWhereIn(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s (id int, cluster int, primary key (id,cluster))`, table)); err != nil { t.Fatal("create table:", err) } if err := session.Query(fmt.Sprintf("INSERT INTO %s (id, cluster) VALUES (?,?)", table), 100, 200).Exec(); err != nil { t.Fatal("insert:", err) } iter := session.Query(fmt.Sprintf("SELECT * FROM %s WHERE id = ? AND cluster IN (?)", table), 100, 200).Iter() var id, cluster int count := 0 for iter.Scan(&id, &cluster) { count++ } if id != 100 || cluster != 200 { t.Fatalf("Was expecting id and cluster to be (100,200) but were (%d,%d)", id, cluster) } } // TestTooManyQueryArgs tests to make sure the library correctly handles the application level bug // whereby too many query arguments are passed to a query func TestTooManyQueryArgs(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s (id int primary key, value int)`, table)); err != nil { t.Fatal("create table:", err) } _, err := session.Query(fmt.Sprintf(`SELECT * FROM %s WHERE id = ?`, table), 1, 2).Iter().SliceMap() if err == nil { t.Fatal("'SELECT * FROM WHERE id = ?, 1, 2' should return an error") } batch := session.Batch(UnloggedBatch) batch.Query(fmt.Sprintf("INSERT INTO %s (id, value) VALUES (?, ?)", table), 1, 2, 3) err = session.ExecuteBatch(batch) if err == nil { t.Fatal("'`INSERT INTO too_many_query_args (id, value) VALUES (?, ?)`, 1, 2, 3' should return an error") } // TODO: should indicate via an error code that it is an invalid arg? } // TestNotEnoughQueryArgs tests to make sure the library correctly handles the application level bug // whereby not enough query arguments are passed to a query func TestNotEnoughQueryArgs(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s (id int, cluster int, value int, primary key (id, cluster))`, table)); err != nil { t.Fatal("create table:", err) } _, err := session.Query(fmt.Sprintf(`SELECT * FROM %s WHERE id = ? and cluster = ?`, table), 1).Iter().SliceMap() if err == nil { t.Fatal("'SELECT * FROM
WHERE id = ? and cluster = ?, 1' should return an error") } batch := session.Batch(UnloggedBatch) batch.Query(fmt.Sprintf("INSERT INTO %s (id, cluster, value) VALUES (?, ?, ?)", table), 1, 2) err = session.ExecuteBatch(batch) if err == nil { t.Fatal("'`INSERT INTO not_enough_query_args (id, cluster, value) VALUES (?, ?, ?)`, 1, 2' should return an error") } } // TestCreateSessionTimeout tests to make sure the CreateSession function timeouts out correctly // and prevents an infinite loop of connection retries. func TestCreateSessionTimeout(t *testing.T) { t.Parallel() ctx, cancel := context.WithCancel(context.Background()) defer cancel() go func() { select { case <-time.After(2 * time.Second): t.Error("no startup timeout") case <-ctx.Done(): } }() cluster := createCluster() cluster.Hosts = []string{"127.0.0.1:1"} session, err := cluster.CreateSession() if err == nil { session.Close() t.Fatal("expected ErrNoConnectionsStarted, but no error was returned.") } } // TestReconnection verifies that a node marked down is eventually reconnected. // WARNING: This test must NOT use t.Parallel(). It calls session.handleNodeDown() // which mutates shared HostInfo state visible to all concurrent sessions. // //nolint:paralleltest // mutates shared HostInfo state via handleNodeDown() func TestReconnection(t *testing.T) { cluster := createCluster() cluster.ReconnectInterval = 1 * time.Second session := createSessionFromCluster(cluster, t) defer session.Close() h := session.hostSource.getHostsList()[0] session.handleNodeDown(h.ConnectAddress(), h.Port()) if h.State() != NodeDown { t.Fatal("Host should be NodeDown but not.") } time.Sleep(cluster.ReconnectInterval + h.Version().nodeUpDelay() + 1*time.Second) if h.State() != NodeUp { t.Fatal("Host should be NodeUp but not. Failed to reconnect.") } } type FullName struct { FirstName string LastName string } func (n FullName) MarshalCQL(info TypeInfo) ([]byte, error) { return []byte(n.FirstName + " " + n.LastName), nil } func (n *FullName) UnmarshalCQL(info TypeInfo, data []byte) error { t := strings.SplitN(string(data), " ", 2) n.FirstName, n.LastName = t[0], t[1] return nil } func TestMapScanWithRefMap(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s ( testtext text PRIMARY KEY, testfullname text, testint int, )`, table)); err != nil { t.Fatal("create table:", err) } m := make(map[string]any) m["testtext"] = "testtext" m["testfullname"] = FullName{FirstName: "John", LastName: "Doe"} m["testint"] = 100 if err := session.Query(fmt.Sprintf(`INSERT INTO %s (testtext, testfullname, testint) values (?,?,?)`, table), m["testtext"], m["testfullname"], m["testint"]).Exec(); err != nil { t.Fatal("insert:", err) } var testText string var testFullName FullName ret := map[string]any{ "testtext": &testText, "testfullname": &testFullName, // testint is not set here. } iter := session.Query(fmt.Sprintf(`SELECT * FROM %s`, table)).Iter() if ok := iter.MapScan(ret); !ok { t.Fatal("select:", iter.Close()) } else { if ret["testtext"] != "testtext" { t.Fatal("returned testtext did not match") } f := ret["testfullname"].(FullName) if f.FirstName != "John" || f.LastName != "Doe" { t.Fatal("returned testfullname did not match") } if ret["testint"] != 100 { t.Fatal("returned testinit did not match") } } if testText != "testtext" { t.Fatal("returned testtext did not match") } if testFullName.FirstName != "John" || testFullName.LastName != "Doe" { t.Fatal("returned testfullname did not match") } // using MapScan to read a nil int value intp := new(int64) ret = map[string]any{ "testint": &intp, } if err := session.Query(fmt.Sprintf("INSERT INTO %s(testtext, testint) VALUES(?, ?)", table), "null-int", nil).Exec(); err != nil { t.Fatal(err) } err := session.Query(fmt.Sprintf(`SELECT testint FROM %s WHERE testtext = ?`, table), "null-int").MapScan(ret) if err != nil { t.Fatal(err) } else if v := ret["testint"].(*int64); v != nil { t.Fatalf("testint should be nil got %+#v", v) } } func TestMapScan(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s ( fullname text PRIMARY KEY, age int, address inet, data blob, )`, table)); err != nil { t.Fatal("create table:", err) } if err := session.Query(fmt.Sprintf(`INSERT INTO %s (fullname, age, address) values (?,?,?)`, table), "Grace Hopper", 31, net.ParseIP("10.0.0.1")).Exec(); err != nil { t.Fatal("insert:", err) } if err := session.Query(fmt.Sprintf(`INSERT INTO %s (fullname, age, address, data) values (?,?,?,?)`, table), "Ada Lovelace", 30, net.ParseIP("10.0.0.2"), []byte(`{"foo": "bar"}`)).Exec(); err != nil { t.Fatal("insert:", err) } iter := session.Query(fmt.Sprintf(`SELECT * FROM %s`, table)).Iter() // First iteration row := make(map[string]any) if !iter.MapScan(row) { t.Fatal("select:", iter.Close()) } tests.AssertEqual(t, "fullname", "Ada Lovelace", row["fullname"]) tests.AssertEqual(t, "age", 30, row["age"]) tests.AssertEqual(t, "address", "10.0.0.2", row["address"]) tests.AssertDeepEqual(t, "data", []byte(`{"foo": "bar"}`), row["data"]) // Second iteration using a new map row = make(map[string]any) if !iter.MapScan(row) { t.Fatal("select:", iter.Close()) } tests.AssertEqual(t, "fullname", "Grace Hopper", row["fullname"]) tests.AssertEqual(t, "age", 31, row["age"]) tests.AssertEqual(t, "address", "10.0.0.1", row["address"]) tests.AssertDeepEqual(t, "data", []byte(nil), row["data"]) } func TestSliceMap(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s ( testuuid timeuuid PRIMARY KEY, testtimestamp timestamp, testvarchar varchar, testbigint bigint, testblob blob, testbool boolean, testfloat float, testdouble double, testint int, testdecimal decimal, testlist list, testset set, testmap map, testvarint varint, testinet inet )`, table)); err != nil { t.Fatal("create table:", err) } m := make(map[string]any) bigInt := new(big.Int) if _, ok := bigInt.SetString("830169365738487321165427203929228", 10); !ok { t.Fatal("Failed setting bigint by string") } m["testuuid"] = TimeUUID() m["testvarchar"] = "Test VarChar" m["testbigint"] = time.Now().Unix() m["testtimestamp"] = time.Now().Truncate(time.Millisecond).UTC() m["testblob"] = []byte("test blob") m["testbool"] = true m["testfloat"] = float32(4.564) m["testdouble"] = float64(4.815162342) m["testint"] = 2343 m["testdecimal"] = inf.NewDec(100, 0) m["testlist"] = []string{"quux", "foo", "bar", "baz", "quux"} m["testset"] = []int{1, 2, 3, 4, 5, 6, 7, 8, 9} m["testmap"] = map[string]string{"field1": "val1", "field2": "val2", "field3": "val3"} m["testvarint"] = bigInt m["testinet"] = "213.212.2.19" sliceMap := []map[string]any{m} if err := session.Query(fmt.Sprintf(`INSERT INTO %s (testuuid, testtimestamp, testvarchar, testbigint, testblob, testbool, testfloat, testdouble, testint, testdecimal, testlist, testset, testmap, testvarint, testinet) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, table), m["testuuid"], m["testtimestamp"], m["testvarchar"], m["testbigint"], m["testblob"], m["testbool"], m["testfloat"], m["testdouble"], m["testint"], m["testdecimal"], m["testlist"], m["testset"], m["testmap"], m["testvarint"], m["testinet"]).Exec(); err != nil { t.Fatal("insert:", err) } if returned, retErr := session.Query(fmt.Sprintf(`SELECT * FROM %s`, table)).Iter().SliceMap(); retErr != nil { t.Fatal("select:", retErr) } else { matchSliceMap(t, sliceMap, returned[0]) } // Test for Iter.MapScan() { testMap := make(map[string]any) if !session.Query(fmt.Sprintf(`SELECT * FROM %s`, table)).Iter().MapScan(testMap) { t.Fatal("MapScan failed to work with one row") } matchSliceMap(t, sliceMap, testMap) } // Test for Query.MapScan() { testMap := make(map[string]any) if session.Query(fmt.Sprintf(`SELECT * FROM %s`, table)).MapScan(testMap) != nil { t.Fatal("MapScan failed to work with one row") } matchSliceMap(t, sliceMap, testMap) } } func matchSliceMap(t *testing.T, sliceMap []map[string]any, testMap map[string]any) { if sliceMap[0]["testuuid"] != testMap["testuuid"] { t.Fatal("returned testuuid did not match") } if sliceMap[0]["testtimestamp"] != testMap["testtimestamp"] { t.Fatal("returned testtimestamp did not match") } if sliceMap[0]["testvarchar"] != testMap["testvarchar"] { t.Fatal("returned testvarchar did not match") } if sliceMap[0]["testbigint"] != testMap["testbigint"] { t.Fatal("returned testbigint did not match") } if !reflect.DeepEqual(sliceMap[0]["testblob"], testMap["testblob"]) { t.Fatal("returned testblob did not match") } if sliceMap[0]["testbool"] != testMap["testbool"] { t.Fatal("returned testbool did not match") } if sliceMap[0]["testfloat"] != testMap["testfloat"] { t.Fatal("returned testfloat did not match") } if sliceMap[0]["testdouble"] != testMap["testdouble"] { t.Fatal("returned testdouble did not match") } if sliceMap[0]["testinet"] != testMap["testinet"] { t.Fatal("returned testinet did not match") } expectedDecimal := sliceMap[0]["testdecimal"].(*inf.Dec) returnedDecimal := testMap["testdecimal"].(*inf.Dec) if expectedDecimal.Cmp(returnedDecimal) != 0 { t.Fatal("returned testdecimal did not match") } if !reflect.DeepEqual(sliceMap[0]["testlist"], testMap["testlist"]) { t.Fatal("returned testlist did not match") } if !reflect.DeepEqual(sliceMap[0]["testset"], testMap["testset"]) { t.Fatal("returned testset did not match") } if !reflect.DeepEqual(sliceMap[0]["testmap"], testMap["testmap"]) { t.Fatal("returned testmap did not match") } if sliceMap[0]["testint"] != testMap["testint"] { t.Fatal("returned testint did not match") } } type MyRetryPolicy struct { } func (*MyRetryPolicy) Attempt(q RetryableQuery) bool { if q.Attempts() > 5 { return false } return true } func (*MyRetryPolicy) GetRetryType(err error) RetryType { var executedErr *QueryError if errors.As(err, &executedErr) && !executedErr.IsIdempotent() { return Ignore } return Retry } func Test_RetryPolicyIdempotence(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() testCases := []struct { name string idempotency bool expectedNumberOfTries int }{ { name: "with retry", idempotency: true, expectedNumberOfTries: 6, }, { name: "without retry", idempotency: false, expectedNumberOfTries: 1, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { q := session.Query("INSERT INTO gocql_test.not_existing_table(event_id, time, args) VALUES (?,?,?)", 4, UUIDFromTime(time.Now()), "test") q.Idempotent(tc.idempotency) q.RetryPolicy(&MyRetryPolicy{}) q.Consistency(All) _ = q.Exec() require.Equal(t, tc.expectedNumberOfTries, q.Attempts()) }) } } func TestSmallInt(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s ( testsmallint smallint PRIMARY KEY, )`, table)); err != nil { t.Fatal("create table:", err) } m := make(map[string]any) m["testsmallint"] = int16(2) sliceMap := []map[string]any{m} if err := session.Query(fmt.Sprintf(`INSERT INTO %s (testsmallint) VALUES (?)`, table), m["testsmallint"]).Exec(); err != nil { t.Fatal("insert:", err) } if returned, retErr := session.Query(fmt.Sprintf(`SELECT * FROM %s`, table)).Iter().SliceMap(); retErr != nil { t.Fatal("select:", retErr) } else { if sliceMap[0]["testsmallint"] != returned[0]["testsmallint"] { t.Fatal("returned testsmallint did not match") } } } func TestScanWithNilArguments(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s ( foo varchar, bar int, PRIMARY KEY (foo, bar) )`, table)); err != nil { t.Fatal("create:", err) } for i := 1; i <= 20; i++ { if err := session.Query(fmt.Sprintf("INSERT INTO %s (foo, bar) VALUES (?, ?)", table), "squares", i*i).Exec(); err != nil { t.Fatal("insert:", err) } } iter := session.Query(fmt.Sprintf("SELECT * FROM %s WHERE foo = ?", table), "squares").Iter() var n int count := 0 for iter.Scan(nil, &n) { count += n } if err := iter.Close(); err != nil { t.Fatal("close:", err) } if count != 2870 { t.Fatalf("expected %d, got %d", 2870, count) } } func TestScanCASWithNilArguments(t *testing.T) { t.Parallel() session := createSessionFromClusterTabletsDisabled(createCluster(), t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf(`CREATE TABLE %s ( foo varchar, bar varchar, PRIMARY KEY (foo, bar) )`, table)); err != nil { t.Fatal("create:", err) } foo := "baz" var cas string if applied, err := session.Query(fmt.Sprintf(`INSERT INTO %s (foo, bar) VALUES (?, ?) IF NOT EXISTS`, table), foo, foo).ScanCAS(nil, nil); err != nil { t.Fatal("insert:", err) } else if !applied { t.Fatal("insert should have been applied") } if applied, err := session.Query(fmt.Sprintf(`INSERT INTO %s (foo, bar) VALUES (?, ?) IF NOT EXISTS`, table), foo, foo).ScanCAS(&cas, nil); err != nil { t.Fatal("insert:", err) } else if applied { t.Fatal("insert should not have been applied") } else if foo != cas { t.Fatalf("expected %v but got %v", foo, cas) } if applied, err := session.Query(fmt.Sprintf(`INSERT INTO %s (foo, bar) VALUES (?, ?) IF NOT EXISTS`, table), foo, foo).ScanCAS(nil, &cas); err != nil { t.Fatal("insert:", err) } else if applied { t.Fatal("insert should not have been applied") } else if foo != cas { t.Fatalf("expected %v but got %v", foo, cas) } } func TestRebindQueryInfo(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf("CREATE TABLE gocql_test.%s (id int, value text, PRIMARY KEY (id))", table)); err != nil { t.Fatalf("failed to create table with error '%v'", err) } if err := session.Query(fmt.Sprintf("INSERT INTO %s (id, value) VALUES (?, ?)", table), 23, "quux").Exec(); err != nil { t.Fatalf("insert into rebind_query failed, err '%v'", err) } if err := session.Query(fmt.Sprintf("INSERT INTO %s (id, value) VALUES (?, ?)", table), 24, "w00t").Exec(); err != nil { t.Fatalf("insert into rebind_query failed, err '%v'", err) } q := session.Query(fmt.Sprintf("SELECT value FROM %s WHERE ID = ?", table)) q.Bind(23) iter := q.Iter() var value string for iter.Scan(&value) { } if value != "quux" { t.Fatalf("expected %v but got %v", "quux", value) } q.Bind(24) iter = q.Iter() for iter.Scan(&value) { } if value != "w00t" { t.Fatalf("expected %v but got %v", "w00t", value) } } // TestStaticQueryInfo makes sure that the application can manually bind query parameters using the simplest possible static binding strategy func TestStaticQueryInfo(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf("CREATE TABLE gocql_test.%s (id int, value text, PRIMARY KEY (id))", table)); err != nil { t.Fatalf("failed to create table with error '%v'", err) } if err := session.Query(fmt.Sprintf("INSERT INTO %s (id, value) VALUES (?, ?)", table), 113, "foo").Exec(); err != nil { t.Fatalf("insert into static_query_info failed, err '%v'", err) } autobinder := func(q *QueryInfo) ([]any, error) { values := make([]any, 1) values[0] = 113 return values, nil } qry := session.Bind(fmt.Sprintf("SELECT id, value FROM %s WHERE id = ?", table), autobinder) if err := qry.Exec(); err != nil { t.Fatalf("expose query info failed, error '%v'", err) } iter := qry.Iter() var id int var value string iter.Scan(&id, &value) if err := iter.Close(); err != nil { t.Fatalf("query with exposed info failed, err '%v'", err) } if value != "foo" { t.Fatalf("Expected value %s, but got %s", "foo", value) } } type ClusteredKeyValue struct { Id int Cluster int Value string } func (kv *ClusteredKeyValue) Bind(q *QueryInfo) ([]any, error) { values := make([]any, len(q.Args)) for i, info := range q.Args { fieldName := upcaseInitial(info.Name) value := reflect.ValueOf(kv) field := reflect.Indirect(value).FieldByName(fieldName) values[i] = field.Addr().Interface() } return values, nil } func upcaseInitial(str string) string { for i, v := range str { return string(unicode.ToUpper(v)) + str[i+1:] } return "" } // TestBoundQueryInfo makes sure that the application can manually bind query parameters using the query meta data supplied at runtime func TestBoundQueryInfo(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf("CREATE TABLE gocql_test.%s (id int, cluster int, value text, PRIMARY KEY (id, cluster))", table)); err != nil { t.Fatalf("failed to create table with error '%v'", err) } write := &ClusteredKeyValue{Id: 200, Cluster: 300, Value: "baz"} insert := session.Bind(fmt.Sprintf("INSERT INTO %s (id, cluster, value) VALUES (?, ?,?)", table), write.Bind) if err := insert.Exec(); err != nil { t.Fatalf("insert into clustered_query_info failed, err '%v'", err) } read := &ClusteredKeyValue{Id: 200, Cluster: 300} qry := session.Bind(fmt.Sprintf("SELECT id, cluster, value FROM %s WHERE id = ? and cluster = ?", table), read.Bind) iter := qry.Iter() var id, cluster int var value string iter.Scan(&id, &cluster, &value) if err := iter.Close(); err != nil { t.Fatalf("query with clustered_query_info info failed, err '%v'", err) } if value != "baz" { t.Fatalf("Expected value %s, but got %s", "baz", value) } } // TestBatchQueryInfo makes sure that the application can manually bind query parameters when executing in a batch func TestBatchQueryInfo(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf("CREATE TABLE gocql_test.%s (id int, cluster int, value text, PRIMARY KEY (id, cluster))", table)); err != nil { t.Fatalf("failed to create table with error '%v'", err) } write := func(q *QueryInfo) ([]any, error) { values := make([]any, 3) values[0] = 4000 values[1] = 5000 values[2] = "bar" return values, nil } batch := session.Batch(LoggedBatch) batch.Bind(fmt.Sprintf("INSERT INTO %s (id, cluster, value) VALUES (?, ?,?)", table), write) if err := session.ExecuteBatch(batch); err != nil { t.Fatalf("batch insert into batch_query_info failed, err '%v'", err) } read := func(q *QueryInfo) ([]any, error) { values := make([]any, 2) values[0] = 4000 values[1] = 5000 return values, nil } qry := session.Bind(fmt.Sprintf("SELECT id, cluster, value FROM %s WHERE id = ? and cluster = ?", table), read) iter := qry.Iter() var id, cluster int var value string iter.Scan(&id, &cluster, &value) if err := iter.Close(); err != nil { t.Fatalf("query with batch_query_info info failed, err '%v'", err) } if value != "bar" { t.Fatalf("Expected value %s, but got %s", "bar", value) } } func getRandomConn(t *testing.T, session *Session) *Conn { conn := session.getConn() if conn == nil { t.Fatal("unable to get a connection") } return conn } func injectInvalidPreparedStatement(t *testing.T, session *Session, table string) (string, *Conn) { if err := createTable(session, `CREATE TABLE gocql_test.`+table+` ( foo varchar, bar int, PRIMARY KEY (foo, bar) )`); err != nil { t.Fatal("create:", err) } stmt := "INSERT INTO " + table + " (foo, bar) VALUES (?, 7)" conn := getRandomConn(t, session) flight := new(inflightPrepare) key := session.stmtsLRU.keyFor(conn.host.HostID(), "", stmt) session.stmtsLRU.add(key, flight) flight.preparedStatment = &preparedStatment{ id: []byte{'f', 'o', 'o', 'b', 'a', 'r'}, request: preparedMetadata{ resultMetadata: resultMetadata{ colCount: 1, actualColCount: 1, columns: []ColumnInfo{ { Keyspace: "gocql_test", Table: table, Name: "foo", TypeInfo: NativeType{ typ: TypeVarchar, }, }, }, }, }, } return stmt, conn } func TestPrepare_MissingSchemaPrepare(t *testing.T) { t.Parallel() ctx, cancel := context.WithCancel(context.Background()) defer cancel() s := createSession(t) conn := getRandomConn(t, s) defer s.Close() table := testTableName(t) insertQry := s.Query(fmt.Sprintf("INSERT INTO %s (val) VALUES (?)", table), 5) if err := conn.executeQuery(ctx, insertQry).err; err == nil { t.Fatal("expected error, but got nil.") } if err := createTable(s, fmt.Sprintf("CREATE TABLE gocql_test.%s (val int, PRIMARY KEY (val))", table)); err != nil { t.Fatal("create table:", err) } if err := conn.executeQuery(ctx, insertQry).err; err != nil { t.Fatal(err) // unconfigured columnfamily } } func TestPrepare_ReprepareStatement(t *testing.T) { t.Parallel() ctx, cancel := context.WithCancel(context.Background()) defer cancel() session := createSession(t) defer session.Close() table := testTableName(t) stmt, conn := injectInvalidPreparedStatement(t, session, table) query := session.Query(stmt, "bar") if err := conn.executeQuery(ctx, query).Close(); err != nil { t.Fatalf("Failed to execute query for reprepare statement: %v", err) } } func TestPrepare_ReprepareBatch(t *testing.T) { t.Parallel() ctx, cancel := context.WithCancel(context.Background()) defer cancel() session := createSession(t) defer session.Close() table := testTableName(t) stmt, conn := injectInvalidPreparedStatement(t, session, table) batch := session.Batch(UnloggedBatch) batch.Query(stmt, "bar") if err := conn.executeBatch(ctx, batch).Close(); err != nil { t.Fatalf("Failed to execute query for reprepare statement: %v", err) } } func TestQueryInfo(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() conn := getRandomConn(t, session) info, err := conn.prepareStatement(context.Background(), "SELECT release_version, host_id FROM system.local WHERE key = ?", nil, time.Second) if err != nil { t.Fatalf("Failed to execute query for preparing statement: %v", err) } if x := len(info.request.columns); x != 1 { t.Fatalf("Was not expecting meta data for %d query arguments, but got %d\n", 1, x) } if x := len(info.response.columns); x != 2 { t.Fatalf("Was not expecting meta data for %d result columns, but got %d\n", 2, x) } } // TestPreparedCacheEviction will make sure that the cache size is maintained func TestPrepare_PreparedCacheEviction(t *testing.T) { t.Parallel() const maxPrepared = 4 clusterHosts := getClusterHosts() host := clusterHosts[0] cluster := createCluster() cluster.MaxPreparedStmts = maxPrepared cluster.Events.DisableSchemaEvents = true cluster.Hosts = []string{host} cluster.HostFilter = WhiteListHostFilter(host) session := createSessionFromCluster(cluster, t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf("CREATE TABLE gocql_test.%s (id int,mod int,PRIMARY KEY (id))", table)); err != nil { t.Fatalf("failed to create table with error '%v'", err) } // clear the cache session.stmtsLRU.clear() //Fill the table for i := 0; i < 2; i++ { if err := session.Query(fmt.Sprintf("INSERT INTO %s (id,mod) VALUES (?, ?)", table), i, 10000%(i+1)).Exec(); err != nil { t.Fatalf("insert into prepcachetest failed, err '%v'", err) } } //Populate the prepared statement cache with select statements var id, mod int for i := 0; i < 2; i++ { err := session.Query(fmt.Sprintf("SELECT id,mod FROM %s WHERE id = ", table)+strconv.FormatInt(int64(i), 10)).Scan(&id, &mod) if err != nil { t.Fatalf("select from prepcachetest failed, error '%v'", err) } } //generate an update statement to test they are prepared err := session.Query(fmt.Sprintf("UPDATE %s SET mod = ? WHERE id = ?", table), 1, 11).Exec() if err != nil { t.Fatalf("update prepcachetest failed, error '%v'", err) } //generate a delete statement to test they are prepared err = session.Query(fmt.Sprintf("DELETE FROM %s WHERE id = ?", table), 1).Exec() if err != nil { t.Fatalf("delete from prepcachetest failed, error '%v'", err) } //generate an insert statement to test they are prepared err = session.Query(fmt.Sprintf("INSERT INTO %s (id,mod) VALUES (?, ?)", table), 3, 11).Exec() if err != nil { t.Fatalf("insert into prepcachetest failed, error '%v'", err) } session.stmtsLRU.mu.Lock() defer session.stmtsLRU.mu.Unlock() //Make sure the cache size is maintained if session.stmtsLRU.lru.Len() != session.stmtsLRU.lru.MaxEntries { t.Fatalf("expected cache size of %v, got %v", session.stmtsLRU.lru.MaxEntries, session.stmtsLRU.lru.Len()) } // Walk through all the configured hosts and test cache retention and eviction for _, host := range session.hostSource.hosts { _, ok := session.stmtsLRU.lru.Get(session.stmtsLRU.keyFor(host.HostID(), session.cfg.Keyspace, fmt.Sprintf("SELECT id,mod FROM %s WHERE id = 0", table))) if ok { t.Errorf("expected first select to be purged but was in cache for host=%q", host) } _, ok = session.stmtsLRU.lru.Get(session.stmtsLRU.keyFor(host.HostID(), session.cfg.Keyspace, fmt.Sprintf("SELECT id,mod FROM %s WHERE id = 1", table))) if !ok { t.Errorf("exepected second select to be in cache for host=%q", host) } _, ok = session.stmtsLRU.lru.Get(session.stmtsLRU.keyFor(host.HostID(), session.cfg.Keyspace, fmt.Sprintf("INSERT INTO %s (id,mod) VALUES (?, ?)", table))) if !ok { t.Errorf("expected insert to be in cache for host=%q", host) } _, ok = session.stmtsLRU.lru.Get(session.stmtsLRU.keyFor(host.HostID(), session.cfg.Keyspace, fmt.Sprintf("UPDATE %s SET mod = ? WHERE id = ?", table))) if !ok { t.Errorf("expected update to be in cached for host=%q", host) } _, ok = session.stmtsLRU.lru.Get(session.stmtsLRU.keyFor(host.HostID(), session.cfg.Keyspace, fmt.Sprintf("DELETE FROM %s WHERE id = ?", table))) if !ok { t.Errorf("expected delete to be cached for host=%q", host) } } } func TestPrepare_PreparedCacheKey(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) // create a second keyspace with a unique name to avoid collisions under parallel execution ks2 := testKeyspaceName(t, "ks2") cluster2 := createCluster() createKeyspace(t, cluster2, ks2, false) cluster2.Keyspace = ks2 session2, err := cluster2.CreateSession() if err != nil { t.Fatal("create session:", err) } defer session2.Close() // both keyspaces have a table named "test_stmt_cache_key" if err := createTable(session, fmt.Sprintf("CREATE TABLE gocql_test.%s (id varchar primary key, field varchar)", table)); err != nil { t.Fatal("create table:", err) } if err := createTable(session2, fmt.Sprintf("CREATE TABLE %s.%s (id varchar primary key, field varchar)", ks2, table)); err != nil { t.Fatal("create table:", err) } // both tables have a single row with the same partition key but different column value if err = session.Query(fmt.Sprintf(`INSERT INTO %s (id, field) VALUES (?, ?)`, table), "key", "one").Exec(); err != nil { t.Fatal("insert:", err) } if err = session2.Query(fmt.Sprintf(`INSERT INTO %s (id, field) VALUES (?, ?)`, table), "key", "two").Exec(); err != nil { t.Fatal("insert:", err) } // should be able to see different values in each keyspace var value string if err = session.Query(fmt.Sprintf("SELECT field FROM %s WHERE id = ?", table), "key").Scan(&value); err != nil { t.Fatal("select:", err) } if value != "one" { t.Errorf("Expected one, got %s", value) } if err = session2.Query(fmt.Sprintf("SELECT field FROM %s WHERE id = ?", table), "key").Scan(&value); err != nil { t.Fatal("select:", err) } if value != "two" { t.Errorf("Expected two, got %s", value) } } // TestMarshalFloat64Ptr tests to see that a pointer to a float64 is marshalled correctly. func TestMarshalFloat64Ptr(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf("CREATE TABLE gocql_test.%s (id double, test double, primary key (id))", table)); err != nil { t.Fatal("create table:", err) } testNum := float64(7500) if err := session.Query(fmt.Sprintf(`INSERT INTO %s (id,test) VALUES (?,?)`, table), float64(7500.00), &testNum).Exec(); err != nil { t.Fatal("insert float64:", err) } } // TestMarshalInet tests to see that a pointer to a float64 is marshalled correctly. func TestMarshalInet(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf("CREATE TABLE gocql_test.%s (ip inet, name text, primary key (ip))", table)); err != nil { t.Fatal("create table:", err) } stringIp := "123.34.45.56" if err := session.Query(fmt.Sprintf(`INSERT INTO %s (ip,name) VALUES (?,?)`, table), stringIp, "Test IP 1").Exec(); err != nil { t.Fatal("insert string inet:", err) } var stringResult string if err := session.Query(fmt.Sprintf("SELECT ip FROM %s", table)).Scan(&stringResult); err != nil { t.Fatalf("select for string from table 1 failed: %v", err) } if stringResult != stringIp { t.Errorf("Expected %s, was %s", stringIp, stringResult) } var ipResult net.IP if err := session.Query(fmt.Sprintf("SELECT ip FROM %s", table)).Scan(&ipResult); err != nil { t.Fatalf("select for net.IP from table 1 failed: %v", err) } if ipResult.String() != stringIp { t.Errorf("Expected %s, was %s", stringIp, ipResult.String()) } if err := session.Query(fmt.Sprintf(`DELETE FROM %s WHERE ip = ?`, table), stringIp).Exec(); err != nil { t.Fatal("delete inet table:", err) } netIp := net.ParseIP("222.43.54.65") if err := session.Query(fmt.Sprintf(`INSERT INTO %s (ip,name) VALUES (?,?)`, table), netIp, "Test IP 2").Exec(); err != nil { t.Fatal("insert netIp inet:", err) } if err := session.Query(fmt.Sprintf("SELECT ip FROM %s", table)).Scan(&stringResult); err != nil { t.Fatalf("select for string from table 2 failed: %v", err) } if stringResult != netIp.String() { t.Errorf("Expected %s, was %s", netIp.String(), stringResult) } if err := session.Query(fmt.Sprintf("SELECT ip FROM %s", table)).Scan(&ipResult); err != nil { t.Fatalf("select for net.IP from table 2 failed: %v", err) } if ipResult.String() != netIp.String() { t.Errorf("Expected %s, was %s", netIp.String(), ipResult.String()) } } func TestVarint(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf("CREATE TABLE gocql_test.%s (id varchar, test varint, test2 varint, primary key (id))", table)); err != nil { t.Fatalf("failed to create table with error '%v'", err) } if err := session.Query(fmt.Sprintf(`INSERT INTO %s (id, test) VALUES (?, ?)`, table), "id", 0).Exec(); err != nil { t.Fatalf("insert varint: %v", err) } var result int if err := session.Query(fmt.Sprintf("SELECT test FROM %s", table)).Scan(&result); err != nil { t.Fatalf("select failed: %v", err) } if result != 0 { t.Errorf("Expected 0, was %d", result) } if err := session.Query(fmt.Sprintf(`INSERT INTO %s (id, test) VALUES (?, ?)`, table), "id", -1).Exec(); err != nil { t.Fatalf("insert varint: %v", err) } if err := session.Query(fmt.Sprintf("SELECT test FROM %s", table)).Scan(&result); err != nil { t.Fatalf("select failed: %v", err) } if result != -1 { t.Errorf("Expected -1, was %d", result) } if err := session.Query(fmt.Sprintf(`INSERT INTO %s (id, test) VALUES (?, ?)`, table), "id", nil).Exec(); err != nil { t.Fatalf("insert varint: %v", err) } if err := session.Query(fmt.Sprintf("SELECT test FROM %s", table)).Scan(&result); err != nil { t.Fatalf("select failed: %v", err) } if result != 0 { t.Errorf("Expected 0, was %d", result) } var nullableResult *int if err := session.Query(fmt.Sprintf("SELECT test FROM %s", table)).Scan(&nullableResult); err != nil { t.Fatalf("select failed: %v", err) } if nullableResult != nil { t.Errorf("Expected nil, was %d", nullableResult) } if err := session.Query(fmt.Sprintf(`INSERT INTO %s (id, test) VALUES (?, ?)`, table), "id", int64(math.MaxInt32)+1).Exec(); err != nil { t.Fatalf("insert varint: %v", err) } var result64 int64 if err := session.Query(fmt.Sprintf("SELECT test FROM %s", table)).Scan(&result64); err != nil { t.Fatalf("select failed: %v", err) } if result64 != int64(math.MaxInt32)+1 { t.Errorf("Expected %d, was %d", int64(math.MaxInt32)+1, result64) } biggie := new(big.Int) biggie.SetString("36893488147419103232", 10) // > 2**64 if err := session.Query(fmt.Sprintf(`INSERT INTO %s (id, test) VALUES (?, ?)`, table), "id", biggie).Exec(); err != nil { t.Fatalf("insert varint: %v", err) } resultBig := new(big.Int) if err := session.Query(fmt.Sprintf("SELECT test FROM %s", table)).Scan(resultBig); err != nil { t.Fatalf("select failed: %v", err) } if resultBig.String() != biggie.String() { t.Errorf("Expected %s, was %s", biggie.String(), resultBig.String()) } err := session.Query(fmt.Sprintf("SELECT test FROM %s", table)).Scan(&result64) if err == nil || strings.Index(err.Error(), "the data value should be in the int64 range") == -1 { t.Errorf("expected out of range error since value is too big for int64, result:%d", result64) } // value not set in cassandra, leave bind variable empty resultBig = new(big.Int) if err := session.Query(fmt.Sprintf("SELECT test2 FROM %s", table)).Scan(resultBig); err != nil { t.Fatalf("select failed: %v", err) } if resultBig.Int64() != 0 { t.Errorf("Expected %s, was %s", biggie.String(), resultBig.String()) } // can use double pointer to explicitly detect value is not set in cassandra if err := session.Query(fmt.Sprintf("SELECT test2 FROM %s", table)).Scan(&resultBig); err != nil { t.Fatalf("select failed: %v", err) } if resultBig != nil { t.Errorf("Expected %v, was %v", nil, *resultBig) } } // TestQueryStats confirms that the stats are returning valid data. Accuracy may be questionable. func TestQueryStats(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() qry := session.Query("SELECT * FROM system.peers") if err := qry.Exec(); err != nil { t.Fatalf("query failed. %v", err) } else { if qry.Attempts() < 1 { t.Fatal("expected at least 1 attempt, but got 0") } if qry.Latency() <= 0 { t.Fatalf("expected latency to be greater than 0, but got %v instead.", qry.Latency()) } } } // TestIterHosts confirms that host is added to Iter when the query succeeds. func TestIterHost(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() iter := session.Query("SELECT * FROM system.peers").Iter() // check if Host method works if iter.Host() == nil { t.Error("No host in iter") } } // TestBatchStats confirms that the stats are returning valid data. Accuracy may be questionable. func TestBatchStats(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf("CREATE TABLE gocql_test.%s (id int, PRIMARY KEY (id))", table)); err != nil { t.Fatalf("failed to create table with error '%v'", err) } b := session.Batch(LoggedBatch) b.Query(fmt.Sprintf("INSERT INTO %s (id) VALUES (?)", table), 1) b.Query(fmt.Sprintf("INSERT INTO %s (id) VALUES (?)", table), 2) if err := session.ExecuteBatch(b); err != nil { t.Fatalf("query failed. %v", err) } else { if b.Attempts() < 1 { t.Fatal("expected at least 1 attempt, but got 0") } if b.Latency() <= 0 { t.Fatalf("expected latency to be greater than 0, but got %v instead.", b.Latency()) } } } type funcBatchObserver func(context.Context, ObservedBatch) func (f funcBatchObserver) ObserveBatch(ctx context.Context, o ObservedBatch) { f(ctx, o) } func TestBatchObserve(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s (id int, other int, PRIMARY KEY (id))`, table)); err != nil { t.Fatal("create table:", err) } type observation struct { observedErr error observedKeyspace string observedStmts []string observedValues [][]any } var observedBatch *observation batch := session.Batch(LoggedBatch) batch.Observer(funcBatchObserver(func(ctx context.Context, o ObservedBatch) { if observedBatch != nil { t.Fatal("batch observe called more than once") } observedBatch = &observation{ observedKeyspace: o.Keyspace, observedStmts: o.Statements, observedErr: o.Err, observedValues: o.Values, } })) for i := 0; i < 100; i++ { // hard coding 'i' into one of the values for better testing of observation batch.Query(fmt.Sprintf(`INSERT INTO %s (id,other) VALUES (?,%d)`, table, i), i) } if err := session.ExecuteBatch(batch); err != nil { t.Fatal("execute batch:", err) } if observedBatch == nil { t.Fatal("batch observation has not been called") } if len(observedBatch.observedStmts) != 100 { t.Fatal("expecting 100 observed statements, got", len(observedBatch.observedStmts)) } if observedBatch.observedErr != nil { t.Fatal("not expecting to observe an error", observedBatch.observedErr) } if observedBatch.observedKeyspace != "gocql_test" { t.Fatalf("expecting keyspace 'gocql_test', got %q", observedBatch.observedKeyspace) } for i, stmt := range observedBatch.observedStmts { if stmt != fmt.Sprintf(`INSERT INTO %s (id,other) VALUES (?,%d)`, table, i) { t.Fatal("unexpected query", stmt) } tests.AssertDeepEqual(t, "observed value", []any{i}, observedBatch.observedValues[i]) } } // TestNilInQuery tests to see that a nil value passed to a query is handled by Cassandra // TODO validate the nil value by reading back the nil. Need to fix Unmarshalling. func TestNilInQuery(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf("CREATE TABLE gocql_test.%s (id int, count int, PRIMARY KEY (id))", table)); err != nil { t.Fatalf("failed to create table with error '%v'", err) } if err := session.Query(fmt.Sprintf("INSERT INTO %s (id,count) VALUES (?,?)", table), 1, nil).Exec(); err != nil { t.Fatalf("failed to insert with err: %v", err) } var id int if err := session.Query(fmt.Sprintf("SELECT id FROM %s", table)).Scan(&id); err != nil { t.Fatalf("failed to select with err: %v", err) } else if id != 1 { t.Fatalf("expected id to be 1, got %v", id) } } // Don't initialize time.Time bind variable if cassandra timestamp column is empty func TestEmptyTimestamp(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf("CREATE TABLE gocql_test.%s (id int, time timestamp, num int, PRIMARY KEY (id))", table)); err != nil { t.Fatalf("failed to create table with error '%v'", err) } if err := session.Query(fmt.Sprintf("INSERT INTO %s (id, num) VALUES (?,?)", table), 1, 561).Exec(); err != nil { t.Fatalf("failed to insert with err: %v", err) } var timeVal time.Time if err := session.Query(fmt.Sprintf("SELECT time FROM %s where id = ?", table), 1).Scan(&timeVal); err != nil { t.Fatalf("failed to select with err: %v", err) } if !timeVal.IsZero() { t.Errorf("time.Time bind variable should be zero (was %s)", timeVal) } } // Integration test of just querying for data from the system.schema_keyspace table where the keyspace DOES exist. func TestGetKeyspaceMetadata(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() keyspaceMetadata, err := getKeyspaceMetadata(session, "gocql_test") if err != nil { t.Fatalf("failed to query the keyspace metadata with err: %v", err) } if keyspaceMetadata == nil { t.Fatal("failed to query the keyspace metadata, nil returned") } if keyspaceMetadata.Name != "gocql_test" { t.Errorf("Expected keyspace name to be 'gocql' but was '%s'", keyspaceMetadata.Name) } if keyspaceMetadata.StrategyClass != "org.apache.cassandra.locator.NetworkTopologyStrategy" { t.Errorf("Expected replication strategy class to be 'org.apache.cassandra.locator.NetworkTopologyStrategy' but was '%s'", keyspaceMetadata.StrategyClass) } if keyspaceMetadata.StrategyOptions == nil { t.Error("Expected replication strategy options map but was nil") } rfStr, ok := keyspaceMetadata.StrategyOptions["datacenter1"] if !ok { t.Fatalf("Expected strategy option 'datacenter1' but was not found in %v", keyspaceMetadata.StrategyOptions) } rfInt, err := strconv.Atoi(rfStr.(string)) if err != nil { t.Fatalf("Error converting string to int with err: %v", err) } if rfInt != *flagRF { t.Errorf("Expected replication factor to be %d but was %d", *flagRF, rfInt) } } func TestSessionMetadataAPIs(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() const ks = "gocql_test" if _, err := session.KeyspaceMetadata(ks); err != nil { t.Fatalf("failed to get initial keyspace metadata: %v", err) } waitForSchemaRefresh := func() { if err := session.control.awaitSchemaAgreement(); err != nil { t.Logf("schema agreement warning: %v", err) } session.metadataDescriber.invalidateKeyspaceSchema(ks) } t.Run("TableMetadata", func(t *testing.T) { t.Run("basic_table_after_create", func(t *testing.T) { table := testTableName(t) if err := createTable(session, fmt.Sprintf( "CREATE TABLE IF NOT EXISTS %s.%s (pk int PRIMARY KEY, v int)", ks, table)); err != nil { t.Fatalf("create table: %v", err) } defer session.Query(fmt.Sprintf("DROP TABLE IF EXISTS %s.%s", ks, table)).Exec() waitForSchemaRefresh() tm, err := session.TableMetadata(ks, table) if err != nil { t.Fatalf("TableMetadata failed: %v", err) } if tm.Name != table { t.Errorf("expected table name %q, got %q", table, tm.Name) } if tm.Keyspace != ks { t.Errorf("expected keyspace %q, got %q", ks, tm.Keyspace) } }) t.Run("columns_and_partition_key", func(t *testing.T) { table := testTableName(t) if err := createTable(session, fmt.Sprintf( "CREATE TABLE IF NOT EXISTS %s.%s (pk1 int, pk2 text, ck int, val blob, PRIMARY KEY ((pk1, pk2), ck))", ks, table)); err != nil { t.Fatalf("create table: %v", err) } defer session.Query(fmt.Sprintf("DROP TABLE IF EXISTS %s.%s", ks, table)).Exec() waitForSchemaRefresh() tm, err := session.TableMetadata(ks, table) if err != nil { t.Fatalf("TableMetadata failed: %v", err) } if len(tm.PartitionKey) != 2 { t.Fatalf("expected 2 partition key columns, got %d", len(tm.PartitionKey)) } if tm.PartitionKey[0].Name != "pk1" || tm.PartitionKey[1].Name != "pk2" { t.Errorf("unexpected partition key columns: %v, %v", tm.PartitionKey[0].Name, tm.PartitionKey[1].Name) } if len(tm.ClusteringColumns) != 1 || tm.ClusteringColumns[0].Name != "ck" { t.Errorf("expected clustering column 'ck', got %v", tm.ClusteringColumns) } for _, col := range []string{"pk1", "pk2", "ck", "val"} { if _, ok := tm.Columns[col]; !ok { t.Errorf("expected column %q in metadata", col) } } }) t.Run("with_secondary_index", func(t *testing.T) { if isTabletsSupported() { t.Skip("secondary indexes are not supported on tables with tablets") } table := testTableName(t) if err := createTable(session, fmt.Sprintf( "CREATE TABLE IF NOT EXISTS %s.%s (pk int PRIMARY KEY, v int)", ks, table)); err != nil { t.Fatalf("create table: %v", err) } defer session.Query(fmt.Sprintf("DROP TABLE IF EXISTS %s.%s", ks, table)).Exec() idxName := table + "_v_idx" if err := createTable(session, fmt.Sprintf( "CREATE INDEX IF NOT EXISTS %s ON %s.%s (v)", idxName, ks, table)); err != nil { t.Fatalf("create index: %v", err) } waitForSchemaRefresh() session.metadataDescriber.invalidateKeyspaceSchema(ks) km, err := session.KeyspaceMetadata(ks) if err != nil { t.Fatalf("KeyspaceMetadata failed: %v", err) } if _, ok := km.Indexes[idxName]; !ok { t.Errorf("expected index %q in keyspace metadata indexes", idxName) } }) t.Run("with_materialized_view", func(t *testing.T) { if flagCassVersion.Before(3, 0, 0) { t.Skip("materialized views require Cassandra 3.0+") } if isTabletsSupported() { t.Skip("materialized views are not supported on tables with tablets") } baseTable := testTableName(t, "base") viewName := testTableName(t, "view") if err := createTable(session, fmt.Sprintf( "CREATE TABLE IF NOT EXISTS %s.%s (pk int, ck int, v int, PRIMARY KEY (pk, ck))", ks, baseTable)); err != nil { t.Fatalf("create base table: %v", err) } defer session.Query(fmt.Sprintf("DROP MATERIALIZED VIEW IF EXISTS %s.%s", ks, viewName)).Exec() defer session.Query(fmt.Sprintf("DROP TABLE IF EXISTS %s.%s", ks, baseTable)).Exec() if err := createTable(session, fmt.Sprintf( "CREATE MATERIALIZED VIEW IF NOT EXISTS %s.%s AS SELECT pk, ck, v FROM %s.%s WHERE pk IS NOT NULL AND ck IS NOT NULL AND v IS NOT NULL PRIMARY KEY (v, pk, ck)", ks, viewName, ks, baseTable)); err != nil { t.Fatalf("create materialized view: %v", err) } waitForSchemaRefresh() tm, err := session.TableMetadata(ks, baseTable) if err != nil { t.Fatalf("TableMetadata for base table failed: %v", err) } if tm.Name != baseTable { t.Errorf("expected table name %q, got %q", baseTable, tm.Name) } session.metadataDescriber.invalidateKeyspaceSchema(ks) km, err := session.KeyspaceMetadata(ks) if err != nil { t.Fatalf("KeyspaceMetadata failed: %v", err) } if _, ok := km.Views[viewName]; !ok { t.Errorf("expected view %q in keyspace metadata", viewName) } if km.Views[viewName].BaseTableName != baseTable { t.Errorf("expected view base table %q, got %q", baseTable, km.Views[viewName].BaseTableName) } }) t.Run("after_alter_table", func(t *testing.T) { table := testTableName(t) if err := createTable(session, fmt.Sprintf( "CREATE TABLE IF NOT EXISTS %s.%s (pk int PRIMARY KEY, v int)", ks, table)); err != nil { t.Fatalf("create table: %v", err) } defer session.Query(fmt.Sprintf("DROP TABLE IF EXISTS %s.%s", ks, table)).Exec() if err := createTable(session, fmt.Sprintf( "ALTER TABLE %s.%s ADD v2 text", ks, table)); err != nil { t.Fatalf("alter table: %v", err) } waitForSchemaRefresh() tm, err := session.TableMetadata(ks, table) if err != nil { t.Fatalf("TableMetadata failed: %v", err) } if _, ok := tm.Columns["v2"]; !ok { t.Errorf("expected column 'v2' after ALTER TABLE, got columns: %v", columnNames(tm.Columns)) } }) t.Run("after_drop_and_recreate", func(t *testing.T) { table := testTableName(t) if err := createTable(session, fmt.Sprintf( "CREATE TABLE IF NOT EXISTS %s.%s (pk int PRIMARY KEY, v int)", ks, table)); err != nil { t.Fatalf("create table: %v", err) } waitForSchemaRefresh() if _, err := session.TableMetadata(ks, table); err != nil { t.Fatalf("TableMetadata before drop failed: %v", err) } if err := createTable(session, fmt.Sprintf("DROP TABLE %s.%s", ks, table)); err != nil { t.Fatalf("drop table: %v", err) } if err := createTable(session, fmt.Sprintf( "CREATE TABLE %s.%s (pk text PRIMARY KEY, new_col int)", ks, table)); err != nil { t.Fatalf("recreate table: %v", err) } defer session.Query(fmt.Sprintf("DROP TABLE IF EXISTS %s.%s", ks, table)).Exec() waitForSchemaRefresh() tm, err := session.TableMetadata(ks, table) if err != nil { t.Fatalf("TableMetadata after recreate failed: %v", err) } if _, ok := tm.Columns["new_col"]; !ok { t.Errorf("expected column 'new_col' after recreate, got columns: %v", columnNames(tm.Columns)) } if _, ok := tm.Columns["v"]; ok { t.Errorf("old column 'v' should not exist after recreate") } }) t.Run("nonexistent_table", func(t *testing.T) { _, err := session.TableMetadata(ks, "does_not_exist_at_all") if err == nil { t.Fatal("expected error for nonexistent table, got nil") } if !errors.Is(err, ErrNotFound) { t.Errorf("expected ErrNotFound, got: %v", err) } }) t.Run("empty_table_name", func(t *testing.T) { _, err := session.TableMetadata(ks, "") if err == nil { t.Fatal("expected error for empty table name, got nil") } if !errors.Is(err, ErrNoTable) { t.Errorf("expected ErrNoTable, got: %v", err) } }) t.Run("empty_keyspace", func(t *testing.T) { _, err := session.TableMetadata("", "some_table") if err == nil { t.Fatal("expected error for empty keyspace, got nil") } if !errors.Is(err, ErrNoKeyspace) { t.Errorf("expected ErrNoKeyspace, got: %v", err) } }) }) t.Run("KeyspaceMetadata", func(t *testing.T) { t.Run("includes_new_table", func(t *testing.T) { table := testTableName(t) if err := createTable(session, fmt.Sprintf( "CREATE TABLE IF NOT EXISTS %s.%s (pk int PRIMARY KEY, v int)", ks, table)); err != nil { t.Fatalf("create table: %v", err) } defer session.Query(fmt.Sprintf("DROP TABLE IF EXISTS %s.%s", ks, table)).Exec() waitForSchemaRefresh() session.metadataDescriber.invalidateKeyspaceSchema(ks) km, err := session.KeyspaceMetadata(ks) if err != nil { t.Fatalf("KeyspaceMetadata failed: %v", err) } if _, ok := km.Tables[table]; !ok { t.Fatalf("expected table %q in keyspace metadata, got tables: %v", table, tableNames(km.Tables)) } }) t.Run("excludes_dropped_table", func(t *testing.T) { table := testTableName(t) if err := createTable(session, fmt.Sprintf( "CREATE TABLE IF NOT EXISTS %s.%s (pk int PRIMARY KEY, v int)", ks, table)); err != nil { t.Fatalf("create table: %v", err) } waitForSchemaRefresh() session.metadataDescriber.invalidateKeyspaceSchema(ks) km, err := session.KeyspaceMetadata(ks) if err != nil { t.Fatalf("KeyspaceMetadata before drop failed: %v", err) } if _, ok := km.Tables[table]; !ok { t.Fatalf("expected table %q before drop", table) } if err := createTable(session, fmt.Sprintf("DROP TABLE %s.%s", ks, table)); err != nil { t.Fatalf("drop table: %v", err) } waitForSchemaRefresh() session.metadataDescriber.invalidateKeyspaceSchema(ks) km, err = session.KeyspaceMetadata(ks) if err != nil { t.Fatalf("KeyspaceMetadata after drop failed: %v", err) } if _, ok := km.Tables[table]; ok { t.Errorf("table %q should not appear after DROP", table) } }) t.Run("multiple_tables", func(t *testing.T) { tables := []string{testTableName(t, "a"), testTableName(t, "b"), testTableName(t, "c")} for _, table := range tables { if err := createTable(session, fmt.Sprintf( "CREATE TABLE IF NOT EXISTS %s.%s (pk int PRIMARY KEY)", ks, table)); err != nil { t.Fatalf("create table %s: %v", table, err) } defer session.Query(fmt.Sprintf("DROP TABLE IF EXISTS %s.%s", ks, table)).Exec() } waitForSchemaRefresh() session.metadataDescriber.invalidateKeyspaceSchema(ks) km, err := session.KeyspaceMetadata(ks) if err != nil { t.Fatalf("KeyspaceMetadata failed: %v", err) } for _, table := range tables { if _, ok := km.Tables[table]; !ok { t.Errorf("expected table %q in keyspace metadata", table) } } }) t.Run("nonexistent_keyspace", func(t *testing.T) { _, err := session.KeyspaceMetadata("keyspace_that_does_not_exist_xyz") if err == nil { t.Fatal("expected error for nonexistent keyspace, got nil") } }) t.Run("empty_keyspace", func(t *testing.T) { _, err := session.KeyspaceMetadata("") if err == nil { t.Fatal("expected error for empty keyspace, got nil") } if !errors.Is(err, ErrNoKeyspace) { t.Errorf("expected ErrNoKeyspace, got: %v", err) } }) }) } func tableNames(tables map[string]*TableMetadata) []string { names := make([]string, 0, len(tables)) for name := range tables { names = append(names, name) } return names } func columnNames(columns map[string]*ColumnMetadata) []string { names := make([]string, 0, len(columns)) for name := range columns { names = append(names, name) } return names } // Integration test of just querying for data from the system.schema_keyspace table where the keyspace DOES NOT exist. func TestGetKeyspaceMetadataFails(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() _, err := getKeyspaceMetadata(session, "gocql_keyspace_does_not_exist") if err != ErrKeyspaceDoesNotExist || err == nil { t.Fatalf("Expected error of type ErrKeySpaceDoesNotExist. Instead, error was %v", err) } } // Integration test of the routing key calculation func TestRoutingKey(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() singleTable := testTableName(t, "single") compositeTable := testTableName(t, "composite") if err := createTable(session, fmt.Sprintf("CREATE TABLE gocql_test.%s (first_id int, second_id int, PRIMARY KEY (first_id, second_id))", singleTable)); err != nil { t.Fatalf("failed to create table with error '%v'", err) } if err := createTable(session, fmt.Sprintf("CREATE TABLE gocql_test.%s (first_id int, second_id int, PRIMARY KEY ((first_id, second_id)))", compositeTable)); err != nil { t.Fatalf("failed to create table with error '%v'", err) } initCacheSize := session.routingKeyInfoCache.lru.Len() routingKeyInfo, err := session.routingKeyInfo(context.Background(), fmt.Sprintf("SELECT * FROM %s WHERE second_id=? AND first_id=?", singleTable), time.Second) if err != nil { t.Fatalf("failed to get routing key info due to error: %v", err) } if routingKeyInfo == nil { t.Fatal("Expected routing key info, but was nil") } if len(routingKeyInfo.indexes) != 1 { t.Fatalf("Expected routing key indexes length to be 1 but was %d", len(routingKeyInfo.indexes)) } if routingKeyInfo.indexes[0] != 1 { t.Errorf("Expected routing key index[0] to be 1 but was %d", routingKeyInfo.indexes[0]) } if len(routingKeyInfo.types) != 1 { t.Fatalf("Expected routing key types length to be 1 but was %d", len(routingKeyInfo.types)) } if routingKeyInfo.types[0] == nil { t.Fatal("Expected routing key types[0] to be non-nil") } if routingKeyInfo.types[0].Type() != TypeInt { t.Fatalf("Expected routing key types[0].Type to be %v but was %v", TypeInt, routingKeyInfo.types[0].Type()) } // verify the cache is working routingKeyInfo, err = session.routingKeyInfo( context.Background(), fmt.Sprintf("SELECT * FROM %s WHERE second_id=? AND first_id=?", singleTable), // Routing info will be pulled from cached prepared statement, it should work with minimal timeout time.Nanosecond) if err != nil { t.Fatalf("failed to get routing key info due to error: %v", err) } if len(routingKeyInfo.indexes) != 1 { t.Fatalf("Expected routing key indexes length to be 1 but was %d", len(routingKeyInfo.indexes)) } if routingKeyInfo.indexes[0] != 1 { t.Errorf("Expected routing key index[0] to be 1 but was %d", routingKeyInfo.indexes[0]) } if len(routingKeyInfo.types) != 1 { t.Fatalf("Expected routing key types length to be 1 but was %d", len(routingKeyInfo.types)) } if routingKeyInfo.types[0] == nil { t.Fatal("Expected routing key types[0] to be non-nil") } if routingKeyInfo.types[0].Type() != TypeInt { t.Fatalf("Expected routing key types[0] to be %v but was %v", TypeInt, routingKeyInfo.types[0].Type()) } cacheSize := session.routingKeyInfoCache.lru.Len() if cacheSize != initCacheSize+1 { t.Errorf("Expected cache size to be %d but was %d", initCacheSize+1, cacheSize) } query := session.Query(fmt.Sprintf("SELECT * FROM %s WHERE second_id=? AND first_id=?", singleTable), 1, 2) routingKey, err := query.GetRoutingKey() if err != nil { t.Fatalf("Failed to get routing key due to error: %v", err) } expectedRoutingKey := []byte{0, 0, 0, 2} if !reflect.DeepEqual(expectedRoutingKey, routingKey) { t.Errorf("Expected routing key %v but was %v", expectedRoutingKey, routingKey) } routingKeyInfo, err = session.routingKeyInfo( context.Background(), fmt.Sprintf("SELECT * FROM %s WHERE second_id=? AND first_id=?", compositeTable), time.Second) if err != nil { t.Fatalf("failed to get routing key info due to error: %v", err) } if routingKeyInfo == nil { t.Fatal("Expected routing key info, but was nil") } if len(routingKeyInfo.indexes) != 2 { t.Fatalf("Expected routing key indexes length to be 2 but was %d", len(routingKeyInfo.indexes)) } if routingKeyInfo.indexes[0] != 1 { t.Errorf("Expected routing key index[0] to be 1 but was %d", routingKeyInfo.indexes[0]) } if routingKeyInfo.indexes[1] != 0 { t.Errorf("Expected routing key index[1] to be 0 but was %d", routingKeyInfo.indexes[1]) } if len(routingKeyInfo.types) != 2 { t.Fatalf("Expected routing key types length to be 1 but was %d", len(routingKeyInfo.types)) } if routingKeyInfo.types[0] == nil { t.Fatal("Expected routing key types[0] to be non-nil") } if routingKeyInfo.types[0].Type() != TypeInt { t.Fatalf("Expected routing key types[0] to be %v but was %v", TypeInt, routingKeyInfo.types[0].Type()) } if routingKeyInfo.types[1] == nil { t.Fatal("Expected routing key types[1] to be non-nil") } if routingKeyInfo.types[1].Type() != TypeInt { t.Fatalf("Expected routing key types[0] to be %v but was %v", TypeInt, routingKeyInfo.types[1].Type()) } query = session.Query(fmt.Sprintf("SELECT * FROM %s WHERE second_id=? AND first_id=?", compositeTable), 1, 2) routingKey, err = query.GetRoutingKey() if err != nil { t.Fatalf("Failed to get routing key due to error: %v", err) } expectedRoutingKey = []byte{0, 4, 0, 0, 0, 2, 0, 0, 4, 0, 0, 0, 1, 0} if !reflect.DeepEqual(expectedRoutingKey, routingKey) { t.Errorf("Expected routing key %v but was %v", expectedRoutingKey, routingKey) } // verify the cache is working cacheSize = session.routingKeyInfoCache.lru.Len() if cacheSize != initCacheSize+2 { t.Errorf("Expected cache size to be %d but was %d", initCacheSize+2, cacheSize) } } // Integration test of the token-aware policy-based connection pool func TestTokenAwareConnPool(t *testing.T) { t.Parallel() cluster := createCluster() cluster.PoolConfig.HostSelectionPolicy = TokenAwareHostPolicy(RoundRobinHostPolicy()) // force metadata query to page cluster.PageSize = 1 session := createSessionFromCluster(cluster, t) defer session.Close() expectedPoolSize := cluster.NumConns * len(session.hostSource.getHostsList()) // wait for pool to fill for i := 0; i < 50; i++ { if session.pool.Size() == expectedPoolSize { break } time.Sleep(100 * time.Millisecond) } if expectedPoolSize != session.pool.Size() { t.Errorf("Expected pool size %d but was %d", expectedPoolSize, session.pool.Size()) } table := testTableName(t) otherTable := testTableName(t, "other") // add another cf so there are two pages when fetching table metadata from our keyspace if err := createTable(session, fmt.Sprintf("CREATE TABLE gocql_test.%s (id int, data text, PRIMARY KEY (id))", otherTable)); err != nil { t.Fatalf("failed to create test_token_aware table with err: %v", err) } if err := createTable(session, fmt.Sprintf("CREATE TABLE gocql_test.%s (id int, data text, PRIMARY KEY (id))", table)); err != nil { t.Fatalf("failed to create test_token_aware table with err: %v", err) } query := session.Query(fmt.Sprintf("INSERT INTO %s (id, data) VALUES (?,?)", table), 42, "8 * 6 =") if err := query.Exec(); err != nil { t.Fatalf("failed to insert with err: %v", err) } query = session.Query(fmt.Sprintf("SELECT data FROM %s where id = ?", table), 42).Consistency(One) var data string if err := query.Scan(&data); err != nil { t.Error(err) } // TODO add verification that the query went to the correct host } func TestNegativeStream(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() conn := getRandomConn(t, session) const stream = -50 writer := frameWriterFunc(func(f *framer, streamID int) error { f.writeHeader(0, frm.OpOptions, stream) return f.finish() }) frame, err := conn.exec(context.Background(), writer, nil, time.Second) if err == nil { t.Fatalf("expected to get an error on stream %d", stream) } else if frame != nil { t.Fatalf("expected to get nil frame got %+v", frame) } } func TestManualQueryPaging(t *testing.T) { t.Parallel() const rowsToInsert = 5 session := createSession(t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf("CREATE TABLE gocql_test.%s (id int, count int, PRIMARY KEY (id))", table)); err != nil { t.Fatal(err) } for i := 0; i < rowsToInsert; i++ { err := session.Query(fmt.Sprintf("INSERT INTO %s(id, count) VALUES(?, ?)", table), i, i*i).Exec() if err != nil { t.Fatal(err) } } // disable auto paging, 1 page per iteration query := session.Query(fmt.Sprintf("SELECT id, count FROM %s", table)).PageState(nil).PageSize(2) var id, count, fetched int iter := query.Iter() // NOTE: this isnt very indicative of how it should be used, the idea is that // the page state is returned to some client who will send it back to manually // page through the results. for { for iter.Scan(&id, &count) { if count != (id * id) { t.Fatalf("got wrong value from iteration: got %d expected %d", count, id*id) } fetched++ } if !iter.LastPage() { // more pages iter = query.PageState(iter.PageState()).Iter() } else { break } } if err := iter.Close(); err != nil { t.Fatal(err) } if fetched != rowsToInsert { t.Fatalf("expected to fetch %d rows got %d", rowsToInsert, fetched) } } // Issue 475 func TestSessionBindRoutingKey(t *testing.T) { t.Parallel() cluster := createCluster() cluster.PoolConfig.HostSelectionPolicy = TokenAwareHostPolicy(RoundRobinHostPolicy()) session := createSessionFromCluster(cluster, t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s ( key varchar, value int, PRIMARY KEY (key) )`, table)); err != nil { t.Fatal(err) } const ( key = "routing-key" value = 5 ) fn := func(info *QueryInfo) ([]any, error) { return []any{key, value}, nil } q := session.Bind(fmt.Sprintf("INSERT INTO %s(key, value) VALUES(?, ?)", table), fn) if err := q.Exec(); err != nil { t.Fatal(err) } } func TestJSONSupport(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() if session.cfg.ProtoVersion < protoVersion4 { t.Skip("skipping JSON support on proto < 4") } table := testTableName(t) if err := createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s ( id text PRIMARY KEY, age int, state text )`, table)); err != nil { t.Fatal(err) } err := session.Query(fmt.Sprintf("INSERT INTO %s JSON ?", table), `{"id": "user123", "age": 42, "state": "TX"}`).Exec() if err != nil { t.Fatal(err) } var ( id string age int state string ) err = session.Query(fmt.Sprintf("SELECT id, age, state FROM %s WHERE id = ?", table), "user123").Scan(&id, &age, &state) if err != nil { t.Fatal(err) } if id != "user123" { t.Errorf("got id %q expected %q", id, "user123") } if age != 42 { t.Errorf("got age %d expected %d", age, 42) } if state != "TX" { t.Errorf("got state %q expected %q", state, "TX") } } func TestUnmarshallNestedTypes(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s ( id text PRIMARY KEY, val list > > )`, table)); err != nil { t.Fatal(err) } m := []map[string]string{ {"key1": "val1"}, {"key2": "val2"}, } const id = "key" err := session.Query(fmt.Sprintf("INSERT INTO %s(id, val) VALUES(?, ?)", table), id, m).Exec() if err != nil { t.Fatal(err) } var data []map[string]string if err := session.Query(fmt.Sprintf("SELECT val FROM %s WHERE id = ?", table), id).Scan(&data); err != nil { t.Fatal(err) } if !reflect.DeepEqual(data, m) { t.Fatalf("%+#v != %+#v", data, m) } } func TestSchemaReset(t *testing.T) { t.Parallel() if flagCassVersion.Major == 0 || flagCassVersion.Before(2, 1, 3) { t.Skipf("skipping TestSchemaReset due to CASSANDRA-7910 in Cassandra <2.1.3 version=%v", flagCassVersion) } cluster := createCluster() cluster.NumConns = 1 session := createSessionFromCluster(cluster, t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s ( id text PRIMARY KEY)`, table)); err != nil { t.Fatal(err) } const key = "test" err := session.Query(fmt.Sprintf("INSERT INTO %s(id) VALUES(?)", table), key).Exec() if err != nil { t.Fatal(err) } var id string err = session.Query(fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), key).Scan(&id) if err != nil { t.Fatal(err) } else if id != key { t.Fatalf("expected to get id=%q got=%q", key, id) } if err := createTable(session, fmt.Sprintf(`ALTER TABLE gocql_test.%s ADD val text`, table)); err != nil { t.Fatal(err) } const expVal = "test-val" err = session.Query(fmt.Sprintf("INSERT INTO %s(id, val) VALUES(?, ?)", table), key, expVal).Exec() if err != nil { t.Fatal(err) } var val string err = session.Query(fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), key).Scan(&id, &val) if err != nil { t.Fatal(err) } if id != key { t.Errorf("expected to get id=%q got=%q", key, id) } if val != expVal { t.Errorf("expected to get val=%q got=%q", expVal, val) } } func TestCreateSession_DontSwallowError(t *testing.T) { t.Parallel() t.Skip("This test is bad, and the resultant error from cassandra changes between versions") cluster := createCluster() cluster.ProtoVersion = 0x100 session, err := cluster.CreateSession() if err == nil { session.Close() t.Fatal("expected to get an error for unsupported protocol") } if flagCassVersion.Major < 3 { // TODO: we should get a distinct error type here which include the underlying // cassandra error about the protocol version, for now check this here. if !strings.Contains(err.Error(), "Invalid or unsupported protocol version") { t.Fatalf(`expcted to get error "unsupported protocol version" got: %q`, err) } } else { if !strings.Contains(err.Error(), "unsupported response version") { t.Fatalf(`expcted to get error "unsupported response version" got: %q`, err) } } } func TestControl_DiscoverProtocol(t *testing.T) { t.Parallel() cluster := createCluster() cluster.ProtoVersion = 0 session, err := cluster.CreateSession() if err != nil { t.Fatal(err) } defer session.Close() if session.cfg.ProtoVersion == 0 { t.Fatal("did not discovery protocol") } } // TestUnsetCol verify unset column will not replace an existing column func TestUnsetCol(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() if session.cfg.ProtoVersion < protoVersion4 { t.Skip("Unset Values are not supported in protocol < 4") } table := testTableName(t) if err := createTable(session, fmt.Sprintf("CREATE TABLE gocql_test.%s (id int, my_int int, my_text text, PRIMARY KEY (id))", table)); err != nil { t.Fatalf("failed to create table with error '%v'", err) } if err := session.Query(fmt.Sprintf("INSERT INTO %s (id,my_int,my_text) VALUES (?,?,?)", table), 1, 2, "3").Exec(); err != nil { t.Fatalf("failed to insert with err: %v", err) } if err := session.Query(fmt.Sprintf("INSERT INTO %s (id,my_int,my_text) VALUES (?,?,?)", table), 1, UnsetValue, UnsetValue).Exec(); err != nil { t.Fatalf("failed to insert with err: %v", err) } var id, mInt int var mText string if err := session.Query(fmt.Sprintf("SELECT id, my_int ,my_text FROM %s", table)).Scan(&id, &mInt, &mText); err != nil { t.Fatalf("failed to select with err: %v", err) } else if id != 1 || mInt != 2 || mText != "3" { t.Fatalf("Expected results: 1, 2, \"3\", got %v, %v, %v", id, mInt, mText) } } // TestUnsetColBatch verify unset column will not replace a column in batch func TestUnsetColBatch(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() if session.cfg.ProtoVersion < protoVersion4 { t.Skip("Unset Values are not supported in protocol < 4") } table := testTableName(t) if err := createTable(session, fmt.Sprintf("CREATE TABLE gocql_test.%s (id int, my_int int, my_text text, PRIMARY KEY (id))", table)); err != nil { t.Fatalf("failed to create table with error '%v'", err) } b := session.Batch(LoggedBatch) b.Query(fmt.Sprintf("INSERT INTO gocql_test.%s(id, my_int, my_text) VALUES (?,?,?)", table), 1, 1, UnsetValue) b.Query(fmt.Sprintf("INSERT INTO gocql_test.%s(id, my_int, my_text) VALUES (?,?,?)", table), 1, UnsetValue, "") b.Query(fmt.Sprintf("INSERT INTO gocql_test.%s(id, my_int, my_text) VALUES (?,?,?)", table), 2, 2, UnsetValue) if err := session.ExecuteBatch(b); err != nil { t.Fatalf("query failed. %v", err) } else { if b.Attempts() < 1 { t.Fatal("expected at least 1 attempt, but got 0") } if b.Latency() <= 0 { t.Fatalf("expected latency to be greater than 0, but got %v instead.", b.Latency()) } } var id, mInt, count int var mText string if err := session.Query(fmt.Sprintf("SELECT count(*) FROM gocql_test.%s;", table)).Scan(&count); err != nil { t.Fatalf("Failed to select with err: %v", err) } else if count != 2 { t.Fatalf("Expected Batch Insert count 2, got %v", count) } if err := session.Query(fmt.Sprintf("SELECT id, my_int ,my_text FROM gocql_test.%s where id=1;", table)).Scan(&id, &mInt, &mText); err != nil { t.Fatalf("failed to select with err: %v", err) } else if id != mInt { t.Fatalf("expected id, my_int to be 1, got %v and %v", id, mInt) } } func TestQuery_NamedValues(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf("CREATE TABLE gocql_test.%s(id int, value text, PRIMARY KEY (id))", table)); err != nil { t.Fatal(err) } err := session.Query(fmt.Sprintf("INSERT INTO gocql_test.%s(id, value) VALUES(:id, :value)", table), NamedValue("id", 1), NamedValue("value", "i am a value")).Exec() if err != nil { t.Fatal(err) } var value string if err := session.Query(fmt.Sprintf("SELECT VALUE from gocql_test.%s WHERE id = :id", table), NamedValue("id", 1)).Scan(&value); err != nil { t.Fatal(err) } } // TestQuery_SetHostID ensures that queries are sent to the specified host only. // WARNING: This test must NOT use t.Parallel(). It calls pool.host.setState(NodeDown) // which mutates shared HostInfo state visible to all concurrent sessions. // //nolint:paralleltest // mutates shared HostInfo state via setState(NodeDown) func TestQuery_SetHostID(t *testing.T) { session := createSession(t) defer session.Close() hosts := session.GetHosts() const iterations = 5 for _, expectedHost := range hosts { for i := 0; i < iterations; i++ { var actualHostID string err := session.Query("SELECT host_id FROM system.local"). SetHostID(expectedHost.HostID()). Scan(&actualHostID) if err != nil { t.Fatal(err) } if expectedHost.HostID() != actualHostID { t.Fatalf("Expected query to be executed on host %s, but it was executed on %s", expectedHost.HostID(), actualHostID, ) } } } // ensuring properly handled invalid host id err := session.Query("SELECT host_id FROM system.local"). SetHostID("[invalid]"). Exec() if !errors.Is(err, ErrNoPool) { t.Fatalf("Expected error to be: %v, but got %v", ErrNoPool, err) } // ensuring that the driver properly handles the case // when specified host for the query is down host := hosts[0] pool, _ := session.pool.getPoolByHostID(host.HostID()) // simulating specified host is down pool.host.setState(NodeDown) err = session.Query("SELECT host_id FROM system.local"). SetHostID(host.HostID()). Exec() if !errors.Is(err, ErrHostDown) { t.Fatalf("Expected error to be: %v, but got %v", ErrHostDown, err) } } ================================================ FILE: ci/clean-old-temporary-docker-images.py ================================================ import os import requests from datetime import datetime, timedelta DOCKERHUB_USERNAME = os.environ["DOCKERHUB_USERNAME"] DOCKERHUB_TOKEN = os.environ["DOCKERHUB_TOKEN"] DELETE_AFTER_DAYS = os.environ["DELETE_AFTER_DAYS"] def get_docker_token(username, password): url = "https://hub.docker.com/v2/users/login/" headers = {"Content-Type": "application/json"} data = {"username": username, "password": password} response = requests.post(url, json=data, headers=headers) if response.status_code == 200: return response.json()["token"] else: print(f"Failed to login to DockerHub: {response.status_code}") return None def get_repo_tags(token): url = f"https://hub.docker.com/v2/repositories/scylladb/gocql-extended-ci/tags/" headers = {"Authorization": f"Bearer {token}"} response = requests.get(url, headers=headers) if response.status_code != 200: print(f"Failed to get tags, Status Code: {response.status_code}, {response.text}") return None return response.json()["results"] def delete_tag(tag, token): url = f"https://hub.docker.com/v2/repositories/scylladb/gocql-extended-ci/tags/{tag}/" headers = {"Authorization": f"Bearer {token}"} response = requests.delete(url, headers=headers) if response.status_code > 200 and response.status_code < 300: print(f"Deleted tag: {tag}") return True print(f"Failed to delete tag: {tag}, Status Code: {response.status_code}") return False def clean_old_images(): token = get_docker_token(DOCKERHUB_USERNAME, DOCKERHUB_TOKEN) if token is None: return False tags = get_repo_tags(token) if tags is None: return False threshold_date = datetime.now() - timedelta(days=int(DELETE_AFTER_DAYS)) status = True for tag in tags: last_updated = datetime.strptime(tag["last_updated"], "%Y-%m-%dT%H:%M:%S.%fZ") if last_updated < threshold_date: status = status and delete_tag(tag["name"], token) return status if __name__ == "__main__": if not clean_old_images(): exit(1) exit(0) ================================================ FILE: client_routes.go ================================================ package gocql import ( "errors" "fmt" "net" "slices" "strings" "sync" "sync/atomic" "time" "github.com/gocql/gocql/events" "github.com/gocql/gocql/internal/debug" "github.com/gocql/gocql/internal/eventbus" ) type ClientRoutesEndpoint struct { // Scylla Cloud ConnectionID to read from `system.client_routes` ConnectionID string // Ip Address or DNS name of the AWS endpoint // Could stay empty, in this case driver will pick it up from system.client_routes table ConnectionAddr string } func (e ClientRoutesEndpoint) Validate() error { if e.ConnectionID == "" { return errors.New("missing ConnectionID") } return nil } type ClientRoutesEndpointList []ClientRoutesEndpoint func (l *ClientRoutesEndpointList) GetAllConnectionIDs() []string { var ids []string for _, endpoint := range *l { ids = append(ids, endpoint.ConnectionID) } return ids } func (l *ClientRoutesEndpointList) GetConnectionAddr(connectionID string) string { for _, endpoint := range *l { if endpoint.ConnectionID == connectionID { return endpoint.ConnectionAddr } } return "" } func (l *ClientRoutesEndpointList) Validate() error { for id, endpoint := range *l { if err := endpoint.Validate(); err != nil { return fmt.Errorf("endpoint #%d is invalid: %w", id, err) } } return nil } type ClientRoutesConfig struct { TableName string Endpoints ClientRoutesEndpointList ResolveHealthyEndpointPeriod time.Duration ResolverCacheDuration time.Duration MaxResolverConcurrency int // Deprecated: BlockUnknownEndpoints no longer has any effect. Unknown // endpoints are always blocked. This field will be removed in a future // release. BlockUnknownEndpoints bool // EnableShardAwareness controls whether the driver should use shard-aware // connections when using ClientRoutes (PrivateLink). // // By default this is false because NAT typically breaks shard-awareness. // Shard-aware routing relies on the driver knowing the source port of connections, // which NAT devices modify, making it impossible for the server to route // requests to the correct shard. // // However, in some deployments shard-awareness can still work: // - When using PROXY Protocol v2, the original source port is preserved // in the protocol header. See https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt // - When using direct connections without NAT (e.g., VPC peering) // - When the load balancer/proxy is shard-aware itself // // Set this to true only if your network setup preserves or correctly handles // the source port information needed for shard-aware routing. EnableShardAwareness bool } func (cfg *ClientRoutesConfig) Validate() error { if cfg == nil { return nil } if len(cfg.Endpoints) == 0 { return errors.New("no endpoints specified") } if err := cfg.Endpoints.Validate(); err != nil { return fmt.Errorf("failed to validate endpoints: %w", err) } if cfg.ResolveHealthyEndpointPeriod < 0 { return errors.New("resolve healthy endpoint period must be >= 0") } if cfg.MaxResolverConcurrency <= 0 { return errors.New("max resolver concurrency must be > 0") } return nil } type UnresolvedClientRoute struct { ConnectionID string HostID string Address string CQLPort uint16 SecureCQLPort uint16 } // Similar returns true if both records targets same host and connection id func (r UnresolvedClientRoute) Similar(o UnresolvedClientRoute) bool { return r.ConnectionID == o.ConnectionID && r.HostID == o.HostID } // Equal returns true if both records are exactly the same func (r UnresolvedClientRoute) Equal(o UnresolvedClientRoute) bool { return r == o } func (r UnresolvedClientRoute) String() string { return fmt.Sprintf( "UnresolvedClientRoute{ConnectionID=%s, HostID=%s, Address=%s, CQLPort=%d, SecureCQLPort=%d}", r.ConnectionID, r.HostID, r.Address, r.CQLPort, r.SecureCQLPort, ) } type UnresolvedClientRouteList []UnresolvedClientRoute func (l *UnresolvedClientRouteList) Len() int { return len(*l) } type ResolvedClientRoute struct { updateTime time.Time UnresolvedClientRoute allKnownIPs []net.IP currentIP net.IP forcedResolve bool } func (r ResolvedClientRoute) String() string { var ip string if r.currentIP == nil { ip = "" } else { ip = r.currentIP.String() } return fmt.Sprintf( "ResolvedClientRoute{ConnectionID=%s, HostID=%s, Address=%s, CQLPort=%d, SecureCQLPort=%d, CurrentIP=%s}", r.ConnectionID, r.HostID, r.Address, r.CQLPort, r.SecureCQLPort, ip, ) } func (r ResolvedClientRoute) Clone() ResolvedClientRoute { res := r if res.allKnownIPs != nil { res.allKnownIPs = make([]net.IP, 0, len(r.allKnownIPs)) for _, ip := range r.allKnownIPs { res.allKnownIPs = append(res.allKnownIPs, slices.Clone(ip)) } } if len(res.currentIP) != 0 { copy(res.currentIP, r.currentIP) res.currentIP = slices.Clone(res.currentIP) } return res } // Newer returns true if o is newer than r func (r ResolvedClientRoute) Newer(o ResolvedClientRoute) bool { if len(r.currentIP) == 0 && len(o.currentIP) != 0 { return true } if len(r.allKnownIPs) == 0 && len(o.allKnownIPs) != 0 { return true } return r.updateTime.Compare(o.updateTime) == -1 } // Similar returns true if both records targets same host and connection id func (r ResolvedClientRoute) Similar(o ResolvedClientRoute) bool { return r.ConnectionID == o.ConnectionID && r.HostID == o.HostID } func (r ResolvedClientRoute) NeedsUpdate() bool { return r.currentIP == nil || len(r.allKnownIPs) == 0 || r.forcedResolve } func (r ResolvedClientRoute) GetCQLPort() uint16 { if r.SecureCQLPort != 0 { return r.SecureCQLPort } return r.CQLPort } type ResolvedClientRouteList []ResolvedClientRoute func (l *ResolvedClientRouteList) Len() int { return len(*l) } func (l *ResolvedClientRouteList) MergeWithUnresolved(unresolved UnresolvedClientRouteList) { for _, unres := range unresolved { found := false for id, res := range *l { if res.UnresolvedClientRoute.Similar(unres) { found = true if res.Equal(unres) { // Records are the same, no information has changed break } // Records are not the same, add unresolved record // It will be picked up by resolver on very next iteration (*l)[id] = ResolvedClientRoute{ UnresolvedClientRoute: unres, forcedResolve: true, } break } } if !found { *l = append(*l, ResolvedClientRoute{ UnresolvedClientRoute: unres, forcedResolve: true, }) } } } func (l *ResolvedClientRouteList) MergeWithResolved(o *ResolvedClientRouteList) { for id, rec := range *l { for _, otherRec := range *o { if rec.Similar(otherRec) { if rec.Newer(otherRec) { (*l)[id] = otherRec } break } } } for _, otherRec := range *o { if !slices.ContainsFunc(*l, otherRec.Similar) { *l = append(*l, otherRec) } } } func (l *ResolvedClientRouteList) UpdateIfNewer(route ResolvedClientRoute) bool { for id, r := range *l { if r.Similar(route) { if !r.Newer(route) { return false } (*l)[id] = route return true } } *l = append(*l, route) return true } func (l *ResolvedClientRouteList) FindByHostID(hostID string) *ResolvedClientRoute { for i := range *l { if (*l)[i].HostID == hostID { return &(*l)[i] } } return nil } func (l *ResolvedClientRouteList) Clone() ResolvedClientRouteList { if len(*l) == 0 { return make(ResolvedClientRouteList, 0) } cpy := make(ResolvedClientRouteList, len(*l)) copy(cpy, *l) return cpy } type ResolvedEndpoint struct { updateTime time.Time connectionID string dc string rack string address string allKnown []net.IP currentIP net.IP forcedResolve bool } type ClientRoutesResolver interface { Resolve(endpoint ResolvedClientRoute) ([]net.IP, net.IP, error) } type resolvedCacheRecord struct { lastTimeResolved time.Time lastResult []net.IP } func (r resolvedCacheRecord) WasResolvedLessThan(cachingTime time.Duration) bool { return time.Now().UTC().Sub(r.lastTimeResolved) < cachingTime } // simpleClientRoutesResolver resolves endpoints using the provided lookup function while enforcing // a minimal period between successive resolutions of the same address. type simpleClientRoutesResolver struct { resolver DNSResolver cache map[string]resolvedCacheRecord cachingTime time.Duration mu sync.RWMutex } func newSimpleClientRoutesResolver(cachingTime time.Duration, resolver DNSResolver) *simpleClientRoutesResolver { if resolver == nil { resolver = defaultDnsResolver } return &simpleClientRoutesResolver{ resolver: resolver, cachingTime: cachingTime, cache: make(map[string]resolvedCacheRecord), } } func (r *simpleClientRoutesResolver) Resolve(endpoint ResolvedClientRoute) (allKnown []net.IP, current net.IP, err error) { r.mu.RLock() cache, ok := r.cache[endpoint.Address] r.mu.RUnlock() if ok && cache.WasResolvedLessThan(r.cachingTime) { allKnown = cache.lastResult } if len(allKnown) == 0 { allKnown, err = r.resolver.LookupIP(endpoint.Address) if err != nil { return endpoint.allKnownIPs, endpoint.currentIP, err } if len(allKnown) == 0 { return endpoint.allKnownIPs, endpoint.currentIP, fmt.Errorf("no addresses returned for %s", endpoint.Address) } } for _, addr := range allKnown { if endpoint.currentIP != nil && endpoint.currentIP.Equal(addr) { current = addr break } } if current == nil { current = allKnown[0] } r.mu.Lock() r.cache[endpoint.Address] = resolvedCacheRecord{ lastTimeResolved: time.Now().UTC(), lastResult: allKnown, } r.mu.Unlock() return allKnown, current, nil } type ClientRoutesHandler struct { log StdLogger c controlConnection resolver ClientRoutesResolver sub *eventbus.Subscriber[events.Event] resolvedEndpoints atomic.Pointer[ResolvedClientRouteList] updateTasks chan updateTask closeChan chan struct{} cfg ClientRoutesConfig pickTLSPorts bool initialized bool } var _ AddressTranslatorV2 = (*ClientRoutesHandler)(nil) // Translate implements old AddressTranslator interface // should not be uses since driver prefer AddressTranslatorV2 API if it is implemented func (p *ClientRoutesHandler) Translate(addr net.IP, port int) (net.IP, int) { panic("should never be called") } func pickProperPort(pickTLSPorts bool, rec *ResolvedClientRoute) uint16 { if pickTLSPorts { return rec.SecureCQLPort } return rec.CQLPort } // TranslateWithHost implements AddressTranslatorV2 interface func (p *ClientRoutesHandler) TranslateHost(host AddressTranslatorHostInfo, addr AddressPort) (AddressPort, error) { hostID := host.HostID() if hostID == "" { return addr, nil } current := p.resolvedEndpoints.Load() rec := current.FindByHostID(hostID) if rec == nil { return addr, fmt.Errorf("no address found for host %s", hostID) } if rec.currentIP != nil { port := pickProperPort(p.pickTLSPorts, rec) if port == 0 { return addr, fmt.Errorf("record %s/%s has target port empty", rec.HostID, rec.ConnectionID) } return AddressPort{ Address: rec.currentIP, Port: port, }, nil } all, currentIP, err := p.resolver.Resolve(*rec) if err != nil { return addr, fmt.Errorf("failed to resolve DNS resolver for host %s: %v", hostID, err) } rec.allKnownIPs = all rec.currentIP = currentIP for { updated := current.Clone() if updated.UpdateIfNewer(*rec) { if p.resolvedEndpoints.CompareAndSwap(current, &updated) { port := pickProperPort(p.pickTLSPorts, rec) if port == 0 { return addr, fmt.Errorf("record %s/%s has target port empty", rec.HostID, rec.ConnectionID) } return AddressPort{ Address: rec.currentIP, Port: port, }, nil } continue } rec = current.FindByHostID(hostID) if rec == nil { return addr, fmt.Errorf("no address found for host %s", hostID) } port := pickProperPort(p.pickTLSPorts, rec) if port == 0 { return addr, fmt.Errorf("record %s/%s has target port empty", rec.HostID, rec.ConnectionID) } return AddressPort{ Address: rec.currentIP, Port: port, }, nil } } var never = time.Unix(1<<63-1, 0) type updateTask struct { result chan error connectionIDs []string hostIDs []string } func (p *ClientRoutesHandler) Initialize(s *Session) error { if p.initialized { return errors.New("already initialized") } connectionIDs := make([]string, 0, len(p.cfg.Endpoints)) for _, ep := range p.cfg.Endpoints { if ep.ConnectionID != "" { connectionIDs = append(connectionIDs, ep.ConnectionID) } } p.c = s.control p.sub = s.eventBus.Subscribe("port-mux", 1024, func(event events.Event) bool { switch event.Type() { case events.SessionEventTypeControlConnectionRecreated, events.ClusterEventTypeClientRoutesChanged: return true default: return false } }) p.startUpdateWorker() p.startReadingEvents() err := p.updateHostPortMappingSync(connectionIDs, nil) if err != nil { p.log.Printf("error updating host ports: %v\n", err) } return nil } func (p *ClientRoutesHandler) Stop() { if p.updateTasks != nil { close(p.updateTasks) } if p.closeChan != nil { close(p.closeChan) } if p.sub != nil { p.sub.Stop() } } // resolveAndUpdateInPlace updates provided list of resolved endpoint in place // If it can't resolve it keeps old record as is. // Logic to pick a single address from all available addresses is delegated to ClientRoutesResolver at p.endpointResolver // It does not resolve everything, it picks endpoints that are: // 1. Marked via forcedResolve=true, // 2. Have not resolved previously and have no ip address information // 3. Was resolved more than cfg.ResolveHealthyEndpointPeriod ago. func (p *ClientRoutesHandler) resolveAndUpdateInPlace(records ResolvedClientRouteList) error { if len(records) == 0 { return nil } errs := make([]error, len(records)) tasks := make(chan int, len(records)) var cutoffTimeForHealthy time.Time if p.cfg.ResolveHealthyEndpointPeriod == 0 { cutoffTimeForHealthy = never } else { cutoffTimeForHealthy = time.Now().UTC().Add(-p.cfg.ResolveHealthyEndpointPeriod) } scheduled := false for id, endpoint := range records { if endpoint.currentIP == nil || len(endpoint.allKnownIPs) == 0 || endpoint.forcedResolve { scheduled = true tasks <- id } else if endpoint.updateTime.Before(cutoffTimeForHealthy) { scheduled = true tasks <- id } } if !scheduled { return nil } var wg sync.WaitGroup for i := 0; i < p.cfg.MaxResolverConcurrency; i++ { wg.Add(1) go func() { defer wg.Done() for id := range tasks { all, currentIP, err := p.resolver.Resolve(records[id]) records[id].updateTime = time.Now().UTC() if err != nil { errs[id] = fmt.Errorf("resolve %s failed: %w", records[id].currentIP, err) continue } else if len(all) == 0 { errs[id] = fmt.Errorf("resolve %s: no addresses returned", records[id].currentIP) } else if currentIP == nil { errs[id] = fmt.Errorf("resolve %s: no current addres has been set, should not happen, please report a bug", records[id].currentIP) } else { // Reset forcedResolve is it was resolved successfully records[id].forcedResolve = false } records[id].allKnownIPs = all records[id].currentIP = currentIP } }() } close(tasks) wg.Wait() return errors.Join(errs...) } func (p *ClientRoutesHandler) updateHostPortMappingAsync(connectionIDs []string, hostIDs []string) { p.updateTasks <- updateTask{ connectionIDs: connectionIDs, hostIDs: hostIDs, } } func (p *ClientRoutesHandler) updateHostPortMappingSync(connectionIDs []string, hostIDs []string) error { result := make(chan error, 1) p.updateTasks <- updateTask{ connectionIDs: connectionIDs, hostIDs: hostIDs, result: result, } return <-result } func (p *ClientRoutesHandler) startReadingEvents() { connectionIDs := p.cfg.Endpoints.GetAllConnectionIDs() go func() { for event := range p.sub.Events() { switch evt := event.(type) { case *events.ClientRoutesChangedEvent: if debug.Enabled { if len(evt.ConnectionIDs) == 0 { p.log.Printf("got CLIENT_ROUTES_CHANGE event with no connection IDs") continue } if len(evt.HostIDs) == 0 { p.log.Printf("got CLIENT_ROUTES_CHANGE event with no host IDs") continue } } var newConnectionIDs []string for _, connectionID := range evt.ConnectionIDs { if connectionID == "" { continue } if slices.ContainsFunc(p.cfg.Endpoints, func(ep ClientRoutesEndpoint) bool { return ep.ConnectionID == connectionID }) { newConnectionIDs = append(newConnectionIDs, connectionID) } } if len(newConnectionIDs) != 0 { p.updateHostPortMappingAsync(newConnectionIDs, evt.HostIDs) } case *events.ControlConnectionRecreatedEvent: p.updateHostPortMappingAsync(connectionIDs, nil) } } }() } func (p *ClientRoutesHandler) startUpdateWorker() { go func() { for task := range p.updateTasks { err := p.updateHostPortMapping(task.connectionIDs, task.hostIDs) if err != nil { if debug.Enabled { p.log.Printf("failed to update host port mapping: %v", err) } } if task.result != nil { task.result <- err close(task.result) } } }() } func (p *ClientRoutesHandler) updateHostPortMapping(connectionIDs []string, hostIDs []string) error { unresolved, err := getHostPortMappingFromCluster(p.c, p.cfg.TableName, connectionIDs, hostIDs) if err != nil { return err } current := p.resolvedEndpoints.Load() updated := slices.Clone(*current) updated.MergeWithUnresolved(unresolved) err = p.resolveAndUpdateInPlace(updated) if err != nil { p.log.Printf("failed to resolve endpoints: %v", err) // Despite an error it is better to save results, it should not corrupt existing and resolved records } // Try to update until it successes // 10 times is more than enough, if it fails for range 10 { if p.resolvedEndpoints.CompareAndSwap(current, &updated) { return nil } current = p.resolvedEndpoints.Load() updated.MergeWithResolved(current) } p.log.Printf("failed to update host port mapping due to collisions") return nil } func NewClientRoutesAddressTranslator( cfg ClientRoutesConfig, resolver DNSResolver, pickTLSPorts bool, log StdLogger, ) *ClientRoutesHandler { res := &ClientRoutesHandler{ cfg: cfg, log: log, pickTLSPorts: pickTLSPorts, closeChan: make(chan struct{}), updateTasks: make(chan updateTask, 1024), resolver: newSimpleClientRoutesResolver(cfg.ResolverCacheDuration, resolver), } res.resolvedEndpoints.Store(&ResolvedClientRouteList{}) return res } var _ AddressTranslator = &ClientRoutesHandler{} func getHostPortMappingFromCluster(c controlConnection, table string, connectionIDs []string, hostIDs []string) (UnresolvedClientRouteList, error) { var res UnresolvedClientRouteList stmt := []string{fmt.Sprintf("select connection_id, host_id, address, port, tls_port from %s", table)} var bounds []any if len(connectionIDs) != 0 { var inClause []string for _, connectionID := range connectionIDs { bounds = append(bounds, connectionID) inClause = append(inClause, "?") } if len(stmt) == 1 { stmt = append(stmt, "where") } stmt = append(stmt, fmt.Sprintf("connection_id in (%s)", strings.Join(inClause, ","))) } if len(hostIDs) != 0 { var inClause []string for _, hostID := range hostIDs { bounds = append(bounds, hostID) inClause = append(inClause, "?") } if len(stmt) == 1 { stmt = append(stmt, "where") } else { stmt = append(stmt, "and") } stmt = append(stmt, fmt.Sprintf("host_id in (%s)", strings.Join(inClause, ","))) } isFullScan := len(hostIDs) == 0 || len(connectionIDs) == 0 if isFullScan { stmt = append(stmt, "allow filtering") } iter := c.query(strings.Join(stmt, " "), bounds...) var rec UnresolvedClientRoute for iter.Scan(&rec.ConnectionID, &rec.HostID, &rec.Address, &rec.CQLPort, &rec.SecureCQLPort) { res = append(res, rec) } if err := iter.Close(); err != nil { return nil, fmt.Errorf("error reading %s table: %v", table, err) } return res, nil } ================================================ FILE: client_routes_test.go ================================================ //go:build integration // +build integration package gocql import ( "fmt" "net" "sort" "testing" "github.com/google/go-cmp/cmp" ) func TestGetHostPortMapping(t *testing.T) { t.Parallel() keyspace := testKeyspaceName(t) cluster := createCluster() createKeyspace(t, cluster, keyspace, true) cluster.Keyspace = keyspace session, err := cluster.CreateSession() if err != nil { t.Fatalf("failed to create session: %v", err) } defer session.Close() table := testTableName(t) qualifiedTable := keyspace + "." + table if err := createTable(session, fmt.Sprintf(`CREATE TABLE %s.%s ( connection_id uuid, host_id uuid, Address text, port int, tls_port int, alternator_port int, alternator_https_port int, Datacenter text, Rack text, PRIMARY KEY (connection_id, host_id))`, keyspace, table)); err != nil { t.Fatal(err) } var hostIDs []string for i := 0; i < 3; i++ { hostIDs = append(hostIDs, MustRandomUUID().String()) } var connectionIDs []string for i := 0; i < 3; i++ { connectionIDs = append(connectionIDs, MustRandomUUID().String()) } racks := []string{"rack1", "rack2", "rack3"} expected := []UnresolvedClientRoute{} for id, hostID := range hostIDs { rack := racks[id] ip := net.ParseIP(fmt.Sprintf("127.0.0.%d", id+1)) for _, connectionID := range connectionIDs { err := session.Query( fmt.Sprintf(`INSERT INTO %s ( connection_id, host_id, Address, port, tls_port, alternator_port, alternator_https_port, Datacenter, Rack) VALUES (?, ?, ?, 9042, 9142, 0, 0, 'dc1', ?);`, qualifiedTable), connectionID, hostID, ip.String(), rack, ).Exec() if err != nil { t.Fatalf("unable to insert connection metadata: %s", err.Error()) } expected = append(expected, UnresolvedClientRoute{ ConnectionID: connectionID, HostID: hostID, Address: ip.String(), CQLPort: 9042, SecureCQLPort: 9142, }) } } sortUnresolvedHostPorts(expected) tcases := []struct { name string method func(controlConnection) ([]UnresolvedClientRoute, error) expected []UnresolvedClientRoute }{ { name: "get-all", method: func(controlConnection) ([]UnresolvedClientRoute, error) { return getHostPortMappingFromCluster(session.control, qualifiedTable, nil, nil) }, expected: expected, }, { name: "get-all-hosts", method: func(controlConnection) ([]UnresolvedClientRoute, error) { return getHostPortMappingFromCluster(session.control, qualifiedTable, connectionIDs, nil) }, expected: expected, }, { name: "get-all-connections", method: func(controlConnection) ([]UnresolvedClientRoute, error) { return getHostPortMappingFromCluster(session.control, qualifiedTable, nil, hostIDs) }, expected: expected, }, { name: "get-concrete", method: func(controlConnection) ([]UnresolvedClientRoute, error) { return getHostPortMappingFromCluster(session.control, qualifiedTable, connectionIDs, hostIDs) }, expected: expected, }, { name: "get-concrete-host", method: func(controlConnection) ([]UnresolvedClientRoute, error) { return getHostPortMappingFromCluster(session.control, qualifiedTable, connectionIDs, hostIDs) }, expected: expected, }, } for _, tc := range tcases { t.Run(tc.name, func(t *testing.T) { got, err := tc.method(session.control) if err != nil { t.Fatal(err) } sortUnresolvedHostPorts(got) if diff := cmp.Diff(got, tc.expected); diff != "" { t.Errorf("got unexpected result: %s", diff) } }) } } func sortUnresolvedHostPorts(xs []UnresolvedClientRoute) { sort.Slice(xs, func(i, j int) bool { a, b := xs[i], xs[j] if a.ConnectionID != b.ConnectionID { return a.ConnectionID < b.ConnectionID // or bytes.Compare if raw [16]byte } return a.HostID < b.HostID }) } ================================================ FILE: client_routes_unit_test.go ================================================ //go:build unit // +build unit package gocql import ( "errors" "fmt" "net" "sync/atomic" "testing" "time" ) type dnsResolverFunc func(string) ([]net.IP, error) // LookupIP implements DNSResolver for dnsResolverFunc. func (f dnsResolverFunc) LookupIP(host string) ([]net.IP, error) { return f(host) } type clientRoutesResolverFunc func(endpoint ResolvedClientRoute) ([]net.IP, net.IP, error) func (f clientRoutesResolverFunc) Resolve(endpoint ResolvedClientRoute) ([]net.IP, net.IP, error) { return f(endpoint) } type fakeControlConn struct { statement string values []any } func (f *fakeControlConn) getConn() *connHost { return nil } func (f *fakeControlConn) awaitSchemaAgreement() error { return nil } func (f *fakeControlConn) query(statement string, values ...any) *Iter { f.statement = statement f.values = values return &Iter{} } func (f *fakeControlConn) querySystem(statement string, values ...any) *Iter { return &Iter{} } func (f *fakeControlConn) discoverProtocol(hosts []*HostInfo) (int, error) { return 0, nil } func (f *fakeControlConn) connect(hosts []*HostInfo) error { return nil } func (f *fakeControlConn) close() {} func (f *fakeControlConn) getSession() *Session { return nil } func (f *fakeControlConn) reconnect() error { return nil } type testHostInfo struct { hostID string } func (t testHostInfo) HostID() string { return t.hostID } func (t testHostInfo) Rack() string { return "" } func (t testHostInfo) DataCenter() string { return "" } func (t testHostInfo) BroadcastAddress() net.IP { return nil } func (t testHostInfo) ListenAddress() net.IP { return nil } func (t testHostInfo) RPCAddress() net.IP { return nil } func (t testHostInfo) PreferredIP() net.IP { return nil } func (t testHostInfo) Peer() net.IP { return nil } func (t testHostInfo) UntranslatedConnectAddress() net.IP { return nil } func (t testHostInfo) Port() int { return 0 } func (t testHostInfo) Partitioner() string { return "" } func (t testHostInfo) ClusterName() string { return "" } func (t testHostInfo) ScyllaShardAwarePort() uint16 { return 0 } func (t testHostInfo) ScyllaShardAwarePortTLS() uint16 { return 0 } func (t testHostInfo) ScyllaShardCount() int { return 0 } func TestResolvedClientRouteCloneNewerNeedsUpdate(t *testing.T) { ip1 := net.ParseIP("127.0.0.1") ip2 := net.ParseIP("127.0.0.2") base := ResolvedClientRoute{ UnresolvedClientRoute: UnresolvedClientRoute{ ConnectionID: "c1", HostID: "h1", Address: "host", CQLPort: 9042, }, allKnownIPs: []net.IP{ip1}, currentIP: ip1, updateTime: time.Unix(10, 0), } clone := base.Clone() clone.allKnownIPs[0][0] = 8 clone.currentIP[0] = 9 if base.allKnownIPs[0][0] == 8 { t.Fatalf("Clone should not share allKnownIPs slices") } if base.currentIP[0] == 9 { t.Fatalf("Clone should not share currentIP slices") } newerIP := ResolvedClientRoute{currentIP: ip2} if !(ResolvedClientRoute{}).Newer(newerIP) { t.Fatalf("expected Newer to prefer non-nil currentIP") } newerTime := ResolvedClientRoute{updateTime: time.Unix(20, 0)} if !base.Newer(newerTime) { t.Fatalf("expected Newer to prefer newer updateTime") } if !(ResolvedClientRoute{currentIP: nil}).NeedsUpdate() { t.Fatalf("expected NeedsUpdate for missing currentIP") } if !(ResolvedClientRoute{currentIP: ip1}).NeedsUpdate() { t.Fatalf("expected NeedsUpdate for missing allKnownIPs") } if !(ResolvedClientRoute{currentIP: ip1, allKnownIPs: []net.IP{ip1}, forcedResolve: true}).NeedsUpdate() { t.Fatalf("expected NeedsUpdate when forcedResolve is set") } if (ResolvedClientRoute{currentIP: ip1, allKnownIPs: []net.IP{ip1}}).NeedsUpdate() { t.Fatalf("did not expect NeedsUpdate for fully resolved route") } } func TestResolvedClientRouteListMergeWithUnresolved(t *testing.T) { list := ResolvedClientRouteList{ { UnresolvedClientRoute: UnresolvedClientRoute{ ConnectionID: "c1", HostID: "h1", Address: "a1", CQLPort: 9042, }, forcedResolve: false, }, } list.MergeWithUnresolved(UnresolvedClientRouteList{ { ConnectionID: "c1", HostID: "h1", Address: "a1", CQLPort: 9042, }, }) if len(list) != 1 || list[0].forcedResolve { t.Fatalf("expected unchanged record when unresolved is equal") } list.MergeWithUnresolved(UnresolvedClientRouteList{ { ConnectionID: "c1", HostID: "h1", Address: "a2", CQLPort: 9043, }, }) if list[0].Address != "a2" || list[0].CQLPort != 9043 || !list[0].forcedResolve { t.Fatalf("expected record to update and force resolve") } list = ResolvedClientRouteList{} list.MergeWithUnresolved(UnresolvedClientRouteList{ { ConnectionID: "c2", HostID: "h2", Address: "a3", CQLPort: 9044, }, }) if len(list) != 1 || !list[0].forcedResolve { t.Fatalf("expected new record to be appended with forcedResolve") } } func TestResolvedClientRouteListMergeWithResolved(t *testing.T) { older := ResolvedClientRoute{ UnresolvedClientRoute: UnresolvedClientRoute{ConnectionID: "c1", HostID: "h1"}, updateTime: time.Unix(10, 0), } newer := ResolvedClientRoute{ UnresolvedClientRoute: UnresolvedClientRoute{ConnectionID: "c1", HostID: "h1"}, updateTime: time.Unix(20, 0), currentIP: net.ParseIP("10.0.0.1"), } list := ResolvedClientRouteList{older} other := ResolvedClientRouteList{newer, {UnresolvedClientRoute: UnresolvedClientRoute{ConnectionID: "c2", HostID: "h2"}}} list.MergeWithResolved(&other) if list[0].updateTime != newer.updateTime || list[0].currentIP == nil { t.Fatalf("expected newer record to replace older one") } if len(list) != 2 { t.Fatalf("expected new record to be appended") } list = ResolvedClientRouteList{newer} stale := ResolvedClientRouteList{older} list.MergeWithResolved(&stale) if list[0].updateTime != newer.updateTime { t.Fatalf("expected newer record to be preserved when other is stale") } } func TestResolvedClientRouteListUpdateIfNewerAndFindByHostID(t *testing.T) { list := ResolvedClientRouteList{{ UnresolvedClientRoute: UnresolvedClientRoute{ConnectionID: "c1", HostID: "h1"}, updateTime: time.Unix(10, 0), }} older := ResolvedClientRoute{UnresolvedClientRoute: UnresolvedClientRoute{ConnectionID: "c1", HostID: "h1"}, updateTime: time.Unix(5, 0)} if list.UpdateIfNewer(older) { t.Fatalf("expected UpdateIfNewer to ignore older record") } newer := ResolvedClientRoute{UnresolvedClientRoute: UnresolvedClientRoute{ConnectionID: "c1", HostID: "h1"}, updateTime: time.Unix(15, 0)} if !list.UpdateIfNewer(newer) { t.Fatalf("expected UpdateIfNewer to accept newer record") } rec := list.FindByHostID("h1") if rec == nil { t.Fatalf("expected FindByHostID to locate record") } rec.ConnectionID = "updated" if list[0].ConnectionID != "updated" { t.Fatalf("expected FindByHostID to return pointer to list element") } } func TestSimpleClientRoutesResolverResolve(t *testing.T) { calls := 0 resolver := dnsResolverFunc(func(host string) ([]net.IP, error) { calls++ return []net.IP{net.ParseIP("10.0.0.1"), net.ParseIP("10.0.0.2")}, nil }) res := newSimpleClientRoutesResolver(time.Hour, resolver) endpoint := ResolvedClientRoute{ UnresolvedClientRoute: UnresolvedClientRoute{Address: "example"}, currentIP: net.ParseIP("10.0.0.2"), } all, current, err := res.Resolve(endpoint) if err != nil { t.Fatalf("unexpected error: %v", err) } if calls != 1 { t.Fatalf("expected resolver to be called once, got %d", calls) } if current == nil || !current.Equal(endpoint.currentIP) { t.Fatalf("expected currentIP to be preserved when present") } if len(all) != 2 { t.Fatalf("expected allKnownIPs to be returned") } _, _, err = res.Resolve(endpoint) if err != nil { t.Fatalf("unexpected error from cached resolve: %v", err) } if calls != 1 { t.Fatalf("expected cached resolve to avoid LookupIP, got %d", calls) } resolveErr := errors.New("resolve failed") errorResolver := dnsResolverFunc(func(host string) ([]net.IP, error) { return nil, resolveErr }) errorRes := newSimpleClientRoutesResolver(0, errorResolver) endpoint.allKnownIPs = []net.IP{net.ParseIP("10.0.0.9")} all, current, err = errorRes.Resolve(endpoint) if !errors.Is(err, resolveErr) { t.Fatalf("expected resolver error to propagate") } if len(all) != 1 || current == nil || !current.Equal(endpoint.currentIP) { t.Fatalf("expected existing values to be returned on error") } emptyResolver := dnsResolverFunc(func(host string) ([]net.IP, error) { return []net.IP{}, nil }) emptyRes := newSimpleClientRoutesResolver(0, emptyResolver) _, _, err = emptyRes.Resolve(ResolvedClientRoute{UnresolvedClientRoute: UnresolvedClientRoute{Address: "example"}}) if err == nil { t.Fatalf("expected error when resolver returns empty list") } } func TestClientRoutesHandlerTranslateHost(t *testing.T) { addr := AddressPort{Address: net.ParseIP("1.1.1.1"), Port: 9042} noHost := testHostInfo{hostID: ""} missingHost := testHostInfo{hostID: "missing"} handler := &ClientRoutesHandler{} handler.resolvedEndpoints.Store(&ResolvedClientRouteList{}) res, err := handler.TranslateHost(noHost, addr) if err != nil { t.Fatalf("unexpected error for empty hostID: %v", err) } if !res.Equal(addr) { t.Fatalf("expected address to pass through when hostID is empty") } _, err = handler.TranslateHost(missingHost, addr) if err == nil { t.Fatalf("expected error for missing host entry") } resolvedList := ResolvedClientRouteList{ { UnresolvedClientRoute: UnresolvedClientRoute{ConnectionID: "c1", HostID: "h1", CQLPort: 9042, SecureCQLPort: 9142}, currentIP: net.ParseIP("10.0.0.1"), }, } handler.pickTLSPorts = false handler.resolvedEndpoints.Store(&resolvedList) res, err = handler.TranslateHost(testHostInfo{hostID: "h1"}, addr) if err != nil { t.Fatalf("unexpected error: %v", err) } if res.Port != 9042 { t.Fatalf("expected non-TLS port, got %d", res.Port) } handler.pickTLSPorts = true res, err = handler.TranslateHost(testHostInfo{hostID: "h1"}, addr) if err != nil { t.Fatalf("unexpected error: %v", err) } if res.Port != 9142 { t.Fatalf("expected TLS port, got %d", res.Port) } errorHandler := &ClientRoutesHandler{ resolver: clientRoutesResolverFunc(func(endpoint ResolvedClientRoute) ([]net.IP, net.IP, error) { return nil, nil, errors.New("lookup failed") }), } errorList := ResolvedClientRouteList{{UnresolvedClientRoute: UnresolvedClientRoute{ConnectionID: "c2", HostID: "h2", Address: "host"}}} errorHandler.resolvedEndpoints.Store(&errorList) _, err = errorHandler.TranslateHost(testHostInfo{hostID: "h2"}, addr) if err == nil { t.Fatalf("expected resolver error to bubble up") } } func TestClientRoutesHandlerTranslateHost_CASCollision(t *testing.T) { addr := AddressPort{Address: net.ParseIP("1.1.1.1"), Port: 9042} resolverStarted := make(chan struct{}) releaseResolver := make(chan struct{}) resolver := clientRoutesResolverFunc(func(endpoint ResolvedClientRoute) ([]net.IP, net.IP, error) { close(resolverStarted) <-releaseResolver ip := net.ParseIP("10.0.0.1") return []net.IP{ip}, ip, nil }) handler := &ClientRoutesHandler{resolver: resolver, pickTLSPorts: false} origList := ResolvedClientRouteList{{UnresolvedClientRoute: UnresolvedClientRoute{ConnectionID: "c1", HostID: "h1", Address: "host", CQLPort: 9042}}} handler.resolvedEndpoints.Store(&origList) done := make(chan error, 1) go func() { _, err := handler.TranslateHost(testHostInfo{hostID: "h1"}, addr) done <- err }() <-resolverStarted altList := ResolvedClientRouteList{} handler.resolvedEndpoints.Store(&altList) close(releaseResolver) time.Sleep(10 * time.Millisecond) handler.resolvedEndpoints.Store(&origList) select { case err := <-done: if err != nil { t.Fatalf("unexpected error after CAS collision: %v", err) } case <-time.After(2 * time.Second): t.Fatal("TranslateHost timed out after CAS collision") } } func TestClientRoutesHandlerResolveAndUpdateInPlace(t *testing.T) { var inFlight int32 var maxInFlight int32 called := make(chan string, 4) resolveErr := errors.New("resolve error") resolver := clientRoutesResolverFunc(func(endpoint ResolvedClientRoute) ([]net.IP, net.IP, error) { curr := atomic.AddInt32(&inFlight, 1) for { prev := atomic.LoadInt32(&maxInFlight) if curr > prev && atomic.CompareAndSwapInt32(&maxInFlight, prev, curr) { break } if curr <= prev { break } } defer atomic.AddInt32(&inFlight, -1) called <- endpoint.Address time.Sleep(10 * time.Millisecond) if endpoint.Address == "err" { return nil, nil, resolveErr } ip := net.ParseIP("10.0.0.1") return []net.IP{ip}, ip, nil }) handler := &ClientRoutesHandler{ resolver: resolver, cfg: ClientRoutesConfig{ MaxResolverConcurrency: 2, ResolveHealthyEndpointPeriod: time.Hour, }, } now := time.Now().UTC() ip := net.ParseIP("10.0.0.2") records := ResolvedClientRouteList{ { UnresolvedClientRoute: UnresolvedClientRoute{ConnectionID: "c1", HostID: "h1", Address: "healthy"}, currentIP: ip, allKnownIPs: []net.IP{ip}, updateTime: now, }, { UnresolvedClientRoute: UnresolvedClientRoute{ConnectionID: "c2", HostID: "h2", Address: "forced"}, forcedResolve: true, }, { UnresolvedClientRoute: UnresolvedClientRoute{ConnectionID: "c3", HostID: "h3", Address: "empty"}, }, { UnresolvedClientRoute: UnresolvedClientRoute{ConnectionID: "c4", HostID: "h4", Address: "stale"}, currentIP: ip, allKnownIPs: []net.IP{ip}, updateTime: now.Add(-2 * time.Hour), }, { UnresolvedClientRoute: UnresolvedClientRoute{ConnectionID: "c5", HostID: "h5", Address: "err"}, }, } err := handler.resolveAndUpdateInPlace(records) if err == nil || !errors.Is(err, resolveErr) { t.Fatalf("expected aggregated error to include resolver error") } close(called) calledMap := map[string]bool{} for addr := range called { calledMap[addr] = true } if calledMap["healthy"] { t.Fatalf("did not expect healthy endpoint to be resolved") } for _, addr := range []string{"forced", "empty", "stale", "err"} { if !calledMap[addr] { t.Fatalf("expected resolver to be called for %s", addr) } } if atomic.LoadInt32(&maxInFlight) > int32(handler.cfg.MaxResolverConcurrency) { t.Fatalf("expected max concurrency <= %d, got %d", handler.cfg.MaxResolverConcurrency, maxInFlight) } if records[1].currentIP == nil || len(records[1].allKnownIPs) == 0 || records[1].forcedResolve { t.Fatalf("expected forced endpoint to be resolved and forcedResolve cleared") } } func TestGetHostPortMappingFromClusterQuery(t *testing.T) { tcases := []struct { name string connectionIDs []string hostIDs []string expectedStmt string expectedVals []any }{ { name: "all", expectedStmt: "select connection_id, host_id, address, port, tls_port from system.client_routes allow filtering", }, { name: "connections-only", connectionIDs: []string{"c1", "c2"}, expectedStmt: "select connection_id, host_id, address, port, tls_port from system.client_routes where connection_id in (?,?) allow filtering", expectedVals: []any{"c1", "c2"}, }, { name: "hosts-only", hostIDs: []string{"h1"}, expectedStmt: "select connection_id, host_id, address, port, tls_port from system.client_routes where host_id in (?) allow filtering", expectedVals: []any{"h1"}, }, { name: "connections-and-hosts", connectionIDs: []string{"c1"}, hostIDs: []string{"h1", "h2"}, expectedStmt: "select connection_id, host_id, address, port, tls_port from system.client_routes where connection_id in (?) and host_id in (?,?)", expectedVals: []any{"c1", "h1", "h2"}, }, { name: "empty-slices", connectionIDs: []string{}, hostIDs: []string{}, expectedStmt: "select connection_id, host_id, address, port, tls_port from system.client_routes allow filtering", }, } for _, tc := range tcases { t.Run(tc.name, func(t *testing.T) { ctrl := &fakeControlConn{} _, err := getHostPortMappingFromCluster(ctrl, "system.client_routes", tc.connectionIDs, tc.hostIDs) if err != nil { t.Fatalf("unexpected error: %v", err) } if ctrl.statement != tc.expectedStmt { t.Fatalf("statement mismatch: got %q want %q", ctrl.statement, tc.expectedStmt) } if fmt.Sprint(ctrl.values) != fmt.Sprint(tc.expectedVals) { t.Fatalf("values mismatch: got %v want %v", ctrl.values, tc.expectedVals) } }) } } ================================================ FILE: cloud_cluster_test.go ================================================ //go:build integration // +build integration package gocql_test import ( "bytes" "context" "crypto/tls" "fmt" "io" "net" "os" "strings" "sync" "testing" "time" "sigs.k8s.io/yaml" "github.com/gocql/gocql" "github.com/gocql/gocql/scyllacloud" ) func TestCloudConnection(t *testing.T) { t.Parallel() if !*gocql.FlagRunSslTest { t.Skip("Skipping because SSL is not enabled on cluster") } if *gocql.FlagDistribution != "scylla" { t.Skip("Skipping because it is designed for scylla, but running on something else") } const ( sslPort = 9142 datacenterName = "datacenter1" ) ctx, cancel := context.WithCancel(context.Background()) defer cancel() hosts := map[string]string{} cluster := gocql.CreateCluster(func(config *gocql.ClusterConfig) { config.Port = sslPort }) session, err := cluster.CreateSession() if err != nil { t.Fatal(err) } var localAddress string var localHostID gocql.UUID scanner := session.Query("SELECT broadcast_address, host_id FROM system.local WHERE key='local'").Iter().Scanner() if scanner.Next() { if err := scanner.Scan(&localAddress, &localHostID); err != nil { t.Fatal(err) } hosts[localHostID.String()] = net.JoinHostPort(localAddress, fmt.Sprintf("%d", sslPort)) } var peerAddress string var peerHostID gocql.UUID scanner = session.Query("SELECT peer, host_id FROM system.peers").Iter().Scanner() for scanner.Next() { if err := scanner.Scan(&peerAddress, &peerHostID); err != nil { t.Fatal(err) } hosts[peerHostID.String()] = net.JoinHostPort(peerAddress, fmt.Sprintf("%d", sslPort)) } session.Close() logger := gocql.TestLogger defer func() { if t.Failed() { os.Stdout.WriteString(logger.String()) } }() proxy := &sniProxy{ hosts: hosts, defaultBackend: net.JoinHostPort(localAddress, fmt.Sprintf("%d", sslPort)), logger: logger, } proxyAddress, err := proxy.Run(ctx) if err != nil { t.Fatal(err) } defer proxy.Close() cc := &scyllacloud.ConnectionConfig{ Datacenters: map[string]*scyllacloud.Datacenter{ datacenterName: { CertificateAuthorityPath: "testdata/pki/ca.crt", Server: proxyAddress, TLSServerName: "any", NodeDomain: "cloud.scylladb.com", InsecureSkipTLSVerify: true, }, }, AuthInfos: map[string]*scyllacloud.AuthInfo{ "ai-1": { Username: "username", Password: "password", ClientKeyPath: "testdata/pki/gocql.key", ClientCertificatePath: "testdata/pki/gocql.crt", }, }, Contexts: map[string]*scyllacloud.Context{ "default-context": { AuthInfoName: "ai-1", DatacenterName: datacenterName, }, }, CurrentContext: "default-context", } configPath, err := writeYamlToTempFile(cc) if err != nil { t.Fatal(err) } defer os.RemoveAll(configPath) cluster, err = scyllacloud.NewCloudCluster(configPath) if err != nil { t.Fatal(err) } // Forward connections directed to node domain to our test sni proxy. cluster.Dialer = dialerContextFunc(func(ctx context.Context, network, addr string) (net.Conn, error) { if strings.Contains(addr, cc.Datacenters[datacenterName].NodeDomain) { addr = cc.Datacenters[datacenterName].Server } return net.Dial(network, addr) }) session, err = cluster.CreateSession() if err != nil { t.Fatal(err) } if err := gocql.WaitUntilPoolsStopFilling(ctx, session, 10*time.Second); err != nil { t.Fatal(err) } ringHosts := gocql.GetRingAllHosts(session) if len(ringHosts) != len(hosts) { t.Errorf("expected %d hosts in ring, got %d", len(hosts), len(ringHosts)) } snisCount := map[string]int{} events := proxy.GetEvents() for _, event := range events { snisCount[event]++ } for hostID := range hosts { sni := fmt.Sprintf("%s.%s", hostID, cc.Datacenters[datacenterName].NodeDomain) count, ok := snisCount[sni] if !ok { t.Errorf("not found connection to host %q", hostID) } if count != cluster.NumConns { t.Errorf("expected %d connections to host %q, got %d", cluster.NumConns, sni, count) } } } func writeYamlToTempFile(obj any) (string, error) { f, err := os.CreateTemp(os.TempDir(), "gocql-cloud") if err != nil { return "", fmt.Errorf("create temp file: %w", err) } if err := f.Close(); err != nil { return "", fmt.Errorf("close temp file: %w", err) } buf, err := yaml.Marshal(obj) if err != nil { return "", fmt.Errorf("marshal yaml: %w", err) } if err := os.WriteFile(f.Name(), buf, 0600); err != nil { return "", fmt.Errorf("write to file %q: %w", f.Name(), err) } return f.Name(), nil } type dialerContextFunc func(ctx context.Context, network, addr string) (net.Conn, error) func (d dialerContextFunc) DialContext(ctx context.Context, network, addr string) (net.Conn, error) { return d(ctx, network, addr) } type sniProxy struct { hosts map[string]string defaultBackend string logger gocql.StdLogger listener net.Listener events []string mu sync.Mutex } func (p *sniProxy) Run(ctx context.Context) (string, error) { listener, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { return "", fmt.Errorf("failed to listen: %w", err) } p.listener = listener go func() { for { conn, err := p.listener.Accept() if err != nil { p.logger.Println("failed to accept connection", err) return } go p.handleConnection(conn) } }() return listener.Addr().String(), nil } func (p *sniProxy) handleConnection(conn net.Conn) { defer conn.Close() var hello *tls.ClientHelloInfo peekedBytes := &bytes.Buffer{} // Ignore error because TLS library returns it when nil TLSConfig is returned. _ = tls.Server(readOnlyConn{reader: io.TeeReader(conn, peekedBytes)}, &tls.Config{ GetConfigForClient: func(argHello *tls.ClientHelloInfo) (*tls.Config, error) { hello = &tls.ClientHelloInfo{} *hello = *argHello return nil, nil }, }).Handshake() if hello == nil { p.logger.Println("client hello not sent") return } p.mu.Lock() p.events = append(p.events, hello.ServerName) p.mu.Unlock() backend, ok := p.hosts[hello.ServerName] if !ok { backend = p.defaultBackend } p.logger.Println("Dialing backend", backend, "SNI", hello.ServerName) backendConn, err := net.Dial("tcp", backend) if err != nil { p.logger.Println("failed to dial backend", backend, err) return } defer backendConn.Close() var wg sync.WaitGroup wg.Add(2) go func() { _, _ = io.Copy(conn, backendConn) wg.Done() }() go func() { _, _ = io.Copy(backendConn, peekedBytes) _, _ = io.Copy(backendConn, conn) wg.Done() }() wg.Wait() } func (p *sniProxy) Close() error { return p.listener.Close() } func (p *sniProxy) GetEvents() []string { p.mu.Lock() defer p.mu.Unlock() events := make([]string, 0, len(p.events)) for _, e := range p.events { events = append(events, e) } return events } type readOnlyConn struct { reader io.Reader } var _ net.Conn = readOnlyConn{} func (conn readOnlyConn) Read(p []byte) (int, error) { return conn.reader.Read(p) } func (conn readOnlyConn) Write(p []byte) (int, error) { return 0, io.ErrClosedPipe } func (conn readOnlyConn) Close() error { return nil } func (conn readOnlyConn) LocalAddr() net.Addr { return nil } func (conn readOnlyConn) RemoteAddr() net.Addr { return nil } func (conn readOnlyConn) SetDeadline(t time.Time) error { return nil } func (conn readOnlyConn) SetReadDeadline(t time.Time) error { return nil } func (conn readOnlyConn) SetWriteDeadline(t time.Time) error { return nil } ================================================ FILE: cluster.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2012, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "context" "crypto/tls" "crypto/x509" "errors" "fmt" "net" "os" "sync/atomic" "time" "github.com/gocql/gocql/internal/eventbus" ) const defaultDriverName = "ScyllaDB GoCQL Driver" // PoolConfig configures the connection pool used by the driver, it defaults to // using a round-robin host selection policy and a round-robin connection selection // policy for each host. type PoolConfig struct { // HostSelectionPolicy sets the policy for selecting which host to use for a // given query (default: RoundRobinHostPolicy()) // It is not supported to use a single HostSelectionPolicy in multiple sessions // (even if you close the old session before using in a new session). HostSelectionPolicy HostSelectionPolicy } func (p PoolConfig) buildPool(session *Session) *policyConnPool { return newPolicyConnPool(session) } // ClusterConfig is a struct to configure the default cluster implementation // of gocql. It has a variety of attributes that can be used to modify the // behavior to fit the most common use cases. Applications that require a // different setup must implement their own cluster. type ClusterConfig struct { // BatchObserver will set the provided batch observer on all queries created from this session. // Use it to collect metrics / stats from batch queries by providing an implementation of BatchObserver. BatchObserver BatchObserver // Dialer will be used to establish all connections created for this Cluster. // If not provided, a default dialer configured with ConnectTimeout will be used. // Dialer is ignored if HostDialer is provided. Dialer Dialer // ApplicationInfo reports application information to the server by inserting it into options of the STARTUP frame ApplicationInfo ApplicationInfo // DNSResolver Resolves DNS names to IP addresses DNSResolver DNSResolver // Logger for this ClusterConfig. // If not specified, defaults to the gocql.defaultLogger. Logger StdLogger // HostDialer will be used to establish all connections for this Cluster. // Unlike Dialer, HostDialer is responsible for setting up the entire connection, including the TLS session. // To support shard-aware port, HostDialer should implement ShardDialer. // If not provided, Dialer will be used instead. HostDialer HostDialer // StreamObserver will be notified of stream state changes. // This can be used to track in-flight protocol requests and responses. StreamObserver StreamObserver // FrameHeaderObserver will set the provided frame header observer on all frames' headers created from this session. // Use it to collect metrics / stats from frames by providing an implementation of FrameHeaderObserver. FrameHeaderObserver FrameHeaderObserver // ConnectObserver will set the provided connect observer on all queries // created from this session. ConnectObserver ConnectObserver // QueryObserver will set the provided query observer on all queries created from this session. // Use it to collect metrics / stats from queries by providing an implementation of QueryObserver. QueryObserver QueryObserver // AddressTranslator will translate addresses found on peer discovery and/or // node change events. AddressTranslator AddressTranslator // HostFilter will filter all incoming events for host, any which don't pass // the filter will be ignored. If set will take precedence over any options set // via Discovery HostFilter HostFilter // Compression algorithm. // Default: nil Compressor Compressor // Default: nil Authenticator Authenticator actualSslOpts atomic.Value // PoolConfig configures the underlying connection pool, allowing the // configuration of host selection and connection selection policies. PoolConfig PoolConfig // Default retry policy to use for queries. // Default: SimpleRetryPolicy{NumRetries: 3}. RetryPolicy RetryPolicy // ConvictionPolicy decides whether to mark host as down based on the error and host info. // Default: SimpleConvictionPolicy ConvictionPolicy ConvictionPolicy // Default reconnection policy to use for reconnecting before trying to mark host as down. ReconnectionPolicy ReconnectionPolicy // A reconnection policy to use for reconnecting when connecting to the cluster first time. InitialReconnectionPolicy ReconnectionPolicy WarningsHandlerBuilder WarningHandlerBuilder // SslOpts configures TLS use when HostDialer is not set. // SslOpts is ignored if HostDialer is set. SslOpts *SslOptions // An Authenticator factory. Can be used to create alternative authenticators. // Default: nil AuthProvider func(h *HostInfo) (Authenticator, error) ClientRoutesConfig *ClientRoutesConfig // The version of the driver that is going to be reported to the server. // Defaulted to current library version DriverVersion string // The name of the driver that is going to be reported to the server. // Default: "ScyllaDB GoLang Driver" DriverName string // Initial keyspace. Optional. Keyspace string // CQL version (default: 3.0.0) CQLVersion string // addresses for the initial connections. It is recommended to use the value set in // the Cassandra config for broadcast_address or listen_address, an IP address not // a domain name. This is because events from Cassandra will use the configured IP // address, which is used to index connected hosts. If the domain name specified // resolves to more than 1 IP address then the driver may connect multiple times to // the same host, and will not mark the node being down or up from events. Hosts []string // The time to wait for frames before flushing the frames connection to Cassandra. // Can help reduce syscall overhead by making less calls to write. Set to 0 to // disable. // // (default: 200 microseconds) WriteCoalesceWaitTime time.Duration // WriteTimeout limits the time the driver waits to write a request to a network connection. // WriteTimeout should be lower than or equal to Timeout. // WriteTimeout defaults to the value of Timeout. WriteTimeout time.Duration // The keepalive period to use, enabled if > 0 (default: 15 seconds) // SocketKeepalive is used to set up the default dialer and is ignored if Dialer or HostDialer is provided. SocketKeepalive time.Duration // If not zero, gocql attempt to reconnect known DOWN nodes in every ReconnectInterval. ReconnectInterval time.Duration // The maximum amount of time to wait for schema agreement in a cluster after // receiving a schema change frame. (default: 60s) MaxWaitSchemaAgreement time.Duration // ProtoVersion sets the version of the native protocol to use, this will // enable features in the driver for specific protocol versions, generally this // should be set to a known version (2,3,4) for the cluster being connected to. // // If it is 0 or unset (the default) then the driver will attempt to discover the // highest supported protocol for the cluster. In clusters with nodes of different // versions the protocol selected is not defined (ie, it can be any of the supported in the cluster) ProtoVersion int // Maximum number of inflight requests allowed per connection. // Default: 32768 for CQL v3 and newer // Default: 128 for older CQL versions MaxRequestsPerConn int // Timeout defines the maximum time to wait for a single server response. // The default is 11 seconds, which is slightly higher than the default // server-side timeout for most query types. // // When a session creates a Query or Batch, it inherits this timeout as // the request timeout. // // Important notes: // 1. This value should be greater than the server timeout for all queries // you execute. Otherwise, you risk creating retry storms: the server // may still be processing the request while the client times out and retries. // 2. This timeout does not apply during initial connection setup. // For that, see ConnectTimeout. Timeout time.Duration // The timeout for the requests to the schema tables. (default: 60s) MetadataSchemaRequestTimeout time.Duration // ConnectTimeout limits the time spent during connection setup. // During initial connection setup, internal queries, AUTH requests will return an error if the client // does not receive a response within the ConnectTimeout period. // ConnectTimeout is applied to the connection setup queries independently. // ConnectTimeout also limits the duration of dialing a new TCP connection // in case there is no Dialer nor HostDialer configured. // ConnectTimeout has a default value of 11 seconds. ConnectTimeout time.Duration // Port used when dialing. // Default: 9042 Port int // The size of the connection pool for each host. // The pool filling runs in separate gourutine during the session initialization phase. // gocql will always try to get 1 connection on each host pool // during session initialization AND it will attempt // to fill each pool afterward asynchronously if NumConns > 1. // Notice: There is no guarantee that pool filling will be finished in the initialization phase. // Also, it describes a maximum number of connections at the same time. // Default: 2 NumConns int // The gocql driver may hold excess shard connections to reuse them when existing connections are dropped. // This configuration variable defines the limit for such excess connections. Once the limit is reached, // gocql starts dropping any additional excess connections. // The limit is computed as `MaxExcessShardConnectionsRate` * . MaxExcessShardConnectionsRate float32 // Maximum cache size for prepared statements globally for gocql. // Default: 1000 MaxPreparedStmts int // Default page size to use for created sessions. // Default: 5000 PageSize int // Maximum cache size for query info about statements for each session. // Default: 1000 MaxRoutingKeyInfo int // ReadTimeout limits the time the driver waits for data from the connection. // It has only one purpose, identify faulty connection early and drop it. // Default: 11 Seconds ReadTimeout time.Duration // Consistency for the serial part of queries, values can be either SERIAL or LOCAL_SERIAL. // Default: unset SerialConsistency Consistency // Default consistency level. // Default: Quorum Consistency Consistency // Configure events the driver will register for Events struct { // disable registering for status events (node up/down) DisableNodeStatusEvents bool // disable registering for topology events (node added/removed/moved) DisableTopologyEvents bool // disable registering for schema events (keyspace/table/function removed/created/updated) DisableSchemaEvents bool } // Default idempotence for queries DefaultIdempotence bool // Sends a client side timestamp for all requests which overrides the timestamp at which it arrives at the server. // Default: true, only enabled for protocol 3 and above. DefaultTimestamp bool // DisableSkipMetadata will override the internal result metadata cache so that the driver does not // send skip_metadata for queries, this means that the result will always contain // the metadata to parse the rows and will not reuse the metadata from the prepared // statement. // // See https://issues.apache.org/jira/browse/CASSANDRA-10786 // See https://github.com/scylladb/scylladb/issues/20860 // // Default: true DisableSkipMetadata bool // DisableShardAwarePort will prevent the driver from connecting to Scylla's shard-aware port, // even if there are nodes in the cluster that support it. // // It is generally recommended to leave this option turned off because gocql can use // the shard-aware port to make the process of establishing more robust. // However, if you have a cluster with nodes which expose shard-aware port // but the port is unreachable due to network configuration issues, you can use // this option to work around the issue. Set it to true only if you neither can fix // your network nor disable shard-aware port on your nodes. DisableShardAwarePort bool // If DisableInitialHostLookup then the driver will not attempt to get host info // from the system.peers table, this will mean that the driver will connect to // hosts supplied and will not attempt to lookup the hosts information, this will // mean that data_center, rack and token information will not be available and as // such host filtering and token aware query routing will not be available. DisableInitialHostLookup bool // internal config for testing disableControlConn bool disableInit bool // If IgnorePeerAddr is true and the address in system.peers does not match // the supplied host by either initial hosts or discovered via events then the // host will be replaced with the supplied address. // // For example if an event comes in with host=10.0.0.1 but when looking up that // address in system.local or system.peers returns 127.0.0.1, the peer will be // set to 10.0.0.1 which is what will be used to connect to. IgnorePeerAddr bool // An event bus configuration EventBusConfig eventbus.EventBusConfig } type DNSResolver interface { LookupIP(host string) ([]net.IP, error) } type ApplicationInfo interface { UpdateStartupOptions(map[string]string) } type StaticApplicationInfo struct { applicationName string applicationVersion string clientID string } func NewStaticApplicationInfo(name, version, clientID string) *StaticApplicationInfo { return &StaticApplicationInfo{ applicationName: name, applicationVersion: version, clientID: clientID, } } func (i *StaticApplicationInfo) UpdateStartupOptions(opts map[string]string) { if i.applicationName != "" { opts["APPLICATION_NAME"] = i.applicationName } if i.applicationVersion != "" { opts["APPLICATION_VERSION"] = i.applicationVersion } if i.clientID != "" { opts["CLIENT_ID"] = i.clientID } } type SimpleDNSResolver struct { hostLookupPreferV4 bool } func NewSimpleDNSResolver(hostLookupPreferV4 bool) *SimpleDNSResolver { return &SimpleDNSResolver{ hostLookupPreferV4, } } func (r SimpleDNSResolver) LookupIP(host string) ([]net.IP, error) { ips, err := net.LookupIP(host) if err != nil { return nil, err } // Filter to v4 addresses if any present if r.hostLookupPreferV4 { var preferredIPs []net.IP for _, v := range ips { if v4 := v.To4(); v4 != nil { preferredIPs = append(preferredIPs, v4) } } if len(preferredIPs) != 0 { ips = preferredIPs } } return ips, nil } var defaultDnsResolver = NewSimpleDNSResolver(os.Getenv("GOCQL_HOST_LOOKUP_PREFER_V4") == "true") type Dialer interface { DialContext(ctx context.Context, network, addr string) (net.Conn, error) } // NewCluster generates a new config for the default cluster implementation. // // The supplied hosts are used to initially connect to the cluster then the rest of // the ring will be automatically discovered. It is recommended to use the value set in // the Cassandra config for broadcast_address or listen_address, an IP address not // a domain name. This is because events from Cassandra will use the configured IP // address, which is used to index connected hosts. If the domain name specified // resolves to more than 1 IP address then the driver may connect multiple times to // the same host, and will not mark the node being down or up from events. func NewCluster(hosts ...string) *ClusterConfig { logger := &defaultLogger{} cfg := &ClusterConfig{ Hosts: hosts, CQLVersion: "3.0.0", Timeout: 11 * time.Second, ConnectTimeout: 60 * time.Second, ReadTimeout: 11 * time.Second, WriteTimeout: 11 * time.Second, Port: 9042, MaxExcessShardConnectionsRate: 2, NumConns: 2, Consistency: Quorum, MaxPreparedStmts: defaultMaxPreparedStmts, MaxRoutingKeyInfo: 1000, PageSize: 5000, DefaultTimestamp: true, DriverName: defaultDriverName, DriverVersion: defaultDriverVersion, MaxWaitSchemaAgreement: 60 * time.Second, ReconnectInterval: 60 * time.Second, ConvictionPolicy: &SimpleConvictionPolicy{}, ReconnectionPolicy: &ConstantReconnectionPolicy{MaxRetries: 3, Interval: 1 * time.Second}, InitialReconnectionPolicy: &NoReconnectionPolicy{}, SocketKeepalive: 15 * time.Second, WriteCoalesceWaitTime: 200 * time.Microsecond, MetadataSchemaRequestTimeout: 60 * time.Second, DisableSkipMetadata: true, WarningsHandlerBuilder: DefaultWarningHandlerBuilder, Logger: logger, DNSResolver: defaultDnsResolver, EventBusConfig: eventbus.EventBusConfig{ InputEventsQueueSize: 10240, }, } return cfg } func (cfg *ClusterConfig) logger() StdLogger { if cfg.Logger == nil { return &defaultLogger{} } return cfg.Logger } // CreateSession initializes the cluster based on this config and returns a // session object that can be used to interact with the database. func (cfg *ClusterConfig) CreateSession() (*Session, error) { return NewSession(*cfg) } func (cfg *ClusterConfig) CreateSessionNonBlocking() (*Session, error) { return NewSessionNonBlocking(*cfg) } func (cfg *ClusterConfig) filterHost(host *HostInfo) bool { return !(cfg.HostFilter == nil || cfg.HostFilter.Accept(host)) } func (cfg *ClusterConfig) ValidateAndInitSSL() error { if cfg.SslOpts == nil { return nil } actualTLSConfig, err := setupTLSConfig(cfg.SslOpts) if err != nil { return fmt.Errorf("failed to initialize ssl configuration: %s", err.Error()) } cfg.actualSslOpts.Store(actualTLSConfig) return nil } func (cfg *ClusterConfig) getActualTLSConfig() *tls.Config { val, ok := cfg.actualSslOpts.Load().(*tls.Config) if !ok { return nil } return val.Clone() } type ClusterOption func(*ClusterConfig) func (cfg *ClusterConfig) WithOptions(opts ...ClusterOption) *ClusterConfig { for _, opt := range opts { opt(cfg) } return cfg } type ClientRoutesOption func(*ClientRoutesConfig) func WithMaxResolverConcurrency(val int) func(*ClientRoutesConfig) { return func(cfg *ClientRoutesConfig) { cfg.MaxResolverConcurrency = val } } func WithResolveHealthyEndpointPeriod(val time.Duration) func(*ClientRoutesConfig) { return func(cfg *ClientRoutesConfig) { cfg.ResolveHealthyEndpointPeriod = val } } func WithEndpoints(endpoints ...ClientRoutesEndpoint) func(*ClientRoutesConfig) { return func(cfg *ClientRoutesConfig) { cfg.Endpoints = endpoints } } func WithTable(tableName string) func(*ClientRoutesConfig) { return func(cfg *ClientRoutesConfig) { cfg.TableName = tableName } } func WithClientRoutes(opts ...ClientRoutesOption) func(*ClusterConfig) { pmCfg := ClientRoutesConfig{ // Don't resolve healthy nodes by default ResolveHealthyEndpointPeriod: 0, MaxResolverConcurrency: 1, TableName: "system.client_routes", ResolverCacheDuration: time.Millisecond * 500, } for _, opt := range opts { opt(&pmCfg) } return func(cfg *ClusterConfig) { cfg.ClientRoutesConfig = &pmCfg if len(cfg.Hosts) == 0 { for _, ep := range pmCfg.Endpoints { if ep.ConnectionAddr != "" { cfg.Hosts = append(cfg.Hosts, ep.ConnectionAddr) } } } // TODO: cfg.ControlConnectionOnlyToInitialNodes } } func (cfg *ClusterConfig) Validate() error { if len(cfg.Hosts) == 0 { return ErrNoHosts } if cfg.Authenticator != nil && cfg.AuthProvider != nil { return errors.New("Can't use both Authenticator and AuthProvider in cluster config.") } if cfg.InitialReconnectionPolicy == nil { return errors.New("InitialReconnectionPolicy is nil") } if cfg.InitialReconnectionPolicy.GetMaxRetries() <= 0 { return errors.New("InitialReconnectionPolicy.GetMaxRetries returns negative number") } if cfg.ReconnectionPolicy == nil { return errors.New("ReconnectionPolicy is nil") } if cfg.InitialReconnectionPolicy.GetMaxRetries() <= 0 { return errors.New("ReconnectionPolicy.GetMaxRetries returns negative number") } if cfg.PageSize < 0 { return errors.New("PageSize should be positive number or zero") } if cfg.MaxRoutingKeyInfo < 0 { return errors.New("MaxRoutingKeyInfo should be positive number or zero") } if cfg.MaxPreparedStmts < 0 { return errors.New("MaxPreparedStmts should be positive number or zero") } if cfg.SocketKeepalive < 0 { return errors.New("SocketKeepalive should be positive time.Duration or zero") } if cfg.MaxRequestsPerConn < 0 { return errors.New("MaxRequestsPerConn should be positive number or zero") } if cfg.NumConns < 0 { return errors.New("NumConns should be positive non-zero number or zero") } if cfg.Port <= 0 || cfg.Port > 65535 { return errors.New("Port should be a valid port number: a number between 1 and 65535") } if cfg.WriteTimeout < 0 { return errors.New("WriteTimeout should be positive time.Duration or zero") } if cfg.Timeout < 0 { return errors.New("Timeout should be positive time.Duration or zero") } if cfg.ConnectTimeout < 0 { return errors.New("ConnectTimeout should be positive time.Duration or zero") } if cfg.MetadataSchemaRequestTimeout < 0 { return errors.New("MetadataSchemaRequestTimeout should be positive time.Duration or zero") } if cfg.WriteCoalesceWaitTime < 0 { return errors.New("WriteCoalesceWaitTime should be positive time.Duration or zero") } if cfg.ReconnectInterval < 0 { return errors.New("ReconnectInterval should be positive time.Duration or zero") } if cfg.MaxWaitSchemaAgreement < 0 { return errors.New("MaxWaitSchemaAgreement should be positive time.Duration or zero") } if cfg.ProtoVersion < 0 { return errors.New("ProtoVersion should be positive number or zero") } if !cfg.DisableSkipMetadata { cfg.Logger.Println("warning: enabling skipping metadata can lead to unpredictable results when executing query and altering columns involved in the query.") } if cfg.SerialConsistency > 0 && !cfg.SerialConsistency.IsSerial() { return fmt.Errorf("the default SerialConsistency level is not allowed to be anything else but SERIAL or LOCAL_SERIAL. Recived value: %v", cfg.SerialConsistency) } if cfg.DNSResolver == nil { return fmt.Errorf("DNSResolver is empty") } if cfg.MaxExcessShardConnectionsRate < 0 { return fmt.Errorf("MaxExcessShardConnectionsRate should be positive number or zero") } if cfg.ClientRoutesConfig != nil { if cfg.AddressTranslator != nil { return fmt.Errorf("AddressTranslator and ClientRoutesConfig should not be set at the same time") } if err := cfg.ClientRoutesConfig.Validate(); err != nil { return fmt.Errorf("ClientRoutesConfig is invalid: %v", err) } } return cfg.ValidateAndInitSSL() } var ( ErrNoHosts = errors.New("no hosts provided") ErrNoConnectionsStarted = errors.New("no connections were made when creating the session") ErrHostQueryFailed = errors.New("unable to populate Hosts") ) func setupTLSConfig(sslOpts *SslOptions) (*tls.Config, error) { // Config.InsecureSkipVerify | EnableHostVerification | Result // Config is nil | true | verify host // Config is nil | false | do not verify host // false | false | verify host // true | false | do not verify host // false | true | verify host // true | true | verify host var tlsConfig *tls.Config if sslOpts.Config == nil { tlsConfig = &tls.Config{ InsecureSkipVerify: !sslOpts.EnableHostVerification, // Ticket max size is 16371 bytes, so it can grow up to 16mb max. ClientSessionCache: tls.NewLRUClientSessionCache(1024), } } else { // use clone to avoid race. tlsConfig = sslOpts.Config.Clone() } if tlsConfig.InsecureSkipVerify && sslOpts.EnableHostVerification { tlsConfig.InsecureSkipVerify = false } // ca cert is optional if sslOpts.CaPath != "" { if tlsConfig.RootCAs == nil { tlsConfig.RootCAs = x509.NewCertPool() } pem, err := os.ReadFile(sslOpts.CaPath) if err != nil { return nil, fmt.Errorf("unable to open CA certs: %v", err) } if !tlsConfig.RootCAs.AppendCertsFromPEM(pem) { return nil, errors.New("failed parsing or CA certs") } } if sslOpts.CertPath != "" || sslOpts.KeyPath != "" { mycert, err := tls.LoadX509KeyPair(sslOpts.CertPath, sslOpts.KeyPath) if err != nil { return nil, fmt.Errorf("unable to load X509 key pair: %v", err) } tlsConfig.Certificates = append(tlsConfig.Certificates, mycert) } return tlsConfig, nil } ================================================ FILE: cluster_test.go ================================================ //go:build unit // +build unit /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "net" "reflect" "testing" "time" "github.com/gocql/gocql/internal/tests" ) func TestNewCluster_Defaults(t *testing.T) { t.Parallel() cfg := NewCluster() tests.AssertEqual(t, "cluster config cql version", "3.0.0", cfg.CQLVersion) tests.AssertEqual(t, "cluster config timeout", 11*time.Second, cfg.Timeout) tests.AssertEqual(t, "cluster config port", 9042, cfg.Port) tests.AssertEqual(t, "cluster config num-conns", 2, cfg.NumConns) tests.AssertEqual(t, "cluster config consistency", Quorum, cfg.Consistency) tests.AssertEqual(t, "cluster config max prepared statements", defaultMaxPreparedStmts, cfg.MaxPreparedStmts) tests.AssertEqual(t, "cluster config max routing key info", 1000, cfg.MaxRoutingKeyInfo) tests.AssertEqual(t, "cluster config page-size", 5000, cfg.PageSize) tests.AssertEqual(t, "cluster config default timestamp", true, cfg.DefaultTimestamp) tests.AssertEqual(t, "cluster config max wait schema agreement", 60*time.Second, cfg.MaxWaitSchemaAgreement) tests.AssertEqual(t, "cluster config reconnect interval", 60*time.Second, cfg.ReconnectInterval) tests.AssertTrue(t, "cluster config conviction policy", reflect.DeepEqual(&SimpleConvictionPolicy{}, cfg.ConvictionPolicy)) tests.AssertTrue(t, "cluster config reconnection policy", reflect.DeepEqual(&ConstantReconnectionPolicy{MaxRetries: 3, Interval: 1 * time.Second}, cfg.ReconnectionPolicy)) } func TestNewCluster_WithHosts(t *testing.T) { t.Parallel() cfg := NewCluster("addr1", "addr2") tests.AssertEqual(t, "cluster config hosts length", 2, len(cfg.Hosts)) tests.AssertEqual(t, "cluster config host 0", "addr1", cfg.Hosts[0]) tests.AssertEqual(t, "cluster config host 1", "addr2", cfg.Hosts[1]) } func TestClusterConfig_translateAddressAndPort_NilTranslator(t *testing.T) { t.Parallel() hh := HostInfoBuilder{ ConnectAddress: net.ParseIP("10.0.0.1"), Port: 1234, }.Build() newAddr, err := translateAddressPort(nil, &hh, AddressPort{ Address: hh.UntranslatedConnectAddress(), Port: uint16(hh.Port()), }, nil) tests.AssertNil(t, "should return no error", err) tests.AssertTrue(t, "same address as provided", net.ParseIP("10.0.0.1").Equal(newAddr.Address)) tests.AssertEqual(t, "translated host and port", uint16(1234), newAddr.Port) } func TestClusterConfig_translateAddressAndPort_EmptyAddr(t *testing.T) { t.Parallel() translator := staticAddressTranslator(net.ParseIP("10.10.10.10"), 5432) hh := HostInfoBuilder{ ConnectAddress: []byte{}, Port: 0, }.Build() newAddr, err := translateAddressPort(translator, &hh, AddressPort{ Address: hh.UntranslatedConnectAddress(), Port: uint16(hh.Port()), }, nil) tests.AssertNil(t, "should return no error", err) tests.AssertTrue(t, "translated address is still empty", len(newAddr.Address) == 0) tests.AssertEqual(t, "translated port", uint16(0), newAddr.Port) } func TestClusterConfig_translateAddressAndPort_Success(t *testing.T) { t.Parallel() translator := staticAddressTranslator(net.ParseIP("10.10.10.10"), 5432) hh := HostInfoBuilder{ ConnectAddress: net.ParseIP("10.0.0.1"), Port: 2345, }.Build() newAddr, err := translateAddressPort(translator, &hh, AddressPort{ Address: hh.UntranslatedConnectAddress(), Port: uint16(hh.Port()), }, nil) tests.AssertNil(t, "should return no error", err) tests.AssertTrue(t, "translated address", net.ParseIP("10.10.10.10").Equal(newAddr.Address)) tests.AssertEqual(t, "translated port", uint16(5432), newAddr.Port) } ================================================ FILE: common_test.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "flag" "fmt" "hash/fnv" "log" "net" "os" "strings" "sync" "testing" "time" ) var ( flagCluster = flag.String("cluster", "127.0.0.1", "a comma-separated list of host:port tuples") flagProto = flag.Int("proto", 0, "protcol version") flagCQL = flag.String("cql", "3.0.0", "CQL version") flagRF = flag.Int("rf", 1, "replication factor for test keyspace") clusterSize = flag.Int("clusterSize", 1, "the expected size of the cluster") flagRetry = flag.Int("retries", 5, "number of times to retry queries") flagAutoWait = flag.Duration("autowait", 1000*time.Millisecond, "time to wait for autodiscovery to fill the hosts poll") flagRunSslTest = flag.Bool("runssl", false, "Set to true to run ssl test") flagRunAuthTest = flag.Bool("runauth", false, "Set to true to run authentication test") flagCompressTest = flag.String("compressor", "", "compressor to use") flagTimeout = flag.Duration("gocql.timeout", 5*time.Second, "sets the connection `timeout` for all operations") flagClusterSocket = flag.String("cluster-socket", "", "nodes socket files separated by comma") flagDistribution = flag.String("distribution", "scylla", "database distribution - scylla or cassandra") flagCassVersion cassVersion ) // integrationTestSetup is set by an init() in an integration-tagged file to run // one-time setup (e.g. tablet probes) before any test executes. var integrationTestSetup func() func init() { flag.Var(&flagCassVersion, "gocql.cversion", "the cassandra version being tested against") log.SetFlags(log.Lshortfile | log.LstdFlags) } func TestMain(m *testing.M) { flag.Parse() if integrationTestSetup != nil { integrationTestSetup() } os.Exit(m.Run()) } func getClusterHosts() []string { return strings.Split(*flagCluster, ",") } func addSslOptions(cluster *ClusterConfig) *ClusterConfig { if *flagRunSslTest { if *flagDistribution == "cassandra" { cluster.Port = 9042 } else { cluster.Port = 9142 } cluster.SslOpts = &SslOptions{ CertPath: "testdata/pki/gocql.crt", KeyPath: "testdata/pki/gocql.key", CaPath: "testdata/pki/ca.crt", EnableHostVerification: false, } } return cluster } type OnceManager struct { keyspaces map[string]*sync.Once mu sync.Mutex } func NewOnceManager() *OnceManager { return &OnceManager{ keyspaces: make(map[string]*sync.Once), } } func (o *OnceManager) GetOnce(key string) *sync.Once { o.mu.Lock() defer o.mu.Unlock() if once, exists := o.keyspaces[key]; exists { return once } o.keyspaces[key] = &sync.Once{} return o.keyspaces[key] } var initKeyspaceOnce = NewOnceManager() var isTabletsSupportedFlag *bool var isTabletsSupportedOnce sync.Once func isTabletsSupported() bool { isTabletsSupportedOnce.Do(probeTabletsSupported) if isTabletsSupportedFlag == nil { return false } return *isTabletsSupportedFlag } func probeTabletsSupported() { s, err := createCluster().CreateSession() if err != nil { panic(fmt.Errorf("failed to create session: %v", err)) } defer s.Close() res := make(map[string]any) err = s.Query("select * from system.local").MapScan(res) if err != nil { panic(fmt.Errorf("failed to read system.local: %v", err)) } features, _ := res["supported_features"] featuresCasted, _ := features.(string) for _, feature := range strings.Split(featuresCasted, ",") { if feature == "TABLETS" { result := true isTabletsSupportedFlag = &result return } } result := false isTabletsSupportedFlag = &result } var isTabletsAutoEnabledFlag *bool var isTabletsAutoEnabledOnce sync.Once func isTabletsAutoEnabled() bool { isTabletsAutoEnabledOnce.Do(probeTabletsAutoEnabled) if isTabletsAutoEnabledFlag == nil { return false } return *isTabletsAutoEnabledFlag } func probeTabletsAutoEnabled() { s, err := createCluster().CreateSession() if err != nil { panic(fmt.Errorf("failed to create session: %v", err)) } defer s.Close() err = s.Query("DROP KEYSPACE IF EXISTS gocql_check_tablets_enabled").Exec() if err != nil { panic(fmt.Errorf("failed to delete keyspace: %v", err)) } err = s.Query("CREATE KEYSPACE gocql_check_tablets_enabled WITH replication = {'class': 'NetworkTopologyStrategy', 'replication_factor': '1'}").Exec() if err != nil { panic(fmt.Errorf("failed to create keyspace: %v", err)) } res := make(map[string]any) err = s.Query("describe keyspace gocql_check_tablets_enabled").MapScan(res) if err != nil { panic(fmt.Errorf("failed to describe keyspace: %v", err)) } err = s.Query("DROP KEYSPACE IF EXISTS gocql_check_tablets_enabled").Exec() if err != nil { panic(fmt.Errorf("failed to drop probe keyspace: %v", err)) } createStmt, _ := res["create_statement"] createStmtCasted, _ := createStmt.(string) result := strings.Contains(strings.ToLower(createStmtCasted), "and tablets") isTabletsAutoEnabledFlag = &result } // initTabletProbes runs the tablet-support and tablet-auto-enabled probes eagerly. // Called from TestMain before any tests run to avoid races with parallel test startup. func initTabletProbes() { probeTabletsSupported() if isTabletsSupportedFlag != nil && *isTabletsSupportedFlag { probeTabletsAutoEnabled() } } func createTable(s *Session, table string) error { if err := s.Query(table).RetryPolicy(&SimpleRetryPolicy{NumRetries: 3}).Idempotent(true).Exec(); err != nil { log.Printf("error creating table table=%q err=%v\n", table, err) return err } if err := s.control.awaitSchemaAgreement(); err != nil { log.Printf("error waiting for schema agreement post create table=%q err=%v\n", table, err) return err } // Invalidate schema cache to avoid races with debounced schema events. // Use per-table invalidation when possible (cheaper than keyspace-wide) // to reduce cache thrashing when parallel tests all perform DDL on the // same shared keyspace. Falls back to keyspace-wide invalidation for // non-TABLE DDL (e.g. DROP KEYSPACE, CREATE TYPE). ks, tbl := extractKeyspaceTableFromDDL(table) if ks == "" { ks = s.cfg.Keyspace } if ks != "" && tbl != "" { s.metadataDescriber.invalidateTableSchema(ks, tbl) } else if ks != "" { s.metadataDescriber.invalidateKeyspaceSchema(ks) } return nil } // createTables executes multiple DDL statements with a single // awaitSchemaAgreement call at the end, reducing the serialization bottleneck // when parallel tests all need schema agreement. Each statement is still // executed and cache-invalidated individually. func createTables(s *Session, ddls ...string) error { for _, ddl := range ddls { if err := s.Query(ddl).RetryPolicy(&SimpleRetryPolicy{NumRetries: 3}).Idempotent(true).Exec(); err != nil { log.Printf("error creating table table=%q err=%v\n", ddl, err) return err } } if err := s.control.awaitSchemaAgreement(); err != nil { log.Printf("error waiting for schema agreement after batch DDL err=%v\n", err) return err } // Invalidate caches for all affected tables/keyspaces. for _, ddl := range ddls { ks, tbl := extractKeyspaceTableFromDDL(ddl) if ks == "" { ks = s.cfg.Keyspace } if ks != "" && tbl != "" { s.metadataDescriber.invalidateTableSchema(ks, tbl) } else if ks != "" { s.metadataDescriber.invalidateKeyspaceSchema(ks) } } return nil } // extractKeyspaceTableFromDDL extracts the keyspace and table names from a DDL // statement like "CREATE TABLE gocql_test.table_name (...)". // Returns ("", "") for non-TABLE DDL or when keyspace is not qualified. func extractKeyspaceTableFromDDL(ddl string) (keyspace, table string) { upper := strings.ToUpper(ddl) idx := strings.Index(upper, "TABLE") if idx < 0 { return "", "" } rest := strings.TrimSpace(ddl[idx+len("TABLE"):]) // Skip optional "IF [NOT] EXISTS" between TABLE and the name. upperRest := strings.ToUpper(rest) if strings.HasPrefix(upperRest, "IF NOT EXISTS") { rest = strings.TrimSpace(rest[len("IF NOT EXISTS"):]) } else if strings.HasPrefix(upperRest, "IF EXISTS") { rest = strings.TrimSpace(rest[len("IF EXISTS"):]) } // Extract keyspace.table dot := strings.Index(rest, ".") if dot < 0 { return "", "" } ks := rest[:dot] // Extract table name: everything after the dot until whitespace or '(' nameRest := rest[dot+1:] end := strings.IndexAny(nameRest, " \t\n(") if end < 0 { return ks, nameRest } return ks, nameRest[:end] } func createCluster(opts ...func(*ClusterConfig)) *ClusterConfig { clusterHosts := getClusterHosts() cluster := NewCluster(clusterHosts...) cluster.ProtoVersion = *flagProto cluster.CQLVersion = *flagCQL cluster.Timeout = *flagTimeout cluster.Consistency = Quorum cluster.MaxWaitSchemaAgreement = 2 * time.Minute // travis might be slow if *flagRetry > 0 { cluster.RetryPolicy = &SimpleRetryPolicy{NumRetries: *flagRetry} } switch *flagCompressTest { case "snappy": cluster.Compressor = &SnappyCompressor{} case "": default: panic("invalid compressor: " + *flagCompressTest) } cluster = addSslOptions(cluster) for _, opt := range opts { opt(cluster) } return cluster } func createKeyspace(tb testing.TB, cluster *ClusterConfig, keyspace string, disableTablets bool) { tb.Helper() c := *cluster c.Keyspace = "system" c.Timeout = 30 * time.Second // Create a fresh policy to avoid sharing the policy instance with the caller. // Shallow copy of cluster config shares the HostSelectionPolicy pointer, which // would cause "sharing token aware host selection policy between sessions" panic // when both createKeyspace's session and the caller's session try to Init() it. c.PoolConfig.HostSelectionPolicy = nil session, err := c.CreateSession() if err != nil { tb.Fatalf("failed to create session: %v", err) } defer session.Close() err = createTable(session, `DROP KEYSPACE IF EXISTS `+keyspace) if err != nil { tb.Fatalf("unable to drop keyspace: %v", err) } query := fmt.Sprintf(`CREATE KEYSPACE %s WITH replication = { 'class' : 'NetworkTopologyStrategy', 'replication_factor' : %d }`, keyspace, *flagRF) if isTabletsSupported() { if disableTablets { query += " AND tablets = {'enabled': false}" } else if !isTabletsAutoEnabled() { query += " AND tablets = {'enabled': true};" } } err = createTable(session, query) if err != nil { tb.Fatalf("unable to create table: %v", err) } } type testKeyspaceOpts struct { tabletsDisabled bool } func (o *testKeyspaceOpts) KeyspaceName() string { if o.tabletsDisabled { return "gocql_test_tablets_disabled" } return "gocql_test" } func createSessionFromClusterHelper(cluster *ClusterConfig, tb testing.TB, opts testKeyspaceOpts) *Session { // Drop and re-create the keyspace once. Different tests should use their own // individual tables, but can assume that the table does not exist before. initKeyspaceOnce.GetOnce(opts.KeyspaceName()).Do(func() { createKeyspace(tb, cluster, opts.KeyspaceName(), opts.tabletsDisabled) }) cluster.Keyspace = opts.KeyspaceName() session, err := cluster.CreateSession() if err != nil { tb.Fatalf("failed to create session: %v", err) } if err := session.control.awaitSchemaAgreement(); err != nil { tb.Fatalf("failed to wait on schema agreement: %v", err) } return session } func getClusterSocketFile() []string { var res []string for _, socketFile := range strings.Split(*flagClusterSocket, ",") { if socketFile != "" { res = append(res, socketFile) } } return res } func createSessionFromClusterTabletsDisabled(cluster *ClusterConfig, tb testing.TB) *Session { return createSessionFromClusterHelper(cluster, tb, testKeyspaceOpts{tabletsDisabled: true}) } func createSessionFromCluster(cluster *ClusterConfig, tb testing.TB) *Session { return createSessionFromClusterHelper(cluster, tb, testKeyspaceOpts{tabletsDisabled: false}) } func createSession(tb testing.TB, opts ...func(config *ClusterConfig)) *Session { cluster := createCluster(opts...) return createSessionFromCluster(cluster, tb) } func createViews(t *testing.T, session *Session) { if err := session.Query(` CREATE TYPE IF NOT EXISTS gocql_test.basicView ( birthday timestamp, nationality text, weight text, height text); `).Exec(); err != nil { t.Fatalf("failed to create view with err: %v", err) } } func createMaterializedViews(t *testing.T, session *Session) { if flagCassVersion.Before(3, 0, 0) { return } table1 := testTableName(t, "1") table2 := testTableName(t, "2") view1 := testTableName(t, "view1") view2 := testTableName(t, "view2") if err := session.Query(fmt.Sprintf(`CREATE TABLE IF NOT EXISTS gocql_test.%s ( userid text, year int, month int, PRIMARY KEY (userid));`, table1)).Exec(); err != nil { t.Fatalf("failed to create materialized view with err: %v", err) } if err := session.Query(fmt.Sprintf(`CREATE TABLE IF NOT EXISTS gocql_test.%s ( userid text, year int, month int, PRIMARY KEY (userid));`, table2)).Exec(); err != nil { t.Fatalf("failed to create materialized view with err: %v", err) } if err := session.Query(fmt.Sprintf(`CREATE MATERIALIZED VIEW IF NOT EXISTS gocql_test.%s AS SELECT year, month, userid FROM gocql_test.%s WHERE year IS NOT NULL AND month IS NOT NULL AND userid IS NOT NULL PRIMARY KEY (userid, year);`, view1, table1)).Exec(); err != nil { t.Fatalf("failed to create materialized view with err: %v", err) } if err := session.Query(fmt.Sprintf(`CREATE MATERIALIZED VIEW IF NOT EXISTS gocql_test.%s AS SELECT year, month, userid FROM gocql_test.%s WHERE year IS NOT NULL AND month IS NOT NULL AND userid IS NOT NULL PRIMARY KEY (userid, year);`, view2, table2)).Exec(); err != nil { t.Fatalf("failed to create materialized view with err: %v", err) } } func createFunctions(t *testing.T, session *Session) { fnState := testTableName(t, "avgstate") fnFinal := testTableName(t, "avgfinal") if err := session.Query(fmt.Sprintf(` CREATE OR REPLACE FUNCTION gocql_test.%s ( state tuple, val int ) CALLED ON NULL INPUT RETURNS tuple LANGUAGE java AS $$if (val !=null) {state.setInt(0, state.getInt(0)+1); state.setLong(1, state.getLong(1)+val.intValue());}return state;$$; `, fnState)).Exec(); err != nil { t.Fatalf("failed to create function with err: %v", err) } if err := session.Query(fmt.Sprintf(` CREATE OR REPLACE FUNCTION gocql_test.%s ( state tuple ) CALLED ON NULL INPUT RETURNS double LANGUAGE java AS $$double r = 0; if (state.getInt(0) == 0) return null; r = state.getLong(1); r/= state.getInt(0); return Double.valueOf(r);$$ `, fnFinal)).Exec(); err != nil { t.Fatalf("failed to create function with err: %v", err) } } func createAggregate(t *testing.T, session *Session) { fnState := testTableName(t, "avgstate") fnFinal := testTableName(t, "avgfinal") aggName := testTableName(t, "average") aggName2 := testTableName(t, "average2") createFunctions(t, session) if err := session.Query(fmt.Sprintf(` CREATE OR REPLACE AGGREGATE gocql_test.%s(int) SFUNC %s STYPE tuple FINALFUNC %s INITCOND (0,0); `, aggName, fnState, fnFinal)).Exec(); err != nil { t.Fatalf("failed to create aggregate with err: %v", err) } if err := session.Query(fmt.Sprintf(` CREATE OR REPLACE AGGREGATE gocql_test.%s(int) SFUNC %s STYPE tuple FINALFUNC %s INITCOND (0,0); `, aggName2, fnState, fnFinal)).Exec(); err != nil { t.Fatalf("failed to create aggregate with err: %v", err) } } const maxCQLIdentifierLen = 48 const testTableNameHashLen = 16 // testTableName builds a CQL-safe table name from t.Name() and optional parts. // Truncates to 48 chars (CQL limit) using __ // when needed. func testTableName(t testing.TB, parts ...string) string { name := strings.ToLower(t.Name()) for _, p := range parts { name += "_" + strings.ToLower(p) } var b strings.Builder prevUnderscore := false for _, r := range name { if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') { b.WriteRune(r) prevUnderscore = false } else if !prevUnderscore { b.WriteByte('_') prevUnderscore = true } } name = strings.Trim(b.String(), "_") if len(name) > maxCQLIdentifierLen { h := fnv.New64a() h.Write([]byte(name)) hash := fmt.Sprintf("%016x", h.Sum64()) // 16 hex chars for better collision resistance remaining := maxCQLIdentifierLen - testTableNameHashLen - 2 prefixLen := remaining / 2 suffixLen := remaining - prefixLen name = name[:prefixLen] + "_" + hash + "_" + name[len(name)-suffixLen:] } return name } // testTypeName builds a CQL-safe UDT type name from t.Name() and optional parts. // Analogous to testTableName but intended for CREATE TYPE / frozen references. func testTypeName(t testing.TB, parts ...string) string { return testTableName(t, parts...) } // testKeyspaceName builds a CQL-safe keyspace name from t.Name() and optional parts. // Analogous to testTableName but intended for CREATE/DROP KEYSPACE statements. func testKeyspaceName(t testing.TB, parts ...string) string { return testTableName(t, parts...) } func staticAddressTranslator(newAddr net.IP, newPort int) AddressTranslator { return AddressTranslatorFunc(func(addr net.IP, port int) (net.IP, int) { return newAddr, newPort }) } ================================================ FILE: compressor.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "github.com/klauspost/compress/s2" ) type Compressor interface { Name() string Encode(data []byte) ([]byte, error) Decode(data []byte) ([]byte, error) } // SnappyCompressor implements the Compressor interface and can be used to // compress incoming and outgoing frames. It uses S2 compression algorithm // that is compatible with snappy and aims for high throughput, which is why // it features concurrent compression for bigger payloads. type SnappyCompressor struct{} func (s SnappyCompressor) Name() string { return "snappy" } func (s SnappyCompressor) Encode(data []byte) ([]byte, error) { return s2.EncodeSnappy(nil, data), nil } func (s SnappyCompressor) Decode(data []byte) ([]byte, error) { return s2.Decode(nil, data) } ================================================ FILE: compressor_test.go ================================================ //go:build unit // +build unit /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql_test import ( "bytes" "os" "testing" "github.com/klauspost/compress/s2" "github.com/gocql/gocql" ) type frameExample struct { Name string Frame []byte FilePath string } var frameExamples = struct { Requests []frameExample Responses []frameExample }{ Requests: []frameExample{ { Name: "Small query request", FilePath: "testdata/frames/small_query_request.bin", }, { Name: "Medium query request", FilePath: "testdata/frames/medium_query_request.bin", }, { Name: "Big query request", FilePath: "testdata/frames/big_query_request.bin", }, { Name: "Prepare statement request", FilePath: "testdata/frames/prepare_statement_request.bin", }, }, Responses: []frameExample{ { Name: "Small query response", FilePath: "testdata/frames/small_query_response.bin", }, { Name: "Medium query response", FilePath: "testdata/frames/medium_query_response.bin", }, { Name: "Big query response", FilePath: "testdata/frames/big_query_response.bin", }, { Name: "Prepare statement response", FilePath: "testdata/frames/prepare_statement_response.bin", }, }, } func TestSnappyCompressor(t *testing.T) { t.Parallel() t.Run("basic", func(t *testing.T) { c := gocql.SnappyCompressor{} if c.Name() != "snappy" { t.Fatalf("expected name to be 'snappy', got %v", c.Name()) } str := "My Test String" //Test Encoding with S2 library, Snappy compatible encoding. expected := s2.EncodeSnappy(nil, []byte(str)) if res, err := c.Encode([]byte(str)); err != nil { t.Fatalf("failed to encode '%v' with error %v", str, err) } else if bytes.Compare(expected, res) != 0 { t.Fatal("failed to match the expected encoded value with the result encoded value.") } val, err := c.Encode([]byte(str)) if err != nil { t.Fatalf("failed to encode '%v' with error '%v'", str, err) } //Test Decoding with S2 library, Snappy compatible encoding. if expected, err := s2.Decode(nil, val); err != nil { t.Fatalf("failed to decode '%v' with error %v", val, err) } else if res, err := c.Decode(val); err != nil { t.Fatalf("failed to decode '%v' with error %v", val, err) } else if bytes.Compare(expected, res) != 0 { t.Fatal("failed to match the expected decoded value with the result decoded value.") } }) t.Run("frame-examples", func(t *testing.T) { c := gocql.SnappyCompressor{} t.Run("Encode", func(t *testing.T) { for id := range frameExamples.Requests { frame := frameExamples.Requests[id] t.Run(frame.Name, func(t *testing.T) { t.Parallel() encoded, err := c.Encode(frame.Frame) if err != nil { t.Fatalf("failed to encode frame %s", frame.Name) } decoded, err := c.Decode(encoded) if err != nil { t.Fatalf("failed to decode frame %s", frame.Name) } if bytes.Compare(decoded, frame.Frame) != 0 { t.Fatalf("failed to match the decoded value with the original value") } t.Logf("Compression rate %f", float64(len(encoded))/float64(len(frame.Frame))) }) } }) t.Run("Decode", func(t *testing.T) { for id := range frameExamples.Responses { frame := frameExamples.Responses[id] t.Run(frame.Name, func(t *testing.T) { t.Parallel() decoded, err := c.Decode(frame.Frame) if err != nil { t.Fatalf("failed to decode frame %s", frame.Name) } if len(decoded) == 0 { t.Fatalf("frame was decoded to empty slice") } }) } }) }) } func BenchmarkSnappyCompressor(b *testing.B) { c := gocql.SnappyCompressor{} b.Run("Decode", func(b *testing.B) { for _, frame := range frameExamples.Responses { b.Run(frame.Name, func(b *testing.B) { for x := 0; x < b.N; x++ { _, _ = c.Decode(frame.Frame) } }) } }) b.Run("Encode", func(b *testing.B) { for _, frame := range frameExamples.Requests { b.Run(frame.Name, func(b *testing.B) { for x := 0; x < b.N; x++ { _, _ = c.Encode(frame.Frame) } }) } }) } func init() { var err error for id, def := range frameExamples.Requests { frameExamples.Requests[id].Frame, err = os.ReadFile(def.FilePath) if err != nil { panic("can't read file " + def.FilePath) } } for id, def := range frameExamples.Responses { frameExamples.Responses[id].Frame, err = os.ReadFile(def.FilePath) if err != nil { panic("can't read file " + def.FilePath) } } } ================================================ FILE: conn.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2012, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "bufio" "context" "crypto/tls" "errors" "fmt" "io" "net" "strconv" "strings" "sync" "sync/atomic" "time" frm "github.com/gocql/gocql/internal/frame" "github.com/gocql/gocql/tablets" "github.com/gocql/gocql/internal/lru" "github.com/gocql/gocql/internal/streams" ) // approve the authenticator with the list of allowed authenticators. If the provided list is empty, // the given authenticator is allowed. func approve(authenticator string, approvedAuthenticators []string) bool { if len(approvedAuthenticators) == 0 { return true } for _, s := range approvedAuthenticators { if authenticator == s { return true } } return false } type Authenticator interface { Challenge(req []byte) (resp []byte, auth Authenticator, err error) Success(data []byte) error } type WarningHandlerBuilder func(session *Session) WarningHandler type WarningHandler interface { HandleWarnings(qry ExecutableQuery, host *HostInfo, warnings []string) } // PasswordAuthenticator specifies credentials to be used when authenticating. // It can be configured with an "allow list" of authenticator class names to avoid // attempting to authenticate with Cassandra if it doesn't provide an expected authenticator. type PasswordAuthenticator struct { Username string Password string // Setting this to nil or empty will allow authenticating with any authenticator // provided by the server. This is the default behavior of most other driver // implementations. AllowedAuthenticators []string } func (p PasswordAuthenticator) Challenge(req []byte) ([]byte, Authenticator, error) { if !approve(string(req), p.AllowedAuthenticators) { return nil, nil, fmt.Errorf("unexpected authenticator %q", req) } resp := make([]byte, 2+len(p.Username)+len(p.Password)) resp[0] = 0 copy(resp[1:], p.Username) resp[len(p.Username)+1] = 0 copy(resp[2+len(p.Username):], p.Password) return resp, nil, nil } func (p PasswordAuthenticator) Success(data []byte) error { return nil } // SslOptions configures TLS use. // // Warning: Due to historical reasons, the SslOptions is insecure by default, so you need to set EnableHostVerification // to true if no Config is set. Most users should set SslOptions.Config to a *tls.Config. // SslOptions and Config.InsecureSkipVerify interact as follows: // // Config.InsecureSkipVerify | EnableHostVerification | Result // Config is nil | false | do not verify host // Config is nil | true | verify host // false | false | verify host // true | false | do not verify host // false | true | verify host // true | true | verify host type SslOptions struct { *tls.Config // CertPath and KeyPath are optional depending on server // config, but both fields must be omitted to avoid using a // client certificate CertPath string KeyPath string CaPath string //optional depending on server config // If you want to verify the hostname and server cert (like a wildcard for cass cluster) then you should turn this // on. // This option is basically the inverse of tls.Config.InsecureSkipVerify. // See InsecureSkipVerify in http://golang.org/pkg/crypto/tls/ for more info. // // See SslOptions documentation to see how EnableHostVerification interacts with the provided tls.Config. EnableHostVerification bool } type ConnConfig struct { Dialer Dialer Logger StdLogger Authenticator Authenticator Compressor Compressor HostDialer HostDialer AuthProvider func(h *HostInfo) (Authenticator, error) tlsConfig *tls.Config CQLVersion string ConnectTimeout time.Duration ReadTimeout time.Duration WriteTimeout time.Duration ProtoVersion int Keepalive time.Duration disableCoalesce bool } func (c *ConnConfig) logger() StdLogger { if c.Logger == nil { return &defaultLogger{} } return c.Logger } type ConnErrorHandler interface { HandleError(conn *Conn, err error, closed bool) } type connErrorHandlerFn func(conn *Conn, err error, closed bool) func (fn connErrorHandlerFn) HandleError(conn *Conn, err error, closed bool) { fn(conn, err, closed) } type ConnInterface interface { Close() exec(ctx context.Context, req frameBuilder, tracer Tracer, requestTimeout time.Duration) (*framer, error) awaitSchemaAgreement(ctx context.Context) error executeQuery(ctx context.Context, qry *Query) *Iter querySystem(ctx context.Context, query string, values ...any) *Iter getIsSchemaV2() bool setSchemaV2(s bool) getScyllaSupported() ScyllaConnectionFeatures } // Conn is a single connection to a Cassandra node. It can be used to execute // queries, but users are usually advised to use a more reliable, higher // level API. type Conn struct { auth Authenticator streamObserver StreamObserver w contextWriter logger StdLogger frameObserver FrameHeaderObserver ctx context.Context errorHandler ConnErrorHandler compressor Compressor conn net.Conn cfg *ConnConfig supported map[string][]string streams *streams.IDGenerator host *HostInfo // calls stores a map from stream ID to callReq. // This map is protected by mu. // calls should not be used when closed is true, calls is set to nil when closed=true. calls map[int]*callReq r *bufio.Reader session *Session framers connFramers cancel context.CancelFunc addr string usingTimeoutClause string currentKeyspace string cqlProtoExts []cqlProtocolExtension scyllaSupported ScyllaConnectionFeatures systemRequestTimeout time.Duration writeTimeout atomic.Int64 readTimeout atomic.Int64 mu sync.Mutex tabletsRoutingV1 int32 headerBuf [headSize]byte isShardAware bool // true if connection close process for the connection started. // closed is protected by mu. closed bool isSchemaV2 bool version uint8 } func (c *Conn) getIsSchemaV2() bool { return c.isSchemaV2 } func (c *Conn) setSchemaV2(s bool) { c.isSchemaV2 = s } func (c *Conn) setSystemRequestTimeout(t time.Duration) { c.systemRequestTimeout = t c.recalculateSystemRequestTimeout() } func (c *Conn) recalculateSystemRequestTimeout() { if c.systemRequestTimeout > time.Duration(0) && c.isScyllaConn() { c.usingTimeoutClause = " USING TIMEOUT " + strconv.FormatInt(c.systemRequestTimeout.Milliseconds(), 10) + "ms" } } func (c *Conn) finalizeConnection() { // When connection just created all timeouts are set to `cfg.ConnectTimeout` // It is done to make sure that connection is easy to establish when users set very low `WriteTimeout` and/or `Timeout` // This method sets timeouts to `operational` values after connection successfully created c.writeTimeout.Store(int64(c.cfg.WriteTimeout)) c.readTimeout.Store(int64(c.cfg.ReadTimeout)) c.setSystemRequestTimeout(c.session.cfg.MetadataSchemaRequestTimeout) c.w.setWriteTimeout(c.cfg.WriteTimeout) } func (c *Conn) getScyllaSupported() ScyllaConnectionFeatures { return c.scyllaSupported } // connect establishes a connection to a Cassandra node using session's connection config. // note: every connection needs to get `conn.finalizeConnection` called ont it when initialization process is done func (s *Session) connect(ctx context.Context, host *HostInfo, errorHandler ConnErrorHandler) (*Conn, error) { return s.dial(ctx, host, s.connCfg, errorHandler) } // connectShard establishes a connection to a shard. // If nrShards is zero, shard-aware dialing is disabled. // note: every connection needs to get `conn.finalizeConnection` called ont it when initialization process is done func (s *Session) connectShard(ctx context.Context, host *HostInfo, errorHandler ConnErrorHandler, shardID, nrShards int) (*Conn, error) { return s.dialShard(ctx, host, s.connCfg, errorHandler, shardID, nrShards) } // dial establishes a connection to a Cassandra node and notifies the session's connectObserver. // note: every connection needs to get `conn.finalizeConnection` called on it when initialization process is done func (s *Session) dial(ctx context.Context, host *HostInfo, connConfig *ConnConfig, errorHandler ConnErrorHandler) (*Conn, error) { return s.dialShard(ctx, host, connConfig, errorHandler, 0, 0) } func translateHostAddresses(addressTranslator AddressTranslator, host *HostInfo, logger StdLogger) (translatedAddresses, error) { addr, err := translateAddressPort(addressTranslator, host, AddressPort{ Address: host.UntranslatedConnectAddress(), Port: uint16(host.Port()), }, logger) if err != nil { return translatedAddresses{}, fmt.Errorf("unable to translate regular cql address: %w", err) } resultedInfo := translatedAddresses{ CQL: addr, } scyllaFeatures := host.ScyllaFeatures() if port := scyllaFeatures.ShardAwarePort(); port != 0 { addr, err = translateAddressPort(addressTranslator, host, AddressPort{ Address: host.UntranslatedConnectAddress(), Port: port, }, logger) if err != nil { return translatedAddresses{}, fmt.Errorf("unable to translate shard aware address: %w", err) } resultedInfo.ShardAware = addr } if port := scyllaFeatures.ShardAwarePortTLS(); port != 0 { addr, err = translateAddressPort(addressTranslator, host, AddressPort{ Address: host.UntranslatedConnectAddress(), Port: port, }, logger) if err != nil { return translatedAddresses{}, fmt.Errorf("unable to translate shard aware tls address: %w", err) } resultedInfo.ShardAwareTLS = addr } return resultedInfo, nil } // dialShard establishes a connection to a host/shard and notifies the session's connectObserver. // If nrShards is zero, shard-aware dialing is disabled. // note: every connection needs to get `conn.finalizeConnection` called on it when initialization process is done func (s *Session) dialShard(ctx context.Context, host *HostInfo, connConfig *ConnConfig, errorHandler ConnErrorHandler, shardID, nrShards int) (*Conn, error) { var obs ObservedConnect current := host.getTranslatedConnectionInfo() updated, err := translateHostAddresses(s.addressTranslator, host, s.logger) if err != nil { return nil, err } if current == nil || !updated.Equal(current) { host.setTranslatedConnectionInfo(updated) } if s.connectObserver != nil { obs.Host = host obs.Start = time.Now() } conn, err := s.dialWithoutObserver(ctx, host, connConfig, errorHandler, shardID, nrShards) if s.connectObserver != nil { obs.End = time.Now() obs.Err = err s.connectObserver.ObserveConnect(obs) } return conn, err } // dialWithoutObserver establishes connection to a Cassandra node. // // dialWithoutObserver does not notify the connection observer, so you most probably want to call dial() instead. // // If nrShards is zero, shard-aware dialing is disabled. func (s *Session) dialWithoutObserver(ctx context.Context, host *HostInfo, cfg *ConnConfig, errorHandler ConnErrorHandler, shardID, nrShards int) (*Conn, error) { shardDialer, ok := cfg.HostDialer.(ShardDialer) var ( dialedHost *DialedHost err error ) isShardAware := false if ok && nrShards > 0 { isShardAware = true dialedHost, err = shardDialer.DialShard(ctx, host, shardID, nrShards) } else { dialedHost, err = cfg.HostDialer.DialHost(ctx, host) } if err != nil { return nil, err } ctx, cancel := context.WithCancel(ctx) c := &Conn{ conn: dialedHost.Conn, r: bufio.NewReader(dialedHost.Conn), cfg: cfg, calls: make(map[int]*callReq), version: uint8(cfg.ProtoVersion), isShardAware: isShardAware, addr: dialedHost.Conn.RemoteAddr().String(), errorHandler: errorHandler, compressor: cfg.Compressor, session: s, streams: s.streamIDGenerator(), host: host, isSchemaV2: true, // Try using "system.peers_v2" until proven otherwise frameObserver: s.frameObserver, w: &deadlineContextWriter{ w: dialedHost.Conn, semaphore: make(chan struct{}, 1), quit: make(chan struct{}), }, ctx: ctx, cancel: cancel, logger: cfg.logger(), streamObserver: s.streamObserver, systemRequestTimeout: cfg.ConnectTimeout, } if err := c.init(ctx, dialedHost); err != nil { cancel() c.Close() return nil, err } return c, nil } func (s *Session) streamIDGenerator() *streams.IDGenerator { if s.cfg.MaxRequestsPerConn > 0 { return streams.NewLimited(s.cfg.MaxRequestsPerConn) } return streams.New() } func (c *Conn) init(ctx context.Context, dialedHost *DialedHost) error { c.readTimeout.Store(int64(c.cfg.ConnectTimeout)) c.writeTimeout.Store(int64(c.cfg.ConnectTimeout)) c.w.setWriteTimeout(c.cfg.ConnectTimeout) if c.session.cfg.AuthProvider != nil { var err error c.auth, err = c.cfg.AuthProvider(c.host) if err != nil { return err } } else { c.auth = c.cfg.Authenticator } startup := &startupCoordinator{ frameTicker: make(chan struct{}), conn: c, } if err := startup.setupConn(ctx); err != nil { return err } // dont coalesce startup frames if c.session.cfg.WriteCoalesceWaitTime > 0 && !c.cfg.disableCoalesce && !dialedHost.DisableCoalesce { c.w = newWriteCoalescer(c.conn, c.cfg.ConnectTimeout, c.session.cfg.WriteCoalesceWaitTime, ctx.Done()) } if c.isScyllaConn() { // ScyllaDB does not support system.peers_v2 c.setSchemaV2(false) } go c.serve(ctx) go c.heartBeat(ctx) return nil } func (c *Conn) Write(p []byte) (n int, err error) { return c.w.writeContext(context.Background(), p) } func (c *Conn) Read(p []byte) (n int, err error) { const maxAttempts = 5 timeout := c.readTimeout.Load() for i := 0; i < maxAttempts; i++ { var nn int if timeout > 0 { err = c.conn.SetReadDeadline(time.Now().Add(time.Duration(timeout))) if err != nil { return 0, err } } nn, err = io.ReadFull(c.r, p[n:]) n += nn if err == nil { break } if verr, ok := err.(net.Error); !ok || !verr.Temporary() { break } } return } type startupCoordinator struct { conn *Conn frameTicker chan struct{} } func (s *startupCoordinator) setupConn(ctx context.Context) error { var cancel context.CancelFunc if s.conn.cfg.ConnectTimeout > 0 { ctx, cancel = context.WithTimeout(ctx, s.conn.cfg.ConnectTimeout) } else { ctx, cancel = context.WithCancel(ctx) } defer cancel() startupErr := make(chan error) go func() { for range s.frameTicker { err := s.conn.recv(ctx) if err != nil { select { case startupErr <- err: case <-ctx.Done(): } return } } }() go func() { defer close(s.frameTicker) err := s.options(ctx) select { case startupErr <- err: case <-ctx.Done(): } }() select { case err := <-startupErr: if err != nil { return err } case <-ctx.Done(): return errors.New("gocql: no response to connection startup within timeout") } return nil } // write sends the given frame on the connection during startup and returns // the parsed response frame. // // NOTE: The returned frame must not retain any byte-slice references to the // framer's read buffer, because the framer is released back to the pool // immediately after parseFrame returns (via defer). Frame types that use // readBytesCopy (e.g. SupportedFrame, AuthChallengeFrame, AuthSuccessFrame) // are safe; frame types that use readBytes and expose []byte fields would not // be safe and must not be returned from this function. func (s *startupCoordinator) write(ctx context.Context, frame frameBuilder) (frame, error) { select { case s.frameTicker <- struct{}{}: case <-ctx.Done(): return nil, ctx.Err() } framer, err := s.conn.exec(ctx, frame, nil, s.conn.cfg.ConnectTimeout) if err != nil { return nil, err } defer framer.Release() return framer.parseFrame() } func (s *startupCoordinator) options(ctx context.Context) error { frame, err := s.write(ctx, &writeOptionsFrame{}) if err != nil { return err } v, ok := frame.(*frm.SupportedFrame) if !ok { return NewErrProtocol("Unknown type of response to startup frame: %T", frame) } // Keep raw supported multimap for debug purposes s.conn.supported = v.Supported s.conn.scyllaSupported = parseSupported(s.conn.supported, s.conn.logger) s.conn.recalculateSystemRequestTimeout() if current := s.conn.host.ScyllaFeatures(); current != s.conn.scyllaSupported.ScyllaHostFeatures { s.conn.host.setScyllaFeatures(s.conn.scyllaSupported.ScyllaHostFeatures) } s.conn.cqlProtoExts = parseCQLProtocolExtensions(s.conn.supported, s.conn.logger) // initFramerCache must be called after startup(), because startup() may // nil out c.compressor if the server does not support the requested // compression algorithm. Calling it before would snapshot a stale // compressor and set FlagCompress, causing protocol errors. err = s.startup(ctx) if err != nil { return err } s.conn.initFramerCache() return nil } func (s *startupCoordinator) startup(ctx context.Context) error { m := map[string]string{} if s.conn.session.cfg.ApplicationInfo != nil { s.conn.session.cfg.ApplicationInfo.UpdateStartupOptions(m) } m["CQL_VERSION"] = s.conn.cfg.CQLVersion m["DRIVER_NAME"] = s.conn.session.cfg.DriverName m["DRIVER_VERSION"] = s.conn.session.cfg.DriverVersion if s.conn.compressor != nil { comp := s.conn.supported["COMPRESSION"] name := s.conn.compressor.Name() for _, compressor := range comp { if compressor == name { m["COMPRESSION"] = compressor break } } if _, ok := m["COMPRESSION"]; !ok { s.conn.compressor = nil } } for _, ext := range s.conn.cqlProtoExts { serialized := ext.serialize() for k, v := range serialized { m[k] = v } } frame, err := s.write(ctx, &writeStartupFrame{opts: m}) if err != nil { return err } switch v := frame.(type) { case error: return v case *frm.ReadyFrame: return nil case *frm.AuthenticateFrame: return s.authenticateHandshake(ctx, v) default: return NewErrProtocol("Unknown type of response to startup frame: %s", v) } } func (s *startupCoordinator) authenticateHandshake(ctx context.Context, authFrame *frm.AuthenticateFrame) error { if s.conn.auth == nil { return fmt.Errorf("authentication required (using %q)", authFrame.Class) } resp, challenger, err := s.conn.auth.Challenge([]byte(authFrame.Class)) if err != nil { return err } req := &writeAuthResponseFrame{data: resp} for { frame, err := s.write(ctx, req) if err != nil { return err } switch v := frame.(type) { case error: return v case *frm.AuthSuccessFrame: if challenger != nil { return challenger.Success(v.Data) } return nil case *frm.AuthChallengeFrame: resp, challenger, err = challenger.Challenge(v.Data) if err != nil { return err } req = &writeAuthResponseFrame{ data: resp, } default: return fmt.Errorf("unknown frame response during authentication: %v", v) } } } func (c *Conn) closeWithError(err error) { if c == nil { return } c.mu.Lock() if c.closed { c.mu.Unlock() return } c.closed = true callsToClose := c.calls // It is safe to change c.calls to nil. Nobody should use it after c.closed is set to true. c.calls = nil c.mu.Unlock() var cerr error if err == nil { // Graceful closes do not inject an error into call.resp, so cancel the // connection first to unblock any exec() calls before waiting for them. c.cancel() cerr = c.close() } for _, req := range callsToClose { if err != nil { // We need to send the error to all waiting queries. select { case req.resp <- callResp{err: err}: // exec() received the error. Wait for it to finish touching the callReq // before recycling it. case <-req.timeout: // exec() already timed out and returned. } } req.waitExecDone("closeWithError") if req.streamObserverContext != nil { req.streamObserverEndOnce.Do(func() { req.streamObserverContext.StreamAbandoned(ObservedStream{ Host: c.host, }) }) } putCallReq(req) } // Allow GC of pooled framers. Safe to do after the drain loop above has // resolved all in-flight exec() calls. Any event goroutines still running // will see pool==nil in releaseFramer and simply drop the framer. c.framers.close() if err != nil { c.cancel() cerr = c.close() } if err != nil { c.errorHandler.HandleError(c, err, true) } else if cerr != nil { // TODO(zariel): is it a good idea to do this? c.errorHandler.HandleError(c, cerr, true) } } func (c *Conn) isTabletSupported() bool { return atomic.LoadInt32(&c.tabletsRoutingV1) == 1 } func (c *Conn) setTabletSupported(val bool) { intVal := int32(0) if val { intVal = 1 } atomic.StoreInt32(&c.tabletsRoutingV1, intVal) } func (c *Conn) close() error { return c.conn.Close() } func (c *Conn) Close() { c.closeWithError(nil) } // Serve starts the stream multiplexer for this connection, which is required // to execute any queries. This method runs as long as the connection is // open and is therefore usually called in a separate goroutine. func (c *Conn) serve(ctx context.Context) { var err error for err == nil { err = c.recv(ctx) } c.closeWithError(err) } func (c *Conn) discardFrame(head frm.FrameHeader) error { _, err := io.CopyN(io.Discard, c, int64(head.Length)) if err != nil { return err } return nil } type protocolError struct { frame frame } func (p *protocolError) Error() string { if err, ok := p.frame.(error); ok { return err.Error() } return fmt.Sprintf("gocql: received unexpected frame on stream %d: %v", p.frame.Header().Stream, p.frame) } func (c *Conn) heartBeat(ctx context.Context) { sleepTime := 1 * time.Second timer := time.NewTimer(sleepTime) defer timer.Stop() var failures int for { if failures > 5 { c.closeWithError(fmt.Errorf("gocql: heartbeat failed")) return } timer.Reset(sleepTime) select { case <-ctx.Done(): return case <-timer.C: } framer, err := c.exec(context.Background(), &writeOptionsFrame{}, nil, c.cfg.ConnectTimeout) if err != nil { failures++ continue } resp, err := framer.parseFrame() framer.Release() if err != nil { // invalid frame failures++ continue } switch resp.(type) { case *frm.SupportedFrame: // Everything ok sleepTime = 30 * time.Second failures = 0 case error: // TODO: should we do something here? default: panic(fmt.Sprintf("gocql: unknown frame in response to options: %T", resp)) } } } func (c *Conn) recv(ctx context.Context) error { // not safe for concurrent reads // read a full header, ignore timeouts, as this is being ran in a loop // TODO: TCP level deadlines? or just query level deadlines? if c.readTimeout.Load() > 0 { c.conn.SetReadDeadline(time.Time{}) } var headStartTime time.Time if c.frameObserver != nil { headStartTime = time.Now() } // were just reading headers over and over and copy bodies head, err := readHeader(c.r, c.headerBuf[:]) if err != nil { return err } if c.frameObserver != nil { headEndTime := time.Now() c.frameObserver.ObserveFrameHeader(context.Background(), ObservedFrameHeader{ Version: head.Version, Flags: head.Flags, Stream: int16(head.Stream), Opcode: head.Op, Length: int32(head.Length), Start: headStartTime, End: headEndTime, Host: c.host, }) } if head.Stream > c.streams.NumStreams { return fmt.Errorf("gocql: frame header stream is beyond call expected bounds: %d", head.Stream) } else if head.Stream <= 0 { // reserved stream that we dont use, probably due to a protocol error // or a bug in Cassandra, this should be an error, parse it and return. framer, err := c.readFrameIntoFramer(head) if err != nil { return err } frame, err := framer.parseFrame() // NOTE: Safe to release the framer here because all error frame types // (from parseErrorFrame) contain only strings, scalars, and defensively- // copied []byte fields. None retain sub-slices of the framer's read buffer. c.releaseReadFramer(framer) if err != nil { if head.Stream == -1 { // Event frame parse errors should not close the connection. c.logger.Printf("gocql: unable to parse event frame: %v\n", err) return nil } return err } if head.Stream == -1 { // reserved stream for events if c.session != nil { go c.session.handleEvent(frame) } return nil } return &protocolError{ frame: frame, } } c.mu.Lock() if c.closed { c.mu.Unlock() return ErrConnectionClosed } call, ok := c.calls[head.Stream] delete(c.calls, head.Stream) c.mu.Unlock() if call == nil || !ok { c.logger.Printf("gocql: received response for stream which has no handler: header=%v\n", head) return c.discardFrame(head) } else if head.Stream != call.streamID { panic(fmt.Sprintf("call has incorrect streamID: got %d expected %d", call.streamID, head.Stream)) } framer := c.getReadFramer() err = framer.readFrame(c, &head) if err != nil { // only net errors should cause the connection to be closed. Though // cassandra returning corrupt frames will be returned here as well. if _, ok := err.(net.Error); ok { c.releaseReadFramer(framer) return err } } // we either, return a response to the caller, the caller timedout, or the // connection has closed. Either way we should never block indefinatly here select { case call.resp <- callResp{framer: framer, err: err}: // Framer ownership transferred to caller case <-call.timeout: c.abandonRecvCall(call, framer) case <-ctx.Done(): c.abandonRecvCall(call, framer) } return nil } func (c *Conn) readFrameIntoFramer(head frm.FrameHeader) (*framer, error) { framer := c.getReadFramer() if err := framer.readFrame(c, &head); err != nil { c.releaseReadFramer(framer) return nil, err } return framer, nil } func (c *Conn) abandonRecvCall(call *callReq, framer *framer) { c.releaseReadFramer(framer) c.releaseStream(call) call.waitExecDone("abandonRecvCall") putCallReq(call) } func (c *Conn) releaseStream(call *callReq) { c.streams.Clear(call.streamID) if call.streamObserverContext != nil { call.streamObserverEndOnce.Do(func() { call.streamObserverContext.StreamFinished(ObservedStream{ Host: c.host, }) }) } } type callReq struct { // streamObserverContext is notified about events regarding this stream streamObserverContext StreamObserverContext // resp will receive the frame that was sent as a response to this stream. resp chan callResp timeout chan struct{} // indicates to recv() that a call has timed out timer *time.Timer streamID int // current stream in use // streamObserverEndOnce ensures that either StreamAbandoned or StreamFinished is called, // but not both. streamObserverEndOnce sync.Once done sync.WaitGroup } var callReqPool = sync.Pool{ New: func() any { return &callReq{ resp: make(chan callResp), } }, } func getCallReq(streamID int) *callReq { call := callReqPool.Get().(*callReq) call.timeout = make(chan struct{}) call.streamID = streamID call.streamObserverContext = nil call.streamObserverEndOnce = sync.Once{} call.done = sync.WaitGroup{} call.done.Add(1) return call } func putCallReq(call *callReq) { if call.timer != nil { if !call.timer.Stop() { select { case <-call.timer.C: default: } } } call.streamObserverContext = nil call.streamObserverEndOnce = sync.Once{} call.streamID = 0 call.timeout = nil callReqPool.Put(call) } func (call *callReq) finishExec() { call.done.Done() } func (call *callReq) waitExecDone(where string) { waitCallReqDone(call, where) } // removeCallIfOpen removes a call from c.calls only if exec() still owns its // cleanup. Once the connection has started closing, closeWithError() becomes // responsible for draining and recycling detached callReqs. func (c *Conn) removeCallIfOpen(streamID int) bool { c.mu.Lock() defer c.mu.Unlock() if c.closed || c.calls == nil { return false } delete(c.calls, streamID) return true } type callResp struct { // framer is the response frame. // May be nil if err is not nil. framer *framer // err is error encountered, if any. err error } // contextWriter is like io.Writer, but takes context as well. type contextWriter interface { // writeContext writes p to the connection. // // If ctx is canceled before we start writing p (e.g. during waiting while another write is currently in progress), // p is not written and ctx.Err() is returned. Context is ignored after we start writing p (i.e. we don't interrupt // blocked writes that are in progress) so that we always either write the full frame or not write it at all. // // It returns the number of bytes written from p (0 <= n <= len(p)) and any error that caused the write to stop // early. writeContext must return a non-nil error if it returns n < len(p). writeContext must not modify the // data in p, even temporarily. writeContext(ctx context.Context, p []byte) (n int, err error) setWriteTimeout(timeout time.Duration) } type deadlineWriter interface { SetWriteDeadline(time.Time) error io.Writer } type deadlineContextWriter struct { w deadlineWriter // semaphore protects critical section for SetWriteDeadline/Write. // It is a channel with capacity 1. semaphore chan struct{} // quit closed once the connection is closed. quit chan struct{} timeout atomic.Int64 } func (c *deadlineContextWriter) setWriteTimeout(timeout time.Duration) { c.timeout.Store(int64(timeout)) } // writeContext implements contextWriter. func (c *deadlineContextWriter) writeContext(ctx context.Context, p []byte) (int, error) { select { case <-ctx.Done(): return 0, ctx.Err() case <-c.quit: return 0, ErrConnectionClosed case c.semaphore <- struct{}{}: // acquired } defer func() { // release <-c.semaphore }() timeout := c.timeout.Load() if timeout > 0 { err := c.w.SetWriteDeadline(time.Now().Add(time.Duration(timeout))) if err != nil { return 0, err } } return c.w.Write(p) } func newWriteCoalescer(conn deadlineWriter, writeTimeout, coalesceDuration time.Duration, quit <-chan struct{}) *writeCoalescer { wc := &writeCoalescer{ writeCh: make(chan writeRequest), c: conn, quit: quit, } wc.setWriteTimeout(writeTimeout) go wc.writeFlusher(coalesceDuration) return wc } type writeCoalescer struct { c deadlineWriter quit <-chan struct{} writeCh chan writeRequest testEnqueuedHook func() testFlushedHook func() timeout atomic.Int64 mu sync.Mutex } func (w *writeCoalescer) setWriteTimeout(timeout time.Duration) { w.timeout.Store(int64(timeout)) } type writeRequest struct { // resultChan is a channel (with buffer size 1) where to send results of the write. resultChan chan<- writeResult // data to write. data []byte } type writeResult struct { err error n int } // writeResultChanPool pools buffered channels used for write coalescer results. // Each channel is used in a strict produce-once/consume-once pattern: // the flusher goroutine sends exactly one writeResult, and writeContext // reads exactly one. After reading, the channel is empty and safe to reuse. var writeResultChanPool = sync.Pool{ New: func() interface{} { return make(chan writeResult, 1) }, } // writeContext implements contextWriter. func (w *writeCoalescer) writeContext(ctx context.Context, p []byte) (int, error) { resultChan := writeResultChanPool.Get().(chan writeResult) wr := writeRequest{ resultChan: resultChan, data: p, } select { case <-ctx.Done(): writeResultChanPool.Put(resultChan) return 0, ctx.Err() case <-w.quit: writeResultChanPool.Put(resultChan) return 0, io.EOF // TODO: better error here? case w.writeCh <- wr: // enqueued for writing } if w.testEnqueuedHook != nil { w.testEnqueuedHook() } result := <-resultChan writeResultChanPool.Put(resultChan) return result.n, result.err } func (w *writeCoalescer) writeFlusher(interval time.Duration) { timer := time.NewTimer(interval) defer timer.Stop() if !timer.Stop() { <-timer.C } w.writeFlusherImpl(timer.C, func() { timer.Reset(interval) }) } func (w *writeCoalescer) writeFlusherImpl(timerC <-chan time.Time, resetTimer func()) { running := false var buffers net.Buffers var resultChans []chan<- writeResult for { select { case req := <-w.writeCh: buffers = append(buffers, req.data) resultChans = append(resultChans, req.resultChan) if !running { // Start timer on first write. resetTimer() running = true } case <-w.quit: result := writeResult{ n: 0, err: io.EOF, // TODO: better error here? } // Unblock whoever was waiting. for _, resultChan := range resultChans { // resultChan has capacity 1, so it does not block. resultChan <- result } return case <-timerC: running = false w.flush(resultChans, buffers) buffers = nil resultChans = nil if w.testFlushedHook != nil { w.testFlushedHook() } } } } func (w *writeCoalescer) flush(resultChans []chan<- writeResult, buffers net.Buffers) { // Flush everything we have so far. timeout := w.timeout.Load() if timeout > 0 { err := w.c.SetWriteDeadline(time.Now().Add(time.Duration(timeout))) if err != nil { for i := range resultChans { resultChans[i] <- writeResult{ n: 0, err: err, } } return } } // Copy buffers because WriteTo modifies buffers in-place. buffers2 := make(net.Buffers, len(buffers)) copy(buffers2, buffers) n, err := buffers2.WriteTo(w.c) // Writes of bytes before n succeeded, writes of bytes starting from n failed with err. // Use n as remaining byte counter. for i := range buffers { if int64(len(buffers[i])) <= n { // this buffer was fully written. resultChans[i] <- writeResult{ n: len(buffers[i]), err: nil, } n -= int64(len(buffers[i])) } else { // this buffer was not (fully) written. resultChans[i] <- writeResult{ n: int(n), err: err, } n = 0 } } } // addCall attempts to add a call to c.calls. // It fails with error if the connection already started closing or if a call for the given stream // already exists. func (c *Conn) addCall(call *callReq) error { c.mu.Lock() defer c.mu.Unlock() if c.closed { return ErrConnectionClosed } existingCall := c.calls[call.streamID] if existingCall != nil { return fmt.Errorf("attempting to use stream already in use: %d -> %d", call.streamID, existingCall.streamID) } c.calls[call.streamID] = call return nil } // exec executes a frame on the connection and returns the response framer. // // IMPORTANT: The caller takes ownership of the returned framer and MUST call // framer.Release() when done reading the response. Failure to release the framer // will leak memory and prevent buffer reuse. // // The framer should be released as soon as the response data is no longer needed, // typically via defer immediately after parsing or after transferring ownership // to an Iter. func (c *Conn) exec(ctx context.Context, req frameBuilder, tracer Tracer, requestTimeout time.Duration) (*framer, error) { if ctxErr := ctx.Err(); ctxErr != nil { return nil, &QueryError{err: ctxErr, potentiallyExecuted: false} } // TODO: move tracer onto conn stream, ok := c.streams.GetStream() if !ok { return nil, &QueryError{err: ErrNoStreams, potentiallyExecuted: false} } // resp is basically a waiting semaphore protecting the framer framer := c.getWriteFramer() call := getCallReq(stream) if c.streamObserver != nil { call.streamObserverContext = c.streamObserver.StreamContext(ctx) } if err := c.addCall(call); err != nil { call.finishExec() putCallReq(call) c.releaseWriteFramer(framer) return nil, &QueryError{err: err, potentiallyExecuted: false} } // After this point, we need to either read from call.resp or close(call.timeout) // since closeWithError can try to write a connection close error to call.resp. // If we don't close(call.timeout) or read from call.resp, closeWithError can deadlock. var ( stopWaiting bool releaseStream bool recycleCall bool closeErr error ) defer func() { if closeErr != nil { c.closeWithError(closeErr) } }() defer func() { if stopWaiting { close(call.timeout) } call.finishExec() if releaseStream { c.releaseStream(call) } if recycleCall { putCallReq(call) } }() if tracer != nil { framer.trace() } if call.streamObserverContext != nil { call.streamObserverContext.StreamStarted(ObservedStream{ Host: c.host, }) } err := req.buildFrame(framer, stream) if err != nil { c.releaseWriteFramer(framer) // closeWithError waits for exec() to stop touching the callReq, so the // deferred epilogue below is responsible for signaling completion. stopWaiting = true if c.removeCallIfOpen(call.streamID) { // We failed to serialize the frame into a buffer. This should not affect // the connection as we didn't write anything, so exec() still owns the // stream/call cleanup. releaseStream = true recycleCall = true } return nil, &QueryError{err: err, potentiallyExecuted: false} } n, err := c.w.writeContext(ctx, framer.buf) c.releaseWriteFramer(framer) if err != nil { // closeWithError waits for exec() to stop touching the callReq, so defer // the completion signal and only record the cleanup we need here. stopWaiting = true if (errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded)) && n == 0 { // We have not started to write this frame. // Release the stream as no response can come from the server on the stream. if c.removeCallIfOpen(call.streamID) { // We need to release the stream after we remove the call from c.calls, // otherwise the existingCall != nil check above could fail. releaseStream = true recycleCall = true } } else { // I think this is the correct thing to do, im not entirely sure. It is not // ideal as readers might still get some data, but they probably wont. // Here we need to be careful as the stream is not available and if all // writes just timeout or fail then the pool might use this connection to // send a frame on, with all the streams used up and not returned. closeErr = err } return nil, &QueryError{err: err, potentiallyExecuted: true} } var timeoutCh <-chan time.Time if requestTimeout > 0 { if call.timer == nil { call.timer = time.NewTimer(requestTimeout) } else { if !call.timer.Stop() { select { case <-call.timer.C: default: } } call.timer.Reset(requestTimeout) } timeoutCh = call.timer.C } var ctxDone <-chan struct{} if ctx != nil { ctxDone = ctx.Done() } select { case resp := <-call.resp: stopWaiting = true if resp.err != nil { c.releaseReadFramer(resp.framer) if !c.Closed() { // if the connection is closed then we cant release the stream, // this is because the request is still outstanding and we have // been handed another error from another stream which caused the // connection to close. releaseStream = true recycleCall = true } return nil, &QueryError{err: resp.err, potentiallyExecuted: true} } // dont release the stream if detect a timeout as another request can reuse // that stream and get a response for the old request, which we have no // easy way of detecting. // // Ensure that the stream is not released if there are potentially outstanding // requests on the stream to prevent nil pointer dereferences in recv(). releaseStream = true recycleCall = true if v := resp.framer.header.Version.Version(); v != c.version { c.releaseReadFramer(resp.framer) return nil, &QueryError{err: NewErrProtocol("unexpected protocol version in response: got %d expected %d", v, c.version), potentiallyExecuted: true} } // NOTE: The returned framer becomes the caller's responsibility to release. // It is not released here to allow zero-copy access to the response data. // The caller must call Release() on the returned read framer when done reading the response. return resp.framer, nil case <-timeoutCh: stopWaiting = true return nil, &QueryError{err: ErrTimeoutNoResponse, potentiallyExecuted: true, timeout: requestTimeout, inFlight: c.streams.InUse()} case <-ctxDone: stopWaiting = true return nil, &QueryError{err: ctx.Err(), potentiallyExecuted: true, timeout: requestTimeout, inFlight: c.streams.InUse()} case <-c.ctx.Done(): stopWaiting = true return nil, &QueryError{err: ErrConnectionClosed, potentiallyExecuted: true} } } // ObservedStream observes a single request/response stream. type ObservedStream struct { // Host of the connection used to send the stream. Host *HostInfo } // StreamObserver is notified about request/response pairs. // Streams are created for executing queries/batches or // internal requests to the database and might live longer than // execution of the query - the stream is still tracked until // response arrives so that stream IDs are not reused. type StreamObserver interface { // StreamContext is called before creating a new stream. // ctx is context passed to Session.Query / Session.Batch, // but might also be an internal context (for example // for internal requests that use control connection). // StreamContext might return nil if it is not interested // in the details of this stream. // StreamContext is called before the stream is created // and the returned StreamObserverContext might be discarded // without any methods called on the StreamObserverContext if // creation of the stream fails. // Note that if you don't need to track per-stream data, // you can always return the same StreamObserverContext. StreamContext(ctx context.Context) StreamObserverContext } // StreamObserverContext is notified about state of a stream. // A stream is started every time a request is written to the server // and is finished when a response is received. // It is abandoned when the underlying network connection is closed // before receiving a response. type StreamObserverContext interface { // StreamStarted is called when the stream is started. // This happens just before a request is written to the wire. StreamStarted(observedStream ObservedStream) // StreamAbandoned is called when we stop waiting for response. // This happens when the underlying network connection is closed. // StreamFinished won't be called if StreamAbandoned is. StreamAbandoned(observedStream ObservedStream) // StreamFinished is called when we receive a response for the stream. StreamFinished(observedStream ObservedStream) } type preparedStatment struct { id []byte response resultMetadata request preparedMetadata } type inflightPrepare struct { done chan struct{} err error preparedStatment *preparedStatment } func (c *Conn) prepareStatement(ctx context.Context, stmt string, tracer Tracer, requestTimeout time.Duration) (*preparedStatment, error) { cacheKey := c.session.stmtsLRU.keyFor(c.host.HostID(), c.currentKeyspace, stmt) flight, ok := c.session.stmtsLRU.execIfMissing(cacheKey, func(cache *lru.Cache[stmtCacheKey]) *inflightPrepare { flight := &inflightPrepare{ done: make(chan struct{}), } cache.Add(cacheKey, flight) return flight }) if !ok { go func() { defer close(flight.done) prep := &writePrepareFrame{ statement: stmt, } if c.version > protoVersion4 { prep.keyspace = c.currentKeyspace } // we won the race to do the load, if our context is canceled we shouldnt // stop the load as other callers are waiting for it but this caller should get // their context cancelled error. framer, err := c.exec(c.ctx, prep, tracer, requestTimeout) if err != nil { flight.err = err c.session.stmtsLRU.remove(cacheKey) return } defer framer.Release() frame, err := framer.parseFrame() if err != nil { flight.err = err c.session.stmtsLRU.remove(cacheKey) return } // TODO(zariel): tidy this up, simplify handling of frame parsing so its not duplicated // everytime we need to parse a frame. if len(framer.traceID) > 0 && tracer != nil { tracer.Trace(framer.traceID) } switch x := frame.(type) { case *resultPreparedFrame: flight.preparedStatment = &preparedStatment{ id: x.preparedID, request: x.reqMeta, response: x.respMeta, } case error: flight.err = x default: flight.err = NewErrProtocol("Unknown type in response to prepare frame: %s", x) } if flight.err != nil { c.session.stmtsLRU.remove(cacheKey) } }() } select { case <-ctx.Done(): return nil, ctx.Err() case <-flight.done: return flight.preparedStatment, flight.err } } func marshalQueryValue(typ TypeInfo, value any, dst *queryValues) error { if named, ok := value.(*namedValue); ok { dst.name = named.name value = named.value } if _, ok := value.(unsetColumn); !ok { val, err := Marshal(typ, value) if err != nil { return err } dst.value = val } else { dst.isUnset = true } return nil } func (c *Conn) executeQuery(ctx context.Context, qry *Query) (iter *Iter) { params := queryParams{ consistency: qry.cons, } // frame checks that it is not 0 params.serialConsistency = qry.serialCons params.defaultTimestamp = qry.defaultTimestamp params.defaultTimestampValue = qry.defaultTimestampValue if len(qry.pageState) > 0 { params.pagingState = qry.pageState } if qry.pageSize > 0 { params.pageSize = qry.pageSize } if c.version > protoVersion4 { params.keyspace = c.currentKeyspace } var ( frame frameBuilder info *preparedStatment ) if !qry.skipPrepare && qry.shouldPrepare() { // Prepare all DML queries. Other queries can not be prepared. var err error info, err = c.prepareStatement(ctx, qry.stmt, qry.trace, qry.GetRequestTimeout()) if err != nil { return &Iter{err: err} } values := qry.values if qry.binding != nil { values, err = qry.binding(&QueryInfo{ Id: info.id, Args: info.request.columns, Rval: info.response.columns, PKeyColumns: info.request.pkeyColumns, }) if err != nil { return &Iter{err: err} } } if len(values) != info.request.actualColCount { return &Iter{err: fmt.Errorf("gocql: expected %d values send got %d", info.request.actualColCount, len(values))} } params.values = make([]queryValues, len(values)) for i := 0; i < len(values); i++ { v := ¶ms.values[i] value := values[i] typ := info.request.columns[i].TypeInfo if err := marshalQueryValue(typ, value, v); err != nil { return &Iter{err: err} } } // if the metadata was not present in the response then we should not skip it params.skipMeta = !(c.session.cfg.DisableSkipMetadata || qry.disableSkipMetadata) && len(info.response.columns) != 0 frame = &writeExecuteFrame{ preparedID: info.id, params: params, customPayload: qry.customPayload, } // Set "lwt", keyspace", "table" property in the query if it is present in preparedMetadata qry.routingInfo.mu.Lock() qry.routingInfo.lwt = info.request.lwt qry.routingInfo.keyspace = info.request.keyspace qry.routingInfo.table = info.request.table qry.routingInfo.mu.Unlock() } else { frame = &writeQueryFrame{ statement: qry.stmt, params: params, customPayload: qry.customPayload, } } framer, err := c.exec(ctx, frame, qry.trace, qry.GetRequestTimeout()) if err != nil { return &Iter{err: err} } warningHandler := WarningHandler(nil) if c.session != nil { warningHandler = c.session.warningHandler } resp, err := framer.parseFrame() if err != nil { return newErrorIterWithReleasedFramer(err, framer).bindWarningHandler(qry, warningHandler) } if len(framer.customPayload) > 0 { if hint, ok := framer.customPayload["tablets-routing-v1"]; ok { tablet, err := unmarshalTabletHint(hint, c.version, qry.routingInfo.keyspace, qry.routingInfo.table) if err != nil { return newErrorIterWithReleasedFramer(err, framer).bindWarningHandler(qry, warningHandler) } c.session.metadataDescriber.AddTablet(tablet) } } if len(framer.traceID) > 0 && qry.trace != nil { qry.trace.Trace(framer.traceID) } switch x := resp.(type) { case *resultVoidFrame: return (&Iter{framer: framer}).bindWarningHandler(qry, warningHandler) case *resultRowsFrame: iter := (&Iter{ meta: x.meta, framer: framer, numRows: x.numRows, }).bindWarningHandler(qry, warningHandler) if params.skipMeta { if info != nil { iter.meta = info.response // pagingState is already independently allocated by readBytesCopy() // during frame parsing, no additional copy needed. iter.meta.pagingState = x.meta.pagingState } else { return newErrorIterWithReleasedFramer(errors.New("gocql: did not receive metadata but prepared info is nil"), framer).bindWarningHandler(qry, warningHandler) } } if x.meta.morePages() && !qry.disableAutoPage { newQry := new(Query) *newQry = *qry newQry.pageState = x.meta.pagingState newQry.metrics = &queryMetrics{m: make(map[UUID]*hostMetrics)} iter.next = newNextIter(newQry, int((1-qry.prefetch)*float64(x.numRows))) if iter.next.pos < 1 { iter.next.pos = 1 } } return iter case *resultKeyspaceFrame: return (&Iter{framer: framer}).bindWarningHandler(qry, warningHandler) case *frm.SchemaChangeKeyspace, *frm.SchemaChangeTable, *frm.SchemaChangeFunction, *frm.SchemaChangeAggregate, *frm.SchemaChangeType: iter := (&Iter{framer: framer}).bindWarningHandler(qry, warningHandler) if err := c.awaitSchemaAgreement(ctx); err != nil { // TODO: should have this behind a flag c.logger.Println(err) } // dont return an error from this, might be a good idea to give a warning // though. The impact of this returning an error would be that the cluster // is not consistent with regards to its schema. return iter case *RequestErrUnprepared: stmtCacheKey := c.session.stmtsLRU.keyFor(c.host.HostID(), c.currentKeyspace, qry.stmt) c.session.stmtsLRU.evictPreparedID(stmtCacheKey, x.StatementId) framer.Release() return c.executeQuery(ctx, qry) case error: return newErrorIterWithReleasedFramer(x, framer).bindWarningHandler(qry, warningHandler) default: return newErrorIterWithReleasedFramer(NewErrProtocol("Unknown type in response to execute query (%T): %s", x, x), framer).bindWarningHandler(qry, warningHandler) } } func (c *Conn) Pick(qry *Query) *Conn { if c.Closed() { return nil } return c } func (c *Conn) Closed() bool { c.mu.Lock() defer c.mu.Unlock() return c.closed } func (c *Conn) Address() string { return c.addr } func (c *Conn) AvailableStreams() int { return c.streams.Available() } func useKeyspaceStmt(keyspace string) string { return `USE "` + strings.ReplaceAll(keyspace, `"`, `""`) + `"` } func (c *Conn) UseKeyspace(keyspace string) error { q := &writeQueryFrame{statement: useKeyspaceStmt(keyspace)} q.params.consistency = c.session.cons framer, err := c.exec(c.ctx, q, nil, c.cfg.ConnectTimeout) if err != nil { return err } defer framer.Release() resp, err := framer.parseFrame() if err != nil { return err } switch x := resp.(type) { case *resultKeyspaceFrame: case error: return x default: return NewErrProtocol("unknown frame in response to USE: %v", x) } c.currentKeyspace = keyspace return nil } func (c *Conn) executeBatch(ctx context.Context, batch *Batch) (iter *Iter) { n := len(batch.Entries) req := &writeBatchFrame{ typ: batch.Type, statements: make([]batchStatment, n), consistency: batch.Cons, serialConsistency: batch.serialCons, defaultTimestamp: batch.defaultTimestamp, defaultTimestampValue: batch.defaultTimestampValue, customPayload: batch.CustomPayload, } stmts := make(map[string]string, len(batch.Entries)) hasLwtEntries := false for i := 0; i < n; i++ { entry := &batch.Entries[i] b := &req.statements[i] if len(entry.Args) > 0 || entry.binding != nil { info, err := c.prepareStatement(batch.Context(), entry.Stmt, batch.trace, batch.GetRequestTimeout()) if err != nil { return &Iter{err: err} } var values []any if entry.binding == nil { values = entry.Args } else { values, err = entry.binding(&QueryInfo{ Id: info.id, Args: info.request.columns, Rval: info.response.columns, PKeyColumns: info.request.pkeyColumns, }) if err != nil { return &Iter{err: err} } } if len(values) != info.request.actualColCount { return &Iter{err: fmt.Errorf("gocql: batch statement %d expected %d values send got %d", i, info.request.actualColCount, len(values))} } b.preparedID = info.id stmts[string(info.id)] = entry.Stmt b.values = make([]queryValues, info.request.actualColCount) for j := 0; j < info.request.actualColCount; j++ { v := &b.values[j] value := values[j] typ := info.request.columns[j].TypeInfo if err := marshalQueryValue(typ, value, v); err != nil { return &Iter{err: err} } } if !hasLwtEntries && info.request.lwt { hasLwtEntries = true } } else { b.statement = entry.Stmt } } // The batch is considered to be conditional if even one of the // statements is conditional. batch.routingInfo.mu.Lock() batch.routingInfo.lwt = hasLwtEntries batch.routingInfo.mu.Unlock() // TODO: should batch support tracing? framer, err := c.exec(batch.Context(), req, batch.trace, batch.GetRequestTimeout()) if err != nil { return &Iter{err: err} } warningHandler := WarningHandler(nil) if c.session != nil { warningHandler = c.session.warningHandler } resp, err := framer.parseFrame() if err != nil { return newErrorIterWithReleasedFramer(err, framer).bindWarningHandler(batch, warningHandler) } if len(framer.traceID) > 0 && batch.trace != nil { batch.trace.Trace(framer.traceID) } switch x := resp.(type) { case *resultVoidFrame: return (&Iter{framer: framer}).bindWarningHandler(batch, warningHandler) case *RequestErrUnprepared: stmt, found := stmts[string(x.StatementId)] if found { key := c.session.stmtsLRU.keyFor(c.host.HostID(), c.currentKeyspace, stmt) c.session.stmtsLRU.evictPreparedID(key, x.StatementId) } framer.Release() return c.executeBatch(ctx, batch) case *resultRowsFrame: iter := (&Iter{ meta: x.meta, framer: framer, numRows: x.numRows, }).bindWarningHandler(batch, warningHandler) return iter case error: return newErrorIterWithReleasedFramer(x, framer).bindWarningHandler(batch, warningHandler) default: return newErrorIterWithReleasedFramer(NewErrProtocol("Unknown type in response to batch statement: %s", x), framer).bindWarningHandler(batch, warningHandler) } } func (c *Conn) querySystem(ctx context.Context, query string, values ...any) *Iter { q := c.session.Query(query+c.usingTimeoutClause, values...).Consistency(One).Trace(nil) q.skipPrepare = true q.disableSkipMetadata = true // we want to keep the query on this connection q.conn = c q.SetRequestTimeout(c.systemRequestTimeout) return c.executeQuery(ctx, q) } const qrySystemPeers = "SELECT * FROM system.peers" const qrySystemPeersV2 = "SELECT * FROM system.peers_v2" const qrySystemLocal = "SELECT * FROM system.local WHERE key='local'" func getSchemaAgreement(queryLocalSchemasRows []string, querySystemPeersRows []schemaAgreementHost, logger StdLogger) (err error) { versions := make(map[string]struct{}) for _, row := range querySystemPeersRows { if !row.IsValid() { logger.Printf("invalid peer or peer with empty schema_version: peer=%q", row) continue } versions[row.SchemaVersion.String()] = struct{}{} } for _, schemaVersion := range queryLocalSchemasRows { versions[schemaVersion] = struct{}{} schemaVersion = "" } if len(versions) > 1 { schemas := make([]string, 0, len(versions)) for schema := range versions { schemas = append(schemas, schema) } return &ErrSchemaMismatch{schemas: schemas} } return nil } type schemaAgreementHost struct { DataCenter string Rack string RPCAddress string HostID UUID SchemaVersion UUID } func (h *schemaAgreementHost) IsValid() bool { return h.DataCenter != "" && h.Rack != "" && h.HostID.String() != "" && h.SchemaVersion.String() != "" } func (c *Conn) awaitSchemaAgreement(ctx context.Context) error { endDeadline := time.Now().Add(c.session.cfg.MaxWaitSchemaAgreement) var err error ticker := time.NewTicker(200 * time.Millisecond) // Create a ticker that ticks every 200ms defer ticker.Stop() waitForNextTick := func() error { select { case <-ctx.Done(): return ctx.Err() case <-ticker.C: return nil } } for time.Now().Before(endDeadline) { var iter *Iter if c.getIsSchemaV2() { iter = c.querySystem(ctx, "SELECT host_id, data_center, rack, schema_version, preferred_ip FROM system.peers_v2") } else { iter = c.querySystem(ctx, "SELECT host_id, data_center, rack, schema_version, rpc_address FROM system.peers") } // data_center, rack, host_id, schema_version, rpc_address var hosts []schemaAgreementHost var tmp schemaAgreementHost for iter.Scan(&tmp.HostID, &tmp.DataCenter, &tmp.Rack, &tmp.SchemaVersion, &tmp.RPCAddress) { hosts = append(hosts, tmp) } err = iter.Close() if err != nil { return err } schemaVersions := []string{} iter = c.querySystem(ctx, "SELECT schema_version FROM system.local WHERE key='local'") var schemaVersion string for iter.Scan(&schemaVersion) { schemaVersions = append(schemaVersions, schemaVersion) schemaVersion = "" } if err = iter.Close(); err != nil { return err } err = getSchemaAgreement(schemaVersions, hosts, c.logger) if err == ErrConnectionClosed || err == nil { return err } if tickerErr := waitForNextTick(); tickerErr != nil { return tickerErr } } return err } var ( ErrQueryArgLength = errors.New("gocql: query argument length mismatch") ErrTimeoutNoResponse = errors.New("gocql: no response received from cassandra within timeout period") // Deprecated: ErrTooManyTimeouts is no longer produced by the library. // It will be removed in a future major release. ErrTooManyTimeouts = errors.New("gocql: too many query timeouts on the connection") ErrConnectionClosed = errors.New("gocql: connection closed waiting for response") ErrNoStreams = errors.New("gocql: no streams available on connection") ErrHostDown = errors.New("gocql: host is nil or down") ErrNoPool = errors.New("gocql: host does not have a pool") ErrNoConnectionsInPool = errors.New("gocql: host pool does not have connections") ) type ErrSchemaMismatch struct { schemas []string } func (e *ErrSchemaMismatch) Error() string { return fmt.Sprintf("gocql: cluster schema versions not consistent: %+v", e.schemas) } type QueryError struct { err error timeout time.Duration inFlight int potentiallyExecuted bool isIdempotent bool } func (e *QueryError) IsIdempotent() bool { return e.isIdempotent } func (e *QueryError) PotentiallyExecuted() bool { return e.potentiallyExecuted } func (e *QueryError) Error() string { if e.timeout > 0 { return fmt.Sprintf("%s (timeout: %v, in-flight: %d) (potentially executed: %v)", e.err.Error(), e.timeout, e.inFlight, e.potentiallyExecuted) } return fmt.Sprintf("%s (potentially executed: %v)", e.err.Error(), e.potentiallyExecuted) } func (e *QueryError) Unwrap() error { return e.err } func unmarshalTabletHint(hint []byte, v uint8, keyspace, table string) (tablets.TabletInfo, error) { tabletBuilder := tablets.NewTabletInfoBuilder() err := Unmarshal(TupleTypeInfo{ NativeType: NativeType{proto: v, typ: TypeTuple}, Elems: []TypeInfo{ NativeType{typ: TypeBigInt}, NativeType{typ: TypeBigInt}, CollectionType{ NativeType: NativeType{proto: v, typ: TypeList}, Elem: TupleTypeInfo{ NativeType: NativeType{proto: v, typ: TypeTuple}, Elems: []TypeInfo{ NativeType{proto: v, typ: TypeUUID}, NativeType{proto: v, typ: TypeInt}, }}, }, }, }, hint, []any{&tabletBuilder.FirstToken, &tabletBuilder.LastToken, &tabletBuilder.Replicas}) if err != nil { return tablets.TabletInfo{}, err } tabletBuilder.KeyspaceName = keyspace tabletBuilder.TableName = table return tabletBuilder.Build() } ================================================ FILE: conn_test.go ================================================ //go:build unit // +build unit /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2012, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "bufio" "bytes" "context" "crypto/tls" "crypto/x509" "encoding/binary" "errors" "fmt" "io" "math/rand" "net" "os" "strings" "sync" "sync/atomic" "testing" "time" "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" frm "github.com/gocql/gocql/internal/frame" "github.com/gocql/gocql/internal/streams" ) const ( defaultProto = protoVersion3 ) type brokenDNSResolver struct{} func (b brokenDNSResolver) LookupIP(host string) ([]net.IP, error) { err := errors.New("this error comes from mocked broken resolver") return nil, &net.DNSError{ UnwrapErr: err, Err: err.Error(), Server: host, } } func TestApprove(t *testing.T) { tests := map[bool]bool{ approve("org.apache.cassandra.auth.PasswordAuthenticator", []string{}): true, approve("org.apache.cassandra.auth.MutualTlsWithPasswordFallbackAuthenticator", []string{}): true, approve("org.apache.cassandra.auth.MutualTlsAuthenticator", []string{}): true, approve("com.instaclustr.cassandra.auth.SharedSecretAuthenticator", []string{}): true, approve("com.datastax.bdp.cassandra.auth.DseAuthenticator", []string{}): true, approve("io.aiven.cassandra.auth.AivenAuthenticator", []string{}): true, approve("com.amazon.helenus.auth.HelenusAuthenticator", []string{}): true, approve("com.ericsson.bss.cassandra.ecaudit.auth.AuditAuthenticator", []string{}): true, approve("com.scylladb.auth.SaslauthdAuthenticator", []string{}): true, approve("com.scylladb.auth.TransitionalAuthenticator", []string{}): true, approve("com.instaclustr.cassandra.auth.InstaclustrPasswordAuthenticator", []string{}): true, approve("com.apache.cassandra.auth.FakeAuthenticator", []string{}): true, approve("com.apache.cassandra.auth.FakeAuthenticator", nil): true, approve("com.apache.cassandra.auth.FakeAuthenticator", []string{"com.apache.cassandra.auth.FakeAuthenticator"}): true, approve("com.apache.cassandra.auth.FakeAuthenticator", []string{"com.apache.cassandra.auth.NotFakeAuthenticator"}): false, } for k, v := range tests { if k != v { t.Fatalf("expected '%v', got '%v'", k, v) } } } func testCluster(proto frm.ProtoVersion, addresses ...string) *ClusterConfig { cluster := NewCluster(addresses...) cluster.ProtoVersion = int(proto) cluster.disableControlConn = true cluster.PoolConfig.HostSelectionPolicy = RoundRobinHostPolicy() return cluster } func TestSimple(t *testing.T) { srv := NewTestServer(t, defaultProto, context.Background()) defer srv.Stop() cluster := testCluster(defaultProto, srv.Address) db, err := cluster.CreateSession() if err != nil { t.Fatalf("0x%x: NewCluster: %v", defaultProto, err) } if err := db.Query("void").Exec(); err != nil { t.Fatalf("0x%x: %v", defaultProto, err) } } func TestSSLSimple(t *testing.T) { srv := NewSSLTestServer(t, defaultProto, context.Background()) defer srv.Stop() db, err := createTestSslCluster(srv.Address, defaultProto, true).CreateSession() if err != nil { t.Fatalf("0x%x: NewCluster: %v", defaultProto, err) } if err := db.Query("void").Exec(); err != nil { t.Fatalf("0x%x: %v", defaultProto, err) } } func TestSSLSimpleNoClientCert(t *testing.T) { srv := NewSSLTestServer(t, defaultProto, context.Background()) defer srv.Stop() db, err := createTestSslCluster(srv.Address, defaultProto, false).CreateSession() if err != nil { t.Fatalf("0x%x: NewCluster: %v", defaultProto, err) } if err := db.Query("void").Exec(); err != nil { t.Fatalf("0x%x: %v", defaultProto, err) } } func createTestSslCluster(addr string, proto frm.ProtoVersion, useClientCert bool) *ClusterConfig { cluster := testCluster(proto, addr) sslOpts := &SslOptions{ CaPath: "testdata/pki/ca.crt", EnableHostVerification: false, } if useClientCert { sslOpts.CertPath = "testdata/pki/gocql.crt" sslOpts.KeyPath = "testdata/pki/gocql.key" } cluster.SslOpts = sslOpts return cluster } func TestClosed(t *testing.T) { t.Skip("Skipping the execution of TestClosed for now to try to concentrate on more important test failures on Travis") srv := NewTestServer(t, defaultProto, context.Background()) defer srv.Stop() session, err := newTestSession(defaultProto, srv.Address) if err != nil { t.Fatalf("0x%x: NewCluster: %v", defaultProto, err) } session.Close() if err := session.Query("void").Exec(); err != ErrSessionClosed { t.Fatalf("0x%x: expected %#v, got %#v", defaultProto, ErrSessionClosed, err) } } func newTestSession(proto frm.ProtoVersion, addresses ...string) (*Session, error) { return testCluster(proto, addresses...).CreateSession() } var _ DNSResolver = brokenDNSResolver{} func TestDNSLookupConnected(t *testing.T) { log := &testLogger{} // Override the default DNS resolver and restore at the end srv := NewTestServer(t, defaultProto, context.Background()) defer srv.Stop() cluster := NewCluster("cassandra1.invalid", srv.Address, "cassandra2.invalid") cluster.Logger = log cluster.ProtoVersion = int(defaultProto) cluster.disableControlConn = true cluster.DNSResolver = brokenDNSResolver{} // CreateSession() should attempt to resolve the DNS name "cassandraX.invalid" // and fail, but continue to connect via srv.Address _, err := cluster.CreateSession() if err != nil { t.Fatal("CreateSession() should have connected") } if !strings.Contains(log.String(), "failed to resolve endpoint") { t.Fatalf("Expected to receive 'failed to resolve endpoint' log message - got '%s' instead", log.String()) } } func TestDNSLookupError(t *testing.T) { log := &testLogger{} // Override the default DNS resolver and restore at the end hosts := []string{"cassandra1.invalid", "cassandra2.invalid"} cluster := NewCluster(hosts...) cluster.Logger = log cluster.ProtoVersion = int(defaultProto) cluster.disableControlConn = true cluster.DNSResolver = brokenDNSResolver{} // CreateSession() should attempt to resolve each DNS name "cassandraX.invalid" // and fail since it could not resolve any dns entries _, err := cluster.CreateSession() if err == nil { t.Fatal("CreateSession() should have returned an error") } if !strings.Contains(log.String(), "failed to resolve endpoint") { t.Fatalf("Expected to receive 'failed to resolve endpoint' log message - got '%s' instead", log.String()) } if !strings.Contains(err.Error(), "unable to create session: failed to resolve any of the provided hostnames") { t.Fatalf("Expected CreateSession() to fail with error message that contains 'unable to create session: failed to resolve any of the provided hostnames'") } for _, host := range hosts { expected := fmt.Sprintf("failed to resolve endpoint \"%s\": lookup on %s: this error comes from mocked broken resolver", host, host) if !strings.Contains(err.Error(), expected) { t.Fatalf("Expected to fail with error message that contains '%s'", expected) } } } func TestStartupTimeout(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) log := &testLogger{} srv := NewTestServer(t, defaultProto, ctx) defer srv.Stop() // Tell the server to never respond to Startup frame atomic.StoreInt32(&srv.TimeoutOnStartup, 1) startTime := time.Now() cluster := NewCluster(srv.Address) cluster.Logger = log cluster.ProtoVersion = int(defaultProto) cluster.disableControlConn = true // Set very long query connection timeout // so we know CreateSession() is using the ConnectTimeout cluster.Timeout = time.Second * 5 cluster.ConnectTimeout = 600 * time.Millisecond // Create session should timeout during connect attempt _, err := cluster.CreateSession() if err == nil { t.Fatal("CreateSession() should have returned a timeout error") } elapsed := time.Since(startTime) if elapsed > time.Second*5 { t.Fatal("ConnectTimeout is not respected") } if !errors.Is(err, ErrNoConnectionsStarted) { t.Fatalf("Expected to receive no connections error - got '%s'", err) } if !strings.Contains(log.String(), "no response to connection startup within timeout") && !strings.Contains(log.String(), "no response received from cassandra within timeout period") { t.Fatalf("Expected to receive timeout log message - got '%s'", log.String()) } cancel() } func TestTimeout(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) srv := NewTestServer(t, defaultProto, ctx) defer srv.Stop() db, err := newTestSession(defaultProto, srv.Address) if err != nil { t.Fatalf("NewCluster: %v", err) } defer db.Close() var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() select { case <-time.After(5 * time.Second): t.Errorf("no timeout") case <-ctx.Done(): } }() if err := db.Query("kill").WithContext(ctx).Exec(); err == nil { t.Fatal("expected error got nil") } cancel() wg.Wait() } func TestCancel(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() srv := NewTestServer(t, defaultProto, ctx) defer srv.Stop() cluster := testCluster(defaultProto, srv.Address) cluster.Timeout = 1 * time.Second db, err := cluster.CreateSession() if err != nil { t.Fatalf("NewCluster: %v", err) } defer db.Close() qry := db.Query("timeout").WithContext(ctx) // Make sure we finish the query without leftovers var wg sync.WaitGroup wg.Add(1) go func() { err = qry.Exec() wg.Done() }() // The query will timeout after about 1 seconds, so cancel it after a short pause time.AfterFunc(20*time.Millisecond, cancel) wg.Wait() if !errors.Is(err, context.Canceled) { t.Fatalf("expected to get context cancel error: '%v', got '%v'", context.Canceled, err) } } type testQueryObserver struct { metrics map[string]*hostMetrics verbose bool logger StdLogger } func (o *testQueryObserver) ObserveQuery(ctx context.Context, q ObservedQuery) { host := q.Host.ConnectAddress().String() o.metrics[host] = q.Metrics if o.verbose { o.logger.Printf("Observed query %q. Returned %v rows, took %v on host %q with %v attempts and total latency %v. Error: %q\n", q.Statement, q.Rows, q.End.Sub(q.Start), host, q.Metrics.Attempts, q.Metrics.TotalLatency, q.Err) } } func (o *testQueryObserver) GetMetrics(host *HostInfo) *hostMetrics { return o.metrics[host.ConnectAddress().String()] } // TestQueryRetry will test to make sure that gocql will execute // the exact amount of retry queries designated by the user. func TestQueryRetry(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() srv := NewTestServer(t, defaultProto, ctx) defer srv.Stop() db, err := newTestSession(defaultProto, srv.Address) if err != nil { t.Fatalf("NewCluster: %v", err) } defer db.Close() go func() { select { case <-ctx.Done(): return case <-time.After(5 * time.Second): t.Errorf("no timeout") } }() rt := &SimpleRetryPolicy{NumRetries: 1} qry := db.Query("kill").RetryPolicy(rt) if err := qry.Exec(); err == nil { t.Fatalf("expected error") } requests := atomic.LoadInt64(&srv.nKillReq) attempts := qry.Attempts() if requests != int64(attempts) { t.Fatalf("expected requests %v to match query attempts %v", requests, attempts) } // the query will only be attempted once, but is being retried if requests != int64(rt.NumRetries) { t.Fatalf("failed to retry the query %v time(s). Query executed %v times", rt.NumRetries, requests-1) } } func TestQueryMultinodeWithMetrics(t *testing.T) { log := &testLogger{} defer func() { os.Stdout.WriteString(log.String()) }() // Build a 3 node cluster to test host metric mapping var nodes []*TestServer var addresses = []string{ "127.0.0.1", "127.0.0.2", "127.0.0.3", } // Can do with 1 context for all servers ctx := context.Background() for _, ip := range addresses { srv := NewTestServerWithAddress(ip+":0", t, defaultProto, ctx) defer srv.Stop() nodes = append(nodes, srv) } db, err := newTestSession(defaultProto, nodes[0].Address, nodes[1].Address, nodes[2].Address) if err != nil { t.Fatalf("NewCluster: %v", err) } defer db.Close() // 1 retry per host rt := &SimpleRetryPolicy{NumRetries: 3} observer := &testQueryObserver{metrics: make(map[string]*hostMetrics), verbose: false, logger: log} qry := db.Query("kill").RetryPolicy(rt).Observer(observer).Idempotent(true) if err := qry.Exec(); err == nil { t.Fatalf("expected error") } for i, ip := range addresses { var host *HostInfo for _, clusterHost := range db.GetHosts() { if clusterHost.connectAddress.String() == ip { host = clusterHost } } if host == nil { t.Fatalf("failed to observe host info for address %v", ip) } queryMetric := qry.metrics.hostMetrics(host) observedMetrics := observer.GetMetrics(host) requests := int(atomic.LoadInt64(&nodes[i].nKillReq)) hostAttempts := queryMetric.Attempts if requests != hostAttempts { t.Fatalf("expected requests %v to match query attempts %v", requests, hostAttempts) } if hostAttempts != observedMetrics.Attempts { t.Fatalf("expected observed attempts %v to match query attempts %v on host %v", observedMetrics.Attempts, hostAttempts, ip) } hostLatency := queryMetric.TotalLatency observedLatency := observedMetrics.TotalLatency if hostLatency != observedLatency { t.Fatalf("expected observed latency %v to match query latency %v on host %v", observedLatency, hostLatency, ip) } } // the query will only be attempted once, but is being retried attempts := qry.Attempts() if attempts != rt.NumRetries { t.Fatalf("failed to retry the query %v time(s). Query executed %v times", rt.NumRetries, attempts) } } type testRetryPolicy struct { NumRetries int } func (t *testRetryPolicy) Attempt(qry RetryableQuery) bool { return qry.Attempts() <= t.NumRetries } func (t *testRetryPolicy) GetRetryType(err error) RetryType { var executedErr *QueryError if errors.As(err, &executedErr) && executedErr.PotentiallyExecuted() && !executedErr.IsIdempotent() { return Rethrow } return Retry } func TestSpeculativeExecution(t *testing.T) { log := &testLogger{} defer func() { os.Stdout.WriteString(log.String()) }() // Build a 3 node cluster var nodes []*TestServer var addresses = []string{ "127.0.0.1", "127.0.0.2", "127.0.0.3", } // Can do with 1 context for all servers ctx := context.Background() for _, ip := range addresses { srv := NewTestServerWithAddress(ip+":0", t, defaultProto, ctx) defer srv.Stop() nodes = append(nodes, srv) } db, err := newTestSession(defaultProto, nodes[0].Address, nodes[1].Address, nodes[2].Address) if err != nil { t.Fatalf("NewCluster: %v", err) } defer db.Close() // Create a test retry policy, 6 retries will cover 2 executions rt := &testRetryPolicy{NumRetries: 8} // test Speculative policy with 1 additional execution sp := &SimpleSpeculativeExecution{NumAttempts: 1, TimeoutDelay: 200 * time.Millisecond} // Build the query qry := db.Query("speculative").RetryPolicy(rt).SetSpeculativeExecutionPolicy(sp).Idempotent(true) // Execute the query and close, check that it doesn't error out if err := qry.Exec(); err != nil { t.Errorf("The query failed with '%v'!\n", err) } requests1 := atomic.LoadInt64(&nodes[0].nKillReq) requests2 := atomic.LoadInt64(&nodes[1].nKillReq) requests3 := atomic.LoadInt64(&nodes[2].nKillReq) // Spec Attempts == 1, so expecting to see only 1 regular + 1 speculative = 2 nodes attempted if requests1 != 0 && requests2 != 0 && requests3 != 0 { t.Error("error: all 3 nodes were attempted, should have been only 2") } // Only the 4th request will generate results, so if requests1 != 4 && requests2 != 4 && requests3 != 4 { t.Error("error: none of 3 nodes was attempted 4 times!") } // "speculative" query will succeed on one arbitrary node after 4 attempts, so // expecting to see 4 (on successful node) + not more than 2 (as cancelled on another node) == 6 if requests1+requests2+requests3 > 6 { t.Errorf("error: expected to see 6 attempts, got %v\n", requests1+requests2+requests3) } } // This tests that the policy connection pool handles SSL correctly func TestPolicyConnPoolSSL(t *testing.T) { srv := NewSSLTestServer(t, defaultProto, context.Background()) defer srv.Stop() cluster := createTestSslCluster(srv.Address, defaultProto, true) cluster.PoolConfig.HostSelectionPolicy = RoundRobinHostPolicy() db, err := cluster.CreateSession() if err != nil { t.Fatalf("failed to create new session: %v", err) } if err := db.Query("void").Exec(); err != nil { t.Fatalf("query failed due to error: %v", err) } db.Close() // wait for the pool to drain time.Sleep(100 * time.Millisecond) size := db.pool.Size() if size != 0 { t.Fatalf("connection pool did not drain, still contains %d connections", size) } } func TestQueryTimeout(t *testing.T) { srv := NewTestServer(t, defaultProto, context.Background()) defer srv.Stop() cluster := testCluster(defaultProto, srv.Address) // Set the timeout arbitrarily low so that the query hits the timeout in a // timely manner. cluster.Timeout = 1 * time.Millisecond db, err := cluster.CreateSession() if err != nil { t.Fatalf("NewCluster: %v", err) } defer db.Close() ch := make(chan error, 1) go func() { err := db.Query("timeout").Exec() if err != nil { ch <- err return } t.Errorf("err was nil, expected to get a timeout after %v", db.cfg.Timeout) }() select { case err := <-ch: if !errors.Is(err, ErrTimeoutNoResponse) { t.Fatalf("expected to get %v for timeout got %v", ErrTimeoutNoResponse, err) } case <-time.After(40*time.Millisecond + db.cfg.Timeout): // ensure that the query goroutines have been scheduled t.Fatalf("query did not timeout after %v", db.cfg.Timeout) } } func BenchmarkSingleConn(b *testing.B) { srv := NewTestServer(b, 3, context.Background()) defer srv.Stop() cluster := testCluster(protoVersion3, srv.Address) // Set the timeout arbitrarily low so that the query hits the timeout in a // timely manner. cluster.Timeout = 500 * time.Millisecond cluster.NumConns = 1 db, err := cluster.CreateSession() if err != nil { b.Fatalf("NewCluster: %v", err) } defer db.Close() b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { err := db.Query("void").Exec() if err != nil { b.Error(err) return } } }) } func TestQueryTimeoutReuseStream(t *testing.T) { t.Skip("no longer tests anything") // TODO(zariel): move this to conn test, we really just want to check what // happens when a conn is srv := NewTestServer(t, defaultProto, context.Background()) defer srv.Stop() cluster := testCluster(defaultProto, srv.Address) // Set the timeout arbitrarily low so that the query hits the timeout in a // timely manner. cluster.Timeout = 1 * time.Millisecond cluster.NumConns = 1 db, err := cluster.CreateSession() if err != nil { t.Fatalf("NewCluster: %v", err) } defer db.Close() db.Query("slow").Exec() err = db.Query("void").Exec() if err != nil { t.Fatal(err) } } func TestQueryTimeoutClose(t *testing.T) { srv := NewTestServer(t, defaultProto, context.Background()) defer srv.Stop() cluster := testCluster(defaultProto, srv.Address) // Set the timeout arbitrarily low so that the query hits the timeout in a // timely manner. cluster.Timeout = 1000 * time.Millisecond cluster.NumConns = 1 db, err := cluster.CreateSession() if err != nil { t.Fatalf("NewCluster: %v", err) } ch := make(chan error) go func() { err := db.Query("timeout").Exec() ch <- err }() // ensure that the above goroutine gets sheduled time.Sleep(50 * time.Millisecond) db.Close() select { case err = <-ch: case <-time.After(1 * time.Second): t.Fatal("timedout waiting to get a response once cluster is closed") } if !errors.Is(err, ErrConnectionClosed) { t.Fatalf("expected to get %v or an error wrapping it, got %v", ErrConnectionClosed, err) } } func TestStream0(t *testing.T) { // TODO: replace this with type check const expErr = "gocql: received unexpected frame on stream 0" var buf bytes.Buffer f := newFramer(nil, protoVersion4) f.writeHeader(0, frm.OpResult, 0) f.writeInt(frm.ResultKindVoid) f.buf[0] |= 0x80 if err := f.finish(); err != nil { t.Fatal(err) } if err := f.writeTo(&buf); err != nil { t.Fatal(err) } conn := &Conn{ r: bufio.NewReader(&buf), streams: streams.New(), logger: &defaultLogger{}, cfg: &ConnConfig{}, } err := conn.recv(context.Background()) if err == nil { t.Fatal("expected to get an error on stream 0") } else if !strings.HasPrefix(err.Error(), expErr) { t.Fatalf("expected to get error prefix %q got %q", expErr, err.Error()) } } func TestContext_Timeout(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() srv := NewTestServer(t, defaultProto, ctx) defer srv.Stop() cluster := testCluster(defaultProto, srv.Address) cluster.Timeout = 5 * time.Second db, err := cluster.CreateSession() if err != nil { t.Fatal(err) } defer db.Close() ctx, cancel = context.WithCancel(ctx) cancel() err = db.Query("timeout").WithContext(ctx).Exec() if !errors.Is(err, context.Canceled) { t.Fatalf("expected to get context cancel error: %v got %v", context.Canceled, err) } } type TestReconnectionPolicy struct { NumRetries int GetIntervalCalls []int } func (c *TestReconnectionPolicy) GetInterval(currentRetry int) time.Duration { c.GetIntervalCalls = append(c.GetIntervalCalls, currentRetry) return time.Duration(0) } func (c *TestReconnectionPolicy) GetMaxRetries() int { return c.NumRetries } func TestInitialRetryPolicy(t *testing.T) { t.Parallel() tcase := []struct { NumRetries int ProtoVersion int ExpectedGetIntervalCalls []int ExpectedErr string }{ { NumRetries: 1, ProtoVersion: 0, ExpectedGetIntervalCalls: nil, ExpectedErr: "gocql: unable to create session: unable to connect to the cluster, last error: unable to discover protocol version:"}, { NumRetries: 2, ProtoVersion: 0, ExpectedGetIntervalCalls: []int{1}, ExpectedErr: "gocql: unable to create session: unable to connect to the cluster, last error: unable to discover protocol version:"}, { NumRetries: 3, ProtoVersion: 0, ExpectedGetIntervalCalls: []int{1, 2}, ExpectedErr: "gocql: unable to create session: unable to connect to the cluster, last error: unable to discover protocol version:"}, { NumRetries: 1, ProtoVersion: protoVersion4, ExpectedGetIntervalCalls: nil, ExpectedErr: "gocql: unable to create session: unable to connect to the cluster, last error: unable to create control connection: unable to connect to initial hosts:"}, { NumRetries: 2, ProtoVersion: protoVersion4, ExpectedGetIntervalCalls: []int{1}, ExpectedErr: "gocql: unable to create session: unable to connect to the cluster, last error: unable to create control connection: unable to connect to initial hosts:"}, { NumRetries: 3, ProtoVersion: protoVersion4, ExpectedGetIntervalCalls: []int{1, 2}, ExpectedErr: "gocql: unable to create session: unable to connect to the cluster, last error: unable to create control connection: unable to connect to initial hosts:"}, } for id := range tcase { tc := tcase[id] t.Run(fmt.Sprintf("NumRetries=%d_ProtocolVersion=%d", tc.NumRetries, tc.ProtoVersion), func(t *testing.T) { t.Parallel() // Use a loopback address with a well-known closed port so the test // remains deterministic even when a local Cassandra-compatible // service is listening on 9042. cluster := NewCluster("127.0.0.1:1") policy := &TestReconnectionPolicy{NumRetries: tc.NumRetries} cluster.InitialReconnectionPolicy = policy cluster.ProtoVersion = tc.ProtoVersion _, err := cluster.CreateSession() if err == nil { t.Fatal("expected to get an error") } if !strings.Contains(err.Error(), tc.ExpectedErr) { t.Errorf("expected error to contain %q got %q", tc.ExpectedErr, err.Error()) } if !cmp.Equal(tc.ExpectedGetIntervalCalls, policy.GetIntervalCalls) { t.Errorf("expected GetInterval calls to be (%+v) but was (%+v) instead", tc.ExpectedGetIntervalCalls, policy.GetIntervalCalls) } }) } } func TestContext_CanceledBeforeExec(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() var reqCount uint64 srv := newTestServerOpts{ addr: "127.0.0.1:0", protocol: defaultProto, recvHook: func(f *framer) { if f.header.Op == frm.OpStartup || f.header.Op == frm.OpOptions { // ignore statup and heartbeat messages return } atomic.AddUint64(&reqCount, 1) }, }.newServer(t, ctx) defer srv.Stop() cluster := testCluster(defaultProto, srv.Address) cluster.Timeout = 5 * time.Second db, err := cluster.CreateSession() if err != nil { t.Fatal(err) } defer db.Close() startupRequestCount := atomic.LoadUint64(&reqCount) ctx, cancel = context.WithCancel(ctx) cancel() err = db.Query("timeout").WithContext(ctx).Exec() if !errors.Is(err, context.Canceled) { t.Fatalf("expected to get context cancel error: %v got %v", context.Canceled, err) } // Queries are executed by separate goroutine and we don't have a synchronization point that would allow us to // check if a request was sent or not. // Fall back to waiting a little bit. time.Sleep(100 * time.Millisecond) queryRequestCount := atomic.LoadUint64(&reqCount) - startupRequestCount if queryRequestCount != 0 { t.Fatalf("expected that no request is sent to server, sent %d requests", queryRequestCount) } } func TestCallReqReuseDoesNotInvalidateOutstandingTimeout(t *testing.T) { t.Parallel() oldCall := getCallReq(1) oldTimeout := oldCall.timeout oldCall.done.Done() putCallReq(oldCall) newCall := getCallReq(2) defer newCall.done.Done() defer close(newCall.timeout) defer func() { if r := recover(); r != nil { t.Fatalf("closing old timeout should not panic after putCallReq: %v", r) } }() close(oldTimeout) select { case <-newCall.timeout: t.Fatal("closing the old timeout unexpectedly closed the new call timeout") default: } } type testContextWriter struct { n int err error onWrite func() } func (w testContextWriter) writeContext(ctx context.Context, p []byte) (int, error) { if w.onWrite != nil { w.onWrite() } if w.n == 0 && w.err == nil { return len(p), nil } return w.n, w.err } func (w testContextWriter) setWriteTimeout(timeout time.Duration) {} type contextWriterFunc func(context.Context, []byte) (int, error) func (fn contextWriterFunc) writeContext(ctx context.Context, p []byte) (int, error) { return fn(ctx, p) } func (fn contextWriterFunc) setWriteTimeout(timeout time.Duration) {} func newTestExecConn(t *testing.T, w contextWriter) (*Conn, net.Conn) { t.Helper() ctx, cancel := context.WithCancel(context.Background()) server, client := net.Pipe() c := newTestConnWithFramerPool() c.ctx = ctx c.cancel = cancel c.conn = client c.w = w c.logger = nopLogger{} c.errorHandler = connErrorHandlerFn(func(*Conn, error, bool) {}) c.streams = streams.New() c.calls = make(map[int]*callReq) return c, server } func waitForSingleCall(t *testing.T, c *Conn) *callReq { t.Helper() deadline := time.After(2 * time.Second) ticker := time.NewTicker(time.Millisecond) defer ticker.Stop() for { c.mu.Lock() for _, call := range c.calls { c.mu.Unlock() return call } c.mu.Unlock() select { case <-deadline: t.Fatal("timed out waiting for in-flight call") case <-ticker.C: } } } func detachSingleCall(t *testing.T, c *Conn) *callReq { t.Helper() c.mu.Lock() defer c.mu.Unlock() for streamID, call := range c.calls { delete(c.calls, streamID) return call } t.Fatal("expected an in-flight call") return nil } type testStreamObserver struct { ctx *testStreamObserverContext } func (o *testStreamObserver) StreamContext(context.Context) StreamObserverContext { return o.ctx } type testStreamObserverContext struct { started chan struct{} abandoned chan struct{} finished chan struct{} } func newTestStreamObserverContext() *testStreamObserverContext { return &testStreamObserverContext{ started: make(chan struct{}, 1), abandoned: make(chan struct{}, 1), finished: make(chan struct{}, 1), } } func (o *testStreamObserverContext) StreamStarted(ObservedStream) { select { case o.started <- struct{}{}: default: } } func (o *testStreamObserverContext) StreamAbandoned(ObservedStream) { select { case o.abandoned <- struct{}{}: default: } } func (o *testStreamObserverContext) StreamFinished(ObservedStream) { select { case o.finished <- struct{}{}: default: } } func TestExecCloseWithError(t *testing.T) { t.Parallel() t.Run("BuildFrameErrorReleasesResources", func(t *testing.T) { c, server := newTestExecConn(t, testContextWriter{}) defer server.Close() _, err := c.exec(context.Background(), frameWriterFunc(func(f *framer, streamID int) error { return io.ErrUnexpectedEOF }), nil, 0) if !errors.Is(err, io.ErrUnexpectedEOF) { t.Fatalf("expected build error %v, got %v", io.ErrUnexpectedEOF, err) } c.mu.Lock() defer c.mu.Unlock() if len(c.calls) != 0 { t.Fatalf("expected no in-flight calls after build error, got %d", len(c.calls)) } }) t.Run("ContextCanceledBeforeWriteReleasesResources", func(t *testing.T) { writeEntered := make(chan struct{}) c, server := newTestExecConn(t, contextWriterFunc(func(ctx context.Context, p []byte) (int, error) { close(writeEntered) <-ctx.Done() return 0, ctx.Err() })) defer server.Close() ctx, cancel := context.WithCancel(context.Background()) errCh := make(chan error, 1) go func() { _, err := c.exec(ctx, frameWriterFunc(func(f *framer, streamID int) error { f.buf = append(f.buf[:0], 'x') return nil }), nil, 0) errCh <- err }() select { case <-writeEntered: case <-time.After(2 * time.Second): t.Fatal("exec never reached the write path") } cancel() select { case err := <-errCh: if !errors.Is(err, context.Canceled) { t.Fatalf("expected context cancel error %v, got %v", context.Canceled, err) } case <-time.After(2 * time.Second): t.Fatal("exec deadlocked after context cancellation before write") } c.mu.Lock() defer c.mu.Unlock() if len(c.calls) != 0 { t.Fatalf("expected no in-flight calls after canceled write, got %d", len(c.calls)) } }) t.Run("ResponseErrorReleasesResources", func(t *testing.T) { c, server := newTestExecConn(t, testContextWriter{}) defer server.Close() errCh := make(chan error, 1) go func() { _, err := c.exec(context.Background(), frameWriterFunc(func(f *framer, streamID int) error { f.buf = append(f.buf[:0], 'x') return nil }), nil, 0) errCh <- err }() waitForSingleCall(t, c) call := detachSingleCall(t, c) call.resp <- callResp{err: io.EOF} select { case err := <-errCh: if !errors.Is(err, io.EOF) { t.Fatalf("expected response error %v, got %v", io.EOF, err) } case <-time.After(2 * time.Second): t.Fatal("exec deadlocked after response error") } c.mu.Lock() defer c.mu.Unlock() if len(c.calls) != 0 { t.Fatalf("expected no in-flight calls after response error, got %d", len(c.calls)) } }) t.Run("PartialWriteDoesNotDeadlock", func(t *testing.T) { c, server := newTestExecConn(t, testContextWriter{ n: 1, err: io.ErrUnexpectedEOF, }) defer server.Close() errCh := make(chan error, 1) go func() { _, err := c.exec(context.Background(), frameWriterFunc(func(f *framer, streamID int) error { f.buf = append(f.buf[:0], 'x') return nil }), nil, 0) errCh <- err }() select { case err := <-errCh: if !errors.Is(err, io.ErrUnexpectedEOF) { t.Fatalf("expected write error %v, got %v", io.ErrUnexpectedEOF, err) } case <-time.After(2 * time.Second): t.Fatal("exec deadlocked after partial write failure") } }) t.Run("ConnectionCloseErrorDoesNotDeadlock", func(t *testing.T) { writeStarted := make(chan struct{}) var writeStartedOnce sync.Once c, server := newTestExecConn(t, testContextWriter{ onWrite: func() { writeStartedOnce.Do(func() { close(writeStarted) }) }, }) defer server.Close() closeDone := make(chan struct{}) go func() { <-writeStarted c.closeWithError(io.EOF) close(closeDone) }() errCh := make(chan error, 1) go func() { _, err := c.exec(context.Background(), frameWriterFunc(func(f *framer, streamID int) error { f.buf = append(f.buf[:0], 'x') return nil }), nil, 0) errCh <- err }() select { case err := <-errCh: if !errors.Is(err, io.EOF) { t.Fatalf("expected close error %v, got %v", io.EOF, err) } case <-time.After(2 * time.Second): t.Fatal("exec deadlocked after closeWithError") } select { case <-closeDone: case <-time.After(2 * time.Second): t.Fatal("closeWithError deadlocked waiting for exec to release the call") } }) t.Run("TimeoutUnblocksAbandonRecvCall", func(t *testing.T) { c, server := newTestExecConn(t, testContextWriter{}) defer server.Close() errCh := make(chan error, 1) go func() { _, err := c.exec(context.Background(), frameWriterFunc(func(f *framer, streamID int) error { f.buf = append(f.buf[:0], 'x') return nil }), nil, time.Millisecond) errCh <- err }() call := waitForSingleCall(t, c) select { case err := <-errCh: if !errors.Is(err, ErrTimeoutNoResponse) { t.Fatalf("expected timeout error %v, got %v", ErrTimeoutNoResponse, err) } case <-time.After(2 * time.Second): t.Fatal("exec deadlocked waiting for timeout") } if !c.removeCallIfOpen(call.streamID) { t.Fatal("expected timed out call to still be registered") } done := make(chan struct{}) go func() { c.abandonRecvCall(call, c.getReadFramer()) close(done) }() select { case <-done: case <-time.After(2 * time.Second): t.Fatal("abandonRecvCall deadlocked after timeout") } }) t.Run("ContextCancelUnblocksAbandonRecvCall", func(t *testing.T) { c, server := newTestExecConn(t, testContextWriter{}) defer server.Close() ctx, cancel := context.WithCancel(context.Background()) errCh := make(chan error, 1) go func() { _, err := c.exec(ctx, frameWriterFunc(func(f *framer, streamID int) error { f.buf = append(f.buf[:0], 'x') return nil }), nil, 0) errCh <- err }() call := waitForSingleCall(t, c) cancel() select { case err := <-errCh: if !errors.Is(err, context.Canceled) { t.Fatalf("expected context cancel error %v, got %v", context.Canceled, err) } case <-time.After(2 * time.Second): t.Fatal("exec deadlocked waiting for context cancellation") } if !c.removeCallIfOpen(call.streamID) { t.Fatal("expected canceled call to still be registered") } done := make(chan struct{}) go func() { c.abandonRecvCall(call, c.getReadFramer()) close(done) }() select { case <-done: case <-time.After(2 * time.Second): t.Fatal("abandonRecvCall deadlocked after context cancellation") } }) t.Run("ConnectionCloseAbandonsInflightStream", func(t *testing.T) { writeStarted := make(chan struct{}) var writeStartedOnce sync.Once observerCtx := newTestStreamObserverContext() c, server := newTestExecConn(t, testContextWriter{ onWrite: func() { writeStartedOnce.Do(func() { close(writeStarted) }) }, }) c.streamObserver = &testStreamObserver{ctx: observerCtx} defer server.Close() errCh := make(chan error, 1) go func() { _, err := c.exec(context.Background(), frameWriterFunc(func(f *framer, streamID int) error { f.buf = append(f.buf[:0], 'x') return nil }), nil, 0) errCh <- err }() select { case <-observerCtx.started: case <-time.After(2 * time.Second): t.Fatal("stream observer did not observe the request start") } select { case <-writeStarted: case <-time.After(2 * time.Second): t.Fatal("exec never reached the write path") } closeDone := make(chan struct{}) go func() { c.Close() close(closeDone) }() select { case err := <-errCh: if !errors.Is(err, ErrConnectionClosed) { t.Fatalf("expected close error %v, got %v", ErrConnectionClosed, err) } case <-time.After(2 * time.Second): t.Fatal("exec deadlocked after Close") } select { case <-observerCtx.abandoned: case <-time.After(2 * time.Second): t.Fatal("Close did not abandon the in-flight stream") } select { case <-observerCtx.finished: t.Fatal("Close should not mark the in-flight stream as finished") default: } select { case <-closeDone: case <-time.After(2 * time.Second): t.Fatal("Close did not wait for the in-flight exec cleanup") } c.mu.Lock() defer c.mu.Unlock() if c.calls != nil { t.Fatal("expected in-flight calls to be detached on Close") } }) } // tcpConnPair returns a matching set of a TCP client side and server side connection. func tcpConnPair() (s, c net.Conn, err error) { l, err := net.Listen("tcp", "localhost:0") if err != nil { // maybe ipv6 works, if ipv4 fails? l, err = net.Listen("tcp6", "[::1]:0") if err != nil { return nil, nil, err } } defer l.Close() // we only try to accept one connection, so will stop listening. addr := l.Addr() done := make(chan struct{}) var errDial error go func(done chan<- struct{}) { c, errDial = net.Dial(addr.Network(), addr.String()) close(done) }(done) s, err = l.Accept() <-done if err == nil { err = errDial } if err != nil { if s != nil { s.Close() } if c != nil { c.Close() } } return s, c, err } func TestWriteCoalescing(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() server, client, err := tcpConnPair() if err != nil { t.Fatal(err) } done := make(chan struct{}, 1) var ( buf bytes.Buffer bufMutex sync.Mutex ) go func() { defer close(done) defer server.Close() var err error b := make([]byte, 256) var n int for { if n, err = server.Read(b); err != nil { break } bufMutex.Lock() buf.Write(b[:n]) bufMutex.Unlock() } if err != io.EOF { t.Errorf("unexpected read error: %v", err) } }() enqueued := make(chan struct{}) resetTimer := make(chan struct{}) w := &writeCoalescer{ writeCh: make(chan writeRequest), c: client, quit: ctx.Done(), testEnqueuedHook: func() { enqueued <- struct{}{} }, testFlushedHook: func() { client.Close() }, } w.setWriteTimeout(500 * time.Millisecond) timerC := make(chan time.Time, 1) go func() { w.writeFlusherImpl(timerC, func() { resetTimer <- struct{}{} }) }() go func() { if _, err := w.writeContext(context.Background(), []byte("one")); err != nil { t.Error(err) } }() go func() { if _, err := w.writeContext(context.Background(), []byte("two")); err != nil { t.Error(err) } }() <-enqueued <-resetTimer <-enqueued // flush timerC <- time.Now() <-done if got := buf.String(); got != "onetwo" && got != "twoone" { t.Fatalf("expected to get %q got %q", "onetwo or twoone", got) } } func TestWriteCoalescing_WriteAfterClose(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() var buf bytes.Buffer defer cancel() server, client, err := tcpConnPair() if err != nil { t.Fatal(err) } done := make(chan struct{}, 1) go func() { io.Copy(&buf, server) server.Close() close(done) }() w := newWriteCoalescer(client, 0, 5*time.Millisecond, ctx.Done()) // ensure 1 write works if _, err := w.writeContext(context.Background(), []byte("one")); err != nil { t.Fatal(err) } client.Close() <-done if v := buf.String(); v != "one" { t.Fatalf("expected buffer to be %q got %q", "one", v) } // now close and do a write, we should error cancel() client.Close() // close client conn too, since server won't see the answer anyway. if _, err := w.writeContext(context.Background(), []byte("two")); err == nil { t.Fatal("expected to get error for write after closing") } else if err != io.EOF { t.Fatalf("expected to get EOF got %v", err) } } func TestSkipMetadata(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() srv := NewTestServer(t, protoVersion4, ctx) defer srv.Stop() cfg := testCluster(protoVersion4, srv.Address) cfg.DisableSkipMetadata = false db, err := cfg.CreateSession() if err != nil { t.Fatalf("NewCluster: %v", err) } defer db.Close() if err := db.Query("select nometadata").Exec(); err != nil { t.Fatalf("expected no error got: %v", err) } if err := db.Query("select metadata").Exec(); err != nil { t.Fatalf("expected no error got: %v", err) } } func TestPrepareBatchMetadataMultipleKeyspaceTables(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() srv := NewTestServer(t, protoVersion4, ctx) defer srv.Stop() cfg := testCluster(protoVersion4, srv.Address) db, err := cfg.CreateSession() if err != nil { t.Fatalf("CreateSession: %v", err) } defer db.Close() conn := db.getConn() if conn == nil { t.Fatal("expected connection, got nil") } stmt := "BEGIN BATCH INSERT INTO ks1.tbl1 (col1) VALUES (?) INSERT INTO ks2.tbl2 (col2) VALUES (?) APPLY BATCH" info, err := conn.prepareStatement(ctx, stmt, nil, time.Second) if err != nil { t.Fatalf("prepareStatement failed: %v", err) } if got := len(info.request.columns); got != 2 { t.Fatalf("expected 2 request columns, got %d", got) } col0 := info.request.columns[0] if col0.Keyspace != "ks1" || col0.Table != "tbl1" || col0.Name != "col1" { t.Fatalf("unexpected column 0: %+v", col0) } col1 := info.request.columns[1] if col1.Keyspace != "ks2" || col1.Table != "tbl2" || col1.Name != "col2" { t.Fatalf("unexpected column 1: %+v", col1) } if info.request.keyspace != "" || info.request.table != "" { t.Fatalf("expected empty prepared keyspace/table for mixed batch, got %q/%q", info.request.keyspace, info.request.table) } } type recordingFrameHeaderObserver struct { t *testing.T mu sync.Mutex frames []ObservedFrameHeader } func (r *recordingFrameHeaderObserver) ObserveFrameHeader(ctx context.Context, frm ObservedFrameHeader) { r.mu.Lock() r.frames = append(r.frames, frm) r.mu.Unlock() } func (r *recordingFrameHeaderObserver) getFrames() []ObservedFrameHeader { r.mu.Lock() defer r.mu.Unlock() return r.frames } func TestFrameHeaderObserver(t *testing.T) { srv := NewTestServer(t, defaultProto, context.Background()) defer srv.Stop() cluster := testCluster(defaultProto, srv.Address) cluster.NumConns = 1 observer := &recordingFrameHeaderObserver{t: t} cluster.FrameHeaderObserver = observer db, err := cluster.CreateSession() if err != nil { t.Fatal(err) } if err := db.Query("void").Exec(); err != nil { t.Fatal(err) } frames := observer.getFrames() expFrames := []frm.Op{frm.OpSupported, frm.OpReady, frm.OpResult} if len(frames) != len(expFrames) { t.Fatalf("Expected to receive %d frames, instead received %d", len(expFrames), len(frames)) } for i, op := range expFrames { if op != frames[i].Opcode { t.Fatalf("expected frame %d to be %v got %v", i, op, frames[i]) } } voidResultFrame := frames[2] if voidResultFrame.Length != int32(4) { t.Fatalf("Expected to receive frame with body length 4, instead received body length %d", voidResultFrame.Length) } } func NewTestServerWithAddress(addr string, t testing.TB, protocol uint8, ctx context.Context) *TestServer { return newTestServerOpts{ addr: addr, protocol: protocol, }.newServer(t, ctx) } func NewTestServerWithAddressAndSupportedFactory(addr string, t testing.TB, protocol uint8, ctx context.Context, supportedFactory testSupportedFactory) *TestServer { return newTestServerOpts{ addr: addr, protocol: protocol, supportedFactory: supportedFactory, }.newServer(t, ctx) } type newTestServerOpts struct { addr string protocol uint8 supportedFactory testSupportedFactory recvHook func(*framer) } func (nts newTestServerOpts) newServer(t testing.TB, ctx context.Context) *TestServer { laddr, err := net.ResolveTCPAddr("tcp", nts.addr) if err != nil { t.Fatal(err) } listen, err := net.ListenTCP("tcp", laddr) if err != nil { t.Fatal(err) } headerSize := 9 ctx, cancel := context.WithCancel(ctx) srv := &TestServer{ Address: listen.Addr().String(), listen: listen, t: t, protocol: nts.protocol, headerSize: headerSize, ctx: ctx, cancel: cancel, supportedFactory: nts.supportedFactory, onRecv: nts.recvHook, } go srv.closeWatch() go srv.serve() return srv } func NewTestServer(t testing.TB, protocol uint8, ctx context.Context) *TestServer { return NewTestServerWithAddress("127.0.0.1:0", t, protocol, ctx) } func NewSSLTestServer(t testing.TB, protocol uint8, ctx context.Context) *TestServer { return NewSSLTestServerWithSupportedFactory(t, protocol, ctx, nil) } func NewSSLTestServerWithSupportedFactory(t testing.TB, protocol uint8, ctx context.Context, supportedFactory testSupportedFactory) *TestServer { pem, err := os.ReadFile("testdata/pki/ca.crt") certPool := x509.NewCertPool() if !certPool.AppendCertsFromPEM(pem) { t.Fatalf("Failed parsing or appending certs") } mycert, err := tls.LoadX509KeyPair("testdata/pki/cassandra.crt", "testdata/pki/cassandra.key") if err != nil { t.Fatalf("could not load cert") } config := &tls.Config{ Certificates: []tls.Certificate{mycert}, RootCAs: certPool, } listen, err := tls.Listen("tcp", "127.0.0.1:0", config) if err != nil { t.Fatal(err) } headerSize := 9 ctx, cancel := context.WithCancel(ctx) srv := &TestServer{ Address: listen.Addr().String(), listen: listen, t: t, protocol: protocol, headerSize: headerSize, ctx: ctx, cancel: cancel, supportedFactory: supportedFactory, } go srv.closeWatch() go srv.serve() return srv } type TestServer struct { Address string TimeoutOnStartup int32 t testing.TB listen net.Listener nKillReq int64 supportedFactory testSupportedFactory protocol byte headerSize int ctx context.Context cancel context.CancelFunc mu sync.Mutex closed bool // onRecv is a hook point for tests, called in receive loop. onRecv func(*framer) } type testSupportedFactory func(conn net.Conn) map[string][]string func (srv *TestServer) session() (*Session, error) { return testCluster(frm.ProtoVersion(srv.protocol), srv.Address).CreateSession() } func (srv *TestServer) host() *HostInfo { hosts, err := resolveInitialEndpoint(nil, srv.Address, 9042) if err != nil { srv.t.Fatal(err) } return hosts[0] } func (srv *TestServer) closeWatch() { <-srv.ctx.Done() srv.mu.Lock() defer srv.mu.Unlock() srv.closeLocked() } func (srv *TestServer) serve() { defer srv.listen.Close() for !srv.isClosed() { conn, err := srv.listen.Accept() if err != nil { break } var exts map[string][]string if srv.supportedFactory != nil { exts = (srv.supportedFactory)(conn) } go func(conn net.Conn, exts map[string][]string) { defer conn.Close() for !srv.isClosed() { framer, err := srv.readFrame(conn) if err != nil { if err == io.EOF || errors.Is(err, net.ErrClosed) { return } srv.errorLocked(err) return } if srv.onRecv != nil { srv.onRecv(framer) } go srv.process(conn, framer, exts) } }(conn, exts) } } func (srv *TestServer) isClosed() bool { srv.mu.Lock() defer srv.mu.Unlock() return srv.closed } func (srv *TestServer) closeLocked() { if srv.closed { return } srv.closed = true srv.listen.Close() srv.cancel() } func (srv *TestServer) Stop() { srv.mu.Lock() defer srv.mu.Unlock() srv.closeLocked() } func (srv *TestServer) errorLocked(err any) { srv.mu.Lock() defer srv.mu.Unlock() if srv.closed { return } srv.t.Error(err) } func (srv *TestServer) process(conn net.Conn, reqFrame *framer, exts map[string][]string) { head := reqFrame.header if head == nil { srv.errorLocked("process frame with a nil header") return } respFrame := newFramer(nil, reqFrame.proto) switch head.Op { case frm.OpStartup: if atomic.LoadInt32(&srv.TimeoutOnStartup) > 0 { // Do not respond to startup command // wait until we get a cancel signal select { case <-srv.ctx.Done(): return } } respFrame.writeHeader(0, frm.OpReady, head.Stream) case frm.OpOptions: respFrame.writeHeader(0, frm.OpSupported, head.Stream) respFrame.writeStringMultiMap(exts) case frm.OpQuery: query := reqFrame.readLongString() first := query if n := strings.Index(query, " "); n > 0 { first = first[:n] } switch strings.ToLower(first) { case "kill": atomic.AddInt64(&srv.nKillReq, 1) respFrame.writeHeader(0, frm.OpError, head.Stream) respFrame.writeInt(0x1001) respFrame.writeString("query killed") case "use": respFrame.writeInt(frm.ResultKindKeyspace) respFrame.writeString(strings.TrimSpace(query[3:])) case "void": respFrame.writeHeader(0, frm.OpResult, head.Stream) respFrame.writeInt(frm.ResultKindVoid) case "timeout": <-srv.ctx.Done() return case "slow": go func() { respFrame.writeHeader(0, frm.OpResult, head.Stream) respFrame.writeInt(frm.ResultKindVoid) respFrame.buf[0] = srv.protocol | 0x80 select { case <-srv.ctx.Done(): return case <-time.After(50 * time.Millisecond): respFrame.finish() respFrame.writeTo(conn) } }() return case "speculative": atomic.AddInt64(&srv.nKillReq, 1) if atomic.LoadInt64(&srv.nKillReq) > 3 { respFrame.writeHeader(0, frm.OpResult, head.Stream) respFrame.writeInt(frm.ResultKindVoid) respFrame.writeString("speculative query success on the node " + srv.Address) } else { respFrame.writeHeader(0, frm.OpError, head.Stream) respFrame.writeInt(0x1001) respFrame.writeString("speculative error") rand.Seed(time.Now().UnixNano()) <-time.After(time.Millisecond * 120) } default: respFrame.writeHeader(0, frm.OpResult, head.Stream) respFrame.writeInt(frm.ResultKindVoid) } case frm.OpError: respFrame.writeHeader(0, frm.OpError, head.Stream) respFrame.buf = append(respFrame.buf, reqFrame.buf...) case frm.OpPrepare: query := strings.TrimSpace(reqFrame.readLongString()) lower := strings.ToLower(query) name := "" if strings.HasPrefix(lower, "select ") { name = strings.TrimPrefix(lower, "select ") if n := strings.Index(name, " "); n > 0 { name = name[:n] } } else if strings.HasPrefix(lower, "begin batch") { name = "batchmetadata" } else { name = lower } switch name { case "nometadata": respFrame.writeHeader(0, frm.OpResult, head.Stream) respFrame.writeInt(frm.ResultKindPrepared) // respFrame.writeShortBytes(binary.BigEndian.AppendUint64(nil, 1)) // respFrame.writeInt(0) // respFrame.writeInt(0) // if srv.protocol >= protoVersion4 { respFrame.writeInt(0) // } // respFrame.writeInt(int32(frm.FlagNoMetaData)) // respFrame.writeInt(0) case "metadata": respFrame.writeHeader(0, frm.OpResult, head.Stream) respFrame.writeInt(frm.ResultKindPrepared) // respFrame.writeShortBytes(binary.BigEndian.AppendUint64(nil, 2)) // respFrame.writeInt(0) // respFrame.writeInt(0) // if srv.protocol >= protoVersion4 { respFrame.writeInt(0) // } // respFrame.writeInt(int32(frm.FlagGlobalTableSpec)) // respFrame.writeInt(1) // // respFrame.writeString("keyspace") respFrame.writeString("table") // respFrame.writeString("col0") // respFrame.writeShort(uint16(TypeBoolean)) // case "batchmetadata": respFrame.writeHeader(0, frm.OpResult, head.Stream) respFrame.writeInt(frm.ResultKindPrepared) // respFrame.writeShortBytes(binary.BigEndian.AppendUint64(nil, 3)) // respFrame.writeInt(0) // respFrame.writeInt(2) // if srv.protocol >= protoVersion4 { respFrame.writeInt(0) // } // respFrame.writeString("ks1") respFrame.writeString("tbl1") respFrame.writeString("col1") respFrame.writeShort(uint16(TypeInt)) // respFrame.writeString("ks2") respFrame.writeString("tbl2") respFrame.writeString("col2") respFrame.writeShort(uint16(TypeInt)) // respFrame.writeInt(int32(frm.FlagNoMetaData)) respFrame.writeInt(0) default: respFrame.writeHeader(0, frm.OpError, head.Stream) respFrame.writeInt(0) respFrame.writeString("unsupported query: " + name) } case frm.OpExecute: b := reqFrame.readShortBytesCopy() id := binary.BigEndian.Uint64(b) // reqFrame.readConsistency() // var flags byte if srv.protocol > protoVersion4 { ui := reqFrame.readInt() flags = byte(ui) } else { flags = reqFrame.readByte() } switch id { case 1: if flags&frm.FlagSkipMetaData != 0 { respFrame.writeHeader(0, frm.OpError, head.Stream) respFrame.writeInt(0) respFrame.writeString("skip metadata unexpected") } else { respFrame.writeHeader(0, frm.OpResult, head.Stream) respFrame.writeInt(frm.ResultKindRows) // respFrame.writeInt(0) // respFrame.writeInt(0) // // respFrame.writeInt(0) } case 2: if flags&frm.FlagSkipMetaData != 0 { respFrame.writeHeader(0, frm.OpResult, head.Stream) respFrame.writeInt(frm.ResultKindRows) // respFrame.writeInt(0) // respFrame.writeInt(0) // // respFrame.writeInt(0) } else { respFrame.writeHeader(0, frm.OpError, head.Stream) respFrame.writeInt(0) respFrame.writeString("skip metadata expected") } default: respFrame.writeHeader(0, frm.OpError, head.Stream) respFrame.writeInt(ErrCodeUnprepared) respFrame.writeString("unprepared") respFrame.writeShortBytes(binary.BigEndian.AppendUint64(nil, id)) } default: respFrame.writeHeader(0, frm.OpError, head.Stream) respFrame.writeInt(0) respFrame.writeString("not supported") } respFrame.buf[0] = srv.protocol | 0x80 if err := respFrame.finish(); err != nil { srv.errorLocked(err) } if err := respFrame.writeTo(conn); err != nil { if !errors.Is(err, net.ErrClosed) { srv.errorLocked(err) } } } func (srv *TestServer) readFrame(conn net.Conn) (*framer, error) { buf := make([]byte, srv.headerSize) head, err := readHeader(conn, buf) if err != nil { return nil, err } framer := newFramer(nil, srv.protocol) err = framer.readFrame(conn, &head) if err != nil { return nil, err } // should be a request frame if head.Version.Response() { return nil, fmt.Errorf("expected to read a request frame got version: %v", head.Version) } else if head.Version.Version() != srv.protocol { return nil, fmt.Errorf("expected to read protocol version 0x%x got 0x%x", srv.protocol, head.Version.Version()) } return framer, nil } func TestGetSchemaAgreement(t *testing.T) { schema_version1 := ParseUUIDMust("af810386-a694-11ef-81fa-3aea73156247") peersRows := []schemaAgreementHost{ { DataCenter: "datacenter1", HostID: ParseUUIDMust("b2035fd9-e0ca-4857-8c45-e63c00fb7c43"), Rack: "rack1", RPCAddress: "127.0.0.3", SchemaVersion: schema_version1, }, { DataCenter: "datacenter1", HostID: ParseUUIDMust("4b21ee4c-acea-4267-8e20-aaed5361a0dd"), Rack: "rack1", RPCAddress: "127.0.0.2", SchemaVersion: schema_version1, }, { DataCenter: "datacenter2", HostID: ParseUUIDMust("dfef4a22-b8d8-47e9-aee5-8c19d4b7a9e3"), Rack: "rack1", RPCAddress: "127.0.0.5", SchemaVersion: ParseUUIDMust("875a938a-a695-11ef-4314-85c8ef0ebaa2"), }, } var logger StdLogger t.Run("SchemaNotConsistent", func(t *testing.T) { err := getSchemaAgreement( []string{"875a938a-a695-11ef-4314-85c8ef0ebaa2"}, peersRows, logger, ) assert.Error(t, err, "error expected when local schema is different then others") }) t.Run("ZeroTokenNodeSchemaNotConsistent", func(t *testing.T) { err := getSchemaAgreement( []string{"af810386-a694-11ef-81fa-3aea73156247"}, peersRows, logger, ) assert.Error(t, err, "expected error when zero-token node has different schema") }) t.Run("SchemaConsistent", func(t *testing.T) { peersRows[2].SchemaVersion = schema_version1 err := getSchemaAgreement( []string{"af810386-a694-11ef-81fa-3aea73156247"}, peersRows, logger, ) assert.NoError(t, err, "expected no error when all nodes have the same schema") }) } func TestUseKeyspaceQuoteEscaping(t *testing.T) { tests := []struct { keyspace string want string }{ {"simple", `USE "simple"`}, {`my"ks`, `USE "my""ks"`}, {`a""b`, `USE "a""""b"`}, {`"`, `USE """"`}, {"", `USE ""`}, } for _, tt := range tests { got := useKeyspaceStmt(tt.keyspace) if got != tt.want { t.Errorf("keyspace %q: got %q, want %q", tt.keyspace, got, tt.want) } } } // newTestConnWithFramerPool creates a minimal Conn with an initialized framer pool // suitable for testing releaseFramer and EWMA logic. func newTestConnWithFramerPool() *Conn { c := &Conn{} c.framers.defaults = framerConfig{ proto: protoVersion4 & protoVersionMask, } c.framers.initPool(c) return c } func buildTestFrame(t *testing.T, f *framer, req frameBuilder, streamID int) ([]byte, frm.FrameHeader) { t.Helper() if err := req.buildFrame(f, streamID); err != nil { t.Fatalf("buildFrame failed: %v", err) } buf := append([]byte(nil), f.buf...) header, err := readHeader(bytes.NewReader(buf), make([]byte, headSize)) if err != nil { t.Fatalf("readHeader failed: %v", err) } return buf, header } func TestReleaseFramer(t *testing.T) { t.Parallel() t.Run("EWMAEquilibrium", func(t *testing.T) { c := newTestConnWithFramerPool() // Release framers with bufCap == avg. EWMA should not drift. for i := 0; i < 20; i++ { f := c.getReadFramer() // readBuffer is defaultBufSize (128), avg starts at defaultBufSize c.releaseReadFramer(f) } avg := c.framers.readPool.bufAvgSize.Load() if avg != defaultBufSize { t.Errorf("EWMA should stay at defaultBufSize=%d when all buffers equal, got %d", defaultBufSize, avg) } }) t.Run("DelegatesFramerReleaseToConn", func(t *testing.T) { c := newTestConnWithFramerPool() f := c.getReadFramer() f.readBuffer = make([]byte, 4096) f.Release() avgAfterFirstRelease := c.framers.readPool.bufAvgSize.Load() if avgAfterFirstRelease <= defaultBufSize { t.Fatalf("framer.Release() should route through Conn.releaseFramer and update EWMA, got %d", avgAfterFirstRelease) } f.Release() if avgAfterSecondRelease := c.framers.readPool.bufAvgSize.Load(); avgAfterSecondRelease != avgAfterFirstRelease { t.Fatalf("second framer.Release() should be a no-op: first avg=%d second avg=%d", avgAfterFirstRelease, avgAfterSecondRelease) } }) t.Run("EWMAConvergesUpward", func(t *testing.T) { c := newTestConnWithFramerPool() // Release framers with a larger buffer; EWMA should converge toward it. const targetSize = 4096 for i := 0; i < 100; i++ { f := c.getReadFramer() f.readBuffer = make([]byte, targetSize) c.releaseReadFramer(f) } avg := c.framers.readPool.bufAvgSize.Load() // After 100 iterations with weight=8, avg should be very close to targetSize. // Allow 1% tolerance. if avg < targetSize*99/100 || avg > targetSize*101/100 { t.Errorf("EWMA should converge to ~%d, got %d", targetSize, avg) } }) t.Run("EWMAConvergesDownward", func(t *testing.T) { c := newTestConnWithFramerPool() // First, push EWMA up. for i := 0; i < 100; i++ { f := c.getReadFramer() f.readBuffer = make([]byte, 4096) c.releaseReadFramer(f) } // Now release framers with small buffers; EWMA should converge back down. // Due to upward bias (+4 rounding), convergence downward is slower. const smallSize = 256 for i := 0; i < 200; i++ { f := c.getReadFramer() f.readBuffer = make([]byte, smallSize) c.releaseReadFramer(f) } avg := c.framers.readPool.bufAvgSize.Load() // Due to the upward-biased rounding (+framerBufEWMAWeight/2), the EWMA settles // slightly above the actual sample value when converging downward. The steady-state // offset is at most framerBufEWMAWeight/2 (i.e., 4) per step which compounds to // roughly framerBufEWMAWeight/2 above the target. Allow generous tolerance. if avg < smallSize || avg > smallSize+2*framerBufEWMAWeight { t.Errorf("EWMA should converge toward ~%d (with upward bias), got %d", smallSize, avg) } }) t.Run("ShrinkOversizedBuffer", func(t *testing.T) { c := newTestConnWithFramerPool() // EWMA starts at defaultBufSize (128). Release a very large framer. f := c.getReadFramer() f.readBuffer = make([]byte, 100000) origBuf := f.readBuffer c.releaseReadFramer(f) // Get the framer back from the pool and check that its buffer was shrunk. f2 := c.getReadFramer() if cap(f2.readBuffer) >= cap(origBuf) { t.Errorf("oversized buffer should have been shrunk: original cap=%d, new cap=%d", cap(origBuf), cap(f2.readBuffer)) } // Shrink target should be at least defaultBufSize. if cap(f2.readBuffer) < defaultBufSize { t.Errorf("shrunk buffer should be at least defaultBufSize=%d, got cap=%d", defaultBufSize, cap(f2.readBuffer)) } c.releaseReadFramer(f2) }) t.Run("NoShrinkNormalBuffer", func(t *testing.T) { c := newTestConnWithFramerPool() // Release a few framers with identical buffers; none should be shrunk. for i := 0; i < 10; i++ { f := c.getReadFramer() origCap := cap(f.readBuffer) c.releaseReadFramer(f) f2 := c.getReadFramer() if cap(f2.readBuffer) != origCap { t.Errorf("iteration %d: normal-sized buffer should not be shrunk: orig cap=%d, new cap=%d", i, origCap, cap(f2.readBuffer)) } c.releaseReadFramer(f2) } }) t.Run("ShrinkFloorIsDefaultBufSize", func(t *testing.T) { c := newTestConnWithFramerPool() // Push EWMA down to a very small value by releasing tiny buffers. // The shrink target should never go below defaultBufSize. for i := 0; i < 100; i++ { f := c.getReadFramer() f.readBuffer = make([]byte, 1) // Tiny buffer c.releaseReadFramer(f) } // Now release a moderately large buffer that triggers shrink. f := c.getReadFramer() f.readBuffer = make([]byte, 10000) c.releaseReadFramer(f) f2 := c.getReadFramer() if cap(f2.readBuffer) < defaultBufSize { t.Errorf("shrink target should respect defaultBufSize floor: got cap=%d, want >= %d", cap(f2.readBuffer), defaultBufSize) } c.releaseReadFramer(f2) }) t.Run("NilFramer", func(t *testing.T) { c := newTestConnWithFramerPool() // Should not panic. c.releaseReadFramer(nil) }) t.Run("NoPool", func(t *testing.T) { c := &Conn{} // No pool initialized. f := newFramer(nil, protoVersion4) // Should not panic, framer is just dropped. c.releaseReadFramer(f) }) t.Run("ReadAndWritePoolsAreSeparate", func(t *testing.T) { c := newTestConnWithFramerPool() readFramer := c.getReadFramer() readFramer.readBuffer = make([]byte, 100000) c.releaseReadFramer(readFramer) writeFramer := c.getWriteFramer() writeFramer.buf = make([]byte, 0, 8192) c.releaseWriteFramer(writeFramer) if writeAvg := c.framers.writePool.bufAvgSize.Load(); writeAvg <= defaultBufSize { t.Fatalf("writer pool should track its own EWMA, got %d", writeAvg) } writeFramer = c.getWriteFramer() if cap(writeFramer.buf) >= cap(readFramer.readBuffer) { t.Fatalf("writer framer should not inherit oversized reader buffer state, got writer cap=%d reader cap=%d", cap(writeFramer.buf), cap(readFramer.readBuffer)) } c.releaseWriteFramer(writeFramer) }) t.Run("WriteFramerResetsCustomPayloadFlagBetweenUses", func(t *testing.T) { c := newTestConnWithFramerPool() const streamID = 7 f := c.getWriteFramer() payloadReq := &writeQueryFrame{ statement: "SELECT now() FROM system.local", customPayload: map[string][]byte{ "k": []byte("v"), }, } _, payloadHeader := buildTestFrame(t, f, payloadReq, streamID) if payloadHeader.Flags&frm.FlagCustomPayload == 0 { t.Fatalf("custom payload frame should set %v, got flags=%08b", frm.FlagCustomPayload, payloadHeader.Flags) } c.releaseWriteFramer(f) if got, want := f.flags, c.framers.defaults.flags; got != want { t.Fatalf("releaseWriteFramer should restore default flags: got %08b want %08b", got, want) } f = c.getWriteFramer() plainReq := &writeQueryFrame{statement: "SELECT now() FROM system.local"} plainBuf, plainHeader := buildTestFrame(t, f, plainReq, streamID) if plainHeader.Flags != c.framers.defaults.flags { t.Fatalf("plain query should use default flags after pooled reuse: got %08b want %08b", plainHeader.Flags, c.framers.defaults.flags) } fresh := newFramer(nil, protoVersion4) freshBuf, freshHeader := buildTestFrame(t, fresh, plainReq, streamID) if plainHeader.Flags != freshHeader.Flags { t.Fatalf("reused plain query flags do not match fresh framer: got %08b want %08b", plainHeader.Flags, freshHeader.Flags) } if !bytes.Equal(plainBuf, freshBuf) { t.Fatal("reused plain query frame does not match fresh framer output") } c.releaseWriteFramer(f) }) t.Run("WriteFramerResetsTracingFlagBetweenUses", func(t *testing.T) { c := newTestConnWithFramerPool() const streamID = 9 f := c.getWriteFramer() f.trace() tracedReq := &writeQueryFrame{statement: "SELECT now() FROM system.local"} _, tracedHeader := buildTestFrame(t, f, tracedReq, streamID) if tracedHeader.Flags&frm.FlagTracing == 0 { t.Fatalf("traced query should set %v, got flags=%08b", frm.FlagTracing, tracedHeader.Flags) } c.releaseWriteFramer(f) if got, want := f.flags, c.framers.defaults.flags; got != want { t.Fatalf("releaseWriteFramer should restore default flags: got %08b want %08b", got, want) } f = c.getWriteFramer() _, plainHeader := buildTestFrame(t, f, tracedReq, streamID) if plainHeader.Flags&frm.FlagTracing != 0 { t.Fatalf("plain query should not inherit tracing flag after pooled reuse: got %08b", plainHeader.Flags) } c.releaseWriteFramer(f) }) } ================================================ FILE: connectionpool.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2012, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "fmt" "math/rand" "net" "sync" "time" "github.com/gocql/gocql/internal/debug" "github.com/gocql/gocql/tablets" "github.com/gocql/gocql/debounce" ) // interface to implement to receive the host information type SetHosts interface { SetHosts(hosts []*HostInfo) } // interface to implement to receive the partitioner value type SetPartitioner interface { SetPartitioner(partitioner string) } // interface to implement to receive the tablets value type SetTablets interface { SetTablets(tablets tablets.TabletInfoList) } type policyConnPool struct { session *Session hostConnPools map[string]*hostConnPool keyspace string port int numConns int mu sync.RWMutex } func connConfig(cfg *ClusterConfig) (*ConnConfig, error) { hostDialer := cfg.HostDialer if hostDialer == nil { dialer := cfg.Dialer if dialer == nil { d := net.Dialer{ Timeout: cfg.ConnectTimeout, } if cfg.SocketKeepalive > 0 { d.KeepAlive = cfg.SocketKeepalive } dialer = &ScyllaShardAwareDialer{Dialer: d} } hostDialer = &scyllaDialer{ dialer: dialer, logger: cfg.logger(), tlsConfig: cfg.getActualTLSConfig(), cfg: cfg, } } return &ConnConfig{ ProtoVersion: cfg.ProtoVersion, CQLVersion: cfg.CQLVersion, WriteTimeout: cfg.WriteTimeout, ReadTimeout: cfg.ReadTimeout, ConnectTimeout: cfg.ConnectTimeout, Dialer: cfg.Dialer, HostDialer: hostDialer, Compressor: cfg.Compressor, Authenticator: cfg.Authenticator, AuthProvider: cfg.AuthProvider, Keepalive: cfg.SocketKeepalive, Logger: cfg.logger(), tlsConfig: cfg.getActualTLSConfig(), }, nil } func newPolicyConnPool(session *Session) *policyConnPool { // create the pool pool := &policyConnPool{ session: session, port: session.cfg.Port, numConns: session.cfg.NumConns, keyspace: session.cfg.Keyspace, hostConnPools: map[string]*hostConnPool{}, } return pool } func (p *policyConnPool) SetHosts(hosts []*HostInfo) { p.mu.Lock() defer p.mu.Unlock() toRemove := make(map[string]struct{}) for hostID := range p.hostConnPools { toRemove[hostID] = struct{}{} } pools := make(chan *hostConnPool) createCount := 0 for _, host := range hosts { if !host.IsUp() { // don't create a connection pool for a down host continue } hostID := host.HostID() if _, exists := p.hostConnPools[hostID]; exists { // still have this host, so don't remove it delete(toRemove, hostID) continue } createCount++ go func(host *HostInfo) { // create a connection pool for the host pools <- newHostConnPool( p.session, host, p.numConns, p.keyspace, ) }(host) } // add created pools for createCount > 0 { pool := <-pools createCount-- if pool.Size() > 0 { // add pool only if there a connections available p.hostConnPools[pool.host.HostID()] = pool } } for addr := range toRemove { pool := p.hostConnPools[addr] delete(p.hostConnPools, addr) go pool.Close() } } func (p *policyConnPool) InFlight() int { p.mu.RLock() count := 0 for _, pool := range p.hostConnPools { count += pool.InFlight() } p.mu.RUnlock() return count } func (p *policyConnPool) Size() int { p.mu.RLock() count := 0 for _, pool := range p.hostConnPools { count += pool.Size() } p.mu.RUnlock() return count } func (p *policyConnPool) getPool(host *HostInfo) (pool *hostConnPool, ok bool) { hostID := host.HostID() p.mu.RLock() pool, ok = p.hostConnPools[hostID] p.mu.RUnlock() return } func (p *policyConnPool) getPoolByHostID(hostID string) (pool *hostConnPool, ok bool) { p.mu.RLock() pool, ok = p.hostConnPools[hostID] p.mu.RUnlock() return } func (p *policyConnPool) iteratePool(iter func(info HostPoolInfo) bool) { p.mu.RLock() for _, pool := range p.hostConnPools { if !iter(pool) { break } } p.mu.RUnlock() } func (p *policyConnPool) Close() { p.mu.Lock() defer p.mu.Unlock() // close the pools for addr, pool := range p.hostConnPools { delete(p.hostConnPools, addr) pool.Close() } } func (p *policyConnPool) addHost(host *HostInfo) { hostID := host.HostID() p.mu.Lock() pool, ok := p.hostConnPools[hostID] if !ok { pool = newHostConnPool( p.session, host, p.numConns, p.keyspace, ) p.hostConnPools[hostID] = pool } p.mu.Unlock() pool.fill_debounce() } func (p *policyConnPool) removeHost(hostID string) { p.mu.Lock() pool, ok := p.hostConnPools[hostID] if !ok { p.mu.Unlock() return } delete(p.hostConnPools, hostID) p.mu.Unlock() go pool.Close() } // hostConnPool is a connection pool for a single host. // Connection selection is based on a provided ConnSelectionPolicy type hostConnPool struct { connPicker ConnPicker logger StdLogger session *Session host *HostInfo debouncer *debounce.SimpleDebouncer keyspace string size int // protection for connPicker, closed, filling mu sync.RWMutex closed bool filling bool } func (pool *hostConnPool) String() string { pool.mu.RLock() defer pool.mu.RUnlock() size, _ := pool.connPicker.Size() return fmt.Sprintf("[filling=%v closed=%v conns=%v size=%v host=%v]", pool.filling, pool.closed, size, pool.size, pool.host) } func newHostConnPool(session *Session, host *HostInfo, size int, keyspace string) *hostConnPool { pool := &hostConnPool{ session: session, host: host, size: size, keyspace: keyspace, connPicker: nopConnPicker{}, filling: false, closed: false, logger: session.logger, debouncer: debounce.NewSimpleDebouncer(), } // the pool is not filled or connected return pool } // Pick a connection from this connection pool for the given query. func (pool *hostConnPool) Pick(token Token, qry ExecutableQuery) *Conn { pool.mu.RLock() defer pool.mu.RUnlock() if pool.closed { return nil } size, missing := pool.connPicker.Size() if missing > 0 { // try to fill the pool go pool.fill_debounce() if size == 0 { return nil } } return pool.connPicker.Pick(token, qry) } // Size returns the number of connections currently active in the pool func (pool *hostConnPool) Size() int { pool.mu.RLock() defer pool.mu.RUnlock() size, _ := pool.connPicker.Size() return size } // Size returns the number of connections currently active in the pool func (pool *hostConnPool) InFlight() int { pool.mu.RLock() defer pool.mu.RUnlock() size := pool.connPicker.InFlight() return size } // Close the connection pool func (pool *hostConnPool) Close() { pool.mu.Lock() if pool.closed { pool.mu.Unlock() return } pool.closed = true pool.mu.Unlock() pool.connPicker.Close() } // Fill the connection pool func (pool *hostConnPool) fill() { pool.mu.RLock() // avoid filling a closed pool, or concurrent filling if pool.closed || pool.filling { pool.mu.RUnlock() return } // determine the filling work to be done startCount, fillCount := pool.connPicker.Size() // avoid filling a full (or overfull) pool if fillCount <= 0 { pool.mu.RUnlock() return } // switch from read to write lock pool.mu.RUnlock() pool.mu.Lock() startCount, fillCount = pool.connPicker.Size() if pool.closed || pool.filling || fillCount <= 0 { // looks like another goroutine already beat this // goroutine to the filling pool.mu.Unlock() return } // ok fill the pool pool.filling = true // allow others to access the pool while filling pool.mu.Unlock() // only this goroutine should make calls to fill/empty the pool at this // point until after this routine or its subordinates calls // fillingStopped // fill only the first connection synchronously if startCount == 0 { err := pool.connect() pool.logConnectErr(err) if err != nil { // probably unreachable host pool.fillingStopped(err) return } // notify the session that this node is connected go pool.session.handleNodeConnected(pool.host) // filled one, let's reload it to see if it has changed pool.mu.RLock() _, fillCount = pool.connPicker.Size() pool.mu.RUnlock() } // fill the rest of the pool asynchronously go func() { err := pool.connectMany(fillCount) // mark the end of filling pool.fillingStopped(err) if err == nil && startCount > 0 { // notify the session that this node is connected again go pool.session.handleNodeConnected(pool.host) } }() } func (pool *hostConnPool) fill_debounce() { pool.debouncer.Debounce(pool.fill) } func (pool *hostConnPool) logConnectErr(err error) { if opErr, ok := err.(*net.OpError); ok && (opErr.Op == "dial" || opErr.Op == "read") { // connection refused // these are typical during a node outage so avoid log spam. if debug.Enabled { pool.logger.Printf("unable to dial %q: %v\n", pool.host, err) } } else if err != nil { // unexpected error pool.logger.Printf("error: failed to connect to %q due to error: %v", pool.host, err) } } // transition back to a not-filling state. func (pool *hostConnPool) fillingStopped(err error) { if err != nil { if debug.Enabled { pool.logger.Printf("gocql: filling stopped %q: %v\n", pool.host.ConnectAddress(), err) } // wait for some time to avoid back-to-back filling // this provides some time between failed attempts // to fill the pool for the host to recover time.Sleep(time.Duration(rand.Int31n(100)+31) * time.Millisecond) } pool.mu.Lock() pool.filling = false count, _ := pool.connPicker.Size() host := pool.host port := pool.host.Port() pool.mu.Unlock() // if we errored and the size is now zero, make sure the host is marked as down // see https://github.com/apache/cassandra-gocql-driver/issues/1614 if debug.Enabled { pool.logger.Printf("gocql: conns of pool after stopped %q: %v\n", host.ConnectAddress(), count) } if err != nil && count == 0 { if pool.session.cfg.ConvictionPolicy.AddFailure(err, host) { pool.session.handleNodeDown(host.ConnectAddress(), port) } } } // connectMany creates new connections concurrent. func (pool *hostConnPool) connectMany(count int) error { if count == 0 { return nil } var ( wg sync.WaitGroup mu sync.Mutex connectErr error ) wg.Add(count) for i := 0; i < count; i++ { go func() { defer wg.Done() err := pool.connect() pool.logConnectErr(err) if err != nil { mu.Lock() connectErr = err mu.Unlock() } }() } // wait for all connections are done wg.Wait() return connectErr } // create a new connection to the host and add it to the pool func (pool *hostConnPool) connect() (err error) { pool.mu.Lock() shardID, nrShards := pool.connPicker.NextShard() pool.mu.Unlock() // TODO: provide a more robust connection retry mechanism, we should also // be able to detect hosts that come up by trying to connect to downed ones. // try to connect var conn *Conn reconnectionPolicy := pool.session.cfg.ReconnectionPolicy for i := 0; i < reconnectionPolicy.GetMaxRetries(); i++ { conn, err = pool.session.connectShard(pool.session.ctx, pool.host, pool, shardID, nrShards) if err == nil { break } if opErr, isOpErr := err.(*net.OpError); isOpErr { // if the error is not a temporary error (ex: network unreachable) don't // retry if !opErr.Temporary() { break } } if debug.Enabled { pool.logger.Printf("gocql: connection failed %q: %v, reconnecting with %T\n", pool.host.ConnectAddress(), err, reconnectionPolicy) } time.Sleep(reconnectionPolicy.GetInterval(i)) } if err != nil { return err } if pool.keyspace != "" { // set the keyspace if err = conn.UseKeyspace(pool.keyspace); err != nil { conn.Close() return err } } // add the Conn to the pool pool.mu.Lock() if pool.closed { pool.mu.Unlock() conn.Close() return nil } // lazily initialize the connPicker when we know the required type pool.initConnPicker(conn) if err := pool.connPicker.Put(conn); err != nil { pool.mu.Unlock() conn.Close() if debug.Enabled { pool.logger.Printf("gocql: pool connection was not added to the pool: %w", err) } return nil } pool.mu.Unlock() conn.finalizeConnection() return nil } func (pool *hostConnPool) initConnPicker(conn *Conn) { if _, ok := pool.connPicker.(nopConnPicker); !ok { return } if conn.isScyllaConn() { pool.connPicker = newScyllaConnPicker(conn, pool.logger) return } pool.connPicker = newDefaultConnPicker(pool.size) } // handle any error from a Conn func (pool *hostConnPool) HandleError(conn *Conn, err error, closed bool) { if !closed { // still an open connection, so continue using it return } // TODO: track the number of errors per host and detect when a host is dead, // then also have something which can detect when a host comes back. pool.mu.Lock() defer pool.mu.Unlock() if pool.closed { // pool closed return } if debug.Enabled { pool.logger.Printf("gocql: pool connection error %q: %v\n", conn.addr, err) } pool.connPicker.Remove(conn) go pool.fill_debounce() } func (pool *hostConnPool) GetConnectionCount() int { pool.mu.Lock() defer pool.mu.Unlock() return pool.connPicker.GetConnectionCount() } func (pool *hostConnPool) GetExcessConnectionCount() int { pool.mu.Lock() defer pool.mu.Unlock() return pool.connPicker.GetExcessConnectionCount() } func (pool *hostConnPool) GetShardCount() int { pool.mu.Lock() defer pool.mu.Unlock() return pool.connPicker.GetShardCount() } func (pool *hostConnPool) Host() HostInformation { return pool.host } func (pool *hostConnPool) IsClosed() bool { pool.mu.Lock() defer pool.mu.Unlock() return pool.closed } ================================================ FILE: connectionpool_test.go ================================================ //go:build unit // +build unit /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "context" "crypto/tls" "errors" "net" "testing" "time" "github.com/gocql/gocql/debounce" ) func TestSetupTLSConfig(t *testing.T) { t.Parallel() tests := []struct { name string opts *SslOptions expectedInsecureSkipVerify bool }{ { name: "Config nil, EnableHostVerification false", opts: &SslOptions{ EnableHostVerification: false, }, expectedInsecureSkipVerify: true, }, { name: "Config nil, EnableHostVerification true", opts: &SslOptions{ EnableHostVerification: true, }, expectedInsecureSkipVerify: false, }, { name: "Config.InsecureSkipVerify false, EnableHostVerification false", opts: &SslOptions{ EnableHostVerification: false, Config: &tls.Config{ InsecureSkipVerify: false, }, }, expectedInsecureSkipVerify: false, }, { name: "Config.InsecureSkipVerify true, EnableHostVerification false", opts: &SslOptions{ EnableHostVerification: false, Config: &tls.Config{ InsecureSkipVerify: true, }, }, expectedInsecureSkipVerify: true, }, { name: "Config.InsecureSkipVerify false, EnableHostVerification true", opts: &SslOptions{ EnableHostVerification: true, Config: &tls.Config{ InsecureSkipVerify: false, }, }, expectedInsecureSkipVerify: false, }, { name: "Config.InsecureSkipVerify true, EnableHostVerification true", opts: &SslOptions{ EnableHostVerification: true, Config: &tls.Config{ InsecureSkipVerify: true, }, }, expectedInsecureSkipVerify: false, }, } for _, test := range tests { test := test t.Run(test.name, func(t *testing.T) { tlsConfig, err := setupTLSConfig(test.opts) if err != nil { t.Fatalf("unexpected error %q", err.Error()) } if tlsConfig.InsecureSkipVerify != test.expectedInsecureSkipVerify { t.Fatalf("got %v, but expected %v", tlsConfig.InsecureSkipVerify, test.expectedInsecureSkipVerify) } }) } } // errorConn is a mock net.Conn whose Close returns an error, // triggering HandleError via Conn.closeWithError. type errorConn struct { net.Conn } func (e errorConn) Close() error { return errors.New("mock close error") } // TestHostConnPoolCloseDeadlock verifies that hostConnPool.Close() does not // self-deadlock when defaultConnPicker closes connections that trigger // HandleError callbacks. // // Deadlock chain (before fix): // // Close() -> connPicker.Close() -> conn.Close() -> HandleError() -> pool.mu.Lock() (DEADLOCK) func TestHostConnPoolCloseDeadlock(t *testing.T) { t.Parallel() host := &HostInfo{connectAddress: net.ParseIP("127.0.0.1"), port: 9042} session := &Session{ cfg: ClusterConfig{ NumConns: 2, ConvictionPolicy: &SimpleConvictionPolicy{}, }, logger: nopLogger{}, } pool := &hostConnPool{ session: session, host: host, size: 2, keyspace: "test", connPicker: nopConnPicker{}, logger: nopLogger{}, debouncer: debounce.NewSimpleDebouncer(), } // Build a defaultConnPicker with Conns that trigger HandleError on Close. picker := newDefaultConnPicker(2) for i := 0; i < 2; i++ { ctx, cancel := context.WithCancel(context.Background()) conn := &Conn{ conn: errorConn{}, errorHandler: pool, cancel: cancel, ctx: ctx, logger: nopLogger{}, } _ = picker.Put(conn) } pool.connPicker = picker // Close must return before timeout; otherwise the pool is deadlocked. done := make(chan struct{}) go func() { pool.Close() close(done) }() select { case <-done: // Success — Close returned without deadlocking. case <-time.After(5 * time.Second): t.Fatal("hostConnPool.Close() deadlocked: timed out after 5 seconds") } } // TestHostConnPoolConnectClosedPoolDoesNotDeadlock verifies that connect's // already-closed-pool path does not close a connection while holding pool.mu. func TestHostConnPoolConnectClosedPoolDoesNotDeadlock(t *testing.T) { t.Parallel() host := &HostInfo{connectAddress: net.ParseIP("127.0.0.1"), port: 9042} session := &Session{ cfg: ClusterConfig{ NumConns: 1, ConvictionPolicy: &SimpleConvictionPolicy{}, }, logger: nopLogger{}, } pool := &hostConnPool{ session: session, host: host, size: 1, keyspace: "test", connPicker: nopConnPicker{}, logger: nopLogger{}, debouncer: debounce.NewSimpleDebouncer(), closed: true, } ctx, cancel := context.WithCancel(context.Background()) conn := &Conn{ conn: errorConn{}, errorHandler: pool, cancel: cancel, ctx: ctx, logger: nopLogger{}, } done := make(chan struct{}) go func() { pool.mu.Lock() if pool.closed { pool.mu.Unlock() conn.Close() } close(done) }() select { case <-done: case <-time.After(5 * time.Second): t.Fatal("closed-pool connect cleanup deadlocked: timed out after 5 seconds") } } ================================================ FILE: connpicker.go ================================================ package gocql import ( "fmt" "sync" "sync/atomic" ) type ConnPicker interface { Pick(Token, ExecutableQuery) *Conn Put(*Conn) error Remove(conn *Conn) InFlight() int Size() (int, int) Close() // NextShard returns the shardID to connect to. // nrShard specifies how many shards the host has. // If nrShards is zero, the caller shouldn't use shard-aware port. NextShard() (shardID, nrShards int) GetConnectionCount() int GetExcessConnectionCount() int GetShardCount() int } type defaultConnPicker struct { conns []*Conn pos uint32 size int mu sync.RWMutex } func (p *defaultConnPicker) GetConnectionCount() int { p.mu.Lock() defer p.mu.Unlock() return len(p.conns) } func (p *defaultConnPicker) GetExcessConnectionCount() int { return 0 } func (p *defaultConnPicker) GetShardCount() int { // It is not supposed to be used for scylla nodes and therefore does not know anything about shards count return 0 } func newDefaultConnPicker(size int) *defaultConnPicker { if size <= 0 { panic(fmt.Sprintf("invalid pool size %d", size)) } return &defaultConnPicker{ size: size, } } func (p *defaultConnPicker) Remove(conn *Conn) { p.mu.Lock() defer p.mu.Unlock() for i, candidate := range p.conns { if candidate == conn { last := len(p.conns) - 1 p.conns[i], p.conns = p.conns[last], p.conns[:last] break } } } func (p *defaultConnPicker) Close() { p.mu.Lock() defer p.mu.Unlock() conns := p.conns p.conns = nil for _, conn := range conns { if conn != nil { conn.Close() } } } func (p *defaultConnPicker) InFlight() int { p.mu.RLock() size := len(p.conns) p.mu.RUnlock() return size } func (p *defaultConnPicker) Size() (int, int) { p.mu.RLock() size := len(p.conns) p.mu.RUnlock() return size, p.size - size } func (p *defaultConnPicker) Pick(Token, ExecutableQuery) *Conn { pos := int(atomic.AddUint32(&p.pos, 1) - 1) p.mu.RLock() size := len(p.conns) var ( leastBusyConn *Conn streamsAvailable int ) // find the conn which has the most available streams, this is racy for i := 0; i < size; i++ { conn := p.conns[(pos+i)%size] if conn == nil { continue } if streams := conn.AvailableStreams(); streams > streamsAvailable { leastBusyConn = conn streamsAvailable = streams } } p.mu.RUnlock() return leastBusyConn } func (p *defaultConnPicker) Put(conn *Conn) error { p.mu.Lock() p.conns = append(p.conns, conn) p.mu.Unlock() return nil } func (*defaultConnPicker) NextShard() (shardID, nrShards int) { return 0, 0 } // nopConnPicker is a no-operation implementation of ConnPicker, it's used when // hostConnPool is created to allow deferring creation of the actual ConnPicker // to the point where we have first connection. type nopConnPicker struct{} func (p nopConnPicker) GetConnectionCount() int { return 0 } func (p nopConnPicker) GetExcessConnectionCount() int { return 0 } func (p nopConnPicker) GetShardCount() int { return 0 } func (nopConnPicker) Pick(Token, ExecutableQuery) *Conn { return nil } func (nopConnPicker) Put(*Conn) error { return nil } func (nopConnPicker) Remove(conn *Conn) { } func (nopConnPicker) InFlight() int { return 0 } func (nopConnPicker) Size() (int, int) { // Return 1 to make hostConnPool to try to establish a connection. // When first connection is established hostConnPool replaces nopConnPicker // with a different ConnPicker implementation. return 0, 1 } func (nopConnPicker) Close() { } func (nopConnPicker) NextShard() (shardID, nrShards int) { return 0, 0 } ================================================ FILE: control.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "context" crand "crypto/rand" "errors" "fmt" "math/rand" "net" "regexp" "strconv" "sync" "sync/atomic" "time" "github.com/gocql/gocql/events" "github.com/gocql/gocql/internal/debug" frm "github.com/gocql/gocql/internal/frame" ) var ( randr *rand.Rand mutRandr sync.Mutex ) func init() { b := make([]byte, 4) if _, err := crand.Read(b); err != nil { panic(fmt.Sprintf("unable to seed random number generator: %v", err)) } randr = rand.New(rand.NewSource(int64(readInt(b)))) } const ( controlConnStarting = 0 controlConnStarted = 1 controlConnClosing = -1 ) type controlConnection interface { getConn() *connHost awaitSchemaAgreement() error query(statement string, values ...any) (iter *Iter) querySystem(statement string, values ...any) (iter *Iter) discoverProtocol(hosts []*HostInfo) (int, error) connect(hosts []*HostInfo) error close() getSession() *Session reconnect() error } // Ensure that the atomic variable is aligned to a 64bit boundary // so that atomic operations can be applied on 32bit architectures. type controlConn struct { conn atomic.Value retry RetryPolicy session *Session quit chan struct{} state int32 reconnecting int32 } func (c *controlConn) getSession() *Session { return c.session } func createControlConn(session *Session) *controlConn { control := &controlConn{ session: session, quit: make(chan struct{}), retry: &SimpleRetryPolicy{NumRetries: 3}, } control.conn.Store((*connHost)(nil)) return control } func (c *controlConn) heartBeat() { if !atomic.CompareAndSwapInt32(&c.state, controlConnStarting, controlConnStarted) { return } sleepTime := 1 * time.Second timer := time.NewTimer(sleepTime) defer timer.Stop() for { timer.Reset(sleepTime) select { case <-c.quit: return case <-timer.C: } resp, err := c.writeFrame(&writeOptionsFrame{}) if err != nil { goto reconn } switch resp.(type) { case *frm.SupportedFrame: // Everything ok sleepTime = 30 * time.Second continue case error: goto reconn default: panic(fmt.Sprintf("gocql: unknown frame in response to options: %T", resp)) } reconn: // try to connect a bit faster sleepTime = 1 * time.Second c.reconnect() continue } } func resolveInitialEndpoint(resolver DNSResolver, addr string, defaultPort int) ([]*HostInfo, error) { var port int host, portStr, err := net.SplitHostPort(addr) if err != nil { host = addr port = defaultPort } else { port, err = strconv.Atoi(portStr) if err != nil { return nil, err } } // Check if host is a literal IP address if ip := net.ParseIP(host); ip != nil { if validIpAddr(ip) { hb := HostInfoBuilder{ // Fake hosts for initial endpoints do not need HostID Hostname: host, ConnectAddress: ip, Port: port, } hh := hb.Build() return []*HostInfo{&hh}, nil } } // Look up host in DNS ips, err := resolver.LookupIP(host) if err != nil { return nil, err } else if len(ips) == 0 { return nil, fmt.Errorf("no IP's returned from DNS lookup for %q", addr) } var hosts []*HostInfo for _, ip := range ips { if validIpAddr(ip) { hb := HostInfoBuilder{ // Fake hosts for initial endpoints do not need HostID Hostname: host, ConnectAddress: ip, Port: port, } hh := hb.Build() hosts = append(hosts, &hh) } } if len(hosts) == 0 { return nil, fmt.Errorf("no IP's founded for %q", addr) } return hosts, nil } func shuffleHosts(hosts []*HostInfo) []*HostInfo { shuffled := make([]*HostInfo, len(hosts)) copy(shuffled, hosts) mutRandr.Lock() randr.Shuffle(len(hosts), func(i, j int) { shuffled[i], shuffled[j] = shuffled[j], shuffled[i] }) mutRandr.Unlock() return shuffled } // this is going to be version dependant and a nightmare to maintain :( var protocolSupportRe = regexp.MustCompile(`the lowest supported version is \d+ and the greatest is (\d+)$`) func parseProtocolFromError(err error) int { // I really wish this had the actual info in the error frame... matches := protocolSupportRe.FindAllStringSubmatch(err.Error(), -1) if len(matches) != 1 || len(matches[0]) != 2 { if verr, ok := err.(*protocolError); ok { return int(verr.frame.Header().Version.Version()) } return 0 } max, err := strconv.Atoi(matches[0][1]) if err != nil { return 0 } return max } func (c *controlConn) discoverProtocol(hosts []*HostInfo) (int, error) { hosts = shuffleHosts(hosts) connCfg := *c.session.connCfg connCfg.ProtoVersion = protoVersion4 // TODO: define maxProtocol handler := connErrorHandlerFn(func(c *Conn, err error, closed bool) { // we should never get here, but if we do it means we connected to a // host successfully which means our attempted protocol version worked if !closed { c.Close() } }) var err error for _, host := range hosts { var conn *Conn conn, err = c.session.dial(c.session.ctx, host, &connCfg, handler) // not need to call conn.finalizeConnection since this connection to be terminated right away if conn != nil { conn.Close() } if err == nil { return connCfg.ProtoVersion, nil } if proto := parseProtocolFromError(err); proto > 0 { return proto, nil } } return 0, err } func (c *controlConn) connect(hosts []*HostInfo) error { if len(hosts) == 0 { return errors.New("control: no endpoints specified") } // shuffle endpoints so not all drivers will connect to the same initial // node. hosts = shuffleHosts(hosts) cfg := *c.session.connCfg cfg.disableCoalesce = true var conn *Conn var err error for _, host := range hosts { conn, err = c.session.dial(c.session.ctx, host, &cfg, c) // conn.finalizeConnection() to be called outside of this function, since initialization process is not completed yet if err != nil { c.session.logger.Printf("gocql: unable to dial control conn %v:%v: %v\n", host.ConnectAddress(), host.Port(), err) continue } err = c.setupConn(conn) if err == nil { break } c.session.logger.Printf("gocql: unable setup control conn %v:%v: %v\n", host.ConnectAddress(), host.Port(), err) conn.Close() conn = nil } if conn == nil { return fmt.Errorf("unable to connect to initial hosts: %v", err) } // we could fetch the initial ring here and update initial host data. So that // when we return from here we have a ring topology ready to go. go c.heartBeat() return nil } type connHost struct { conn ConnInterface host *HostInfo } func (c *controlConn) setupConn(conn *Conn) error { // we need up-to-date host info for the filterHost call below iter := conn.querySystem(context.TODO(), qrySystemLocal) host, err := hostInfoFromIter(iter, c.session.cfg.Port) if err != nil { return err } if c.session.cfg.filterHost(host) { return fmt.Errorf("host was filtered: %v", host.ConnectAddress()) } if err := c.registerEvents(conn); err != nil { return fmt.Errorf("register events: %v", err) } ch := &connHost{ conn: conn, host: host, } old, _ := c.conn.Swap(ch).(*connHost) var oldHost events.HostInfo if old != nil && old.host != nil { oldHost.HostID = old.host.HostID() oldHost.Host = old.host.ConnectAddress() oldHost.Port = old.host.Port() } c.session.publishEvent(&events.ControlConnectionRecreatedEvent{ OldHost: oldHost, NewHost: events.HostInfo{ HostID: host.HostID(), Host: host.ConnectAddress(), Port: host.Port(), }, }) if c.session.initialized() { // We connected to control conn, so add the connect the host in pool as well. // Notify session we can start trying to connect to the node. // We can't start the fill before the session is initialized, otherwise the fill would interfere // with the fill called by Session.init. Session.init needs to wait for its fill to finish and that // would return immediately if we started the fill here. // TODO(martin-sucha): Trigger pool refill for all hosts, like in reconnectDownedHosts? go c.session.startPoolFill(host) } return nil } func (c *controlConn) registerEvents(conn *Conn) error { var events []string if !c.session.cfg.Events.DisableTopologyEvents { events = append(events, "TOPOLOGY_CHANGE") } if !c.session.cfg.Events.DisableNodeStatusEvents { events = append(events, "STATUS_CHANGE") } if !c.session.cfg.Events.DisableSchemaEvents { events = append(events, "SCHEMA_CHANGE") } if c.session.cfg.ClientRoutesConfig != nil { events = append(events, "CLIENT_ROUTES_CHANGE") } if len(events) == 0 { return nil } framer, err := conn.exec(context.Background(), &writeRegisterFrame{ events: events, }, nil, conn.cfg.ConnectTimeout) if err != nil { return err } defer framer.Release() frame, err := framer.parseFrame() if err != nil { return err } else if _, ok := frame.(*frm.ReadyFrame); !ok { return fmt.Errorf("unexpected frame in response to register: got %T: %v\n", frame, frame) } return nil } func (c *controlConn) reconnect() error { if atomic.LoadInt32(&c.state) == controlConnClosing { return fmt.Errorf("control connection is closing") } if !atomic.CompareAndSwapInt32(&c.reconnecting, 0, 1) { return fmt.Errorf("control connection is reconnecting") } defer atomic.StoreInt32(&c.reconnecting, 0) err := c.attemptReconnect() if err != nil { err = fmt.Errorf("gocql: unable to reconnect control connection: %w\n", err) c.session.logger.Printf(err.Error()) return err } err = c.session.refreshRingNow() if err != nil { c.session.logger.Printf("gocql: unable to refresh ring: %v\n", err) } err = c.session.metadataDescriber.refreshAllSchema() if err != nil { c.session.logger.Printf("gocql: unable to refresh the schema: %v\n", err) } return nil } func (c *controlConn) attemptReconnect() error { hosts := c.session.hostSource.getHostsList() hosts = shuffleHosts(hosts) // keep the old behavior of connecting to the old host first by moving it to // the front of the slice ch := c.getConn() if ch != nil { for i := range hosts { if hosts[i].Equal(ch.host) { hosts[0], hosts[i] = hosts[i], hosts[0] break } } ch.conn.Close() } err := c.attemptReconnectToAnyOfHosts(hosts) if err == nil { return nil } c.session.logger.Printf("gocql: unable to connect to any ring node: %v\n", err) c.session.logger.Printf("gocql: control falling back to initial contact points.\n") // Fallback to initial contact points, as it may be the case that all known initialHosts // changed their IPs while keeping the same hostname(s). initialHosts, resolvErr := resolveInitialEndpoints(c.session.cfg.DNSResolver, c.session.cfg.Hosts, c.session.cfg.Port, c.session.logger) if resolvErr != nil { return fmt.Errorf("resolve contact points' hostnames: %v", resolvErr) } return c.attemptReconnectToAnyOfHosts(initialHosts) } func (c *controlConn) attemptReconnectToAnyOfHosts(hosts []*HostInfo) error { for _, host := range hosts { conn, err := c.session.connect(c.session.ctx, host, c) if err != nil { if c.session.cfg.ConvictionPolicy.AddFailure(err, host) { c.session.handleNodeDown(host.ConnectAddress(), host.Port()) } c.session.logger.Printf("gocql: unable to dial control conn %v:%v: %v\n", host.ConnectAddress(), host.Port(), err) continue } err = c.setupConn(conn) if err != nil { c.session.logger.Printf("gocql: unable setup control conn %v:%v: %v\n", host.ConnectAddress(), host.Port(), err) conn.Close() continue } conn.finalizeConnection() c.session.publishEvent(&events.ControlConnectionRecreatedEvent{ NewHost: events.HostInfo{ Host: host.ConnectAddress(), Port: host.Port(), HostID: host.HostID(), }, }) return nil } return fmt.Errorf("unable to connect to any known node: %v", hosts) } func (c *controlConn) HandleError(conn *Conn, err error, closed bool) { if !closed { return } oldConn := c.getConn() // If connection has long gone, and not been attempted for awhile, // it's possible to have oldConn as nil here (#1297). if oldConn != nil && oldConn.conn != conn { return } go c.reconnect() } func (c *controlConn) getConn() *connHost { return c.conn.Load().(*connHost) } // writeFrame sends frame w on the control connection and returns the parsed // response frame. // // NOTE: The returned frame must not retain any byte-slice references to the // framer's read buffer, because the framer is released back to the pool // immediately after parseFrame returns (via defer). Frame types that use // readBytesCopy (e.g. SupportedFrame, AuthChallengeFrame, AuthSuccessFrame) // are safe; frame types that use readBytes and expose []byte fields would not // be safe and must not be returned from this function. func (c *controlConn) writeFrame(w frameBuilder) (frame, error) { ch := c.getConn() if ch == nil { return nil, errNoControl } framer, err := ch.conn.exec(context.Background(), w, nil, c.session.cfg.MetadataSchemaRequestTimeout) if err != nil { return nil, err } defer framer.Release() return framer.parseFrame() } // query will return nil if the connection is closed or nil func (c *controlConn) querySystem(statement string, values ...any) (iter *Iter) { conn := c.getConn().conn.(*Conn) return c.runQuery(c.session.Query(statement+conn.usingTimeoutClause, values...). Consistency(One). SetRequestTimeout(conn.systemRequestTimeout). RoutingKey([]byte{}). Trace(nil)) } // query will return nil if the connection is closed or nil func (c *controlConn) query(statement string, values ...any) (iter *Iter) { return c.runQuery(c.session.Query(statement, values...).Consistency(One).RoutingKey([]byte{}).Trace(nil)) } // query will return nil if the connection is closed or nil func (c *controlConn) runQuery(qry *Query) (iter *Iter) { for { ch := c.getConn() qry.conn = ch.conn iter = ch.conn.executeQuery(context.TODO(), qry) if debug.Enabled && iter.err != nil { c.session.logger.Printf("control: error executing %q: %v\n", qry.stmt, iter.err) } qry.AddAttempts(1, ch.host) if iter.err == nil || !c.retry.Attempt(qry) { break } iter.finalize(true) } return } func (c *controlConn) awaitSchemaAgreement() error { ch := c.getConn() return (&Iter{err: ch.conn.awaitSchemaAgreement(context.TODO())}).err } func (c *controlConn) close() { if atomic.CompareAndSwapInt32(&c.state, controlConnStarted, controlConnClosing) { c.quit <- struct{}{} } ch := c.getConn() if ch != nil { ch.conn.Close() } } var errNoControl = errors.New("gocql: no control connection available") ================================================ FILE: control_integration_test.go ================================================ //go:build integration // +build integration package gocql import ( "context" "fmt" "net" "testing" ) // unixSocketDialer is a special dialer which connects only to the maintenance_socket. type unixSocketDialer struct { dialer net.Dialer socketPath string } func (d unixSocketDialer) DialContext(_ context.Context, _, _ string) (net.Conn, error) { return d.dialer.Dial("unix", d.socketPath) } func TestUnixSockets(t *testing.T) { t.Parallel() socketFiles := getClusterSocketFile() if len(socketFiles) == 0 { t.Skip("this test needs path to socket file provided into -cluster-socket cli option") } c := createCluster() c.NumConns = 1 c.DisableInitialHostLookup = true c.ProtoVersion = protoVersion3 c.ReconnectInterval = 0 c.WriteCoalesceWaitTime = 0 c.Events.DisableNodeStatusEvents = true c.Events.DisableTopologyEvents = true c.Events.DisableSchemaEvents = true d := net.Dialer{ Timeout: c.Timeout, } if c.SocketKeepalive > 0 { d.KeepAlive = c.SocketKeepalive } c.Dialer = unixSocketDialer{ dialer: d, socketPath: socketFiles[0], } sess, err := c.CreateSession() if err != nil { t.Fatalf("unable to create session: %v", err) } defer sess.Close() keyspace := testKeyspaceName(t) err = createTable(sess, `DROP KEYSPACE IF EXISTS `+keyspace) if err != nil { t.Fatal("unable to drop keyspace if exists:", err) } err = createTable(sess, fmt.Sprintf(`CREATE KEYSPACE %s WITH replication = { 'class' : 'NetworkTopologyStrategy', 'replication_factor' : 1 }`, keyspace)) if err != nil { t.Fatal("unable to create keyspace:", err) } } ================================================ FILE: control_test.go ================================================ //go:build unit // +build unit /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "net" "testing" frm "github.com/gocql/gocql/internal/frame" ) func TestHostInfo_Lookup(t *testing.T) { t.Parallel() resolver := NewSimpleDNSResolver(true) tests := [...]struct { addr string ip net.IP }{ {"127.0.0.1", net.IPv4(127, 0, 0, 1)}, {"localhost", net.IPv4(127, 0, 0, 1)}, // TODO: this may be host dependant } for i, test := range tests { hosts, err := resolveInitialEndpoint(resolver, test.addr, 1) if err != nil { t.Errorf("%d: %v", i, err) continue } host := hosts[0] if !host.ConnectAddress().Equal(test.ip) { t.Errorf("expected ip %v got %v for addr %q", test.ip, host.ConnectAddress(), test.addr) } } } func TestParseProtocol(t *testing.T) { t.Parallel() tests := [...]struct { err error proto int }{ { err: &protocolError{ frame: frm.ErrorFrame{ Code: 0x10, Message: "Invalid or unsupported protocol version (5); the lowest supported version is 3 and the greatest is 4", }, }, proto: protoVersion4, }, { err: &protocolError{ frame: frm.ErrorFrame{ FrameHeader: frm.FrameHeader{ Version: 0x83, }, Code: 0x10, Message: "Invalid or unsupported protocol version: 5", }, }, proto: protoVersion3, }, } for i, test := range tests { if proto := parseProtocolFromError(test.err); proto != test.proto { t.Errorf("%d: exepcted proto %d got %d", i, test.proto, proto) } } } ================================================ FILE: cqltypes.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2012, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql type Duration struct { Months int32 Days int32 Nanoseconds int64 } ================================================ FILE: debounce/refresh_deboucer.go ================================================ package debounce import ( "sync" "time" ) const ( RingRefreshDebounceTime = 1 * time.Second ) // debounces requests to call a refresh function (currently used for ring refresh). It also supports triggering a refresh immediately. type RefreshDebouncer struct { broadcaster *errorBroadcaster timer *time.Timer refreshNowCh chan struct{} quit chan struct{} refreshFn func() error interval time.Duration mu sync.Mutex stopped bool } func NewRefreshDebouncer(interval time.Duration, refreshFn func() error) *RefreshDebouncer { d := &RefreshDebouncer{ stopped: false, broadcaster: nil, refreshNowCh: make(chan struct{}, 1), quit: make(chan struct{}), interval: interval, timer: time.NewTimer(interval), refreshFn: refreshFn, } d.timer.Stop() go d.flusher() return d } // debounces a request to call the refresh function func (d *RefreshDebouncer) Debounce() { d.mu.Lock() defer d.mu.Unlock() if d.stopped { return } d.timer.Reset(d.interval) } // requests an immediate refresh which will cancel pending refresh requests func (d *RefreshDebouncer) RefreshNow() <-chan error { d.mu.Lock() defer d.mu.Unlock() if d.broadcaster == nil { d.broadcaster = newErrorBroadcaster() select { case d.refreshNowCh <- struct{}{}: default: // already a refresh pending } } return d.broadcaster.newListener() } func (d *RefreshDebouncer) flusher() { for { select { case <-d.refreshNowCh: case <-d.timer.C: case <-d.quit: } d.mu.Lock() if d.stopped { if d.broadcaster != nil { d.broadcaster.stop() d.broadcaster = nil } d.timer.Stop() d.mu.Unlock() return } // make sure both request channels are cleared before we refresh select { case <-d.refreshNowCh: default: } d.timer.Stop() select { case <-d.timer.C: default: } curBroadcaster := d.broadcaster d.broadcaster = nil d.mu.Unlock() err := d.refreshFn() if curBroadcaster != nil { curBroadcaster.broadcast(err) } } } func (d *RefreshDebouncer) Stop() { d.mu.Lock() if d.stopped { d.mu.Unlock() return } d.stopped = true d.mu.Unlock() d.quit <- struct{}{} // sync with flusher close(d.quit) } // broadcasts an error to multiple channels (listeners) type errorBroadcaster struct { listeners []chan<- error mu sync.Mutex } func newErrorBroadcaster() *errorBroadcaster { return &errorBroadcaster{ listeners: nil, mu: sync.Mutex{}, } } func (b *errorBroadcaster) newListener() <-chan error { ch := make(chan error, 1) b.mu.Lock() defer b.mu.Unlock() b.listeners = append(b.listeners, ch) return ch } func (b *errorBroadcaster) broadcast(err error) { b.mu.Lock() defer b.mu.Unlock() curListeners := b.listeners if len(curListeners) > 0 { b.listeners = nil } else { return } for _, listener := range curListeners { listener <- err close(listener) } } func (b *errorBroadcaster) stop() { b.mu.Lock() defer b.mu.Unlock() if len(b.listeners) == 0 { return } for _, listener := range b.listeners { close(listener) } b.listeners = nil } ================================================ FILE: debounce/refresh_debouncer_test.go ================================================ //go:build unit // +build unit package debounce import ( "errors" "sync" "sync/atomic" "testing" "time" ) // This test sends debounce requests and waits until the refresh function is called (which should happen when the timer elapses). func TestRefreshDebouncer_MultipleEvents(t *testing.T) { const numberOfEvents = 10 channel := make(chan int, numberOfEvents) // should never use more than 1 but allow for more to possibly detect bugs fn := func() error { channel <- 0 return nil } beforeEvents := time.Now() wg := sync.WaitGroup{} d := NewRefreshDebouncer(2*time.Second, fn) defer d.Stop() for i := 0; i < numberOfEvents; i++ { wg.Add(1) go func() { defer wg.Done() d.Debounce() }() } wg.Wait() timeoutCh := time.After(2500 * time.Millisecond) // extra time to avoid flakiness select { case <-channel: case <-timeoutCh: t.Fatalf("timeout elapsed without flush function being called") } afterFunctionCall := time.Now() // use 1.5 seconds instead of 2 seconds to avoid timer precision issues if afterFunctionCall.Sub(beforeEvents) < 1500*time.Millisecond { t.Fatalf("function was called after %v ms instead of ~2 seconds", afterFunctionCall.Sub(beforeEvents).Milliseconds()) } // wait another 2 seconds and check if function was called again time.Sleep(2500 * time.Millisecond) if len(channel) > 0 { t.Fatalf("function was called more than once") } } // This test: // // 1 - Sends debounce requests when test starts // 2 - Calls refreshNow() before the timer elapsed (which stops the timer) about 1.5 seconds after test starts // // The end result should be 1 refresh function call when refreshNow() is called. func TestRefreshDebouncer_RefreshNow(t *testing.T) { const numberOfEvents = 10 channel := make(chan int, numberOfEvents) // should never use more than 1 but allow for more to possibly detect bugs fn := func() error { channel <- 0 return nil } beforeEvents := time.Now() eventsWg := sync.WaitGroup{} d := NewRefreshDebouncer(2*time.Second, fn) defer d.Stop() for i := 0; i < numberOfEvents; i++ { eventsWg.Add(1) go func() { defer eventsWg.Done() d.Debounce() }() } refreshNowWg := sync.WaitGroup{} refreshNowWg.Add(1) go func() { defer refreshNowWg.Done() time.Sleep(1500 * time.Millisecond) d.RefreshNow() }() eventsWg.Wait() select { case <-channel: t.Fatalf("function was called before the expected time") default: } refreshNowWg.Wait() timeoutCh := time.After(200 * time.Millisecond) // allow for 200ms of delay to prevent flakiness select { case <-channel: case <-timeoutCh: t.Fatalf("timeout elapsed without flush function being called") } afterFunctionCall := time.Now() // use 1 second instead of 1.5s to avoid timer precision issues if afterFunctionCall.Sub(beforeEvents) < 1000*time.Millisecond { t.Fatalf("function was called after %v ms instead of ~1.5 seconds", afterFunctionCall.Sub(beforeEvents).Milliseconds()) } // wait some time and check if function was called again time.Sleep(2500 * time.Millisecond) if len(channel) > 0 { t.Fatalf("function was called more than once") } } // This test: // // 1 - Sends debounce requests when test starts // 2 - Calls refreshNow() before the timer elapsed (which stops the timer) about 1 second after test starts // 3 - Sends more debounce requests (which resets the timer with a 3-second interval) about 2 seconds after test starts // // The end result should be 2 refresh function calls: // // 1 - When refreshNow() is called (1 second after the test starts) // 2 - When the timer elapses after the second "wave" of debounce requests (5 seconds after the test starts) func TestRefreshDebouncer_EventsAfterRefreshNow(t *testing.T) { const numberOfEvents = 10 channel := make(chan int, numberOfEvents) // should never use more than 2 but allow for more to possibly detect bugs fn := func() error { channel <- 0 return nil } beforeEvents := time.Now() wg := sync.WaitGroup{} d := NewRefreshDebouncer(3*time.Second, fn) defer d.Stop() for i := 0; i < numberOfEvents; i++ { wg.Add(1) go func() { defer wg.Done() d.Debounce() time.Sleep(2000 * time.Millisecond) d.Debounce() }() } go func() { time.Sleep(1 * time.Second) d.RefreshNow() }() wg.Wait() timeoutCh := time.After(1500 * time.Millisecond) // extra 500ms to prevent flakiness select { case <-channel: case <-timeoutCh: t.Fatalf("timeout elapsed without flush function being called after refreshNow()") } afterFunctionCall := time.Now() // use 500ms instead of 1s to avoid timer precision issues if afterFunctionCall.Sub(beforeEvents) < 500*time.Millisecond { t.Fatalf("function was called after %v ms instead of ~1 second", afterFunctionCall.Sub(beforeEvents).Milliseconds()) } timeoutCh = time.After(4 * time.Second) // extra 1s to prevent flakiness select { case <-channel: case <-timeoutCh: t.Fatalf("timeout elapsed without flush function being called after debounce requests") } afterSecondFunctionCall := time.Now() // use 2.5s instead of 3s to avoid timer precision issues if afterSecondFunctionCall.Sub(afterFunctionCall) < 2500*time.Millisecond { t.Fatalf("function was called after %v ms instead of ~3 seconds", afterSecondFunctionCall.Sub(afterFunctionCall).Milliseconds()) } if len(channel) > 0 { t.Fatalf("function was called more than twice") } } func TestErrorBroadcaster_MultipleListeners(t *testing.T) { b := newErrorBroadcaster() defer b.stop() const numberOfListeners = 10 var listeners []<-chan error for i := 0; i < numberOfListeners; i++ { listeners = append(listeners, b.newListener()) } err := errors.New("expected error") wg := sync.WaitGroup{} result := atomic.Value{} for _, listener := range listeners { currentListener := listener wg.Add(1) go func() { defer wg.Done() receivedErr, ok := <-currentListener if !ok { result.Store(errors.New("listener was closed")) } else if receivedErr != err { result.Store(errors.New("expected received error to be the same as the one that was broadcasted")) } }() } wg.Add(1) go func() { defer wg.Done() b.broadcast(err) b.stop() }() wg.Wait() if loadedVal := result.Load(); loadedVal != nil { t.Error(loadedVal.(error).Error()) } } func TestErrorBroadcaster_StopWithoutBroadcast(t *testing.T) { var b = newErrorBroadcaster() defer b.stop() const numberOfListeners = 10 var listeners []<-chan error for i := 0; i < numberOfListeners; i++ { listeners = append(listeners, b.newListener()) } wg := sync.WaitGroup{} result := atomic.Value{} for _, listener := range listeners { currentListener := listener wg.Add(1) go func() { defer wg.Done() // broadcaster stopped, expect listener to be closed _, ok := <-currentListener if ok { result.Store(errors.New("expected listener to be closed")) } }() } wg.Add(1) go func() { defer wg.Done() // call stop without broadcasting anything to current listeners b.stop() }() wg.Wait() if loadedVal := result.Load(); loadedVal != nil { t.Error(loadedVal.(error).Error()) } } ================================================ FILE: debounce/simple_debouncer.go ================================================ package debounce import ( "sync" "sync/atomic" ) // SimpleDebouncer is are tool for queuing immutable functions calls. It provides: // 1. Blocking simultaneous calls // 2. If there is no running call and no waiting call, then the current call go through // 3. If there is running call and no waiting call, then the current call go waiting // 4. If there is running call and waiting call, then the current call are voided type SimpleDebouncer struct { m sync.Mutex count atomic.Int32 } // NewSimpleDebouncer creates a new SimpleDebouncer. func NewSimpleDebouncer() *SimpleDebouncer { return &SimpleDebouncer{} } // Debounce attempts to execute the function if the logic of the SimpleDebouncer allows it. func (d *SimpleDebouncer) Debounce(fn func()) bool { if d.count.Add(1) > 2 { d.count.Add(-1) return false } d.m.Lock() fn() d.count.Add(-1) d.m.Unlock() return true } ================================================ FILE: debounce/simple_debouncer_test.go ================================================ //go:build unit // +build unit package debounce import ( "runtime" "sync" "sync/atomic" "testing" "time" ) // TestSimpleDebouncerRace tests SimpleDebouncer for the fact that it does not allow concurrent writing, reading. func TestSimpleDebouncerRace(t *testing.T) { t.Parallel() operations := 1000 runs := 100 count := 3 d := NewSimpleDebouncer() for r := 0; r < runs; r++ { var counter atomic.Int32 var wg sync.WaitGroup wg.Add(count) results := make([]bool, count) fails := make([]bool, count) for c := range results { result := &results[c] fail := &fails[c] go func() { *result = d.Debounce(func() { for i := 0; i < operations; i++ { if counter.Add(1) != 1 { *fail = true } time.Sleep(time.Microsecond) counter.Add(-1) } }) wg.Done() }() } wg.Wait() // check results finished := 0 for i, done := range results { if done { finished++ } if fails[i] { t.Fatalf("Simultaneous execution detected") } } if finished < 2 { t.Fatalf("In one run should be finished more than 2 `Debounce` method calls, but finished %d", finished) } } } // TestDebouncerExtreme tests SimpleDebouncer in the conditions fast multi `Debounce` method calls and fast execution of the `debounced function`. func TestDebouncerExtreme(t *testing.T) { t.Parallel() type runResult struct { executedN int32 done bool } runs := 10000 count := 20 d := NewSimpleDebouncer() var wg sync.WaitGroup for r := 0; r < runs; r++ { var executionsC atomic.Int32 wg.Add(count) results := make([]runResult, count) for c := range results { result := &results[c] go func() { result.done = d.Debounce(func() { result.executedN = executionsC.Add(1) }) wg.Done() }() } wg.Wait() // check results finished := 0 for _, result := range results { if result.done { if result.executedN == 0 { t.Fatalf("Wrong execution detected: \n%#v", result) } finished++ } } if finished < 2 { t.Fatalf("In one run should be finished more than 2 `Debounce` method calls, but finished %d", finished) } } } // TestSimpleDebouncerCount tests SimpleDebouncer for the fact that it pended only one function call. func TestSimpleDebouncerCount(t *testing.T) { t.Parallel() calls := 10 // Subtracting a one call that will be performed directly (not through goroutines) calls-- d := NewSimpleDebouncer() var prepared, start, done sync.WaitGroup prepared.Add(calls) start.Add(1) done.Add(calls) finished := 0 for c := 0; c < calls; c++ { go func() { prepared.Done() start.Wait() d.Debounce(func() { finished++ }) done.Done() }() } d.Debounce(func() { prepared.Wait() start.Done() finished++ time.Sleep(time.Second) }) done.Wait() // check results if finished != 2 { t.Fatalf("Should be finished 2 `Debounce` method calls, but finished %d", finished) } } // TestDebouncer tests that the debouncer allows only one function to execute at a time func TestSimpleDebouncer(t *testing.T) { t.Parallel() d := NewSimpleDebouncer() var executions int32 startedCh := make(chan struct{}, 1) doneCh := make(chan struct{}, 1) // Function to increment executions fn := func() { <-startedCh // Simulate work atomic.AddInt32(&executions, 1) <-doneCh // Simulate work } t.Run("Case 1", func(t *testing.T) { // Case 1: Normal single execution startedCh <- struct{}{} doneCh <- struct{}{} d.Debounce(fn) // We expect that the function has only executed once due to debouncing if atomic.LoadInt32(&executions) != 1 { t.Errorf("Expected function to be executed only once, but got %d executions", executions) } }) atomic.StoreInt32(&executions, 0) t.Run("Case 2", func(t *testing.T) { // Case 2: Debounce the function multiple times at row when body is started go d.Debounce(fn) startedCh <- struct{}{} // Wait until first call execution started waitTillChannelIsEmpty(startedCh) // Call function twice, due to debounce only one should be executed go d.Debounce(fn) go d.Debounce(fn) // Let first call to complete doneCh <- struct{}{} // Let second call to complete startedCh <- struct{}{} doneCh <- struct{}{} // Make sure second call is completed waitTillChannelIsEmpty(doneCh) // We expect that the function has only executed once due to debouncing if atomic.LoadInt32(&executions) != 2 { t.Errorf("Expected function to be executed twice, but got %d executions", executions) } }) } func waitTillChannelIsEmpty(ch chan struct{}) { for { if len(ch) == 0 { return } runtime.Gosched() } } ================================================ FILE: dial.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "context" "crypto/tls" "fmt" "net" "strconv" "strings" ) // HostDialer allows customizing connection to cluster nodes. type HostDialer interface { // DialHost establishes a connection to the host. // The returned connection must be directly usable for CQL protocol, // specifically DialHost is responsible also for setting up the TLS session if needed. // DialHost should disable write coalescing if the returned net.Conn does not support writev. // As of Go 1.18, only plain TCP connections support writev, TLS sessions should disable coalescing. // You can use WrapTLS helper function if you don't need to override the TLS setup. DialHost(ctx context.Context, host *HostInfo) (*DialedHost, error) } // DialedHost contains information about established connection to a host. type DialedHost struct { // Conn used to communicate with the server. Conn net.Conn // DisableCoalesce disables write coalescing for the Conn. // If true, the effect is the same as if WriteCoalesceWaitTime was configured to 0. DisableCoalesce bool } // defaultHostDialer dials host in a default way. type defaultHostDialer struct { dialer Dialer tlsConfig *tls.Config } func (hd *defaultHostDialer) DialHost(ctx context.Context, host *HostInfo) (*DialedHost, error) { ip := host.ConnectAddress() port := host.Port() if !validIpAddr(ip) { return nil, fmt.Errorf("host missing connect ip address: %v", ip) } else if port == 0 { return nil, fmt.Errorf("host missing port: %v", port) } addr := net.JoinHostPort(ip.String(), strconv.Itoa(port)) translatedInfo := host.getTranslatedConnectionInfo() if translatedInfo != nil { addr = translatedInfo.CQL.ToNetAddr() } conn, err := hd.dialer.DialContext(ctx, "tcp", addr) if err != nil { return nil, err } return WrapTLS(ctx, conn, addr, hd.tlsConfig) } func tlsConfigForAddr(tlsConfig *tls.Config, addr string) *tls.Config { // the TLS config is safe to be reused by connections but it must not // be modified after being used. if !tlsConfig.InsecureSkipVerify && tlsConfig.ServerName == "" { colonPos := strings.LastIndex(addr, ":") if colonPos == -1 { colonPos = len(addr) } hostname := addr[:colonPos] // clone config to avoid modifying the shared one. tlsConfig = tlsConfig.Clone() tlsConfig.ServerName = hostname } return tlsConfig } // WrapTLS optionally wraps a net.Conn connected to addr with the given tlsConfig. // If the tlsConfig is nil, conn is not wrapped into a TLS session, so is insecure. // If the tlsConfig does not have server name set, it is updated based on the default gocql rules. func WrapTLS(ctx context.Context, conn net.Conn, addr string, tlsConfig *tls.Config) (*DialedHost, error) { if tlsConfig != nil { tlsConfig := tlsConfigForAddr(tlsConfig, addr) tconn := tls.Client(conn, tlsConfig) if err := tconn.HandshakeContext(ctx); err != nil { conn.Close() return nil, err } conn = tconn } return &DialedHost{ Conn: conn, DisableCoalesce: tlsConfig != nil, // write coalescing can't use writev when the connection is wrapped. }, nil } ================================================ FILE: dialer/recorder/recorder.go ================================================ package recorder import ( "context" "encoding/json" "fmt" "io" "net" "os" "path" "time" "github.com/gocql/gocql" "github.com/gocql/gocql/dialer" ) func NewRecordDialer(dir string) *RecordDialer { return &RecordDialer{ dir: dir, } } type RecordDialer struct { dir string net.Dialer } func (d *RecordDialer) DialContext(ctx context.Context, network, addr string) (conn net.Conn, err error) { fmt.Println("Dial Context Record Dialer") sourcePort := gocql.ScyllaGetSourcePort(ctx) fmt.Println("Source port: ", sourcePort) dialerWithLocalAddr := d.Dialer dialerWithLocalAddr.LocalAddr, err = net.ResolveTCPAddr(network, fmt.Sprintf(":%d", sourcePort)) if err != nil { fmt.Println(err) return nil, err } conn, err = dialerWithLocalAddr.DialContext(ctx, network, addr) if err != nil { return nil, err } return NewConnectionRecorder(path.Join(d.dir, fmt.Sprintf("%s-%d", addr, sourcePort)), conn) } func NewConnectionRecorder(fname string, conn net.Conn) (net.Conn, error) { fd_writes, err := os.OpenFile(fname+"Writes", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) if err != nil { return nil, err } fd_reads, err2 := os.OpenFile(fname+"Reads", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) if err2 != nil { return nil, err2 } return &ConnectionRecorder{fd_writes: fd_writes, fd_reads: fd_reads, orig: conn, write_record: FrameWriter{new: true}, read_record: FrameWriter{new: true}}, nil } type FrameWriter struct { record dialer.Record to_record int new bool } func (f *FrameWriter) Write(b []byte, n int, file *os.File) (err error) { if f.new { f.to_record = -1 f.record = dialer.Record{} } recorded_ealier := len(f.record.Data) f.record.Data = append(f.record.Data, b[:n]...) if f.to_record == -1 && len(f.record.Data) >= 9 { f.to_record = 9 + int(f.record.Data[5+0])<<24 | int(f.record.Data[6])<<16 | int(f.record.Data[7])<<8 | int(f.record.Data[8]) - recorded_ealier f.record.StreamID = int(f.record.Data[2])<<8 | int(f.record.Data[3]) } else if f.to_record == -1 { return err } f.to_record = f.to_record - n if f.to_record <= 0 { f.new = true // Write JSON record to file jsonData, marshalErr := json.Marshal(f.record) if marshalErr != nil { return fmt.Errorf("failed to encode JSON record: %w", marshalErr) } _, writeErr := file.Write(append(jsonData, '\n')) if writeErr != nil { return fmt.Errorf("failed to record: %w", writeErr) } } return err } type ConnectionRecorder struct { fd_writes *os.File fd_reads *os.File orig net.Conn read_record FrameWriter write_record FrameWriter } func (c *ConnectionRecorder) Read(b []byte) (n int, err error) { n, err = c.orig.Read(b) if err != nil && err != io.EOF { return n, err } return n, c.read_record.Write(b, n, c.fd_reads) } func (c *ConnectionRecorder) Write(b []byte) (n int, err error) { n, err = c.orig.Write(b) if err != nil { return n, err } return n, c.write_record.Write(b, n, c.fd_writes) } func (c ConnectionRecorder) Close() error { if err := c.fd_writes.Close(); err != nil { return fmt.Errorf("failed to close the file: %w", err) } if err := c.fd_reads.Close(); err != nil { return fmt.Errorf("failed to close the file: %w", err) } return c.orig.Close() } func (c ConnectionRecorder) LocalAddr() net.Addr { return c.orig.LocalAddr() } func (c ConnectionRecorder) RemoteAddr() net.Addr { return c.orig.RemoteAddr() } func (c ConnectionRecorder) SetDeadline(t time.Time) error { return c.orig.SetDeadline(t) } func (c ConnectionRecorder) SetReadDeadline(t time.Time) error { return c.orig.SetReadDeadline(t) } func (c ConnectionRecorder) SetWriteDeadline(t time.Time) error { return c.orig.SetWriteDeadline(t) } ================================================ FILE: dialer/replayer/replayer.go ================================================ package replayer import ( "bufio" "context" "encoding/json" "fmt" "io" "net" "os" "path" "time" "github.com/gocql/gocql" "github.com/gocql/gocql/dialer" ) func NewReplayDialer(dir string) *ReplayDialer { return &ReplayDialer{ dir: dir, } } type ReplayDialer struct { dir string net.Dialer } func (d *ReplayDialer) DialContext(ctx context.Context, network, addr string) (conn net.Conn, err error) { sourcePort := gocql.ScyllaGetSourcePort(ctx) return NewConnectionReplayer(path.Join(d.dir, fmt.Sprintf("%s-%d", addr, sourcePort))) } func NewConnectionReplayer(fname string) (net.Conn, error) { frames, err := loadResponseFramesFromFiles(fname+"Reads", fname+"Writes") if err != nil { return nil, err } return &ConnectionReplayer{frames: frames, frameIdsToReplay: []int{}, streamIdsToReplay: []int{}, frameIdx: 0, frameResponsePosition: 0, gotRequest: make(chan struct{}, 1)}, nil } type ConnectionReplayer struct { gotRequest chan struct{} frames []*FrameRecorded frameIdsToReplay []int streamIdsToReplay []int frameIdx int frameResponsePosition int closed bool } func (c *ConnectionReplayer) frameStreamID() int { return c.streamIdsToReplay[c.frameIdx] } func (c *ConnectionReplayer) getPendingFrame() *FrameRecorded { if c.frameIdx < 0 || c.frameIdx >= len(c.frameIdsToReplay) { return nil } frameId := c.frameIdsToReplay[c.frameIdx] if frameId < 0 || frameId >= len(c.frames) { return nil } return c.frames[frameId] } func (c *ConnectionReplayer) pushStreamIDToReplay(b []byte, idx int) { if b[0] > 0x02 { c.streamIdsToReplay = append(c.streamIdsToReplay, int(b[2])<<8|int(b[3])) } else { c.streamIdsToReplay = append(c.streamIdsToReplay, int(b[2])) } c.frameIdsToReplay = append(c.frameIdsToReplay, idx) select { case c.gotRequest <- struct{}{}: default: } } func replaceFrameStreamID(b []byte, stream int) { if b[0] > 0x02 { b[2] = byte(stream >> 8) b[3] = byte(stream) } else { b[2] = byte(stream) } } func (c *ConnectionReplayer) Read(b []byte) (n int, err error) { frame := c.getPendingFrame() for frame == nil { <-c.gotRequest frame = c.getPendingFrame() } if c.Closed() { return 0, io.EOF } response := frame.Response[c.frameResponsePosition:] if len(b) < len(response) { copy(b, response[:len(b)]) c.frameResponsePosition = c.frameResponsePosition + len(b) return len(b), err } copy(b, response) if c.frameResponsePosition == 0 { replaceFrameStreamID(b, c.frameStreamID()) } c.frameIdx = c.frameIdx + 1 c.frameResponsePosition = 0 return len(response), err } func (c *ConnectionReplayer) Write(b []byte) (n int, err error) { writeHash := dialer.GetFrameHash(b) for i, q := range c.frames { if q.Hash == writeHash { c.pushStreamIDToReplay(b, i) return len(b), nil } } panic(fmt.Errorf("unable to find a response to replay")) } func (c *ConnectionReplayer) Close() error { close(c.gotRequest) c.closed = true return nil } func (c *ConnectionReplayer) Closed() bool { return c.closed } type MockAddr struct { network string address string } func (m *MockAddr) Network() string { return m.network } func (m *MockAddr) String() string { return m.address } func (c ConnectionReplayer) LocalAddr() net.Addr { return &MockAddr{ network: "tcp", address: "10.0.0.1:54321", } } func (c ConnectionReplayer) RemoteAddr() net.Addr { return &MockAddr{ network: "tcp", address: "192.168.1.100:12345", } } func (c ConnectionReplayer) SetDeadline(t time.Time) error { return nil } func (c ConnectionReplayer) SetReadDeadline(t time.Time) error { return nil } func (c ConnectionReplayer) SetWriteDeadline(t time.Time) error { return nil } func loadFramesFromFile(filename string) (map[int]dialer.Record, error) { records := make(map[int]dialer.Record) file, err := os.Open(filename) if err != nil { return nil, fmt.Errorf("failed to open file %s: %w", filename, err) } defer file.Close() scanner := bufio.NewScanner(file) for scanner.Scan() { var record dialer.Record if err := json.Unmarshal(scanner.Bytes(), &record); err != nil { fmt.Printf("Error decoding JSON in %s: %s\n", filename, err) continue } records[record.StreamID] = record } if err := scanner.Err(); err != nil { return nil, fmt.Errorf("error reading file %s: %w", filename, err) } return records, nil } func loadResponseFramesFromFiles(read_file, write_file string) ([]*FrameRecorded, error) { read_records, err := loadFramesFromFile(read_file) if err != nil { return nil, err } write_records, err := loadFramesFromFile(write_file) if err != nil { return nil, err } var frames = []*FrameRecorded{} for streamID, record1 := range read_records { if record2, exists := write_records[streamID]; exists { frames = append(frames, &FrameRecorded{Response: record1.Data, Hash: dialer.GetFrameHash(record2.Data)}) } } return frames, nil } type FrameRecorded struct { Response []byte Hash int64 } ================================================ FILE: dialer/utils.go ================================================ package dialer import ( frm "github.com/gocql/gocql/internal/frame" "github.com/gocql/gocql/internal/murmur" ) type Record struct { Data []byte `json:"data"` StreamID int `json:"stream_id"` } type frameOp byte const ( // header ops opError frameOp = 0x00 opStartup frameOp = 0x01 opReady frameOp = 0x02 opAuthenticate frameOp = 0x03 opOptions frameOp = 0x05 opSupported frameOp = 0x06 opQuery frameOp = 0x07 opResult frameOp = 0x08 opPrepare frameOp = 0x09 opExecute frameOp = 0x0A opRegister frameOp = 0x0B opEvent frameOp = 0x0C opBatch frameOp = 0x0D opAuthChallenge frameOp = 0x0E opAuthResponse frameOp = 0x0F opAuthSuccess frameOp = 0x10 ) func addBytes(frame []byte, index int) int { bytesLength := int(frame[index+0])<<24 | int(frame[index+1])<<16 | int(frame[index+2])<<8 | int(frame[index+3]) index = index + 4 if bytesLength > 0 { index = index + bytesLength } return index } func addQueryParams(frame []byte, index int) int { //use consistency index = index + 2 //use query flags var flags byte if frame[0] > 0x04 { flags = frame[index+3] index = index + 4 } else { flags = frame[index] index = index + 1 } names := false // protoV3 specific things if frame[0] > 0x02 { if flags&frm.FlagValues == frm.FlagValues && flags&frm.FlagWithNameValues == frm.FlagWithNameValues { names = true } } if flags&frm.FlagValues == frm.FlagValues { valuesLen := int(frame[index])<<8 | int(frame[index+1]) index = index + 2 for i := 0; i < valuesLen; i++ { if names { stringLenght := int(frame[index])<<8 | int(frame[index+1]) index = index + 2 + stringLenght } index = addBytes(frame, index) } } if flags&frm.FlagPageSize == frm.FlagPageSize { index = index + 4 } if flags&frm.FlagWithPagingState == frm.FlagWithPagingState { index = addBytes(frame, index) } if flags&frm.FlagWithSerialConsistency == frm.FlagWithSerialConsistency { index = index + 2 } // do not use timelaps and keyspace return index } func addHeader(index int) int { return index + 8 } func addCustomPayload(frame []byte, index int, p int) int { customPayloadLenght := int(frame[8+p])<<8 | int(frame[9+p]) if customPayloadLenght > 0 { index = index + 2 } for i := 0; i < customPayloadLenght; i++ { stringLenght := int(frame[index])<<8 | int(frame[index+1]) index = index + 2 + stringLenght index = addBytes(frame, index) } return index } func GetFrameHash(frame []byte) int64 { var p int if frame[0] > 0x02 { p = 1 streamID1 := frame[2] streamID2 := frame[3] defer func() { frame[2] = streamID1 frame[3] = streamID2 }() frame[2] = byte('0') frame[3] = byte('0') } else { p = 0 streamID1 := frame[2] defer func() { frame[2] = streamID1 }() frame[2] = byte('0') } switch frame[3+p] { case byte(opStartup): return murmur.Murmur3H1(frame[:8+p]) case byte(opPrepare): return murmur.Murmur3H1(frame) case byte(opAuthResponse): return murmur.Murmur3H1(frame) case byte(opQuery): index := addHeader(p) if frame[1]&frm.FlagCustomPayload == frm.FlagCustomPayload { index = addCustomPayload(frame, index, p) } endIndex := index endIndex = addQueryParams(frame, endIndex) return murmur.Murmur3H1(frame[index:endIndex]) case byte(opExecute): index := addHeader(p) if frame[1]&frm.FlagCustomPayload == frm.FlagCustomPayload { index = addCustomPayload(frame, index, p) } endIndex := index preparedIDLen := int(frame[index])<<8 | int(frame[index+1]) endIndex = endIndex + 2 + preparedIDLen if frame[0] > 0x01 { endIndex = addQueryParams(frame, endIndex) } else { valuesLen := int(frame[index])<<8 | int(frame[index+1]) index = index + 2 for i := 0; i < valuesLen; i++ { index = addBytes(frame, index) } index = index + 2 } return murmur.Murmur3H1(frame[index:endIndex]) case byte(opBatch): return murmur.Murmur3H1(frame) case byte(opOptions): return murmur.Murmur3H1(frame) case byte(opRegister): return murmur.Murmur3H1(frame) default: return murmur.Murmur3H1(frame) } } ================================================ FILE: dns_test.go ================================================ //go:build integration // +build integration package gocql import ( "errors" "fmt" "net" "sync" "testing" ) type mockDNSResolver struct { lock sync.RWMutex data map[string][]net.IP } func newMockDNSResolver() *mockDNSResolver { return &mockDNSResolver{ data: make(map[string][]net.IP), lock: sync.RWMutex{}, } } func (r *mockDNSResolver) LookupIP(host string) ([]net.IP, error) { r.lock.RLock() defer r.lock.RUnlock() ips, _ := r.data[host] if len(ips) == 0 { return nil, &net.DNSError{Err: errors.New("no IP addresses").Error(), Name: host} } return ips, nil } func (r *mockDNSResolver) Update(host string, ips ...net.IP) { r.lock.Lock() r.data[host] = ips defer r.lock.Unlock() } func (r *mockDNSResolver) Delete(hosts ...string) { r.lock.Lock() for _, host := range hosts { delete(r.data, host) } defer r.lock.Unlock() } func MustIP(ip string) net.IP { out := net.ParseIP(ip) if out == nil { panic("failed to parse IP: " + ip) } return out } func TestDNS(t *testing.T) { t.Parallel() checkIfSessionWorking := func(t *testing.T, cluster *ClusterConfig, hosts []string) { s, err := cluster.CreateSession() if err != nil { t.Fatalf("failed to create session: %v", err) } defer s.Close() err = s.refreshRingNow() if err != nil { t.Fatalf("failed to refresh ring: %v", err) } err = s.Query("select * from system.peers").Exec() if err != nil { t.Fatalf("failed to execute query: %v", err) } ringHosts := s.hostSource.getHostsList() if len(ringHosts) != len(hosts) { t.Fatalf("wrong number of hosts: got %d, want %d", len(ringHosts), len(hosts)) } } OneDNSPerNode := func(c *ClusterConfig) { r := newMockDNSResolver() var dnsRecords []string for id, host := range c.Hosts { dns := fmt.Sprintf("node%d.cluster.local", id+1) dnsRecords = append(dnsRecords, dns) r.Update(dns, MustIP(host)) } c.DNSResolver = r c.Hosts = dnsRecords } OneDNSPerCluster := func(c *ClusterConfig) { r := newMockDNSResolver() var hostIPs []net.IP for _, host := range c.Hosts { hostIPs = append(hostIPs, MustIP(host)) } r.Update("cluster.local", hostIPs...) c.DNSResolver = r c.Hosts = []string{"cluster.local"} } OneDNSPerClusterFirstBroken := func(c *ClusterConfig) { r := newMockDNSResolver() var hostIPs []net.IP for _, host := range c.Hosts { hostIPs = append(hostIPs, MustIP(host)) } hostIPs[0] = MustIP("0.0.0.0") r.Update("cluster.local", hostIPs...) c.DNSResolver = r c.Hosts = []string{"cluster.local"} } WithAddressTranslator := func(c *ClusterConfig) { var toAddresses []net.IP var fromAddresses []net.IP var clusterHosts []string for _, host := range c.Hosts { ip := MustIP(host) var fromAddress net.IP if ip.To4().String() == ip.String() { ip = ip.To4() fromAddress = net.IPv4(ip[0], ip[1], ip[2]+1, ip[3]) } else { fromAddress = net.IP{ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7], ip[8], ip[9], ip[10], ip[11], ip[12] + 1, ip[13], ip[14], ip[15]} } toAddresses = append(toAddresses, ip) fromAddresses = append(fromAddresses, fromAddress) clusterHosts = append(clusterHosts, fromAddress.String()) } c.AddressTranslator = AddressTranslatorFunc(func(addr net.IP, port int) (net.IP, int) { for id, host := range fromAddresses { if host.Equal(addr) { return toAddresses[id], port } } for _, host := range toAddresses { if host.Equal(addr) { return addr, port } } panic("failed to translate address") }) c.Hosts = clusterHosts } testCases := []struct { name string clusterMods []func(*ClusterConfig) }{ { name: "OneDNSPerNode", clusterMods: []func(*ClusterConfig){OneDNSPerNode}, }, { name: "OneDNSPerCluster", clusterMods: []func(*ClusterConfig){OneDNSPerCluster}, }, { name: "AddressTranslator+OneDNSPerNode", clusterMods: []func(*ClusterConfig){WithAddressTranslator, OneDNSPerNode}, }, { name: "AddressTranslator+OneDNSPerCluster", clusterMods: []func(*ClusterConfig){WithAddressTranslator, OneDNSPerCluster}, }, { name: "OneDNSPerClusterFirstBroken", clusterMods: []func(*ClusterConfig){OneDNSPerClusterFirstBroken}, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { cluster := createCluster(tc.clusterMods...) checkIfSessionWorking(t, cluster, getClusterHosts()) }) } } ================================================ FILE: doc.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ // Package gocql implements a fast and robust Cassandra driver for the // Go programming language. // // # Connecting to the cluster // // Pass a list of initial node IP addresses to NewCluster to create a new cluster configuration: // // cluster := gocql.NewCluster("192.168.1.1", "192.168.1.2", "192.168.1.3") // // Port can be specified as part of the address, the above is equivalent to: // // cluster := gocql.NewCluster("192.168.1.1:9042", "192.168.1.2:9042", "192.168.1.3:9042") // // It is recommended to use the value set in the Cassandra config for broadcast_address or listen_address, // an IP address not a domain name. This is because events from Cassandra will use the configured IP // address, which is used to index connected hosts. If the domain name specified resolves to more than 1 IP address // then the driver may connect multiple times to the same host, and will not mark the node being down or up from events. // // Then you can customize more options (see ClusterConfig): // // cluster.Keyspace = "example" // cluster.Consistency = gocql.Quorum // cluster.ProtoVersion = protoVersion4 // // The driver tries to automatically detect the protocol version to use if not set, but you might want to set the // protocol version explicitly, as it's not defined which version will be used in certain situations (for example // during upgrade of the cluster when some of the nodes support different set of protocol versions than other nodes). // // The driver advertises the module name and version in the STARTUP message, so servers are able to detect the version. // If you use replace directive in go.mod, the driver will send information about the replacement module instead. // // When ready, create a session from the configuration. Don't forget to Close the session once you are done with it: // // session, err := cluster.CreateSession() // if err != nil { // return err // } // defer session.Close() // // # Authentication // // CQL protocol uses a SASL-based authentication mechanism and so consists of an exchange of server challenges and // client response pairs. The details of the exchanged messages depend on the authenticator used. // // To use authentication, set ClusterConfig.Authenticator or ClusterConfig.AuthProvider. // // PasswordAuthenticator is provided to use for username/password authentication: // // cluster := gocql.NewCluster("192.168.1.1", "192.168.1.2", "192.168.1.3") // cluster.Authenticator = gocql.PasswordAuthenticator{ // Username: "user", // Password: "password" // } // session, err := cluster.CreateSession() // if err != nil { // return err // } // defer session.Close() // // By default, PasswordAuthenticator will attempt to authenticate regardless of what implementation the server returns // in its AUTHENTICATE message as its authenticator, (e.g. org.apache.cassandra.auth.PasswordAuthenticator). If you // wish to restrict this you may use PasswordAuthenticator.AllowedAuthenticators: // // cluster.Authenticator = gocql.PasswordAuthenticator { // Username: "user", // Password: "password" // AllowedAuthenticators: []string{"org.apache.cassandra.auth.PasswordAuthenticator"}, // } // // # Transport layer security // // It is possible to secure traffic between the client and server with TLS. // // To use TLS, set the ClusterConfig.SslOpts field. SslOptions embeds *tls.Config so you can set that directly. // There are also helpers to load keys/certificates from files. // // Warning: Due to historical reasons, the SslOptions is insecure by default, so you need to set EnableHostVerification // to true if no Config is set. Most users should set SslOptions.Config to a *tls.Config. // SslOptions and Config.InsecureSkipVerify interact as follows: // // Config.InsecureSkipVerify | EnableHostVerification | Result // Config is nil | false | do not verify host // Config is nil | true | verify host // false | false | verify host // true | false | do not verify host // false | true | verify host // true | true | verify host // // For example: // // cluster := gocql.NewCluster("192.168.1.1", "192.168.1.2", "192.168.1.3") // cluster.SslOpts = &gocql.SslOptions{ // EnableHostVerification: true, // } // session, err := cluster.CreateSession() // if err != nil { // return err // } // defer session.Close() // // # Data-center awareness and query routing // // To route queries to local DC first, use DCAwareRoundRobinPolicy. For example, if the datacenter you // want to primarily connect is called dc1 (as configured in the database): // // cluster := gocql.NewCluster("192.168.1.1", "192.168.1.2", "192.168.1.3") // cluster.PoolConfig.HostSelectionPolicy = gocql.DCAwareRoundRobinPolicy("dc1") // // The driver can route queries to nodes that hold data replicas based on partition key (preferring local DC). // // cluster := gocql.NewCluster("192.168.1.1", "192.168.1.2", "192.168.1.3") // cluster.PoolConfig.HostSelectionPolicy = gocql.TokenAwareHostPolicy(gocql.DCAwareRoundRobinPolicy("dc1")) // // Note that TokenAwareHostPolicy can take options such as gocql.ShuffleReplicas and gocql.NonLocalReplicasFallback. // // We recommend running with a token aware host policy in production for maximum performance. // // The driver can only use token-aware routing for queries where all partition key columns are query parameters. // For example, instead of // // session.Query("select value from mytable where pk1 = 'abc' AND pk2 = ?", "def") // // use // // session.Query("select value from mytable where pk1 = ? AND pk2 = ?", "abc", "def") // // # Rack-level awareness // // The DCAwareRoundRobinPolicy can be replaced with RackAwareRoundRobinPolicy, which takes two parameters, datacenter and rack. // // Instead of dividing hosts with two tiers (local datacenter and remote datacenters) it divides hosts into three // (the local rack, the rest of the local datacenter, and everything else). // // For example, to route queries to a specific rack within a datacenter: // // cluster := gocql.NewCluster("192.168.1.1", "192.168.1.2", "192.168.1.3") // cluster.PoolConfig.HostSelectionPolicy = gocql.RackAwareRoundRobinPolicy("dc1", "rack1") // // RackAwareRoundRobinPolicy can be combined with TokenAwareHostPolicy in the same way as DCAwareRoundRobinPolicy: // // cluster := gocql.NewCluster("192.168.1.1", "192.168.1.2", "192.168.1.3") // cluster.PoolConfig.HostSelectionPolicy = gocql.TokenAwareHostPolicy(gocql.RackAwareRoundRobinPolicy("dc1", "rack1")) // // # AWS-specific considerations // // When using rack-aware policies with AWS, note that Availability Zone (AZ) names like "us-east-1a" are not consistent // between different AWS accounts. The same physical AZ may have different names in different accounts. // // For consistent rack-aware routing in AWS, you should use AZ IDs instead of AZ names. AZ IDs (e.g., "use1-az1") are // consistent identifiers across AWS accounts for the same physical location. // // To configure your Cassandra or ScyllaDB nodes with AZ IDs, you can retrieve the AZ ID using AWS CLI or APIs. // For more information, see AWS documentation on AZ IDs: https://docs.aws.amazon.com/ram/latest/userguide/working-with-az-ids.html // // Example configuration for AWS using AZ IDs: // // cluster := gocql.NewCluster("192.168.1.1", "192.168.1.2", "192.168.1.3") // cluster.PoolConfig.HostSelectionPolicy = gocql.RackAwareRoundRobinPolicy("us-east-1", "use1-az1") // // # Executing queries // // Create queries with Session.Query. Query values must not be reused between different executions and must not be // modified after starting execution of the query. // // To execute a query without reading results, use Query.Exec: // // err := session.Query(`INSERT INTO tweet (timeline, id, text) VALUES (?, ?, ?)`, // "me", gocql.TimeUUID(), "hello world").WithContext(ctx).Exec() // // Single row can be read by calling Query.Scan: // // err := session.Query(`SELECT id, text FROM tweet WHERE timeline = ? LIMIT 1`, // "me").WithContext(ctx).Consistency(gocql.One).Scan(&id, &text) // // Multiple rows can be read using Iter.Scanner: // // scanner := session.Query(`SELECT id, text FROM tweet WHERE timeline = ?`, // "me").WithContext(ctx).Iter().Scanner() // for scanner.Next() { // var ( // id gocql.UUID // text string // ) // err = scanner.Scan(&id, &text) // if err != nil { // log.Fatal(err) // } // fmt.Println("Tweet:", id, text) // } // // scanner.Err() closes the iterator, so scanner nor iter should be used afterwards. // if err := scanner.Err(); err != nil { // log.Fatal(err) // } // // See Example for complete example. // // # Prepared statements // // The driver automatically prepares DML queries (SELECT/INSERT/UPDATE/DELETE/BATCH statements) and maintains a cache // of prepared statements. // CQL protocol does not support preparing other query types. // // When using CQL protocol >= 4, it is possible to use gocql.UnsetValue as the bound value of a column. // This will cause the database to ignore writing the column. // The main advantage is the ability to keep the same prepared statement even when you don't // want to update some fields, where before you needed to make another prepared statement. // // # Executing multiple queries concurrently // // Session is safe to use from multiple goroutines, so to execute multiple concurrent queries, just execute them // from several worker goroutines. Gocql provides synchronously-looking API (as recommended for Go APIs) and the queries // are executed asynchronously at the protocol level. // // results := make(chan error, 2) // go func() { // results <- session.Query(`INSERT INTO tweet (timeline, id, text) VALUES (?, ?, ?)`, // "me", gocql.TimeUUID(), "hello world 1").Exec() // }() // go func() { // results <- session.Query(`INSERT INTO tweet (timeline, id, text) VALUES (?, ?, ?)`, // "me", gocql.TimeUUID(), "hello world 2").Exec() // }() // // # Nulls // // Null values are are unmarshalled as zero value of the type. If you need to distinguish for example between text // column being null and empty string, you can unmarshal into *string variable instead of string. // // var text *string // err := scanner.Scan(&text) // if err != nil { // // handle error // } // if text != nil { // // not null // } // else { // // null // } // // See Example_nulls for full example. // // # Reusing slices // // The driver reuses backing memory of slices when unmarshalling. This is an optimization so that a buffer does not // need to be allocated for every processed row. However, you need to be careful when storing the slices to other // memory structures. // // scanner := session.Query(`SELECT myints FROM table WHERE pk = ?`, "key").WithContext(ctx).Iter().Scanner() // var myInts []int // for scanner.Next() { // // This scan reuses backing store of myInts for each row. // err = scanner.Scan(&myInts) // if err != nil { // log.Fatal(err) // } // } // // When you want to save the data for later use, pass a new slice every time. A common pattern is to declare the // slice variable within the scanner loop: // // scanner := session.Query(`SELECT myints FROM table WHERE pk = ?`, "key").WithContext(ctx).Iter().Scanner() // for scanner.Next() { // var myInts []int // // This scan always gets pointer to fresh myInts slice, so does not reuse memory. // err = scanner.Scan(&myInts) // if err != nil { // log.Fatal(err) // } // } // // # Paging // // The driver supports paging of results with automatic prefetch, see ClusterConfig.PageSize, Session.SetPrefetch, // Query.PageSize, and Query.Prefetch. // // It is also possible to control the paging manually with Query.PageState (this disables automatic prefetch). // Manual paging is useful if you want to store the page state externally, for example in a URL to allow users // browse pages in a result. You might want to sign/encrypt the paging state when exposing it externally since // it contains data from primary keys. // // Paging state is specific to the CQL protocol version and the exact query used. It is meant as opaque state that // should not be modified. If you send paging state from different query or protocol version, then the behaviour // is not defined (you might get unexpected results or an error from the server). For example, do not send paging state // returned by node using protocol version 3 to a node using protocol version 4. Also, when using protocol version 4, // paging state between Cassandra 2.2 and 3.0 is incompatible (https://issues.apache.org/jira/browse/CASSANDRA-10880). // // The driver does not check whether the paging state is from the same protocol version/statement. // You might want to validate yourself as this could be a problem if you store paging state externally. // For example, if you store paging state in a URL, the URLs might become broken when you upgrade your cluster. // // Call Query.PageState(nil) to fetch just the first page of the query results. Pass the page state returned by // Iter.PageState to Query.PageState of a subsequent query to get the next page. If the length of slice returned // by Iter.PageState is zero, there are no more pages available (or an error occurred). // // Using too low values of PageSize will negatively affect performance, a value below 100 is probably too low. // While Cassandra returns exactly PageSize items (except for last page) in a page currently, the protocol authors // explicitly reserved the right to return smaller or larger amount of items in a page for performance reasons, so don't // rely on the page having the exact count of items. // // See Example_paging for an example of manual paging. // // # Dynamic list of columns // // There are certain situations when you don't know the list of columns in advance, mainly when the query is supplied // by the user. Iter.Columns, Iter.RowData, Iter.MapScan and Iter.SliceMap can be used to handle this case. // // See Example_dynamicColumns. // // # Batches // // The CQL protocol supports sending batches of DML statements (INSERT/UPDATE/DELETE) and so does gocql. // Use Session.Batch to create a new batch and then fill-in details of individual queries. // Then execute the batch with Session.ExecuteBatch. // // Logged batches ensure atomicity, either all or none of the operations in the batch will succeed, but they have // overhead to ensure this property. // Unlogged batches don't have the overhead of logged batches, but don't guarantee atomicity. // Updates of counters are handled specially by Cassandra so batches of counter updates have to use CounterBatch type. // A counter batch can only contain statements to update counters. // // For unlogged batches it is recommended to send only single-partition batches (i.e. all statements in the batch should // involve only a single partition). // Multi-partition batch needs to be split by the coordinator node and re-sent to // correct nodes. // With single-partition batches you can send the batch directly to the node for the partition without incurring the // additional network hop. // // It is also possible to pass entire BEGIN BATCH .. APPLY BATCH statement to Query.Exec. // There are differences how those are executed. // BEGIN BATCH statement passed to Query.Exec is prepared as a whole in a single statement. // Session.ExecuteBatch prepares individual statements in the batch. // If you have variable-length batches using the same statement, using Session.ExecuteBatch is more efficient. // // See Example_batch for an example. // // # Lightweight transactions // // Query.ScanCAS or Query.MapScanCAS can be used to execute a single-statement lightweight transaction (an // INSERT/UPDATE .. IF statement) and reading its result. See example for Query.MapScanCAS. // // Multiple-statement lightweight transactions can be executed as a logged batch that contains at least one conditional // statement. All the conditions must return true for the batch to be applied. You can use Session.ExecuteBatchCAS and // Session.MapExecuteBatchCAS when executing the batch to learn about the result of the LWT. See example for // Session.MapExecuteBatchCAS. // // # Retries and speculative execution // // Queries can be marked as idempotent. Marking the query as idempotent tells the driver that the query can be executed // multiple times without affecting its result. Non-idempotent queries are not eligible for retrying nor speculative // execution. // // Idempotent queries are retried in case of errors based on the configured RetryPolicy. // If the query is LWT and the configured RetryPolicy additionally implements LWTRetryPolicy // interface, then the policy will be cast to LWTRetryPolicy and used this way. // // Queries can be retried even before they fail by setting a SpeculativeExecutionPolicy. The policy can // cause the driver to retry on a different node if the query is taking longer than a specified delay even before the // driver receives an error or timeout from the server. When a query is speculatively executed, the original execution // is still executing. The two parallel executions of the query race to return a result, the first received result will // be returned. // // # User-defined types // // UDTs can be mapped (un)marshaled from/to map[string]any a Go struct (or a type implementing // UDTUnmarshaler, UDTMarshaler, Unmarshaler or Marshaler interfaces). // // For structs, cql tag can be used to specify the CQL field name to be mapped to a struct field: // // type MyUDT struct { // FieldA int32 `cql:"a"` // FieldB string `cql:"b"` // } // // See Example_userDefinedTypesMap, Example_userDefinedTypesStruct, ExampleUDTMarshaler, ExampleUDTUnmarshaler. // // # Metrics and tracing // // It is possible to provide observer implementations that could be used to gather metrics: // // - QueryObserver for monitoring individual queries. // - BatchObserver for monitoring batch queries. // - ConnectObserver for monitoring new connections from the driver to the database. // - FrameHeaderObserver for monitoring individual protocol frames. // // CQL protocol also supports tracing of queries. When enabled, the database will write information about // internal events that happened during execution of the query. You can use Query.Trace to request tracing and receive // the session ID that the database used to store the trace information in system_traces.sessions and // system_traces.events tables. NewTraceWriter returns an implementation of Tracer that writes the events to a writer. // Gathering trace information might be essential for debugging and optimizing queries, but writing traces has overhead, // so this feature should not be used on production systems with very high load unless you know what you are doing. // There is also a new implementation of Tracer - TracerEnhanced, that is intended to be more reliable and convinient to use. // It has a funcionality to check if trace is ready to be extracted and only actually gets it if requested which makes // the impact on a performance smaller. package gocql // import "github.com/gocql/gocql" ================================================ FILE: docs/Makefile ================================================ SHELL=bash # Global variables # You can set these variables from the command line. UV = uv SPHINXOPTS = -j auto SPHINXBUILD = $(UV) run sphinx-build PAPER = BUILDDIR = _build SOURCEDIR = source # Internal variables PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(SOURCEDIR) TESTSPHINXOPTS = $(ALLSPHINXOPTS) -W --keep-going .PHONY: all all: dirhtml # Setup commands .PHONY: setupenv setupenv: @while IFS= read -r line; do \ if [[ "$${line}" =~ ^[[:space:]]*requires[[:space:]]*= ]]; then\ content="$${line#*=}";\ content="$${content//[[:space:]]/}";\ content="$${content#[}";\ content="$${content%]}";\ IFS=',' read -ra items <<< "$$content";\ for item in "$${items[@]}"; do\ item="$${item%\"}";\ item="$${item#\"}";\ pip install -q uv;\ done;\ fi;\ done < pyproject.toml .PHONY: setup setup: $(UV) sync .PHONY: update update: $(UV) sync --upgrade # Clean commands .PHONY: pristine pristine: clean git clean -dfX .PHONY: clean clean: rm -rf $(BUILDDIR)/* # Generate output commands .PHONY: dirhtml dirhtml: setup $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." .PHONY: singlehtml singlehtml: setup $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." .PHONY: epub epub: setup $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." .PHONY: epub3 epub3: setup $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3 @echo @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3." .PHONY: multiversion multiversion: setup $(UV) run sphinx-multiversion source $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." .PHONY: redirects redirects: setup $(UV) run redirects-cli fromfile --yaml-file _utils/redirects.yaml --output-dir $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." # Preview commands .PHONY: preview preview: setup $(UV) run sphinx-autobuild -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml --port 5500 .PHONY: multiversionpreview multiversionpreview: multiversion $(UV) run python -m http.server 5500 --directory $(BUILDDIR)/dirhtml # Test commands .PHONY: test test: setup $(SPHINXBUILD) -b dirhtml $(TESTSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." .PHONY: linkcheck linkcheck: setup $(SPHINXBUILD) -b linkcheck $(SOURCEDIR) $(BUILDDIR)/linkcheck ================================================ FILE: docs/_utils/redirects.yaml ================================================ ### a dictionary of redirects #old path: new path # # removing redirection html script files # test: / # /stable/test-redirect.html: /stable/index.html ================================================ FILE: docs/pyproject.toml ================================================ [project] name = "sphinx-docs" description = "ScyllaDB Documentation" version = "0.1.0" requires-python = ">=3.11" dependencies = [ "pygments>=2.19.2", "sphinx-scylladb-theme>=1.9.1", "myst-parser>=5.0.0", "sphinx-autobuild>=2025.4.8", "sphinx>=9.0", "sphinx-multiversion-scylla>=0.3.7", "sphinx-sitemap>=2.9.0", "redirects_cli>=0.1.3", ] ================================================ FILE: docs/source/conf.py ================================================ # -*- coding: utf-8 -*- import warnings from datetime import date from sphinx_scylladb_theme.utils import multiversion_regex_builder # -- Global variables # Builds documentation for the following tags and branches. TAGS = [] BRANCHES = [ "master", ] # Sets the latest version. LATEST_VERSION = "master" # Set which versions are not released yet. UNSTABLE_VERSIONS = [] # Set which versions are deprecated DEPRECATED_VERSIONS = [""] # Sets custom build. FLAGS = [] # -- General configuration ------------------------------------------ # Add any Sphinx extension module names here, as strings. extensions = [ "sphinx.ext.autodoc", "sphinx.ext.todo", "sphinx.ext.mathjax", "sphinx.ext.githubpages", "sphinx.ext.extlinks", "sphinx_sitemap", "sphinx_scylladb_theme", "sphinx_multiversion", # optional "myst_parser", # optional ] # The suffix(es) of source filenames. source_suffix = [".rst", ".md"] # The master toctree document. master_doc = "index" # General information about the project. project = "ScyllaDB gocql driver" copyright = str(date.today().year) + " ScyllaDB" author = "ScyllaDB Project Contributors" # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", "**/_partials", ".venv"] # The name of the Pygments (syntax highlighting) style to use. pygments_style = "sphinx" # List of substitutions rst_prolog = """ .. |rst| replace:: restructuredText """ # -- Options for myst parser ---------------------------------------- myst_enable_extensions = ["colon_fence"] # -- Options for not found extension -------------------------------- # Template used to render the 404.html generated by this extension. notfound_template = "404.html" # Prefix added to all the URLs generated in the 404 page. notfound_urls_prefix = "" # -- Options for sitemap extension ---------------------------------- sitemap_url_scheme = "/stable/{link}" # -- Options for multiversion extension ----------------------------- # Whitelist pattern for tags smv_tag_whitelist = multiversion_regex_builder(TAGS) # Whitelist pattern for branches smv_branch_whitelist = multiversion_regex_builder(BRANCHES) # Defines which version is considered to be the latest stable version. smv_latest_version = LATEST_VERSION # Defines the new name for the latest version. smv_rename_latest_version = "stable" # Whitelist pattern for remotes (set to None to use local branches only) smv_remote_whitelist = r"^origin$" # Pattern for released versions smv_released_pattern = r"^tags/.*$" # Format for versioned output directories inside the build directory smv_outputdir_format = "{ref.name}" # -- Options for HTML output ---------------------------------------- # The theme to use for pages. html_theme = "sphinx_scylladb_theme" # html_theme_path = ["../.."] # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for the theme, see the # documentation. html_theme_options = { "conf_py_path": "docs/source/", "hide_edit_this_page_button": "false", "hide_feedback_buttons": "false", "github_issues_repository": "scylladb/gocql", "github_repository": "scylladb/gocql", "site_description": "ScyllaDB gocql driver.", "hide_version_dropdown": [], "zendesk_tag": "gq6ltsh3nfex3cnwfy4aj9", "versions_unstable": UNSTABLE_VERSIONS, "versions_deprecated": DEPRECATED_VERSIONS, } # Last updated format html_last_updated_fmt = "%d %b %Y" # Custom sidebar templates, maps document names to template names. html_sidebars = {"**": ["side-nav.html"]} # Output file base name for HTML help builder. htmlhelp_basename = "ScyllaDocumentationdoc" # URL which points to the root of the HTML documentation. html_baseurl = "https://gocql-driver.docs.scylladb.com" # Dictionary of values to pass into the template engine’s context for all pages html_context = {"html_baseurl": html_baseurl} # -- Initialize Sphinx ---------------------------------------------- def setup(sphinx): warnings.filterwarnings( action="ignore", category=UserWarning, message=r".*Container node skipped.*", ) ================================================ FILE: docs/source/index.rst ================================================ ===================== ScyllaDB gocql driver ===================== Lorem ipsum. .. toctree:: sample-page ================================================ FILE: docs/source/sample-page.rst ================================================ =========== Sample page =========== Lorem ipsum. ================================================ FILE: errors.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "fmt" frm "github.com/gocql/gocql/internal/frame" ) // See CQL Binary Protocol v5, section 8 for more details. // https://github.com/apache/cassandra/blob/7337fc0/doc/native_protocol_v5.spec const ( // ErrCodeServer indicates unexpected error on server-side. // // See https://github.com/apache/cassandra/blob/7337fc0/doc/native_protocol_v5.spec#L1246-L1247 ErrCodeServer = 0x0000 // ErrCodeProtocol indicates a protocol violation by some client message. // // See https://github.com/apache/cassandra/blob/7337fc0/doc/native_protocol_v5.spec#L1248-L1250 ErrCodeProtocol = 0x000A // ErrCodeCredentials indicates missing required authentication. // // See https://github.com/apache/cassandra/blob/7337fc0/doc/native_protocol_v5.spec#L1251-L1254 ErrCodeCredentials = 0x0100 // ErrCodeUnavailable indicates unavailable error. // // See https://github.com/apache/cassandra/blob/7337fc0/doc/native_protocol_v5.spec#L1255-L1265 ErrCodeUnavailable = 0x1000 // ErrCodeOverloaded returned in case of request on overloaded node coordinator. // // See https://github.com/apache/cassandra/blob/7337fc0/doc/native_protocol_v5.spec#L1266-L1267 ErrCodeOverloaded = 0x1001 // ErrCodeBootstrapping returned from the coordinator node in bootstrapping phase. // // See https://github.com/apache/cassandra/blob/7337fc0/doc/native_protocol_v5.spec#L1268-L1269 ErrCodeBootstrapping = 0x1002 // ErrCodeTruncate indicates truncation exception. // // See https://github.com/apache/cassandra/blob/7337fc0/doc/native_protocol_v5.spec#L1270 ErrCodeTruncate = 0x1003 // ErrCodeWriteTimeout returned in case of timeout during the request write. // // See https://github.com/apache/cassandra/blob/7337fc0/doc/native_protocol_v5.spec#L1271-L1304 ErrCodeWriteTimeout = 0x1100 // ErrCodeReadTimeout returned in case of timeout during the request read. // // See https://github.com/apache/cassandra/blob/7337fc0/doc/native_protocol_v5.spec#L1305-L1321 ErrCodeReadTimeout = 0x1200 // ErrCodeReadFailure indicates request read error which is not covered by ErrCodeReadTimeout. // // See https://github.com/apache/cassandra/blob/7337fc0/doc/native_protocol_v5.spec#L1322-L1340 ErrCodeReadFailure = 0x1300 // ErrCodeFunctionFailure indicates an error in user-defined function. // // See https://github.com/apache/cassandra/blob/7337fc0/doc/native_protocol_v5.spec#L1341-L1347 ErrCodeFunctionFailure = 0x1400 // ErrCodeWriteFailure indicates request write error which is not covered by ErrCodeWriteTimeout. // // See https://github.com/apache/cassandra/blob/7337fc0/doc/native_protocol_v5.spec#L1348-L1385 ErrCodeWriteFailure = 0x1500 // ErrCodeCDCWriteFailure is defined, but not yet documented in CQLv5 protocol. // // See https://github.com/apache/cassandra/blob/7337fc0/doc/native_protocol_v5.spec#L1386 ErrCodeCDCWriteFailure = 0x1600 // ErrCodeCASWriteUnknown indicates only partially completed CAS operation. // // See https://github.com/apache/cassandra/blob/7337fc0/doc/native_protocol_v5.spec#L1387-L1397 ErrCodeCASWriteUnknown = 0x1700 // ErrCodeSyntax indicates the syntax error in the query. // // See https://github.com/apache/cassandra/blob/7337fc0/doc/native_protocol_v5.spec#L1399 ErrCodeSyntax = 0x2000 // ErrCodeUnauthorized indicates access rights violation by user on performed operation. // // See https://github.com/apache/cassandra/blob/7337fc0/doc/native_protocol_v5.spec#L1400-L1401 ErrCodeUnauthorized = 0x2100 // ErrCodeInvalid indicates invalid query error which is not covered by ErrCodeSyntax. // // See https://github.com/apache/cassandra/blob/7337fc0/doc/native_protocol_v5.spec#L1402 ErrCodeInvalid = 0x2200 // ErrCodeConfig indicates the configuration error. // // See https://github.com/apache/cassandra/blob/7337fc0/doc/native_protocol_v5.spec#L1403 ErrCodeConfig = 0x2300 // ErrCodeAlreadyExists is returned for the requests creating the existing keyspace/table. // // See https://github.com/apache/cassandra/blob/7337fc0/doc/native_protocol_v5.spec#L1404-L1413 ErrCodeAlreadyExists = 0x2400 // ErrCodeUnprepared returned from the host for prepared statement which is unknown. // // See https://github.com/apache/cassandra/blob/7337fc0/doc/native_protocol_v5.spec#L1414-L1417 ErrCodeUnprepared = 0x2500 ) type RequestError interface { Code() int Message() string Error() string } type RequestErrUnavailable struct { frm.ErrorFrame Consistency Consistency Required int Alive int } func (e *RequestErrUnavailable) String() string { return fmt.Sprintf("[request_error_unavailable consistency=%s required=%d alive=%d]", e.Consistency, e.Required, e.Alive) } type ErrorMap map[string]uint16 type RequestErrWriteTimeout struct { WriteType string frm.ErrorFrame Received int BlockFor int Consistency Consistency } type RequestErrWriteFailure struct { ErrorMap ErrorMap WriteType string frm.ErrorFrame Received int BlockFor int NumFailures int Consistency Consistency } type RequestErrCDCWriteFailure struct { frm.ErrorFrame } type RequestErrReadTimeout struct { frm.ErrorFrame Received int BlockFor int Consistency Consistency DataPresent byte } type RequestErrAlreadyExists struct { Keyspace string Table string frm.ErrorFrame } type RequestErrUnprepared struct { StatementId []byte frm.ErrorFrame } type RequestErrReadFailure struct { ErrorMap ErrorMap frm.ErrorFrame Received int BlockFor int NumFailures int Consistency Consistency DataPresent bool } type RequestErrFunctionFailure struct { Keyspace string Function string ArgTypes []string frm.ErrorFrame } // RequestErrCASWriteUnknown is distinct error for ErrCodeCasWriteUnknown. // // See https://github.com/apache/cassandra/blob/7337fc0/doc/native_protocol_v5.spec#L1387-L1397 type RequestErrCASWriteUnknown struct { frm.ErrorFrame Consistency Consistency Received int BlockFor int } type UnknownServerError struct { frm.ErrorFrame } type OpType uint8 const ( OpTypeRead OpType = 0 OpTypeWrite OpType = 1 ) type RequestErrRateLimitReached struct { frm.ErrorFrame OpType OpType RejectedByCoordinator bool } func (e *RequestErrRateLimitReached) String() string { var opType string if e.OpType == OpTypeRead { opType = "Read" } else if e.OpType == OpTypeWrite { opType = "Write" } else { opType = "Other" } return fmt.Sprintf("[request_error_rate_limit_reached OpType=%s RejectedByCoordinator=%t]", opType, e.RejectedByCoordinator) } ================================================ FILE: errors_test.go ================================================ //go:build integration // +build integration /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "errors" "fmt" "testing" ) func TestErrorsParse(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s (id int primary key)`, table)); err != nil { t.Fatal("create:", err) } if err := createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s (id int primary key)`, table)); err == nil { t.Fatal("Should have gotten already exists error from cassandra server.") } else { e := &RequestErrAlreadyExists{} if errors.As(err, &e) { if e.Table != table { t.Fatalf("expected error table to be %q but was %q", table, e.Table) } } else { t.Fatalf("expected to get RequestErrAlreadyExists instead got %T", e) } } } ================================================ FILE: events/event_converter.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package events import ( frm "github.com/gocql/gocql/internal/frame" ) // FrameToEvent converts an internal frame to a public Event interface. // This function has access to internal frame types and can perform // type-safe conversions. // Returns nil if the frame is not an event frame. func FrameToEvent(f any) Event { if f == nil { return nil } switch frame := f.(type) { case *frm.TopologyChangeEventFrame: return &TopologyChangeEvent{ Change: frame.Change, Host: frame.Host, Port: frame.Port, } case *frm.StatusChangeEventFrame: return &StatusChangeEvent{ Change: frame.Change, Host: frame.Host, Port: frame.Port, } case *frm.SchemaChangeKeyspace: return &SchemaChangeKeyspaceEvent{ Change: frame.Change, Keyspace: frame.Keyspace, } case *frm.SchemaChangeTable: return &SchemaChangeTableEvent{ Change: frame.Change, Keyspace: frame.Keyspace, Table: frame.Object, } case *frm.SchemaChangeType: return &SchemaChangeTypeEvent{ Change: frame.Change, Keyspace: frame.Keyspace, TypeName: frame.Object, } case *frm.SchemaChangeFunction: return &SchemaChangeFunctionEvent{ Change: frame.Change, Keyspace: frame.Keyspace, Function: frame.Name, Arguments: frame.Args, } case *frm.SchemaChangeAggregate: return &SchemaChangeAggregateEvent{ Change: frame.Change, Keyspace: frame.Keyspace, Aggregate: frame.Name, Arguments: frame.Args, } case *frm.ClientRoutesChanged: return &ClientRoutesChangedEvent{ ChangeType: frame.ChangeType, ConnectionIDs: frame.ConnectionIDs, HostIDs: frame.HostIDs, } default: return nil } } ================================================ FILE: events/event_converter_test.go ================================================ //go:build unit // +build unit /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package events_test import ( "net" "testing" "github.com/gocql/gocql/events" frm "github.com/gocql/gocql/internal/frame" ) func TestFrameToEvent_TopologyChange(t *testing.T) { frame := &frm.TopologyChangeEventFrame{ Change: "NEW_NODE", Host: net.ParseIP("192.168.1.1"), Port: 9042, } event := events.FrameToEvent(frame) if event == nil { t.Fatal("FrameToEvent returned nil") } topologyEvent, ok := event.(*events.TopologyChangeEvent) if !ok { t.Fatalf("Expected *TopologyChangeEvent, got %T", event) } if topologyEvent.Change != "NEW_NODE" { t.Errorf("Change = %v, want NEW_NODE", topologyEvent.Change) } if !topologyEvent.Host.Equal(net.ParseIP("192.168.1.1")) { t.Errorf("Host = %v, want 192.168.1.1", topologyEvent.Host) } if topologyEvent.Port != 9042 { t.Errorf("Port = %v, want 9042", topologyEvent.Port) } if topologyEvent.Type() != events.ClusterEventTypeTopologyChange { t.Errorf("Type() = %v, want EventTypeTopologyChange", topologyEvent.Type()) } } func TestFrameToEvent_StatusChange(t *testing.T) { frame := &frm.StatusChangeEventFrame{ Change: "UP", Host: net.ParseIP("192.168.1.2"), Port: 9042, } event := events.FrameToEvent(frame) if event == nil { t.Fatal("FrameToEvent returned nil") } statusEvent, ok := event.(*events.StatusChangeEvent) if !ok { t.Fatalf("Expected *StatusChangeEvent, got %T", event) } if statusEvent.Change != "UP" { t.Errorf("Change = %v, want UP", statusEvent.Change) } if !statusEvent.Host.Equal(net.ParseIP("192.168.1.2")) { t.Errorf("Host = %v, want 192.168.1.2", statusEvent.Host) } if statusEvent.Port != 9042 { t.Errorf("Port = %v, want 9042", statusEvent.Port) } if statusEvent.Type() != events.ClusterEventTypeStatusChange { t.Errorf("Type() = %v, want EventTypeStatusChange", statusEvent.Type()) } } func TestFrameToEvent_SchemaChangeKeyspace(t *testing.T) { frame := &frm.SchemaChangeKeyspace{ Change: "CREATED", Keyspace: "test_keyspace", } event := events.FrameToEvent(frame) if event == nil { t.Fatal("FrameToEvent returned nil") } schemaEvent, ok := event.(*events.SchemaChangeKeyspaceEvent) if !ok { t.Fatalf("Expected *SchemaChangeKeyspaceEvent, got %T", event) } if schemaEvent.Change != "CREATED" { t.Errorf("Change = %v, want CREATED", schemaEvent.Change) } if schemaEvent.Keyspace != "test_keyspace" { t.Errorf("Keyspace = %v, want test_keyspace", schemaEvent.Keyspace) } if schemaEvent.Type() != events.ClusterEventTypeSchemaChangeKeyspace { t.Errorf("Type() = %v, want EventTypeSchemaChangeKeyspace", schemaEvent.Type()) } } func TestFrameToEvent_SchemaChangeTable(t *testing.T) { frame := &frm.SchemaChangeTable{ Change: "UPDATED", Keyspace: "test_keyspace", Object: "test_table", } event := events.FrameToEvent(frame) if event == nil { t.Fatal("FrameToEvent returned nil") } schemaEvent, ok := event.(*events.SchemaChangeTableEvent) if !ok { t.Fatalf("Expected *SchemaChangeTableEvent, got %T", event) } if schemaEvent.Change != "UPDATED" { t.Errorf("Change = %v, want UPDATED", schemaEvent.Change) } if schemaEvent.Keyspace != "test_keyspace" { t.Errorf("Keyspace = %v, want test_keyspace", schemaEvent.Keyspace) } if schemaEvent.Table != "test_table" { t.Errorf("Table = %v, want test_table", schemaEvent.Table) } if schemaEvent.Type() != events.ClusterEventTypeSchemaChangeTable { t.Errorf("Type() = %v, want EventTypeSchemaChangeTable", schemaEvent.Type()) } } func TestFrameToEvent_SchemaChangeType(t *testing.T) { frame := &frm.SchemaChangeType{ Change: "DROPPED", Keyspace: "test_keyspace", Object: "test_type", } event := events.FrameToEvent(frame) if event == nil { t.Fatal("FrameToEvent returned nil") } schemaEvent, ok := event.(*events.SchemaChangeTypeEvent) if !ok { t.Fatalf("Expected *SchemaChangeTypeEvent, got %T", event) } if schemaEvent.Change != "DROPPED" { t.Errorf("Change = %v, want DROPPED", schemaEvent.Change) } if schemaEvent.Keyspace != "test_keyspace" { t.Errorf("Keyspace = %v, want test_keyspace", schemaEvent.Keyspace) } if schemaEvent.TypeName != "test_type" { t.Errorf("TypeName = %v, want test_type", schemaEvent.TypeName) } if schemaEvent.Type() != events.ClusterEventTypeSchemaChangeType { t.Errorf("Type() = %v, want EventTypeSchemaChangeType", schemaEvent.Type()) } } func TestFrameToEvent_SchemaChangeFunction(t *testing.T) { frame := &frm.SchemaChangeFunction{ Change: "CREATED", Keyspace: "test_keyspace", Name: "test_function", Args: []string{"int", "text"}, } event := events.FrameToEvent(frame) if event == nil { t.Fatal("FrameToEvent returned nil") } schemaEvent, ok := event.(*events.SchemaChangeFunctionEvent) if !ok { t.Fatalf("Expected *SchemaChangeFunctionEvent, got %T", event) } if schemaEvent.Change != "CREATED" { t.Errorf("Change = %v, want CREATED", schemaEvent.Change) } if schemaEvent.Keyspace != "test_keyspace" { t.Errorf("Keyspace = %v, want test_keyspace", schemaEvent.Keyspace) } if schemaEvent.Function != "test_function" { t.Errorf("Function = %v, want test_function", schemaEvent.Function) } if len(schemaEvent.Arguments) != 2 { t.Errorf("len(Arguments) = %v, want 2", len(schemaEvent.Arguments)) } if schemaEvent.Type() != events.ClusterEventTypeSchemaChangeFunction { t.Errorf("Type() = %v, want EventTypeSchemaChangeFunction", schemaEvent.Type()) } } func TestFrameToEvent_SchemaChangeAggregate(t *testing.T) { frame := &frm.SchemaChangeAggregate{ Change: "UPDATED", Keyspace: "test_keyspace", Name: "test_aggregate", Args: []string{"int"}, } event := events.FrameToEvent(frame) if event == nil { t.Fatal("FrameToEvent returned nil") } schemaEvent, ok := event.(*events.SchemaChangeAggregateEvent) if !ok { t.Fatalf("Expected *SchemaChangeAggregateEvent, got %T", event) } if schemaEvent.Change != "UPDATED" { t.Errorf("Change = %v, want UPDATED", schemaEvent.Change) } if schemaEvent.Keyspace != "test_keyspace" { t.Errorf("Keyspace = %v, want test_keyspace", schemaEvent.Keyspace) } if schemaEvent.Aggregate != "test_aggregate" { t.Errorf("Aggregate = %v, want test_aggregate", schemaEvent.Aggregate) } if len(schemaEvent.Arguments) != 1 { t.Errorf("len(Arguments) = %v, want 1", len(schemaEvent.Arguments)) } if schemaEvent.Type() != events.ClusterEventTypeSchemaChangeAggregate { t.Errorf("Type() = %v, want EventTypeSchemaChangeAggregate", schemaEvent.Type()) } } func TestFrameToEvent_Nil(t *testing.T) { event := events.FrameToEvent(nil) if event != nil { t.Errorf("FrameToEvent(nil) = %v, want nil", event) } } func TestFrameToEvent_NonEventFrame(t *testing.T) { // Test with a non-event frame type frame := &frm.ErrorFrame{} event := events.FrameToEvent(frame) if event != nil { t.Errorf("FrameToEvent(non-event) = %v, want nil", event) } } func TestFrameToEvent_ClientRoutesChanged(t *testing.T) { frame := &frm.ClientRoutesChanged{ ChangeType: "UPDATED", ConnectionIDs: []string{"c1", ""}, HostIDs: []string{}, } event := events.FrameToEvent(frame) if event == nil { t.Fatal("FrameToEvent returned nil") } clientEvent, ok := event.(*events.ClientRoutesChangedEvent) if !ok { t.Fatalf("Expected *ClientRoutesChangedEvent, got %T", event) } if clientEvent.ChangeType != "UPDATED" { t.Errorf("ChangeType = %v, want UPDATED", clientEvent.ChangeType) } if len(clientEvent.ConnectionIDs) != 2 || clientEvent.ConnectionIDs[1] != "" { t.Errorf("ConnectionIDs = %v, want [c1 \"\"]", clientEvent.ConnectionIDs) } if len(clientEvent.HostIDs) != 0 { t.Errorf("HostIDs = %v, want empty", clientEvent.HostIDs) } if clientEvent.Type() != events.ClusterEventTypeClientRoutesChanged { t.Errorf("Type() = %v, want ClusterEventTypeClientRoutesChanged", clientEvent.Type()) } } ================================================ FILE: events/events.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // Package events provides public event types for Cassandra/ScyllaDB server events. // These events are sent by the server to notify clients of topology changes, status changes, // and schema changes. package events import ( "fmt" "net" ) // EventType represents the type of event type EventType int const ( // ClusterEventTypeTopologyChange represents a topology change event (NEW_NODE, REMOVED_NODE, MOVED_NODE) ClusterEventTypeTopologyChange EventType = iota // ClusterEventTypeStatusChange represents a status change event (UP, DOWN) ClusterEventTypeStatusChange // ClusterEventTypeSchemaChangeKeyspace represents a keyspace schema change ClusterEventTypeSchemaChangeKeyspace // ClusterEventTypeSchemaChangeTable represents a table schema change ClusterEventTypeSchemaChangeTable // ClusterEventTypeSchemaChangeType represents a UDT schema change ClusterEventTypeSchemaChangeType // ClusterEventTypeSchemaChangeFunction represents a function schema change ClusterEventTypeSchemaChangeFunction // ClusterEventTypeSchemaChangeAggregate represents an aggregate schema change ClusterEventTypeSchemaChangeAggregate // ClusterEventTypeClientRoutesChanged represents an event of update of `system.client_routes` table ClusterEventTypeClientRoutesChanged // SessionEventTypeControlConnectionRecreated is fired when the session loses it's control connection to the cluster and has just been re-established it. SessionEventTypeControlConnectionRecreated ) func (t EventType) IsClusterEvent() bool { switch t { case ClusterEventTypeTopologyChange: return true case ClusterEventTypeStatusChange: return true case ClusterEventTypeSchemaChangeKeyspace: return true case ClusterEventTypeSchemaChangeTable: return true case ClusterEventTypeSchemaChangeType: return true case ClusterEventTypeSchemaChangeFunction: return true case ClusterEventTypeSchemaChangeAggregate: return true case ClusterEventTypeClientRoutesChanged: return true default: return false } } func (t EventType) String() string { switch t { case ClusterEventTypeTopologyChange: return "CLUSTER" case ClusterEventTypeStatusChange: return "CLUSTER" case ClusterEventTypeSchemaChangeKeyspace: return "CLUSTER" case ClusterEventTypeSchemaChangeTable: return "CLUSTER" case ClusterEventTypeSchemaChangeType: return "CLUSTER" case ClusterEventTypeSchemaChangeFunction: return "CLUSTER" case ClusterEventTypeSchemaChangeAggregate: return "CLUSTER" case ClusterEventTypeClientRoutesChanged: return "CLUSTER" case SessionEventTypeControlConnectionRecreated: return "SESSION" default: return fmt.Sprintf("UNKNOWN(%d)", t) } } // Event is the common interface for all event types type Event interface { // Type returns the type of event Type() EventType // String returns a string representation of the event String() string } // TopologyChangeEvent represents a topology change in the cluster type TopologyChangeEvent struct { // Change is the type of topology change (NEW_NODE, REMOVED_NODE, MOVED_NODE) Change string // Host is the IP address of the node Host net.IP // Port is the port number Port int } // Type returns ClusterEventTypeTopologyChange func (e *TopologyChangeEvent) Type() EventType { return ClusterEventTypeTopologyChange } // String returns a string representation of the event func (e *TopologyChangeEvent) String() string { return fmt.Sprintf("TopologyChange{change=%s, host=%s, port=%d}", e.Change, e.Host, e.Port) } // StatusChangeEvent represents a status change of a node type StatusChangeEvent struct { // Change is the type of status change (UP, DOWN) Change string // Host is the IP address of the node Host net.IP // Port is the port number Port int } // Type returns ClusterEventTypeStatusChange func (e *StatusChangeEvent) Type() EventType { return ClusterEventTypeStatusChange } // String returns a string representation of the event func (e *StatusChangeEvent) String() string { return fmt.Sprintf("StatusChange{change=%s, host=%s, port=%d}", e.Change, e.Host, e.Port) } // SchemaChangeKeyspaceEvent represents a keyspace schema change type SchemaChangeKeyspaceEvent struct { // Change is the type of change (CREATED, UPDATED, DROPPED) Change string // Keyspace is the name of the keyspace Keyspace string } // Type returns ClusterEventTypeSchemaChangeKeyspace func (e *SchemaChangeKeyspaceEvent) Type() EventType { return ClusterEventTypeSchemaChangeKeyspace } // String returns a string representation of the event func (e *SchemaChangeKeyspaceEvent) String() string { return fmt.Sprintf("SchemaChangeKeyspace{change=%s, keyspace=%s}", e.Change, e.Keyspace) } // SchemaChangeTableEvent represents a table schema change type SchemaChangeTableEvent struct { // Change is the type of change (CREATED, UPDATED, DROPPED) Change string // Keyspace is the name of the keyspace Keyspace string // Table is the name of the table Table string } // Type returns ClusterEventTypeSchemaChangeTable func (e *SchemaChangeTableEvent) Type() EventType { return ClusterEventTypeSchemaChangeTable } // String returns a string representation of the event func (e *SchemaChangeTableEvent) String() string { return fmt.Sprintf("SchemaChangeTable{change=%s, keyspace=%s, table=%s}", e.Change, e.Keyspace, e.Table) } // SchemaChangeTypeEvent represents a UDT (User Defined Type) schema change type SchemaChangeTypeEvent struct { // Change is the type of change (CREATED, UPDATED, DROPPED) Change string // Keyspace is the name of the keyspace Keyspace string // TypeName is the name of the UDT TypeName string } // Type returns ClusterEventTypeSchemaChangeType func (e *SchemaChangeTypeEvent) Type() EventType { return ClusterEventTypeSchemaChangeType } // String returns a string representation of the event func (e *SchemaChangeTypeEvent) String() string { return fmt.Sprintf("SchemaChangeType{change=%s, keyspace=%s, type=%s}", e.Change, e.Keyspace, e.TypeName) } // SchemaChangeFunctionEvent represents a function schema change type SchemaChangeFunctionEvent struct { // Change is the type of change (CREATED, UPDATED, DROPPED) Change string // Keyspace is the name of the keyspace Keyspace string // Function is the name of the function Function string // Arguments is the list of argument types Arguments []string } // Type returns ClusterEventTypeSchemaChangeFunction func (e *SchemaChangeFunctionEvent) Type() EventType { return ClusterEventTypeSchemaChangeFunction } // String returns a string representation of the event func (e *SchemaChangeFunctionEvent) String() string { return fmt.Sprintf("SchemaChangeFunction{change=%s, keyspace=%s, function=%s, args=%v}", e.Change, e.Keyspace, e.Function, e.Arguments) } // SchemaChangeAggregateEvent represents an aggregate schema change type SchemaChangeAggregateEvent struct { // Change is the type of change (CREATED, UPDATED, DROPPED) Change string // Keyspace is the name of the keyspace Keyspace string // Aggregate is the name of the aggregate Aggregate string // Arguments is the list of argument types Arguments []string } // Type returns ClusterEventTypeSchemaChangeAggregate func (e *SchemaChangeAggregateEvent) Type() EventType { return ClusterEventTypeSchemaChangeAggregate } // String returns a string representation of the event func (e *SchemaChangeAggregateEvent) String() string { return fmt.Sprintf("SchemaChangeAggregate{change=%s, keyspace=%s, aggregate=%s, args=%v}", e.Change, e.Keyspace, e.Aggregate, e.Arguments) } // ClientRoutesChangedEvent represents an aggregate schema change type ClientRoutesChangedEvent struct { // Change is the type of change (UPDATED) ChangeType string // List of connection ids involved into update ConnectionIDs []string // List of host ids involved into update HostIDs []string } // Type returns ClusterEventTypeClientRoutesChanged func (e *ClientRoutesChangedEvent) Type() EventType { return ClusterEventTypeClientRoutesChanged } // String returns a string representation of the event func (e *ClientRoutesChangedEvent) String() string { return fmt.Sprintf("ConnectionMetadataChanged{changeType=%s, ConnectionIDs=%s, HostIDs=%s}", e.ChangeType, e.ConnectionIDs, e.HostIDs) } type HostInfo struct { HostID string Host net.IP Port int } // String returns a string representation of the event func (h *HostInfo) String() string { return fmt.Sprintf("HostInfo{Host=%s, Port=%d, HostID=%s}", h.Host, h.Port, h.HostID) } // ControlConnectionRecreatedEvent represents a control connection reconnection event. type ControlConnectionRecreatedEvent struct { OldHost HostInfo NewHost HostInfo } // Type returns SessionEventTypeControlConnectionRecreated func (e *ControlConnectionRecreatedEvent) Type() EventType { return SessionEventTypeControlConnectionRecreated } // String returns a string representation of the event func (e *ControlConnectionRecreatedEvent) String() string { return fmt.Sprintf("ControlConnectionRecreatedEvent{OldHost=%s, NewHost=%s}", e.OldHost.String(), e.NewHost.String()) } ================================================ FILE: events/events_test.go ================================================ //go:build unit // +build unit /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package events import ( "net" "testing" ) func TestTopologyChangeEvent(t *testing.T) { event := &TopologyChangeEvent{ Change: "NEW_NODE", Host: net.ParseIP("192.168.1.1"), Port: 9042, } if event.Type() != ClusterEventTypeTopologyChange { t.Errorf("Type() = %v, want %v", event.Type(), ClusterEventTypeTopologyChange) } str := event.String() if str == "" { t.Error("String() returned empty string") } t.Logf("TopologyChangeEvent.String() = %s", str) } func TestStatusChangeEvent(t *testing.T) { event := &StatusChangeEvent{ Change: "UP", Host: net.ParseIP("192.168.1.2"), Port: 9042, } if event.Type() != ClusterEventTypeStatusChange { t.Errorf("Type() = %v, want %v", event.Type(), ClusterEventTypeStatusChange) } str := event.String() if str == "" { t.Error("String() returned empty string") } t.Logf("StatusChangeEvent.String() = %s", str) } func TestSchemaChangeKeyspaceEvent(t *testing.T) { event := &SchemaChangeKeyspaceEvent{ Change: "CREATED", Keyspace: "test_keyspace", } if event.Type() != ClusterEventTypeSchemaChangeKeyspace { t.Errorf("Type() = %v, want %v", event.Type(), ClusterEventTypeSchemaChangeKeyspace) } str := event.String() if str == "" { t.Error("String() returned empty string") } t.Logf("SchemaChangeKeyspaceEvent.String() = %s", str) } func TestSchemaChangeTableEvent(t *testing.T) { event := &SchemaChangeTableEvent{ Change: "UPDATED", Keyspace: "test_keyspace", Table: "test_table", } if event.Type() != ClusterEventTypeSchemaChangeTable { t.Errorf("Type() = %v, want %v", event.Type(), ClusterEventTypeSchemaChangeTable) } str := event.String() if str == "" { t.Error("String() returned empty string") } t.Logf("SchemaChangeTableEvent.String() = %s", str) } func TestSchemaChangeTypeEvent(t *testing.T) { event := &SchemaChangeTypeEvent{ Change: "DROPPED", Keyspace: "test_keyspace", TypeName: "test_type", } if event.Type() != ClusterEventTypeSchemaChangeType { t.Errorf("Type() = %v, want %v", event.Type(), ClusterEventTypeSchemaChangeType) } str := event.String() if str == "" { t.Error("String() returned empty string") } t.Logf("SchemaChangeTypeEvent.String() = %s", str) } func TestSchemaChangeFunctionEvent(t *testing.T) { event := &SchemaChangeFunctionEvent{ Change: "CREATED", Keyspace: "test_keyspace", Function: "test_function", Arguments: []string{"int", "text"}, } if event.Type() != ClusterEventTypeSchemaChangeFunction { t.Errorf("Type() = %v, want %v", event.Type(), ClusterEventTypeSchemaChangeFunction) } str := event.String() if str == "" { t.Error("String() returned empty string") } t.Logf("SchemaChangeFunctionEvent.String() = %s", str) } func TestSchemaChangeAggregateEvent(t *testing.T) { event := &SchemaChangeAggregateEvent{ Change: "UPDATED", Keyspace: "test_keyspace", Aggregate: "test_aggregate", Arguments: []string{"int"}, } if event.Type() != ClusterEventTypeSchemaChangeAggregate { t.Errorf("Type() = %v, want %v", event.Type(), ClusterEventTypeSchemaChangeAggregate) } str := event.String() if str == "" { t.Error("String() returned empty string") } t.Logf("SchemaChangeAggregateEvent.String() = %s", str) } func TestClientRoutesChangedEvent(t *testing.T) { event := &ClientRoutesChangedEvent{ ChangeType: "UPDATED", ConnectionIDs: []string{"c1"}, HostIDs: []string{}, } if event.Type() != ClusterEventTypeClientRoutesChanged { t.Errorf("Type() = %v, want %v", event.Type(), ClusterEventTypeClientRoutesChanged) } str := event.String() if str == "" { t.Error("String() returned empty string") } t.Logf("ClientRoutesChangedEvent.String() = %s", str) } func TestEventInterface(t *testing.T) { events := []Event{ &TopologyChangeEvent{Change: "NEW_NODE", Host: net.ParseIP("127.0.0.1"), Port: 9042}, &StatusChangeEvent{Change: "UP", Host: net.ParseIP("127.0.0.2"), Port: 9042}, &SchemaChangeKeyspaceEvent{Change: "CREATED", Keyspace: "ks"}, &SchemaChangeTableEvent{Change: "UPDATED", Keyspace: "ks", Table: "tbl"}, &SchemaChangeTypeEvent{Change: "DROPPED", Keyspace: "ks", TypeName: "typ"}, &SchemaChangeFunctionEvent{Change: "CREATED", Keyspace: "ks", Function: "fn", Arguments: []string{}}, &SchemaChangeAggregateEvent{Change: "UPDATED", Keyspace: "ks", Aggregate: "agg", Arguments: []string{}}, &ClientRoutesChangedEvent{ChangeType: "UPDATED", ConnectionIDs: []string{"c1"}, HostIDs: []string{}}, } for _, event := range events { if event.Type() < ClusterEventTypeTopologyChange || event.Type() > ClusterEventTypeClientRoutesChanged { t.Errorf("Invalid event type: %v", event.Type()) } if event.String() == "" { t.Errorf("Event.String() returned empty string for %T", event) } } } ================================================ FILE: events.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "net" "sync" "time" "github.com/gocql/gocql/events" "github.com/gocql/gocql/internal/debug" frm "github.com/gocql/gocql/internal/frame" ) type eventDebouncer struct { logger StdLogger timer *time.Timer callback func([]frame) quit chan struct{} name string events []frame mu sync.Mutex } func newEventDebouncer(name string, eventHandler func([]frame), logger StdLogger) *eventDebouncer { e := &eventDebouncer{ name: name, quit: make(chan struct{}), timer: time.NewTimer(eventDebounceTime), callback: eventHandler, logger: logger, } e.timer.Stop() go e.flusher() return e } func (e *eventDebouncer) stop() { e.quit <- struct{}{} // sync with flusher close(e.quit) } func (e *eventDebouncer) flusher() { for { select { case <-e.timer.C: e.mu.Lock() e.flush() e.mu.Unlock() case <-e.quit: return } } } const ( eventBufferSize = 1000 eventDebounceTime = 1 * time.Second ) // flush must be called with mu locked func (e *eventDebouncer) flush() { if len(e.events) == 0 { return } // if the flush interval is faster than the callback then we will end up calling // the callback multiple times, probably a bad idea. In this case we could drop // frames? go e.callback(e.events) e.events = make([]frame, 0, eventBufferSize) } func (e *eventDebouncer) debounce(frame frame) { e.mu.Lock() e.timer.Reset(eventDebounceTime) // TODO: probably need a warning to track if this threshold is too low if len(e.events) < eventBufferSize { e.events = append(e.events, frame) } else { e.logger.Printf("%s: buffer full, dropping event frame: %s", e.name, frame) } e.mu.Unlock() } func (s *Session) publishEvent(event events.Event) { if s.eventBus == nil { return } if !s.eventBus.PublishEvent(event) { s.logger.Printf("can't publish event, eventbus is full, increase Cluster.EventBusConfig.InputEventsQueueSize; event is dropped") } } func (s *Session) handleEvent(frame frame) { if debug.Enabled { s.logger.Printf("gocql: handling frame: %v\n", frame) } if event := events.FrameToEvent(frame); event != nil { s.publishEvent(event) } switch f := frame.(type) { case *frm.SchemaChangeKeyspace, *frm.SchemaChangeFunction, *frm.SchemaChangeTable, *frm.SchemaChangeAggregate, *frm.SchemaChangeType: s.schemaEvents.debounce(frame) case *frm.TopologyChangeEventFrame, *frm.StatusChangeEventFrame: s.nodeEvents.debounce(frame) case *frm.ClientRoutesChanged: break default: s.logger.Printf("gocql: invalid event frame (%T): %v\n", f, f) } } func (s *Session) handleSchemaEvent(frames []frame) { // TODO: debounce events for _, frame := range frames { switch f := frame.(type) { case *frm.SchemaChangeKeyspace: s.metadataDescriber.invalidateKeyspaceSchema(f.Keyspace) s.handleKeyspaceChange(f.Keyspace, f.Change) case *frm.SchemaChangeTable: s.metadataDescriber.invalidateTableSchema(f.Keyspace, f.Object) s.handleTableChange(f.Keyspace, f.Object, f.Change) case *frm.SchemaChangeAggregate: s.metadataDescriber.invalidateKeyspaceSchema(f.Keyspace) case *frm.SchemaChangeFunction: s.metadataDescriber.invalidateKeyspaceSchema(f.Keyspace) case *frm.SchemaChangeType: s.metadataDescriber.invalidateKeyspaceSchema(f.Keyspace) } } } func (s *Session) handleKeyspaceChange(keyspace, change string) { s.control.awaitSchemaAgreement() if change == "DROPPED" || change == "UPDATED" { s.metadataDescriber.RemoveTabletsWithKeyspace(keyspace) } s.policy.KeyspaceChanged(KeyspaceUpdateEvent{Keyspace: keyspace, Change: change}) } func (s *Session) handleTableChange(keyspace, table, change string) { if change == "DROPPED" || change == "UPDATED" { s.metadataDescriber.RemoveTabletsWithTable(keyspace, table) } } // handleNodeEvent handles inbound status and topology change events. // // Status events are debounced by host IP; only the latest event is processed. // // Topology events are debounced by performing a single full topology refresh // whenever any topology event comes in. // // Processing topology change events before status change events ensures // that a NEW_NODE event is not dropped in favor of a newer UP event (which // would itself be dropped/ignored, as the node is not yet known). func (s *Session) handleNodeEvent(frames []frame) { type nodeEvent struct { change string host net.IP port int } topologyEventReceived := false // status change events sEvents := make(map[string]*nodeEvent) for _, frame := range frames { switch f := frame.(type) { case *frm.TopologyChangeEventFrame: topologyEventReceived = true case *frm.StatusChangeEventFrame: event, ok := sEvents[f.Host.String()] if !ok { event = &nodeEvent{change: f.Change, host: f.Host, port: f.Port} sEvents[f.Host.String()] = event } event.change = f.Change } } if topologyEventReceived && !s.cfg.Events.DisableTopologyEvents { s.debounceRingRefresh() } for _, f := range sEvents { if debug.Enabled { s.logger.Printf("gocql: dispatching status change event: %+v\n", f) } // ignore events we received if they were disabled // see https://github.com/apache/cassandra-gocql-driver/issues/1591 switch f.change { case "UP": if !s.cfg.Events.DisableNodeStatusEvents { s.handleNodeUp(f.host, f.port) } case "DOWN": if !s.cfg.Events.DisableNodeStatusEvents { s.handleNodeDown(f.host, f.port) } } } } func (s *Session) handleNodeUp(eventIp net.IP, eventPort int) { if debug.Enabled { s.logger.Printf("gocql: Session.handleNodeUp: %s:%d\n", eventIp.String(), eventPort) } host, ok := s.hostSource.getHostByIP(eventIp.String()) if !ok { s.debounceRingRefresh() return } if s.cfg.filterHost(host) { return } if d := host.Version().nodeUpDelay(); d > 0 { time.Sleep(d) } s.startPoolFill(host) } func (s *Session) startPoolFill(host *HostInfo) { // we let the pool call handleNodeConnected to change the host state s.pool.addHost(host) s.policy.AddHost(host) } func (s *Session) handleNodeConnected(host *HostInfo) { if debug.Enabled { s.logger.Printf("gocql: Session.handleNodeConnected: %s:%d\n", host.ConnectAddress(), host.Port()) } host.setState(NodeUp) if !s.cfg.filterHost(host) { s.policy.HostUp(host) } } func (s *Session) handleNodeDown(ip net.IP, port int) { if debug.Enabled { s.logger.Printf("gocql: Session.handleNodeDown: %s:%d\n", ip.String(), port) } host, ok := s.hostSource.getHostByIP(ip.String()) if ok { host.setState(NodeDown) if s.cfg.filterHost(host) { return } s.policy.HostDown(host) hostID := host.HostID() s.pool.removeHost(hostID) } } ================================================ FILE: events_test.go ================================================ //go:build unit // +build unit /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "net" "sync/atomic" "testing" "time" frm "github.com/gocql/gocql/internal/frame" ) func TestEventDebounce(t *testing.T) { t.Parallel() const eventCount = 150 var eventsSeen atomic.Int64 done := make(chan struct{}, 1) debouncer := newEventDebouncer("testDebouncer", func(events []frame) { if eventsSeen.Add(int64(len(events))) >= eventCount { select { case done <- struct{}{}: default: } } }, &defaultLogger{}) defer debouncer.stop() for i := 0; i < eventCount; i++ { debouncer.debounce(&frm.StatusChangeEventFrame{ Change: "UP", Host: net.IPv4(127, 0, 0, 1), Port: 9042, }) } select { case <-done: case <-time.After(10 * time.Second): t.Fatalf("timed out waiting for events: saw %d of %d", eventsSeen.Load(), eventCount) } if n := eventsSeen.Load(); n != eventCount { t.Fatalf("expected to see %d events but got %d", eventCount, n) } } // TestEventDebounceMultipleFlushes verifies that the debouncer correctly // accumulates events across multiple flush cycles without panicking. // This is a regression test for a race where the callback could fire // more than once (due to timer re-fires), causing a negative WaitGroup // counter panic in the original test. func TestEventDebounceMultipleFlushes(t *testing.T) { t.Parallel() const eventCount = 50 var eventsSeen atomic.Int64 var flushCount atomic.Int64 firstFlushDone := make(chan struct{}, 1) done := make(chan struct{}, 1) debouncer := newEventDebouncer("testDebouncerMulti", func(events []frame) { if flushCount.Add(1) == 1 { select { case firstFlushDone <- struct{}{}: default: } } if eventsSeen.Add(int64(len(events))) >= eventCount { select { case done <- struct{}{}: default: } } }, &defaultLogger{}) defer debouncer.stop() // Send events in two batches separated by more than eventDebounceTime // to force at least two separate flush cycles. for i := 0; i < eventCount/2; i++ { debouncer.debounce(&frm.StatusChangeEventFrame{ Change: "UP", Host: net.IPv4(127, 0, 0, 1), Port: 9042, }) } select { case <-firstFlushDone: case <-time.After(10 * time.Second): t.Fatalf("timed out waiting for first flush: saw %d events across %d flushes", eventsSeen.Load(), flushCount.Load()) } for i := 0; i < eventCount/2; i++ { debouncer.debounce(&frm.StatusChangeEventFrame{ Change: "UP", Host: net.IPv4(127, 0, 0, 1), Port: 9042, }) } select { case <-done: case <-time.After(10 * time.Second): t.Fatalf("timed out waiting for events: saw %d of %d", eventsSeen.Load(), eventCount) } if n := eventsSeen.Load(); n != eventCount { t.Fatalf("expected to see %d events but got %d", eventCount, n) } if f := flushCount.Load(); f < 2 { t.Fatalf("expected at least 2 flush cycles but got %d", f) } } ================================================ FILE: events_unit_test.go ================================================ //go:build unit // +build unit /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package gocql import ( "fmt" "log" "runtime" "strings" "sync" "testing" "time" "github.com/gocql/gocql/internal/tests/mock" "github.com/gocql/gocql/tablets" frm "github.com/gocql/gocql/internal/frame" ) var ( typeVarchar = NativeType{proto: 4, typ: TypeVarchar} typeBoolean = NativeType{proto: 4, typ: TypeBoolean} typeDouble = NativeType{proto: 4, typ: TypeDouble} typeInt = NativeType{proto: 4, typ: TypeInt} typeMapSS = CollectionType{ NativeType: NativeType{proto: 4, typ: TypeMap}, Key: NativeType{proto: 4, typ: TypeVarchar}, Elem: NativeType{proto: 4, typ: TypeVarchar}, } typeMapSB = CollectionType{ NativeType: NativeType{proto: 4, typ: TypeMap}, Key: NativeType{proto: 4, typ: TypeVarchar}, Elem: NativeType{proto: 4, typ: TypeBlob}, } typeSetS = CollectionType{ NativeType: NativeType{proto: 4, typ: TypeSet}, Elem: NativeType{proto: 4, typ: TypeVarchar}, } ) var keyspaceMeta = resultMetadata{ columns: []ColumnInfo{ {Name: "durable_writes", TypeInfo: typeBoolean}, {Name: "replication", TypeInfo: typeMapSS}, }, actualColCount: 2, colCount: 2, } var tableMeta = resultMetadata{ columns: []ColumnInfo{ {Name: "table_name", TypeInfo: typeVarchar}, {Name: "bloom_filter_fp_chance", TypeInfo: typeDouble}, {Name: "caching", TypeInfo: typeMapSS}, {Name: "comment", TypeInfo: typeVarchar}, {Name: "compaction", TypeInfo: typeMapSS}, {Name: "compression", TypeInfo: typeMapSS}, {Name: "crc_check_chance", TypeInfo: typeDouble}, {Name: "default_time_to_live", TypeInfo: typeInt}, {Name: "gc_grace_seconds", TypeInfo: typeInt}, {Name: "max_index_interval", TypeInfo: typeInt}, {Name: "memtable_flush_period_in_ms", TypeInfo: typeInt}, {Name: "min_index_interval", TypeInfo: typeInt}, {Name: "speculative_retry", TypeInfo: typeVarchar}, {Name: "flags", TypeInfo: typeSetS}, {Name: "extensions", TypeInfo: typeMapSB}, }, actualColCount: 15, colCount: 15, } var columnMeta = resultMetadata{ columns: []ColumnInfo{ {Name: "table_name", TypeInfo: typeVarchar}, {Name: "column_name", TypeInfo: typeVarchar}, {Name: "clustering_order", TypeInfo: typeVarchar}, {Name: "type", TypeInfo: typeVarchar}, {Name: "kind", TypeInfo: typeVarchar}, {Name: "position", TypeInfo: typeInt}, }, actualColCount: 6, colCount: 6, } func mustMarshal(info TypeInfo, value any) []byte { b, err := Marshal(info, value) if err != nil { panic(fmt.Sprintf("mustMarshal(%v, %v): %v", info, value, err)) } return b } func marshalRow(meta resultMetadata, values []any) [][]byte { if len(meta.columns) != len(values) { panic(fmt.Sprintf("marshalRow: column count %d != value count %d", len(meta.columns), len(values))) } row := make([][]byte, len(values)) for i, col := range meta.columns { row[i] = mustMarshal(col.TypeInfo, values[i]) } return row } func makeKeyspaceRow(durableWrites bool) [][]byte { replication := map[string]string{ "class": "org.apache.cassandra.locator.SimpleStrategy", "replication_factor": "1", } return marshalRow(keyspaceMeta, []any{durableWrites, replication}) } func makeTableRow(tableName string) [][]byte { return marshalRow(tableMeta, []any{ tableName, // table_name float64(0.01), // bloom_filter_fp_chance map[string]string(nil), // caching "", // comment map[string]string(nil), // compaction map[string]string(nil), // compression float64(0), // crc_check_chance 0, // default_time_to_live 0, // gc_grace_seconds 0, // max_index_interval 0, // memtable_flush_period_in_ms 0, // min_index_interval "", // speculative_retry []string(nil), // flags map[string][]byte(nil), // extensions }) } func makeColumnRow(tableName, colName, kind string, position int) [][]byte { return marshalRow(columnMeta, []any{ tableName, // table_name colName, // column_name "none", // clustering_order "int", // type kind, // kind position, // position }) } func makeIter(meta resultMetadata, rows ...[][]byte) *Iter { if len(rows) == 0 { return &Iter{} } var allData [][]byte for _, row := range rows { allData = append(allData, row...) } return &Iter{ meta: meta, framer: &mock.MockFramer{Data: allData}, numRows: len(rows), } } type tableInfo struct { name string columns []columnInfo } type columnInfo struct { name string kind string // "partition_key", "clustering", "regular" position int } type schemaDataMock struct { fakeControlConn mu sync.Mutex awaitSchemaAgreementCalls int queries []queryRecord knownKeyspaces map[string][]tableInfo queryDelay time.Duration queryError error // if set, querySystem returns an Iter with this error } func (m *schemaDataMock) awaitSchemaAgreement() error { m.mu.Lock() defer m.mu.Unlock() m.awaitSchemaAgreementCalls++ return nil } func (m *schemaDataMock) query(statement string, values ...any) *Iter { m.mu.Lock() m.queries = append(m.queries, queryRecord{method: "query", stmt: statement}) delay := m.queryDelay m.mu.Unlock() if delay > 0 { time.Sleep(delay) } return &Iter{} } func (m *schemaDataMock) querySystem(statement string, values ...any) *Iter { m.mu.Lock() m.queries = append(m.queries, queryRecord{method: "querySystem", stmt: statement}) delay := m.queryDelay queryErr := m.queryError m.mu.Unlock() if delay > 0 { time.Sleep(delay) } if queryErr != nil { return &Iter{err: queryErr} } if strings.HasPrefix(statement, "SELECT durable_writes, replication FROM system_schema.keyspaces") { ksName, _ := values[0].(string) if _, ok := m.knownKeyspaces[ksName]; ok { return makeIter(keyspaceMeta, makeKeyspaceRow(true)) } return &Iter{} } if strings.HasPrefix(statement, "SELECT * FROM system_schema.tables WHERE keyspace_name = ?") && !strings.Contains(statement, "AND table_name") { ksName, _ := values[0].(string) tables, ok := m.knownKeyspaces[ksName] if !ok || len(tables) == 0 { return &Iter{} } var rows [][]byte for _, t := range tables { rows = append(rows, makeTableRow(t.name)...) } return &Iter{ meta: tableMeta, framer: &mock.MockFramer{Data: rows}, numRows: len(tables), } } if strings.HasPrefix(statement, "SELECT * FROM system_schema.tables WHERE keyspace_name = ? AND table_name = ?") { ksName, _ := values[0].(string) tblName, _ := values[1].(string) tables, ok := m.knownKeyspaces[ksName] if ok { for _, t := range tables { if t.name == tblName { return makeIter(tableMeta, makeTableRow(t.name)) } } } return &Iter{} } if strings.HasPrefix(statement, "SELECT "+columnMetadataColumns+" FROM system_schema.columns WHERE keyspace_name = ?") && !strings.Contains(statement, "AND table_name") { ksName, _ := values[0].(string) tables, ok := m.knownKeyspaces[ksName] if !ok { return &Iter{} } var rows [][]byte count := 0 for _, t := range tables { for _, c := range t.columns { rows = append(rows, makeColumnRow(t.name, c.name, c.kind, c.position)...) count++ } } if count == 0 { return &Iter{} } return &Iter{ meta: columnMeta, framer: &mock.MockFramer{Data: rows}, numRows: count, } } if strings.HasPrefix(statement, "SELECT "+columnMetadataColumns+" FROM system_schema.columns WHERE keyspace_name = ? AND table_name = ?") { ksName, _ := values[0].(string) tblName, _ := values[1].(string) tables, ok := m.knownKeyspaces[ksName] if !ok { return &Iter{} } var rows [][]byte count := 0 for _, t := range tables { if t.name != tblName { continue } for _, c := range t.columns { rows = append(rows, makeColumnRow(t.name, c.name, c.kind, c.position)...) count++ } } if count == 0 { return &Iter{} } return &Iter{ meta: columnMeta, framer: &mock.MockFramer{Data: rows}, numRows: count, } } return &Iter{} } func (m *schemaDataMock) setQueryError(err error) { m.mu.Lock() defer m.mu.Unlock() m.queryError = err } func (m *schemaDataMock) resetQueries() { m.mu.Lock() defer m.mu.Unlock() m.queries = nil } func (m *schemaDataMock) getStatements() []string { m.mu.Lock() defer m.mu.Unlock() stmts := make([]string, len(m.queries)) for i, q := range m.queries { stmts[i] = q.stmt } return stmts } func (m *schemaDataMock) getQueryCount() int { m.mu.Lock() defer m.mu.Unlock() return len(m.queries) } func (m *schemaDataMock) getAwaitSchemaAgreementCalls() int { m.mu.Lock() defer m.mu.Unlock() return m.awaitSchemaAgreementCalls } func newSchemaEventTestSession(control controlConnection, policy HostSelectionPolicy, keyspace string) *Session { s := &Session{ control: control, policy: policy, logger: log.Default(), cfg: ClusterConfig{Keyspace: keyspace}, } s.hostSource = &ringDescriber{cfg: &s.cfg, logger: s.logger} s.metadataDescriber = &metadataDescriber{ session: s, metadata: &Metadata{ tabletsMetadata: tablets.NewCowTabletList(), }, } runtime.SetFinalizer(s, func(s *Session) { s.Close() }) return s } func newSchemaEventTestSessionWithMock(mockCtrl *schemaDataMock) *Session { s := newSchemaEventTestSession(mockCtrl, &trackingPolicy{}, "") s.useSystemSchema = true s.hasAggregatesAndFunctions = false return s } func populateKeyspace(s *Session, ksName string, tableNames ...string) { ks := &KeyspaceMetadata{ Name: ksName, DurableWrites: true, Tables: make(map[string]*TableMetadata), } for _, tbl := range tableNames { ks.Tables[tbl] = &TableMetadata{ Keyspace: ksName, Name: tbl, Columns: map[string]*ColumnMetadata{ "id": {Keyspace: ksName, Table: tbl, Name: "id", Kind: ColumnPartitionKey}, }, } } s.metadataDescriber.metadata.keyspaceMetadata.set(ksName, ks) } type trackingPolicy struct { roundRobinHostPolicy mu sync.Mutex keyspaceChangedCalls []KeyspaceUpdateEvent } func (t *trackingPolicy) KeyspaceChanged(event KeyspaceUpdateEvent) { t.mu.Lock() defer t.mu.Unlock() t.keyspaceChangedCalls = append(t.keyspaceChangedCalls, event) } func (t *trackingPolicy) getKeyspaceChangedCalls() []KeyspaceUpdateEvent { t.mu.Lock() defer t.mu.Unlock() dst := make([]KeyspaceUpdateEvent, len(t.keyspaceChangedCalls)) copy(dst, t.keyspaceChangedCalls) return dst } type queryRecord struct { method string stmt string } func addTestTablets(t *testing.T, session *Session, ksName, tblName string) { t.Helper() t1, err := tablets.TabletInfoBuilder{ KeyspaceName: ksName, TableName: tblName, FirstToken: 0, LastToken: 100, }.Build() if err != nil { t.Fatal(err) } t2, err := tablets.TabletInfoBuilder{ KeyspaceName: ksName, TableName: tblName, FirstToken: 101, LastToken: 200, }.Build() if err != nil { t.Fatal(err) } session.metadataDescriber.AddTablet(t1) session.metadataDescriber.AddTablet(t2) session.metadataDescriber.metadata.tabletsMetadata.Flush() } func TestHandleSchemaEvent(t *testing.T) { t.Parallel() t.Run("cache_state", func(t *testing.T) { t.Parallel() tests := []struct { name string keyspaces map[string][]string // ks → tables to pre-populate tablets [][2]string // (ks, table) pairs to add tablets for event frame wantKsGone []string // keyspaces removed from cache wantKsPresent []string // keyspaces still in cache wantTblGone [][2]string // (ks, table) removed from Tables map wantTblPresent [][2]string // (ks, table) still in Tables map wantTblInvalid [][2]string // (ks, table) in tablesInvalidated wantTablets int // expected tablet count; -1 to skip check }{ { name: "keyspace/CREATED clears cache", keyspaces: map[string][]string{"test_ks": {"tbl_a", "tbl_b"}}, event: &frm.SchemaChangeKeyspace{Change: "CREATED", Keyspace: "test_ks"}, wantKsGone: []string{"test_ks"}, wantTablets: -1, }, { name: "keyspace/UPDATED clears cache and removes tablets", keyspaces: map[string][]string{"test_ks": {"tbl_a"}}, tablets: [][2]string{{"test_ks", "tbl_a"}}, event: &frm.SchemaChangeKeyspace{Change: "UPDATED", Keyspace: "test_ks"}, wantKsGone: []string{"test_ks"}, wantTablets: 0, }, { name: "keyspace/DROPPED clears cache and removes tablets", keyspaces: map[string][]string{"test_ks": {"tbl_a"}}, tablets: [][2]string{{"test_ks", "tbl_a"}}, event: &frm.SchemaChangeKeyspace{Change: "DROPPED", Keyspace: "test_ks"}, wantKsGone: []string{"test_ks"}, wantTablets: 0, }, { name: "keyspace/CREATED does not remove tablets", keyspaces: map[string][]string{"test_ks": {"tbl_a"}}, tablets: [][2]string{{"test_ks", "tbl_a"}}, event: &frm.SchemaChangeKeyspace{Change: "CREATED", Keyspace: "test_ks"}, wantKsGone: []string{"test_ks"}, wantTablets: 2, }, { name: "table/CREATED invalidates table only", keyspaces: map[string][]string{"test_ks": {"tbl_a", "tbl_b"}}, event: &frm.SchemaChangeTable{Change: "CREATED", Keyspace: "test_ks", Object: "tbl_a"}, wantKsPresent: []string{"test_ks"}, wantTblGone: [][2]string{{"test_ks", "tbl_a"}}, wantTblPresent: [][2]string{{"test_ks", "tbl_b"}}, wantTblInvalid: [][2]string{{"test_ks", "tbl_a"}}, wantTablets: -1, }, { name: "table/UPDATED invalidates table and removes tablets", keyspaces: map[string][]string{"test_ks": {"tbl_a", "tbl_b"}}, tablets: [][2]string{{"test_ks", "tbl_a"}}, event: &frm.SchemaChangeTable{Change: "UPDATED", Keyspace: "test_ks", Object: "tbl_a"}, wantKsPresent: []string{"test_ks"}, wantTblGone: [][2]string{{"test_ks", "tbl_a"}}, wantTblPresent: [][2]string{{"test_ks", "tbl_b"}}, wantTablets: 0, }, { name: "table/DROPPED removes tablets", keyspaces: map[string][]string{"test_ks": {"tbl_a"}}, tablets: [][2]string{{"test_ks", "tbl_a"}}, event: &frm.SchemaChangeTable{Change: "DROPPED", Keyspace: "test_ks", Object: "tbl_a"}, wantTablets: 0, }, { name: "table/CREATED does not remove tablets", keyspaces: map[string][]string{"test_ks": {"tbl_a"}}, tablets: [][2]string{{"test_ks", "tbl_a"}}, event: &frm.SchemaChangeTable{Change: "CREATED", Keyspace: "test_ks", Object: "tbl_a"}, wantTablets: 2, }, { name: "type/CREATED clears entire keyspace", keyspaces: map[string][]string{"test_ks": {"tbl_a", "tbl_b"}}, event: &frm.SchemaChangeType{Change: "CREATED", Keyspace: "test_ks", Object: "my_type"}, wantKsGone: []string{"test_ks"}, wantTablets: -1, }, { name: "function/CREATED clears entire keyspace", keyspaces: map[string][]string{"test_ks": {"tbl_a"}}, event: &frm.SchemaChangeFunction{Change: "CREATED", Keyspace: "test_ks", Name: "fn", Args: []string{"int"}}, wantKsGone: []string{"test_ks"}, wantTablets: -1, }, { name: "aggregate/CREATED clears entire keyspace", keyspaces: map[string][]string{"test_ks": {"tbl_a"}}, event: &frm.SchemaChangeAggregate{Change: "CREATED", Keyspace: "test_ks", Name: "agg", Args: []string{"int"}}, wantKsGone: []string{"test_ks"}, wantTablets: -1, }, // Cross-isolation { name: "keyspace/DROPPED does not affect other keyspace", keyspaces: map[string][]string{"ks_a": {"tbl_a"}, "ks_b": {"tbl_b"}}, event: &frm.SchemaChangeKeyspace{Change: "DROPPED", Keyspace: "ks_a"}, wantKsGone: []string{"ks_a"}, wantKsPresent: []string{"ks_b"}, wantTblPresent: [][2]string{{"ks_b", "tbl_b"}}, wantTablets: -1, }, { name: "table/UPDATED does not affect other tables", keyspaces: map[string][]string{"test_ks": {"tbl_a", "tbl_b", "tbl_c"}}, event: &frm.SchemaChangeTable{Change: "UPDATED", Keyspace: "test_ks", Object: "tbl_a"}, wantKsPresent: []string{"test_ks"}, wantTblGone: [][2]string{{"test_ks", "tbl_a"}}, wantTblPresent: [][2]string{{"test_ks", "tbl_b"}, {"test_ks", "tbl_c"}}, wantTablets: -1, }, // Tablet cross-isolation { name: "table/DROPPED for different table keeps tablets", keyspaces: map[string][]string{"test_ks": {"tbl_a", "tbl_b"}}, tablets: [][2]string{{"test_ks", "tbl_a"}}, event: &frm.SchemaChangeTable{Change: "DROPPED", Keyspace: "test_ks", Object: "tbl_b"}, wantTablets: 2, }, { name: "keyspace/DROPPED for different keyspace keeps tablets", keyspaces: map[string][]string{"ks_a": {"tbl_a"}, "ks_b": {"tbl_b"}}, tablets: [][2]string{{"ks_a", "tbl_a"}}, event: &frm.SchemaChangeKeyspace{Change: "DROPPED", Keyspace: "ks_b"}, wantTablets: 2, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() ctrl := &schemaDataMock{knownKeyspaces: map[string][]tableInfo{}} s := newSchemaEventTestSessionWithMock(ctrl) defer s.Close() for ks, tables := range tt.keyspaces { populateKeyspace(s, ks, tables...) } for _, tb := range tt.tablets { addTestTablets(t, s, tb[0], tb[1]) } s.handleSchemaEvent([]frame{tt.event}) s.metadataDescriber.metadata.tabletsMetadata.Flush() for _, ks := range tt.wantKsGone { if _, found := s.metadataDescriber.metadata.keyspaceMetadata.getKeyspace(ks); found { t.Errorf("keyspace %q should have been removed from cache", ks) } } for _, ks := range tt.wantKsPresent { if _, found := s.metadataDescriber.metadata.keyspaceMetadata.getKeyspace(ks); !found { t.Errorf("keyspace %q should still be in cache", ks) } } for _, pair := range tt.wantTblGone { ks, _ := s.metadataDescriber.metadata.keyspaceMetadata.getKeyspace(pair[0]) if ks != nil { if _, ok := ks.Tables[pair[1]]; ok { t.Errorf("table %s.%s should have been removed from Tables map", pair[0], pair[1]) } } } for _, pair := range tt.wantTblPresent { ks, _ := s.metadataDescriber.metadata.keyspaceMetadata.getKeyspace(pair[0]) if ks == nil { t.Errorf("keyspace %q not found when checking table %s", pair[0], pair[1]) } else if _, ok := ks.Tables[pair[1]]; !ok { t.Errorf("table %s.%s should still be in Tables map", pair[0], pair[1]) } } for _, pair := range tt.wantTblInvalid { ks, _ := s.metadataDescriber.metadata.keyspaceMetadata.getKeyspace(pair[0]) if ks == nil { t.Errorf("keyspace %q not found when checking tablesInvalidated %s", pair[0], pair[1]) } else if _, ok := ks.tablesInvalidated[pair[1]]; !ok { t.Errorf("table %s.%s should be in tablesInvalidated", pair[0], pair[1]) } } if tt.wantTablets >= 0 { if n := len(s.metadataDescriber.getTablets()); n != tt.wantTablets { t.Errorf("expected %d tablets, got %d", tt.wantTablets, n) } } }) } }) t.Run("callbacks", func(t *testing.T) { t.Parallel() tests := []struct { name string populateTables []string event frame wantSchemaAgreement int wantKsChanged []KeyspaceUpdateEvent }{ { name: "keyspace event calls schema agreement and policy", event: &frm.SchemaChangeKeyspace{Change: "UPDATED", Keyspace: "test_ks"}, wantSchemaAgreement: 1, wantKsChanged: []KeyspaceUpdateEvent{{Keyspace: "test_ks", Change: "UPDATED"}}, }, { name: "table event: no schema agreement, no policy callback", populateTables: []string{"tbl_a"}, event: &frm.SchemaChangeTable{Change: "CREATED", Keyspace: "test_ks", Object: "tbl_a"}, wantSchemaAgreement: 0, }, { name: "type event: no schema agreement, no policy callback", event: &frm.SchemaChangeType{Change: "CREATED", Keyspace: "test_ks", Object: "my_type"}, wantSchemaAgreement: 0, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() ctrl := &schemaDataMock{knownKeyspaces: map[string][]tableInfo{}} policy := &trackingPolicy{} s := newSchemaEventTestSession(ctrl, policy, "") defer s.Close() s.useSystemSchema = true populateKeyspace(s, "test_ks", tt.populateTables...) s.handleSchemaEvent([]frame{tt.event}) if got := ctrl.getAwaitSchemaAgreementCalls(); got != tt.wantSchemaAgreement { t.Fatalf("awaitSchemaAgreement: got %d, want %d", got, tt.wantSchemaAgreement) } kc := policy.getKeyspaceChangedCalls() if len(tt.wantKsChanged) == 0 { if len(kc) != 0 { t.Fatalf("KeyspaceChanged should not be called, got %+v", kc) } } else { if len(kc) != len(tt.wantKsChanged) { t.Fatalf("KeyspaceChanged: got %d calls, want %d", len(kc), len(tt.wantKsChanged)) } for i, want := range tt.wantKsChanged { if kc[i] != want { t.Errorf("KeyspaceChanged[%d]: got %+v, want %+v", i, kc[i], want) } } } }) } }) fullRefresh := func(ksName string) map[string]int { return map[string]int{ "SELECT durable_writes, replication FROM system_schema.keyspaces WHERE keyspace_name = ?": 1, "SELECT * FROM system_schema.tables WHERE keyspace_name = ?": 1, "SELECT " + columnMetadataColumns + " FROM system_schema.columns WHERE keyspace_name = ?": 1, "SELECT * FROM system_schema.types WHERE keyspace_name = ?": 1, "SELECT * FROM system_schema.indexes WHERE keyspace_name = ?": 1, "SELECT * FROM system_schema.views WHERE keyspace_name = ?": 1, fmt.Sprintf("DESCRIBE KEYSPACE %s WITH INTERNALS", ksName): 1, } } tableRefresh := map[string]int{ "SELECT * FROM system_schema.tables WHERE keyspace_name = ? AND table_name = ?": 1, "SELECT " + columnMetadataColumns + " FROM system_schema.columns WHERE keyspace_name = ? AND table_name = ?": 1, "SELECT * FROM system_schema.indexes WHERE keyspace_name = ? AND table_name = ?": 1, "SELECT * FROM system_schema.views WHERE keyspace_name = ? AND base_table_name = ? ALLOW FILTERING": 1, } noQueries := map[string]int{} assertExpectedQueries := func(t *testing.T, ctrl *schemaDataMock, expected map[string]int) { t.Helper() stmts := ctrl.getStatements() if len(expected) == 0 { if len(stmts) != 0 { t.Errorf("expected 0 queries, got %d:\n%s", len(stmts), strings.Join(stmts, "\n")) } return } wantTotal := 0 for stmt, wantCount := range expected { wantTotal += wantCount gotCount := 0 for _, s := range stmts { if s == stmt { gotCount++ } } if gotCount != wantCount { t.Errorf("query %q: got %d, want %d", stmt, gotCount, wantCount) } } if len(stmts) != wantTotal { t.Errorf("total queries: got %d, want %d\nqueries:\n%s", len(stmts), wantTotal, strings.Join(stmts, "\n")) } } t.Run("GetKeyspace", func(t *testing.T) { t.Parallel() tests := []struct { name string knownKeyspaces map[string][]tableInfo populateKs map[string][]string disableSystemSchema bool event frame // nil = no event getKeyspace string wantError bool expectedQueries map[string]int // nil = skip check; empty = expect 0 queries wantNoRequery bool // second identical call fires 0 queries }{ { name: "after keyspace event: refreshes and caches", knownKeyspaces: map[string][]tableInfo{ "test_ks": {{name: "tbl_a", columns: []columnInfo{{name: "id", kind: "partition_key", position: 0}}}}, }, populateKs: map[string][]string{"test_ks": {"tbl_a"}}, event: &frm.SchemaChangeKeyspace{Change: "UPDATED", Keyspace: "test_ks"}, getKeyspace: "test_ks", expectedQueries: fullRefresh("test_ks"), wantNoRequery: true, }, { name: "after table event: returns cached, no queries", populateKs: map[string][]string{"test_ks": {"tbl_a"}}, event: &frm.SchemaChangeTable{Change: "UPDATED", Keyspace: "test_ks", Object: "tbl_a"}, getKeyspace: "test_ks", expectedQueries: noQueries, }, { name: "after type event: refreshes and caches", knownKeyspaces: map[string][]tableInfo{ "test_ks": {{name: "tbl_a", columns: []columnInfo{{name: "id", kind: "partition_key", position: 0}}}}, }, populateKs: map[string][]string{"test_ks": {"tbl_a"}}, event: &frm.SchemaChangeType{Change: "CREATED", Keyspace: "test_ks", Object: "my_type"}, getKeyspace: "test_ks", expectedQueries: fullRefresh("test_ks"), wantNoRequery: true, }, { name: "uncached keyspace: refreshes and caches", knownKeyspaces: map[string][]tableInfo{"new_ks": {}}, getKeyspace: "new_ks", expectedQueries: fullRefresh("new_ks"), wantNoRequery: true, }, { name: "unknown keyspace: returns error", getKeyspace: "nonexistent", wantError: true, }, { name: "useSystemSchema=false: returns error", disableSystemSchema: true, getKeyspace: "test_ks", wantError: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() knownKs := tt.knownKeyspaces if knownKs == nil { knownKs = map[string][]tableInfo{} } ctrl := &schemaDataMock{knownKeyspaces: knownKs} s := newSchemaEventTestSessionWithMock(ctrl) defer s.Close() if tt.disableSystemSchema { s.useSystemSchema = false } for ks, tables := range tt.populateKs { populateKeyspace(s, ks, tables...) } if tt.event != nil { s.handleSchemaEvent([]frame{tt.event}) } ctrl.resetQueries() ks, err := s.metadataDescriber.GetKeyspace(tt.getKeyspace) if tt.wantError { if err == nil { t.Fatal("expected error") } return } if err != nil { t.Fatalf("GetKeyspace failed: %v", err) } if ks.Name != tt.getKeyspace { t.Fatalf("expected keyspace %s, got %s", tt.getKeyspace, ks.Name) } if tt.expectedQueries != nil { assertExpectedQueries(t, ctrl, tt.expectedQueries) } if tt.wantNoRequery { ctrl.resetQueries() _, err = s.metadataDescriber.GetKeyspace(tt.getKeyspace) if err != nil { t.Fatalf("second GetKeyspace failed: %v", err) } assertExpectedQueries(t, ctrl, noQueries) } }) } }) t.Run("GetTable", func(t *testing.T) { t.Parallel() tests := []struct { name string knownKeyspaces map[string][]tableInfo populateKs map[string][]string event frame getTable [2]string // [ks, table] wantError bool expectedQueries map[string]int // nil = skip check; empty = expect 0 queries expectNoRequery bool }{ { name: "after table event: refreshes only that table", knownKeyspaces: map[string][]tableInfo{ "test_ks": { {name: "tbl_a", columns: []columnInfo{{name: "id", kind: "partition_key", position: 0}}}, {name: "tbl_b", columns: []columnInfo{{name: "id", kind: "partition_key", position: 0}}}, }, }, populateKs: map[string][]string{"test_ks": {"tbl_a", "tbl_b"}}, event: &frm.SchemaChangeTable{Change: "UPDATED", Keyspace: "test_ks", Object: "tbl_a"}, getTable: [2]string{"test_ks", "tbl_a"}, expectedQueries: tableRefresh, expectNoRequery: true, }, { name: "after table event: other table returns cached directly", populateKs: map[string][]string{"test_ks": {"tbl_a", "tbl_b"}}, event: &frm.SchemaChangeTable{Change: "UPDATED", Keyspace: "test_ks", Object: "tbl_a"}, getTable: [2]string{"test_ks", "tbl_b"}, expectedQueries: noQueries, }, { name: "after keyspace event: refreshes full keyspace", knownKeyspaces: map[string][]tableInfo{ "test_ks": {{name: "tbl_a", columns: []columnInfo{{name: "id", kind: "partition_key", position: 0}}}}, }, populateKs: map[string][]string{"test_ks": {"tbl_a"}}, event: &frm.SchemaChangeKeyspace{Change: "UPDATED", Keyspace: "test_ks"}, getTable: [2]string{"test_ks", "tbl_a"}, expectedQueries: fullRefresh("test_ks"), expectNoRequery: true, }, { name: "after type event: refreshes full keyspace", knownKeyspaces: map[string][]tableInfo{ "test_ks": {{name: "tbl_a", columns: []columnInfo{{name: "id", kind: "partition_key", position: 0}}}}, }, populateKs: map[string][]string{"test_ks": {"tbl_a"}}, event: &frm.SchemaChangeType{Change: "CREATED", Keyspace: "test_ks", Object: "my_type"}, getTable: [2]string{"test_ks", "tbl_a"}, expectedQueries: fullRefresh("test_ks"), }, { name: "unknown table: returns error", knownKeyspaces: map[string][]tableInfo{ "test_ks": {{name: "tbl_a", columns: []columnInfo{{name: "id", kind: "partition_key", position: 0}}}}, }, populateKs: map[string][]string{"test_ks": {"tbl_a"}}, getTable: [2]string{"test_ks", "nonexistent"}, wantError: true, }, { name: "unknown keyspace: returns error", getTable: [2]string{"nonexistent", "tbl_a"}, wantError: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() knownKs := tt.knownKeyspaces if knownKs == nil { knownKs = map[string][]tableInfo{} } ctrl := &schemaDataMock{knownKeyspaces: knownKs} s := newSchemaEventTestSessionWithMock(ctrl) defer s.Close() for ks, tables := range tt.populateKs { populateKeyspace(s, ks, tables...) } if tt.event != nil { s.handleSchemaEvent([]frame{tt.event}) } ctrl.resetQueries() tbl, err := s.metadataDescriber.GetTable(tt.getTable[0], tt.getTable[1]) if tt.wantError { if err == nil { t.Fatal("expected error") } return } if err != nil { t.Fatalf("GetTable failed: %v", err) } if tbl.Name != tt.getTable[1] { t.Fatalf("expected table %s, got %s", tt.getTable[1], tbl.Name) } if tt.expectedQueries != nil { assertExpectedQueries(t, ctrl, tt.expectedQueries) } if tt.expectNoRequery { ctrl.resetQueries() _, err = s.metadataDescriber.GetTable(tt.getTable[0], tt.getTable[1]) if err != nil { t.Fatalf("second GetTable failed: %v", err) } assertExpectedQueries(t, ctrl, noQueries) } }) } }) t.Run("batch/multiple_table_events", func(t *testing.T) { t.Parallel() ctrl := &schemaDataMock{ knownKeyspaces: map[string][]tableInfo{ "test_ks": { {name: "tbl_a", columns: []columnInfo{{name: "id", kind: "partition_key", position: 0}}}, {name: "tbl_b", columns: []columnInfo{{name: "id", kind: "partition_key", position: 0}}}, {name: "tbl_c", columns: []columnInfo{{name: "id", kind: "partition_key", position: 0}}}, }, }, } s := newSchemaEventTestSessionWithMock(ctrl) defer s.Close() populateKeyspace(s, "test_ks", "tbl_a", "tbl_b", "tbl_c") s.handleSchemaEvent([]frame{ &frm.SchemaChangeTable{Change: "UPDATED", Keyspace: "test_ks", Object: "tbl_a"}, &frm.SchemaChangeTable{Change: "UPDATED", Keyspace: "test_ks", Object: "tbl_b"}, }) ks, _ := s.metadataDescriber.metadata.keyspaceMetadata.getKeyspace("test_ks") if _, ok := ks.Tables["tbl_a"]; ok { t.Fatal("tbl_a should be invalidated") } if _, ok := ks.Tables["tbl_b"]; ok { t.Fatal("tbl_b should be invalidated") } if _, ok := ks.Tables["tbl_c"]; !ok { t.Fatal("tbl_c should still be cached") } ctrl.resetQueries() _, err := s.metadataDescriber.GetTable("test_ks", "tbl_a") if err != nil { t.Fatalf("GetTable(tbl_a) failed: %v", err) } if ctrl.getQueryCount() == 0 { t.Fatal("expected queries for tbl_a") } ctrl.resetQueries() _, err = s.metadataDescriber.GetTable("test_ks", "tbl_b") if err != nil { t.Fatalf("GetTable(tbl_b) failed: %v", err) } if ctrl.getQueryCount() == 0 { t.Fatal("expected queries for tbl_b") } ctrl.resetQueries() _, err = s.metadataDescriber.GetTable("test_ks", "tbl_c") if err != nil { t.Fatalf("GetTable(tbl_c) failed: %v", err) } if got := ctrl.getQueryCount(); got != 0 { t.Fatalf("tbl_c not invalidated, expected 0 queries, got %d", got) } }) t.Run("batch/mixed_keyspace_and_table_events", func(t *testing.T) { t.Parallel() ctrl := &schemaDataMock{ knownKeyspaces: map[string][]tableInfo{ "test_ks": { {name: "tbl_a", columns: []columnInfo{{name: "id", kind: "partition_key", position: 0}}}, }, }, } policy := &trackingPolicy{} s := newSchemaEventTestSession(ctrl, policy, "") defer s.Close() s.useSystemSchema = true s.hasAggregatesAndFunctions = false populateKeyspace(s, "test_ks", "tbl_a") s.handleSchemaEvent([]frame{ &frm.SchemaChangeTable{Change: "CREATED", Keyspace: "test_ks", Object: "tbl_a"}, &frm.SchemaChangeKeyspace{Change: "UPDATED", Keyspace: "test_ks"}, }) if got := ctrl.getAwaitSchemaAgreementCalls(); got != 1 { t.Fatalf("awaitSchemaAgreement: got %d, want 1", got) } if _, found := s.metadataDescriber.metadata.keyspaceMetadata.getKeyspace("test_ks"); found { t.Fatal("keyspace should have been removed from cache by keyspace event") } ctrl.resetQueries() ks, err := s.metadataDescriber.GetKeyspace("test_ks") if err != nil { t.Fatalf("GetKeyspace failed: %v", err) } if ks.Name != "test_ks" { t.Fatalf("expected test_ks, got %s", ks.Name) } if got := ctrl.getQueryCount(); got == 0 { t.Fatal("expected queries after keyspace was cleared") } }) } // TestSchemaRefreshConcurrent validates that concurrent GetKeyspace/GetTable // calls for an uncached or invalidated keyspace result in only a single set // of schema queries, not one per caller. func TestSchemaRefreshConcurrent(t *testing.T) { t.Parallel() const concurrency = 10 knownKeyspaces := map[string][]tableInfo{ "test_ks": { {name: "tbl_a", columns: []columnInfo{{name: "id", kind: "partition_key", position: 0}}}, }, } fullRefreshCount := 7 // keyspace + tables + columns + types + indexes + views + DESCRIBE tableRefreshCount := 4 // tables + columns + indexes + views (filtered by table_name) t.Run("GetKeyspace/uncached", func(t *testing.T) { t.Parallel() ctrl := &schemaDataMock{ knownKeyspaces: knownKeyspaces, queryDelay: 10 * time.Millisecond, } s := newSchemaEventTestSessionWithMock(ctrl) defer s.Close() var wg sync.WaitGroup for range concurrency { wg.Add(1) go func() { defer wg.Done() _, _ = s.metadataDescriber.GetKeyspace("test_ks") }() } wg.Wait() if got := ctrl.getQueryCount(); got != fullRefreshCount { t.Errorf("expected %d queries (single full refresh), got %d", fullRefreshCount, got) } }) t.Run("GetKeyspace/after_invalidation", func(t *testing.T) { t.Parallel() ctrl := &schemaDataMock{ knownKeyspaces: knownKeyspaces, queryDelay: 10 * time.Millisecond, } s := newSchemaEventTestSessionWithMock(ctrl) defer s.Close() populateKeyspace(s, "test_ks", "tbl_a") s.handleSchemaEvent([]frame{ &frm.SchemaChangeKeyspace{Change: "UPDATED", Keyspace: "test_ks"}, }) ctrl.resetQueries() var wg sync.WaitGroup for range concurrency { wg.Add(1) go func() { defer wg.Done() _, _ = s.metadataDescriber.GetKeyspace("test_ks") }() } wg.Wait() if got := ctrl.getQueryCount(); got != fullRefreshCount { t.Errorf("expected %d queries (single full refresh), got %d", fullRefreshCount, got) } }) t.Run("GetTable/after_table_invalidation", func(t *testing.T) { t.Parallel() ctrl := &schemaDataMock{ knownKeyspaces: knownKeyspaces, queryDelay: 10 * time.Millisecond, } s := newSchemaEventTestSessionWithMock(ctrl) defer s.Close() populateKeyspace(s, "test_ks", "tbl_a") s.handleSchemaEvent([]frame{ &frm.SchemaChangeTable{Change: "UPDATED", Keyspace: "test_ks", Object: "tbl_a"}, }) ctrl.resetQueries() var wg sync.WaitGroup for range concurrency { wg.Add(1) go func() { defer wg.Done() _, _ = s.metadataDescriber.GetTable("test_ks", "tbl_a") }() } wg.Wait() if got := ctrl.getQueryCount(); got != tableRefreshCount { t.Errorf("expected %d queries (single table refresh), got %d", tableRefreshCount, got) } }) t.Run("GetTable/stale_snapshot_after_refresh_does_not_refresh_twice", func(t *testing.T) { t.Parallel() ctrl := &schemaDataMock{ knownKeyspaces: knownKeyspaces, } s := newSchemaEventTestSessionWithMock(ctrl) defer s.Close() populateKeyspace(s, "test_ks", "tbl_a") s.handleSchemaEvent([]frame{ &frm.SchemaChangeTable{Change: "UPDATED", Keyspace: "test_ks", Object: "tbl_a"}, }) staleKeyspace, wasReloaded, err := s.metadataDescriber.getKeyspaceInternal("test_ks") if err != nil { t.Fatalf("getKeyspaceInternal returned unexpected error: %v", err) } if _, found := staleKeyspace.Tables["tbl_a"]; found { t.Fatal("expected stale keyspace snapshot to have invalidated table removed") } if err := s.metadataDescriber.deduplicatedRefreshTable("test_ks", "tbl_a"); err != nil { t.Fatalf("deduplicatedRefreshTable returned unexpected error: %v", err) } ctrl.resetQueries() tbl, refreshNeeded, err := s.metadataDescriber.getTableFromSnapshot("test_ks", "tbl_a", staleKeyspace, wasReloaded) if err != nil { t.Fatalf("getTableFromSnapshot returned unexpected error: %v", err) } if refreshNeeded { t.Fatal("expected latest published keyspace metadata to suppress an extra refresh") } if tbl == nil || tbl.Name != "tbl_a" { t.Fatalf("unexpected table metadata: %#v", tbl) } if got := ctrl.getQueryCount(); got != 0 { t.Fatalf("expected stale snapshot lookup to avoid extra queries, got %d", got) } }) } // TestConcurrentSchemaRefreshErrorHandling verifies that concurrent // GetKeyspace and GetTable calls behave correctly when the underlying // schema queries succeed or fail, including mixed scenarios where // errors are injected mid-flight. func TestConcurrentSchemaRefreshErrorHandling(t *testing.T) { t.Parallel() const concurrency = 10 defaultTables := map[string][]tableInfo{ "test_ks": { {name: "tbl_a", columns: []columnInfo{{name: "id", kind: "partition_key", position: 0}}}, {name: "tbl_b", columns: []columnInfo{{name: "pk", kind: "partition_key", position: 0}}}, }, } t.Run("GetKeyspace/all_succeed", func(t *testing.T) { t.Parallel() ctrl := &schemaDataMock{ knownKeyspaces: defaultTables, queryDelay: 10 * time.Millisecond, } s := newSchemaEventTestSessionWithMock(ctrl) defer s.Close() var wg sync.WaitGroup results := make([]*KeyspaceMetadata, concurrency) errs := make([]error, concurrency) for i := range concurrency { wg.Add(1) go func(idx int) { defer wg.Done() results[idx], errs[idx] = s.metadataDescriber.GetKeyspace("test_ks") }(i) } wg.Wait() for i := range concurrency { if errs[i] != nil { t.Errorf("goroutine %d: unexpected error: %v", i, errs[i]) } if results[i] == nil { t.Errorf("goroutine %d: got nil metadata", i) } else if results[i].Name != "test_ks" { t.Errorf("goroutine %d: expected keyspace test_ks, got %s", i, results[i].Name) } } }) t.Run("GetKeyspace/all_fail", func(t *testing.T) { t.Parallel() injectedErr := fmt.Errorf("injected query failure") ctrl := &schemaDataMock{ knownKeyspaces: defaultTables, queryDelay: 10 * time.Millisecond, queryError: injectedErr, } s := newSchemaEventTestSessionWithMock(ctrl) defer s.Close() var wg sync.WaitGroup errs := make([]error, concurrency) for i := range concurrency { wg.Add(1) go func(idx int) { defer wg.Done() _, errs[idx] = s.metadataDescriber.GetKeyspace("test_ks") }(i) } wg.Wait() for i := range concurrency { if errs[i] == nil { t.Errorf("goroutine %d: expected error, got nil", i) } } }) t.Run("GetKeyspace/fail_then_succeed", func(t *testing.T) { t.Parallel() // First wave fails, second wave succeeds — verifies singleflight // does not cache the error permanently. injectedErr := fmt.Errorf("transient failure") ctrl := &schemaDataMock{ knownKeyspaces: defaultTables, queryDelay: 10 * time.Millisecond, queryError: injectedErr, } s := newSchemaEventTestSessionWithMock(ctrl) defer s.Close() // Wave 1: all fail. var wg sync.WaitGroup for range concurrency { wg.Add(1) go func() { defer wg.Done() _, _ = s.metadataDescriber.GetKeyspace("test_ks") }() } wg.Wait() // Clear error and retry — should succeed. ctrl.setQueryError(nil) ctrl.resetQueries() ks, err := s.metadataDescriber.GetKeyspace("test_ks") if err != nil { t.Fatalf("second attempt should succeed, got: %v", err) } if ks.Name != "test_ks" { t.Fatalf("expected keyspace test_ks, got %s", ks.Name) } }) t.Run("GetKeyspace/nonexistent_keyspace", func(t *testing.T) { t.Parallel() ctrl := &schemaDataMock{ knownKeyspaces: defaultTables, queryDelay: 10 * time.Millisecond, } s := newSchemaEventTestSessionWithMock(ctrl) defer s.Close() var wg sync.WaitGroup errs := make([]error, concurrency) for i := range concurrency { wg.Add(1) go func(idx int) { defer wg.Done() _, errs[idx] = s.metadataDescriber.GetKeyspace("no_such_ks") }(i) } wg.Wait() for i := range concurrency { if errs[i] == nil { t.Errorf("goroutine %d: expected ErrKeyspaceDoesNotExist, got nil", i) } } }) t.Run("GetTable/all_succeed", func(t *testing.T) { t.Parallel() ctrl := &schemaDataMock{ knownKeyspaces: defaultTables, queryDelay: 10 * time.Millisecond, } s := newSchemaEventTestSessionWithMock(ctrl) defer s.Close() populateKeyspace(s, "test_ks", "tbl_a") s.metadataDescriber.invalidateTableSchema("test_ks", "tbl_a") var wg sync.WaitGroup results := make([]*TableMetadata, concurrency) errs := make([]error, concurrency) for i := range concurrency { wg.Add(1) go func(idx int) { defer wg.Done() results[idx], errs[idx] = s.metadataDescriber.GetTable("test_ks", "tbl_a") }(i) } wg.Wait() for i := range concurrency { if errs[i] != nil { t.Errorf("goroutine %d: unexpected error: %v", i, errs[i]) } if results[i] == nil { t.Errorf("goroutine %d: got nil table metadata", i) } else if results[i].Name != "tbl_a" { t.Errorf("goroutine %d: expected tbl_a, got %s", i, results[i].Name) } } }) t.Run("GetTable/all_fail", func(t *testing.T) { t.Parallel() injectedErr := fmt.Errorf("injected table query failure") ctrl := &schemaDataMock{ knownKeyspaces: defaultTables, queryError: injectedErr, queryDelay: 10 * time.Millisecond, } s := newSchemaEventTestSessionWithMock(ctrl) defer s.Close() populateKeyspace(s, "test_ks", "tbl_a") s.metadataDescriber.invalidateTableSchema("test_ks", "tbl_a") var wg sync.WaitGroup errs := make([]error, concurrency) for i := range concurrency { wg.Add(1) go func(idx int) { defer wg.Done() _, errs[idx] = s.metadataDescriber.GetTable("test_ks", "tbl_a") }(i) } wg.Wait() for i := range concurrency { if errs[i] == nil { t.Errorf("goroutine %d: expected error, got nil", i) } } }) t.Run("GetTable/fail_then_succeed", func(t *testing.T) { t.Parallel() injectedErr := fmt.Errorf("transient table failure") ctrl := &schemaDataMock{ knownKeyspaces: defaultTables, queryDelay: 10 * time.Millisecond, queryError: injectedErr, } s := newSchemaEventTestSessionWithMock(ctrl) defer s.Close() populateKeyspace(s, "test_ks", "tbl_a") s.metadataDescriber.invalidateTableSchema("test_ks", "tbl_a") // Wave 1: all fail. var wg sync.WaitGroup for range concurrency { wg.Add(1) go func() { defer wg.Done() _, _ = s.metadataDescriber.GetTable("test_ks", "tbl_a") }() } wg.Wait() // Clear error, re-invalidate (the failed refresh may have left // tablesInvalidated in an inconsistent state), and retry. ctrl.setQueryError(nil) ctrl.resetQueries() s.metadataDescriber.invalidateTableSchema("test_ks", "tbl_a") tbl, err := s.metadataDescriber.GetTable("test_ks", "tbl_a") if err != nil { t.Fatalf("second attempt should succeed, got: %v", err) } if tbl.Name != "tbl_a" { t.Fatalf("expected tbl_a, got %s", tbl.Name) } }) t.Run("GetTable/nonexistent_table", func(t *testing.T) { t.Parallel() ctrl := &schemaDataMock{ knownKeyspaces: defaultTables, queryDelay: 10 * time.Millisecond, } s := newSchemaEventTestSessionWithMock(ctrl) defer s.Close() populateKeyspace(s, "test_ks", "tbl_a") var wg sync.WaitGroup errs := make([]error, concurrency) for i := range concurrency { wg.Add(1) go func(idx int) { defer wg.Done() _, errs[idx] = s.metadataDescriber.GetTable("test_ks", "no_such_table") }(i) } wg.Wait() for i := range concurrency { if errs[i] == nil { t.Errorf("goroutine %d: expected ErrNotFound, got nil", i) } } }) t.Run("GetKeyspace_and_GetTable/concurrent_mixed", func(t *testing.T) { t.Parallel() // Exercises the interplay between concurrent keyspace and table // refreshes hitting the singleflight groups simultaneously. ctrl := &schemaDataMock{ knownKeyspaces: defaultTables, queryDelay: 5 * time.Millisecond, } s := newSchemaEventTestSessionWithMock(ctrl) defer s.Close() var wg sync.WaitGroup ksErrs := make([]error, concurrency) tblErrs := make([]error, concurrency) for i := range concurrency { wg.Add(2) go func(idx int) { defer wg.Done() _, ksErrs[idx] = s.metadataDescriber.GetKeyspace("test_ks") }(i) go func(idx int) { defer wg.Done() _, tblErrs[idx] = s.metadataDescriber.GetTable("test_ks", "tbl_a") }(i) } wg.Wait() for i := range concurrency { if ksErrs[i] != nil { t.Errorf("GetKeyspace goroutine %d: unexpected error: %v", i, ksErrs[i]) } if tblErrs[i] != nil { t.Errorf("GetTable goroutine %d: unexpected error: %v", i, tblErrs[i]) } } }) t.Run("GetTable/different_tables_concurrent", func(t *testing.T) { t.Parallel() // Two different tables invalidated concurrently: each gets its own // singleflight key, so both refresh independently. ctrl := &schemaDataMock{ knownKeyspaces: defaultTables, queryDelay: 5 * time.Millisecond, } s := newSchemaEventTestSessionWithMock(ctrl) defer s.Close() populateKeyspace(s, "test_ks", "tbl_a", "tbl_b") s.metadataDescriber.invalidateTableSchema("test_ks", "tbl_a") s.metadataDescriber.invalidateTableSchema("test_ks", "tbl_b") var wg sync.WaitGroup aErrs := make([]error, concurrency) bErrs := make([]error, concurrency) for i := range concurrency { wg.Add(2) go func(idx int) { defer wg.Done() _, aErrs[idx] = s.metadataDescriber.GetTable("test_ks", "tbl_a") }(i) go func(idx int) { defer wg.Done() _, bErrs[idx] = s.metadataDescriber.GetTable("test_ks", "tbl_b") }(i) } wg.Wait() for i := range concurrency { if aErrs[i] != nil { t.Errorf("tbl_a goroutine %d: unexpected error: %v", i, aErrs[i]) } if bErrs[i] != nil { t.Errorf("tbl_b goroutine %d: unexpected error: %v", i, bErrs[i]) } } // Verify both tables are now cached. for _, name := range []string{"tbl_a", "tbl_b"} { tbl, err := s.metadataDescriber.GetTable("test_ks", name) if err != nil { t.Errorf("GetTable(%s) after refresh: %v", name, err) } else if tbl.Name != name { t.Errorf("expected %s, got %s", name, tbl.Name) } } }) t.Run("GetTable/different_tables_one_fails", func(t *testing.T) { t.Parallel() // tbl_a exists in the mock, tbl_x does not — concurrent refreshes // for both: one succeeds, one gets ErrNotFound. ctrl := &schemaDataMock{ knownKeyspaces: defaultTables, queryDelay: 5 * time.Millisecond, } s := newSchemaEventTestSessionWithMock(ctrl) defer s.Close() populateKeyspace(s, "test_ks", "tbl_a", "tbl_x") s.metadataDescriber.invalidateTableSchema("test_ks", "tbl_a") s.metadataDescriber.invalidateTableSchema("test_ks", "tbl_x") var wg sync.WaitGroup aErrs := make([]error, concurrency) xErrs := make([]error, concurrency) for i := range concurrency { wg.Add(2) go func(idx int) { defer wg.Done() _, aErrs[idx] = s.metadataDescriber.GetTable("test_ks", "tbl_a") }(i) go func(idx int) { defer wg.Done() _, xErrs[idx] = s.metadataDescriber.GetTable("test_ks", "tbl_x") }(i) } wg.Wait() for i := range concurrency { if aErrs[i] != nil { t.Errorf("tbl_a goroutine %d: unexpected error: %v", i, aErrs[i]) } if xErrs[i] == nil { t.Errorf("tbl_x goroutine %d: expected ErrNotFound, got nil", i) } } }) } ================================================ FILE: example_batch_test.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql_test import ( "context" "fmt" "log" "github.com/gocql/gocql" ) // Example_batch demonstrates how to execute a batch of statements. func Example_batch() { /* The example assumes the following CQL was used to setup the keyspace: create keyspace example with replication = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 }; create table example.batches(pk int, ck int, description text, PRIMARY KEY(pk, ck)); */ cluster := gocql.NewCluster("localhost:9042") cluster.Keyspace = "example" cluster.ProtoVersion = 4 session, err := cluster.CreateSession() if err != nil { log.Fatal(err) } defer session.Close() ctx := context.Background() b := session.Batch(gocql.UnloggedBatch).WithContext(ctx) b.Entries = append(b.Entries, gocql.BatchEntry{ Stmt: "INSERT INTO example.batches (pk, ck, description) VALUES (?, ?, ?)", Args: []any{1, 2, "1.2"}, Idempotent: true, }) b.Entries = append(b.Entries, gocql.BatchEntry{ Stmt: "INSERT INTO example.batches (pk, ck, description) VALUES (?, ?, ?)", Args: []any{1, 3, "1.3"}, Idempotent: true, }) err = session.ExecuteBatch(b) if err != nil { log.Fatal(err) } err = b.Query("INSERT INTO example.batches (pk, ck, description) VALUES (?, ?, ?)", 1, 4, "1.4"). Query("INSERT INTO example.batches (pk, ck, description) VALUES (?, ?, ?)", 1, 5, "1.5"). Exec() if err != nil { log.Fatal(err) } scanner := session.Query("SELECT pk, ck, description FROM example.batches").Iter().Scanner() for scanner.Next() { var pk, ck int32 var description string err = scanner.Scan(&pk, &ck, &description) if err != nil { log.Fatal(err) } fmt.Println(pk, ck, description) } // 1 2 1.2 // 1 3 1.3 // 1 4 1.4 // 1 5 1.5 } ================================================ FILE: example_dynamic_columns_test.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql_test import ( "context" "fmt" "log" "os" "reflect" "text/tabwriter" gocql "github.com/gocql/gocql" ) // Example_dynamicColumns demonstrates how to handle dynamic column list. func Example_dynamicColumns() { /* The example assumes the following CQL was used to setup the keyspace: create keyspace example with replication = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 }; create table example.table1(pk text, ck int, value1 text, value2 int, PRIMARY KEY(pk, ck)); insert into example.table1 (pk, ck, value1, value2) values ('a', 1, 'b', 2); insert into example.table1 (pk, ck, value1, value2) values ('c', 3, 'd', 4); insert into example.table1 (pk, ck, value1, value2) values ('c', 5, null, null); create table example.table2(pk int, value1 timestamp, PRIMARY KEY(pk)); insert into example.table2 (pk, value1) values (1, '2020-01-02 03:04:05'); */ cluster := gocql.NewCluster("localhost:9042") cluster.Keyspace = "example" cluster.ProtoVersion = 4 session, err := cluster.CreateSession() if err != nil { log.Fatal(err) } defer session.Close() printQuery := func(ctx context.Context, session *gocql.Session, stmt string, values ...any) error { iter := session.Query(stmt, values...).WithContext(ctx).Iter() fmt.Println(stmt) w := tabwriter.NewWriter(os.Stdout, 0, 0, 1, ' ', 0) for i, columnInfo := range iter.Columns() { if i > 0 { fmt.Fprint(w, "\t| ") } fmt.Fprintf(w, "%s (%s)", columnInfo.Name, columnInfo.TypeInfo) } for { rd, err := iter.RowData() if err != nil { return err } if !iter.Scan(rd.Values...) { break } fmt.Fprint(w, "\n") for i, val := range rd.Values { if i > 0 { fmt.Fprint(w, "\t| ") } fmt.Fprint(w, reflect.Indirect(reflect.ValueOf(val)).Interface()) } } fmt.Fprint(w, "\n") w.Flush() fmt.Println() return iter.Close() } ctx := context.Background() err = printQuery(ctx, session, "SELECT * FROM table1") if err != nil { log.Fatal(err) } err = printQuery(ctx, session, "SELECT value2, pk, ck FROM table1") if err != nil { log.Fatal(err) } err = printQuery(ctx, session, "SELECT * FROM table2") if err != nil { log.Fatal(err) } // SELECT * FROM table1 // pk (varchar) | ck (int) | value1 (varchar) | value2 (int) // a | 1 | b | 2 // c | 3 | d | 4 // c | 5 | | 0 // // SELECT value2, pk, ck FROM table1 // value2 (int) | pk (varchar) | ck (int) // 2 | a | 1 // 4 | c | 3 // 0 | c | 5 // // SELECT * FROM table2 // pk (int) | value1 (timestamp) // 1 | 2020-01-02 03:04:05 +0000 UTC } ================================================ FILE: example_lwt_batch_test.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql_test import ( "context" "fmt" "log" "github.com/gocql/gocql" ) // ExampleSession_MapExecuteBatchCAS demonstrates how to execute a batch lightweight transaction. func ExampleSession_MapExecuteBatchCAS() { /* The example assumes the following CQL was used to setup the keyspace: create keyspace example with replication = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 }; create table example.my_lwt_batch_table(pk text, ck text, version int, value text, PRIMARY KEY(pk, ck)); */ cluster := gocql.NewCluster("localhost:9042") cluster.Keyspace = "example" cluster.ProtoVersion = 4 session, err := cluster.CreateSession() if err != nil { log.Fatal(err) } defer session.Close() ctx := context.Background() err = session.Query("INSERT INTO example.my_lwt_batch_table (pk, ck, version, value) VALUES (?, ?, ?, ?)", "pk1", "ck1", 1, "a").WithContext(ctx).Exec() if err != nil { log.Fatal(err) } err = session.Query("INSERT INTO example.my_lwt_batch_table (pk, ck, version, value) VALUES (?, ?, ?, ?)", "pk1", "ck2", 1, "A").WithContext(ctx).Exec() if err != nil { log.Fatal(err) } executeBatch := func(ck2Version int) { b := session.Batch(gocql.LoggedBatch) b.Entries = append(b.Entries, gocql.BatchEntry{ Stmt: "UPDATE my_lwt_batch_table SET value=? WHERE pk=? AND ck=? IF version=?", Args: []any{"b", "pk1", "ck1", 1}, }) b.Entries = append(b.Entries, gocql.BatchEntry{ Stmt: "UPDATE my_lwt_batch_table SET value=? WHERE pk=? AND ck=? IF version=?", Args: []any{"B", "pk1", "ck2", ck2Version}, }) m := make(map[string]any) applied, iter, err := session.MapExecuteBatchCAS(b.WithContext(ctx), m) if err != nil { log.Fatal(err) } fmt.Println(applied, m) m = make(map[string]any) for iter.MapScan(m) { fmt.Println(m) m = make(map[string]any) } if err := iter.Close(); err != nil { log.Fatal(err) } } printState := func() { scanner := session.Query("SELECT ck, value FROM example.my_lwt_batch_table WHERE pk = ?", "pk1"). WithContext(ctx).Iter().Scanner() for scanner.Next() { var ck, value string err = scanner.Scan(&ck, &value) if err != nil { log.Fatal(err) } fmt.Println(ck, value) } if err := scanner.Err(); err != nil { log.Fatal(err) } } executeBatch(0) printState() executeBatch(1) printState() // false map[ck:ck1 pk:pk1 version:1] // map[[applied]:false ck:ck2 pk:pk1 version:1] // ck1 a // ck2 A // true map[] // ck1 b // ck2 B } ================================================ FILE: example_lwt_test.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql_test import ( "context" "fmt" "log" gocql "github.com/gocql/gocql" ) // ExampleQuery_MapScanCAS demonstrates how to execute a single-statement lightweight transaction. func ExampleQuery_MapScanCAS() { /* The example assumes the following CQL was used to setup the keyspace: create keyspace example with replication = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 }; create table example.my_lwt_table(pk int, version int, value text, PRIMARY KEY(pk)); */ cluster := gocql.NewCluster("localhost:9042") cluster.Keyspace = "example" cluster.ProtoVersion = 4 session, err := cluster.CreateSession() if err != nil { log.Fatal(err) } defer session.Close() ctx := context.Background() err = session.Query("INSERT INTO example.my_lwt_table (pk, version, value) VALUES (?, ?, ?)", 1, 1, "a").WithContext(ctx).Exec() if err != nil { log.Fatal(err) } m := make(map[string]any) applied, err := session.Query("UPDATE example.my_lwt_table SET value = ? WHERE pk = ? IF version = ?", "b", 1, 0).WithContext(ctx).MapScanCAS(m) if err != nil { log.Fatal(err) } fmt.Println(applied, m) var value string err = session.Query("SELECT value FROM example.my_lwt_table WHERE pk = ?", 1).WithContext(ctx). Scan(&value) if err != nil { log.Fatal(err) } fmt.Println(value) m = make(map[string]any) applied, err = session.Query("UPDATE example.my_lwt_table SET value = ? WHERE pk = ? IF version = ?", "b", 1, 1).WithContext(ctx).MapScanCAS(m) if err != nil { log.Fatal(err) } fmt.Println(applied, m) var value2 string err = session.Query("SELECT value FROM example.my_lwt_table WHERE pk = ?", 1).WithContext(ctx). Scan(&value2) if err != nil { log.Fatal(err) } fmt.Println(value2) // false map[version:1] // a // true map[] // b } ================================================ FILE: example_marshaler_test.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql_test import ( "context" "fmt" "log" "strconv" "strings" gocql "github.com/gocql/gocql" ) // MyMarshaler implements Marshaler and Unmarshaler. // It represents a version number stored as string. type MyMarshaler struct { major, minor, patch int } func (m MyMarshaler) MarshalCQL(info gocql.TypeInfo) ([]byte, error) { return gocql.Marshal(info, fmt.Sprintf("%d.%d.%d", m.major, m.minor, m.patch)) } func (m *MyMarshaler) UnmarshalCQL(info gocql.TypeInfo, data []byte) error { var s string err := gocql.Unmarshal(info, data, &s) if err != nil { return err } parts := strings.SplitN(s, ".", 3) if len(parts) != 3 { return fmt.Errorf("parse version %q: %d parts instead of 3", s, len(parts)) } major, err := strconv.Atoi(parts[0]) if err != nil { return fmt.Errorf("parse version %q major number: %v", s, err) } minor, err := strconv.Atoi(parts[1]) if err != nil { return fmt.Errorf("parse version %q minor number: %v", s, err) } patch, err := strconv.Atoi(parts[2]) if err != nil { return fmt.Errorf("parse version %q patch number: %v", s, err) } m.major = major m.minor = minor m.patch = patch return nil } // Example_marshalerUnmarshaler demonstrates how to implement a Marshaler and Unmarshaler. func Example_marshalerUnmarshaler() { /* The example assumes the following CQL was used to setup the keyspace: create keyspace example with replication = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 }; create table example.my_marshaler_table(pk int, value text, PRIMARY KEY(pk)); */ cluster := gocql.NewCluster("localhost:9042") cluster.Keyspace = "example" cluster.ProtoVersion = 4 session, err := cluster.CreateSession() if err != nil { log.Fatal(err) } defer session.Close() ctx := context.Background() value := MyMarshaler{ major: 1, minor: 2, patch: 3, } err = session.Query("INSERT INTO example.my_marshaler_table (pk, value) VALUES (?, ?)", 1, value).WithContext(ctx).Exec() if err != nil { log.Fatal(err) } var stringValue string err = session.Query("SELECT value FROM example.my_marshaler_table WHERE pk = 1").WithContext(ctx). Scan(&stringValue) if err != nil { log.Fatal(err) } fmt.Println(stringValue) var unmarshaledValue MyMarshaler err = session.Query("SELECT value FROM example.my_marshaler_table WHERE pk = 1").WithContext(ctx). Scan(&unmarshaledValue) if err != nil { log.Fatal(err) } fmt.Println(unmarshaledValue) // 1.2.3 // {1 2 3} } ================================================ FILE: example_nulls_test.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql_test import ( "fmt" "log" gocql "github.com/gocql/gocql" ) // Example_nulls demonstrates how to distinguish between null and zero value when needed. // // Null values are unmarshalled as zero value of the type. If you need to distinguish for example between text // column being null and empty string, you can unmarshal into *string field. func Example_nulls() { /* The example assumes the following CQL was used to setup the keyspace: create keyspace example with replication = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 }; create table example.stringvals(id int, value text, PRIMARY KEY(id)); insert into example.stringvals (id, value) values (1, null); insert into example.stringvals (id, value) values (2, ''); insert into example.stringvals (id, value) values (3, 'hello'); */ cluster := gocql.NewCluster("localhost:9042") cluster.Keyspace = "example" session, err := cluster.CreateSession() if err != nil { log.Fatal(err) } defer session.Close() scanner := session.Query(`SELECT id, value FROM stringvals`).Iter().Scanner() for scanner.Next() { var ( id int32 val *string ) err := scanner.Scan(&id, &val) if err != nil { log.Fatal(err) } if val != nil { fmt.Printf("Row %d is %q\n", id, *val) } else { fmt.Printf("Row %d is null\n", id) } } err = scanner.Err() if err != nil { log.Fatal(err) } // Row 1 is null // Row 2 is "" // Row 3 is "hello" } ================================================ FILE: example_paging_test.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql_test import ( "fmt" "log" gocql "github.com/gocql/gocql" ) // Example_paging demonstrates how to manually fetch pages and use page state. // // See also package documentation about paging. func Example_paging() { /* The example assumes the following CQL was used to setup the keyspace: create keyspace example with replication = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 }; create table example.itoa(id int, description text, PRIMARY KEY(id)); insert into example.itoa (id, description) values (1, 'one'); insert into example.itoa (id, description) values (2, 'two'); insert into example.itoa (id, description) values (3, 'three'); insert into example.itoa (id, description) values (4, 'four'); insert into example.itoa (id, description) values (5, 'five'); insert into example.itoa (id, description) values (6, 'six'); */ cluster := gocql.NewCluster("localhost:9042") cluster.Keyspace = "example" cluster.ProtoVersion = 4 session, err := cluster.CreateSession() if err != nil { log.Fatal(err) } defer session.Close() var pageState []byte for { // We use PageSize(2) for the sake of example, use larger values in production (default is 5000) for performance // reasons. iter := session.Query(`SELECT id, description FROM itoa`).PageSize(2).PageState(pageState).Iter() nextPageState := iter.PageState() scanner := iter.Scanner() for scanner.Next() { var ( id int description string ) err = scanner.Scan(&id, &description) if err != nil { log.Fatal(err) } fmt.Println(id, description) } err = scanner.Err() if err != nil { log.Fatal(err) } fmt.Printf("next page state: %+v\n", nextPageState) if len(nextPageState) == 0 { break } pageState = nextPageState } // 5 five // 1 one // next page state: [4 0 0 0 1 0 240 127 255 255 253 0] // 2 two // 4 four // next page state: [4 0 0 0 4 0 240 127 255 255 251 0] // 6 six // 3 three // next page state: [4 0 0 0 3 0 240 127 255 255 249 0] // next page state: [] } ================================================ FILE: example_set_test.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql_test import ( "fmt" "log" "sort" gocql "github.com/gocql/gocql" ) // Example_set demonstrates how to use sets. func Example_set() { /* The example assumes the following CQL was used to setup the keyspace: create keyspace example with replication = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 }; create table example.sets(id int, value set, PRIMARY KEY(id)); */ cluster := gocql.NewCluster("localhost:9042") cluster.Keyspace = "example" session, err := cluster.CreateSession() if err != nil { log.Fatal(err) } defer session.Close() err = session.Query(`UPDATE sets SET value=? WHERE id=1`, []string{"alpha", "beta", "gamma"}).Exec() if err != nil { log.Fatal(err) } err = session.Query(`UPDATE sets SET value=value+? WHERE id=1`, "epsilon").Exec() if err != nil { // This does not work because the ? expects a set, not a single item. fmt.Printf("expected error: %v\n", err) } err = session.Query(`UPDATE sets SET value=value+? WHERE id=1`, []string{"delta"}).Exec() if err != nil { log.Fatal(err) } // map[x]struct{} is supported too. toRemove := map[string]struct{}{ "alpha": {}, "gamma": {}, } err = session.Query(`UPDATE sets SET value=value-? WHERE id=1`, toRemove).Exec() if err != nil { log.Fatal(err) } scanner := session.Query(`SELECT id, value FROM sets`).Iter().Scanner() for scanner.Next() { var ( id int32 val []string ) err := scanner.Scan(&id, &val) if err != nil { log.Fatal(err) } sort.Strings(val) fmt.Printf("Row %d is %v\n", id, val) } err = scanner.Err() if err != nil { log.Fatal(err) } // expected error: can not marshal string into set(varchar) // Row 1 is [beta delta] } ================================================ FILE: example_test.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql_test import ( "context" "fmt" "log" gocql "github.com/gocql/gocql" ) func Example() { /* The example assumes the following CQL was used to setup the keyspace: create keyspace example with replication = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 }; create table example.tweet(timeline text, id UUID, text text, PRIMARY KEY(id)); create index on example.tweet(timeline); */ cluster := gocql.NewCluster("localhost:9042") cluster.Keyspace = "example" cluster.Consistency = gocql.Quorum // connect to the cluster session, err := cluster.CreateSession() if err != nil { log.Fatal(err) } defer session.Close() ctx := context.Background() // insert a tweet if err := session.Query(`INSERT INTO tweet (timeline, id, text) VALUES (?, ?, ?)`, "me", gocql.TimeUUID(), "hello world").WithContext(ctx).Exec(); err != nil { log.Fatal(err) } var id gocql.UUID var text string /* Search for a specific set of records whose 'timeline' column matches * the value 'me'. The secondary index that we created earlier will be * used for optimizing the search */ if err := session.Query(`SELECT id, text FROM tweet WHERE timeline = ? LIMIT 1`, "me").WithContext(ctx).Consistency(gocql.One).Scan(&id, &text); err != nil { log.Fatal(err) } fmt.Println("Tweet:", id, text) fmt.Println() // list all tweets scanner := session.Query(`SELECT id, text FROM tweet WHERE timeline = ?`, "me").WithContext(ctx).Iter().Scanner() for scanner.Next() { err = scanner.Scan(&id, &text) if err != nil { log.Fatal(err) } fmt.Println("Tweet:", id, text) } // scanner.Err() closes the iterator, so scanner nor iter should be used afterwards. if err := scanner.Err(); err != nil { log.Fatal(err) } // Tweet: cad53821-3731-11eb-971c-708bcdaada84 hello world // // Tweet: cad53821-3731-11eb-971c-708bcdaada84 hello world // Tweet: d577ab85-3731-11eb-81eb-708bcdaada84 hello world } ================================================ FILE: example_udt_map_test.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql_test import ( "context" "fmt" "log" gocql "github.com/gocql/gocql" ) // Example_userDefinedTypesMap demonstrates how to work with user-defined types as maps. // See also Example_userDefinedTypesStruct and examples for UDTMarshaler and UDTUnmarshaler if you want to map to structs. func Example_userDefinedTypesMap() { /* The example assumes the following CQL was used to setup the keyspace: create keyspace example with replication = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 }; create type example.my_udt (field_a text, field_b int); create table example.my_udt_table(pk int, value frozen, PRIMARY KEY(pk)); */ cluster := gocql.NewCluster("localhost:9042") cluster.Keyspace = "example" cluster.ProtoVersion = 4 session, err := cluster.CreateSession() if err != nil { log.Fatal(err) } defer session.Close() ctx := context.Background() value := map[string]any{ "field_a": "a value", "field_b": 42, } err = session.Query("INSERT INTO example.my_udt_table (pk, value) VALUES (?, ?)", 1, value).WithContext(ctx).Exec() if err != nil { log.Fatal(err) } var readValue map[string]any err = session.Query("SELECT value FROM example.my_udt_table WHERE pk = 1").WithContext(ctx).Scan(&readValue) if err != nil { log.Fatal(err) } fmt.Println(readValue["field_a"]) fmt.Println(readValue["field_b"]) // a value // 42 } ================================================ FILE: example_udt_marshaler_test.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql_test import ( "context" "log" gocql "github.com/gocql/gocql" ) // MyUDTMarshaler implements UDTMarshaler. type MyUDTMarshaler struct { fieldA string fieldB int32 } // MarshalUDT marshals the selected field to bytes. func (m MyUDTMarshaler) MarshalUDT(name string, info gocql.TypeInfo) ([]byte, error) { switch name { case "field_a": return gocql.Marshal(info, m.fieldA) case "field_b": return gocql.Marshal(info, m.fieldB) default: // If you want to be strict and return error un unknown field, you can do so here instead. // Returning nil, nil will set the value of unknown fields to null, which might be handy if you want // to be forward-compatible when a new field is added to the UDT. return nil, nil } } // ExampleUDTMarshaler demonstrates how to implement a UDTMarshaler. func ExampleUDTMarshaler() { /* The example assumes the following CQL was used to setup the keyspace: create keyspace example with replication = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 }; create type example.my_udt (field_a text, field_b int); create table example.my_udt_table(pk int, value frozen, PRIMARY KEY(pk)); */ cluster := gocql.NewCluster("localhost:9042") cluster.Keyspace = "example" cluster.ProtoVersion = 4 session, err := cluster.CreateSession() if err != nil { log.Fatal(err) } defer session.Close() ctx := context.Background() value := MyUDTMarshaler{ fieldA: "a value", fieldB: 42, } err = session.Query("INSERT INTO example.my_udt_table (pk, value) VALUES (?, ?)", 1, value).WithContext(ctx).Exec() if err != nil { log.Fatal(err) } } ================================================ FILE: example_udt_struct_test.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql_test import ( "context" "fmt" "log" gocql "github.com/gocql/gocql" ) type MyUDT struct { FieldA string `cql:"field_a"` FieldB int32 `cql:"field_b"` } // Example_userDefinedTypesStruct demonstrates how to work with user-defined types as structs. // See also examples for UDTMarshaler and UDTUnmarshaler if you need more control/better performance. func Example_userDefinedTypesStruct() { /* The example assumes the following CQL was used to setup the keyspace: create keyspace example with replication = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 }; create type example.my_udt (field_a text, field_b int); create table example.my_udt_table(pk int, value frozen, PRIMARY KEY(pk)); */ cluster := gocql.NewCluster("localhost:9042") cluster.Keyspace = "example" cluster.ProtoVersion = 4 session, err := cluster.CreateSession() if err != nil { log.Fatal(err) } defer session.Close() ctx := context.Background() value := MyUDT{ FieldA: "a value", FieldB: 42, } err = session.Query("INSERT INTO example.my_udt_table (pk, value) VALUES (?, ?)", 1, value).WithContext(ctx).Exec() if err != nil { log.Fatal(err) } var readValue MyUDT err = session.Query("SELECT value FROM example.my_udt_table WHERE pk = 1").WithContext(ctx).Scan(&readValue) if err != nil { log.Fatal(err) } fmt.Println(readValue.FieldA) fmt.Println(readValue.FieldB) // a value // 42 } ================================================ FILE: example_udt_unmarshaler_test.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql_test import ( "context" "fmt" "log" gocql "github.com/gocql/gocql" ) // MyUDTUnmarshaler implements UDTUnmarshaler. type MyUDTUnmarshaler struct { fieldA string fieldB int32 } // UnmarshalUDT unmarshals the field identified by name into MyUDTUnmarshaler. func (m *MyUDTUnmarshaler) UnmarshalUDT(name string, info gocql.TypeInfo, data []byte) error { switch name { case "field_a": return gocql.Unmarshal(info, data, &m.fieldA) case "field_b": return gocql.Unmarshal(info, data, &m.fieldB) default: // If you want to be strict and return error un unknown field, you can do so here instead. // Returning nil will ignore unknown fields, which might be handy if you want // to be forward-compatible when a new field is added to the UDT. return nil } } // ExampleUDTUnmarshaler demonstrates how to implement a UDTUnmarshaler. func ExampleUDTUnmarshaler() { /* The example assumes the following CQL was used to setup the keyspace: create keyspace example with replication = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 }; create type example.my_udt (field_a text, field_b int); create table example.my_udt_table(pk int, value frozen, PRIMARY KEY(pk)); insert into example.my_udt_table (pk, value) values (1, {field_a: 'a value', field_b: 42}); */ cluster := gocql.NewCluster("localhost:9042") cluster.Keyspace = "example" cluster.ProtoVersion = 4 session, err := cluster.CreateSession() if err != nil { log.Fatal(err) } defer session.Close() ctx := context.Background() var value MyUDTUnmarshaler err = session.Query("SELECT value FROM example.my_udt_table WHERE pk = 1").WithContext(ctx).Scan(&value) if err != nil { log.Fatal(err) } fmt.Println(value.fieldA) fmt.Println(value.fieldB) // a value // 42 } ================================================ FILE: exec.go ================================================ package gocql import ( "fmt" ) // SingleHostQueryExecutor allows to quickly execute diagnostic queries while // connected to only a single node. // The executor opens only a single connection to a node and does not use // connection pools. // Consistency level used is ONE. // Retry policy is applied, attempts are visible in query metrics but query // observer is not notified. type SingleHostQueryExecutor struct { session *Session control *controlConn } // Exec executes the query without returning any rows. func (e SingleHostQueryExecutor) Exec(stmt string, values ...any) error { return e.control.query(stmt, values...).Close() } // Iter executes the query and returns an iterator capable of iterating // over all results. func (e SingleHostQueryExecutor) Iter(stmt string, values ...any) *Iter { return e.control.query(stmt, values...) } func (e SingleHostQueryExecutor) Close() { if e.control != nil { e.control.close() } if e.session != nil { e.session.Close() } } // NewSingleHostQueryExecutor creates a SingleHostQueryExecutor by connecting // to one of the hosts specified in the ClusterConfig. // If ProtoVersion is not specified version 4 is used. // Caller is responsible for closing the executor after use. func NewSingleHostQueryExecutor(cfg *ClusterConfig) (e SingleHostQueryExecutor, err error) { // Check that hosts in the ClusterConfig is not empty if len(cfg.Hosts) < 1 { err = ErrNoHosts return } c := *cfg // If protocol version not set assume 4 and skip discovery if c.ProtoVersion == 0 { c.ProtoVersion = protoVersion4 } // Close in case of error defer func() { if err != nil { e.Close() } }() // Create uninitialised session c.disableInit = true if e.session, err = NewSession(c); err != nil { err = fmt.Errorf("new session: %w", err) return } var hosts []*HostInfo if hosts, err = resolveInitialEndpoints(c.DNSResolver, c.Hosts, c.Port, c.Logger); err != nil { err = fmt.Errorf("addrs to hosts: %w", err) return } // Create control connection to one of the hosts e.control = createControlConn(e.session) // shuffle endpoints so not all drivers will connect to the same initial // node. hosts = shuffleHosts(hosts) conncfg := *e.control.session.connCfg conncfg.disableCoalesce = true var conn *Conn for _, host := range hosts { conn, err = e.control.session.dial(e.control.session.ctx, host, &conncfg, e.control) if err != nil { e.control.session.logger.Printf("gocql: unable to dial control conn %v:%v: %v\n", host.ConnectAddress(), host.Port(), err) continue } err = e.control.setupConn(conn) if err == nil { conn.finalizeConnection() break } e.control.session.logger.Printf("gocql: unable setup control conn %v:%v: %v\n", host.ConnectAddress(), host.Port(), err) conn.Close() conn = nil } if conn == nil { err = fmt.Errorf("setup: %w", err) return } return } ================================================ FILE: exec_test.go ================================================ //go:build integration // +build integration package gocql import ( "testing" ) func TestSingleHostQueryExecutor(t *testing.T) { t.Parallel() cluster := createCluster() e, err := NewSingleHostQueryExecutor(cluster) if err != nil { t.Fatal(err) } defer e.Close() iter := e.Iter("SELECT now() FROM system.local") var date []byte iter.Scan(&date) if err := iter.Close(); err != nil { t.Fatal(err) } if len(date) == 0 { t.Fatal("expected date") } } ================================================ FILE: export_test.go ================================================ //go:build integration // +build integration package gocql var FlagRunSslTest = flagRunSslTest var FlagDistribution = flagDistribution var CreateCluster = createCluster var TestLogger = &testLogger{} var WaitUntilPoolsStopFilling = waitUntilPoolsStopFilling func GetRingAllHosts(sess *Session) []*HostInfo { return sess.hostSource.getHostsList() } ================================================ FILE: filters.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import "fmt" // HostFilter interface is used when a host is discovered via server sent events. type HostFilter interface { // Called when a new host is discovered, returning true will cause the host // to be added to the pools. Accept(host *HostInfo) bool } // HostFilterFunc converts a func(host HostInfo) bool into a HostFilter type HostFilterFunc func(host *HostInfo) bool func (fn HostFilterFunc) Accept(host *HostInfo) bool { return fn(host) } // AcceptAllFilter will accept all hosts func AcceptAllFilter() HostFilter { return HostFilterFunc(func(host *HostInfo) bool { return true }) } func DenyAllFilter() HostFilter { return HostFilterFunc(func(host *HostInfo) bool { return false }) } // DataCenterHostFilter filters all hosts such that they are in the same data center // as the supplied data center. func DataCenterHostFilter(dataCenter string) HostFilter { return HostFilterFunc(func(host *HostInfo) bool { return host.DataCenter() == dataCenter }) } // Deprecated: Use DataCenterHostFilter instead. // DataCentreHostFilter is an alias that doesn't use the preferred spelling. func DataCentreHostFilter(dataCenter string) HostFilter { return DataCenterHostFilter(dataCenter) } // WhiteListHostFilter filters incoming hosts by checking that their address is // in the initial hosts whitelist. It probes all known addresses of a host // (connect, rpc, broadcast, listen, peer, preferred, translated CQL) for a match. func WhiteListHostFilter(hosts ...string) HostFilter { hostInfos, err := resolveInitialEndpoints(defaultDnsResolver, hosts, 9042, nopLogger{}) if err != nil { // dont want to panic here, but rather not break the API panic(fmt.Errorf("unable to lookup host info from address: %v", err)) } m := make(map[string]bool, len(hostInfos)) for _, host := range hostInfos { m[host.ConnectAddress().String()] = true } return HostFilterFunc(func(host *HostInfo) bool { host.mu.RLock() defer host.mu.RUnlock() if validIpAddr(host.rpcAddress) && m[host.rpcAddress.String()] { return true } if validIpAddr(host.broadcastAddress) && m[host.broadcastAddress.String()] { return true } if validIpAddr(host.listenAddress) && m[host.listenAddress.String()] { return true } if validIpAddr(host.peer) && m[host.peer.String()] { return true } if validIpAddr(host.preferredIP) && m[host.preferredIP.String()] { return true } if host.translatedAddresses != nil && host.translatedAddresses.CQL.IsValid() && m[host.translatedAddresses.CQL.Address.String()] { return true } return false }) } ================================================ FILE: filters_test.go ================================================ //go:build unit // +build unit /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "net" "testing" ) func TestFilter_WhiteList(t *testing.T) { t.Parallel() f := WhiteListHostFilter("127.0.0.1", "127.0.0.2") tests := [...]struct { addr net.IP accept bool }{ {net.ParseIP("127.0.0.1"), true}, {net.ParseIP("127.0.0.2"), true}, {net.ParseIP("127.0.0.3"), false}, } for i, test := range tests { if f.Accept(&HostInfo{rpcAddress: test.addr}) { if !test.accept { t.Errorf("%d: should not have been accepted but was", i) } } else if test.accept { t.Errorf("%d: should have been accepted but wasn't", i) } } } func TestFilter_AllowAll(t *testing.T) { t.Parallel() f := AcceptAllFilter() tests := [...]struct { addr net.IP accept bool }{ {net.ParseIP("127.0.0.1"), true}, {net.ParseIP("127.0.0.2"), true}, {net.ParseIP("127.0.0.3"), true}, } for i, test := range tests { if f.Accept(&HostInfo{connectAddress: test.addr}) { if !test.accept { t.Errorf("%d: should not have been accepted but was", i) } } else if test.accept { t.Errorf("%d: should have been accepted but wasn't", i) } } } func TestFilter_DenyAll(t *testing.T) { t.Parallel() f := DenyAllFilter() tests := [...]struct { addr net.IP accept bool }{ {net.ParseIP("127.0.0.1"), false}, {net.ParseIP("127.0.0.2"), false}, {net.ParseIP("127.0.0.3"), false}, } for i, test := range tests { if f.Accept(&HostInfo{connectAddress: test.addr}) { if !test.accept { t.Errorf("%d: should not have been accepted but was", i) } } else if test.accept { t.Errorf("%d: should have been accepted but wasn't", i) } } } func TestFilter_WhiteList_MatchesRPCAddress(t *testing.T) { t.Parallel() f := WhiteListHostFilter("127.0.0.1") host := &HostInfo{ connectAddress: net.ParseIP("10.0.0.1"), rpcAddress: net.ParseIP("127.0.0.1"), } if !f.Accept(host) { t.Error("should have been accepted via rpcAddress but wasn't") } } func TestFilter_WhiteList_MatchesBroadcastAddress(t *testing.T) { t.Parallel() f := WhiteListHostFilter("127.0.0.1") host := &HostInfo{ connectAddress: net.ParseIP("10.0.0.1"), broadcastAddress: net.ParseIP("127.0.0.1"), } if !f.Accept(host) { t.Error("should have been accepted via broadcastAddress but wasn't") } } func TestFilter_WhiteList_MatchesListenAddress(t *testing.T) { t.Parallel() f := WhiteListHostFilter("127.0.0.1") host := &HostInfo{ connectAddress: net.ParseIP("10.0.0.1"), listenAddress: net.ParseIP("127.0.0.1"), } if !f.Accept(host) { t.Error("should have been accepted via listenAddress but wasn't") } } func TestFilter_WhiteList_MatchesPeer(t *testing.T) { t.Parallel() f := WhiteListHostFilter("127.0.0.1") host := &HostInfo{ connectAddress: net.ParseIP("10.0.0.1"), peer: net.ParseIP("127.0.0.1"), } if !f.Accept(host) { t.Error("should have been accepted via peer but wasn't") } } func TestFilter_WhiteList_MatchesPreferredIP(t *testing.T) { t.Parallel() f := WhiteListHostFilter("127.0.0.1") host := &HostInfo{ connectAddress: net.ParseIP("10.0.0.1"), preferredIP: net.ParseIP("127.0.0.1"), } if !f.Accept(host) { t.Error("should have been accepted via preferredIP but wasn't") } } func TestFilter_WhiteList_MatchesTranslatedAddress(t *testing.T) { t.Parallel() f := WhiteListHostFilter("127.0.0.1") host := &HostInfo{ connectAddress: net.ParseIP("10.0.0.1"), translatedAddresses: &translatedAddresses{ CQL: AddressPort{Address: net.ParseIP("127.0.0.1"), Port: 9042}, }, } if !f.Accept(host) { t.Error("should have been accepted via translatedAddresses but wasn't") } } func TestFilter_WhiteList_NoMatchWhenNoAddressMatches(t *testing.T) { t.Parallel() f := WhiteListHostFilter("127.0.0.1") host := &HostInfo{ connectAddress: net.ParseIP("10.0.0.1"), rpcAddress: net.ParseIP("10.0.0.2"), broadcastAddress: net.ParseIP("10.0.0.3"), listenAddress: net.ParseIP("10.0.0.4"), peer: net.ParseIP("10.0.0.5"), preferredIP: net.ParseIP("10.0.0.6"), } if f.Accept(host) { t.Error("should not have been accepted but was") } } func TestFilter_WhiteList_EmptyHost(t *testing.T) { t.Parallel() f := WhiteListHostFilter("127.0.0.1") host := &HostInfo{} if f.Accept(host) { t.Error("empty host should not have been accepted") } } func TestFilter_DataCenter(t *testing.T) { t.Parallel() f := DataCenterHostFilter("dc1") fDeprecated := DataCentreHostFilter("dc1") tests := [...]struct { dc string accept bool }{ {"dc1", true}, {"dc2", false}, } for i, test := range tests { if f.Accept(&HostInfo{dataCenter: test.dc}) { if !test.accept { t.Errorf("%d: should not have been accepted but was", i) } } else if test.accept { t.Errorf("%d: should have been accepted but wasn't", i) } if f.Accept(&HostInfo{dataCenter: test.dc}) != fDeprecated.Accept(&HostInfo{dataCenter: test.dc}) { t.Errorf("%d: DataCenterHostFilter and DataCentreHostFilter should be the same", i) } } } ================================================ FILE: frame.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2012, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "context" "encoding/binary" "errors" "fmt" "io" "net" "runtime" "strconv" "strings" "sync/atomic" "time" frm "github.com/gocql/gocql/internal/frame" ) type unsetColumn struct{} // UnsetValue represents a value used in a query binding that will be ignored by Cassandra. // // By setting a field to the unset value Cassandra will ignore the write completely. // The main advantage is the ability to keep the same prepared statement even when you don't // want to update some fields, where before you needed to make another prepared statement. // // UnsetValue is only available when using the version 4 of the protocol. var UnsetValue = unsetColumn{} type namedValue struct { value any name string } // NamedValue produce a value which will bind to the named parameter in a query func NamedValue(name string, value any) any { return &namedValue{ name: name, value: value, } } const ( protoDirectionMask = 0x80 protoVersionMask = 0x7F protoVersion1 = 0x01 protoVersion2 = 0x02 protoVersion3 = 0x03 protoVersion4 = 0x04 protoVersion5 = 0x05 maxFrameSize = 256 * 1024 * 1024 ) // DEPRECATED use Consistency type, SerialConsistency is now an alias for backwards compatibility. type SerialConsistency = Consistency type Consistency uint16 const ( Any Consistency = 0x00 One Consistency = 0x01 Two Consistency = 0x02 Three Consistency = 0x03 Quorum Consistency = 0x04 All Consistency = 0x05 LocalQuorum Consistency = 0x06 EachQuorum Consistency = 0x07 Serial Consistency = 0x08 LocalSerial Consistency = 0x09 LocalOne Consistency = 0x0A ) func (c Consistency) String() string { switch c { case Any: return "ANY" case One: return "ONE" case Two: return "TWO" case Three: return "THREE" case Quorum: return "QUORUM" case All: return "ALL" case LocalQuorum: return "LOCAL_QUORUM" case EachQuorum: return "EACH_QUORUM" case Serial: return "SERIAL" case LocalSerial: return "LOCAL_SERIAL" case LocalOne: return "LOCAL_ONE" default: return fmt.Sprintf("UNKNOWN_CONS_0x%x", uint16(c)) } } func (c Consistency) IsSerial() bool { return c == Serial || c == LocalSerial } func (c Consistency) MarshalText() (text []byte, err error) { return []byte(c.String()), nil } func (c *Consistency) UnmarshalText(text []byte) error { switch string(text) { case "ANY": *c = Any case "ONE": *c = One case "TWO": *c = Two case "THREE": *c = Three case "QUORUM": *c = Quorum case "ALL": *c = All case "LOCAL_QUORUM": *c = LocalQuorum case "EACH_QUORUM": *c = EachQuorum case "SERIAL": *c = Serial case "LOCAL_SERIAL": *c = LocalSerial case "LOCAL_ONE": *c = LocalOne default: return fmt.Errorf("invalid consistency %q", string(text)) } return nil } func ParseConsistency(s string) Consistency { var c Consistency if err := c.UnmarshalText([]byte(strings.ToUpper(s))); err != nil { panic(err) } return c } // ParseConsistencyWrapper wraps gocql.ParseConsistency to provide an err // return instead of a panic func ParseConsistencyWrapper(s string) (consistency Consistency, err error) { err = consistency.UnmarshalText([]byte(strings.ToUpper(s))) return } const ( apacheCassandraTypePrefix = "org.apache.cassandra.db.marshal." ) var ( ErrFrameTooBig = errors.New("frame length is bigger than the maximum allowed") ) func readInt(p []byte) int32 { return int32(binary.BigEndian.Uint32(p[:4])) } const defaultBufSize = 128 type ObservedFrameHeader struct { // StartHeader is the time we started reading the frame header off the network connection. Start time.Time // EndHeader is the time we finished reading the frame header off the network connection. End time.Time // Host is Host of the connection the frame header was read from. Host *HostInfo Length int32 Stream int16 Version frm.ProtoVersion Flags byte Opcode frm.Op } func (f ObservedFrameHeader) String() string { return fmt.Sprintf("[observed header version=%s flags=0x%x stream=%d op=%s length=%d]", f.Version, f.Flags, f.Stream, f.Opcode, f.Length) } // FrameHeaderObserver is the interface implemented by frame observers / stat collectors. // // Experimental, this interface and use may change type FrameHeaderObserver interface { // ObserveFrameHeader gets called on every received frame header. ObserveFrameHeader(context.Context, ObservedFrameHeader) } // framerInterface represents a frame reader/writer for the CQL protocol. // // Framers are pooled and reused. Any byte slices returned from frame parsing // methods may be backed by pooled buffers that are reused after Release() is // called. If data must outlive the framer, use readBytesCopy() instead of // readBytes() when implementing parseFrame(), or copy returned byte slices // before calling Release(). // // After Release() is called, the framer and any slices derived from its // buffers must not be accessed. type framerInterface interface { ReadBytesInternal() ([]byte, error) GetCustomPayload() map[string][]byte GetHeaderWarnings() []string // Release returns the framer to its pool (if pooled). // Must be called when the framer is no longer needed. // Safe to call multiple times; subsequent calls are no-ops. Release() } const headSize = 9 // a framer is responsible for reading, writing and parsing frames on a single stream type framer struct { compressor Compressor header *frm.FrameHeader customPayload map[string][]byte release func() traceID []byte readBuffer []byte buf []byte flagLWT int rateLimitingErrorCode int flags byte proto byte tabletsRoutingV1 bool released atomic.Bool } func newFramer(compressor Compressor, version byte) *framer { buf := make([]byte, defaultBufSize) f := &framer{ buf: buf[:0], readBuffer: buf, } var flags byte if compressor != nil { flags |= frm.FlagCompress } if version == protoVersion5 { flags |= frm.FlagBetaProtocol } version &= protoVersionMask f.compressor = compressor f.proto = version f.flags = flags f.header = nil f.traceID = nil f.tabletsRoutingV1 = false return f } // Release returns the framer to its pool. If the framer was not obtained // from a pool (release is nil), this is a no-op. // // Conn.releaseFramer owns the released-state guard, so this method delegates // directly to the release closure. func (f *framer) Release() { if f.release != nil { f.release() } } func newFramerWithExts(compressor Compressor, version byte, cqlProtoExts []cqlProtocolExtension, logger StdLogger) *framer { f := newFramer(compressor, version) if lwtExt := findCQLProtoExtByName(cqlProtoExts, lwtAddMetadataMarkKey); lwtExt != nil { castedExt, ok := lwtExt.(*lwtAddMetadataMarkExt) if !ok { logger.Println( fmt.Errorf("failed to cast CQL protocol extension identified by name %s to type %T", lwtAddMetadataMarkKey, lwtAddMetadataMarkExt{})) return f } f.flagLWT = castedExt.lwtOptMetaBitMask } if rateLimitErrorExt := findCQLProtoExtByName(cqlProtoExts, rateLimitError); rateLimitErrorExt != nil { castedExt, ok := rateLimitErrorExt.(*rateLimitExt) if !ok { logger.Println( fmt.Errorf("failed to cast CQL protocol extension identified by name %s to type %T", rateLimitError, rateLimitExt{})) return f } f.rateLimitingErrorCode = castedExt.rateLimitErrorCode } if tabletsExt := findCQLProtoExtByName(cqlProtoExts, tabletsRoutingV1); tabletsExt != nil { _, ok := tabletsExt.(*tabletsRoutingV1Ext) if !ok { logger.Println( fmt.Errorf("failed to cast CQL protocol extension identified by name %s to type %T", tabletsRoutingV1, tabletsRoutingV1Ext{})) return f } f.tabletsRoutingV1 = true } return f } type frame interface { Header() frm.FrameHeader } func readHeader(r io.Reader, p []byte) (head frm.FrameHeader, err error) { _, err = io.ReadFull(r, p[:headSize]) if err != nil { return frm.FrameHeader{}, err } head.Version = frm.ProtoVersion(p[0]) version := head.Version.Version() if version < protoVersion3 || version > protoVersion5 { return frm.FrameHeader{}, fmt.Errorf("gocql: unsupported protocol response version: %d", version) } head.Flags = p[1] head.Stream = int(int16(binary.BigEndian.Uint16(p[2:4]))) head.Op = frm.Op(p[4]) head.Length = int(readInt(p[5:])) return head, nil } // explicitly enables tracing for the framers outgoing requests func (f *framer) trace() { f.flags |= frm.FlagTracing } // explicitly enables the custom payload flag func (f *framer) payload() { f.flags |= frm.FlagCustomPayload } // reads a frame form the wire into the framers buffer func (f *framer) readFrame(r io.Reader, head *frm.FrameHeader) error { if head.Length < 0 { return fmt.Errorf("frame body length can not be less than 0: %d", head.Length) } else if head.Length > maxFrameSize { // need to free up the connection to be used again _, err := io.CopyN(io.Discard, r, int64(head.Length)) if err != nil { return fmt.Errorf("error whilst trying to discard frame with invalid length: %v", err) } return ErrFrameTooBig } if cap(f.readBuffer) >= head.Length { f.buf = f.readBuffer[:head.Length] } else { f.readBuffer = make([]byte, head.Length) f.buf = f.readBuffer } // assume the underlying reader takes care of timeouts and retries n, err := io.ReadFull(r, f.buf) if err != nil { return fmt.Errorf("unable to read frame body: read %d/%d bytes: %v", n, head.Length, err) } if head.Flags&frm.FlagCompress == frm.FlagCompress { if f.compressor == nil { return NewErrProtocol("no compressor available with compressed frame body") } f.buf, err = f.compressor.Decode(f.buf) if err != nil { return err } } f.header = head return nil } func (f *framer) parseFrame() (frame frame, err error) { defer func() { if r := recover(); r != nil { if _, ok := r.(runtime.Error); ok { panic(r) } err = r.(error) } }() if f.header.Version.Request() { return nil, NewErrProtocol("got a request frame from server: %v", f.header.Version) } if f.header.Flags&frm.FlagTracing == frm.FlagTracing { f.readTrace() } if f.header.Flags&frm.FlagWarning == frm.FlagWarning { f.header.Warnings = f.readStringList() } if f.header.Flags&frm.FlagCustomPayload == frm.FlagCustomPayload { f.customPayload = f.readBytesMap() } // assumes that the frame body has been read into rbuf switch f.header.Op { case frm.OpError: frame = f.parseErrorFrame() case frm.OpReady: frame = f.parseReadyFrame() case frm.OpResult: frame, err = f.parseResultFrame() case frm.OpSupported: frame = f.parseSupportedFrame() case frm.OpAuthenticate: frame = f.parseAuthenticateFrame() case frm.OpAuthChallenge: frame = f.parseAuthChallengeFrame() case frm.OpAuthSuccess: frame = f.parseAuthSuccessFrame() case frm.OpEvent: frame = f.parseEventFrame() default: return nil, NewErrProtocol("unknown op in frame header: %s", f.header.Op) } return } func (f *framer) parseErrorFrame() frame { code := f.readInt() msg := f.readString() errD := frm.ErrorFrame{ FrameHeader: *f.header, Code: code, Message: msg, } switch code { case ErrCodeUnavailable: cl := f.readConsistency() required := f.readInt() alive := f.readInt() return &RequestErrUnavailable{ ErrorFrame: errD, Consistency: cl, Required: required, Alive: alive, } case ErrCodeWriteTimeout: cl := f.readConsistency() received := f.readInt() blockfor := f.readInt() writeType := f.readString() return &RequestErrWriteTimeout{ ErrorFrame: errD, Consistency: cl, Received: received, BlockFor: blockfor, WriteType: writeType, } case ErrCodeReadTimeout: cl := f.readConsistency() received := f.readInt() blockfor := f.readInt() dataPresent := f.readByte() return &RequestErrReadTimeout{ ErrorFrame: errD, Consistency: cl, Received: received, BlockFor: blockfor, DataPresent: dataPresent, } case ErrCodeAlreadyExists: ks := f.readString() table := f.readString() return &RequestErrAlreadyExists{ ErrorFrame: errD, Keyspace: ks, Table: table, } case ErrCodeUnprepared: return &RequestErrUnprepared{ ErrorFrame: errD, StatementId: f.readShortBytesCopy(), } case ErrCodeReadFailure: res := &RequestErrReadFailure{ ErrorFrame: errD, } res.Consistency = f.readConsistency() res.Received = f.readInt() res.BlockFor = f.readInt() if f.proto > protoVersion4 { res.ErrorMap = f.readErrorMap() res.NumFailures = len(res.ErrorMap) } else { res.NumFailures = f.readInt() } res.DataPresent = f.readByte() != 0 return res case ErrCodeWriteFailure: res := &RequestErrWriteFailure{ ErrorFrame: errD, } res.Consistency = f.readConsistency() res.Received = f.readInt() res.BlockFor = f.readInt() if f.proto > protoVersion4 { res.ErrorMap = f.readErrorMap() res.NumFailures = len(res.ErrorMap) } else { res.NumFailures = f.readInt() } res.WriteType = f.readString() return res case ErrCodeFunctionFailure: res := &RequestErrFunctionFailure{ ErrorFrame: errD, } res.Keyspace = f.readString() res.Function = f.readString() res.ArgTypes = f.readStringList() return res case ErrCodeCDCWriteFailure: res := &RequestErrCDCWriteFailure{ ErrorFrame: errD, } return res case ErrCodeCASWriteUnknown: res := &RequestErrCASWriteUnknown{ ErrorFrame: errD, } res.Consistency = f.readConsistency() res.Received = f.readInt() res.BlockFor = f.readInt() return res case ErrCodeInvalid, ErrCodeBootstrapping, ErrCodeConfig, ErrCodeCredentials, ErrCodeOverloaded, ErrCodeProtocol, ErrCodeServer, ErrCodeSyntax, ErrCodeTruncate, ErrCodeUnauthorized: // TODO(zariel): we should have some distinct types for these errors return errD default: if f.rateLimitingErrorCode != 0 && code == f.rateLimitingErrorCode { res := &RequestErrRateLimitReached{ ErrorFrame: errD, } res.OpType = OpType(f.readByte()) res.RejectedByCoordinator = f.readByte() != 0 return res } else { return &UnknownServerError{ ErrorFrame: errD, } } } } func (f *framer) readErrorMap() (errMap ErrorMap) { errMap = make(ErrorMap) numErrs := f.readInt() for i := 0; i < numErrs; i++ { ip := f.readInetAdressOnly().String() errMap[ip] = f.readShort() } return } func (f *framer) writeHeader(flags byte, op frm.Op, stream int) { f.buf = append(f.buf[:0], f.proto, flags, byte(stream>>8), byte(stream), // pad out length byte(op), 0, 0, 0, 0, ) } func (f *framer) setLength(length int) { f.buf[5] = byte(length >> 24) f.buf[6] = byte(length >> 16) f.buf[7] = byte(length >> 8) f.buf[8] = byte(length) } func (f *framer) finish() error { bufLen := len(f.buf) if bufLen > maxFrameSize { // huge app frame, lets remove it so it doesn't bloat the heap f.buf = make([]byte, defaultBufSize) return ErrFrameTooBig } if f.buf[1]&frm.FlagCompress == frm.FlagCompress { if f.compressor == nil { panic("compress flag set with no compressor") } // TODO: only compress frames which are big enough compressed, err := f.compressor.Encode(f.buf[headSize:]) if err != nil { return err } f.buf = append(f.buf[:headSize], compressed...) bufLen = len(f.buf) } length := bufLen - headSize f.setLength(length) return nil } func (f *framer) writeTo(w io.Writer) error { _, err := w.Write(f.buf) return err } func (f *framer) readTrace() { if len(f.buf) < 16 { panic(fmt.Errorf("not enough bytes in buffer to read trace uuid require 16 got: %d", len(f.buf))) } if len(f.traceID) != 16 { f.traceID = make([]byte, 16) } copy(f.traceID, f.buf[:16]) f.buf = f.buf[16:] } func (f *framer) parseReadyFrame() frame { return &frm.ReadyFrame{ FrameHeader: *f.header, } } // TODO: if we move the body buffer onto the frameHeader then we only need a single // framer, and can move the methods onto the header. func (f *framer) parseSupportedFrame() frame { return &frm.SupportedFrame{ FrameHeader: *f.header, Supported: f.readStringMultiMap(), } } type writeStartupFrame struct { opts map[string]string } func (w writeStartupFrame) String() string { return fmt.Sprintf("[startup opts=%+v]", w.opts) } func (w *writeStartupFrame) buildFrame(f *framer, streamID int) error { f.writeHeader(f.flags&^frm.FlagCompress, frm.OpStartup, streamID) f.writeStringMap(w.opts) return f.finish() } type writePrepareFrame struct { customPayload map[string][]byte statement string keyspace string } func (w *writePrepareFrame) buildFrame(f *framer, streamID int) error { if len(w.customPayload) > 0 { f.payload() } f.writeHeader(f.flags, frm.OpPrepare, streamID) f.writeCustomPayload(&w.customPayload) f.writeLongString(w.statement) var flags uint32 = 0 if w.keyspace != "" { if f.proto > protoVersion4 { flags |= frm.FlagWithPreparedKeyspace } else { panic(fmt.Errorf("the keyspace can only be set with protocol 5 or higher")) } } if f.proto > protoVersion4 { f.writeUint(flags) } if w.keyspace != "" { f.writeString(w.keyspace) } return f.finish() } func (f *framer) readTypeInfo() TypeInfo { // TODO: factor this out so the same code paths can be used to parse custom // types and other types, as much of the logic will be duplicated. id := f.readShort() simple := NativeType{ proto: f.proto, typ: Type(id), } // Fast path for simple native types (through TypeDuration). if id > 0 && id <= uint16(TypeDuration) { return simple } if simple.typ == TypeCustom { simple.custom = f.readString() if cassType := getApacheCassandraType(simple.custom); cassType != TypeCustom { simple.typ = cassType } } switch simple.typ { case TypeTuple: n := f.readShort() tuple := TupleTypeInfo{ NativeType: simple, Elems: make([]TypeInfo, n), } for i := 0; i < int(n); i++ { tuple.Elems[i] = f.readTypeInfo() } return tuple case TypeUDT: udt := UDTTypeInfo{ NativeType: simple, } udt.KeySpace = f.readString() udt.Name = f.readString() n := f.readShort() udt.Elements = make([]UDTField, n) for i := 0; i < int(n); i++ { field := &udt.Elements[i] field.Name = f.readString() field.Type = f.readTypeInfo() } return udt case TypeMap, TypeList, TypeSet: collection := CollectionType{ NativeType: simple, } if simple.typ == TypeMap { collection.Key = f.readTypeInfo() } collection.Elem = f.readTypeInfo() return collection case TypeCustom: vectorTypePrefix := apacheCassandraTypePrefix + "VectorType" if strings.HasPrefix(simple.custom, vectorTypePrefix) { spec := strings.TrimPrefix(simple.custom, vectorTypePrefix) spec = spec[1 : len(spec)-1] // remove parenthesis idx := strings.LastIndex(spec, ",") typeStr := spec[:idx] dimStr := spec[idx+1:] subType := getCassandraLongType(strings.TrimSpace(typeStr), f.proto, nopLogger{}) dim, _ := strconv.Atoi(strings.TrimSpace(dimStr)) vector := VectorType{ NativeType: simple, SubType: subType, Dimensions: dim, } return vector } } return simple } type preparedMetadata struct { keyspace string table string // proto v4+ pkeyColumns []int resultMetadata // LWT query detected lwt bool } func (r preparedMetadata) String() string { return fmt.Sprintf("[prepared flags=0x%x pkey=%v paging_state=% X columns=%v col_count=%d actual_col_count=%d lwt=%t]", r.flags, r.pkeyColumns, r.pagingState, r.columns, r.colCount, r.actualColCount, r.lwt) } func (f *framer) parsePreparedMetadata() preparedMetadata { // TODO: deduplicate this from parseMetadata meta := preparedMetadata{} meta.flags = f.readInt() meta.colCount = f.readInt() if meta.colCount < 0 { panic(fmt.Errorf("received negative column count: %d", meta.colCount)) } meta.actualColCount = meta.colCount if f.proto >= protoVersion4 { pkeyCount := f.readInt() pkeys := make([]int, pkeyCount) for i := 0; i < pkeyCount; i++ { pkeys[i] = int(f.readShort()) } meta.pkeyColumns = pkeys } meta.lwt = meta.flags&f.flagLWT == f.flagLWT if meta.flags&frm.FlagHasMorePages == frm.FlagHasMorePages { meta.pagingState = f.readBytesCopy() } if meta.flags&frm.FlagNoMetaData == frm.FlagNoMetaData { return meta } globalSpec := meta.flags&frm.FlagGlobalTableSpec == frm.FlagGlobalTableSpec if globalSpec { meta.keyspace = f.readString() meta.table = f.readString() } var cols []ColumnInfo readPerColumnSpec := !globalSpec var tracker keyspaceTableTracker if meta.colCount < 1000 { // preallocate columninfo to avoid excess copying cols = make([]ColumnInfo, meta.colCount) for i := 0; i < meta.colCount; i++ { col := &cols[i] keyspace, table := f.readColWithSpec(col, &meta.resultMetadata, globalSpec, meta.keyspace, meta.table, i, readPerColumnSpec) if readPerColumnSpec { tracker.track(i, keyspace, table) } } } else { // use append, huge number of columns usually indicates a corrupt frame or // just a huge row. for i := 0; i < meta.colCount; i++ { var col ColumnInfo keyspace, table := f.readColWithSpec(&col, &meta.resultMetadata, globalSpec, meta.keyspace, meta.table, i, readPerColumnSpec) if readPerColumnSpec { tracker.track(i, keyspace, table) } cols = append(cols, col) } } if !globalSpec && meta.colCount > 0 && tracker.allSame { meta.keyspace = tracker.keyspace meta.table = tracker.table } meta.columns = cols return meta } type resultMetadata struct { pagingState []byte // this is a count of the total number of columns which can be scanned, // it is at minimum len(columns) but may be larger, for instance when a column // is a UDT or tuple. columns []ColumnInfo flags int colCount int actualColCount int } func (r *resultMetadata) morePages() bool { return r.flags&frm.FlagHasMorePages == frm.FlagHasMorePages } func (r resultMetadata) String() string { return fmt.Sprintf("[metadata flags=0x%x paging_state=% X columns=%v]", r.flags, r.pagingState, r.columns) } // keyspaceTableTracker tracks whether all columns share the same keyspace/table. type keyspaceTableTracker struct { keyspace string table string allSame bool } func (t *keyspaceTableTracker) track(colIndex int, keyspace, table string) { if colIndex == 0 { t.keyspace = keyspace t.table = table t.allSame = true } else if t.allSame && (keyspace != t.keyspace || table != t.table) { t.allSame = false } } func (f *framer) readColWithSpec(col *ColumnInfo, meta *resultMetadata, globalSpec bool, keyspace, table string, colIndex int, readPerColumnSpec bool) (string, string) { if readPerColumnSpec { // Per-column table spec encoding: read keyspace/table for this column. col.Keyspace = f.readString() col.Table = f.readString() } else { if !globalSpec && colIndex != 0 { // Skip per-column keyspace/table already read from column 0. f.skipString() f.skipString() } col.Keyspace = keyspace col.Table = table } col.Name = f.readString() col.TypeInfo = f.readTypeInfo() if tuple, ok := col.TypeInfo.(TupleTypeInfo); ok { // -1 because we already included the tuple column meta.actualColCount += len(tuple.Elems) - 1 } return col.Keyspace, col.Table } func (f *framer) parseResultMetadata() resultMetadata { var meta resultMetadata meta.flags = f.readInt() meta.colCount = f.readInt() if meta.colCount < 0 { panic(fmt.Errorf("received negative column count: %d", meta.colCount)) } meta.actualColCount = meta.colCount if meta.flags&frm.FlagHasMorePages == frm.FlagHasMorePages { meta.pagingState = f.readBytesCopy() } if meta.flags&frm.FlagNoMetaData == frm.FlagNoMetaData { return meta } globalSpec := meta.flags&frm.FlagGlobalTableSpec == frm.FlagGlobalTableSpec // Read keyspace/table once and reuse for all columns. ROWS results are // always single-table; when !globalSpec this consumes column 0's wire // values and readColWithSpec skips the rest via skipString(). var keyspace, table string if globalSpec || meta.colCount > 0 { keyspace = f.readString() table = f.readString() } var cols []ColumnInfo if meta.colCount < 1000 { // preallocate columninfo to avoid excess copying cols = make([]ColumnInfo, meta.colCount) for i := 0; i < meta.colCount; i++ { f.readColWithSpec(&cols[i], &meta, globalSpec, keyspace, table, i, false) } } else { // use append, huge number of columns usually indicates a corrupt frame or // just a huge row. for i := 0; i < meta.colCount; i++ { var col ColumnInfo f.readColWithSpec(&col, &meta, globalSpec, keyspace, table, i, false) cols = append(cols, col) } } meta.columns = cols return meta } type resultVoidFrame struct { frm.FrameHeader } func (f *resultVoidFrame) String() string { return "[result_void]" } func (f *framer) parseResultFrame() (frame, error) { kind := f.readInt() switch kind { case frm.ResultKindVoid: return &resultVoidFrame{FrameHeader: *f.header}, nil case frm.ResultKindRows: return f.parseResultRows(), nil case frm.ResultKindKeyspace: return f.parseResultSetKeyspace(), nil case frm.ResultKindPrepared: return f.parseResultPrepared(), nil case frm.ResultKindSchemaChanged: return f.parseResultSchemaChange(), nil } return nil, NewErrProtocol("unknown result kind: %x", kind) } type resultRowsFrame struct { frm.FrameHeader meta resultMetadata // dont parse the rows here as we only need to do it once numRows int } func (f *resultRowsFrame) String() string { return fmt.Sprintf("[result_rows meta=%v]", f.meta) } func (f *framer) parseResultRows() frame { result := &resultRowsFrame{} result.meta = f.parseResultMetadata() result.numRows = f.readInt() if result.numRows < 0 { panic(fmt.Errorf("invalid row_count in result frame: %d", result.numRows)) } return result } type resultKeyspaceFrame struct { keyspace string frm.FrameHeader } func (r *resultKeyspaceFrame) String() string { return fmt.Sprintf("[result_keyspace keyspace=%s]", r.keyspace) } func (f *framer) parseResultSetKeyspace() frame { return &resultKeyspaceFrame{ FrameHeader: *f.header, keyspace: f.readString(), } } type resultPreparedFrame struct { preparedID []byte respMeta resultMetadata frm.FrameHeader reqMeta preparedMetadata } func (f *framer) parseResultPrepared() frame { frame := &resultPreparedFrame{ FrameHeader: *f.header, preparedID: f.readShortBytesCopy(), reqMeta: f.parsePreparedMetadata(), } frame.respMeta = f.parseResultMetadata() return frame } func (f *framer) parseResultSchemaChange() frame { change := f.readString() target := f.readString() // TODO: could just use a separate type for each target switch target { case "KEYSPACE": return &frm.SchemaChangeKeyspace{ FrameHeader: *f.header, Change: change, Keyspace: f.readString(), } case "TABLE": return &frm.SchemaChangeTable{ FrameHeader: *f.header, Change: change, Keyspace: f.readString(), Object: f.readString(), } case "TYPE": return &frm.SchemaChangeType{ FrameHeader: *f.header, Change: change, Keyspace: f.readString(), Object: f.readString(), } case "FUNCTION": return &frm.SchemaChangeFunction{ FrameHeader: *f.header, Change: change, Keyspace: f.readString(), Name: f.readString(), Args: f.readStringList(), } case "AGGREGATE": return &frm.SchemaChangeAggregate{ FrameHeader: *f.header, Change: change, Keyspace: f.readString(), Name: f.readString(), Args: f.readStringList(), } default: panic(fmt.Errorf("gocql: unknown SCHEMA_CHANGE target: %q change: %q", target, change)) } } func (f *framer) parseAuthenticateFrame() frame { return &frm.AuthenticateFrame{ FrameHeader: *f.header, Class: f.readString(), } } func (f *framer) parseAuthSuccessFrame() frame { return &frm.AuthSuccessFrame{ FrameHeader: *f.header, Data: f.readBytesCopy(), } } func (f *framer) parseAuthChallengeFrame() frame { return &frm.AuthChallengeFrame{ FrameHeader: *f.header, Data: f.readBytesCopy(), } } func (f *framer) parseEventFrame() frame { eventType := f.readString() switch eventType { case "TOPOLOGY_CHANGE": frame := &frm.TopologyChangeEventFrame{FrameHeader: *f.header} frame.Change = f.readString() frame.Host, frame.Port = f.readInet() return frame case "STATUS_CHANGE": frame := &frm.StatusChangeEventFrame{FrameHeader: *f.header} frame.Change = f.readString() frame.Host, frame.Port = f.readInet() return frame case "SCHEMA_CHANGE": // this should work for all versions return f.parseResultSchemaChange() case "CLIENT_ROUTES_CHANGE": return &frm.ClientRoutesChanged{ FrameHeader: *f.header, ChangeType: f.readString(), ConnectionIDs: f.readStringList(), HostIDs: f.readStringList(), } default: panic(fmt.Errorf("gocql: unknown event type: %q", eventType)) } } type writeAuthResponseFrame struct { data []byte } func (a *writeAuthResponseFrame) String() string { return fmt.Sprintf("[auth_response data=%q]", a.data) } func (a *writeAuthResponseFrame) buildFrame(framer *framer, streamID int) error { return framer.writeAuthResponseFrame(streamID, a.data) } func (f *framer) writeAuthResponseFrame(streamID int, data []byte) error { f.writeHeader(f.flags, frm.OpAuthResponse, streamID) f.writeBytes(data) return f.finish() } type queryValues struct { name string value []byte isUnset bool } type queryParams struct { keyspace string values []queryValues pagingState []byte pageSize int defaultTimestampValue int64 consistency Consistency serialConsistency Consistency skipMeta bool defaultTimestamp bool } func (q queryParams) String() string { return fmt.Sprintf("[query_params consistency=%v skip_meta=%v page_size=%d paging_state=%q serial_consistency=%v default_timestamp=%v values=%v keyspace=%s]", q.consistency, q.skipMeta, q.pageSize, q.pagingState, q.serialConsistency, q.defaultTimestamp, q.values, q.keyspace) } func (f *framer) writeQueryParams(opts *queryParams) { f.writeConsistency(opts.consistency) var flags byte if len(opts.values) > 0 { flags |= frm.FlagValues } if opts.skipMeta { flags |= frm.FlagSkipMetaData } if opts.pageSize > 0 { flags |= frm.FlagPageSize } if len(opts.pagingState) > 0 { flags |= frm.FlagWithPagingState } if opts.serialConsistency > 0 { flags |= frm.FlagWithSerialConsistency } names := false // protoV3 specific things if opts.defaultTimestamp { flags |= frm.FlagDefaultTimestamp } if len(opts.values) > 0 && opts.values[0].name != "" { flags |= frm.FlagWithNameValues names = true } if opts.keyspace != "" { if f.proto > protoVersion4 { flags |= frm.FlagWithKeyspace } else { panic(fmt.Errorf("the keyspace can only be set with protocol 5 or higher")) } } if f.proto > protoVersion4 { f.writeUint(uint32(flags)) } else { f.writeByte(flags) } if n := len(opts.values); n > 0 { f.writeShort(uint16(n)) for i := 0; i < n; i++ { if names { f.writeString(opts.values[i].name) } if opts.values[i].isUnset { f.writeUnset() } else { f.writeBytes(opts.values[i].value) } } } if opts.pageSize > 0 { f.writeInt(int32(opts.pageSize)) } if len(opts.pagingState) > 0 { f.writeBytes(opts.pagingState) } if opts.serialConsistency > 0 { f.writeConsistency(opts.serialConsistency) } if opts.defaultTimestamp { // timestamp in microseconds var ts int64 if opts.defaultTimestampValue != 0 { ts = opts.defaultTimestampValue } else { ts = time.Now().UnixNano() / 1000 } f.writeLong(ts) } if opts.keyspace != "" { f.writeString(opts.keyspace) } } type writeQueryFrame struct { customPayload map[string][]byte statement string params queryParams } func (w *writeQueryFrame) String() string { return fmt.Sprintf("[query statement=%q params=%v]", w.statement, w.params) } func (w *writeQueryFrame) buildFrame(framer *framer, streamID int) error { return framer.writeQueryFrame(streamID, w.statement, &w.params, w.customPayload) } func (f *framer) writeQueryFrame(streamID int, statement string, params *queryParams, customPayload map[string][]byte) error { if len(customPayload) > 0 { f.payload() } f.writeHeader(f.flags, frm.OpQuery, streamID) f.writeCustomPayload(&customPayload) f.writeLongString(statement) f.writeQueryParams(params) return f.finish() } type frameBuilder interface { buildFrame(framer *framer, streamID int) error } type frameWriterFunc func(framer *framer, streamID int) error func (f frameWriterFunc) buildFrame(framer *framer, streamID int) error { return f(framer, streamID) } type writeExecuteFrame struct { customPayload map[string][]byte preparedID []byte params queryParams } func (e *writeExecuteFrame) String() string { return fmt.Sprintf("[execute id=% X params=%v]", e.preparedID, &e.params) } func (e *writeExecuteFrame) buildFrame(fr *framer, streamID int) error { return fr.writeExecuteFrame(streamID, e.preparedID, &e.params, &e.customPayload) } func (f *framer) writeExecuteFrame(streamID int, preparedID []byte, params *queryParams, customPayload *map[string][]byte) error { if len(*customPayload) > 0 { f.payload() } f.writeHeader(f.flags, frm.OpExecute, streamID) f.writeCustomPayload(customPayload) f.writeShortBytes(preparedID) f.writeQueryParams(params) return f.finish() } // TODO: can we replace BatchStatemt with batchStatement? As they prety much // duplicate each other type batchStatment struct { preparedID []byte statement string values []queryValues } type writeBatchFrame struct { customPayload map[string][]byte statements []batchStatment defaultTimestampValue int64 consistency Consistency serialConsistency Consistency typ BatchType defaultTimestamp bool } func (w *writeBatchFrame) buildFrame(framer *framer, streamID int) error { return framer.writeBatchFrame(streamID, w, w.customPayload) } func (f *framer) writeBatchFrame(streamID int, w *writeBatchFrame, customPayload map[string][]byte) error { if len(customPayload) > 0 { f.payload() } f.writeHeader(f.flags, frm.OpBatch, streamID) f.writeCustomPayload(&customPayload) f.writeByte(byte(w.typ)) n := len(w.statements) f.writeShort(uint16(n)) var flags byte for i := 0; i < n; i++ { b := &w.statements[i] if len(b.preparedID) == 0 { f.writeByte(0) f.writeLongString(b.statement) } else { f.writeByte(1) f.writeShortBytes(b.preparedID) } f.writeShort(uint16(len(b.values))) for j := range b.values { col := b.values[j] if col.name != "" { // TODO: move this check into the caller and set a flag on writeBatchFrame // to indicate using named values if f.proto <= protoVersion5 { return fmt.Errorf("gocql: named query values are not supported in batches, please see https://issues.apache.org/jira/browse/CASSANDRA-10246") } flags |= frm.FlagWithNameValues f.writeString(col.name) } if col.isUnset { f.writeUnset() } else { f.writeBytes(col.value) } } } f.writeConsistency(w.consistency) if w.serialConsistency > 0 { flags |= frm.FlagWithSerialConsistency } if w.defaultTimestamp { flags |= frm.FlagDefaultTimestamp } if f.proto > protoVersion4 { f.writeUint(uint32(flags)) } else { f.writeByte(flags) } if w.serialConsistency > 0 { f.writeConsistency(w.serialConsistency) } if w.defaultTimestamp { var ts int64 if w.defaultTimestampValue != 0 { ts = w.defaultTimestampValue } else { ts = time.Now().UnixNano() / 1000 } f.writeLong(ts) } return f.finish() } type writeOptionsFrame struct{} func (w *writeOptionsFrame) buildFrame(framer *framer, streamID int) error { return framer.writeOptionsFrame(streamID, w) } func (f *framer) writeOptionsFrame(stream int, _ *writeOptionsFrame) error { f.writeHeader(f.flags&^frm.FlagCompress, frm.OpOptions, stream) return f.finish() } type writeRegisterFrame struct { events []string } func (w *writeRegisterFrame) buildFrame(framer *framer, streamID int) error { return framer.writeRegisterFrame(streamID, w) } func (f *framer) writeRegisterFrame(streamID int, w *writeRegisterFrame) error { f.writeHeader(f.flags, frm.OpRegister, streamID) f.writeStringList(w.events) return f.finish() } func (f *framer) readByte() byte { if len(f.buf) < 1 { panic(fmt.Errorf("not enough bytes in buffer to read byte require 1 got: %d", len(f.buf))) } b := f.buf[0] f.buf = f.buf[1:] return b } func (f *framer) readInt() (n int) { if len(f.buf) < 4 { panic(fmt.Errorf("not enough bytes in buffer to read int require 4 got: %d", len(f.buf))) } n = int(int32(binary.BigEndian.Uint32(f.buf[:4]))) f.buf = f.buf[4:] return } func (f *framer) readShort() (n uint16) { if len(f.buf) < 2 { panic(fmt.Errorf("not enough bytes in buffer to read short require 2 got: %d", len(f.buf))) } n = binary.BigEndian.Uint16(f.buf[:2]) f.buf = f.buf[2:] return } func (f *framer) readString() (s string) { size := f.readShort() if len(f.buf) < int(size) { panic(fmt.Errorf("not enough bytes in buffer to read string require %d got: %d", size, len(f.buf))) } s = string(f.buf[:size]) f.buf = f.buf[size:] return } // skipString advances past a string without allocating. func (f *framer) skipString() { size := f.readShort() if len(f.buf) < int(size) { panic(fmt.Errorf("not enough bytes in buffer to skip string, requires %d got %d", size, len(f.buf))) } f.buf = f.buf[size:] } func (f *framer) readLongString() (s string) { size := f.readInt() if len(f.buf) < size { panic(fmt.Errorf("not enough bytes in buffer to read long string require %d got: %d", size, len(f.buf))) } s = string(f.buf[:size]) f.buf = f.buf[size:] return } func (f *framer) readStringList() []string { size := f.readShort() l := make([]string, size) for i := 0; i < int(size); i++ { l[i] = f.readString() } return l } func (f *framer) ReadBytesInternal() ([]byte, error) { size := f.readInt() if size < 0 { return nil, nil } if len(f.buf) < size { return nil, fmt.Errorf("not enough bytes in buffer to read bytes require %d got: %d", size, len(f.buf)) } l := f.buf[:size] f.buf = f.buf[size:] return l, nil } func (f *framer) readBytesCopy() []byte { size := f.readInt() if size < 0 { return nil } if len(f.buf) < size { panic(fmt.Errorf("not enough bytes in buffer to read bytes require %d got: %d", size, len(f.buf))) } out := make([]byte, size) copy(out, f.buf[:size]) f.buf = f.buf[size:] return out } func (f *framer) readShortBytesCopy() []byte { size := f.readShort() if len(f.buf) < int(size) { panic(fmt.Errorf("not enough bytes in buffer to read short bytes: require %d got %d", size, len(f.buf))) } out := make([]byte, size) copy(out, f.buf[:size]) f.buf = f.buf[size:] return out } func (f *framer) readInetAdressOnly() net.IP { if len(f.buf) < 1 { panic(fmt.Errorf("not enough bytes in buffer to read inet size require %d got: %d", 1, len(f.buf))) } size := f.buf[0] f.buf = f.buf[1:] if !(size == 4 || size == 16) { panic(fmt.Errorf("invalid IP size: %d", size)) } if len(f.buf) < int(size) { panic(fmt.Errorf("not enough bytes in buffer to read inet require %d got: %d", size, len(f.buf))) } ip := make(net.IP, size) copy(ip, f.buf[:size]) f.buf = f.buf[size:] return ip } func (f *framer) readInet() (net.IP, int) { return f.readInetAdressOnly(), f.readInt() } func (f *framer) readConsistency() Consistency { return Consistency(f.readShort()) } func (f *framer) readBytesMap() map[string][]byte { size := f.readShort() m := make(map[string][]byte, size) for i := 0; i < int(size); i++ { m[f.readString()] = f.readBytesCopy() } return m } func (f *framer) readStringMultiMap() map[string][]string { size := f.readShort() m := make(map[string][]string, size) for i := 0; i < int(size); i++ { k := f.readString() v := f.readStringList() m[k] = v } return m } func (f *framer) writeByte(b byte) { f.buf = append(f.buf, b) } func appendBytes(p []byte, d []byte) []byte { if d == nil { return appendIntNeg1(p) } p = appendInt(p, int32(len(d))) p = append(p, d...) return p } func appendShort(p []byte, n uint16) []byte { return append(p, byte(n>>8), byte(n), ) } func appendInt(p []byte, n int32) []byte { return append(p, byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) } func appendIntNeg1(p []byte) []byte { return append(p, 255, 255, 255, 255) } func appendUint(p []byte, n uint32) []byte { return append(p, byte(n>>24), byte(n>>16), byte(n>>8), byte(n)) } func appendLong(p []byte, n int64) []byte { return append(p, byte(n>>56), byte(n>>48), byte(n>>40), byte(n>>32), byte(n>>24), byte(n>>16), byte(n>>8), byte(n), ) } func (f *framer) writeCustomPayload(customPayload *map[string][]byte) { if len(*customPayload) > 0 { if f.proto < protoVersion4 { panic("Custom payload is not supported with version V3 or less") } f.writeBytesMap(*customPayload) } } func (f *framer) GetCustomPayload() map[string][]byte { return f.customPayload } func (f *framer) GetHeaderWarnings() []string { return f.header.Warnings } // these are protocol level binary types func (f *framer) writeInt(n int32) { f.buf = appendInt(f.buf, n) } func (f *framer) writeIntNeg1() { f.buf = appendIntNeg1(f.buf) } func (f *framer) writeIntNeg2() { f.buf = append(f.buf, 255, 255, 255, 254) } func (f *framer) writeUint(n uint32) { f.buf = appendUint(f.buf, n) } func (f *framer) writeShort(n uint16) { f.buf = appendShort(f.buf, n) } func (f *framer) writeLong(n int64) { f.buf = appendLong(f.buf, n) } func (f *framer) writeString(s string) { f.writeShort(uint16(len(s))) f.buf = append(f.buf, s...) } func (f *framer) writeLongString(s string) { f.writeInt(int32(len(s))) f.buf = append(f.buf, s...) } func (f *framer) writeStringList(l []string) { f.writeShort(uint16(len(l))) for _, s := range l { f.writeString(s) } } func (f *framer) writeUnset() { // Protocol version 4 specifies that bind variables do not require having a // value when executing a statement. Bind variables without a value are // called 'unset'. The 'unset' bind variable is serialized as the int // value '-2' without following bytes. f.writeIntNeg2() } func (f *framer) writeBytes(p []byte) { // TODO: handle null case correctly, // [bytes] A [int] n, followed by n bytes if n >= 0. If n < 0, // no byte should follow and the value represented is `null`. if p == nil { f.writeIntNeg1() } else { f.writeInt(int32(len(p))) f.buf = append(f.buf, p...) } } func (f *framer) writeShortBytes(p []byte) { f.writeShort(uint16(len(p))) f.buf = append(f.buf, p...) } func (f *framer) writeConsistency(cons Consistency) { f.writeShort(uint16(cons)) } func (f *framer) writeStringMap(m map[string]string) { f.writeShort(uint16(len(m))) for k, v := range m { f.writeString(k) f.writeString(v) } } func (f *framer) writeStringMultiMap(m map[string][]string) { f.writeShort(uint16(len(m))) for k, v := range m { f.writeString(k) f.writeStringList(v) } } func (f *framer) writeBytesMap(m map[string][]byte) { f.writeShort(uint16(len(m))) for k, v := range m { f.writeString(k) f.writeBytes(v) } } ================================================ FILE: frame_test.go ================================================ //go:build unit // +build unit /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "bytes" "os" "testing" frm "github.com/gocql/gocql/internal/frame" ) func TestFuzzBugs(t *testing.T) { t.Parallel() // these inputs are found using go-fuzz (https://github.com/dvyukov/go-fuzz) // and should cause a panic unless fixed. tests := [][]byte{ []byte("00000\xa0000"), []byte("\x8000\x0e\x00\x00\x00\x000"), []byte("\x8000\x00\x00\x00\x00\t0000000000"), []byte("\xa0\xff\x01\xae\xefqE\xf2\x1a"), []byte("\x8200\b\x00\x00\x00c\x00\x00\x00\x02000\x01\x00\x00\x00\x03" + "\x00\n0000000000\x00\x14000000" + "00000000000000\x00\x020000" + "\x00\a000000000\x00\x050000000" + "\xff0000000000000000000" + "0000000"), []byte("\x82\xe600\x00\x00\x00\x000"), []byte("\x8200\b\x00\x00\x00\b0\x00\x00\x00\x040000"), []byte("\x83000\b\x00\x00\x00\x14\x00\x00\x00\x020000000" + "000000000"), []byte("\x83000\b\x00\x00\x000\x00\x00\x00\x04\x00\x1000000" + "00000000000000e00000" + "000\x800000000000000000" + "0000000000000"), } for i, test := range tests { t.Logf("test %d input: %q", i, test) r := bytes.NewReader(test) head, err := readHeader(r, make([]byte, 9)) if err != nil { continue } framer := newFramer(nil, byte(head.Version)) err = framer.readFrame(r, &head) if err != nil { continue } frame, err := framer.parseFrame() if err != nil { continue } t.Errorf("(%d) expected to fail for input % X", i, test) t.Errorf("(%d) frame=%+#v", i, frame) } } func TestFrameWriteTooLong(t *testing.T) { t.Parallel() if os.Getenv("TRAVIS") == "true" { t.Skip("skipping test in travis due to memory pressure with the race detecor") } framer := newFramer(nil, 3) framer.writeHeader(0, frm.OpStartup, 1) framer.writeBytes(make([]byte, maxFrameSize+1)) err := framer.finish() if err != ErrFrameTooBig { t.Fatalf("expected to get %v got %v", ErrFrameTooBig, err) } } func TestFrameReadTooLong(t *testing.T) { t.Parallel() if os.Getenv("TRAVIS") == "true" { t.Skip("skipping test in travis due to memory pressure with the race detecor") } r := &bytes.Buffer{} r.Write(make([]byte, maxFrameSize+1)) // write a new header right after this frame to verify that we can read it r.Write([]byte{0x03, 0x00, 0x00, 0x00, byte(frm.OpReady), 0x00, 0x00, 0x00, 0x00}) framer := newFramer(nil, 3) head := frm.FrameHeader{ Version: protoVersion3, Op: frm.OpReady, Length: r.Len() - 9, } err := framer.readFrame(r, &head) if err != ErrFrameTooBig { t.Fatalf("expected to get %v got %v", ErrFrameTooBig, err) } head, err = readHeader(r, make([]byte, 9)) if err != nil { t.Fatal(err) } if head.Op != frm.OpReady { t.Fatalf("expected to get header %v got %v", frm.OpReady, head.Op) } } func TestParseResultMetadata_PerColumnSpec(t *testing.T) { t.Parallel() // Build a synthetic ROWS result metadata frame with FlagGlobalTableSpec unset // (per-column keyspace/table encoding). This tests the !globalSpec optimization // in parseResultMetadata() which reads keyspace/table from the first column // position and reuses them for all columns via skipString(). fr := newFramer(nil, protoVersion4) fr.header = &frm.FrameHeader{Version: protoVersion4} // flags: no FlagGlobalTableSpec — per-column keyspace/table fr.writeInt(0) // colCount fr.writeInt(3) // Column 0: keyspace/table + name + type fr.writeString("test_ks") fr.writeString("test_tbl") fr.writeString("col_a") fr.writeShort(uint16(TypeInt)) // Column 1: same keyspace/table (will be skipped by optimization) fr.writeString("test_ks") fr.writeString("test_tbl") fr.writeString("col_b") fr.writeShort(uint16(TypeVarchar)) // Column 2: same keyspace/table fr.writeString("test_ks") fr.writeString("test_tbl") fr.writeString("col_c") fr.writeShort(uint16(TypeBoolean)) meta := fr.parseResultMetadata() if meta.colCount != 3 { t.Fatalf("colCount = %d, want 3", meta.colCount) } if len(meta.columns) != 3 { t.Fatalf("len(columns) = %d, want 3", len(meta.columns)) } // Verify all columns got the correct keyspace/table from the optimization for i, col := range meta.columns { if col.Keyspace != "test_ks" { t.Errorf("columns[%d].Keyspace = %q, want %q", i, col.Keyspace, "test_ks") } if col.Table != "test_tbl" { t.Errorf("columns[%d].Table = %q, want %q", i, col.Table, "test_tbl") } } // Verify column names expectedNames := []string{"col_a", "col_b", "col_c"} for i, col := range meta.columns { if col.Name != expectedNames[i] { t.Errorf("columns[%d].Name = %q, want %q", i, col.Name, expectedNames[i]) } } // Verify column types expectedTypes := []Type{TypeInt, TypeVarchar, TypeBoolean} for i, col := range meta.columns { nt, ok := col.TypeInfo.(NativeType) if !ok { t.Fatalf("columns[%d].TypeInfo is %T, want NativeType", i, col.TypeInfo) } if nt.typ != expectedTypes[i] { t.Errorf("columns[%d].Type = %v, want %v", i, nt.typ, expectedTypes[i]) } } // Verify the entire buffer was consumed (no misalignment from skipString) if len(fr.buf) != 0 { t.Errorf("buffer has %d unconsumed bytes, want 0 (possible skipString misalignment)", len(fr.buf)) } } func TestParseEventFrame_ClientRoutesChanged(t *testing.T) { t.Parallel() fr := newFramer(nil, protoVersion4) fr.header = &frm.FrameHeader{Version: protoVersion4} fr.writeString("CLIENT_ROUTES_CHANGE") fr.writeString("UPDATED") fr.writeStringList([]string{"c1", ""}) fr.writeStringList([]string{}) frame := fr.parseEventFrame() evt, ok := frame.(*frm.ClientRoutesChanged) if !ok { t.Fatalf("expected ClientRoutesChanged frame, got %T", frame) } if evt.ChangeType != "UPDATED" { t.Fatalf("ChangeType = %v, want UPDATED", evt.ChangeType) } if len(evt.ConnectionIDs) != 2 || evt.ConnectionIDs[1] != "" { t.Fatalf("ConnectionIDs = %v, want [c1 \"\"]", evt.ConnectionIDs) } if len(evt.HostIDs) != 0 { t.Fatalf("HostIDs = %v, want empty", evt.HostIDs) } } ================================================ FILE: framer.go ================================================ /* * Copyright (C) 2026 ScyllaDB * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package gocql import ( "sync" "sync/atomic" frm "github.com/gocql/gocql/internal/frame" ) // framerPool owns one sync.Pool plus the adaptive buffer-sizing state for one // framer usage class. type framerPool struct { pool sync.Pool bufAvgSize atomic.Int64 enabled atomic.Bool } // connFramers owns connection-scoped framer configuration and reader/writer pools. type connFramers struct { readPool framerPool writePool framerPool defaults framerConfig } // framerConfig holds precomputed default framer parameters for the connection. // Populated once during connection setup and used to initialize framers from the pool. type framerConfig struct { compressor Compressor flagLWT int rateLimitingErrorCode int proto byte flags byte tabletsRoutingV1 bool } // framerBufEWMAWeight controls how quickly the exponential weighted moving average // of framer buffer sizes adapts. A value of 8 means each sample contributes ~12.5%, // so it takes roughly 8 samples to converge to a new steady state. // // Lower values (e.g., 4) adapt faster but are more sensitive to outliers. // Higher values (e.g., 16) are more stable but adapt slower to workload changes. // The value 8 was chosen as a reasonable balance for typical CQL query patterns. const framerBufEWMAWeight = 8 // framerBufShrinkThreshold is the multiplier applied to the EWMA to decide when a // framer's read buffer is too large relative to typical usage and should be shrunk. const framerBufShrinkThreshold = 2 // maxReasonableBufferSize is a safety limit to prevent overflow in EWMA calculations // and to catch pathological cases where buffers grow unreasonably large. const maxReasonableBufferSize = 512 * 1024 * 1024 // 512MB // maxCASRetries is the maximum number of CAS retries for updating EWMA. // This prevents infinite loops under extreme contention. const maxCASRetries = 100 // initFramerCache precomputes framer fields from cqlProtoExts so that // per-query framer creation avoids repeated linear scans and allocations. func (c *Conn) initFramerCache() { c.framers.initCache(c) } func (cf *connFramers) initCache(c *Conn) { cfg := framerConfig{ compressor: c.compressor, proto: c.version & protoVersionMask, } if c.compressor != nil { cfg.flags |= frm.FlagCompress } if c.version == protoVersion5 { cfg.flags |= frm.FlagBetaProtocol } if lwtExt := findCQLProtoExtByName(c.cqlProtoExts, lwtAddMetadataMarkKey); lwtExt != nil { if castedExt, ok := lwtExt.(*lwtAddMetadataMarkExt); ok { cfg.flagLWT = castedExt.lwtOptMetaBitMask } else { c.logger.Printf("gocql: failed to cast CQL protocol extension %s to %T", lwtAddMetadataMarkKey, lwtAddMetadataMarkExt{}) } } if rateLimitErrorExt := findCQLProtoExtByName(c.cqlProtoExts, rateLimitError); rateLimitErrorExt != nil { if castedExt, ok := rateLimitErrorExt.(*rateLimitExt); ok { cfg.rateLimitingErrorCode = castedExt.rateLimitErrorCode } else { c.logger.Printf("gocql: failed to cast CQL protocol extension %s to %T", rateLimitError, rateLimitExt{}) } } if tabletsExt := findCQLProtoExtByName(c.cqlProtoExts, tabletsRoutingV1); tabletsExt != nil { if _, ok := tabletsExt.(*tabletsRoutingV1Ext); ok { cfg.tabletsRoutingV1 = true } else { c.logger.Printf("gocql: failed to cast CQL protocol extension %s to %T", tabletsRoutingV1, tabletsRoutingV1Ext{}) } } cf.defaults = cfg c.setTabletSupported(cfg.tabletsRoutingV1) cf.initPool(c) } func (cf *connFramers) initPool(c *Conn) { defaults := cf.defaults cf.readPool.init(defaults, func(f *framer) { c.releaseReadFramer(f) }) cf.writePool.init(defaults, func(f *framer) { c.releaseWriteFramer(f) }) } // getReadFramer returns a pooled framer for reading responses and events. func (c *Conn) getReadFramer() *framer { return c.framers.getRead(c) } func (cf *connFramers) getRead(c *Conn) *framer { f := cf.readPool.get(c) f.released.Store(false) return f } // getWriteFramer returns a pooled framer for building outgoing requests. func (c *Conn) getWriteFramer() *framer { return c.framers.getWrite(c) } func (cf *connFramers) getWrite(c *Conn) *framer { f := cf.writePool.get(c) f.released.Store(false) f.flags = cf.defaults.flags return f } // releaseReadFramer returns a response/event framer to the reader pool. func (c *Conn) releaseReadFramer(f *framer) { c.framers.releaseRead(c, f) } func (cf *connFramers) releaseRead(c *Conn, f *framer) { if f == nil { return } if f.released.Swap(true) { return // already released } f.header = nil f.traceID = nil f.customPayload = nil if !cf.readPool.enabled.Load() { return } bufCap := int64(cap(f.readBuffer)) newAvg, success := cf.readPool.updateAvg(c.logger, bufCap) if !success { cf.readPool.resetAndPut(f, false, 0) return } cf.readPool.resetAndPut(f, true, fpShrinkSize(bufCap, newAvg)) } // releaseWriteFramer returns a request-builder framer to the writer pool. func (c *Conn) releaseWriteFramer(f *framer) { c.framers.releaseWrite(f) } func (cf *connFramers) releaseWrite(f *framer) { if f == nil { return } if f.released.Swap(true) { return } f.header = nil f.traceID = nil f.customPayload = nil f.flags = cf.defaults.flags if !cf.writePool.enabled.Load() { return } bufCap := int64(cap(f.buf)) newAvg, success := cf.writePool.updateAvg(nil, bufCap) if !success { cf.writePool.resetAndPut(f, false, 0) return } cf.writePool.resetAndPut(f, false, fpShrinkSize(bufCap, newAvg)) } func (cf *connFramers) close() { cf.readPool.close() cf.writePool.close() } func (fp *framerPool) init(defaults framerConfig, release func(*framer)) { fp.bufAvgSize.Store(int64(defaultBufSize)) fp.enabled.Store(true) fp.pool = sync.Pool{ New: func() any { buf := make([]byte, defaultBufSize) f := &framer{ buf: buf[:0], readBuffer: buf, compressor: defaults.compressor, proto: defaults.proto, flags: defaults.flags, flagLWT: defaults.flagLWT, rateLimitingErrorCode: defaults.rateLimitingErrorCode, tabletsRoutingV1: defaults.tabletsRoutingV1, } f.release = func() { release(f) } return f }, } } func (fp *framerPool) get(c *Conn) *framer { if !fp.enabled.Load() { return newFramer(c.compressor, c.version) } return fp.pool.Get().(*framer) } func (fp *framerPool) put(f *framer) { if !fp.enabled.Load() { return } fp.pool.Put(f) } func (fp *framerPool) close() { fp.enabled.Store(false) } func (fp *framerPool) updateAvg(logger StdLogger, bufCap int64) (int64, bool) { if bufCap > maxReasonableBufferSize { bufCap = maxReasonableBufferSize } if bufCap < 0 { bufCap = defaultBufSize } for i := 0; i < maxCASRetries; i++ { avg := fp.bufAvgSize.Load() // EWMA update with upward-biased rounding: the +framerBufEWMAWeight/2 term // biases the integer division toward ceiling for all deltas. This means: // - When bufCap > avg (growth): the average increases slightly faster // - When bufCap < avg (shrink): the average decreases slightly slower // Both effects are intentional — favoring larger buffers reduces // reallocation churn at the cost of slightly more memory. // In practice, this means the steady-state EWMA settles ~framerBufEWMAWeight/2 // bytes above the true average when tracking decreasing buffer sizes. newAvg := avg + (bufCap-avg+framerBufEWMAWeight/2)/framerBufEWMAWeight if fp.bufAvgSize.CompareAndSwap(avg, newAvg) { return newAvg, true } } if logger != nil { logger.Printf("gocql: EWMA update failed after %d retries, skipping shrink decision", maxCASRetries) } return fp.bufAvgSize.Load(), false } func fpShrinkSize(bufCap, newAvg int64) int64 { // If this framer's buffer is much larger than the running average, // reallocate it to prevent a single large query from permanently // bloating all pooled framers. if bufCap <= newAvg*framerBufShrinkThreshold { return 0 } if newAvg < defaultBufSize { return defaultBufSize } return newAvg } func (fp *framerPool) resetAndPut(f *framer, alignBufWithReadBuffer bool, shrinkSize int64) { if shrinkSize > 0 { buf := make([]byte, shrinkSize) f.readBuffer = buf f.buf = buf[:0] fp.put(f) return } if alignBufWithReadBuffer { f.buf = f.readBuffer[:0] } else { f.buf = f.buf[:0] } fp.put(f) } ================================================ FILE: framer_bench_test.go ================================================ //go:build bench // +build bench /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "compress/gzip" "io" "os" "testing" ) func readGzipData(path string) ([]byte, error) { f, err := os.Open(path) if err != nil { return nil, err } defer f.Close() r, err := gzip.NewReader(f) if err != nil { return nil, err } defer r.Close() return io.ReadAll(r) } func BenchmarkParseRowsFrame(b *testing.B) { data, err := readGzipData("testdata/frames/bench_parse_result.gz") if err != nil { b.Fatal(err) } b.ResetTimer() for i := 0; i < b.N; i++ { framer := &framer{ header: &frameHeader{ version: protoVersion4 | 0x80, op: frm.OpResult, length: len(data), }, buf: data, } _, err = framer.parseFrame() if err != nil { b.Fatal(err) } } } ================================================ FILE: go.mod ================================================ // // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // module github.com/gocql/gocql require ( github.com/google/go-cmp v0.7.0 github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed github.com/klauspost/compress v1.18.5 golang.org/x/net v0.53.0 golang.org/x/sync v0.20.0 gopkg.in/inf.v0 v0.9.1 sigs.k8s.io/yaml v1.6.0 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) require ( github.com/bitly/go-hostpool v0.1.1 // indirect github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect github.com/google/uuid v1.6.0 github.com/kr/pretty v0.3.1 // indirect github.com/stretchr/testify v1.11.1 ) retract ( v1.10.0 // tag from kiwicom/gocql added by mistake to scylladb/gocql v1.9.0 // tag from kiwicom/gocql added by mistake to scylladb/gocql v1.8.1 // tag from kiwicom/gocql added by mistake to scylladb/gocql v1.8.0 // tag from kiwicom/gocql added by mistake to scylladb/gocql ) go 1.25.0 ================================================ FILE: go.sum ================================================ github.com/bitly/go-hostpool v0.1.1 h1:SsovT4BFqgJQBAESkk2QgeeL7bqKq9oJie8JnD00R+Q= github.com/bitly/go-hostpool v0.1.1/go.mod h1:iwXQOF7+y3cO8vituSqGpBYf02TYTzxK4S2c4rf4cJs= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE= github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE= go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI= golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA= golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= ================================================ FILE: helpers.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2012, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "encoding/hex" "fmt" "math/big" "reflect" "strconv" "strings" "time" "gopkg.in/inf.v0" ) type RowData struct { Columns []string Values []any } // asVectorType attempts to convert a NativeType(custom) which represents a VectorType // into a concrete VectorType. It also works recursively (nested vectors). func asVectorType(t TypeInfo) (VectorType, bool) { if v, ok := t.(VectorType); ok { return v, true } n, ok := t.(NativeType) if !ok || n.Type() != TypeCustom { return VectorType{}, false } const vectorTypePrefix = apacheCassandraTypePrefix + "VectorType" if !strings.HasPrefix(n.Custom(), vectorTypePrefix+"(") { return VectorType{}, false } spec := strings.TrimPrefix(n.Custom(), vectorTypePrefix) spec = strings.Trim(spec, "()") // split last comma -> subtype spec , dimensions idx := strings.LastIndex(spec, ",") if idx <= 0 { return VectorType{}, false } subStr := strings.TrimSpace(spec[:idx]) dimStr := strings.TrimSpace(spec[idx+1:]) dim, err := strconv.Atoi(dimStr) if err != nil { return VectorType{}, false } subType := getCassandraLongType(subStr, n.Version(), nopLogger{}) // recurse if subtype itself is still a custom vector if innerVec, ok := asVectorType(subType); ok { subType = innerVec } return VectorType{ NativeType: NewCustomType(n.Version(), TypeCustom, vectorTypePrefix), SubType: subType, Dimensions: dim, }, true } func goType(t TypeInfo) (reflect.Type, error) { switch t.Type() { case TypeVarchar, TypeAscii, TypeInet, TypeText: return reflect.TypeOf(*new(string)), nil case TypeBigInt, TypeCounter: return reflect.TypeOf(*new(int64)), nil case TypeTime: return reflect.TypeOf(*new(time.Duration)), nil case TypeTimestamp: return reflect.TypeOf(*new(time.Time)), nil case TypeBlob: return reflect.TypeOf(*new([]byte)), nil case TypeBoolean: return reflect.TypeOf(*new(bool)), nil case TypeFloat: return reflect.TypeOf(*new(float32)), nil case TypeDouble: return reflect.TypeOf(*new(float64)), nil case TypeInt: return reflect.TypeOf(*new(int)), nil case TypeSmallInt: return reflect.TypeOf(*new(int16)), nil case TypeTinyInt: return reflect.TypeOf(*new(int8)), nil case TypeDecimal: return reflect.TypeOf(*new(*inf.Dec)), nil case TypeUUID, TypeTimeUUID: return reflect.TypeOf(*new(UUID)), nil case TypeList, TypeSet: elemType, err := goType(t.(CollectionType).Elem) if err != nil { return nil, err } return reflect.SliceOf(elemType), nil case TypeMap: keyType, err := goType(t.(CollectionType).Key) if err != nil { return nil, err } valueType, err := goType(t.(CollectionType).Elem) if err != nil { return nil, err } return reflect.MapOf(keyType, valueType), nil case TypeVarint: return reflect.TypeOf(*new(*big.Int)), nil case TypeTuple: // what can we do here? all there is to do is to make a list of any tuple := t.(TupleTypeInfo) return reflect.TypeOf(make([]any, len(tuple.Elems))), nil case TypeUDT: return reflect.TypeOf(make(map[string]any)), nil case TypeDate: return reflect.TypeOf(*new(time.Time)), nil case TypeDuration: return reflect.TypeOf(*new(Duration)), nil case TypeCustom: // Handle VectorType encoded as custom if vec, ok := asVectorType(t); ok { innerPtr, err := vec.SubType.NewWithError() if err != nil { return nil, err } elemType := reflect.TypeOf(innerPtr) if elemType.Kind() == reflect.Ptr { elemType = elemType.Elem() } return reflect.SliceOf(elemType), nil } return nil, fmt.Errorf("cannot create Go type for unknown CQL type %s", t) default: return nil, fmt.Errorf("cannot create Go type for unknown CQL type %s", t) } } func dereference(i any) any { // Fast path: avoid reflect for the common pointer types returned by // NativeType.NewWithError and used in RowData/MapScan. switch v := i.(type) { case *string: return *v case *int: return *v case *int64: return *v case *int32: return *v case *int16: return *v case *int8: return *v case *float64: return *v case *float32: return *v case *bool: return *v case *[]byte: return *v case *time.Time: return *v case *time.Duration: return *v case *UUID: return *v case *Duration: return *v case *inf.Dec: return *v case *big.Int: return *v case *[]any: return *v case *map[string]any: return *v default: return reflect.Indirect(reflect.ValueOf(i)).Interface() } } // TODO: Cover with unit tests. // Parses long Java-style type definition to internal data structures. func getCassandraLongType(name string, protoVer byte, logger StdLogger) TypeInfo { const prefix = apacheCassandraTypePrefix if strings.HasPrefix(name, prefix+"SetType") { return CollectionType{ NativeType: NewNativeType(protoVer, TypeSet), Elem: getCassandraLongType(unwrapCompositeTypeDefinition(name, prefix+"SetType", '('), protoVer, logger), } } else if strings.HasPrefix(name, prefix+"ListType") { return CollectionType{ NativeType: NewNativeType(protoVer, TypeList), Elem: getCassandraLongType(unwrapCompositeTypeDefinition(name, prefix+"ListType", '('), protoVer, logger), } } else if strings.HasPrefix(name, prefix+"MapType") { names := splitJavaCompositeTypes(name, prefix+"MapType") if len(names) != 2 { logger.Printf("gocql: error parsing map type, it has %d subelements, expecting 2\n", len(names)) return NewNativeType(protoVer, TypeCustom) } return CollectionType{ NativeType: NewNativeType(protoVer, TypeMap), Key: getCassandraLongType(names[0], protoVer, logger), Elem: getCassandraLongType(names[1], protoVer, logger), } } else if strings.HasPrefix(name, prefix+"TupleType") { names := splitJavaCompositeTypes(name, prefix+"TupleType") types := make([]TypeInfo, len(names)) for i, name := range names { types[i] = getCassandraLongType(name, protoVer, logger) } return TupleTypeInfo{ NativeType: NewNativeType(protoVer, TypeTuple), Elems: types, } } else if strings.HasPrefix(name, prefix+"UserType") { names := splitJavaCompositeTypes(name, prefix+"UserType") fields := make([]UDTField, len(names)-2) for i := 2; i < len(names); i++ { spec := strings.Split(names[i], ":") fieldName, _ := hex.DecodeString(spec[0]) fields[i-2] = UDTField{ Name: string(fieldName), Type: getCassandraLongType(spec[1], protoVer, logger), } } udtName, _ := hex.DecodeString(names[1]) return UDTTypeInfo{ NativeType: NewNativeType(protoVer, TypeUDT), KeySpace: names[0], Name: string(udtName), Elements: fields, } } else if strings.HasPrefix(name, prefix+"VectorType") { names := splitJavaCompositeTypes(name, prefix+"VectorType") subType := getCassandraLongType(strings.TrimSpace(names[0]), protoVer, logger) dim, err := strconv.Atoi(strings.TrimSpace(names[1])) if err != nil { logger.Printf("gocql: error parsing vector dimensions: %v\n", err) return NewNativeType(protoVer, TypeCustom) } return VectorType{ NativeType: NewCustomType(protoVer, TypeCustom, prefix+"VectorType"), SubType: subType, Dimensions: dim, } } else if strings.HasPrefix(name, prefix+"FrozenType") { names := splitJavaCompositeTypes(name, prefix+"FrozenType") return getCassandraLongType(strings.TrimSpace(names[0]), protoVer, logger) } else { // basic type return NativeType{ proto: protoVer, typ: getApacheCassandraType(name), } } } func splitJavaCompositeTypes(name string, typeName string) []string { return splitCompositeTypes(name, typeName, '(', ')') } func unwrapCompositeTypeDefinition(name string, typeName string, typeOpen int32) string { return strings.TrimPrefix(name[:len(name)-1], typeName+string(typeOpen)) } func splitCompositeTypes(name string, typeName string, typeOpen int32, typeClose int32) []string { def := unwrapCompositeTypeDefinition(name, typeName, typeOpen) if !strings.Contains(def, string(typeOpen)) { parts := strings.Split(def, ",") for i := range parts { parts[i] = strings.TrimSpace(parts[i]) } return parts } var parts []string lessCount := 0 segment := "" for _, char := range def { if char == ',' && lessCount == 0 { if segment != "" { parts = append(parts, strings.TrimSpace(segment)) } segment = "" continue } segment += string(char) if char == typeOpen { lessCount++ } else if char == typeClose { lessCount-- } } if segment != "" { parts = append(parts, strings.TrimSpace(segment)) } return parts } func getApacheCassandraType(class string) Type { switch strings.TrimPrefix(class, apacheCassandraTypePrefix) { case "AsciiType": return TypeAscii case "LongType": return TypeBigInt case "BytesType": return TypeBlob case "BooleanType": return TypeBoolean case "CounterColumnType": return TypeCounter case "DecimalType": return TypeDecimal case "DoubleType": return TypeDouble case "FloatType": return TypeFloat case "Int32Type": return TypeInt case "ShortType": return TypeSmallInt case "ByteType": return TypeTinyInt case "TimeType": return TypeTime case "DateType", "TimestampType": return TypeTimestamp case "UUIDType", "LexicalUUIDType": return TypeUUID case "UTF8Type": return TypeVarchar case "IntegerType": return TypeVarint case "TimeUUIDType": return TypeTimeUUID case "InetAddressType": return TypeInet case "MapType": return TypeMap case "ListType": return TypeList case "SetType": return TypeSet case "TupleType": return TypeTuple case "DurationType": return TypeDuration case "SimpleDateType": return TypeDate case "UserType": return TypeUDT default: return TypeCustom } } func (r *RowData) rowMap(m map[string]any) { for i, column := range r.Columns { val := dereference(r.Values[i]) if valVal := reflect.ValueOf(val); valVal.Kind() == reflect.Slice && !valVal.IsNil() { valCopy := reflect.MakeSlice(valVal.Type(), valVal.Len(), valVal.Cap()) reflect.Copy(valCopy, valVal) m[column] = valCopy.Interface() } else { m[column] = val } } } // TupeColumnName will return the column name of a tuple value in a column named // c at index n. It should be used if a specific element within a tuple is needed // to be extracted from a map returned from SliceMap or MapScan. func TupleColumnName(c string, n int) string { return fmt.Sprintf("%s[%d]", c, n) } // RowData returns the RowData for the iterator. func (iter *Iter) RowData() (RowData, error) { if iter.err != nil { return RowData{}, iter.err } columns, err := iter.getScanColumns() if err != nil { return RowData{}, err } values, err := iter.newScanValues() if err != nil { return RowData{}, err } return RowData{ Columns: columns, Values: values, }, nil } // getScanColumns returns the cached column names for this iterator, // computing them on the first call. Column names don't change between // rows, so they are computed once and reused. // // The returned slice is shared across all callers and must not be mutated. func (iter *Iter) getScanColumns() ([]string, error) { if iter.scanColumns != nil { return iter.scanColumns, nil } actualSize := iter.meta.actualColCount columns := make([]string, actualSize) idx := 0 for _, column := range iter.Columns() { if c, ok := column.TypeInfo.(TupleTypeInfo); !ok { if idx >= actualSize { err := fmt.Errorf("gocql: column count overflow in RowData: metadata predicted %d columns but encountered more", actualSize) iter.err = err return nil, err } columns[idx] = column.Name idx++ } else { for i := range c.Elems { if idx >= actualSize { err := fmt.Errorf("gocql: column count overflow in RowData: metadata predicted %d columns but encountered more", actualSize) iter.err = err return nil, err } columns[idx] = TupleColumnName(column.Name, i) idx++ } } } if idx != actualSize { err := fmt.Errorf("gocql: column count mismatch in RowData: metadata predicted %d columns but got %d", actualSize, idx) iter.err = err return nil, err } iter.scanColumns = columns return columns, nil } // newScanValues allocates fresh zero-value pointers for each column, // suitable for passing to Scan. Values must be freshly allocated each // call because Scan mutates them. func (iter *Iter) newScanValues() ([]any, error) { actualSize := iter.meta.actualColCount values := make([]any, actualSize) idx := 0 for _, column := range iter.Columns() { if c, ok := column.TypeInfo.(TupleTypeInfo); !ok { if idx >= actualSize { err := fmt.Errorf("gocql: column count overflow in newScanValues: metadata predicted %d columns but encountered more", actualSize) iter.err = err return nil, err } val, err := column.TypeInfo.NewWithError() if err != nil { iter.err = err return nil, err } values[idx] = val idx++ } else { for _, elem := range c.Elems { if idx >= actualSize { err := fmt.Errorf("gocql: column count overflow in newScanValues: metadata predicted %d columns but encountered more", actualSize) iter.err = err return nil, err } val, err := elem.NewWithError() if err != nil { iter.err = err return nil, err } values[idx] = val idx++ } } } if idx != actualSize { err := fmt.Errorf("gocql: column count mismatch in newScanValues: metadata predicted %d columns but got %d", actualSize, idx) iter.err = err return nil, err } return values, nil } // TODO(zariel): is it worth exporting this? func (iter *Iter) rowMap() (map[string]any, error) { if iter.err != nil { return nil, iter.err } rowData, err := iter.RowData() if err != nil { return nil, err } iter.Scan(rowData.Values...) m := make(map[string]any, len(rowData.Columns)) rowData.rowMap(m) return m, nil } // SliceMap is a helper function to make the API easier to use. // It consumes the remaining rows, closes the iterator, and returns the data // in the form of []map[string]any. func (iter *Iter) SliceMap() ([]map[string]any, error) { defer iter.Close() if iter.err != nil { return nil, iter.err } // Not checking for the error because we just did rowData, err := iter.RowData() if err != nil { return nil, err } dataToReturn := make([]map[string]any, 0) for iter.Scan(rowData.Values...) { m := make(map[string]any, len(rowData.Columns)) rowData.rowMap(m) dataToReturn = append(dataToReturn, m) } if iter.err != nil { return nil, iter.err } return dataToReturn, nil } // MapScan takes a map[string]any and populates it with a row // that is returned from cassandra. // // Each call to MapScan() must be called with a new map object. // During the call to MapScan() any pointers in the existing map // are replaced with non pointer types before the call returns // // iter := session.Query(`SELECT * FROM mytable`).Iter() // for { // // New map each iteration // row := make(map[string]any) // if !iter.MapScan(row) { // break // } // // Do things with row // if fullname, ok := row["fullname"]; ok { // fmt.Printf("Full Name: %s\n", fullname) // } // } // if err := iter.Close(); err != nil { // return err // } // // You can also pass pointers in the map before each call // // var fullName FullName // Implements gocql.Unmarshaler and gocql.Marshaler interfaces // var address net.IP // var age int // iter := session.Query(`SELECT * FROM scan_map_table`).Iter() // for { // // New map each iteration // row := map[string]any{ // "fullname": &fullName, // "age": &age, // "address": &address, // } // if !iter.MapScan(row) { // break // } // fmt.Printf("First: %s Age: %d Address: %q\n", fullName.FirstName, age, address) // } // if err := iter.Close(); err != nil { // return err // } func (iter *Iter) MapScan(m map[string]any) bool { if iter.err != nil { return false } rowData, err := iter.RowData() if err != nil { return false } for i, col := range rowData.Columns { if dest, ok := m[col]; ok { rowData.Values[i] = dest } } if iter.Scan(rowData.Values...) { rowData.rowMap(m) return true } return false } func copyBytes(p []byte) []byte { b := make([]byte, len(p)) copy(b, p) return b } ================================================ FILE: helpers_bench_test.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package gocql import ( "fmt" "testing" ) // createMockIter creates a mock iterator with the specified number of simple columns func createMockIter(numColumns int) *Iter { columns := make([]ColumnInfo, numColumns) for i := 0; i < numColumns; i++ { columns[i] = ColumnInfo{ Keyspace: "test_keyspace", Table: "test_table", Name: fmt.Sprintf("column_%d", i), TypeInfo: NativeType{typ: TypeInt, proto: protoVersion4}, } } return &Iter{ meta: resultMetadata{ columns: columns, colCount: numColumns, actualColCount: numColumns, }, numRows: 1, } } // createMockIterWithTypes creates a mock iterator with varied column types func createMockIterWithTypes() *Iter { columns := []ColumnInfo{ {Name: "id", TypeInfo: NativeType{typ: TypeInt, proto: protoVersion4}}, {Name: "name", TypeInfo: NativeType{typ: TypeVarchar, proto: protoVersion4}}, {Name: "created", TypeInfo: NativeType{typ: TypeTimestamp, proto: protoVersion4}}, {Name: "score", TypeInfo: NativeType{typ: TypeBigInt, proto: protoVersion4}}, {Name: "active", TypeInfo: NativeType{typ: TypeBoolean, proto: protoVersion4}}, {Name: "data", TypeInfo: NativeType{typ: TypeBlob, proto: protoVersion4}}, {Name: "uuid", TypeInfo: NativeType{typ: TypeUUID, proto: protoVersion4}}, {Name: "value", TypeInfo: NativeType{typ: TypeDouble, proto: protoVersion4}}, {Name: "count", TypeInfo: NativeType{typ: TypeCounter, proto: protoVersion4}}, {Name: "text", TypeInfo: NativeType{typ: TypeText, proto: protoVersion4}}, } return &Iter{ meta: resultMetadata{ columns: columns, colCount: len(columns), actualColCount: len(columns), }, numRows: 1, } } // createMockIterWithTuples creates a mock iterator with tuple columns func createMockIterWithTuples() *Iter { // Create a tuple with 3 elements tupleElems := []TypeInfo{ NativeType{typ: TypeInt, proto: protoVersion4}, NativeType{typ: TypeVarchar, proto: protoVersion4}, NativeType{typ: TypeTimestamp, proto: protoVersion4}, } columns := []ColumnInfo{ {Name: "id", TypeInfo: NativeType{typ: TypeInt, proto: protoVersion4}}, {Name: "coords", TypeInfo: TupleTypeInfo{ NativeType: NativeType{typ: TypeTuple, proto: protoVersion4}, Elems: tupleElems, }}, {Name: "name", TypeInfo: NativeType{typ: TypeVarchar, proto: protoVersion4}}, } // actualColCount accounts for tuple expansion: 1 (id) + 3 (tuple elements) + 1 (name) = 5 actualColCount := 1 + len(tupleElems) + 1 return &Iter{ meta: resultMetadata{ columns: columns, colCount: len(columns), actualColCount: actualColCount, }, numRows: 1, } } // BenchmarkRowData measures the performance of RowData() with simple columns func BenchmarkRowData(b *testing.B) { iter := createMockIter(10) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { rd, err := iter.RowData() if err != nil { b.Fatal(err) } _ = rd } } // BenchmarkRowDataSmall measures performance with few columns (typical for narrow tables) func BenchmarkRowDataSmall(b *testing.B) { iter := createMockIter(3) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { rd, err := iter.RowData() if err != nil { b.Fatal(err) } _ = rd } } // BenchmarkRowDataLarge measures performance with many columns (wide tables) func BenchmarkRowDataLarge(b *testing.B) { iter := createMockIter(50) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { rd, err := iter.RowData() if err != nil { b.Fatal(err) } _ = rd } } // BenchmarkRowDataWithTypes measures performance with varied column types func BenchmarkRowDataWithTypes(b *testing.B) { iter := createMockIterWithTypes() b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { rd, err := iter.RowData() if err != nil { b.Fatal(err) } _ = rd } } // BenchmarkRowDataWithTuples measures performance with tuple columns func BenchmarkRowDataWithTuples(b *testing.B) { iter := createMockIterWithTuples() b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { rd, err := iter.RowData() if err != nil { b.Fatal(err) } _ = rd } } // BenchmarkRowDataRepeated simulates MapScan calling RowData repeatedly func BenchmarkRowDataRepeated(b *testing.B) { iter := createMockIter(10) b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { // Simulate 100 rows being scanned with MapScan for j := 0; j < 100; j++ { rd, err := iter.RowData() if err != nil { b.Fatal(err) } _ = rd } } } // BenchmarkRowDataAllocation focuses on allocation patterns func BenchmarkRowDataAllocation(b *testing.B) { benchmarks := []struct { iter *Iter name string }{ {name: "10cols", iter: createMockIter(10)}, {name: "100cols", iter: createMockIter(100)}, {name: "1000cols", iter: createMockIter(1000)}, {name: "WithTuples", iter: createMockIterWithTuples()}, } for _, bm := range benchmarks { b.Run(bm.name, func(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { rd, err := bm.iter.RowData() if err != nil { b.Fatal(err) } _ = rd } }) } } // BenchmarkDereference measures the fast-path vs reflect performance of dereference(). func BenchmarkDereference(b *testing.B) { n := 42 s := "hello" u := TimeUUID() b.Run("int_ptr", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { _ = dereference(&n) } }) b.Run("string_ptr", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { _ = dereference(&s) } }) b.Run("uuid_ptr", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { _ = dereference(&u) } }) } // BenchmarkRowDataRepeatedCached measures the improvement from column name caching // when RowData is called repeatedly (as MapScan does per-row). func BenchmarkRowDataRepeatedCached(b *testing.B) { iter := createMockIterWithTypes() b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { // Simulate 100 rows being scanned with MapScan for j := 0; j < 100; j++ { rd, err := iter.RowData() if err != nil { b.Fatal(err) } _ = rd } } } ================================================ FILE: host_source.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "errors" "fmt" "net" "strconv" "strings" "sync" "time" frm "github.com/gocql/gocql/internal/frame" ) var ( ErrCannotFindHost = errors.New("cannot find host") ErrHostAlreadyExists = errors.New("host already exists") ) type nodeState int32 func (n nodeState) String() string { if n == NodeUp { return "UP" } else if n == NodeDown { return "DOWN" } return fmt.Sprintf("UNKNOWN_%d", n) } const ( NodeUp nodeState = iota NodeDown ) type cassVersion struct { Qualifier string Major int Minor int Patch int } func (c *cassVersion) Set(v string) error { if v == "" { return nil } return c.UnmarshalCQL(nil, []byte(v)) } func (c *cassVersion) UnmarshalCQL(info TypeInfo, data []byte) error { return c.unmarshal(data) } func (c *cassVersion) unmarshal(data []byte) error { v := strings.SplitN(strings.TrimPrefix(strings.TrimSuffix(string(data), "-SNAPSHOT"), "v"), ".", 3) if len(v) < 2 { return fmt.Errorf("invalid version string: %s", data) } var err error c.Major, err = strconv.Atoi(v[0]) if err != nil { return fmt.Errorf("invalid major version %v: %v", v[0], err) } if len(v) == 2 { vMinor := strings.SplitN(v[1], "-", 2) c.Minor, err = strconv.Atoi(vMinor[0]) if err != nil { return fmt.Errorf("invalid minor version %v: %v", vMinor[0], err) } if len(vMinor) == 2 { c.Qualifier = vMinor[1] } return nil } c.Minor, err = strconv.Atoi(v[1]) if err != nil { return fmt.Errorf("invalid minor version %v: %v", v[1], err) } vPatch := strings.SplitN(v[2], "-", 2) c.Patch, err = strconv.Atoi(vPatch[0]) if err != nil { return fmt.Errorf("invalid patch version %v: %v", vPatch[0], err) } if len(vPatch) == 2 { c.Qualifier = vPatch[1] } return nil } func (c cassVersion) Before(major, minor, patch int) bool { // We're comparing us (cassVersion) with the provided version (major, minor, patch) // We return true if our version is lower (comes before) than the provided one. if c.Major < major { return true } else if c.Major == major { if c.Minor < minor { return true } else if c.Minor == minor && c.Patch < patch { return true } } return false } func (c cassVersion) AtLeast(major, minor, patch int) bool { return !c.Before(major, minor, patch) } func (c cassVersion) String() string { if c.Qualifier != "" { return fmt.Sprintf("%d.%d.%d-%v", c.Major, c.Minor, c.Patch, c.Qualifier) } return fmt.Sprintf("v%d.%d.%d", c.Major, c.Minor, c.Patch) } func (c cassVersion) nodeUpDelay() time.Duration { if c.Major >= 2 && c.Minor >= 2 { // CASSANDRA-8236 return 0 } return 10 * time.Second } type AddressPort struct { Address net.IP Port uint16 } func (a AddressPort) Equal(o AddressPort) bool { return a.Address.Equal(o.Address) && a.Port == o.Port } func (a AddressPort) IsValid() bool { return len(a.Address) != 0 && !a.Address.IsUnspecified() && a.Port != 0 } func (a AddressPort) String() string { return fmt.Sprintf("%s:%d", a.Address, a.Port) } func (a AddressPort) ToNetAddr() string { return net.JoinHostPort(a.Address.String(), strconv.Itoa(int(a.Port))) } type translatedAddresses struct { CQL AddressPort ShardAware AddressPort ShardAwareTLS AddressPort } func (h translatedAddresses) Equal(o *translatedAddresses) bool { return h.CQL.Equal(o.CQL) && h.ShardAware.Equal(o.ShardAware) && h.ShardAwareTLS.Equal(o.ShardAwareTLS) } type HostInfoBuilder struct { TranslatedAddresses *translatedAddresses Workload string HostId string SchemaVersion string Hostname string ClusterName string Partitioner string Rack string DseVersion string DataCenter string ConnectAddress net.IP BroadcastAddress net.IP PreferredIP net.IP RpcAddress net.IP Peer net.IP ListenAddress net.IP Tokens []string Version cassVersion Port int } func (b HostInfoBuilder) Build() HostInfo { var hostUUID UUID if b.HostId != "" { var err error hostUUID, err = ParseUUID(b.HostId) if err != nil { // Fall back: treat as opaque identifier (for tests with non-UUID strings). copy(hostUUID[:], b.HostId) } } return HostInfo{ dseVersion: b.DseVersion, hostId: hostUUID, dataCenter: b.DataCenter, schemaVersion: b.SchemaVersion, hostname: b.Hostname, clusterName: b.ClusterName, partitioner: b.Partitioner, rack: b.Rack, workload: b.Workload, tokens: b.Tokens, preferredIP: b.PreferredIP, broadcastAddress: b.BroadcastAddress, rpcAddress: b.RpcAddress, connectAddress: b.ConnectAddress, listenAddress: b.ListenAddress, translatedAddresses: b.TranslatedAddresses, version: b.Version, port: b.Port, peer: b.Peer, } } type HostInfo struct { translatedAddresses *translatedAddresses workload string dseVersion string dataCenter string schemaVersion string hostname string clusterName string partitioner string rack string rpcAddress net.IP broadcastAddress net.IP tokens []string preferredIP net.IP peer net.IP listenAddress net.IP connectAddress net.IP version cassVersion scyllaFeatures ScyllaHostFeatures port int // TODO(zariel): reduce locking maybe, not all values will change, but to ensure // that we are thread safe use a mutex to access all fields. mu sync.RWMutex state nodeState hostId UUID graph bool } func (h *HostInfo) Equal(host *HostInfo) bool { if h == host { // prevent rlock reentry return true } return h.HostID() == host.HostID() && h.ConnectAddress().Equal(host.ConnectAddress()) && h.Port() == host.Port() } func (h *HostInfo) Peer() net.IP { h.mu.RLock() defer h.mu.RUnlock() return h.peer } func (h *HostInfo) invalidConnectAddr() bool { h.mu.RLock() defer h.mu.RUnlock() addr, _ := h.connectAddressLocked() return !validIpAddr(addr) } func validIpAddr(addr net.IP) bool { return addr != nil && !addr.IsUnspecified() } func (h *HostInfo) connectAddressLocked() (net.IP, string) { if h.translatedAddresses != nil && h.translatedAddresses.CQL.IsValid() { return h.translatedAddresses.CQL.Address, "connect_address" } else if validIpAddr(h.connectAddress) { return h.connectAddress, "connect_address" } else if validIpAddr(h.rpcAddress) { return h.rpcAddress, "rpc_adress" } else if validIpAddr(h.preferredIP) { // where does perferred_ip get set? return h.preferredIP, "preferred_ip" } else if validIpAddr(h.broadcastAddress) { return h.broadcastAddress, "broadcast_address" } else if validIpAddr(h.peer) { return h.peer, "peer" } return net.IPv4zero, "invalid" } func (h *HostInfo) getDriverFacingIpAddressLocked() net.IP { if validIpAddr(h.rpcAddress) { return h.rpcAddress } else if validIpAddr(h.preferredIP) { return h.preferredIP } else if validIpAddr(h.broadcastAddress) { return h.broadcastAddress } else if validIpAddr(h.peer) { return h.peer } return net.IPv4zero } // nodeToNodeAddress returns address broadcasted between node to nodes. // It's either `broadcast_address` if host info is read from system.local or `peer` if read from system.peers. // This IP address is also part of CQL Event emitted on topology/status changes, // but does not uniquely identify the node in case multiple nodes use the same IP address. func (h *HostInfo) nodeToNodeAddress() net.IP { h.mu.RLock() defer h.mu.RUnlock() if validIpAddr(h.broadcastAddress) { return h.broadcastAddress } else if validIpAddr(h.peer) { return h.peer } return net.IPv4zero } // Returns the address that should be used to connect to the host. // If you wish to override this, use an AddressTranslator func (h *HostInfo) ConnectAddress() net.IP { h.mu.RLock() defer h.mu.RUnlock() if addr, _ := h.connectAddressLocked(); validIpAddr(addr) { return addr } panic(fmt.Sprintf("no valid connect address for host: %v. Is your cluster configured correctly?", h)) } func (h *HostInfo) UntranslatedConnectAddress() net.IP { h.mu.RLock() defer h.mu.RUnlock() return h.connectAddress } func (h *HostInfo) BroadcastAddress() net.IP { h.mu.RLock() defer h.mu.RUnlock() return h.broadcastAddress } func (h *HostInfo) ListenAddress() net.IP { h.mu.RLock() defer h.mu.RUnlock() return h.listenAddress } func (h *HostInfo) RPCAddress() net.IP { h.mu.RLock() defer h.mu.RUnlock() return h.rpcAddress } func (h *HostInfo) PreferredIP() net.IP { h.mu.RLock() defer h.mu.RUnlock() return h.preferredIP } func (h *HostInfo) DataCenter() string { h.mu.RLock() dc := h.dataCenter h.mu.RUnlock() return dc } func (h *HostInfo) Rack() string { h.mu.RLock() rack := h.rack h.mu.RUnlock() return rack } func (h *HostInfo) HostID() string { h.mu.RLock() defer h.mu.RUnlock() if h.hostId.IsEmpty() { return "" } return h.hostId.String() } // hostUUID returns the raw binary host UUID under the read lock. // Use this instead of direct field access on shared HostInfo to avoid data races. func (h *HostInfo) hostUUID() UUID { h.mu.RLock() defer h.mu.RUnlock() return h.hostId } func (h *HostInfo) WorkLoad() string { h.mu.RLock() defer h.mu.RUnlock() return h.workload } func (h *HostInfo) Graph() bool { h.mu.RLock() defer h.mu.RUnlock() return h.graph } func (h *HostInfo) DSEVersion() string { h.mu.RLock() defer h.mu.RUnlock() return h.dseVersion } func (h *HostInfo) Partitioner() string { h.mu.RLock() defer h.mu.RUnlock() if h.partitioner != "" { return h.partitioner } return h.scyllaFeatures.partitioner } func (h *HostInfo) ClusterName() string { h.mu.RLock() defer h.mu.RUnlock() return h.clusterName } func (h *HostInfo) Version() cassVersion { h.mu.RLock() defer h.mu.RUnlock() return h.version } func (h *HostInfo) State() nodeState { h.mu.RLock() defer h.mu.RUnlock() return h.state } func (h *HostInfo) setState(state nodeState) *HostInfo { h.mu.Lock() defer h.mu.Unlock() h.state = state return h } func (h *HostInfo) Tokens() []string { h.mu.RLock() defer h.mu.RUnlock() return h.tokens } func (h *HostInfo) Port() int { h.mu.RLock() defer h.mu.RUnlock() return h.port } func (h *HostInfo) update(from *HostInfo) { if h == from { return } h.mu.Lock() defer h.mu.Unlock() from.mu.RLock() defer from.mu.RUnlock() // autogenerated do not update if h.peer == nil { h.peer = from.peer } if h.broadcastAddress == nil { h.broadcastAddress = from.broadcastAddress } if h.listenAddress == nil { h.listenAddress = from.listenAddress } if h.rpcAddress == nil { h.rpcAddress = from.rpcAddress } if h.preferredIP == nil { h.preferredIP = from.preferredIP } if h.connectAddress == nil { h.connectAddress = from.connectAddress } if h.port == 0 { h.port = from.port } if h.dataCenter == "" { h.dataCenter = from.dataCenter } if h.rack == "" { h.rack = from.rack } if h.hostId.IsEmpty() { h.hostId = from.hostId } if h.workload == "" { h.workload = from.workload } if h.dseVersion == "" { h.dseVersion = from.dseVersion } if h.partitioner == "" { h.partitioner = from.partitioner } if h.clusterName == "" { h.clusterName = from.clusterName } if h.version == (cassVersion{}) { h.version = from.version } if h.tokens == nil { h.tokens = from.tokens } } func (h *HostInfo) IsUp() bool { return h != nil && h.State() == NodeUp } func (h *HostInfo) IsBusy(s *Session) bool { pool, ok := s.pool.getPool(h) return ok && h != nil && pool.InFlight() >= MAX_IN_FLIGHT_THRESHOLD } // ConnectAddressAndPort returns "{ConnectAddress}:{Port}" // Deprecated: Use ConnectAddress and Port separately. func (h *HostInfo) ConnectAddressAndPort() string { h.mu.RLock() defer h.mu.RUnlock() addr, _ := h.connectAddressLocked() return net.JoinHostPort(addr.String(), strconv.Itoa(h.port)) } func (h *HostInfo) String() string { h.mu.RLock() defer h.mu.RUnlock() connectAddr, source := h.connectAddressLocked() return fmt.Sprintf("[HostInfo hostname=%q connectAddress=%q peer=%q rpc_address=%q broadcast_address=%q "+ "preferred_ip=%q connect_addr=%q connect_addr_source=%q "+ "port=%d data_center=%q rack=%q host_id=%q version=%q state=%s num_tokens=%d]", h.hostname, h.connectAddress, h.peer, h.rpcAddress, h.broadcastAddress, h.preferredIP, connectAddr, source, h.port, h.dataCenter, h.rack, h.hostId.String(), h.version, h.state, len(h.tokens)) } func (h *HostInfo) setScyllaFeatures(s ScyllaHostFeatures) { h.mu.Lock() defer h.mu.Unlock() h.scyllaFeatures = s } func (h *HostInfo) ScyllaFeatures() ScyllaHostFeatures { h.mu.Lock() defer h.mu.Unlock() return h.scyllaFeatures } // ScyllaShardAwarePort returns the shard aware port of this host. // Returns zero if the shard aware port is not known. func (h *HostInfo) ScyllaShardAwarePort() uint16 { h.mu.RLock() defer h.mu.RUnlock() return h.scyllaFeatures.ShardAwarePort() } // ScyllaShardAwarePortTLS returns the TLS-enabled shard aware port of this host. // Returns zero if the shard aware port is not known. func (h *HostInfo) ScyllaShardAwarePortTLS() uint16 { h.mu.RLock() defer h.mu.RUnlock() return h.scyllaFeatures.ShardAwarePortTLS() } // ScyllaShardCount returns count of shards on the node. func (h *HostInfo) ScyllaShardCount() int { h.mu.RLock() defer h.mu.RUnlock() return h.scyllaFeatures.ShardsCount() } func (h *HostInfo) setTranslatedConnectionInfo(info translatedAddresses) { h.mu.Lock() defer h.mu.Unlock() h.translatedAddresses = &info } func (h *HostInfo) getTranslatedConnectionInfo() *translatedAddresses { h.mu.Lock() defer h.mu.Unlock() return h.translatedAddresses } // Returns true if we are using system_schema.keyspaces instead of system.schema_keyspaces func checkSystemSchema(control controlConnection) (bool, error) { iter := control.querySystem("SELECT * FROM system_schema.keyspaces") if iter == nil { return false, errNoControl } defer iter.Close() if err := iter.err; err != nil { if errf, ok := err.(*frm.ErrorFrame); ok { if errf.Code == ErrCodeSyntax { return false, nil } } return false, err } return true, nil } // Given a map that represents a row from either system.local or system.peers // return as much information as we can in *HostInfo func hostInfoFromMap(row map[string]any, defaultPort int) (*HostInfo, error) { const assertErrorMsg = "Assertion failed for %s" var ok bool host := HostInfo{} // Default to our connected port if the cluster doesn't have port information for key, value := range row { switch key { case "data_center": host.dataCenter, ok = value.(string) if !ok { return nil, fmt.Errorf(assertErrorMsg, "data_center") } case "rack": host.rack, ok = value.(string) if !ok { return nil, fmt.Errorf(assertErrorMsg, "rack") } case "host_id": hostId, ok := value.(UUID) if !ok { return nil, fmt.Errorf(assertErrorMsg, "host_id") } host.hostId = hostId case "release_version": version, ok := value.(string) if !ok { return nil, fmt.Errorf(assertErrorMsg, "release_version") } host.version.Set(version) case "peer": ip, ok := value.(string) if !ok { return nil, fmt.Errorf(assertErrorMsg, "peer") } host.peer = net.ParseIP(ip) case "cluster_name": host.clusterName, ok = value.(string) if !ok { return nil, fmt.Errorf(assertErrorMsg, "cluster_name") } case "partitioner": host.partitioner, ok = value.(string) if !ok { return nil, fmt.Errorf(assertErrorMsg, "partitioner") } case "broadcast_address": ip, ok := value.(string) if !ok { return nil, fmt.Errorf(assertErrorMsg, "broadcast_address") } host.broadcastAddress = net.ParseIP(ip) case "preferred_ip": ip, ok := value.(string) if !ok { return nil, fmt.Errorf(assertErrorMsg, "preferred_ip") } host.preferredIP = net.ParseIP(ip) case "rpc_address": ip, ok := value.(string) if !ok { return nil, fmt.Errorf(assertErrorMsg, "rpc_address") } host.rpcAddress = net.ParseIP(ip) case "native_address": ip, ok := value.(string) if !ok { return nil, fmt.Errorf(assertErrorMsg, "native_address") } host.rpcAddress = net.ParseIP(ip) case "listen_address": ip, ok := value.(string) if !ok { return nil, fmt.Errorf(assertErrorMsg, "listen_address") } host.listenAddress = net.ParseIP(ip) case "native_port": native_port, ok := value.(int) if !ok { return nil, fmt.Errorf(assertErrorMsg, "native_port") } host.port = native_port case "workload": host.workload, ok = value.(string) if !ok { return nil, fmt.Errorf(assertErrorMsg, "workload") } case "graph": host.graph, ok = value.(bool) if !ok { return nil, fmt.Errorf(assertErrorMsg, "graph") } case "tokens": host.tokens, ok = value.([]string) if !ok { return nil, fmt.Errorf(assertErrorMsg, "tokens") } case "dse_version": host.dseVersion, ok = value.(string) if !ok { return nil, fmt.Errorf(assertErrorMsg, "dse_version") } case "schema_version": schemaVersion, ok := value.(UUID) if !ok { return nil, fmt.Errorf(assertErrorMsg, "schema_version") } host.schemaVersion = schemaVersion.String() } // TODO(thrawn01): Add 'port'? once CASSANDRA-7544 is complete // Not sure what the port field will be called until the JIRA issue is complete } if host.port == 0 { host.port = defaultPort } host.connectAddress = host.getDriverFacingIpAddressLocked() return &host, nil } func hostInfoFromIter(iter *Iter, defaultPort int) (*HostInfo, error) { defer iter.Close() rows, err := iter.SliceMap() if err != nil { // TODO(zariel): make typed error return nil, err } if len(rows) == 0 { return nil, errors.New("query returned 0 rows") } host, err := hostInfoFromMap(rows[0], defaultPort) if err != nil { return nil, err } return host, nil } // debounceRingRefresh submits a ring refresh request to the ring refresh debouncer. func (s *Session) debounceRingRefresh() { s.ringRefresher.Debounce() } // refreshRing executes a ring refresh immediately and cancels pending debounce ring refresh requests. func (s *Session) refreshRingNow() error { err, ok := <-s.ringRefresher.RefreshNow() if !ok { return errors.New("could not refresh ring because stop was requested") } return err } func (s *Session) refreshRing() error { hosts, partitioner, err := s.hostSource.GetHostsFromSystem() if err != nil { return err } prevHosts := s.hostSource.getHostsMap() for _, h := range hosts { if s.cfg.filterHost(h) { continue } if host, ok := s.hostSource.addHostIfMissing(h); !ok { s.startPoolFill(h) } else { // host (by hostID) already exists; determine if IP has changed newHostID := h.HostID() existing, ok := prevHosts[newHostID] if !ok { return fmt.Errorf("get existing host=%s from prevHosts: %w", h, ErrCannotFindHost) } if h.UntranslatedConnectAddress().Equal(existing.UntranslatedConnectAddress()) && h.nodeToNodeAddress().Equal(existing.nodeToNodeAddress()) { // no host IP change host.update(h) } else { // host IP has changed // remove old HostInfo (w/old IP) s.removeHost(existing) if _, alreadyExists := s.hostSource.addHostIfMissing(h); alreadyExists { return fmt.Errorf("add new host=%s after removal: %w", h, ErrHostAlreadyExists) } // add new HostInfo (same hostID, new IP) s.startPoolFill(h) } } delete(prevHosts, h.HostID()) } for _, host := range prevHosts { s.metadataDescriber.RemoveTabletsWithHost(host) s.removeHost(host) } s.policy.SetPartitioner(partitioner) return nil } ================================================ FILE: host_source_scylla.go ================================================ package gocql func (h *HostInfo) SetDatacenter(dc string) { h.mu.Lock() defer h.mu.Unlock() h.dataCenter = dc } ================================================ FILE: host_source_test.go ================================================ //go:build unit // +build unit /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "net" "testing" frm "github.com/gocql/gocql/internal/frame" "github.com/gocql/gocql/internal/tests/mock" ) type trackingMockFramer struct { mock.MockFramer released bool } func (f *trackingMockFramer) Release() { f.released = true } type systemSchemaTestControl struct { iter *Iter } func (*systemSchemaTestControl) getConn() *connHost { return nil } func (*systemSchemaTestControl) awaitSchemaAgreement() error { return nil } func (*systemSchemaTestControl) query(string, ...any) (iter *Iter) { return nil } func (c *systemSchemaTestControl) querySystem(string, ...any) (iter *Iter) { return c.iter } func (*systemSchemaTestControl) discoverProtocol([]*HostInfo) (int, error) { return 0, nil } func (*systemSchemaTestControl) connect([]*HostInfo) error { return nil } func (*systemSchemaTestControl) close() {} func (*systemSchemaTestControl) getSession() *Session { return nil } func (*systemSchemaTestControl) reconnect() error { return nil } func TestUnmarshalCassVersion(t *testing.T) { t.Parallel() tests := [...]struct { data string version cassVersion }{ {"3.2", cassVersion{Major: 3, Minor: 2, Patch: 0, Qualifier: ""}}, {"2.10.1-SNAPSHOT", cassVersion{Major: 2, Minor: 10, Patch: 1, Qualifier: ""}}, {"1.2.3", cassVersion{Major: 1, Minor: 2, Patch: 3, Qualifier: ""}}, {"4.0-rc2", cassVersion{Major: 4, Minor: 0, Patch: 0, Qualifier: "rc2"}}, {"4.3.2-rc1", cassVersion{Major: 4, Minor: 3, Patch: 2, Qualifier: "rc1"}}, {"4.3.2-rc1-qualifier1", cassVersion{Major: 4, Minor: 3, Patch: 2, Qualifier: "rc1-qualifier1"}}, {"4.3-rc1-qualifier1", cassVersion{Major: 4, Minor: 3, Patch: 0, Qualifier: "rc1-qualifier1"}}, } for i, test := range tests { v := &cassVersion{} if err := v.UnmarshalCQL(nil, []byte(test.data)); err != nil { t.Errorf("%d: %v", i, err) } else if *v != test.version { t.Errorf("%d: expected %#+v got %#+v", i, test.version, *v) } } } func TestCassVersionBefore(t *testing.T) { t.Parallel() tests := [...]struct { version cassVersion major, minor, patch int Qualifier string }{ {cassVersion{Major: 1, Minor: 0, Patch: 0, Qualifier: ""}, 0, 0, 0, ""}, {cassVersion{Major: 0, Minor: 1, Patch: 0, Qualifier: ""}, 0, 0, 0, ""}, {cassVersion{Major: 0, Minor: 0, Patch: 1, Qualifier: ""}, 0, 0, 0, ""}, {cassVersion{Major: 1, Minor: 0, Patch: 0, Qualifier: ""}, 0, 1, 0, ""}, {cassVersion{Major: 0, Minor: 1, Patch: 0, Qualifier: ""}, 0, 0, 1, ""}, {cassVersion{Major: 4, Minor: 1, Patch: 0, Qualifier: ""}, 3, 1, 2, ""}, {cassVersion{Major: 4, Minor: 1, Patch: 0, Qualifier: ""}, 3, 1, 2, ""}, } for i, test := range tests { if test.version.Before(test.major, test.minor, test.patch) { t.Errorf("%d: expected v%d.%d.%d to be before %v", i, test.major, test.minor, test.patch, test.version) } } } func TestIsValidPeer(t *testing.T) { t.Parallel() host := &HostInfo{ rpcAddress: net.ParseIP("0.0.0.0"), rack: "myRack", hostId: tUUID(1), dataCenter: "datacenter", tokens: []string{"0", "1"}, } if !isValidPeer(host) { t.Errorf("expected %+v to be a valid peer", host) } host.rack = "" if isValidPeer(host) { t.Errorf("expected %+v to NOT be a valid peer", host) } } func TestIsZeroToken(t *testing.T) { t.Parallel() host := &HostInfo{ rpcAddress: net.ParseIP("0.0.0.0"), rack: "myRack", hostId: tUUID(1), dataCenter: "datacenter", tokens: []string{"0", "1"}, } if isZeroToken(host) { t.Errorf("expected %+v to NOT be a zero-token host", host) } host.tokens = []string{} if !isZeroToken(host) { t.Errorf("expected %+v to be a zero-token host", host) } } func TestCheckSystemSchemaClosesIter(t *testing.T) { t.Parallel() t.Run("NilIterReturnsNoControl", func(t *testing.T) { ok, err := checkSystemSchema(&systemSchemaTestControl{}) if err != errNoControl { t.Fatalf("expected errNoControl, got %v", err) } if ok { t.Fatal("expected system schema v2 detection to fail without a control iterator") } }) t.Run("Success", func(t *testing.T) { framer := &trackingMockFramer{} ok, err := checkSystemSchema(&systemSchemaTestControl{ iter: &Iter{framer: framer}, }) if err != nil { t.Fatalf("unexpected error: %v", err) } if !ok { t.Fatal("expected system schema v2 detection to succeed") } if !framer.released { t.Fatal("expected iterator framer to be released") } }) t.Run("SyntaxError", func(t *testing.T) { framer := &trackingMockFramer{} ok, err := checkSystemSchema(&systemSchemaTestControl{ iter: &Iter{ err: &frm.ErrorFrame{Code: ErrCodeSyntax}, framer: framer, }, }) if err != nil { t.Fatalf("unexpected error: %v", err) } if ok { t.Fatal("expected schema v2 detection to fall back on syntax error") } if !framer.released { t.Fatal("expected iterator framer to be released") } }) } func TestHostInfoFromIterClosesIter(t *testing.T) { t.Parallel() row := []any{ "local", "COMPLETED", net.IPv4(192, 168, 100, 12), "cluster", "3.3.1", "datacenter1", 1733834239, ParseUUIDMust("045859a7-6b9f-4efd-a5e7-acd64a295e13"), net.IPv4(192, 168, 100, 12), "4", "org.apache.cassandra.dht.Murmur3Partitioner", "rack1", "3.0.8", net.IPv4(192, 168, 100, 12), ParseUUIDMust("daf4df2c-b708-11ef-5c25-3004361afd71"), "", []string{"1"}, map[UUID]byte{}, } framer := &trackingMockFramer{ MockFramer: mock.MockFramer{Data: marshalMetadataMust(systemLocalResultMetadata, row)}, } host, err := hostInfoFromIter(&Iter{ meta: systemLocalResultMetadata, framer: framer, numRows: 1, }, 9042) if err != nil { t.Fatalf("unexpected error: %v", err) } if host == nil { t.Fatal("expected host info") } if !framer.released { t.Fatal("expected iterator framer to be released") } } func TestHostInfo_ConnectAddress(t *testing.T) { t.Parallel() var localhost = net.IPv4(127, 0, 0, 1) tests := []struct { name string connectAddr net.IP rpcAddr net.IP broadcastAddr net.IP peer net.IP }{ {name: "rpc_address", rpcAddr: localhost}, {name: "connect_address", connectAddr: localhost}, {name: "broadcast_address", broadcastAddr: localhost}, {name: "peer", peer: localhost}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { host := &HostInfo{ connectAddress: test.connectAddr, rpcAddress: test.rpcAddr, broadcastAddress: test.broadcastAddr, peer: test.peer, } if addr := host.ConnectAddress(); !addr.Equal(localhost) { t.Fatalf("expected ConnectAddress to be %s got %s", localhost, addr) } }) } } func TestAddressPort(t *testing.T) { t.Parallel() t.Run("IsValid", func(t *testing.T) { t.Parallel() tests := []struct { name string addr AddressPort expected bool }{ { name: "valid IPv4 address with port", addr: AddressPort{Address: net.IPv4(127, 0, 0, 1), Port: 9042}, expected: true, }, { name: "valid IPv6 address with port", addr: AddressPort{Address: net.ParseIP("::1"), Port: 9042}, expected: true, }, { name: "nil address", addr: AddressPort{Address: nil, Port: 9042}, expected: false, }, { name: "unspecified IPv4 address", addr: AddressPort{Address: net.IPv4zero, Port: 9042}, expected: false, }, { name: "unspecified IPv6 address", addr: AddressPort{Address: net.IPv6unspecified, Port: 9042}, expected: false, }, { name: "zero port", addr: AddressPort{Address: net.IPv4(127, 0, 0, 1), Port: 0}, expected: false, }, { name: "nil address and zero port", addr: AddressPort{Address: nil, Port: 0}, expected: false, }, { name: "empty AddressPort", addr: AddressPort{}, expected: false, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { result := test.addr.IsValid() if result != test.expected { t.Errorf("IsValid() = %v, expected %v for %+v", result, test.expected, test.addr) } }) } }) t.Run("Equal", func(t *testing.T) { t.Parallel() addr1 := AddressPort{Address: net.IPv4(127, 0, 0, 1), Port: 9042} addr2 := AddressPort{Address: net.IPv4(127, 0, 0, 1), Port: 9042} addr3 := AddressPort{Address: net.IPv4(192, 168, 1, 1), Port: 9042} addr4 := AddressPort{Address: net.IPv4(127, 0, 0, 1), Port: 9043} tests := []struct { name string a AddressPort b AddressPort expected bool }{ { name: "equal addresses and ports", a: addr1, b: addr2, expected: true, }, { name: "different addresses, same port", a: addr1, b: addr3, expected: false, }, { name: "same address, different ports", a: addr1, b: addr4, expected: false, }, { name: "IPv6 addresses equal", a: AddressPort{Address: net.ParseIP("::1"), Port: 9042}, b: AddressPort{Address: net.ParseIP("::1"), Port: 9042}, expected: true, }, { name: "empty", a: AddressPort{}, b: AddressPort{}, expected: true, }, { name: "empty, non-empty", a: AddressPort{}, b: AddressPort{Address: net.ParseIP("::1"), Port: 9042}, expected: false, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { result := test.a.Equal(test.b) if result != test.expected { t.Errorf("Equal() = %v, expected %v for a=%+v, b=%+v", result, test.expected, test.a, test.b) } }) } }) t.Run("String", func(t *testing.T) { t.Parallel() tests := []struct { name string addr AddressPort expected string }{ { name: "IPv4 address", addr: AddressPort{Address: net.IPv4(127, 0, 0, 1), Port: 9042}, expected: "127.0.0.1:9042", }, { name: "IPv6 address", addr: AddressPort{Address: net.ParseIP("::1"), Port: 9042}, expected: "::1:9042", }, { name: "different port", addr: AddressPort{Address: net.IPv4(192, 168, 1, 1), Port: 8080}, expected: "192.168.1.1:8080", }, { name: "nil address", addr: AddressPort{Address: nil, Port: 9042}, expected: ":9042", }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { result := test.addr.String() if result != test.expected { t.Errorf("String() = %q, expected %q", result, test.expected) } }) } }) t.Run("ToNetAddr", func(t *testing.T) { t.Parallel() tests := []struct { name string addr AddressPort expected string }{ { name: "IPv4 address", addr: AddressPort{Address: net.IPv4(127, 0, 0, 1), Port: 9042}, expected: "127.0.0.1:9042", }, { name: "IPv6 address", addr: AddressPort{Address: net.ParseIP("::1"), Port: 9042}, expected: "[::1]:9042", }, { name: "IPv6 address with zone", addr: AddressPort{Address: net.ParseIP("fe80::1"), Port: 9043}, expected: "[fe80::1]:9043", }, { name: "different port", addr: AddressPort{Address: net.IPv4(192, 168, 1, 1), Port: 8080}, expected: "192.168.1.1:8080", }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { result := test.addr.ToNetAddr() if result != test.expected { t.Errorf("ToNetAddr() = %q, expected %q", result, test.expected) } }) } }) } func TestHostInfoBuilder(t *testing.T) { t.Parallel() t.Run("Build", func(t *testing.T) { t.Run("basic fields", func(t *testing.T) { t.Parallel() builder := HostInfoBuilder{ HostId: "a0000000-0000-0000-0000-000000000123", DataCenter: "dc1", Rack: "rack1", Tokens: []string{"token1", "token2"}, Port: 9042, Workload: "Analytics", DseVersion: "6.8.0", ClusterName: "test-cluster", Partitioner: "Murmur3Partitioner", Hostname: "node1.example.com", SchemaVersion: "schema-v1", } host := builder.Build() if host.HostID() != builder.HostId { t.Errorf("HostID() = %q, expected %q", host.HostID(), builder.HostId) } if host.DataCenter() != builder.DataCenter { t.Errorf("DataCenter() = %q, expected %q", host.DataCenter(), builder.DataCenter) } if host.Rack() != builder.Rack { t.Errorf("Rack() = %q, expected %q", host.Rack(), builder.Rack) } if host.Port() != builder.Port { t.Errorf("Port() = %d, expected %d", host.Port(), builder.Port) } if host.WorkLoad() != builder.Workload { t.Errorf("WorkLoad() = %q, expected %q", host.WorkLoad(), builder.Workload) } if host.DSEVersion() != builder.DseVersion { t.Errorf("DSEVersion() = %q, expected %q", host.DSEVersion(), builder.DseVersion) } if host.ClusterName() != builder.ClusterName { t.Errorf("ClusterName() = %q, expected %q", host.ClusterName(), builder.ClusterName) } if host.Partitioner() != builder.Partitioner { t.Errorf("Partitioner() = %q, expected %q", host.Partitioner(), builder.Partitioner) } if len(host.Tokens()) != len(builder.Tokens) { t.Errorf("len(Tokens()) = %d, expected %d", len(host.Tokens()), len(builder.Tokens)) } for i, token := range host.Tokens() { if token != builder.Tokens[i] { t.Errorf("Tokens()[%d] = %q, expected %q", i, token, builder.Tokens[i]) } } }) t.Run("IP addresses", func(t *testing.T) { t.Parallel() connectAddr := net.IPv4(192, 168, 1, 1) broadcastAddr := net.IPv4(192, 168, 1, 2) preferredIP := net.IPv4(192, 168, 1, 3) rpcAddr := net.IPv4(192, 168, 1, 4) peer := net.IPv4(192, 168, 1, 5) listenAddr := net.IPv4(192, 168, 1, 6) builder := HostInfoBuilder{ ConnectAddress: connectAddr, BroadcastAddress: broadcastAddr, PreferredIP: preferredIP, RpcAddress: rpcAddr, Peer: peer, ListenAddress: listenAddr, Port: 9042, } host := builder.Build() if !host.UntranslatedConnectAddress().Equal(connectAddr) { t.Errorf("UntranslatedConnectAddress() = %v, expected %v", host.UntranslatedConnectAddress(), connectAddr) } if !host.BroadcastAddress().Equal(broadcastAddr) { t.Errorf("BroadcastAddress() = %v, expected %v", host.BroadcastAddress(), broadcastAddr) } if !host.PreferredIP().Equal(preferredIP) { t.Errorf("PreferredIP() = %v, expected %v", host.PreferredIP(), preferredIP) } if !host.RPCAddress().Equal(rpcAddr) { t.Errorf("RPCAddress() = %v, expected %v", host.RPCAddress(), rpcAddr) } if !host.Peer().Equal(peer) { t.Errorf("Peer() = %v, expected %v", host.Peer(), peer) } if !host.ListenAddress().Equal(listenAddr) { t.Errorf("ListenAddress() = %v, expected %v", host.ListenAddress(), listenAddr) } }) t.Run("translated addresses", func(t *testing.T) { t.Parallel() translatedAddrs := &translatedAddresses{ CQL: AddressPort{ Address: net.IPv4(10, 0, 0, 1), Port: 9042, }, ShardAware: AddressPort{ Address: net.IPv4(10, 0, 0, 2), Port: 19042, }, ShardAwareTLS: AddressPort{ Address: net.IPv4(10, 0, 0, 3), Port: 19043, }, } builder := HostInfoBuilder{ TranslatedAddresses: translatedAddrs, Port: 9042, } host := builder.Build() // ConnectAddress should use translated CQL address expectedAddr := translatedAddrs.CQL.Address if !host.ConnectAddress().Equal(expectedAddr) { t.Errorf("ConnectAddress() = %v, expected %v", host.ConnectAddress(), expectedAddr) } // Verify translated addresses are set retrievedAddrs := host.getTranslatedConnectionInfo() if retrievedAddrs == nil { t.Fatal("getTranslatedConnectionInfo() returned nil") } if !retrievedAddrs.Equal(translatedAddrs) { t.Errorf("translated addresses not equal: got %+v, expected %+v", retrievedAddrs, translatedAddrs) } }) t.Run("version", func(t *testing.T) { t.Parallel() version := cassVersion{ Major: 4, Minor: 0, Patch: 3, Qualifier: "rc1", } builder := HostInfoBuilder{ Version: version, Port: 9042, } host := builder.Build() if host.Version() != version { t.Errorf("Version() = %+v, expected %+v", host.Version(), version) } }) t.Run("empty builder", func(t *testing.T) { t.Parallel() builder := HostInfoBuilder{} host := builder.Build() if host.HostID() != "" { t.Errorf("HostID() = %q, expected empty string", host.HostID()) } if host.DataCenter() != "" { t.Errorf("DataCenter() = %q, expected empty string", host.DataCenter()) } if host.Port() != 0 { t.Errorf("Port() = %d, expected 0", host.Port()) } if host.Tokens() != nil { t.Errorf("Tokens() = %v, expected nil", host.Tokens()) } }) t.Run("all fields populated", func(t *testing.T) { t.Parallel() translatedAddrs := &translatedAddresses{ CQL: AddressPort{ Address: net.IPv4(10, 0, 0, 1), Port: 9042, }, } version := cassVersion{ Major: 3, Minor: 11, Patch: 4, } builder := HostInfoBuilder{ TranslatedAddresses: translatedAddrs, Workload: "Cassandra", HostId: "b0000000-0000-0000-0000-000000000456", SchemaVersion: "schema-v2", Hostname: "cassandra-node.local", ClusterName: "production-cluster", Partitioner: "Murmur3Partitioner", Rack: "rack2", DseVersion: "6.8.1", DataCenter: "dc2", ConnectAddress: net.IPv4(192, 168, 2, 1), BroadcastAddress: net.IPv4(192, 168, 2, 2), PreferredIP: net.IPv4(192, 168, 2, 3), RpcAddress: net.IPv4(192, 168, 2, 4), Peer: net.IPv4(192, 168, 2, 5), ListenAddress: net.IPv4(192, 168, 2, 6), Tokens: []string{"token-a", "token-b", "token-c"}, Version: version, Port: 9043, } host := builder.Build() // Verify all fields if host.WorkLoad() != builder.Workload { t.Errorf("WorkLoad() = %q, expected %q", host.WorkLoad(), builder.Workload) } if host.HostID() != builder.HostId { t.Errorf("HostID() = %q, expected %q", host.HostID(), builder.HostId) } if host.ClusterName() != builder.ClusterName { t.Errorf("ClusterName() = %q, expected %q", host.ClusterName(), builder.ClusterName) } if host.Partitioner() != builder.Partitioner { t.Errorf("Partitioner() = %q, expected %q", host.Partitioner(), builder.Partitioner) } if host.Rack() != builder.Rack { t.Errorf("Rack() = %q, expected %q", host.Rack(), builder.Rack) } if host.DSEVersion() != builder.DseVersion { t.Errorf("DSEVersion() = %q, expected %q", host.DSEVersion(), builder.DseVersion) } if host.DataCenter() != builder.DataCenter { t.Errorf("DataCenter() = %q, expected %q", host.DataCenter(), builder.DataCenter) } if !host.UntranslatedConnectAddress().Equal(builder.ConnectAddress) { t.Errorf("UntranslatedConnectAddress() = %v, expected %v", host.UntranslatedConnectAddress(), builder.ConnectAddress) } if !host.BroadcastAddress().Equal(builder.BroadcastAddress) { t.Errorf("BroadcastAddress() = %v, expected %v", host.BroadcastAddress(), builder.BroadcastAddress) } if !host.PreferredIP().Equal(builder.PreferredIP) { t.Errorf("PreferredIP() = %v, expected %v", host.PreferredIP(), builder.PreferredIP) } if !host.RPCAddress().Equal(builder.RpcAddress) { t.Errorf("RPCAddress() = %v, expected %v", host.RPCAddress(), builder.RpcAddress) } if !host.Peer().Equal(builder.Peer) { t.Errorf("Peer() = %v, expected %v", host.Peer(), builder.Peer) } if !host.ListenAddress().Equal(builder.ListenAddress) { t.Errorf("ListenAddress() = %v, expected %v", host.ListenAddress(), builder.ListenAddress) } if host.Version() != builder.Version { t.Errorf("Version() = %+v, expected %+v", host.Version(), builder.Version) } if host.Port() != builder.Port { t.Errorf("Port() = %d, expected %d", host.Port(), builder.Port) } if len(host.Tokens()) != len(builder.Tokens) { t.Errorf("len(Tokens()) = %d, expected %d", len(host.Tokens()), len(builder.Tokens)) } // Verify ConnectAddress uses translated address if !host.ConnectAddress().Equal(translatedAddrs.CQL.Address) { t.Errorf("ConnectAddress() = %v, expected %v", host.ConnectAddress(), translatedAddrs.CQL.Address) } }) t.Run("nil IP addresses", func(t *testing.T) { t.Parallel() builder := HostInfoBuilder{ ConnectAddress: nil, BroadcastAddress: nil, PreferredIP: nil, RpcAddress: nil, Peer: nil, ListenAddress: nil, Port: 9042, } host := builder.Build() if host.UntranslatedConnectAddress() != nil { t.Errorf("UntranslatedConnectAddress() = %v, expected nil", host.UntranslatedConnectAddress()) } if host.BroadcastAddress() != nil { t.Errorf("BroadcastAddress() = %v, expected nil", host.BroadcastAddress()) } if host.PreferredIP() != nil { t.Errorf("PreferredIP() = %v, expected nil", host.PreferredIP()) } if host.RPCAddress() != nil { t.Errorf("RPCAddress() = %v, expected nil", host.RPCAddress()) } if host.Peer() != nil { t.Errorf("Peer() = %v, expected nil", host.Peer()) } if host.ListenAddress() != nil { t.Errorf("ListenAddress() = %v, expected nil", host.ListenAddress()) } }) t.Run("connect address priority without translated addresses", func(t *testing.T) { t.Parallel() // Test that ConnectAddress follows the priority order when translated addresses are not set tests := []struct { name string builder HostInfoBuilder expectedAddr net.IP shouldPanic bool }{ { name: "connectAddress takes priority", builder: HostInfoBuilder{ ConnectAddress: net.IPv4(1, 1, 1, 1), RpcAddress: net.IPv4(2, 2, 2, 2), PreferredIP: net.IPv4(3, 3, 3, 3), BroadcastAddress: net.IPv4(4, 4, 4, 4), Peer: net.IPv4(5, 5, 5, 5), Port: 9042, }, expectedAddr: net.IPv4(1, 1, 1, 1), }, { name: "rpcAddress when connectAddress is nil", builder: HostInfoBuilder{ RpcAddress: net.IPv4(2, 2, 2, 2), PreferredIP: net.IPv4(3, 3, 3, 3), BroadcastAddress: net.IPv4(4, 4, 4, 4), Peer: net.IPv4(5, 5, 5, 5), Port: 9042, }, expectedAddr: net.IPv4(2, 2, 2, 2), }, { name: "preferredIP when connectAddress and rpcAddress are nil", builder: HostInfoBuilder{ PreferredIP: net.IPv4(3, 3, 3, 3), BroadcastAddress: net.IPv4(4, 4, 4, 4), Peer: net.IPv4(5, 5, 5, 5), Port: 9042, }, expectedAddr: net.IPv4(3, 3, 3, 3), }, { name: "broadcastAddress when others are nil", builder: HostInfoBuilder{ BroadcastAddress: net.IPv4(4, 4, 4, 4), Peer: net.IPv4(5, 5, 5, 5), Port: 9042, }, expectedAddr: net.IPv4(4, 4, 4, 4), }, { name: "peer when all others are nil", builder: HostInfoBuilder{ Peer: net.IPv4(5, 5, 5, 5), Port: 9042, }, expectedAddr: net.IPv4(5, 5, 5, 5), }, { name: "no valid addresses panics", builder: HostInfoBuilder{ Port: 9042, }, shouldPanic: true, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { host := test.builder.Build() if test.shouldPanic { defer func() { if r := recover(); r == nil { t.Error("ConnectAddress() should have panicked but did not") } }() } addr := host.ConnectAddress() if !test.shouldPanic && !addr.Equal(test.expectedAddr) { t.Errorf("ConnectAddress() = %v, expected %v", addr, test.expectedAddr) } }) } }) }) } ================================================ FILE: hostpolicy/hostpool.go ================================================ package hostpolicy import ( "sync" "github.com/hailocab/go-hostpool" "github.com/gocql/gocql" ) // HostPool is a host policy which uses the bitly/go-hostpool library // to distribute queries between hosts and prevent sending queries to // unresponsive hosts. When creating the host pool that is passed to the policy // use an empty slice of hosts as the hostpool will be populated later by gocql. // See below for examples of usage: // // // Create host selection policy using a simple host pool // cluster.PoolConfig.HostSelectionPolicy = HostPool(hostpool.New(nil)) // // // Create host selection policy using an epsilon greedy pool // cluster.PoolConfig.HostSelectionPolicy = HostPool( // hostpool.NewEpsilonGreedy(nil, 0, &hostpool.LinearEpsilonValueCalculator{}), // ) func HostPool(hp hostpool.HostPool) gocql.HostSelectionPolicy { return &hostPoolHostPolicy{hostMap: map[string]*gocql.HostInfo{}, hp: hp} } type hostPoolHostPolicy struct { hp hostpool.HostPool hostMap map[string]*gocql.HostInfo mu sync.RWMutex } func (r *hostPoolHostPolicy) Init(*gocql.Session) {} func (r *hostPoolHostPolicy) Reset() {} func (r *hostPoolHostPolicy) IsOperational(*gocql.Session) error { return nil } func (r *hostPoolHostPolicy) KeyspaceChanged(gocql.KeyspaceUpdateEvent) {} func (r *hostPoolHostPolicy) SetPartitioner(string) {} func (r *hostPoolHostPolicy) IsLocal(*gocql.HostInfo) bool { return true } func (r *hostPoolHostPolicy) SetHosts(hosts []*gocql.HostInfo) { peers := make([]string, len(hosts)) hostMap := make(map[string]*gocql.HostInfo, len(hosts)) for i, host := range hosts { ip := host.ConnectAddress().String() peers[i] = ip hostMap[ip] = host } r.mu.Lock() r.hp.SetHosts(peers) r.hostMap = hostMap r.mu.Unlock() } func (r *hostPoolHostPolicy) AddHost(host *gocql.HostInfo) { ip := host.ConnectAddress().String() r.mu.Lock() defer r.mu.Unlock() // If the host addr is present and isn't nil return if h, ok := r.hostMap[ip]; ok && h != nil { return } // otherwise, add the host to the map r.hostMap[ip] = host // and construct a new peer list to give to the HostPool hosts := make([]string, 0, len(r.hostMap)) for addr := range r.hostMap { hosts = append(hosts, addr) } r.hp.SetHosts(hosts) } func (r *hostPoolHostPolicy) RemoveHost(host *gocql.HostInfo) { ip := host.ConnectAddress().String() r.mu.Lock() defer r.mu.Unlock() if _, ok := r.hostMap[ip]; !ok { return } delete(r.hostMap, ip) hosts := make([]string, 0, len(r.hostMap)) for _, host := range r.hostMap { hosts = append(hosts, host.ConnectAddress().String()) } r.hp.SetHosts(hosts) } func (r *hostPoolHostPolicy) HostUp(host *gocql.HostInfo) { r.AddHost(host) } func (r *hostPoolHostPolicy) HostDown(host *gocql.HostInfo) { r.RemoveHost(host) } func (r *hostPoolHostPolicy) Pick(qry gocql.ExecutableQuery) gocql.NextHost { return func() gocql.SelectedHost { r.mu.RLock() defer r.mu.RUnlock() if len(r.hostMap) == 0 { return nil } hostR := r.hp.Get() host, ok := r.hostMap[hostR.Host()] if !ok { return nil } return selectedHostPoolHost{ policy: r, info: host, hostR: hostR, } } } // selectedHostPoolHost is a host returned by the hostPoolHostPolicy and // implements the SelectedHost interface type selectedHostPoolHost struct { policy *hostPoolHostPolicy info *gocql.HostInfo hostR hostpool.HostPoolResponse } func (host selectedHostPoolHost) Info() *gocql.HostInfo { return host.info } func (host selectedHostPoolHost) Token() gocql.Token { return nil } func (host selectedHostPoolHost) Mark(err error) { ip := host.info.ConnectAddress().String() host.policy.mu.RLock() defer host.policy.mu.RUnlock() if _, ok := host.policy.hostMap[ip]; !ok { // host was removed between pick and mark return } host.hostR.Mark(err) } ================================================ FILE: hostpolicy/hostpool_test.go ================================================ package hostpolicy import ( "fmt" "net" "testing" "github.com/hailocab/go-hostpool" "github.com/gocql/gocql" ) // Tests of the host pool host selection policy implementation func TestHostPolicy_HostPool(t *testing.T) { policy := HostPool(hostpool.New(nil)) //hosts := []*gocql.HostInfo{ // {hostId: "0", connectAddress: net.IPv4(10, 0, 0, 0)}, // {hostId: "1", connectAddress: net.IPv4(10, 0, 0, 1)}, //} firstHost := gocql.HostInfoBuilder{ HostId: "a0000000-0000-0000-0000-000000000000", ConnectAddress: net.IPv4(10, 0, 0, 0), }.Build() secHost := gocql.HostInfoBuilder{ HostId: "a0000000-0000-0000-0000-000000000001", ConnectAddress: net.IPv4(10, 0, 0, 1), }.Build() hosts := []*gocql.HostInfo{&firstHost, &secHost} // Using set host to control the ordering of the hosts as calling "AddHost" iterates the map // which will result in an unpredictable ordering policy.(*hostPoolHostPolicy).SetHosts(hosts) // the first host selected is actually at [1], but this is ok for RR // interleaved iteration should always increment the host iter := policy.Pick(nil) actualA := iter() if actualA.Info().HostID() != "a0000000-0000-0000-0000-000000000000" { t.Errorf("Expected hosts[0] but was hosts[%s]", actualA.Info().HostID()) } actualA.Mark(nil) actualB := iter() if actualB.Info().HostID() != "a0000000-0000-0000-0000-000000000001" { t.Errorf("Expected hosts[1] but was hosts[%s]", actualB.Info().HostID()) } actualB.Mark(fmt.Errorf("error")) actualC := iter() if actualC.Info().HostID() != "a0000000-0000-0000-0000-000000000000" { t.Errorf("Expected hosts[0] but was hosts[%s]", actualC.Info().HostID()) } actualC.Mark(nil) actualD := iter() if actualD.Info().HostID() != "a0000000-0000-0000-0000-000000000000" { t.Errorf("Expected hosts[0] but was hosts[%s]", actualD.Info().HostID()) } actualD.Mark(nil) } ================================================ FILE: install_test_deps.sh ================================================ ================================================ FILE: integration.sh ================================================ #!/bin/bash # # Copyright (C) 2017 ScyllaDB # readonly SCYLLA_IMAGE=${SCYLLA_IMAGE} set -eu -o pipefail function scylla_up() { local -r exec="docker compose exec -T" echo "==> Running Scylla ${SCYLLA_IMAGE}" docker pull ${SCYLLA_IMAGE} docker compose up -d --wait || ( docker compose ps --format json | jq -M 'select(.Health == "unhealthy") | .Service' | xargs docker compose logs; exit 1 ) } function scylla_down() { echo "==> Stopping Scylla" docker compose down } function scylla_restart() { scylla_down scylla_up } scylla_restart sudo chmod 0777 /tmp/scylla_node_1/cql.m sudo chmod 0777 /tmp/scylla_node_2/cql.m sudo chmod 0777 /tmp/scylla_node_3/cql.m readonly clusterSize=3 readonly scylla_liveset="192.168.100.11,192.168.100.12,192.168.100.13" readonly cversion="3.11.4" readonly proto=4 readonly args="-cluster-socket /tmp/scylla_node_1/cql.m -gocql.timeout=60s -proto=${proto} -rf=1 -clusterSize=${clusterSize} -autowait=2000ms -compressor=snappy -gocql.cversion=${cversion} -cluster=${scylla_liveset}" TAGS=$* if [ ! -z "$TAGS" ]; then echo "==> Running ${TAGS} tests with args: ${args}" go test -v -timeout=5m -race -tags="$TAGS" ${args} ./... fi ================================================ FILE: integration_only.go ================================================ //go:build integration // +build integration package gocql // This file contains code to enable easy access to driver internals // To be used only for integration test import "fmt" func (p *policyConnPool) MissingConnections() (int, error) { p.mu.Lock() defer p.mu.Unlock() total := 0 // close the pools for _, pool := range p.hostConnPools { missing := pool.GetShardCount() - pool.GetConnectionCount() if pool.IsClosed() { return 0, fmt.Errorf("pool for %s is closed", pool.host.HostID()) } total += missing } return total, nil } func (s *Session) MissingConnections() (int, error) { if s.pool == nil { return 0, fmt.Errorf("pool is nil") } return s.pool.MissingConnections() } type ConnPickerIntegration interface { Pick(Token, ExecutableQuery) *Conn Put(*Conn) error Remove(conn *Conn) InFlight() int Size() (int, int) Close() CloseAllConnections() // NextShard returns the shardID to connect to. // nrShard specifies how many shards the host has. // If nrShards is zero, the caller shouldn't use shard-aware port. NextShard() (shardID, nrShards int) } func (p *scyllaConnPicker) CloseAllConnections() { p.nrConns = 0 closeConns(p.conns...) for id := range p.conns { p.conns[id] = nil } } func (p *defaultConnPicker) CloseAllConnections() { closeConns(p.conns...) p.conns = p.conns[:0] } func (p *nopConnPicker) CloseAllConnections() { } func (pool *hostConnPool) CloseAllConnections() { if !pool.closed { return } pool.mu.Lock() println("Closing all connections in a pool") pool.connPicker.(ConnPickerIntegration).CloseAllConnections() println("Filling the pool") pool.mu.Unlock() pool.fill() } func (p *policyConnPool) CloseAllConnections() { p.mu.Lock() defer p.mu.Unlock() // close the pools for _, pool := range p.hostConnPools { pool.CloseAllConnections() } } func (s *Session) CloseAllConnections() { if s.pool != nil { s.pool.CloseAllConnections() } } ================================================ FILE: integration_serialization_scylla_test.go ================================================ //go:build integration // +build integration package gocql import ( "bytes" "fmt" "math/big" "reflect" "strings" "testing" "time" "unsafe" "gopkg.in/inf.v0" "github.com/gocql/gocql/internal/tests/serialization/valcases" ) func TestSerializationSimpleTypesCassandra(t *testing.T) { t.Parallel() const ( pkColumn = "test_id" testColumn = "test_col" ) typeCases := valcases.GetSimple() session := createSession(t) defer session.Close() //Checks data and values conversion t.Run("Marshal", func(t *testing.T) { for _, tc := range typeCases { checkTypeMarshal(t, tc) } }) t.Run("Unmarshal", func(t *testing.T) { for _, tc := range typeCases { checkTypeUnmarshal(t, tc) } }) //Create are tables tables := make([]string, len(typeCases)) for i, tc := range typeCases { table := testTableName(t, tc.CQLName) stmt := fmt.Sprintf(`CREATE TABLE %s (%s text, %s %s, PRIMARY KEY (test_id))`, table, pkColumn, testColumn, tc.CQLName) if err := createTable(session, stmt); err != nil { t.Fatalf("failed to create table for cqltype (%s) with error '%v'", tc.CQLName, err) } tables[i] = table } //Check Insert and Select are values t.Run("InsertSelect", func(t *testing.T) { for i, tc := range typeCases { insertStmt := fmt.Sprintf("INSERT INTO %s (%s, %s) VALUES(?, ?)", tables[i], pkColumn, testColumn) selectStmt := fmt.Sprintf("SELECT %s FROM %s WHERE %s = ?", testColumn, tables[i], pkColumn) checkTypeInsertSelect(t, session, insertStmt, selectStmt, tc) } }) } func checkTypeMarshal(t *testing.T, tc valcases.SimpleTypeCases) { cqlName := tc.CQLName t.Run(cqlName, func(t *testing.T) { tp := Type(tc.CQLType) cqlType := NewNativeType(4, tp) for _, valCase := range tc.Cases { for _, langCase := range valCase.LangCases { receivedData, err := Marshal(cqlType, langCase.Value) if !langCase.ErrInsert && err != nil { t.Errorf("failed to marshal case (%s)(%s) value (%T) with error '%v'", valCase.Name, langCase.LangType, langCase.Value, err) } else if langCase.ErrInsert && err == nil { t.Errorf("expected an error on marshal case (%s)(%s) value (%T)(%[2]v), but have no error", valCase.Name, langCase.LangType, langCase.Value) } else if !bytes.Equal(valCase.Data, receivedData) { t.Errorf("failed to equal case (%s)(%s) data: expected %d, got %d", valCase.Name, langCase.LangType, valCase.Data, receivedData) } } } }) } func checkTypeUnmarshal(t *testing.T, tc valcases.SimpleTypeCases) { cqlName := tc.CQLName t.Run(cqlName, func(t *testing.T) { tp := Type(tc.CQLType) cqlType := NewNativeType(4, tp) for _, valCase := range tc.Cases { for _, langCase := range valCase.LangCases { received := newRef(langCase.Value) err := Unmarshal(cqlType, valCase.Data, received) if !langCase.ErrSelect && err != nil { t.Errorf("failed to unmarshal case (%s)(%s) value (%T) with error '%v'", valCase.Name, langCase.LangType, langCase.Value, err) } if langCase.ErrSelect && err == nil { t.Errorf("expected an error on unmarshal case (%s)(%s) value (%T)(%[2]v), but have no error", valCase.Name, langCase.LangType, langCase.Value) } received = deReference(received) if !equalVals(langCase.Value, received) { t.Errorf("failed to equal case (%s)(%s) value: expected %d, got %d", valCase.Name, langCase.LangType, langCase.Value, received) } } } }) } func checkTypeInsertSelect(t *testing.T, session *Session, insertStmt, selectStmt string, tc valcases.SimpleTypeCases) { cqlName := tc.CQLName t.Run(cqlName, func(t *testing.T) { tp := Type(tc.CQLType) cqlType := NewNativeType(4, tp) for _, valCase := range tc.Cases { valCaseName := valCase.Name for _, langCase := range valCase.LangCases { var insertedValue any //Check Insert value as values insertedValue = langCase.Value err := session.Query(insertStmt, valCaseName, insertedValue).Exec() if !langCase.ErrInsert && err != nil { t.Errorf("failed to insert case (%s) value (%T)(%[2]v) with error '%v'", valCaseName, insertedValue, err) } else if langCase.ErrInsert && err == nil { t.Errorf("expected an error on insert case (%s) value (%T)(%[2]v), but have no error", valCaseName, insertedValue, err) } //Check Select value as value selectedValue := newRef(langCase.Value) err = session.Query(selectStmt, valCase.Name).Scan(selectedValue) if !langCase.ErrSelect && err != nil { t.Errorf("failed to select case (%s) value (%T) with error '%v'", valCaseName, selectedValue, err) } else if langCase.ErrSelect && err == nil { t.Errorf("expected an error on select case (%s) value (%T)(%[2]v), but have no error", valCaseName, selectedValue) } selectedValue = deReference(selectedValue) if !equalVals(langCase.Value, selectedValue) { t.Errorf("failed to equal case (%s) value: expected: %d, got: %d", valCaseName, langCase.Value, selectedValue) } //Check Select value as bytes selectedValue = &DirectUnmarshal{} err = session.Query(selectStmt, valCase.Name).Scan(selectedValue) if err != nil { t.Errorf("failed to select case (%s) value (%T) for cqltype (%s) with error '%v'", valCaseName, selectedValue, cqlType, err) } selectedValue = *(*[]byte)(selectedValue.(*DirectUnmarshal)) if !equalVals(valCase.Data, selectedValue) { t.Errorf("failed to equal case (%s) value for cqltype (%s): expected: %d, got: %d", valCaseName, cqlType, valCase.Data, selectedValue) } } } }) } // newRef returns the nil reference to the input type value (*type)(nil) func newRef(in any) any { out := reflect.New(reflect.TypeOf(in)).Interface() return out } func deReference(in any) any { return reflect.Indirect(reflect.ValueOf(in)).Interface() } func equalVals(in1, in2 any) bool { rin1 := reflect.ValueOf(in1) rin2 := reflect.ValueOf(in2) if rin1.Kind() != rin2.Kind() { return false } if rin1.Kind() == reflect.Ptr && (rin1.IsNil() || rin2.IsNil()) { return rin1.IsNil() && rin2.IsNil() } switch vin1 := in1.(type) { case float32: vin2 := in2.(float32) return *(*[4]byte)(unsafe.Pointer(&vin1)) == *(*[4]byte)(unsafe.Pointer(&vin2)) case *float32: vin2 := in2.(*float32) return *(*[4]byte)(unsafe.Pointer(vin1)) == *(*[4]byte)(unsafe.Pointer(vin2)) case float64: vin2 := in2.(float64) return *(*[8]byte)(unsafe.Pointer(&vin1)) == *(*[8]byte)(unsafe.Pointer(&vin2)) case *float64: vin2 := in2.(*float64) return *(*[8]byte)(unsafe.Pointer(vin1)) == *(*[8]byte)(unsafe.Pointer(vin2)) case big.Int: vin2 := in2.(big.Int) return vin1.Cmp(&vin2) == 0 case *big.Int: vin2 := in2.(*big.Int) return vin1.Cmp(vin2) == 0 case inf.Dec: vin2 := in2.(inf.Dec) if vin1.Scale() != vin2.Scale() { return false } return vin1.UnscaledBig().Cmp(vin2.UnscaledBig()) == 0 case *inf.Dec: vin2 := in2.(*inf.Dec) if vin1.Scale() != vin2.Scale() { return false } return vin1.UnscaledBig().Cmp(vin2.UnscaledBig()) == 0 case fmt.Stringer: vin2 := in2.(fmt.Stringer) return vin1.String() == vin2.String() default: return reflect.DeepEqual(in1, in2) } } // SliceMapTypesTestCase defines a test case for validating SliceMap/MapScan behavior type SliceMapTypesTestCase struct { CQLType string CQLValue string // Non-NULL value to insert ExpectedValue any // Expected value for non-NULL case ExpectedNullValue any // Expected value for NULL } // compareCollectionValues compares collection values (lists, sets, maps) with special handling func compareCollectionValues(t *testing.T, cqlType string, expected, actual any) bool { switch { case strings.HasPrefix(cqlType, "set<"): // Sets are returned as slices, but order is not guaranteed expectedSlice := reflect.ValueOf(expected) actualSlice := reflect.ValueOf(actual) if expectedSlice.Kind() != reflect.Slice || actualSlice.Kind() != reflect.Slice { return false } if expectedSlice.Len() != actualSlice.Len() { return false } // Convert to maps for unordered comparison expectedSet := make(map[any]bool) for i := 0; i < expectedSlice.Len(); i++ { expectedSet[expectedSlice.Index(i).Interface()] = true } actualSet := make(map[any]bool) for i := 0; i < actualSlice.Len(); i++ { actualSet[actualSlice.Index(i).Interface()] = true } return reflect.DeepEqual(expectedSet, actualSet) default: // For lists, maps, and other collections, reflect.DeepEqual works fine return reflect.DeepEqual(expected, actual) } } // compareValues compares expected and actual values with type-specific logic func compareValues(t *testing.T, cqlType string, expected, actual any) bool { switch cqlType { case "varint": // big.Int needs Cmp() for proper comparison, but handle nil pointers safely if expectedBig, ok := expected.(*big.Int); ok { if actualBig, ok := actual.(*big.Int); ok { // Handle nil cases if expectedBig == nil && actualBig == nil { return true } if expectedBig == nil || actualBig == nil { return false } return expectedBig.Cmp(actualBig) == 0 } } return reflect.DeepEqual(expected, actual) case "decimal": // inf.Dec needs Cmp() for proper comparison, but handle nil pointers safely if expectedDec, ok := expected.(*inf.Dec); ok { if actualDec, ok := actual.(*inf.Dec); ok { // Handle nil cases if expectedDec == nil && actualDec == nil { return true } if expectedDec == nil || actualDec == nil { return false } return expectedDec.Cmp(actualDec) == 0 } } return reflect.DeepEqual(expected, actual) default: // reflect.DeepEqual handles nil vs empty slice/map distinction correctly for all types // including inet (net.IP), blob ([]byte), collections ([]T, map[K]V), etc. // This is critical for catching zero value behavior changes in the driver return reflect.DeepEqual(expected, actual) } } // TestSliceMapMapScanTypes tests SliceMap and MapScan with various CQL types func TestSliceMapMapScanTypes(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) tableCQL := fmt.Sprintf(` CREATE TABLE IF NOT EXISTS gocql_test.%s ( id int PRIMARY KEY, tinyint_col tinyint, smallint_col smallint, int_col int, bigint_col bigint, float_col float, double_col double, boolean_col boolean, text_col text, ascii_col ascii, varchar_col varchar, timestamp_col timestamp, uuid_col uuid, timeuuid_col timeuuid, inet_col inet, blob_col blob, varint_col varint, decimal_col decimal, date_col date, time_col time, duration_col duration )`, table) if err := createTable(session, tableCQL); err != nil { t.Fatal("Failed to create test table:", err) } if err := session.Query(fmt.Sprintf("TRUNCATE gocql_test.%s", table)).Exec(); err != nil { t.Fatal("Failed to truncate test table:", err) } testCases := []SliceMapTypesTestCase{ {"tinyint", "42", int8(42), int8(0)}, {"smallint", "1234", int16(1234), int16(0)}, {"int", "123456", int(123456), int(0)}, {"bigint", "1234567890", int64(1234567890), int64(0)}, {"float", "3.14", float32(3.14), float32(0)}, {"double", "2.718281828", float64(2.718281828), float64(0)}, {"boolean", "true", true, false}, {"text", "'hello world'", "hello world", ""}, {"ascii", "'hello ascii'", "hello ascii", ""}, {"varchar", "'hello varchar'", "hello varchar", ""}, {"timestamp", "1388534400000", time.Unix(1388534400, 0).UTC(), time.Time{}}, {"uuid", "550e8400-e29b-41d4-a716-446655440000", mustParseUUID("550e8400-e29b-41d4-a716-446655440000"), UUID{}}, {"timeuuid", "60d79c23-5793-11f0-8afe-bcfce78b517a", mustParseUUID("60d79c23-5793-11f0-8afe-bcfce78b517a"), UUID{}}, {"inet", "'127.0.0.1'", "127.0.0.1", ""}, {"blob", "0x48656c6c6f", []byte("Hello"), []byte(nil)}, {"varint", "123456789012345678901234567890", mustParseBigInt("123456789012345678901234567890"), (*big.Int)(nil)}, {"decimal", "123.45", mustParseDecimal("123.45"), (*inf.Dec)(nil)}, {"date", "'2015-05-03'", time.Date(2015, 5, 3, 0, 0, 0, 0, time.UTC), time.Date(-5877641, 06, 23, 0, 0, 0, 0, time.UTC)}, {"time", "'13:30:54.234'", 13*time.Hour + 30*time.Minute + 54*time.Second + 234*time.Millisecond, time.Duration(0)}, {"duration", "1y2mo3d4h5m6s789ms", mustCreateDuration(14, 3, 4*time.Hour+5*time.Minute+6*time.Second+789*time.Millisecond), Duration{}}, } for i, tc := range testCases { t.Run(tc.CQLType, func(t *testing.T) { testSliceMapMapScanSimple(t, session, tc, i, table) }) } } // Simplified test function that tests both SliceMap and MapScan with both NULL and non-NULL values func testSliceMapMapScanSimple(t *testing.T, session *Session, tc SliceMapTypesTestCase, id int, table string) { colName := tc.CQLType + "_col" t.Run("NonNull", func(t *testing.T) { insertQuery := fmt.Sprintf("INSERT INTO gocql_test.%s (id, %s) VALUES (?, %s)", table, colName, tc.CQLValue) if err := session.Query(insertQuery, id*2).Exec(); err != nil { t.Fatalf("Failed to insert non-NULL value: %v", err) } for _, method := range []string{"SliceMap", "MapScan"} { t.Run(method, func(t *testing.T) { result := queryAndExtractValue(t, session, colName, id*2, method, table) validateResult(t, tc.CQLType, tc.ExpectedValue, result, method, "non-NULL") }) } }) t.Run("Null", func(t *testing.T) { insertQuery := fmt.Sprintf("INSERT INTO gocql_test.%s (id, %s) VALUES (?, NULL)", table, colName) if err := session.Query(insertQuery, id*2+1).Exec(); err != nil { t.Fatalf("Failed to insert NULL value: %v", err) } // Test both SliceMap and MapScan for _, method := range []string{"SliceMap", "MapScan"} { t.Run(method, func(t *testing.T) { result := queryAndExtractValue(t, session, colName, id*2+1, method, table) validateResult(t, tc.CQLType, tc.ExpectedNullValue, result, method, "NULL") }) } }) } func queryAndExtractValue(t *testing.T, session *Session, colName string, id int, method string, table string) any { fmt.Println("queryAndExtractValue") selectQuery := fmt.Sprintf("SELECT %s FROM gocql_test.%s WHERE id = ?", colName, table) switch method { case "SliceMap": iter := session.Query(selectQuery, id).Iter() sliceResults, err := iter.SliceMap() fmt.Println("Slice results: ", sliceResults[0][colName]) iter.Close() if err != nil { t.Fatalf("SliceMap failed: %v", err) } if len(sliceResults) != 1 { t.Fatalf("Expected 1 result, got %d", len(sliceResults)) } return sliceResults[0][colName] case "MapScan": mapResult := make(map[string]any) if err := session.Query(selectQuery, id).MapScan(mapResult); err != nil { t.Fatalf("MapScan failed: %v", err) } return mapResult[colName] default: t.Fatalf("Unknown method: %s", method) return nil } } func validateResult(t *testing.T, cqlType string, expected, actual any, method, valueType string) { if expected != nil && actual != nil { expectedType := reflect.TypeOf(expected) actualType := reflect.TypeOf(actual) if expectedType != actualType { t.Errorf("%s %s %s: expected type %v, got %v", method, valueType, cqlType, expectedType, actualType) } } if !compareValues(t, cqlType, expected, actual) { t.Errorf("%s %s %s: expected value %v (type %T), got %v (type %T)", method, valueType, cqlType, expected, expected, actual, actual) } } func mustParseUUID(s string) UUID { u, err := ParseUUID(s) if err != nil { panic(err) } return u } func mustParseBigInt(s string) *big.Int { i := new(big.Int) if _, ok := i.SetString(s, 10); !ok { panic("failed to parse big.Int: " + s) } return i } func mustParseDecimal(s string) *inf.Dec { dec := new(inf.Dec) if _, ok := dec.SetString(s); !ok { panic("failed to parse inf.Dec: " + s) } return dec } func mustCreateDuration(months int32, days int32, timeDuration time.Duration) Duration { return Duration{ Months: months, Days: days, Nanoseconds: timeDuration.Nanoseconds(), } } // TestSliceMapMapScanCounterTypes tests counter types separately since they have special restrictions // (counter columns can't be mixed with other column types in the same table) func TestSliceMapMapScanCounterTypes(t *testing.T) { t.Parallel() session := createSessionFromClusterTabletsDisabled(createCluster(), t) defer session.Close() // Create separate table for counter types table := testTableName(t) if err := createTable(session, fmt.Sprintf(` CREATE TABLE IF NOT EXISTS gocql_test_tablets_disabled.%s ( id int PRIMARY KEY, counter_col counter ) `, table)); err != nil { t.Fatal("Failed to create counter test table:", err) } // Clear existing data if err := session.Query(fmt.Sprintf("TRUNCATE gocql_test_tablets_disabled.%s", table)).Exec(); err != nil { t.Fatal("Failed to truncate counter test table:", err) } testID := 1 expectedValue := int64(42) // Increment counter (can't INSERT into counter, must UPDATE) err := session.Query(fmt.Sprintf("UPDATE gocql_test_tablets_disabled.%s SET counter_col = counter_col + 42 WHERE id = ?", table), testID).Exec() if err != nil { t.Fatalf("Failed to increment counter: %v", err) } // Test both SliceMap and MapScan for _, method := range []string{"SliceMap", "MapScan"} { t.Run(method, func(t *testing.T) { var result any selectQuery := fmt.Sprintf("SELECT counter_col FROM gocql_test_tablets_disabled.%s WHERE id = ?", table) if method == "SliceMap" { iter := session.Query(selectQuery, testID).Iter() sliceResults, err := iter.SliceMap() iter.Close() if err != nil { t.Fatalf("SliceMap failed: %v", err) } if len(sliceResults) != 1 { t.Fatalf("Expected 1 result, got %d", len(sliceResults)) } result = sliceResults[0]["counter_col"] } else { mapResult := make(map[string]any) if err := session.Query(selectQuery, testID).MapScan(mapResult); err != nil { t.Fatalf("MapScan failed: %v", err) } result = mapResult["counter_col"] } validateResult(t, "counter", expectedValue, result, method, "incremented") }) } } // TestSliceMapMapScanTupleTypes tests tuple types separately since they have special handling // (tuple elements get split into individual columns) func TestSliceMapMapScanTupleTypes(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() // Create test table with tuple column table := testTableName(t) if err := createTable(session, fmt.Sprintf(` CREATE TABLE IF NOT EXISTS gocql_test.%s ( id int PRIMARY KEY, tuple_col tuple ) `, table)); err != nil { t.Fatal("Failed to create tuple test table:", err) } // Clear existing data if err := session.Query(fmt.Sprintf("TRUNCATE gocql_test.%s", table)).Exec(); err != nil { t.Fatal("Failed to truncate tuple test table:", err) } // Test non-NULL tuple t.Run("NonNull", func(t *testing.T) { testID := 1 // Insert tuple value err := session.Query(fmt.Sprintf("INSERT INTO gocql_test.%s (id, tuple_col) VALUES (?, (42, 'hello'))", table), testID).Exec() if err != nil { t.Fatalf("Failed to insert tuple value: %v", err) } // Test both SliceMap and MapScan for _, method := range []string{"SliceMap", "MapScan"} { t.Run(method, func(t *testing.T) { var result map[string]any selectQuery := fmt.Sprintf("SELECT tuple_col FROM gocql_test.%s WHERE id = ?", table) if method == "SliceMap" { iter := session.Query(selectQuery, testID).Iter() sliceResults, err := iter.SliceMap() iter.Close() if err != nil { t.Fatalf("SliceMap failed: %v", err) } if len(sliceResults) != 1 { t.Fatalf("Expected 1 result, got %d", len(sliceResults)) } result = sliceResults[0] } else { result = make(map[string]any) if err := session.Query(selectQuery, testID).MapScan(result); err != nil { t.Fatalf("MapScan failed: %v", err) } } // Check tuple elements (tuples get split into individual columns) elem0Key := TupleColumnName("tuple_col", 0) elem1Key := TupleColumnName("tuple_col", 1) if result[elem0Key] != 42 { t.Errorf("%s tuple[0]: expected 42, got %v", method, result[elem0Key]) } if result[elem1Key] != "hello" { t.Errorf("%s tuple[1]: expected 'hello', got %v", method, result[elem1Key]) } }) } }) // Test NULL tuple t.Run("Null", func(t *testing.T) { testID := 2 // Insert NULL tuple err := session.Query(fmt.Sprintf("INSERT INTO gocql_test.%s (id, tuple_col) VALUES (?, NULL)", table), testID).Exec() if err != nil { t.Fatalf("Failed to insert NULL tuple: %v", err) } // Test both SliceMap and MapScan for _, method := range []string{"SliceMap", "MapScan"} { t.Run(method, func(t *testing.T) { var result map[string]any selectQuery := fmt.Sprintf("SELECT tuple_col FROM gocql_test.%s WHERE id = ?", table) if method == "SliceMap" { iter := session.Query(selectQuery, testID).Iter() sliceResults, err := iter.SliceMap() iter.Close() if err != nil { t.Fatalf("SliceMap failed: %v", err) } if len(sliceResults) != 1 { t.Fatalf("Expected 1 result, got %d", len(sliceResults)) } result = sliceResults[0] } else { result = make(map[string]any) if err := session.Query(selectQuery, testID).MapScan(result); err != nil { t.Fatalf("MapScan failed: %v", err) } } // Check tuple elements (NULL tuple gives zero values) elem0Key := TupleColumnName("tuple_col", 0) elem1Key := TupleColumnName("tuple_col", 1) if result[elem0Key] != 0 { t.Errorf("%s NULL tuple[0]: expected 0, got %v", method, result[elem0Key]) } if result[elem1Key] != "" { t.Errorf("%s NULL tuple[1]: expected '', got %v", method, result[elem1Key]) } }) } }) } // TestSliceMapMapScanVectorTypes tests vector types separately since they need Cassandra 5.0+ and special table setup // (vectors need separate tables and version checks) func TestSliceMapMapScanVectorTypes(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() if *flagDistribution == "cassandra" && flagCassVersion.Before(5, 0, 0) { t.Skip("Vector types have been introduced in Cassandra 5.0") } if *flagDistribution == "scylla" && flagCassVersion.Before(2025, 3, 0) { t.Skip("Vector types have been introduced in ScyllaDB 2025.3") } // Create test table with vector columns table := testTableName(t) if err := createTable(session, fmt.Sprintf(` CREATE TABLE IF NOT EXISTS gocql_test.%s ( id int PRIMARY KEY, vector_float_col vector, vector_text_col vector ) `, table)); err != nil { t.Fatal("Failed to create vector test table:", err) } // Clear existing data if err := session.Query(fmt.Sprintf("TRUNCATE gocql_test.%s", table)).Exec(); err != nil { t.Fatal("Failed to truncate vector test table:", err) } testCases := []struct { colName string cqlValue string expectedValue any expectedNull any }{ {"vector_float_col", "[1.0, 2.5, -3.0]", []float32{1.0, 2.5, -3.0}, []float32(nil)}, {"vector_text_col", "['hello', 'world']", []string{"hello", "world"}, []string(nil)}, } for _, tc := range testCases { t.Run(tc.colName, func(t *testing.T) { // Test non-NULL value t.Run("NonNull", func(t *testing.T) { testID := 1 // Insert non-NULL value insertQuery := fmt.Sprintf("INSERT INTO gocql_test.%s (id, %s) VALUES (?, %s)", table, tc.colName, tc.cqlValue) if err := session.Query(insertQuery, testID).Exec(); err != nil { t.Fatalf("Failed to insert non-NULL value: %v", err) } // Test both SliceMap and MapScan for _, method := range []string{"SliceMap", "MapScan"} { t.Run(method, func(t *testing.T) { var result any selectQuery := fmt.Sprintf("SELECT %s FROM gocql_test.%s WHERE id = ?", tc.colName, table) if method == "SliceMap" { iter := session.Query(selectQuery, testID).Iter() sliceResults, err := iter.SliceMap() iter.Close() if err != nil { t.Fatalf("SliceMap failed: %v", err) } if len(sliceResults) != 1 { t.Fatalf("Expected 1 result, got %d", len(sliceResults)) } result = sliceResults[0][tc.colName] } else { mapResult := make(map[string]any) if err := session.Query(selectQuery, testID).MapScan(mapResult); err != nil { t.Fatalf("MapScan failed: %v", err) } result = mapResult[tc.colName] } validateResult(t, tc.colName, tc.expectedValue, result, method, "non-NULL") }) } }) // Test NULL value t.Run("Null", func(t *testing.T) { testID := 2 // Insert NULL value insertQuery := fmt.Sprintf("INSERT INTO gocql_test.%s (id, %s) VALUES (?, NULL)", table, tc.colName) if err := session.Query(insertQuery, testID).Exec(); err != nil { t.Fatalf("Failed to insert NULL value: %v", err) } // Test both SliceMap and MapScan for _, method := range []string{"SliceMap", "MapScan"} { t.Run(method, func(t *testing.T) { var result any selectQuery := fmt.Sprintf("SELECT %s FROM gocql_test.%s WHERE id = ?", tc.colName, table) if method == "SliceMap" { iter := session.Query(selectQuery, testID).Iter() sliceResults, err := iter.SliceMap() iter.Close() if err != nil { t.Fatalf("SliceMap failed: %v", err) } if len(sliceResults) != 1 { t.Fatalf("Expected 1 result, got %d", len(sliceResults)) } result = sliceResults[0][tc.colName] } else { mapResult := make(map[string]any) if err := session.Query(selectQuery, testID).MapScan(mapResult); err != nil { t.Fatalf("MapScan failed: %v", err) } result = mapResult[tc.colName] } // Vectors should return nil slices for NULL values for consistency validateResult(t, tc.colName, tc.expectedNull, result, method, "NULL") }) } }) }) } } // TestSliceMapMapScanCollectionTypes tests collection types separately since they have special handling // (collections should return nil slices/maps for NULL values for consistency with other slice-based types) func TestSliceMapMapScanCollectionTypes(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() // Create test table with collection columns table := testTableName(t) if err := createTable(session, fmt.Sprintf(` CREATE TABLE IF NOT EXISTS gocql_test.%s ( id int PRIMARY KEY, list_col list, set_col set, map_col map ) `, table)); err != nil { t.Fatal("Failed to create collection test table:", err) } // Clear existing data if err := session.Query(fmt.Sprintf("TRUNCATE gocql_test.%s", table)).Exec(); err != nil { t.Fatal("Failed to truncate collection test table:", err) } testCases := []struct { colName string cqlValue string expectedValue any expectedNull any }{ {"list_col", "['a', 'b', 'c']", []string{"a", "b", "c"}, []string(nil)}, {"set_col", "{1, 2, 3}", []int{1, 2, 3}, []int(nil)}, {"map_col", "{'key1': 1, 'key2': 2}", map[string]int{"key1": 1, "key2": 2}, map[string]int(nil)}, } for _, tc := range testCases { t.Run(tc.colName, func(t *testing.T) { // Test non-NULL value t.Run("NonNull", func(t *testing.T) { testID := 1 // Insert non-NULL value insertQuery := fmt.Sprintf("INSERT INTO gocql_test.%s (id, %s) VALUES (?, %s)", table, tc.colName, tc.cqlValue) if err := session.Query(insertQuery, testID).Exec(); err != nil { t.Fatalf("Failed to insert non-NULL value: %v", err) } // Test both SliceMap and MapScan for _, method := range []string{"SliceMap", "MapScan"} { t.Run(method, func(t *testing.T) { var result any selectQuery := fmt.Sprintf("SELECT %s FROM gocql_test.%s WHERE id = ?", tc.colName, table) if method == "SliceMap" { iter := session.Query(selectQuery, testID).Iter() sliceResults, err := iter.SliceMap() iter.Close() if err != nil { t.Fatalf("SliceMap failed: %v", err) } if len(sliceResults) != 1 { t.Fatalf("Expected 1 result, got %d", len(sliceResults)) } result = sliceResults[0][tc.colName] } else { mapResult := make(map[string]any) if err := session.Query(selectQuery, testID).MapScan(mapResult); err != nil { t.Fatalf("MapScan failed: %v", err) } result = mapResult[tc.colName] } // For sets, we need special comparison since order is not guaranteed if strings.HasPrefix(tc.colName, "set_") { if !compareCollectionValues(t, tc.colName, tc.expectedValue, result) { t.Errorf("%s non-NULL %s: expected %v, got %v", method, tc.colName, tc.expectedValue, result) } } else { validateResult(t, tc.colName, tc.expectedValue, result, method, "non-NULL") } }) } }) // Test NULL value t.Run("Null", func(t *testing.T) { testID := 2 // Insert NULL value insertQuery := fmt.Sprintf("INSERT INTO gocql_test.%s (id, %s) VALUES (?, NULL)", table, tc.colName) if err := session.Query(insertQuery, testID).Exec(); err != nil { t.Fatalf("Failed to insert NULL value: %v", err) } // Test both SliceMap and MapScan for _, method := range []string{"SliceMap", "MapScan"} { t.Run(method, func(t *testing.T) { var result any selectQuery := fmt.Sprintf("SELECT %s FROM gocql_test.%s WHERE id = ?", tc.colName, table) if method == "SliceMap" { iter := session.Query(selectQuery, testID).Iter() sliceResults, err := iter.SliceMap() iter.Close() if err != nil { t.Fatalf("SliceMap failed: %v", err) } if len(sliceResults) != 1 { t.Fatalf("Expected 1 result, got %d", len(sliceResults)) } result = sliceResults[0][tc.colName] } else { mapResult := make(map[string]any) if err := session.Query(selectQuery, testID).MapScan(mapResult); err != nil { t.Fatalf("MapScan failed: %v", err) } result = mapResult[tc.colName] } // Collections should return nil slices/maps for NULL values for consistency validateResult(t, tc.colName, tc.expectedNull, result, method, "NULL") }) } }) }) } } ================================================ FILE: integration_test.go ================================================ //go:build integration // +build integration /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql // This file groups integration tests where Cassandra has to be set up with some special integration variables import ( "context" "errors" "fmt" "reflect" "testing" "time" "github.com/gocql/gocql/internal/tests" ) func init() { // Register integration-only setup that runs before any test (called from TestMain). // This eagerly probes tablet support so parallel tests don't race on lazy init. integrationTestSetup = initTabletProbes } // TestAuthentication verifies that gocql will work with a host configured to only accept authenticated connections func TestAuthentication(t *testing.T) { t.Parallel() if !*flagRunAuthTest { t.Skip("Authentication is not configured in the target cluster") } cluster := createCluster() cluster.Authenticator = PasswordAuthenticator{ Username: "cassandra", Password: "cassandra", } session, err := cluster.CreateSession() if err != nil { t.Fatalf("Authentication error: %s", err) } session.Close() } func TestGetHostsFromSystem(t *testing.T) { t.Parallel() clusterHosts := getClusterHosts() cluster := createCluster() session := createSessionFromCluster(cluster, t) hosts, partitioner, err := session.hostSource.GetHostsFromSystem() tests.AssertTrue(t, "err == nil", err == nil) tests.AssertEqual(t, "len(hosts)", len(clusterHosts), len(hosts)) tests.AssertTrue(t, "len(partitioner) != 0", len(partitioner) != 0) } // TestRingDiscovery makes sure that you can autodiscover other cluster members // when you seed a cluster config with just one node func TestRingDiscovery(t *testing.T) { t.Parallel() clusterHosts := getClusterHosts() cluster := createCluster() cluster.Hosts = clusterHosts[:1] session := createSessionFromCluster(cluster, t) defer session.Close() if *clusterSize > 1 { // wait for autodiscovery to update the pool with the list of known hosts time.Sleep(*flagAutoWait) } session.pool.mu.RLock() defer session.pool.mu.RUnlock() size := len(session.pool.hostConnPools) if *clusterSize != size { for p, pool := range session.pool.hostConnPools { t.Logf("p=%q host=%v ips=%s", p, pool.host, pool.host.ConnectAddress().String()) } t.Errorf("Expected a cluster size of %d, but actual size was %d", *clusterSize, size) } } // TestHostFilterDiscovery ensures that host filtering works even when we discover hosts func TestHostFilterDiscovery(t *testing.T) { t.Parallel() clusterHosts := getClusterHosts() if len(clusterHosts) < 2 { t.Skip("skipping because we don't have 2 or more hosts") } cluster := createCluster() rr := RoundRobinHostPolicy().(*roundRobinHostPolicy) cluster.PoolConfig.HostSelectionPolicy = rr // we'll filter out the second host filtered := clusterHosts[1] cluster.Hosts = clusterHosts[:1] cluster.HostFilter = HostFilterFunc(func(host *HostInfo) bool { if host.ConnectAddress().String() == filtered { return false } return true }) session := createSessionFromCluster(cluster, t) defer session.Close() tests.AssertEqual(t, "len(clusterHosts)-1 != len(rr.hosts.get())", len(clusterHosts)-1, len(rr.hosts.get())) } // TestHostFilterInitial ensures that host filtering works for the initial // connection including the control connection func TestHostFilterInitial(t *testing.T) { t.Parallel() clusterHosts := getClusterHosts() if len(clusterHosts) < 2 { t.Skip("skipping because we don't have 2 or more hosts") } cluster := createCluster() rr := RoundRobinHostPolicy().(*roundRobinHostPolicy) cluster.PoolConfig.HostSelectionPolicy = rr // we'll filter out the second host filtered := clusterHosts[1] cluster.HostFilter = HostFilterFunc(func(host *HostInfo) bool { if host.ConnectAddress().String() == filtered { return false } return true }) session := createSessionFromCluster(cluster, t) defer session.Close() tests.AssertEqual(t, "len(clusterHosts)-1 != len(rr.hosts.get())", len(clusterHosts)-1, len(rr.hosts.get())) } func TestApplicationInformation(t *testing.T) { t.Parallel() cluster := createCluster() s, err := cluster.CreateSession() if err != nil { t.Fatalf("ApplicationInformation error: %s", err) } var clientsTableName string for _, tableName := range []string{"system_views.clients", "system.clients"} { iter := s.Query("select client_options from " + tableName).Iter() _, err = iter.SliceMap() if err == nil { clientsTableName = tableName break } } if clientsTableName == "" { t.Skip("Skipping because server does have `client_options` in clients table") } tcases := []struct { testName string name string version string clientID string }{ { testName: "full", name: "my-application", version: "1.0.0", clientID: "my-client-id", }, { testName: "empty", }, { testName: "name-only", name: "my-application", }, { testName: "version-only", version: "1.0.0", }, { testName: "client-id-only", clientID: "my-client-id", }, } for _, tcase := range tcases { t.Run(tcase.testName, func(t *testing.T) { cluster := createCluster() cluster.ApplicationInfo = NewStaticApplicationInfo(tcase.name, tcase.version, tcase.clientID) s, err := cluster.CreateSession() if err != nil { t.Fatalf("failed to connect to the cluster: %s", err) } defer s.Close() var row map[string]string iter := s.Query("select client_options from " + clientsTableName).Iter() found := false for iter.Scan(&row) { if tcase.name != "" { if row["APPLICATION_NAME"] != tcase.name { continue } } else { if _, ok := row["APPLICATION_NAME"]; ok { continue } } if tcase.version != "" { if row["APPLICATION_VERSION"] != tcase.version { continue } } else { if _, ok := row["APPLICATION_VERSION"]; ok { continue } } if tcase.clientID != "" { if row["CLIENT_ID"] != tcase.clientID { continue } } else { if _, ok := row["CLIENT_ID"]; ok { continue } } found = true break } if iter.Close() != nil { t.Fatalf("failed to execute query: %s", iter.Close().Error()) } if !found { t.Fatalf("failed to find the application info row") } }) } } func TestWriteFailure(t *testing.T) { t.Parallel() t.Skip("skipped due to unknown purpose") cluster := createCluster() createKeyspace(t, cluster, "test", false) cluster.Keyspace = "test" session, err := cluster.CreateSession() if err != nil { t.Fatal("create session:", err) } defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf("CREATE TABLE test.%s (id int,value int,PRIMARY KEY (id))", table)); err != nil { t.Fatalf("failed to create table with error '%v'", err) } if err := session.Query(fmt.Sprintf(`INSERT INTO test.%s (id, value) VALUES (1, 1)`, table)).Exec(); err != nil { errWrite, ok := err.(*RequestErrWriteFailure) if ok { if session.cfg.ProtoVersion >= protoVersion5 { // ErrorMap should be filled with some hosts that should've errored if len(errWrite.ErrorMap) == 0 { t.Fatal("errWrite.ErrorMap should have some failed hosts but it didn't have any") } } else { // Map doesn't get filled for V4 if len(errWrite.ErrorMap) != 0 { t.Fatal("errWrite.ErrorMap should have length 0, it's: ", len(errWrite.ErrorMap)) } } } else { t.Fatalf("error (%s) should be RequestErrWriteFailure, it's: %T", err, err) } } else { t.Fatal("a write fail error should have happened when querying test keyspace") } if err = session.Query("DROP KEYSPACE test").Exec(); err != nil { t.Fatal(err) } } func TestCustomPayloadMessages(t *testing.T) { t.Parallel() t.Skip("SKIPPING") cluster := createCluster() session := createSessionFromCluster(cluster, t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf("CREATE TABLE gocql_test.%s (id int, value int, PRIMARY KEY (id))", table)); err != nil { t.Fatal(err) } // QueryMessage var customPayload = map[string][]byte{"a": []byte{10, 20}, "b": []byte{20, 30}} query := session.Query(fmt.Sprintf("SELECT id FROM %s where id = ?", table), 42).Consistency(One).CustomPayload(customPayload) iter := query.Iter() rCustomPayload := iter.GetCustomPayload() if !reflect.DeepEqual(customPayload, rCustomPayload) { t.Fatal("The received custom payload should match the sent") } iter.Close() // Insert query query = session.Query(fmt.Sprintf("INSERT INTO %s(id,value) VALUES(1, 1)", table)).Consistency(One).CustomPayload(customPayload) iter = query.Iter() rCustomPayload = iter.GetCustomPayload() if !reflect.DeepEqual(customPayload, rCustomPayload) { t.Fatal("The received custom payload should match the sent") } iter.Close() // Batch Message b := session.Batch(LoggedBatch) b.CustomPayload = customPayload b.Query(fmt.Sprintf("INSERT INTO %s(id,value) VALUES(1, 1)", table)) if err := session.ExecuteBatch(b); err != nil { t.Fatalf("query failed. %v", err) } } func TestCustomPayloadValues(t *testing.T) { t.Parallel() t.Skip("SKIPPING") cluster := createCluster() session := createSessionFromCluster(cluster, t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf("CREATE TABLE gocql_test.%s (id int, value int, PRIMARY KEY (id))", table)); err != nil { t.Fatal(err) } values := []map[string][]byte{map[string][]byte{"a": []byte{10, 20}, "b": []byte{20, 30}}, nil, map[string][]byte{"a": []byte{10, 20}, "b": nil}} for _, customPayload := range values { query := session.Query(fmt.Sprintf("SELECT id FROM %s where id = ?", table), 42).Consistency(One).CustomPayload(customPayload) iter := query.Iter() rCustomPayload := iter.GetCustomPayload() if !reflect.DeepEqual(customPayload, rCustomPayload) { t.Fatal("The received custom payload should match the sent") } } } func TestSessionAwaitSchemaAgreement(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() if err := session.AwaitSchemaAgreement(context.Background()); err != nil { t.Fatalf("expected session.AwaitSchemaAgreement to not return an error but got '%v'", err) } } func TestSessionAwaitSchemaAgreementSessionClosed(t *testing.T) { t.Parallel() session := createSession(t) session.Close() if err := session.AwaitSchemaAgreement(context.Background()); !errors.Is(err, ErrConnectionClosed) { t.Fatalf("expected session.AwaitSchemaAgreement to return ErrConnectionClosed but got '%v'", err) } } func TestSessionAwaitSchemaAgreementContextCanceled(t *testing.T) { t.Parallel() session := createSession(t) ctx, cancel := context.WithCancel(context.Background()) cancel() if err := session.AwaitSchemaAgreement(ctx); !errors.Is(err, context.Canceled) { t.Fatalf("expected session.AwaitSchemaAgreement to return 'context canceled' but got '%v'", err) } } func TestNewConnectWithLowTimeout(t *testing.T) { t.Parallel() // Point of these tests to make sure that with low timeout connection creation will gracefully fail type TestExpectation int const ( DontRun TestExpectation = iota Fail TestExpectation = iota Pass TestExpectation = iota CanPass TestExpectation = iota ) match := func(t *testing.T, e TestExpectation, result error) { t.Helper() switch e { case DontRun: t.Fatal("should not be run") case Fail: if result == nil { t.Fatal("should return an error") } case Pass: if result != nil { t.Fatalf("should pass, but returned an error: %s", result.Error()) } case CanPass: if result == nil { t.Log("test passed due to high timeout") } default: panic(fmt.Sprintf("unknown test expectation: %v", e)) } } for _, lowTimeout := range []time.Duration{1 * time.Nanosecond, 10 * time.Nanosecond, 100 * time.Nanosecond} { canPassOnHighTimeout := Fail if lowTimeout >= 100*time.Nanosecond { canPassOnHighTimeout = CanPass } t.Run(lowTimeout.String(), func(t *testing.T) { for _, tcase := range []struct { name string getCluster func() *ClusterConfig connect TestExpectation regularQuery TestExpectation controlQuery TestExpectation controlQueryAfterReconnect TestExpectation }{ { name: "Timeout", getCluster: func() *ClusterConfig { cluster := createCluster() cluster.Timeout = lowTimeout return cluster }, connect: Pass, regularQuery: Fail, controlQuery: Pass, controlQueryAfterReconnect: Pass, }, { name: "MetadataSchemaRequestTimeout", getCluster: func() *ClusterConfig { cluster := createCluster() cluster.MetadataSchemaRequestTimeout = lowTimeout return cluster }, connect: Pass, regularQuery: Pass, controlQuery: Fail, // It breaks control connection, then it can start reconnecting in any moment // As result test is not stable controlQueryAfterReconnect: Fail, }, { name: "WriteTimeout", getCluster: func() *ClusterConfig { cluster := createCluster() cluster.WriteTimeout = lowTimeout return cluster }, connect: Pass, regularQuery: canPassOnHighTimeout, controlQuery: canPassOnHighTimeout, // It breaks control connection, then it can start reconnecting in any moment // As result test is not stable controlQueryAfterReconnect: canPassOnHighTimeout, }, { name: "ReadTimeout", getCluster: func() *ClusterConfig { cluster := createCluster() cluster.ReadTimeout = lowTimeout return cluster }, connect: Pass, // When data is available immediately reading from socket is not failing, // despite that deadline is in the past // Because of that even with low read timeout it can pass regularQuery: CanPass, controlQuery: CanPass, // It breaks control connection, then it can start reconnecting in any moment // As result test is not stable controlQueryAfterReconnect: CanPass, }, { name: "AllTimeouts", getCluster: func() *ClusterConfig { cluster := createCluster() cluster.Timeout = lowTimeout cluster.ReadTimeout = lowTimeout cluster.WriteTimeout = lowTimeout cluster.MetadataSchemaRequestTimeout = lowTimeout return cluster }, connect: Pass, regularQuery: Fail, controlQuery: Fail, controlQueryAfterReconnect: Fail, }, } { t.Run(tcase.name, func(t *testing.T) { var ( s *Session err error ) t.Run("Connect", func(t *testing.T) { s, err = tcase.getCluster().CreateSession() match(t, tcase.connect, err) if err != nil { t.Fatal("failed to create session", err.Error()) } }) if s != nil { defer s.Close() } else { if tcase.connect == Fail { t.FailNow() } else { t.Fatal("session was not created") } } if tcase.regularQuery != DontRun { t.Run("Regular Query", func(t *testing.T) { err = s.Query("SELECT key FROM system.local WHERE key='local'").Exec() match(t, tcase.regularQuery, err) }) } if tcase.controlQuery != DontRun { t.Run("Query from control connection", func(t *testing.T) { err = s.control.querySystem("SELECT key FROM system.local WHERE key='local'").err match(t, tcase.controlQuery, err) }) } if tcase.controlQueryAfterReconnect != DontRun { t.Run("Query from control connection after reconnect", func(t *testing.T) { s, err = tcase.getCluster().CreateSession() if err != nil { t.Fatal("failed to create session", err.Error()) } defer s.Close() err = s.control.reconnect() if err != nil { t.Fatalf("failed to reconnect to control connection: %v", err) } err = s.control.querySystem("SELECT key FROM system.local WHERE key='local'").err match(t, tcase.controlQueryAfterReconnect, err) }) } }) } }) } } ================================================ FILE: internal/ccm/ccm.go ================================================ //go:build ccm // +build ccm /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package ccm import ( "bufio" "bytes" "errors" "fmt" "os/exec" "runtime" "strings" ) func execCmd(args ...string) (*bytes.Buffer, error) { execName := "ccm" if runtime.GOOS == "windows" { args = append([]string{"/c", execName}, args...) execName = "cmd.exe" } cmd := exec.Command(execName, args...) stdout := &bytes.Buffer{} cmd.Stdout = stdout cmd.Stderr = &bytes.Buffer{} if err := cmd.Run(); err != nil { return nil, errors.New(cmd.Stderr.(*bytes.Buffer).String()) } return stdout, nil } func AllUp() error { status, err := Status() if err != nil { return err } for _, host := range status { if !host.State.IsUp() { if err := NodeUp(host.Name); err != nil { return err } } } return nil } func NodeUp(node string) error { args := []string{node, "start", "--wait-for-binary-proto"} if runtime.GOOS == "windows" { args = append(args, "--quiet-windows") } _, err := execCmd(args...) return err } func NodeDown(node string) error { _, err := execCmd(node, "stop") return err } type Host struct { State NodeState Addr string Name string } type NodeState int func (n NodeState) String() string { if n == NodeStateUp { return "UP" } else if n == NodeStateDown { return "DOWN" } else { return fmt.Sprintf("UNKNOWN_STATE_%d", n) } } func (n NodeState) IsUp() bool { return n == NodeStateUp } const ( NodeStateUp NodeState = iota NodeStateDown ) func Status() (map[string]Host, error) { // TODO: parse into struct to manipulate out, err := execCmd("status", "-v") if err != nil { return nil, err } const ( stateCluster = iota stateCommas stateNode stateOption ) nodes := make(map[string]Host) // didnt really want to write a full state machine parser state := stateCluster sc := bufio.NewScanner(out) var host Host for sc.Scan() { switch state { case stateCluster: text := sc.Text() if !strings.HasPrefix(text, "Cluster:") { return nil, fmt.Errorf("expected 'Cluster:' got %q", text) } state = stateCommas case stateCommas: text := sc.Text() if !strings.HasPrefix(text, "-") { return nil, fmt.Errorf("expected commas got %q", text) } state = stateNode case stateNode: // assume nodes start with node text := sc.Text() if !strings.HasPrefix(text, "node") { return nil, fmt.Errorf("expected 'node' got %q", text) } line := strings.Split(text, ":") host.Name = line[0] nodeState := strings.TrimSpace(line[1]) switch nodeState { case "UP": host.State = NodeStateUp case "DOWN": host.State = NodeStateDown default: return nil, fmt.Errorf("unknown node state from ccm: %q", nodeState) } state = stateOption case stateOption: text := sc.Text() if text == "" { state = stateNode nodes[host.Name] = host host = Host{} continue } line := strings.Split(strings.TrimSpace(text), "=") k, v := line[0], line[1] if k == "binary" { // could check errors // ('127.0.0.1', 9042) v = v[2:] // ('' if i := strings.IndexByte(v, '\''); i < 0 { return nil, fmt.Errorf("invalid binary v=%q", v) } else { host.Addr = v[:i] // dont need port } } default: return nil, fmt.Errorf("unexpected state: %q", state) } } if err := sc.Err(); err != nil { return nil, fmt.Errorf("unable to parse ccm status: %v", err) } return nodes, nil } ================================================ FILE: internal/ccm/ccm_test.go ================================================ //go:build all || ccm // +build all ccm /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package ccm import ( "testing" ) func TestCCM(t *testing.T) { if err := AllUp(); err != nil { t.Fatal(err) } status, err := Status() if err != nil { t.Fatal(err) } if host, ok := status["node1"]; !ok { t.Fatal("node1 not in status list") } else if !host.State.IsUp() { t.Fatal("node1 is not up") } NodeDown("node1") status, err = Status() if err != nil { t.Fatal(err) } if host, ok := status["node1"]; !ok { t.Fatal("node1 not in status list") } else if host.State.IsUp() { t.Fatal("node1 is not down") } NodeUp("node1") status, err = Status() if err != nil { t.Fatal(err) } if host, ok := status["node1"]; !ok { t.Fatal("node1 not in status list") } else if !host.State.IsUp() { t.Fatal("node1 is not up") } } ================================================ FILE: internal/debug/debug_off.go ================================================ //go:build !gocql_debug // +build !gocql_debug /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package debug const Enabled = false ================================================ FILE: internal/debug/debug_on.go ================================================ //go:build gocql_debug // +build gocql_debug /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package debug const Enabled = true ================================================ FILE: internal/eventbus/README.md ================================================ # EventBus A generic, thread-safe event processing package for Go based on channels. EventBus allows multiple subscribers to receive events from a single input channel, with optional event filtering and configurable buffer sizes. ## Features - **Generic Type Support**: Works with any type using Go generics - **Thread-Safe**: Safe for concurrent use by multiple goroutines - **Event Filtering**: Subscribers can filter events using custom filter functions - **Configurable Buffers**: Each subscriber can have its own channel buffer size - **Non-Blocking Distribution**: Slow subscribers don't block event distribution - **Context Support**: Automatic unsubscription when context is cancelled - **Zero External Dependencies**: Pure Go implementation ## Installation ```bash go get github.com/gocql/gocql/eventbus ``` ## Quick Start ```go package main import ( "fmt" "github.com/gocql/gocql/internal/eventbus" ) func main() { // Create a new EventBus for integer events eb := eventbus.New[int](10) // input buffer size of 10 // Start processing events eb.Start() defer eb.Stop() // Subscribe to all events allSub, _ := eb.Subscribe("subscriber1", 10, nil) // Subscribe with a filter (only even numbers) evenFilter := func(n int) bool { return n%2 == 0 } evenSub, _ := eb.Subscribe("subscriber2", 10, evenFilter) // Send events go func() { for i := 1; i <= 5; i++ { eb.PublishEvent(i) } }() // Receive events for event := range allSub.Events() { fmt.Println("All:", event) // Process event... if event == 5 { break } } // Clean up allSub.Stop() evenSub.Stop() } ``` ## API Overview ### Creating an EventBus ```go eb := eventbus.New[T](inputChanSize int) *EventBus[T] ``` Creates a new EventBus for type `T` with specified input channel buffer size. ### Starting and Stopping ```go err := eb.Start() // Start processing events err := eb.Stop() // Stop processing and close all subscriber channels ``` ### Subscribing ```go // Subscribe without filter sub, err := eb.Subscribe("subscriber-name", chanSize, nil) // Subscribe with filter filter := func(event T) bool { return /* condition */ } sub, err := eb.Subscribe("subscriber-name", chanSize, filter) // Subscribe with context (auto-unsubscribes when context is cancelled) sub, err := eb.SubscribeWithContext(ctx, "subscriber-name", chanSize, filter) // Access events from subscriber events := sub.Events() // Returns <-chan T ``` ### Unsubscribing ```go // Using the Subscriber instance (recommended) err := sub.Stop() // Or using the EventBus directly err := eb.Unsubscribe("subscriber-name") ``` ### Sending Events ```go eb.PublishEvent(event) ``` ### Getting Information ```go count := eb.SubscriberCount() str := eb.String() // Debug string representation ``` ## Examples ### Basic Usage ```go eb := eventbus.New[string](10) eb.Start() defer eb.Stop() sub, _ := eb.Subscribe("logger", 10, nil) defer sub.Stop() go func() { eb.PublishEvent("Hello") eb.PublishEvent("World") }() for msg := range sub.Events() { fmt.Println(msg) } ``` ### With Event Filtering ```go type LogEvent struct { Level string Message string } eb := eventbus.New[LogEvent](10) eb.Start() defer eb.Stop() // Subscribe to errors only errorFilter := func(e LogEvent) bool { return e.Level == "ERROR" } errorSub, _ := eb.Subscribe("error-handler", 10, errorFilter) defer errorSub.Stop() // Subscribe to all events allSub, _ := eb.Subscribe("all-handler", 10, nil) defer allSub.Stop() // Access events for event := range errorSub.Events() { // Handle error events only } ``` ### With Context ```go ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() eb := eventbus.New[int](10) eb.Start() defer eb.Stop() // Automatically unsubscribes after 5 seconds sub, _ := eb.SubscribeWithContext(ctx, "temp", 10, nil) for { select { case event := <-sub.Events(): // Process event case <-ctx.Done(): return } } ``` ## Design Decisions ### Non-Blocking Distribution EventBus uses non-blocking sends to subscriber channels. If a subscriber's channel is full, the event is dropped for that subscriber only, ensuring that slow subscribers don't block the event bus or other subscribers. ### Channel Closure - Subscriber channels are closed when `Subscriber.Stop()` or `EventBus.Unsubscribe()` is called - All subscriber channels are closed when `EventBus.Stop()` is called - When using `SubscribeWithContext()`, channels are closed when the context is cancelled ### Subscriber API The `Subscribe()` and `SubscribeWithContext()` methods return a `Subscriber` instance that provides: - `Events()` - Returns the receive-only channel for events - `Stop()` - Unsubscribes and closes the channel ### Thread Safety All public methods are thread-safe and can be called concurrently from multiple goroutines. The EventBus uses read-write mutexes to minimize contention during event distribution. ## Performance Considerations - **Buffer Sizes**: Choose appropriate buffer sizes based on your event rate and processing speed - **Filter Functions**: Keep filter functions fast; they're called for every event-subscriber pair - **Slow Subscribers**: Slow subscribers with small buffers will drop events; increase buffer size or process events asynchronously ## Testing Run unit tests: ```bash go test -tags unit -v ./eventbus ``` Run benchmarks: ```bash go test -tags unit -bench=. ./eventbus ``` ## License Licensed under the Apache License, Version 2.0. See LICENSE file for details. ================================================ FILE: internal/eventbus/eventbus.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // Package eventbus provides a generic event processing system based on channels. // It allows multiple subscribers to receive events from a single input channel, // with optional filtering and configurable buffer sizes. package eventbus import ( "context" "errors" "fmt" "sync" ) var ( // ErrAlreadyStarted is returned when Start is called on an already running EventBus ErrAlreadyStarted = errors.New("eventbus: already started") // ErrNotStarted is returned when operations are attempted on a non-started EventBus ErrNotStarted = errors.New("eventbus: not started") // ErrAlreadyStopped is returned when Stop is called on an already stopped EventBus ErrAlreadyStopped = errors.New("eventbus: already stopped") // ErrSubscriberNotFound is returned when unsubscribing a non-existent subscriber ErrSubscriberNotFound = errors.New("eventbus: subscriber not found") ) // FilterFunc is a function type that filters events. // It returns true if the event should be sent to the subscriber, false otherwise. type FilterFunc[T any] func(T) bool // subscriber represents a single subscriber to the event bus type subscriber[T any] struct { ch chan T filter FilterFunc[T] name string id int } // Subscriber provides access to events and control over the subscription type Subscriber[T any] struct { ch <-chan T eb *EventBus[T] name string id int } // Events returns the channel to receive events from func (s *Subscriber[T]) Events() <-chan T { return s.ch } // Stop unsubscribes and closes the subscriber's channel func (s *Subscriber[T]) Stop() error { return s.eb.remove(s) } type status uint8 const ( statusInitialized status = iota statusStarted statusStopped ) // EventBus manages event distribution to multiple subscribers type EventBus[T any] struct { logger StdLogger input chan T closedSignal chan struct{} subscribers []*subscriber[T] wg sync.WaitGroup cfg EventBusConfig mu sync.RWMutex status status } type StdLogger interface { Print(v ...any) Printf(format string, v ...any) Println(v ...any) } type EventBusConfig struct { // Size of the event queue, when queue gets more events than queue events are getting dropped. InputEventsQueueSize int } // New creates a new EventBus with the specified input channel buffer size. // The EventBus must be status with Start() before it begins processing events. func New[T any](cfg EventBusConfig, logger StdLogger) *EventBus[T] { return &EventBus[T]{ cfg: cfg, logger: logger, input: make(chan T, cfg.InputEventsQueueSize), closedSignal: make(chan struct{}, 1), status: statusInitialized, } } // Start begins processing events from the input channel and distributing them to subscribers. // Returns ErrAlreadyStarted if the EventBus is already running. func (eb *EventBus[T]) Start() error { eb.mu.Lock() defer eb.mu.Unlock() switch eb.status { case statusStarted: return ErrAlreadyStarted case statusStopped: return ErrAlreadyStopped default: } eb.status = statusStarted eb.wg.Add(1) go eb.run() return nil } // Stop halts event processing and closes all subscriber channels. // It waits for the processing goroutine to finish. // Returns ErrNotStarted if the EventBus was never started, or ErrAlreadyStopped if already stopped. func (eb *EventBus[T]) Stop() error { eb.mu.Lock() defer eb.mu.Unlock() switch eb.status { case statusStopped: return ErrAlreadyStopped case statusInitialized: return ErrNotStarted default: eb.status = statusStopped } close(eb.closedSignal) // Wait for the run goroutine to finish eb.mu.Unlock() eb.wg.Wait() eb.mu.Lock() for _, sub := range eb.subscribers { close(sub.ch) } eb.subscribers = nil return nil } // PublishEvent sends an event onto the bus. If the input buffer is full the // event is dropped to avoid blocking publishers. func (eb *EventBus[T]) PublishEvent(e T) bool { select { case eb.input <- e: return true default: return false } } // PublishEventBlocking sends an event onto the bus. If the input buffer is full is blocks, until event is published func (eb *EventBus[T]) PublishEventBlocking(e T) { select { case eb.input <- e: } } // Subscribe adds a new subscriber to the event bus. // name: unique identifier for the subscriber // queueSize: buffer size for the subscriber's channel (must be >= 0) // filter: optional filter function (can be nil to receive all events) // // Returns a Subscriber instance that provides access to events and a Stop method. func (eb *EventBus[T]) Subscribe(name string, queueSize int, filter FilterFunc[T]) *Subscriber[T] { eb.mu.Lock() defer eb.mu.Unlock() sub := &subscriber[T]{ name: name, ch: make(chan T, queueSize), filter: filter, } eb.subscribers = append(eb.subscribers, sub) return &Subscriber[T]{ ch: sub.ch, name: name, eb: eb, } } // Unsubscribe removes a subscriber from the event bus and closes its channel. // Returns ErrSubscriberNotFound if the subscriber doesn't exist. func (eb *EventBus[T]) remove(s *Subscriber[T]) error { eb.mu.Lock() defer eb.mu.Unlock() subID := -1 for id, sub := range eb.subscribers { if s.ch == sub.ch { subID = id close(sub.ch) } } if subID == -1 { return ErrSubscriberNotFound } eb.subscribers = append(eb.subscribers[0:subID], eb.subscribers[subID+1:]...) return nil } // SubscriberCount returns the current number of active subscribers func (eb *EventBus[T]) SubscriberCount() int { eb.mu.RLock() defer eb.mu.RUnlock() return len(eb.subscribers) } // run is the main event processing loop func (eb *EventBus[T]) run() { defer eb.wg.Done() for { select { case <-eb.closedSignal: return case event, ok := <-eb.input: if !ok { if eb.logger == nil { eb.logger.Printf("eventbus channel has been closed, it should not have happened, report the bug please.") } return } eb.distribute(event) } } } // distribute sends an event to all matching subscribers func (eb *EventBus[T]) distribute(event T) { eb.mu.RLock() defer eb.mu.RUnlock() for _, sub := range eb.subscribers { if sub.filter != nil && !sub.filter(event) { continue } // Non-blocking send to avoid slow subscribers blocking the bus select { case sub.ch <- event: default: if eb.logger != nil { eb.logger.Printf("eventbus: dropped event for subscriber %s, make sure it is running and update it's channel size\n", sub.name) } } } } // SubscribeWithContext subscribes with a context that can cancel the subscription. // The subscriber will be automatically unsubscribed when the context is cancelled. // Returns a Subscriber instance and an error if subscription fails. func (eb *EventBus[T]) SubscribeWithContext(ctx context.Context, name string, chanSize int, filter FilterFunc[T]) *Subscriber[T] { sub := eb.Subscribe(name, chanSize, filter) // Start a goroutine to handle context cancellation go func() { <-ctx.Done() _ = eb.remove(sub) // Ignore error if already unsubscribed }() return sub } // String returns a string representation of the EventBus for debugging func (eb *EventBus[T]) String() string { eb.mu.RLock() defer eb.mu.RUnlock() return fmt.Sprintf("EventBus{subscribers: %d, status: %v}", len(eb.subscribers), eb.status) } ================================================ FILE: internal/eventbus/eventbus_test.go ================================================ //go:build unit // +build unit /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package eventbus import ( "context" "sync" "testing" "time" ) func TestNew(t *testing.T) { eb := New[int]( EventBusConfig{ InputEventsQueueSize: 10, }, nil) if eb == nil { t.Fatal("New returned nil") } if eb.input == nil { t.Error("input channel is nil") } if len(eb.subscribers) != 0 { t.Error("subscribers list is not empty") } if eb.status != statusInitialized { t.Error("EventBus should not be status initially") } } func TestStartStop(t *testing.T) { eb := New[int]( EventBusConfig{ InputEventsQueueSize: 10, }, nil) // Test starting err := eb.Start() if err != nil { t.Fatalf("Start failed: %v", err) } // Test double start err = eb.Start() if err != ErrAlreadyStarted { t.Errorf("Expected ErrAlreadyStarted, got: %v", err) } // Test stopping err = eb.Stop() if err != nil { t.Fatalf("Stop failed: %v", err) } // Test double stop err = eb.Stop() if err != ErrAlreadyStopped { t.Errorf("Expected ErrAlreadyStopped, got: %v", err) } } func TestStopWithoutStart(t *testing.T) { eb := New[int]( EventBusConfig{ InputEventsQueueSize: 10, }, nil) err := eb.Stop() if err != ErrNotStarted { t.Errorf("Expected ErrNotStarted, got: %v", err) } } func TestEventDistribution(t *testing.T) { eb := New[int]( EventBusConfig{ InputEventsQueueSize: 10, }, nil) err := eb.Start() if err != nil { t.Fatalf("Start failed: %v", err) } defer eb.Stop() sub1 := eb.Subscribe("sub1", 10, nil) sub2 := eb.Subscribe("sub2", 10, nil) // Send events eb.PublishEvent(1) eb.PublishEvent(2) eb.PublishEvent(3) // Verify both subscribers receive all events for i := 1; i <= 3; i++ { select { case val := <-sub1.Events(): if val != i { t.Errorf("sub1: expected %d, got %d", i, val) } case <-time.After(1 * time.Second): t.Fatal("sub1: timeout waiting for event") } select { case val := <-sub2.Events(): if val != i { t.Errorf("sub2: expected %d, got %d", i, val) } case <-time.After(1 * time.Second): t.Fatal("sub2: timeout waiting for event") } } } func TestEventFiltering(t *testing.T) { eb := New[int]( EventBusConfig{ InputEventsQueueSize: 10, }, nil) err := eb.Start() if err != nil { t.Fatalf("Start failed: %v", err) } defer eb.Stop() // Subscriber 1: no filter (receives all) sub1 := eb.Subscribe("all", 10, nil) // Subscriber 2: only even numbers evenFilter := func(n int) bool { return n%2 == 0 } sub2 := eb.Subscribe("even", 10, evenFilter) // Subscriber 3: only odd numbers oddFilter := func(n int) bool { return n%2 != 0 } sub3 := eb.Subscribe("odd", 10, oddFilter) // Send events for i := 1; i <= 6; i++ { eb.PublishEvent(i) } // Verify subscriber 1 gets all events received1 := make([]int, 0, 6) for i := 0; i < 6; i++ { select { case val := <-sub1.Events(): received1 = append(received1, val) case <-time.After(1 * time.Second): t.Fatal("timeout waiting for event on ch1") } } if len(received1) != 6 { t.Errorf("ch1: expected 6 events, got %d", len(received1)) } // Verify subscriber 2 gets only even numbers received2 := make([]int, 0, 3) for i := 0; i < 3; i++ { select { case val := <-sub2.Events(): if val%2 != 0 { t.Errorf("ch2: received odd number %d", val) } received2 = append(received2, val) case <-time.After(1 * time.Second): t.Fatal("timeout waiting for event on ch2") } } // Verify subscriber 3 gets only odd numbers received3 := make([]int, 0, 3) for i := 0; i < 3; i++ { select { case val := <-sub3.Events(): if val%2 == 0 { t.Errorf("ch3: received even number %d", val) } received3 = append(received3, val) case <-time.After(1 * time.Second): t.Fatal("timeout waiting for event on ch3") } } } func TestSubscriberCount(t *testing.T) { eb := New[int]( EventBusConfig{ InputEventsQueueSize: 10, }, nil) if eb.SubscriberCount() != 0 { t.Errorf("Expected 0 subscribers, got %d", eb.SubscriberCount()) } sub1 := eb.Subscribe("sub1", 5, nil) if eb.SubscriberCount() != 1 { t.Errorf("Expected 1 subscriber, got %d", eb.SubscriberCount()) } eb.Subscribe("sub2", 5, nil) if eb.SubscriberCount() != 2 { t.Errorf("Expected 2 subscribers, got %d", eb.SubscriberCount()) } err := sub1.Stop() if err != nil { t.Fatalf("Stop failed: %v", err) } if eb.SubscriberCount() != 1 { t.Errorf("Expected 1 subscriber, got %d", eb.SubscriberCount()) } } func TestConcurrentSubscribers(t *testing.T) { eb := New[int]( EventBusConfig{ InputEventsQueueSize: 10, }, nil) err := eb.Start() if err != nil { t.Fatalf("Start failed: %v", err) } defer eb.Stop() numSubscribers := 10 eventsPerSubscriber := 100 var wg sync.WaitGroup wg.Add(numSubscribers) // Create multiple subscribers for i := 0; i < numSubscribers; i++ { sub := eb.Subscribe(string(rune('A'+i)), 100, nil) go func(sub *Subscriber[int], subName string) { defer wg.Done() count := 0 for range sub.Events() { count++ if count == eventsPerSubscriber { return } } }(sub, string(rune('A'+i))) } // Send events go func() { for i := 0; i < eventsPerSubscriber; i++ { eb.PublishEventBlocking(i) } }() // Wait for all subscribers to receive their events done := make(chan struct{}) go func() { wg.Wait() close(done) }() select { case <-done: // All subscribers received events case <-time.After(5 * time.Second): t.Fatal("timeout waiting for subscribers") } } func TestSubscribeWithContext(t *testing.T) { eb := New[int]( EventBusConfig{ InputEventsQueueSize: 10, }, nil) err := eb.Start() if err != nil { t.Fatalf("Start failed: %v", err) } defer eb.Stop() ctx, cancel := context.WithCancel(context.Background()) sub := eb.SubscribeWithContext(ctx, "test", 10, nil) if err != nil { t.Fatalf("SubscribeWithContext failed: %v", err) } // Send an event eb.PublishEvent(42) // Verify event is received select { case val := <-sub.Events(): if val != 42 { t.Errorf("Expected 42, got %d", val) } case <-time.After(1 * time.Second): t.Fatal("timeout waiting for event") } // Cancel context cancel() // Give it time to be removed time.Sleep(100 * time.Millisecond) // Verify subscriber was removed if eb.SubscriberCount() != 0 { t.Errorf("Expected 0 subscribers after context cancel, got %d", eb.SubscriberCount()) } } func TestChannelClosedOnStop(t *testing.T) { eb := New[int]( EventBusConfig{ InputEventsQueueSize: 10, }, nil) err := eb.Start() if err != nil { t.Fatalf("Start failed: %v", err) } sub := eb.Subscribe("test", 10, nil) if err != nil { t.Fatalf("Subscribe failed: %v", err) } err = eb.Stop() if err != nil { t.Fatalf("Stop failed: %v", err) } // Verify channel is closed select { case _, ok := <-sub.Events(): if ok { t.Error("Channel should be closed") } case <-time.After(1 * time.Second): t.Fatal("timeout waiting for channel close") } } func TestSubscriberStopAfterEventBusStop(t *testing.T) { eb := New[int]( EventBusConfig{ InputEventsQueueSize: 10, }, nil) err := eb.Start() if err != nil { t.Fatalf("Start failed: %v", err) } sub := eb.Subscribe("test", 10, nil) err = eb.Stop() if err != nil { t.Fatalf("Stop failed: %v", err) } defer func() { if r := recover(); r != nil { t.Fatalf("Stop should not panic after eventbus Stop: %v", r) } }() err = sub.Stop() if err != ErrSubscriberNotFound { t.Fatalf("Expected ErrSubscriberNotFound, got: %v", err) } } func TestChannelClosedOnUnsubscribe(t *testing.T) { eb := New[int]( EventBusConfig{ InputEventsQueueSize: 10, }, nil) sub := eb.Subscribe("test", 10, nil) err := sub.Stop() if err != nil { t.Fatalf("Stop failed: %v", err) } // Verify channel is closed select { case _, ok := <-sub.Events(): if ok { t.Error("Channel should be closed") } default: // Channel might not be immediately readable, try with timeout time.Sleep(10 * time.Millisecond) select { case _, ok := <-sub.Events(): if ok { t.Error("Channel should be closed") } default: t.Error("Channel should be closed but is not readable") } } } func TestString(t *testing.T) { eb := New[int]( EventBusConfig{ InputEventsQueueSize: 10, }, nil) str := eb.String() if str == "" { t.Error("String() returned empty string") } eb.Subscribe("test", 5, nil) str = eb.String() if str == "" { t.Error("String() returned empty string after subscription") } } func TestSlowSubscriberDoesNotBlockBus(t *testing.T) { eb := New[int]( EventBusConfig{ InputEventsQueueSize: 10, }, nil) err := eb.Start() if err != nil { t.Fatalf("Start failed: %v", err) } defer eb.Stop() // Fast subscriber with buffer fastSub := eb.Subscribe("fast", 100, nil) // Slow subscriber with small buffer slowSub := eb.Subscribe("slow", 1, nil) // Send many events quickly for i := 0; i < 50; i++ { eb.PublishEventBlocking(i) } // Fast subscriber should receive most/all events fastCount := 0 timeout := time.After(1 * time.Second) drainFast: for { select { case <-fastSub.Events(): fastCount++ if fastCount == 50 { break drainFast } case <-timeout: break drainFast } } if fastCount < 50 { // Should receive most events t.Errorf("Fast subscriber only received %d events, expected 50", fastCount) } // Slow subscriber may have dropped events (buffer overflow) slowCount := 0 drainSlow: for { select { case <-slowSub.Events(): slowCount++ default: break drainSlow } } // Slow subscriber should have received some events but likely not all t.Logf("Slow subscriber received %d events (some may have been dropped)", slowCount) } func BenchmarkEventDistribution(b *testing.B) { eb := New[int]( EventBusConfig{ InputEventsQueueSize: 1000, }, nil) eb.Start() defer eb.Stop() // Create 10 subscribers for i := 0; i < 10; i++ { eb.Subscribe(string(rune('A'+i)), 1000, nil) } b.ResetTimer() for i := 0; i < b.N; i++ { eb.PublishEvent(i) } } func BenchmarkEventDistributionWithFilter(b *testing.B) { eb := New[int]( EventBusConfig{ InputEventsQueueSize: 1000, }, nil) eb.Start() defer eb.Stop() filter := func(n int) bool { return n%2 == 0 } // Create 10 subscribers with filters for i := 0; i < 10; i++ { eb.Subscribe(string(rune('A'+i)), 1000, filter) } b.ResetTimer() for i := 0; i < b.N; i++ { eb.PublishEvent(i) } } ================================================ FILE: internal/eventbus/example_test.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package eventbus_test import ( "context" "fmt" "time" "github.com/gocql/gocql/internal/eventbus" ) // Example demonstrates basic usage of EventBus func Example() { // Create a new EventBus for integer events with input buffer of 10 eb := eventbus.New[int](eventbus.EventBusConfig{ InputEventsQueueSize: 10, }, nil) // Start the event bus if err := eb.Start(); err != nil { panic(err) } defer eb.Stop() // Subscribe to all events allSub := eb.Subscribe("all-subscriber", 10, nil) // Subscribe to even numbers only evenFilter := func(n int) bool { return n%2 == 0 } evenSub := eb.Subscribe("even-subscriber", 10, evenFilter) // Send some events go func() { for i := 1; i <= 5; i++ { eb.PublishEvent(i) } }() // Receive events time.Sleep(100 * time.Millisecond) // Give time for events to be distributed // Drain all events from allEvents for { select { case val := <-allSub.Events(): fmt.Printf("All subscriber received: %d\n", val) default: goto evenLoop } } evenLoop: // Drain even events for { select { case val := <-evenSub.Events(): fmt.Printf("Even subscriber received: %d\n", val) default: return } } // Output: // All subscriber received: 1 // All subscriber received: 2 // All subscriber received: 3 // All subscriber received: 4 // All subscriber received: 5 // Even subscriber received: 2 // Even subscriber received: 4 } // Example_withContext demonstrates using context-based subscriptions func Example_withContext() { eb := eventbus.New[string]( eventbus.EventBusConfig{ InputEventsQueueSize: 10, }, nil) eb.Start() defer eb.Stop() // Create a context that will be cancelled ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) defer cancel() // Subscribe with context - will auto-remove when context is cancelled sub := eb.SubscribeWithContext(ctx, "temp-subscriber", 10, nil) // Send some events go func() { for i := 0; i < 3; i++ { eb.PublishEvent(fmt.Sprintf("event-%d", i)) time.Sleep(30 * time.Millisecond) } }() // Receive events until context is cancelled for { select { case event := <-sub.Events(): fmt.Println("Received:", event) case <-ctx.Done(): fmt.Println("Context cancelled, subscription ended") return } } // Output: // Received: event-0 // Received: event-1 // Received: event-2 // Context cancelled, subscription ended } // Example_multipleSubscribers demonstrates multiple subscribers with different filters func Example_multipleSubscribers() { type LogEvent struct { Level string Message string } eb := eventbus.New[LogEvent](eventbus.EventBusConfig{ InputEventsQueueSize: 10, }, nil) eb.Start() defer eb.Stop() // Subscribe to error logs only errorFilter := func(e LogEvent) bool { return e.Level == "ERROR" } errorSub := eb.Subscribe("error-logger", 5, errorFilter) // Subscribe to all logs allSub := eb.Subscribe("all-logger", 10, nil) // Send various log events go func() { logs := []LogEvent{ {Level: "INFO", Message: "Application status"}, {Level: "ERROR", Message: "Connection failed"}, {Level: "INFO", Message: "Retrying connection"}, {Level: "ERROR", Message: "Max retries exceeded"}, } for _, log := range logs { eb.PublishEvent(log) } }() time.Sleep(100 * time.Millisecond) fmt.Println("Error logger received:") for { select { case event := <-errorSub.Events(): fmt.Printf(" [%s] %s\n", event.Level, event.Message) default: goto allLogs } } allLogs: fmt.Println("All logger received:") for { select { case event := <-allSub.Events(): fmt.Printf(" [%s] %s\n", event.Level, event.Message) default: return } } // Output: // Error logger received: // [ERROR] Connection failed // [ERROR] Max retries exceeded // All logger received: // [INFO] Application status // [ERROR] Connection failed // [INFO] Retrying connection // [ERROR] Max retries exceeded } // Example_unsubscribe demonstrates dynamic subscription management func Example_unsubscribe() { eb := eventbus.New[int](eventbus.EventBusConfig{ InputEventsQueueSize: 10, }, nil) eb.Start() defer eb.Stop() // Subscribe sub := eb.Subscribe("temporary", 10, nil) // Send first batch of events eb.PublishEvent(1) eb.PublishEvent(2) // Receive first batch fmt.Println("Before remove:") for i := 0; i < 2; i++ { fmt.Println("Received:", <-sub.Events()) } // Unsubscribe using Stop method err := sub.Stop() if err != nil { panic(err) } // Send more events (won't be received) eb.PublishEvent(3) eb.PublishEvent(4) // Channel is now closed if _, ok := <-sub.Events(); !ok { fmt.Println("Channel closed after remove") } // Output: // Before remove: // Received: 1 // Received: 2 // Channel closed after remove } ================================================ FILE: internal/frame/frames.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2012, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package frame import ( "fmt" "net" ) const ( protoDirectionMask = 0x80 protoVersionMask = 0x7F protoVersion1 = 0x01 protoVersion2 = 0x02 protoVersion3 = 0x03 protoVersion4 = 0x04 protoVersion5 = 0x05 maxFrameSize = 256 * 1024 * 1024 ) const ( // result kind ResultKindVoid = 1 ResultKindRows = 2 ResultKindKeyspace = 3 ResultKindPrepared = 4 ResultKindSchemaChanged = 5 // rows flags FlagGlobalTableSpec int = 0x01 FlagHasMorePages int = 0x02 FlagNoMetaData int = 0x04 // query flags FlagValues byte = 0x01 FlagSkipMetaData byte = 0x02 FlagPageSize byte = 0x04 FlagWithPagingState byte = 0x08 FlagWithSerialConsistency byte = 0x10 FlagDefaultTimestamp byte = 0x20 FlagWithNameValues byte = 0x40 FlagWithKeyspace byte = 0x80 // prepare flags FlagWithPreparedKeyspace uint32 = 0x01 // header flags FlagCompress byte = 0x01 FlagTracing byte = 0x02 FlagCustomPayload byte = 0x04 FlagWarning byte = 0x08 FlagBetaProtocol byte = 0x10 ) type ProtoVersion byte func (p ProtoVersion) Request() bool { return p&protoDirectionMask == 0x00 } func (p ProtoVersion) Response() bool { return p&protoDirectionMask == 0x80 } func (p ProtoVersion) Version() byte { return byte(p) & protoVersionMask } func (p ProtoVersion) String() string { dir := "REQ" if p.Response() { dir = "RESP" } return fmt.Sprintf("[version=%d direction=%s]", p.Version(), dir) } type Op byte const ( // header ops OpError Op = 0x00 OpStartup Op = 0x01 OpReady Op = 0x02 OpAuthenticate Op = 0x03 OpOptions Op = 0x05 OpSupported Op = 0x06 OpQuery Op = 0x07 OpResult Op = 0x08 OpPrepare Op = 0x09 OpExecute Op = 0x0A OpRegister Op = 0x0B OpEvent Op = 0x0C OpBatch Op = 0x0D OpAuthChallenge Op = 0x0E OpAuthResponse Op = 0x0F OpAuthSuccess Op = 0x10 ) func (f Op) String() string { switch f { case OpError: return "ERROR" case OpStartup: return "STARTUP" case OpReady: return "READY" case OpAuthenticate: return "AUTHENTICATE" case OpOptions: return "OPTIONS" case OpSupported: return "SUPPORTED" case OpQuery: return "QUERY" case OpResult: return "RESULT" case OpPrepare: return "PREPARE" case OpExecute: return "EXECUTE" case OpRegister: return "REGISTER" case OpEvent: return "EVENT" case OpBatch: return "BATCH" case OpAuthChallenge: return "AUTH_CHALLENGE" case OpAuthResponse: return "AUTH_RESPONSE" case OpAuthSuccess: return "AUTH_SUCCESS" default: return fmt.Sprintf("UNKNOWN_OP_%d", f) } } type FrameHeader struct { Warnings []string Stream int Length int Version ProtoVersion Flags byte Op Op } func (f FrameHeader) String() string { return fmt.Sprintf("[header version=%s flags=0x%x stream=%d op=%s length=%d]", f.Version, f.Flags, f.Stream, f.Op, f.Length) } func (f FrameHeader) Header() FrameHeader { return f } type Frame interface { Header() FrameHeader } type ReadyFrame struct { FrameHeader } type SupportedFrame struct { Supported map[string][]string FrameHeader } type SchemaChangeKeyspace struct { Change string Keyspace string FrameHeader } func (f SchemaChangeKeyspace) String() string { return fmt.Sprintf("[event schema_change_keyspace change=%q keyspace=%q]", f.Change, f.Keyspace) } type SchemaChangeTable struct { Change string Keyspace string Object string FrameHeader } func (f SchemaChangeTable) String() string { return fmt.Sprintf("[event schema_change change=%q keyspace=%q object=%q]", f.Change, f.Keyspace, f.Object) } type SchemaChangeType struct { Change string Keyspace string Object string FrameHeader } type SchemaChangeFunction struct { Change string Keyspace string Name string Args []string FrameHeader } type SchemaChangeAggregate struct { Change string Keyspace string Name string Args []string FrameHeader } type ClientRoutesChanged struct { ChangeType string ConnectionIDs []string HostIDs []string FrameHeader } type AuthenticateFrame struct { Class string FrameHeader } func (a *AuthenticateFrame) String() string { return fmt.Sprintf("[authenticate class=%q]", a.Class) } type AuthSuccessFrame struct { Data []byte FrameHeader } func (a *AuthSuccessFrame) String() string { return fmt.Sprintf("[auth_success data=%q]", a.Data) } type AuthChallengeFrame struct { Data []byte FrameHeader } func (a *AuthChallengeFrame) String() string { return fmt.Sprintf("[auth_challenge data=%q]", a.Data) } type StatusChangeEventFrame struct { Change string Host net.IP FrameHeader Port int } func (t StatusChangeEventFrame) String() string { return fmt.Sprintf("[status_change change=%s host=%v port=%v]", t.Change, t.Host, t.Port) } // essentially the same as statusChange type TopologyChangeEventFrame struct { Change string Host net.IP FrameHeader Port int } func (t TopologyChangeEventFrame) String() string { return fmt.Sprintf("[topology_change change=%s host=%v port=%v]", t.Change, t.Host, t.Port) } type ErrorFrame struct { Message string FrameHeader Code int } func (e ErrorFrame) GetCode() int { return e.Code } func (e ErrorFrame) GetMessage() string { return e.Message } func (e ErrorFrame) Error() string { return e.GetMessage() } func (e ErrorFrame) String() string { return fmt.Sprintf("[error code=%x message=%q]", e.Code, e.Message) } ================================================ FILE: internal/lru/lru.go ================================================ /* Copyright 2015 To gocql authors Copyright 2013 Google Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ // Package lru implements an LRU cache. /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package lru import "container/list" // Cache is a generic LRU cache. It is not safe for concurrent access. // // This cache has been forked from github.com/golang/groupcache/lru and // generalized with a comparable type parameter to avoid the allocations // caused by wrapping keys in any. type Cache[K comparable] struct { // OnEvicted optionally specifies a callback function to be // executed when an entry is purged from the cache. OnEvicted func(key K, value any) ll *list.List cache map[K]*list.Element // MaxEntries is the maximum number of cache entries before // an item is evicted. Zero means no limit. MaxEntries int } type entry[K comparable] struct { value any key K } // New creates a new Cache. // If maxEntries is zero, the cache has no limit and it's assumed // that eviction is done by the caller. func New[K comparable](maxEntries int) *Cache[K] { return &Cache[K]{ MaxEntries: maxEntries, ll: list.New(), cache: make(map[K]*list.Element), } } // Add adds a value to the cache. func (c *Cache[K]) Add(key K, value any) { if c.cache == nil { c.cache = make(map[K]*list.Element) c.ll = list.New() } if ee, ok := c.cache[key]; ok { c.ll.MoveToFront(ee) ee.Value.(*entry[K]).value = value return } ele := c.ll.PushFront(&entry[K]{key: key, value: value}) c.cache[key] = ele if c.MaxEntries != 0 && c.ll.Len() > c.MaxEntries { c.RemoveOldest() } } // Get looks up a key's value from the cache. func (c *Cache[K]) Get(key K) (value any, ok bool) { if c.cache == nil { return } if ele, hit := c.cache[key]; hit { c.ll.MoveToFront(ele) return ele.Value.(*entry[K]).value, true } return } // Remove removes the provided key from the cache. func (c *Cache[K]) Remove(key K) bool { if c.cache == nil { return false } if ele, hit := c.cache[key]; hit { c.removeElement(ele) return true } return false } // RemoveOldest removes the oldest item from the cache. func (c *Cache[K]) RemoveOldest() { if c.cache == nil { return } ele := c.ll.Back() if ele != nil { c.removeElement(ele) } } func (c *Cache[K]) removeElement(e *list.Element) { c.ll.Remove(e) kv := e.Value.(*entry[K]) delete(c.cache, kv.key) if c.OnEvicted != nil { c.OnEvicted(kv.key, kv.value) } } // Len returns the number of items in the cache. func (c *Cache[K]) Len() int { if c.cache == nil { return 0 } return c.ll.Len() } ================================================ FILE: internal/lru/lru_test.go ================================================ //go:build unit // +build unit /* Copyright 2015 To gocql authors Copyright 2013 Google Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package lru import ( "fmt" "testing" ) var getTests = []struct { name string keyToAdd string keyToGet string expectedOk bool }{ {"string_hit", "mystring", "mystring", true}, {"string_miss", "mystring", "nonsense", false}, {"simple_struct_hit", "two", "two", true}, {"simeple_struct_miss", "two", "noway", false}, } func TestGet(t *testing.T) { t.Parallel() for _, tt := range getTests { lru := New[string](0) lru.Add(tt.keyToAdd, 1234) val, ok := lru.Get(tt.keyToGet) if ok != tt.expectedOk { t.Fatalf("%s: cache hit = %v; want %v", tt.name, ok, !ok) } else if ok && val != 1234 { t.Fatalf("%s expected get to return 1234 but got %v", tt.name, val) } } } func TestRemove(t *testing.T) { t.Parallel() lru := New[string](0) lru.Add("mystring", 1234) if val, ok := lru.Get("mystring"); !ok { t.Fatal("TestRemove returned no match") } else if val != 1234 { t.Fatalf("TestRemove failed. Expected %d, got %v", 1234, val) } lru.Remove("mystring") if _, ok := lru.Get("mystring"); ok { t.Fatal("TestRemove returned a removed entry") } } // TestStructKey verifies that struct keys work correctly with the generic cache. func TestStructKey(t *testing.T) { t.Parallel() type compositeKey struct { A string B string } c := New[compositeKey](0) k1 := compositeKey{A: "ab", B: "cd"} k2 := compositeKey{A: "a", B: "bcd"} c.Add(k1, "value1") c.Add(k2, "value2") if val, ok := c.Get(k1); !ok || val != "value1" { t.Fatalf("expected value1 for k1, got %v (ok=%v)", val, ok) } if val, ok := c.Get(k2); !ok || val != "value2" { t.Fatalf("expected value2 for k2, got %v (ok=%v)", val, ok) } // Verify that keys with same concatenation but different field boundaries // are distinct (this was a bug with string concatenation keys). if c.Len() != 2 { t.Fatalf("expected 2 entries, got %d", c.Len()) } } type stmtKey struct { hostID string keyspace string statement string } // BenchmarkStructKeyLookup benchmarks the hot path: looking up a struct key // in a populated cache. func BenchmarkStructKeyLookup(b *testing.B) { c := New[stmtKey](1000) key := stmtKey{ hostID: "550e8400-e29b-41d4-a716-446655440000", keyspace: "my_keyspace", statement: "SELECT id, name, email FROM users WHERE id = ?", } c.Add(key, "prepared-id") b.ReportAllocs() for i := 0; i < b.N; i++ { c.Get(key) } } // BenchmarkStringKeyLookup benchmarks the old approach: looking up a // concatenated string key in a populated cache. func BenchmarkStringKeyLookup(b *testing.B) { c := New[string](1000) key := "550e8400-e29b-41d4-a716-446655440000" + "my_keyspace" + "SELECT id, name, email FROM users WHERE id = ?" c.Add(key, "prepared-id") b.ReportAllocs() for i := 0; i < b.N; i++ { c.Get(key) } } // BenchmarkStructKeyInsert benchmarks inserting entries with struct keys, // including eviction when the cache is full. func BenchmarkStructKeyInsert(b *testing.B) { c := New[stmtKey](1000) b.ReportAllocs() for i := 0; i < b.N; i++ { k := stmtKey{ hostID: "550e8400-e29b-41d4-a716-446655440000", keyspace: "my_keyspace", statement: fmt.Sprintf("SELECT id FROM users WHERE id = %d", i), } c.Add(k, "prepared-id") } } // BenchmarkStringKeyInsert benchmarks inserting entries with concatenated // string keys, including the per-query allocation cost of key construction. func BenchmarkStringKeyInsert(b *testing.B) { c := New[string](1000) b.ReportAllocs() for i := 0; i < b.N; i++ { k := fmt.Sprintf("%s%s%s", "550e8400-e29b-41d4-a716-446655440000", "my_keyspace", fmt.Sprintf("SELECT id FROM users WHERE id = %d", i)) c.Add(k, "prepared-id") } } ================================================ FILE: internal/murmur/murmur.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package murmur const ( c1 int64 = -8663945395140668459 // 0x87c37b91114253d5 c2 int64 = 5545529020109919103 // 0x4cf5ad432745937f fmix1 int64 = -49064778989728563 // 0xff51afd7ed558ccd fmix2 int64 = -4265267296055464877 // 0xc4ceb9fe1a85ec53 ) func fmix(n int64) int64 { // cast to unsigned for logical right bitshift (to match C* MM3 implementation) n ^= int64(uint64(n) >> 33) n *= fmix1 n ^= int64(uint64(n) >> 33) n *= fmix2 n ^= int64(uint64(n) >> 33) return n } func block(p byte) int64 { return int64(int8(p)) } func rotl(x int64, r uint8) int64 { // cast to unsigned for logical right bitshift (to match C* MM3 implementation) return (x << r) | (int64)((uint64(x) >> (64 - r))) } func Murmur3H1(data []byte) int64 { length := len(data) var h1, h2, k1, k2 int64 // body nBlocks := length / 16 for i := 0; i < nBlocks; i++ { k1, k2 = getBlock(data, i) k1 *= c1 k1 = rotl(k1, 31) k1 *= c2 h1 ^= k1 h1 = rotl(h1, 27) h1 += h2 h1 = h1*5 + 0x52dce729 k2 *= c2 k2 = rotl(k2, 33) k2 *= c1 h2 ^= k2 h2 = rotl(h2, 31) h2 += h1 h2 = h2*5 + 0x38495ab5 } // tail tail := data[nBlocks*16:] k1 = 0 k2 = 0 switch length & 15 { case 15: k2 ^= block(tail[14]) << 48 fallthrough case 14: k2 ^= block(tail[13]) << 40 fallthrough case 13: k2 ^= block(tail[12]) << 32 fallthrough case 12: k2 ^= block(tail[11]) << 24 fallthrough case 11: k2 ^= block(tail[10]) << 16 fallthrough case 10: k2 ^= block(tail[9]) << 8 fallthrough case 9: k2 ^= block(tail[8]) k2 *= c2 k2 = rotl(k2, 33) k2 *= c1 h2 ^= k2 fallthrough case 8: k1 ^= block(tail[7]) << 56 fallthrough case 7: k1 ^= block(tail[6]) << 48 fallthrough case 6: k1 ^= block(tail[5]) << 40 fallthrough case 5: k1 ^= block(tail[4]) << 32 fallthrough case 4: k1 ^= block(tail[3]) << 24 fallthrough case 3: k1 ^= block(tail[2]) << 16 fallthrough case 2: k1 ^= block(tail[1]) << 8 fallthrough case 1: k1 ^= block(tail[0]) k1 *= c1 k1 = rotl(k1, 31) k1 *= c2 h1 ^= k1 } h1 ^= int64(length) h2 ^= int64(length) h1 += h2 h2 += h1 h1 = fmix(h1) h2 = fmix(h2) h1 += h2 // the following is extraneous since h2 is discarded // h2 += h1 return h1 } ================================================ FILE: internal/murmur/murmur_appengine.go ================================================ //go:build appengine || s390x // +build appengine s390x /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package murmur import "encoding/binary" func getBlock(data []byte, n int) (int64, int64) { k1 := int64(binary.LittleEndian.Uint64(data[n*16:])) k2 := int64(binary.LittleEndian.Uint64(data[(n*16)+8:])) return k1, k2 } ================================================ FILE: internal/murmur/murmur_test.go ================================================ //go:build unit // +build unit /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package murmur import ( "encoding/hex" "fmt" "strconv" "testing" ) func TestRotl(t *testing.T) { t.Parallel() tests := []struct { in, rotate, exp int64 }{ {123456789, 33, 1060485742448345088}, {-123456789, 33, -1060485733858410497}, {-12345678987654, 33, 1756681988166642059}, {7210216203459776512, 31, -4287945813905642825}, {2453826951392495049, 27, -2013042863942636044}, {270400184080946339, 33, -3553153987756601583}, {2060965185473694757, 31, 6290866853133484661}, {3075794793055692309, 33, -3158909918919076318}, {-6486402271863858009, 31, 405973038345868736}, } for _, test := range tests { t.Run(fmt.Sprintf("%d >> %d", test.in, test.rotate), func(t *testing.T) { if v := rotl(test.in, uint8(test.rotate)); v != test.exp { t.Fatalf("expected %d got %d", test.exp, v) } }) } } func TestFmix(t *testing.T) { t.Parallel() tests := []struct { in, exp int64 }{ {123456789, -8107560010088384378}, {-123456789, -5252787026298255965}, {-12345678987654, -1122383578793231303}, {-1241537367799374202, 3388197556095096266}, {-7566534940689533355, 4729783097411765989}, } for _, test := range tests { t.Run(strconv.Itoa(int(test.in)), func(t *testing.T) { if v := fmix(test.in); v != test.exp { t.Fatalf("expected %d got %d", test.exp, v) } }) } } func TestMurmur3H1_CassandraSign(t *testing.T) { t.Parallel() key, err := hex.DecodeString("00104327529fb645dd00b883ec39ae448bb800000400066a6b00") if err != nil { t.Fatal(err) } h := Murmur3H1(key) const exp int64 = -9223371632693506265 if h != exp { t.Fatalf("expected %d got %d", exp, h) } } // Test the implementation of murmur3 func TestMurmur3H1(t *testing.T) { t.Parallel() // these examples are based on adding a index number to a sample string in // a loop. The expected values were generated by the java datastax murmur3 // implementation. The number of examples here of increasing lengths ensure // test coverage of all tail-length branches in the murmur3 algorithm seriesExpected := [...]uint64{ 0x0000000000000000, // "" 0x2ac9debed546a380, // "0" 0x649e4eaa7fc1708e, // "01" 0xce68f60d7c353bdb, // "012" 0x0f95757ce7f38254, // "0123" 0x0f04e459497f3fc1, // "01234" 0x88c0a92586be0a27, // "012345" 0x13eb9fb82606f7a6, // "0123456" 0x8236039b7387354d, // "01234567" 0x4c1e87519fe738ba, // "012345678" 0x3f9652ac3effeb24, // "0123456789" 0x3f33760ded9006c6, // "01234567890" 0xaed70a6631854cb1, // "012345678901" 0x8a299a8f8e0e2da7, // "0123456789012" 0x624b675c779249a6, // "01234567890123" 0xa4b203bb1d90b9a3, // "012345678901234" 0xa3293ad698ecb99a, // "0123456789012345" 0xbc740023dbd50048, // "01234567890123456" 0x3fe5ab9837d25cdd, // "012345678901234567" 0x2d0338c1ca87d132, // "0123456789012345678" } sample := "" for i, expected := range seriesExpected { assertMurmur3H1(t, []byte(sample), expected) sample = sample + strconv.Itoa(i%10) } // Here are some test examples from other driver implementations assertMurmur3H1(t, []byte("hello"), 0xcbd8a7b341bd9b02) assertMurmur3H1(t, []byte("hello, world"), 0x342fac623a5ebc8e) assertMurmur3H1(t, []byte("19 Jan 2038 at 3:14:07 AM"), 0xb89e5988b737affc) assertMurmur3H1(t, []byte("The quick brown fox jumps over the lazy dog."), 0xcd99481f9ee902c9) } // helper function for testing the murmur3 implementation func assertMurmur3H1(t *testing.T, data []byte, expected uint64) { actual := Murmur3H1(data) if actual != int64(expected) { t.Errorf("Expected h1 = %x for data = %x, but was %x", int64(expected), data, actual) } } // Benchmark of the performance of the murmur3 implementation func BenchmarkMurmur3H1(b *testing.B) { data := make([]byte, 1024) for i := 0; i < 1024; i++ { data[i] = byte(i) } b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { h1 := Murmur3H1(data) if h1 != int64(7627370222079200297) { b.Fatalf("expected %d got %d", int64(7627370222079200297), h1) } } }) } ================================================ FILE: internal/murmur/murmur_unsafe.go ================================================ //go:build !appengine && !s390x // +build !appengine,!s390x /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package murmur import ( "unsafe" ) func getBlock(data []byte, n int) (int64, int64) { block := (*[2]int64)(unsafe.Pointer(&data[n*16])) k1 := block[0] k2 := block[1] return k1, k2 } ================================================ FILE: internal/streams/streams.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package streams import ( "math" "strconv" "sync/atomic" ) const bucketBits = 64 // IDGenerator tracks and allocates streams which are in use. type IDGenerator struct { // streams is a bitset where each bit represents a stream, a 1 implies in use streams []uint64 NumStreams int inuseStreams int32 numBuckets uint32 offset uint32 } func New() *IDGenerator { return NewLimited(32768) } func NewLimited(maxStreams int) *IDGenerator { // Round up maxStreams to a nearest // multiple of 64 maxStreams = ((maxStreams + 63) / 64) * 64 buckets := maxStreams / 64 // reserve stream 0 streams := make([]uint64, buckets) streams[0] = 1 << 63 return &IDGenerator{ NumStreams: maxStreams, streams: streams, numBuckets: uint32(buckets), offset: uint32(buckets) - 1, } } func streamFromBucket(bucket, streamInBucket int) int { return (bucket * bucketBits) + streamInBucket } func (s *IDGenerator) GetStream() (int, bool) { // Reduce collisions by offsetting the starting point offset := atomic.AddUint32(&s.offset, 1) for i := uint32(0); i < s.numBuckets; i++ { pos := int((i + offset) % s.numBuckets) bucket := atomic.LoadUint64(&s.streams[pos]) if bucket == math.MaxUint64 { // all streams in use continue } for j := 0; j < bucketBits; j++ { mask := uint64(1 << streamOffset(j)) for bucket&mask == 0 { if atomic.CompareAndSwapUint64(&s.streams[pos], bucket, bucket|mask) { atomic.AddInt32(&s.inuseStreams, 1) return streamFromBucket(int(pos), j), true } bucket = atomic.LoadUint64(&s.streams[pos]) } } } return 0, false } func bitfmt(b uint64) string { return strconv.FormatUint(b, 16) } // returns the bucket offset of a given stream func bucketOffset(i int) int { return i / bucketBits } func streamOffset(stream int) uint64 { return bucketBits - uint64(stream%bucketBits) - 1 } func isSet(bits uint64, stream int) bool { return bits>>streamOffset(stream)&1 == 1 } func (s *IDGenerator) isSet(stream int) bool { bits := atomic.LoadUint64(&s.streams[bucketOffset(stream)]) return isSet(bits, stream) } func (s *IDGenerator) String() string { size := s.numBuckets * (bucketBits + 1) buf := make([]byte, 0, size) for i := 0; i < int(s.numBuckets); i++ { bits := atomic.LoadUint64(&s.streams[i]) buf = append(buf, bitfmt(bits)...) buf = append(buf, ' ') } return string(buf[: size-1 : size-1]) } func (s *IDGenerator) Clear(stream int) (inuse bool) { offset := bucketOffset(stream) bucket := atomic.LoadUint64(&s.streams[offset]) mask := uint64(1) << streamOffset(stream) if bucket&mask != mask { // already cleared return false } for !atomic.CompareAndSwapUint64(&s.streams[offset], bucket, bucket & ^mask) { bucket = atomic.LoadUint64(&s.streams[offset]) if bucket&mask != mask { // already cleared return false } } // TODO: make this account for 0 stream being reserved if atomic.AddInt32(&s.inuseStreams, -1) < 0 { // TODO(zariel): remove this panic("negative streams inuse") } return true } func (s *IDGenerator) Available() int { return s.NumStreams - int(atomic.LoadInt32(&s.inuseStreams)) - 1 } func (s *IDGenerator) InUse() int { return int(atomic.LoadInt32(&s.inuseStreams)) } ================================================ FILE: internal/streams/streams_test.go ================================================ //go:build unit // +build unit /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package streams import ( "math" "strconv" "sync/atomic" "testing" ) func TestUsesAllStreams(t *testing.T) { t.Parallel() streams := New() got := make(map[int]struct{}) for i := 1; i < streams.NumStreams; i++ { stream, ok := streams.GetStream() if !ok { t.Fatalf("unable to get stream %d", i) } if _, ok = got[stream]; ok { t.Fatalf("got an already allocated stream: %d", stream) } got[stream] = struct{}{} if !streams.isSet(stream) { bucket := atomic.LoadUint64(&streams.streams[bucketOffset(stream)]) t.Logf("bucket=%d: %s\n", bucket, strconv.FormatUint(bucket, 2)) t.Fatalf("stream not set: %d", stream) } } for i := 1; i < streams.NumStreams; i++ { if _, ok := got[i]; !ok { t.Errorf("did not use stream %d", i) } } if _, ok := got[0]; ok { t.Fatal("expected to not use stream 0") } for i, bucket := range streams.streams { if bucket != math.MaxUint64 { t.Errorf("did not use all streams in offset=%d bucket=%s", i, bitfmt(bucket)) } } } func TestFullStreams(t *testing.T) { t.Parallel() streams := New() for i := range streams.streams { streams.streams[i] = math.MaxUint64 } stream, ok := streams.GetStream() if ok { t.Fatalf("should not get stream when all in use: stream=%d", stream) } } func TestClearStreams(t *testing.T) { t.Parallel() streams := New() for i := range streams.streams { streams.streams[i] = math.MaxUint64 } streams.inuseStreams = int32(streams.NumStreams) for i := 0; i < streams.NumStreams; i++ { streams.Clear(i) } for i, bucket := range streams.streams { if bucket != 0 { t.Errorf("did not clear streams in offset=%d bucket=%s", i, bitfmt(bucket)) } } } func TestDoubleClear(t *testing.T) { t.Parallel() streams := New() stream, ok := streams.GetStream() if !ok { t.Fatal("did not get stream") } if !streams.Clear(stream) { t.Fatalf("stream not indicated as in use: %d", stream) } if streams.Clear(stream) { t.Fatalf("stream not as in use after clear: %d", stream) } } func BenchmarkConcurrentUse(b *testing.B) { streams := New() b.RunParallel(func(pb *testing.PB) { for pb.Next() { stream, ok := streams.GetStream() if !ok { b.Error("unable to get stream") return } if !streams.Clear(stream) { b.Errorf("stream was already cleared: %d", stream) return } } }) } func TestStreamOffset(t *testing.T) { t.Parallel() tests := [...]struct { n int off uint64 }{ {0, 63}, {1, 62}, {2, 61}, {3, 60}, {63, 0}, {64, 63}, {128, 63}, } for _, test := range tests { if off := streamOffset(test.n); off != test.off { t.Errorf("n=%d expected %d got %d", test.n, off, test.off) } } } func TestIsSet(t *testing.T) { t.Parallel() tests := [...]struct { stream int bucket uint64 set bool }{ {0, 0, false}, {0, 1 << 63, true}, {1, 0, false}, {1, 1 << 62, true}, {63, 1, true}, {64, 1 << 63, true}, {0, 0x8000000000000000, true}, } for i, test := range tests { if set := isSet(test.bucket, test.stream); set != test.set { t.Errorf("[%d] stream=%d expected %v got %v", i, test.stream, test.set, set) } } for i := 0; i < bucketBits; i++ { if !isSet(math.MaxUint64, i) { var shift uint64 = math.MaxUint64 >> streamOffset(i) t.Errorf("expected isSet for all i=%d got=%d", i, shift) } } } func TestBucketOfset(t *testing.T) { t.Parallel() tests := [...]struct { n int bucket int }{ {0, 0}, {1, 0}, {63, 0}, {64, 1}, } for _, test := range tests { if bucket := bucketOffset(test.n); bucket != test.bucket { t.Errorf("n=%d expected %v got %v", test.n, test.bucket, bucket) } } } func TestStreamFromBucket(t *testing.T) { t.Parallel() tests := [...]struct { bucket int pos int stream int }{ {0, 0, 0}, {0, 1, 1}, {0, 2, 2}, {0, 63, 63}, {1, 0, 64}, {1, 1, 65}, } for _, test := range tests { if stream := streamFromBucket(test.bucket, test.pos); stream != test.stream { t.Errorf("bucket=%d pos=%d expected %v got %v", test.bucket, test.pos, test.stream, stream) } } } ================================================ FILE: internal/tests/common.go ================================================ package tests import ( "fmt" "reflect" "strconv" "testing" "github.com/google/uuid" ) func AssertTrue(t *testing.T, description string, value bool) { t.Helper() if !value { t.Fatalf("expected %s to be true", description) } } func AssertEqual(t *testing.T, description string, expected, actual any) { t.Helper() if expected != actual { t.Fatalf("expected %s to be (%+v) but was (%+v) instead", description, expected, actual) } } func AssertDeepEqual(t *testing.T, description string, expected, actual any) { t.Helper() if !reflect.DeepEqual(expected, actual) { t.Fatalf("expected %s to be (%+v) but was (%+v) instead", description, expected, actual) } } func AssertNil(t *testing.T, description string, actual any) { t.Helper() if actual != nil { t.Fatalf("expected %s to be (nil) but was (%+v) instead", description, actual) } } func RandomUUID() string { val, err := uuid.NewRandom() if err != nil { panic(fmt.Sprintf("failed to generate UUID: %s", err.Error())) } return val.String() } // GenerateHostNames generates a slice of host names with the format "host0", "host1", ..., "hostN-1", // where N is the specified hostCount. // // Parameters: // // hostCount - the number of host names to generate. // // Returns: // // A slice of strings containing host names. func GenerateHostNames(hostCount int) []string { hosts := make([]string, hostCount) for i := 0; i < hostCount; i++ { hosts[i] = "host" + strconv.Itoa(i) } return hosts } ================================================ FILE: internal/tests/err_equal.go ================================================ package tests func ErrEqual(err1, err2 error) bool { if err1 != nil && err2 != nil { return err1.Error() == err2.Error() } return err1 == nil && err2 == nil } ================================================ FILE: internal/tests/mock/mock_framer.go ================================================ package mock type MockFramer struct { Data [][]byte pos int } func (m *MockFramer) ReadBytesInternal() ([]byte, error) { if m.pos < len(m.Data) { m.pos = m.pos + 1 return m.Data[m.pos-1], nil } return []byte{}, nil } func (*MockFramer) GetCustomPayload() map[string][]byte { return map[string][]byte{} } func (*MockFramer) GetHeaderWarnings() []string { return []string{} } func (*MockFramer) Release() {} ================================================ FILE: internal/tests/rand.go ================================================ package tests import ( "math/rand" "sync" "time" ) // RandInterface defines the thread-safe random number generator interface. // It abstracts all methods provided by ThreadSafeRand. type RandInterface interface { Uint64() uint64 Uint32() uint32 Int() int Intn(n int) int Int63() int64 Int63n(n int64) int64 Int31() int32 Int31n(n int32) int32 Float64() float64 Float32() float32 ExpFloat64() float64 NormFloat64() float64 Shuffle(n int, swap func(i, j int)) Read(p []byte) (n int, err error) } // RandomTokens generates a slice of n random int64 tokens using a thread-safe random number generator. // // Parameters: // // n - the number of random tokens to generate. // // Returns: // // A slice of n randomly generated int64 tokens. func RandomTokens(rnd RandInterface, n int) []int64 { var tokens []int64 for i := 0; i < n; i++ { tokens = append(tokens, rnd.Int63()) } return tokens } // ShuffledIndexes returns a slice containing integers from 0 to n-1 in random order. // // It uses a thread-safe random number generator to perform an in-place shuffle. // // Parameters: // // n - the number of elements to include in the shuffled list. // // Returns: // // A randomly shuffled slice of integers from 0 to n-1. func ShuffledIndexes(rnd RandInterface, n int) []int { indexes := make([]int, n) for i := range indexes { indexes[i] = i } rnd.Shuffle(n, func(i, j int) { indexes[i], indexes[j] = indexes[j], indexes[i] }) return indexes } // ThreadSafeRand provides a concurrency-safe wrapper around math/rand.Rand. // It allows safe usage of random number generation methods from multiple goroutines. // All access to the underlying rand.Rand is synchronized via a mutex. type ThreadSafeRand struct { r *rand.Rand mux sync.Mutex } // NewThreadSafeRand creates and returns a new instance of ThreadSafeRand, // initialized with the given seed. The resulting generator is safe for concurrent use. func NewThreadSafeRand(seed int64) *ThreadSafeRand { return &ThreadSafeRand{ r: rand.New(rand.NewSource(seed)), } } func (r *ThreadSafeRand) Uint64() uint64 { r.mux.Lock() defer r.mux.Unlock() return r.r.Uint64() } func (r *ThreadSafeRand) Uint32() uint32 { r.mux.Lock() defer r.mux.Unlock() return r.r.Uint32() } func (r *ThreadSafeRand) Int() int { r.mux.Lock() defer r.mux.Unlock() return r.r.Int() } func (r *ThreadSafeRand) Intn(n int) int { r.mux.Lock() defer r.mux.Unlock() return r.r.Intn(n) } func (r *ThreadSafeRand) Int63() int64 { r.mux.Lock() defer r.mux.Unlock() return r.r.Int63() } func (r *ThreadSafeRand) Int63n(n int64) int64 { r.mux.Lock() defer r.mux.Unlock() return r.r.Int63n(n) } func (r *ThreadSafeRand) Int31() int32 { r.mux.Lock() defer r.mux.Unlock() return r.r.Int31() } func (r *ThreadSafeRand) Int31n(n int32) int32 { r.mux.Lock() defer r.mux.Unlock() return r.r.Int31n(n) } func (r *ThreadSafeRand) Float64() float64 { r.mux.Lock() defer r.mux.Unlock() return r.r.Float64() } func (r *ThreadSafeRand) Float32() float32 { r.mux.Lock() defer r.mux.Unlock() return r.r.Float32() } func (r *ThreadSafeRand) ExpFloat64() float64 { r.mux.Lock() defer r.mux.Unlock() return r.r.ExpFloat64() } func (r *ThreadSafeRand) NormFloat64() float64 { r.mux.Lock() defer r.mux.Unlock() return r.r.NormFloat64() } func (r *ThreadSafeRand) Shuffle(n int, swap func(i, j int)) { r.mux.Lock() defer r.mux.Unlock() r.r.Shuffle(n, swap) } func (r *ThreadSafeRand) Read(p []byte) (n int, err error) { r.mux.Lock() defer r.mux.Unlock() return r.r.Read(p) } var seededRand *rand.Rand = rand.New(rand.NewSource(time.Now().UnixNano())) const randCharset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" func RandomText(size int) string { result := make([]byte, size) for i := range result { result[i] = randCharset[rand.Intn(len(randCharset))] } return string(result) } ================================================ FILE: internal/tests/serialization/mod/all.go ================================================ package mod var All = []Mod{CustomType, Reference, CustomTypeRef} // Mod - value modifiers. type Mod func(vals ...any) []any type Values []any func (v Values) AddVariants(mods ...Mod) Values { out := append(make([]any, 0), v...) for _, mod := range mods { out = append(out, mod(v...)...) } return out } ================================================ FILE: internal/tests/serialization/mod/custom.go ================================================ package mod type ( Bool bool Int8 int8 Int16 int16 Int32 int32 Int64 int64 Int int Uint8 uint8 Uint16 uint16 Uint32 uint32 Uint64 uint64 Uint uint Float32 float32 Float64 float64 String string Bytes []byte Bytes3 [3]byte Bytes4 [4]byte Bytes5 [5]byte Bytes15 [15]byte Bytes16 [16]byte Bytes17 [17]byte SliceInt16 []int16 SliceInt16R []*int16 SliceInt16C []Int16 SliceInt16CR []*Int16 SliceInt32 []int32 SliceInt32R []*int32 SliceInt32C []Int32 SliceInt32CR []*Int32 SliceAny []any Arr1Int16 [1]int16 Arr1Int16R [1]*int16 Arr1Int16C [1]Int16 Arr1Int16CR [1]*Int16 Arr1Int32 [1]int32 Arr1Int32R [1]*int32 Arr1Int32C [1]Int32 Arr1Int32CR [1]*Int32 ArrAny [1]any MapInt16 map[int16]int16 MapInt16R map[int16]*int16 MapInt16C map[Int16]Int16 MapInt16CR map[Int16]*Int16 MapInt32 map[int32]int32 MapInt32R map[int32]*int32 MapInt32C map[Int32]Int32 MapInt32CR map[Int32]*Int32 MapUDT map[string]any ) var CustomType Mod = func(vals ...any) []any { out := make([]any, 0) for i := range vals { if vals[i] == nil { continue } ct := customType(vals[i]) if ct != nil { out = append(out, ct) } } return out } func customType(i any) any { switch v := i.(type) { case bool: return Bool(v) case int8: return Int8(v) case int16: return Int16(v) case int32: return Int32(v) case int64: return Int64(v) case int: return Int(v) case uint: return Uint(v) case uint8: return Uint8(v) case uint16: return Uint16(v) case uint32: return Uint32(v) case uint64: return Uint64(v) case float32: return Float32(v) case float64: return Float64(v) case string: return String(v) case []byte: return Bytes(v) case [3]byte: return Bytes3(v) case [4]byte: return Bytes4(v) case [5]byte: return Bytes5(v) case [15]byte: return Bytes15(v) case [16]byte: return Bytes16(v) case [17]byte: return Bytes17(v) case []int16: return SliceInt16(v) case []*int16: return SliceInt16R(v) case []Int16: return SliceInt16C(v) case []*Int16: return SliceInt16CR(v) case []int32: return SliceInt32(v) case []*int32: return SliceInt32R(v) case []Int32: return SliceInt32C(v) case []*Int32: return SliceInt32CR(v) case [1]int16: return Arr1Int16(v) case [1]*int16: return Arr1Int16R(v) case [1]Int16: return Arr1Int16C(v) case [1]*Int16: return Arr1Int16CR(v) case [1]int32: return Arr1Int32(v) case [1]*int32: return Arr1Int32R(v) case [1]Int32: return Arr1Int32C(v) case [1]*Int32: return Arr1Int32CR(v) case map[int16]int16: return MapInt16(v) case map[int16]*int16: return MapInt16R(v) case map[Int16]Int16: return MapInt16C(v) case map[Int16]*Int16: return MapInt16CR(v) case map[int32]int32: return MapInt32(v) case map[int32]*int32: return MapInt32R(v) case map[Int32]Int32: return MapInt32C(v) case map[Int32]*Int32: return MapInt32CR(v) case map[string]any: return MapUDT(v) case []any: return SliceAny(v) case [1]any: return ArrAny(v) default: return intoCustomR(i) } } func intoCustomR(i any) any { switch v := i.(type) { case *bool: return (*Bool)(v) case *int8: return (*Int8)(v) case *int16: return (*Int16)(v) case *int32: return (*Int32)(v) case *int64: return (*Int64)(v) case *int: return (*Int)(v) case *uint: return (*Uint)(v) case *uint8: return (*Uint8)(v) case *uint16: return (*Uint16)(v) case *uint32: return (*Uint32)(v) case *uint64: return (*Uint64)(v) case *float32: return (*Float32)(v) case *float64: return (*Float64)(v) case *string: return (*String)(v) case *[]byte: return (*Bytes)(v) case *[4]byte: return (*Bytes4)(v) case *[16]byte: return (*Bytes16)(v) case *[]int16: return (*SliceInt16)(v) case *[]*int16: return (*SliceInt16R)(v) case *[]Int16: return (*SliceInt16C)(v) case *[]*Int16: return (*SliceInt16CR)(v) case *[]int32: return (*SliceInt32)(v) case *[]*int32: return (*SliceInt32R)(v) case *[]Int32: return (*SliceInt32C)(v) case *[]*Int32: return (*SliceInt32CR)(v) case *[1]int16: return (*Arr1Int16)(v) case *[1]*int16: return (*Arr1Int16R)(v) case *[1]Int16: return (*Arr1Int16C)(v) case *[1]*Int16: return (*Arr1Int16CR)(v) case *[1]int32: return (*Arr1Int32)(v) case *[1]*int32: return (*Arr1Int32R)(v) case *[1]Int32: return (*Arr1Int32C)(v) case *[1]*Int32: return (*Arr1Int32CR)(v) case *map[int16]int16: return (*MapInt16)(v) case *map[int16]*int16: return (*MapInt16R)(v) case *map[Int16]Int16: return (*MapInt16C)(v) case *map[Int16]*Int16: return (*MapInt16CR)(v) case *map[int32]int32: return (*MapInt32)(v) case *map[int32]*int32: return (*MapInt32R)(v) case *map[Int32]Int32: return (*MapInt32C)(v) case *map[Int32]*Int32: return (*MapInt32CR)(v) case *map[string]any: return (*MapUDT)(v) case *[]any: return (*SliceAny)(v) case *[1]any: return (*ArrAny)(v) default: return nil } } ================================================ FILE: internal/tests/serialization/mod/custom_refs.go ================================================ package mod var CustomTypeRef Mod = func(vals ...any) []any { return Reference(CustomType(vals...)...) } ================================================ FILE: internal/tests/serialization/mod/refs.go ================================================ package mod import "reflect" var Reference Mod = func(vals ...any) []any { out := make([]any, 0) for i := range vals { if vals[i] != nil { out = append(out, reference(vals[i])) } } return out } func reference(val any) any { inV := reflect.ValueOf(val) out := reflect.New(reflect.TypeOf(val)) out.Elem().Set(inV) return out.Interface() } ================================================ FILE: internal/tests/serialization/pointers.go ================================================ package serialization import ( "errors" "reflect" ) // errFirstPtrChanged this error indicates that a double or single reference was passed to the Unmarshal function // (example (**int)(**0) or (*int)(*0)) and Unmarshal overwritten first reference. var errFirstPtrChanged = errors.New("unmarshal function rewrote first pointer") // errSecondPtrNotChanged this error indicates that a double reference was passed to the Unmarshal function // (example (**int)(**0)) and the function did not overwrite the second reference. // Of course, it's not friendly to the garbage collector, overwriting references to values all the time, // but this is the current implementation `gocql` and changing it can lead to unexpected results in some cases. var errSecondPtrNotChanged = errors.New("unmarshal function did not rewrite second pointer") func getPointers(i any) *pointer { rv := reflect.ValueOf(i) if rv.Kind() != reflect.Ptr { return nil } out := pointer{ Fist: rv.Pointer(), } rt := rv.Type() if rt.Elem().Kind() == reflect.Ptr && !rv.Elem().IsNil() { out.Second = rv.Elem().Pointer() } return &out } type pointer struct { Fist uintptr Second uintptr } func (p *pointer) NotNil() bool { return p != nil } // Valid validates if pointers has been manipulated by unmarshal functions in an expected manner: // Fist pointer should not be overwritten, // Second pointer, if applicable, should be overwritten. func (p *pointer) Valid(v any) error { p2 := getPointers(v) if p.Fist != p2.Fist { return errFirstPtrChanged } if p.Second != 0 && p2.Second != 0 && p2.Second == p.Second { return errSecondPtrNotChanged } return nil } ================================================ FILE: internal/tests/serialization/pointers_test.go ================================================ //go:build unit // +build unit package serialization import "testing" func Test1Pointers(t *testing.T) { t.Parallel() val1 := new(int16) *val1 = int16(0) testPtr := getPointers(val1) // the first pointer has not been changed - it must be not error. if err := testPtr.Valid(val1); err != nil { t.Error("valid function not should return error") } val2 := new(int16) // the first pointer has been changed - it must be an error. if err := testPtr.Valid(val2); err == nil { t.Error("valid function should return error") } } func Test2Pointers(t *testing.T) { t.Parallel() val1 := new(*int16) *val1 = new(int16) testPtr := getPointers(val1) // the first pointer has not been changed - it must be not error, // but the second pointer has not been changed too - it must be an error. if err := testPtr.Valid(val1); err == nil { t.Error("valid function should return error") } *val1 = new(int16) // the first pointer has not been changed - it must be not error, // the second pointer has been changed - it must be not error. if err := testPtr.Valid(val1); err != nil { t.Error("valid function not should return error") } } ================================================ FILE: internal/tests/serialization/set_negative_marshal.go ================================================ package serialization import ( "errors" "reflect" "runtime/debug" "testing" ) // NegativeMarshalSet is a tool for marshal funcs testing for cases when the function should an error. type NegativeMarshalSet struct { Values []any BrokenTypes []reflect.Type } func (s NegativeMarshalSet) Run(name string, t *testing.T, marshal func(any) ([]byte, error)) { if name == "" { t.Fatal("name should be provided") } if marshal == nil { t.Fatal("marshal function should be provided") } t.Run(name, func(t *testing.T) { for m := range s.Values { val := s.Values[m] t.Run(stringValue(val), func(t *testing.T) { _, err := func() (d []byte, err error) { defer func() { if r := recover(); r != nil { err = panicErr{err: r.(error), stack: debug.Stack()} } }() return marshal(val) }() testFailed := false wasPanic := errors.As(err, &panicErr{}) if err == nil || wasPanic { testFailed = true } if isTypeOf(val, s.BrokenTypes) { if testFailed { t.Skipf("skipped bacause there is unsolved problem") } t.Fatalf("expected to panic or no error for (%T), but got an error", val) } if testFailed { if wasPanic { t.Fatalf("was panic %s", err) } t.Errorf("expected an error for (%T), but got no error", val) } }) } }) } ================================================ FILE: internal/tests/serialization/set_negative_unmarshal.go ================================================ package serialization import ( "bytes" "errors" "fmt" "reflect" "runtime/debug" "testing" ) // NegativeUnmarshalSet is a tool for unmarshal funcs testing for cases when the function should an error. type NegativeUnmarshalSet struct { Data []byte Values []any BrokenTypes []reflect.Type } func (s NegativeUnmarshalSet) Run(name string, t *testing.T, unmarshal func([]byte, any) error) { if name == "" { t.Fatal("name should be provided") } if unmarshal == nil { t.Fatal("unmarshal function should be provided") } t.Run(name, func(t *testing.T) { for m := range s.Values { val := s.Values[m] if rt := reflect.TypeOf(val); rt.Kind() != reflect.Ptr { unmarshalIn := newRef(val) s.run(fmt.Sprintf("%T", val), t, unmarshal, val, unmarshalIn) } else { // Test unmarshal to (*type)(nil) unmarshalIn := newRef(val) s.run(fmt.Sprintf("%T**nil", val), t, unmarshal, val, unmarshalIn) // Test unmarshal to &type{} unmarshalInZero := newRefToZero(val) s.run(fmt.Sprintf("%T**zero", val), t, unmarshal, val, unmarshalInZero) } } }) } func (s NegativeUnmarshalSet) run(name string, t *testing.T, f func([]byte, any) error, val, unmarshalIn any) { t.Run(name, func(t *testing.T) { err := func() (err error) { defer func() { if r := recover(); r != nil { err = panicErr{err: r.(error), stack: debug.Stack()} } }() return f(bytes.Clone(s.Data), unmarshalIn) }() testFailed := false wasPanic := errors.As(err, &panicErr{}) if err == nil || wasPanic { testFailed = true } if isTypeOf(val, s.BrokenTypes) { if testFailed { t.Skipf("skipped bacause there is unsolved problem") } t.Fatalf("expected to panic or no error for (%T), but got an error", unmarshalIn) } if testFailed { if wasPanic { t.Fatalf("was panic %s", err) } t.Errorf("expected an error for (%T), but got no error", unmarshalIn) } }) } ================================================ FILE: internal/tests/serialization/set_positive.go ================================================ package serialization import ( "bytes" "errors" "fmt" "reflect" "runtime/debug" "testing" ) // PositiveSet is a tool for marshal and unmarshall funcs testing for cases when the function should no error, // on marshal - marshaled data from PositiveSet.Values should be equal with PositiveSet.Data, // on unmarshall - unmarshalled value from PositiveSet.Data should be equal with PositiveSet.Values. type PositiveSet struct { Data []byte Values []any BrokenMarshalTypes []reflect.Type BrokenUnmarshalTypes []reflect.Type } func (s PositiveSet) Run(name string, t *testing.T, marshal func(any) ([]byte, error), unmarshal func([]byte, any) error) { if name == "" { t.Fatal("name should be provided") } t.Run(name, func(t *testing.T) { for i := range s.Values { val := s.Values[i] t.Run(fmt.Sprintf("%T", val), func(t *testing.T) { if marshal != nil { s.runMarshalTest(t, marshal, val) } if unmarshal != nil { if rt := reflect.TypeOf(val); rt.Kind() != reflect.Ptr { unmarshalIn := newRef(val) s.runUnmarshalTest("unmarshal", t, unmarshal, val, unmarshalIn) } else { // Test unmarshal to (*type)(nil) unmarshalIn := newRef(val) s.runUnmarshalTest("unmarshal**nil", t, unmarshal, val, unmarshalIn) // Test unmarshal to &type{} unmarshalInZero := newRefToZero(val) s.runUnmarshalTest("unmarshal**zero", t, unmarshal, val, unmarshalInZero) } } }) } }) } func (s PositiveSet) runMarshalTest(t *testing.T, f func(any) ([]byte, error), val any) { t.Run("marshal", func(t *testing.T) { result, err := func() (d []byte, err error) { defer func() { if r := recover(); r != nil { err = panicErr{err: r.(error), stack: debug.Stack()} } }() return f(val) }() expected := bytes.Clone(s.Data) if err != nil { if !errors.As(err, &panicErr{}) { err = errors.Join(marshalErr, err) } } else if !equalData(expected, result) { err = unequalError{Expected: stringData(s.Data), Got: stringData(result)} } if isTypeOf(val, s.BrokenMarshalTypes) { if err == nil { t.Fatalf("expected to fail for (%T), but did not fail", val) } t.Skipf("skipped bacause there is unsolved problem") } if err != nil { t.Error(err) } }) } func (s PositiveSet) runUnmarshalTest(name string, t *testing.T, f func([]byte, any) error, expected, result any) { t.Run(name, func(t *testing.T) { expectedPtr := getPointers(result) err := func() (err error) { defer func() { if r := recover(); r != nil { err = panicErr{err: fmt.Errorf("%s", r), stack: debug.Stack()} } }() return f(bytes.Clone(s.Data), result) }() if err != nil { if !errors.As(err, &panicErr{}) { err = errors.Join(unmarshalErr, err) } } else if !equalVals(expected, deReference(result)) { err = unequalError{Expected: stringValue(expected), Got: stringValue(deReference(result))} } else { err = expectedPtr.Valid(result) } if isTypeOf(expected, s.BrokenUnmarshalTypes) { if err == nil { t.Fatalf("expected to fail for (%T), but did not fail", expected) } t.Skipf("skipped bacause there is unsolved problem") } if err != nil { t.Error(err) } }) } ================================================ FILE: internal/tests/serialization/utils.go ================================================ package serialization import ( "reflect" ) func GetTypes(values ...any) []reflect.Type { types := make([]reflect.Type, len(values)) for i, value := range values { types[i] = reflect.TypeOf(value) } return types } func isTypeOf(value any, types []reflect.Type) bool { valueType := reflect.TypeOf(value) for i := range types { if types[i] == valueType { return true } } return false } func deReference(in any) any { return reflect.Indirect(reflect.ValueOf(in)).Interface() } ================================================ FILE: internal/tests/serialization/utils_equal.go ================================================ package serialization import ( "bytes" "fmt" "math/big" "reflect" "unsafe" "gopkg.in/inf.v0" "github.com/gocql/gocql/internal/tests/serialization/mod" ) func equalData(in1, in2 []byte) bool { if in1 == nil || in2 == nil { return in1 == nil && in2 == nil } return bytes.Equal(in1, in2) } func equalVals(in1, in2 any) bool { rin1 := reflect.ValueOf(in1) rin2 := reflect.ValueOf(in2) if rin1.Kind() != rin2.Kind() { return false } if rin1.Kind() == reflect.Ptr && (rin1.IsNil() || rin2.IsNil()) { return rin1.IsNil() && rin2.IsNil() } switch vin1 := in1.(type) { case float32: vin2 := in2.(float32) return *(*[4]byte)(unsafe.Pointer(&vin1)) == *(*[4]byte)(unsafe.Pointer(&vin2)) case *float32: vin2 := in2.(*float32) return *(*[4]byte)(unsafe.Pointer(vin1)) == *(*[4]byte)(unsafe.Pointer(vin2)) case *mod.Float32: vin2 := in2.(*mod.Float32) return *(*[4]byte)(unsafe.Pointer(vin1)) == *(*[4]byte)(unsafe.Pointer(vin2)) case mod.Float32: vin2 := in2.(mod.Float32) return *(*[4]byte)(unsafe.Pointer(&vin1)) == *(*[4]byte)(unsafe.Pointer(&vin2)) case float64: vin2 := in2.(float64) return *(*[8]byte)(unsafe.Pointer(&vin1)) == *(*[8]byte)(unsafe.Pointer(&vin2)) case *float64: vin2 := in2.(*float64) return *(*[8]byte)(unsafe.Pointer(vin1)) == *(*[8]byte)(unsafe.Pointer(vin2)) case *mod.Float64: vin2 := in2.(*mod.Float64) return *(*[8]byte)(unsafe.Pointer(vin1)) == *(*[8]byte)(unsafe.Pointer(vin2)) case mod.Float64: vin2 := in2.(mod.Float64) return *(*[8]byte)(unsafe.Pointer(&vin1)) == *(*[8]byte)(unsafe.Pointer(&vin2)) case big.Int: vin2 := in2.(big.Int) return vin1.Cmp(&vin2) == 0 case *big.Int: vin2 := in2.(*big.Int) return vin1.Cmp(vin2) == 0 case inf.Dec: vin2 := in2.(inf.Dec) if vin1.Scale() != vin2.Scale() { return false } return vin1.UnscaledBig().Cmp(vin2.UnscaledBig()) == 0 case *inf.Dec: vin2 := in2.(*inf.Dec) if vin1.Scale() != vin2.Scale() { return false } return vin1.UnscaledBig().Cmp(vin2.UnscaledBig()) == 0 case fmt.Stringer: vin2 := in2.(fmt.Stringer) return vin1.String() == vin2.String() default: return reflect.DeepEqual(in1, in2) } } ================================================ FILE: internal/tests/serialization/utils_error.go ================================================ package serialization import ( "errors" "fmt" ) var unmarshalErr = errors.New("unmarshal unexpectedly failed with error") var marshalErr = errors.New("marshal unexpectedly failed with error") type unequalError struct { Expected string Got string } func (e unequalError) Error() string { return fmt.Sprintf("expect %s but got %s", e.Expected, e.Got) } type panicErr struct { err error stack []byte } func (e panicErr) Error() string { return fmt.Sprintf("%v\n%s", e.err, e.stack) } ================================================ FILE: internal/tests/serialization/utils_new.go ================================================ package serialization import ( "reflect" ) func newRef(in any) any { out := reflect.New(reflect.TypeOf(in)).Interface() return out } func newRefToZero(in any) any { rv := reflect.ValueOf(in) nw := reflect.New(rv.Type().Elem()) out := reflect.New(rv.Type()) out.Elem().Set(nw) return out.Interface() } ================================================ FILE: internal/tests/serialization/utils_str.go ================================================ package serialization import ( "fmt" "math/big" "net" "reflect" "time" "gopkg.in/inf.v0" ) const printLimit = 100 // stringValue returns (value_type)(value) in the human-readable format. func stringValue(in any) string { valStr := stringVal(in) if len(valStr) > printLimit { return fmt.Sprintf("(%T)", in) } return fmt.Sprintf("(%T)(%s)", in, valStr) } func stringData(p []byte) string { if len(p) > printLimit { p = p[:printLimit] } if p == nil { return "[nil]" } return fmt.Sprintf("[%x]", p) } func stringVal(in any) string { switch i := in.(type) { case string: return i case inf.Dec: return fmt.Sprintf("%v", i.String()) case big.Int: return fmt.Sprintf("%v", i.String()) case net.IP: return fmt.Sprintf("%v", []byte(i)) case time.Time: return fmt.Sprintf("%v", i.UnixMilli()) case nil: return "nil" } rv := reflect.ValueOf(in) switch rv.Kind() { case reflect.Ptr: if rv.IsNil() { return "*nil" } return fmt.Sprintf("*%s", stringVal(rv.Elem().Interface())) case reflect.Slice: if rv.IsNil() { return "[nil]" } return fmt.Sprintf("%v", rv.Interface()) default: return fmt.Sprintf("%v", in) } } ================================================ FILE: internal/tests/serialization/valcases/get.go ================================================ package valcases import ( "reflect" ) type SimpleTypes []SimpleTypeCases type SimpleTypeCases struct { CQLName string Cases []SimpleTypeCase CQLType int } type SimpleTypeCase struct { Name string Data []byte LangCases []LangCase } type LangCase struct { Value any LangType string ErrInsert bool ErrSelect bool } var nilBytes = ([]byte)(nil) func GetSimple() SimpleTypes { return simpleTypesCases } func nilRef(in any) any { out := reflect.NewAt(reflect.TypeOf(in), nil).Interface() return out } ================================================ FILE: internal/tests/serialization/valcases/simple.go ================================================ package valcases import ( "math" "math/big" "net" "time" "gopkg.in/inf.v0" "github.com/gocql/gocql/serialization/duration" ) var simpleTypesCases = SimpleTypes{ { CQLName: "boolean", CQLType: 0x0004, Cases: []SimpleTypeCase{ { Name: "max", Data: []byte{1}, LangCases: []LangCase{ {LangType: "bool", Value: true}, }, }, { Name: "min", Data: []byte{0}, LangCases: []LangCase{ {LangType: "bool", Value: false}, }, }, { Name: "nil", Data: nilBytes, LangCases: []LangCase{ {LangType: "bool", Value: nilRef(false)}, }, }, }, }, { CQLName: "tinyint", CQLType: 0x0014, Cases: []SimpleTypeCase{ { Name: "max", Data: []byte("\x7f"), LangCases: []LangCase{ {LangType: "int8", Value: int8(math.MaxInt8)}, {LangType: "big.Int", Value: big.NewInt(math.MaxInt8)}, }, }, { Name: "min", Data: []byte("\x80"), LangCases: []LangCase{ {LangType: "int8", Value: int8(math.MinInt8)}, {LangType: "big.Int", Value: big.NewInt(math.MinInt8)}, }, }, { Name: "+1", Data: []byte("\x01"), LangCases: []LangCase{ {LangType: "int8", Value: int8(1)}, {LangType: "big.Int", Value: big.NewInt(1)}, }, }, { Name: "-1", Data: []byte("\xff"), LangCases: []LangCase{ {LangType: "int8", Value: int8(-1)}, {LangType: "big.Int", Value: big.NewInt(-1)}, }, }, { Name: "zeros", Data: []byte("\x00"), LangCases: []LangCase{ {LangType: "int8", Value: int8(0)}, {LangType: "big.Int", Value: big.NewInt(0)}, }, }, { Name: "nil", Data: nilBytes, LangCases: []LangCase{ {LangType: "int8", Value: nilRef(int8(0))}, {LangType: "big.Int", Value: nilRef(big.Int{})}, }, }, }, }, { CQLName: "smallint", CQLType: 0x0013, Cases: []SimpleTypeCase{ { Name: "max", Data: []byte("\x7f\xff"), LangCases: []LangCase{ {LangType: "int16", Value: int16(math.MaxInt16)}, {LangType: "big.Int", Value: big.NewInt(math.MaxInt16)}, }, }, { Name: "min", Data: []byte("\x80\x00"), LangCases: []LangCase{ {LangType: "int16", Value: int16(math.MinInt16)}, {LangType: "big.Int", Value: big.NewInt(math.MinInt16)}, }, }, { Name: "+1", Data: []byte("\x00\x01"), LangCases: []LangCase{ {LangType: "int16", Value: int16(1)}, {LangType: "big.Int", Value: big.NewInt(1)}, }, }, { Name: "-1", Data: []byte("\xff\xff"), LangCases: []LangCase{ {LangType: "int16", Value: int16(-1)}, {LangType: "big.Int", Value: big.NewInt(-1)}, }, }, { Name: "zeros", Data: []byte("\x00\x00"), LangCases: []LangCase{ {LangType: "int16", Value: int16(0)}, {LangType: "big.Int", Value: big.NewInt(0)}, }, }, { Name: "nil", Data: nilBytes, LangCases: []LangCase{ {LangType: "int16", Value: nilRef(int16(0))}, {LangType: "big.Int", Value: nilRef(big.Int{})}, }, }, }, }, { CQLName: "int", CQLType: 0x0009, Cases: []SimpleTypeCase{ { Name: "max", Data: []byte("\x7f\xff\xff\xff"), LangCases: []LangCase{ {LangType: "int32", Value: int32(math.MaxInt32)}, {LangType: "big.Int", Value: big.NewInt(math.MaxInt32)}, }, }, { Name: "min", Data: []byte("\x80\x00\x00\x00"), LangCases: []LangCase{ {LangType: "int32", Value: int32(math.MinInt32)}, {LangType: "big.Int", Value: big.NewInt(math.MinInt32)}, }, }, { Name: "+1", Data: []byte("\x00\x00\x00\x01"), LangCases: []LangCase{ {LangType: "int32", Value: int32(1)}, {LangType: "big.Int", Value: big.NewInt(1)}, }, }, { Name: "-1", Data: []byte("\xff\xff\xff\xff"), LangCases: []LangCase{ {LangType: "int32", Value: int32(-1)}, {LangType: "big.Int", Value: big.NewInt(-1)}, }, }, { Name: "zeros", Data: []byte("\x00\x00\x00\x00"), LangCases: []LangCase{ {LangType: "int32", Value: int32(0)}, {LangType: "big.Int", Value: big.NewInt(0)}, }, }, { Name: "nil", Data: nilBytes, LangCases: []LangCase{ {LangType: "int32", Value: nilRef(int32(0))}, {LangType: "big.Int", Value: nilRef(big.Int{})}, }, }, }, }, { CQLName: "bigint", CQLType: 0x0002, Cases: []SimpleTypeCase{ { Name: "max", Data: []byte("\x7f\xff\xff\xff\xff\xff\xff\xff"), LangCases: []LangCase{ {LangType: "int64", Value: int64(math.MaxInt64)}, {LangType: "big.Int", Value: big.NewInt(math.MaxInt64)}, }, }, { Name: "min", Data: []byte("\x80\x00\x00\x00\x00\x00\x00\x00"), LangCases: []LangCase{ {LangType: "int64", Value: int64(math.MinInt64)}, {LangType: "big.Int", Value: big.NewInt(math.MinInt64)}, }, }, { Name: "+1", Data: []byte("\x00\x00\x00\x00\x00\x00\x00\x01"), LangCases: []LangCase{ {LangType: "int64", Value: int64(1)}, {LangType: "big.Int", Value: big.NewInt(1)}, }, }, { Name: "-1", Data: []byte("\xff\xff\xff\xff\xff\xff\xff\xff"), LangCases: []LangCase{ {LangType: "int64", Value: int64(-1)}, {LangType: "big.Int", Value: big.NewInt(-1)}, }, }, { Name: "zeros", Data: []byte("\x00\x00\x00\x00\x00\x00\x00\x00"), LangCases: []LangCase{ {LangType: "int64", Value: int64(0)}, {LangType: "big.Int", Value: big.NewInt(0)}, }, }, { Name: "nil", Data: nilBytes, LangCases: []LangCase{ {LangType: "int64", Value: nilRef(int64(0))}, {LangType: "big.Int", Value: nilRef(big.Int{})}, }, }, }, }, { CQLName: "varint", CQLType: 0x000E, Cases: []SimpleTypeCase{ { Name: "max", Data: []byte("\x7f\xff\xff\xff\xff\xff\xff\xff"), LangCases: []LangCase{ {LangType: "int64", Value: int64(math.MaxInt64)}, {LangType: "big.Int", Value: big.NewInt(math.MaxInt64)}, {LangType: "string", Value: "9223372036854775807"}, }, }, { Name: "min", Data: []byte("\x80\x00\x00\x00\x00\x00\x00\x00"), LangCases: []LangCase{ {LangType: "int64", Value: int64(math.MinInt64)}, {LangType: "big.Int", Value: big.NewInt(math.MinInt64)}, {LangType: "string", Value: "-9223372036854775808"}, }, }, { Name: "+1", Data: []byte("\x01"), LangCases: []LangCase{ {LangType: "int64", Value: int64(1)}, {LangType: "big.Int", Value: big.NewInt(1)}, {LangType: "string", Value: "1"}, }, }, { Name: "-1", Data: []byte("\xff"), LangCases: []LangCase{ {LangType: "int64", Value: int64(-1)}, {LangType: "big.Int", Value: big.NewInt(-1)}, {LangType: "string", Value: "-1"}, }, }, { Name: "zeros", Data: []byte("\x00"), LangCases: []LangCase{ {LangType: "int64", Value: int64(0)}, {LangType: "big.Int", Value: big.NewInt(0)}, {LangType: "string", Value: "0"}, }, }, { Name: "nil", Data: nilBytes, LangCases: []LangCase{ {LangType: "int64", Value: nilRef(int64(0))}, {LangType: "big.Int", Value: nilRef(big.Int{})}, {LangType: "string", Value: ""}, {LangType: "string_ref", Value: nilRef("")}, }, }, }, }, { CQLName: "float", CQLType: 0x0008, Cases: []SimpleTypeCase{ { Name: "max", Data: []byte("\x7f\x7f\xff\xff"), LangCases: []LangCase{ {LangType: "float32", Value: float32(math.MaxFloat32)}, }, }, { Name: "min", Data: []byte("\xff\x7f\xff\xff"), LangCases: []LangCase{ {LangType: "float32", Value: float32(-math.MaxFloat32)}, }, }, { Name: "smallest_pos", Data: []byte("\x00\x00\x00\x01"), LangCases: []LangCase{ {LangType: "float32", Value: float32(math.SmallestNonzeroFloat32)}, }, }, { Name: "smallest_neg", Data: []byte("\x80\x00\x00\x01"), LangCases: []LangCase{ {LangType: "float32", Value: float32(-math.SmallestNonzeroFloat32)}, }, }, { Name: "zeros", Data: []byte("\x00\x00\x00\x00"), LangCases: []LangCase{ {LangType: "float32", Value: float32(0)}, }, }, { Name: "nil", Data: nilBytes, LangCases: []LangCase{ {LangType: "float32", Value: nilRef(float32(0))}, }, }, }, }, { CQLName: "double", CQLType: 0x0007, Cases: []SimpleTypeCase{ { Name: "max", Data: []byte("\x7f\xef\xff\xff\xff\xff\xff\xff"), LangCases: []LangCase{ {LangType: "float64", Value: math.MaxFloat64}, }, }, { Name: "min", Data: []byte("\xff\xef\xff\xff\xff\xff\xff\xff"), LangCases: []LangCase{ {LangType: "float64", Value: -math.MaxFloat64}, }, }, { Name: "smallest_pos", Data: []byte("\x00\x00\x00\x00\x00\x00\x00\x01"), LangCases: []LangCase{ {LangType: "float64", Value: math.SmallestNonzeroFloat64}, }, }, { Name: "smallest_neg", Data: []byte("\x80\x00\x00\x00\x00\x00\x00\x01"), LangCases: []LangCase{ {LangType: "float64", Value: -math.SmallestNonzeroFloat64}, }, }, { Name: "zeros", Data: []byte("\x00\x00\x00\x00\x00\x00\x00\x00"), LangCases: []LangCase{ {LangType: "float64", Value: float64(0)}, }, }, { Name: "nil", Data: nilBytes, LangCases: []LangCase{ {LangType: "float64", Value: nilRef(float64(0))}, }, }, }, }, { CQLName: "decimal", CQLType: 0x0006, Cases: []SimpleTypeCase{ { Name: "max", Data: []byte("\x00\x00\x7f\xff\x7f\xff\xff\xff\xff\xff\xff\xff"), LangCases: []LangCase{ {LangType: "inf.Dec", Value: *inf.NewDec(math.MaxInt64, math.MaxInt16)}, {LangType: "string", Value: "32767;9223372036854775807"}, }, }, { Name: "min", Data: []byte("\xff\xff\x80\x00\x80\x00\x00\x00\x00\x00\x00\x00"), LangCases: []LangCase{ {LangType: "inf.Dec", Value: *inf.NewDec(math.MinInt64, math.MinInt16)}, {LangType: "string", Value: "-32768;-9223372036854775808"}, }, }, { Name: "zeros", Data: []byte("\x00\x00\x00\x00\x00"), LangCases: []LangCase{ {LangType: "inf.Dec", Value: *inf.NewDec(0, 0)}, {LangType: "string", Value: "0;0"}, }, }, { Name: "nil", Data: nilBytes, LangCases: []LangCase{ {LangType: "inf.Dec", Value: nilRef(inf.Dec{})}, {LangType: "string", Value: ""}, {LangType: "string_ref", Value: nilRef("")}, }, }, }, }, { CQLName: "varchar", CQLType: 0x000D, Cases: []SimpleTypeCase{ { Name: "val", Data: []byte("test string"), LangCases: []LangCase{ {LangType: "string", Value: "test string"}, }, }, { Name: "zeros", Data: make([]byte, 0), LangCases: []LangCase{ {LangType: "string", Value: ""}, }, }, { Name: "nil", Data: nilBytes, LangCases: []LangCase{ {LangType: "string", Value: nilRef("")}, }, }, }, }, { CQLName: "text", CQLType: 0x000A, Cases: []SimpleTypeCase{ { Name: "val", Data: []byte("test string"), LangCases: []LangCase{ {LangType: "string", Value: "test string"}, }, }, { Name: "zeros", Data: make([]byte, 0), LangCases: []LangCase{ {LangType: "string", Value: ""}, }, }, { Name: "nil", Data: nilBytes, LangCases: []LangCase{ {LangType: "string", Value: nilRef("")}, }, }, }, }, { CQLName: "blob", CQLType: 0x0003, Cases: []SimpleTypeCase{ { Name: "val", Data: []byte("test string"), LangCases: []LangCase{ {LangType: "string", Value: "test string"}, }, }, { Name: "zeros", Data: make([]byte, 0), LangCases: []LangCase{ {LangType: "string", Value: ""}, }, }, { Name: "nil", Data: nilBytes, LangCases: []LangCase{ {LangType: "string", Value: nilRef("")}, }, }, }, }, { CQLName: "ascii", CQLType: 0x0001, Cases: []SimpleTypeCase{ { Name: "val", Data: []byte("test string"), LangCases: []LangCase{ {LangType: "string", Value: "test string"}, }, }, { Name: "zeros", Data: make([]byte, 0), LangCases: []LangCase{ {LangType: "string", Value: ""}, }, }, { Name: "nil", Data: nilBytes, LangCases: []LangCase{ {LangType: "string", Value: nilRef("")}, }, }, }, }, { CQLName: "uuid", CQLType: 0x000C, Cases: []SimpleTypeCase{ { Name: "max", Data: []byte("\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"), LangCases: []LangCase{ {LangType: "string", Value: "ffffffff-ffff-ffff-ffff-ffffffffffff"}, {LangType: "[16]byte", Value: [16]byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}}, }, }, { Name: "val", Data: []byte("\xe9\x39\xf5\x2a\xd6\x90\x11\xef\x9c\xd2\x02\x42\xac\x12\x00\x02"), LangCases: []LangCase{ {LangType: "string", Value: "e939f52a-d690-11ef-9cd2-0242ac120002"}, {LangType: "[16]byte", Value: [16]byte{233, 57, 245, 42, 214, 144, 17, 239, 156, 210, 2, 66, 172, 18, 0, 2}}, }, }, { Name: "zeros", Data: make([]byte, 16), LangCases: []LangCase{ {LangType: "string", Value: "00000000-0000-0000-0000-000000000000"}, {LangType: "[16]byte", Value: [16]byte{}}, }, }, { Name: "nil", Data: nilBytes, LangCases: []LangCase{ {LangType: "string", Value: ""}, {LangType: "string_ref", Value: nilRef("")}, {LangType: "[16]byte", Value: nilRef([16]byte{})}, }, }, }, }, { CQLName: "timeuuid", CQLType: 0x000F, Cases: []SimpleTypeCase{ { Name: "max", Data: []byte("\xff\xff\xff\xff\xff\xff\x1f\xff\xff\xff\xff\xff\xff\xff\xff\xff"), LangCases: []LangCase{ {LangType: "string", Value: "ffffffff-ffff-1fff-ffff-ffffffffffff"}, {LangType: "[16]byte", Value: [16]byte{255, 255, 255, 255, 255, 255, 31, 255, 255, 255, 255, 255, 255, 255, 255, 255}}, }, }, { Name: "val", Data: []byte("\xe9\x39\xf5\x2a\xd6\x90\x11\xef\x9c\xd2\x02\x42\xac\x12\x00\x02"), LangCases: []LangCase{ {LangType: "string", Value: "e939f52a-d690-11ef-9cd2-0242ac120002"}, {LangType: "[16]byte", Value: [16]byte{233, 57, 245, 42, 214, 144, 17, 239, 156, 210, 2, 66, 172, 18, 0, 2}}, }, }, { Name: "zeros", Data: []byte{0, 0, 0, 0, 0, 0, 1 << 4, 0, 0, 0, 0, 0, 0, 0, 0, 0}, LangCases: []LangCase{ {LangType: "string", Value: "00000000-0000-1000-0000-000000000000"}, {LangType: "[16]byte", Value: [16]byte{0, 0, 0, 0, 0, 0, 1 << 4, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, }, }, { Name: "nil", Data: nilBytes, LangCases: []LangCase{ {LangType: "string", Value: ""}, {LangType: "string_ref", Value: nilRef("")}, {LangType: "[16]byte", Value: nilRef([16]byte{})}, }, }, }, }, { CQLName: "inet", CQLType: 0x0010, Cases: []SimpleTypeCase{ { Name: "v6max", Data: []byte("\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"), LangCases: []LangCase{ {LangType: "string", Value: "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"}, {LangType: "net.IP", Value: net.IP("\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff")}, }, }, { Name: "v4max", Data: []byte("\xff\xff\xff\xff"), LangCases: []LangCase{ {LangType: "string", Value: "255.255.255.255"}, {LangType: "net.IP", Value: net.IP("\xff\xff\xff\xff")}, }, }, { Name: "v6zeros", Data: make([]byte, 16), LangCases: []LangCase{ {LangType: "string", Value: "::"}, {LangType: "net.IP", Value: make(net.IP, 16)}, }, }, { Name: "v4zeros", Data: make([]byte, 4), LangCases: []LangCase{ {LangType: "string", Value: "0.0.0.0"}, {LangType: "net.IP", Value: make(net.IP, 4)}, }, }, { Name: "nil", Data: nilBytes, LangCases: []LangCase{ {LangType: "string", Value: ""}, {LangType: "string_ref", Value: nilRef("")}, {LangType: "net.IP", Value: (net.IP)(nil)}, {LangType: "net.IP_ref", Value: nilRef(net.IP{})}, }, }, }, }, { CQLName: "time", CQLType: 0x0012, Cases: []SimpleTypeCase{ { Name: "max", Data: []byte("\x00\x00\x4e\x94\x91\x4e\xff\xff"), LangCases: []LangCase{ {LangType: "int64", Value: int64(86399999999999)}, {LangType: "time.Duration", Value: time.Duration(86399999999999)}, }, }, { Name: "zeros", Data: []byte("\x00\x00\x00\x00\x00\x00\x00\x00"), LangCases: []LangCase{ {LangType: "int64", Value: int64(0)}, {LangType: "time.Duration", Value: time.Duration(0)}, }, }, { Name: "nil", Data: nilBytes, LangCases: []LangCase{ {LangType: "int64", Value: nilRef(int64(0))}, {LangType: "time.Duration", Value: nilRef(time.Duration(0))}, }, }, }, }, { CQLName: "timestamp", CQLType: 0x000B, Cases: []SimpleTypeCase{ { Name: "max", Data: []byte("\x7f\xff\xff\xff\xff\xff\xff\xff"), LangCases: []LangCase{ {LangType: "int64", Value: int64(math.MaxInt64)}, {LangType: "time.Time", Value: time.UnixMilli(math.MaxInt64).UTC()}, }, }, { Name: "min", Data: []byte("\x80\x00\x00\x00\x00\x00\x00\x00"), LangCases: []LangCase{ {LangType: "int64", Value: int64(math.MinInt64)}, {LangType: "time.Time", Value: time.UnixMilli(math.MinInt64).UTC()}, }, }, { Name: "+1", Data: []byte("\x00\x00\x00\x00\x00\x00\x00\x01"), LangCases: []LangCase{ {LangType: "int64", Value: int64(1)}, {LangType: "time.Time", Value: time.UnixMilli(1).UTC()}, }, }, { Name: "-1", Data: []byte("\xff\xff\xff\xff\xff\xff\xff\xff"), LangCases: []LangCase{ {LangType: "int64", Value: int64(-1)}, {LangType: "time.Time", Value: time.UnixMilli(-1).UTC()}, }, }, { Name: "zeros", Data: []byte("\x00\x00\x00\x00\x00\x00\x00\x00"), LangCases: []LangCase{ {LangType: "int64", Value: int64(0)}, {LangType: "time.Time", Value: time.UnixMilli(0).UTC()}, }, }, { Name: "nil", Data: nilBytes, LangCases: []LangCase{ {LangType: "int64", Value: nilRef(int64(0))}, {LangType: "time.Time", Value: nilRef(time.Time{})}, }, }, }, }, { CQLName: "date", CQLType: 0x0011, Cases: []SimpleTypeCase{ { Name: "max", Data: []byte("\xff\xff\xff\xff"), LangCases: []LangCase{ {LangType: "uint32", Value: uint32(math.MaxUint32)}, {LangType: "int32", Value: int32(-1)}, {LangType: "time.Time", Value: time.Date(5881580, 07, 11, 0, 0, 0, 0, time.UTC)}, {LangType: "string", Value: "5881580-07-11"}, }, }, { Name: "mid", Data: []byte("\x80\x00\x00\x00"), LangCases: []LangCase{ {LangType: "uint32", Value: uint32(1 << 31)}, {LangType: "int32", Value: int32(math.MinInt32)}, {LangType: "time.Time", Value: time.Date(1970, 01, 01, 0, 0, 0, 0, time.UTC)}, {LangType: "string", Value: "1970-01-01"}, }, }, { Name: "1", Data: []byte("\x00\x00\x00\x01"), LangCases: []LangCase{ {LangType: "uint32", Value: uint32(1)}, {LangType: "int32", Value: int32(1)}, {LangType: "time.Time", Value: time.Date(-5877641, 06, 24, 0, 0, 0, 0, time.UTC)}, {LangType: "string", Value: "-5877641-06-24"}, }, }, { Name: "zeros", Data: []byte("\x00\x00\x00\x00"), LangCases: []LangCase{ {LangType: "uint32", Value: uint32(0)}, {LangType: "int32", Value: int32(0)}, {LangType: "time.Time", Value: time.Date(-5877641, 06, 23, 0, 0, 0, 0, time.UTC)}, {LangType: "string", Value: "-5877641-06-23"}, }, }, { Name: "nil", Data: nilBytes, LangCases: []LangCase{ {LangType: "int32", Value: nilRef(int32(0))}, {LangType: "time.Time", Value: nilRef(time.Time{})}, {LangType: "string", Value: ""}, {LangType: "string_ref", Value: nilRef("")}, }, }, }, }, { CQLName: "duration", CQLType: 0x0015, Cases: []SimpleTypeCase{ { Name: "max", Data: []byte("\xf0\xff\xff\xff\xfe\xf0\xff\xff\xff\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xfe"), LangCases: []LangCase{ {LangType: "duration", Value: duration.Duration{Days: math.MaxInt32, Months: math.MaxInt32, Nanoseconds: math.MaxInt64}}, {LangType: "string", Value: "178956970y7mo306783378w1d2562047h47m16.854775807s"}, }, }, { Name: "min", Data: []byte("\xf0\xff\xff\xff\xff\xf0\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"), LangCases: []LangCase{ {LangType: "duration", Value: duration.Duration{Days: math.MinInt32, Months: math.MinInt32, Nanoseconds: math.MinInt64}}, {LangType: "string", Value: "-178956970y8mo306783378w2d2562047h47m16.854775808s"}, }, }, { Name: "+1", Data: []byte("\x02\x02\x02"), LangCases: []LangCase{ {LangType: "duration", Value: duration.Duration{Days: 1, Months: 1, Nanoseconds: 1}}, {LangType: "string", Value: "1mo1d1ns"}, }, }, { Name: "-1", Data: []byte("\x01\x01\x01"), LangCases: []LangCase{ {LangType: "duration", Value: duration.Duration{Days: -1, Months: -1, Nanoseconds: -1}}, {LangType: "string", Value: "-1mo1d1ns"}, }, }, { Name: "maxNanos", Data: []byte("\x00\xc3\x41\xfe\xfc\x9b\xc5\xc4\x9d\xff\xfe"), LangCases: []LangCase{ {LangType: "duration", Value: duration.Duration{Days: 106751, Months: 0, Nanoseconds: 85636854775807}}, {LangType: "int64", Value: int64(math.MaxInt64)}, {LangType: "time.Duration", Value: time.Duration(math.MaxInt64)}, {LangType: "string", Value: "15250w1d23h47m16.854775807s"}, }, }, { Name: "minNanos", Data: []byte("\x00\xc3\x41\xfd\xfc\x9b\xc5\xc4\x9d\xff\xff"), LangCases: []LangCase{ {LangType: "duration", Value: duration.Duration{Days: -106751, Months: 0, Nanoseconds: -85636854775808}}, {LangType: "int64", Value: int64(math.MinInt64)}, {LangType: "time.Duration", Value: time.Duration(math.MinInt64)}, {LangType: "string", Value: "-15250w1d23h47m16.854775808s"}, }, }, { Name: "zeros", Data: []byte("\x00\x00\x00"), LangCases: []LangCase{ {LangType: "duration", Value: duration.Duration{}}, {LangType: "int64", Value: int64(0)}, {LangType: "time.Duration", Value: time.Duration(0)}, {LangType: "string", Value: "0s"}, }, }, { Name: "nil", Data: nilBytes, LangCases: []LangCase{ {LangType: "duration", Value: nilRef(duration.Duration{})}, {LangType: "int64", Value: nilRef(int64(0))}, {LangType: "time.Duration", Value: nilRef(time.Duration(0))}, {LangType: "string", Value: ""}, {LangType: "string_ref", Value: nilRef("")}, }, }, }, }, } ================================================ FILE: keyspace_table_test.go ================================================ //go:build integration // +build integration /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "context" "fmt" "testing" "github.com/gocql/gocql/internal/tests" ) // Keyspace_table checks if Query.Keyspace() is updated based on prepared statement func TestKeyspaceTable(t *testing.T) { t.Parallel() cluster := createCluster() fallback := RoundRobinHostPolicy() cluster.PoolConfig.HostSelectionPolicy = TokenAwareHostPolicy(fallback) session, err := cluster.CreateSession() if err != nil { t.Fatal("createSession:", err) } cluster.Keyspace = "wrong_keyspace" keyspace := testKeyspaceName(t) table := testTableName(t) err = createTable(session, `DROP KEYSPACE IF EXISTS `+keyspace) if err != nil { t.Fatal("unable to drop keyspace:", err) } err = createTable(session, fmt.Sprintf(`CREATE KEYSPACE %s WITH replication = { 'class' : 'NetworkTopologyStrategy', 'replication_factor' : 1 }`, keyspace)) if err != nil { t.Fatal("unable to create keyspace:", err) } if err := session.control.awaitSchemaAgreement(); err != nil { t.Fatal(err) } err = createTable(session, fmt.Sprintf(`CREATE TABLE %s.%s (pk int, ck int, v int, PRIMARY KEY (pk, ck)); `, keyspace, table)) if err != nil { t.Fatal("unable to create table:", err) } if err := session.control.awaitSchemaAgreement(); err != nil { t.Fatal(err) } ctx := context.Background() // insert a row if err := session.Query(fmt.Sprintf(`INSERT INTO %s.%s(pk, ck, v) VALUES (?, ?, ?)`, keyspace, table), 1, 2, 3).WithContext(ctx).Consistency(One).Exec(); err != nil { t.Fatal(err) } var pk int /* Search for a specific set of records whose 'pk' column matches * the value of inserted row. */ qry := session.Query(fmt.Sprintf(`SELECT pk FROM %s.%s WHERE pk = ? LIMIT 1`, keyspace, table), 1).WithContext(ctx).Consistency(One) if err := qry.Scan(&pk); err != nil { t.Fatal(err) } // cluster.Keyspace was set to "wrong_keyspace", but during prepering statement // Keyspace in Query should be changed to "test" and Table should be changed to table1 tests.AssertEqual(t, "qry.Keyspace()", keyspace, qry.Keyspace()) tests.AssertEqual(t, "qry.Table()", table, qry.Table()) } ================================================ FILE: logger.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "bytes" "fmt" "log" ) type StdLogger interface { Print(v ...any) Printf(format string, v ...any) Println(v ...any) } type nopLogger struct{} func (n nopLogger) Print(_ ...any) {} func (n nopLogger) Printf(_ string, _ ...any) {} func (n nopLogger) Println(_ ...any) {} type testLogger struct { capture bytes.Buffer } func (l *testLogger) Print(v ...any) { fmt.Fprint(&l.capture, v...) } func (l *testLogger) Printf(format string, v ...any) { fmt.Fprintf(&l.capture, format, v...) } func (l *testLogger) Println(v ...any) { fmt.Fprintln(&l.capture, v...) } func (l *testLogger) String() string { return l.capture.String() } type defaultLogger struct{} func (l *defaultLogger) Print(v ...any) { log.Print(v...) } func (l *defaultLogger) Printf(format string, v ...any) { log.Printf(format, v...) } func (l *defaultLogger) Println(v ...any) { log.Println(v...) } ================================================ FILE: lz4/go.mod ================================================ // // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // module github.com/gocql/gocql/lz4 go 1.25.0 require ( github.com/pierrec/lz4/v4 v4.1.26 github.com/stretchr/testify v1.11.1 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) ================================================ FILE: lz4/go.sum ================================================ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/pierrec/lz4/v4 v4.1.26 h1:GrpZw1gZttORinvzBdXPUXATeqlJjqUG/D87TKMnhjY= github.com/pierrec/lz4/v4 v4.1.26/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= ================================================ FILE: lz4/lz4.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package lz4 import ( "encoding/binary" "fmt" "github.com/pierrec/lz4/v4" ) // LZ4Compressor implements the gocql.Compressor interface and can be used to // compress incoming and outgoing frames. According to the Cassandra docs the // LZ4 protocol should be preferred over snappy. (For details refer to // https://cassandra.apache.org/doc/latest/operating/compression.html) // // Implementation note: Cassandra prefixes each compressed block with 4 bytes // of the uncompressed block length, written in big endian order. But the LZ4 // compression library github.com/pierrec/lz4/v4 does not expect the length // field, so it needs to be added to compressed blocks sent to Cassandra, and // removed from ones received from Cassandra before decompression. type LZ4Compressor struct{} func (s LZ4Compressor) Name() string { return "lz4" } func (s LZ4Compressor) Encode(data []byte) ([]byte, error) { dataLen := len(data) buf := make([]byte, lz4.CompressBlockBound(dataLen)+4) n, err := lz4.CompressBlock(data, buf[4:], nil) // According to lz4.CompressBlock doc, it doesn't fail as long as the dst // buffer length is at least lz4.CompressBlockBound(len(data))) bytes, but // we check for error anyway just to be thorough. if err != nil { return nil, err } binary.BigEndian.PutUint32(buf, uint32(dataLen)) return buf[:n+4], nil } func (s LZ4Compressor) Decode(data []byte) ([]byte, error) { if len(data) < 4 { return nil, fmt.Errorf("cassandra lz4 block size should be >4, got=%d", len(data)) } uncompressedLength := binary.BigEndian.Uint32(data) if uncompressedLength == 0 { return nil, nil } buf := make([]byte, uncompressedLength) n, err := lz4.UncompressBlock(data[4:], buf) return buf[:n], err } ================================================ FILE: lz4/lz4_bench_test.go ================================================ //go:build bench // +build bench package lz4 import ( "testing" ) func BenchmarkLZ4Compressor(b *testing.B) { original := []byte("My Test String") var c LZ4Compressor b.Run("Encode", func(b *testing.B) { for i := 0; i < b.N; i++ { _, err := c.Encode(original) if err != nil { b.Fatal(err) } } }) } ================================================ FILE: lz4/lz4_test.go ================================================ //go:build unit // +build unit /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package lz4 import ( "testing" "github.com/stretchr/testify/require" ) func TestLZ4Compressor(t *testing.T) { t.Parallel() var c LZ4Compressor require.Equal(t, "lz4", c.Name()) _, err := c.Decode([]byte{0, 1, 2}) require.EqualError(t, err, "cassandra lz4 block size should be >4, got=3") _, err = c.Decode([]byte{0, 1, 2, 4, 5}) require.EqualError(t, err, "lz4: invalid source or destination buffer too short") // If uncompressed size is zero then nothing is decoded even if present. decoded, err := c.Decode([]byte{0, 0, 0, 0, 5, 7, 8}) require.NoError(t, err) require.Nil(t, decoded) original := []byte("My Test String") encoded, err := c.Encode(original) require.NoError(t, err) decoded, err = c.Decode(encoded) require.NoError(t, err) require.Equal(t, original, decoded) } ================================================ FILE: marshal.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2012, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "bytes" "errors" "fmt" "math" "math/big" "math/bits" "reflect" "time" "unsafe" "gopkg.in/inf.v0" "github.com/gocql/gocql/serialization/ascii" "github.com/gocql/gocql/serialization/bigint" "github.com/gocql/gocql/serialization/blob" "github.com/gocql/gocql/serialization/boolean" "github.com/gocql/gocql/serialization/counter" "github.com/gocql/gocql/serialization/cqlint" "github.com/gocql/gocql/serialization/cqltime" "github.com/gocql/gocql/serialization/date" "github.com/gocql/gocql/serialization/decimal" "github.com/gocql/gocql/serialization/double" "github.com/gocql/gocql/serialization/duration" "github.com/gocql/gocql/serialization/float" "github.com/gocql/gocql/serialization/inet" "github.com/gocql/gocql/serialization/smallint" "github.com/gocql/gocql/serialization/text" "github.com/gocql/gocql/serialization/timestamp" "github.com/gocql/gocql/serialization/timeuuid" "github.com/gocql/gocql/serialization/tinyint" "github.com/gocql/gocql/serialization/uuid" "github.com/gocql/gocql/serialization/varchar" "github.com/gocql/gocql/serialization/varint" ) var ( emptyValue reflect.Value ) var ( ErrorUDTUnavailable = errors.New("UDT are not available on protocols less than 3, please update config") ) // Marshaler is an interface for custom unmarshaler. // Each value of the 'CQL binary protocol' consist of and . // can be 'unset'(-2), 'nil'(-1), 'zero'(0) or any value up to 2147483647. // When is 'unset', 'nil' or 'zero', is not present. // 'unset' is applicable only to columns, with some exceptions. // As you can see from API MarshalCQL only returns , but there is a way for it to control : // 1. If MarshalCQL returns (gocql.UnsetValue, nil), gocql writes 'unset' to // 2. If MarshalCQL returns ([]byte(nil), nil), gocql writes 'nil' to // 3. If MarshalCQL returns ([]byte{}, nil), gocql writes 'zero' to // // Some CQL databases have proprietary value coding features, which you may want to consider. // CQL binary protocol info:https://github.com/apache/cassandra/tree/trunk/doc type Marshaler interface { MarshalCQL(info TypeInfo) ([]byte, error) } type DirectMarshal []byte func (m DirectMarshal) MarshalCQL(_ TypeInfo) ([]byte, error) { return m, nil } // Unmarshaler is an interface for custom unmarshaler. // Each value of the 'CQL binary protocol' consist of and . // can be 'unset'(-2), 'nil'(-1), 'zero'(0) or any value up to 2147483647. // When is 'unset', 'nil' or 'zero', is not present. // As you can see from an API UnmarshalCQL receives only 'info TypeInfo' and // 'data []byte', but gocql has the following way to signal about : // 1. When is 'nil' gocql feeds nil to 'data []byte' // 2. When is 'zero' gocql feeds []byte{} to 'data []byte' // // The data []byte slice passed to UnmarshalCQL is only valid for the duration // of the call. The backing memory may be reused after the call returns. // Implementations that need to retain data must copy it (e.g. using // bytes.Clone or append([]byte(nil), data...)). // // Some CQL databases have proprietary value coding features, which you may want to consider. // CQL binary protocol info:https://github.com/apache/cassandra/tree/trunk/doc type Unmarshaler interface { UnmarshalCQL(info TypeInfo, data []byte) error } type DirectUnmarshal []byte func (d *DirectUnmarshal) UnmarshalCQL(_ TypeInfo, data []byte) error { *d = bytes.Clone(data) return nil } // Marshal returns the CQL encoding of the value for the Cassandra // internal type described by the info parameter. // // nil is serialized as CQL null. // If value implements Marshaler, its MarshalCQL method is called to marshal the data. // If value is a pointer, the pointed-to value is marshaled. // // Supported conversions are as follows, other type combinations may be added in the future: // // CQL type | Go type (value) | Note // varchar, ascii, blob, text | string, []byte | // boolean | bool | // tinyint, smallint, int | integer types | // tinyint, smallint, int | string | formatted as base 10 number // bigint, counter | integer types | // bigint, counter | big.Int | value limited as int64 // bigint, counter | string | formatted as base 10 number // float | float32 | // double | float64 | // decimal | inf.Dec | // time | int64 | nanoseconds since start of day // time | time.Duration | duration since start of day // timestamp | int64 | milliseconds since Unix epoch // timestamp | time.Time | // list, set | slice, array | // list, set | map[X]struct{} | // map | map[X]Y | // uuid, timeuuid | gocql.UUID | // uuid, timeuuid | [16]byte | raw UUID bytes // uuid, timeuuid | []byte | raw UUID bytes, length must be 16 bytes // uuid, timeuuid | string | hex representation, see ParseUUID // varint | integer types | // varint | big.Int | // varint | string | value of number in decimal notation // inet | net.IP | // inet | string | IPv4 or IPv6 address string // tuple | slice, array | // tuple | struct | fields are marshaled in order of declaration // user-defined type | gocql.UDTMarshaler | MarshalUDT is called // user-defined type | map[string]any | // user-defined type | struct | struct fields' cql tags are used for column names // date | int64 | milliseconds since Unix epoch to start of day (in UTC) // date | time.Time | start of day (in UTC) // date | string | parsed using "2006-01-02" format // duration | int64 | duration in nanoseconds // duration | time.Duration | // duration | gocql.Duration | // duration | string | parsed with time.ParseDuration // // The marshal/unmarshal error provides a list of supported types when an unsupported type is attempted. func Marshal(info TypeInfo, value any) ([]byte, error) { if info.Version() < protoVersion1 { panic("protocol version not set") } if valueRef := reflect.ValueOf(value); valueRef.Kind() == reflect.Ptr { if valueRef.IsNil() { return nil, nil } else if v, ok := value.(Marshaler); ok { return v.MarshalCQL(info) } else { return Marshal(info, valueRef.Elem().Interface()) } } if v, ok := value.(Marshaler); ok { return v.MarshalCQL(info) } switch info.Type() { case TypeVarchar: return marshalVarchar(value) case TypeText: return marshalText(value) case TypeBlob: return marshalBlob(value) case TypeAscii: return marshalAscii(value) case TypeBoolean: return marshalBool(value) case TypeTinyInt: return marshalTinyInt(value) case TypeSmallInt: return marshalSmallInt(value) case TypeInt: return marshalInt(value) case TypeBigInt: return marshalBigInt(value) case TypeCounter: return marshalCounter(value) case TypeFloat: return marshalFloat(value) case TypeDouble: return marshalDouble(value) case TypeDecimal: return marshalDecimal(value) case TypeTime: return marshalTime(value) case TypeTimestamp: return marshalTimestamp(value) case TypeList, TypeSet: return marshalList(info, value) case TypeMap: return marshalMap(info, value) case TypeUUID: return marshalUUID(value) case TypeTimeUUID: return marshalTimeUUID(value) case TypeVarint: return marshalVarint(value) case TypeInet: return marshalInet(value) case TypeTuple: return marshalTuple(info, value) case TypeUDT: return marshalUDT(info, value) case TypeDate: return marshalDate(value) case TypeDuration: return marshalDuration(value) case TypeCustom: if vector, ok := info.(VectorType); ok { return marshalVector(vector, value) } } // TODO(tux21b): add the remaining types return nil, fmt.Errorf("can not marshal %T into %s", value, info) } // Unmarshal parses the CQL encoded data based on the info parameter that // describes the Cassandra internal data type and stores the result in the // value pointed by value. // // If value implements Unmarshaler, it's UnmarshalCQL method is called to // unmarshal the data. // If value is a pointer to pointer, it is set to nil if the CQL value is // null. Otherwise, nulls are unmarshalled as zero value. // // Supported conversions are as follows, other type combinations may be added in the future: // // CQL type | Go type (value) | Note // varchar, ascii, blob, text | *string | // varchar, ascii, blob, text | *[]byte | non-nil buffer is reused // bool | *bool | // tinyint, smallint, int, bigint, counter | *integer types | // tinyint, smallint, int, bigint, counter | *big.Int | // tinyint, smallint, int, bigint, counter | *string | formatted as base 10 number // float | *float32 | // double | *float64 | // decimal | *inf.Dec | // time | *int64 | nanoseconds since start of day // time | *time.Duration | // timestamp | *int64 | milliseconds since Unix epoch // timestamp | *time.Time | // list, set | *slice, *array | // map | *map[X]Y | // uuid, timeuuid | *string | see UUID.String // uuid, timeuuid | *[]byte | raw UUID bytes // uuid, timeuuid | *gocql.UUID | // timeuuid | *time.Time | timestamp of the UUID // inet | *net.IP | // inet | *string | IPv4 or IPv6 address string // tuple | *slice, *array | // tuple | *struct | struct fields are set in order of declaration // user-defined types | gocql.UDTUnmarshaler | UnmarshalUDT is called // user-defined types | *map[string]any | // user-defined types | *struct | cql tag is used to determine field name // date | *time.Time | time of beginning of the day (in UTC) // date | *string | formatted with 2006-01-02 format // duration | *gocql.Duration | func Unmarshal(info TypeInfo, data []byte, value any) error { if v, ok := value.(Unmarshaler); ok { return v.UnmarshalCQL(info, data) } if isNullableValue(value) { return unmarshalNullable(info, data, value) } switch info.Type() { case TypeVarchar: return unmarshalVarchar(data, value) case TypeText: return unmarshalText(data, value) case TypeBlob: return unmarshalBlob(data, value) case TypeAscii: return unmarshalAscii(data, value) case TypeBoolean: return unmarshalBool(data, value) case TypeInt: return unmarshalInt(data, value) case TypeBigInt: return unmarshalBigInt(data, value) case TypeCounter: return unmarshalCounter(data, value) case TypeVarint: return unmarshalVarint(data, value) case TypeSmallInt: return unmarshalSmallInt(data, value) case TypeTinyInt: return unmarshalTinyInt(data, value) case TypeFloat: return unmarshalFloat(data, value) case TypeDouble: return unmarshalDouble(data, value) case TypeDecimal: return unmarshalDecimal(data, value) case TypeTime: return unmarshalTime(data, value) case TypeTimestamp: return unmarshalTimestamp(data, value) case TypeList, TypeSet: return unmarshalList(info, data, value) case TypeMap: return unmarshalMap(info, data, value) case TypeTimeUUID: return unmarshalTimeUUID(data, value) case TypeUUID: return unmarshalUUID(data, value) case TypeInet: return unmarshalInet(data, value) case TypeTuple: return unmarshalTuple(info, data, value) case TypeUDT: return unmarshalUDT(info, data, value) case TypeDate: return unmarshalDate(data, value) case TypeDuration: return unmarshalDuration(data, value) case TypeCustom: if vector, ok := info.(VectorType); ok { return unmarshalVector(vector, data, value) } } // TODO(tux21b): add the remaining types return fmt.Errorf("can not unmarshal %s into %T", info, value) } func isNullableValue(value any) bool { v := reflect.ValueOf(value) return v.Kind() == reflect.Ptr && v.Type().Elem().Kind() == reflect.Ptr } func isNullData(info TypeInfo, data []byte) bool { return data == nil } func unmarshalNullable(info TypeInfo, data []byte, value any) error { valueRef := reflect.ValueOf(value) if isNullData(info, data) { nilValue := reflect.Zero(valueRef.Type().Elem()) valueRef.Elem().Set(nilValue) return nil } newValue := reflect.New(valueRef.Type().Elem().Elem()) valueRef.Elem().Set(newValue) return Unmarshal(info, data, newValue.Interface()) } func marshalVarchar(value any) ([]byte, error) { data, err := varchar.Marshal(value) if err != nil { return nil, wrapMarshalError(err, "marshal error") } return data, nil } func marshalText(value any) ([]byte, error) { data, err := text.Marshal(value) if err != nil { return nil, wrapMarshalError(err, "marshal error") } return data, nil } func marshalBlob(value any) ([]byte, error) { data, err := blob.Marshal(value) if err != nil { return nil, wrapMarshalError(err, "marshal error") } return data, nil } func marshalAscii(value any) ([]byte, error) { data, err := ascii.Marshal(value) if err != nil { return nil, wrapMarshalError(err, "marshal error") } return data, nil } func unmarshalVarchar(data []byte, value any) error { err := varchar.Unmarshal(data, value) if err != nil { return wrapUnmarshalError(err, "unmarshal error") } return nil } func unmarshalText(data []byte, value any) error { err := text.Unmarshal(data, value) if err != nil { return wrapUnmarshalError(err, "unmarshal error") } return nil } func unmarshalBlob(data []byte, value any) error { err := blob.Unmarshal(data, value) if err != nil { return wrapUnmarshalError(err, "unmarshal error") } return nil } func unmarshalAscii(data []byte, value any) error { err := ascii.Unmarshal(data, value) if err != nil { return wrapUnmarshalError(err, "unmarshal error") } return nil } func marshalSmallInt(value any) ([]byte, error) { data, err := smallint.Marshal(value) if err != nil { return nil, wrapMarshalError(err, "marshal error") } return data, nil } func marshalTinyInt(value any) ([]byte, error) { data, err := tinyint.Marshal(value) if err != nil { return nil, wrapMarshalError(err, "marshal error") } return data, nil } func marshalInt(value any) ([]byte, error) { data, err := cqlint.Marshal(value) if err != nil { return nil, wrapMarshalError(err, "marshal error") } return data, nil } func marshalBigInt(value any) ([]byte, error) { data, err := bigint.Marshal(value) if err != nil { return nil, wrapMarshalError(err, "marshal error") } return data, nil } func marshalCounter(value any) ([]byte, error) { data, err := counter.Marshal(value) if err != nil { return nil, wrapMarshalError(err, "marshal error") } return data, nil } func unmarshalCounter(data []byte, value any) error { err := counter.Unmarshal(data, value) if err != nil { return wrapUnmarshalError(err, "unmarshal error") } return nil } func unmarshalInt(data []byte, value any) error { err := cqlint.Unmarshal(data, value) if err != nil { return wrapUnmarshalError(err, "unmarshal error") } return nil } func unmarshalBigInt(data []byte, value any) error { err := bigint.Unmarshal(data, value) if err != nil { return wrapUnmarshalError(err, "unmarshal error") } return nil } func unmarshalSmallInt(data []byte, value any) error { err := smallint.Unmarshal(data, value) if err != nil { return wrapUnmarshalError(err, "unmarshal error") } return nil } func unmarshalTinyInt(data []byte, value any) error { if err := tinyint.Unmarshal(data, value); err != nil { return wrapUnmarshalError(err, "unmarshal error") } return nil } func unmarshalVarint(data []byte, value any) error { if err := varint.Unmarshal(data, value); err != nil { return wrapUnmarshalError(err, "unmarshal error") } return nil } func marshalVarint(value any) ([]byte, error) { data, err := varint.Marshal(value) if err != nil { return nil, wrapMarshalError(err, "marshal error") } return data, nil } func marshalBool(value any) ([]byte, error) { data, err := boolean.Marshal(value) if err != nil { return nil, wrapMarshalError(err, "marshal error") } return data, nil } func unmarshalBool(data []byte, value any) error { if err := boolean.Unmarshal(data, value); err != nil { return wrapUnmarshalError(err, "unmarshal error") } return nil } func marshalFloat(value any) ([]byte, error) { data, err := float.Marshal(value) if err != nil { return nil, wrapMarshalError(err, "marshal error") } return data, nil } func unmarshalFloat(data []byte, value any) error { if err := float.Unmarshal(data, value); err != nil { return wrapUnmarshalError(err, "unmarshal error") } return nil } func marshalDouble(value any) ([]byte, error) { data, err := double.Marshal(value) if err != nil { return nil, wrapMarshalError(err, "marshal error") } return data, nil } func unmarshalDouble(data []byte, value any) error { err := double.Unmarshal(data, value) if err != nil { return wrapUnmarshalError(err, "unmarshal error") } return nil } func marshalDecimal(value any) ([]byte, error) { data, err := decimal.Marshal(value) if err != nil { return nil, wrapMarshalError(err, "marshal error") } return data, nil } func unmarshalDecimal(data []byte, value any) error { if err := decimal.Unmarshal(data, value); err != nil { return wrapUnmarshalError(err, "unmarshal error") } return nil } func marshalTime(value any) ([]byte, error) { data, err := cqltime.Marshal(value) if err != nil { return nil, wrapMarshalError(err, "marshal error") } return data, nil } func unmarshalTime(data []byte, value any) error { err := cqltime.Unmarshal(data, value) if err != nil { return wrapUnmarshalError(err, "unmarshal error") } return nil } func marshalTimestamp(value any) ([]byte, error) { data, err := timestamp.Marshal(value) if err != nil { return nil, wrapMarshalError(err, "marshal error") } return data, nil } func unmarshalTimestamp(data []byte, value any) error { err := timestamp.Unmarshal(data, value) if err != nil { return wrapUnmarshalError(err, "unmarshal error") } return nil } func marshalDate(value any) ([]byte, error) { data, err := date.Marshal(value) if err != nil { return nil, wrapMarshalError(err, "marshal error") } return data, nil } func unmarshalDate(data []byte, value any) error { err := date.Unmarshal(data, value) if err != nil { return wrapUnmarshalError(err, "unmarshal error") } return nil } func marshalDuration(value any) ([]byte, error) { switch uv := value.(type) { case Duration: value = duration.Duration(uv) case *Duration: value = (*duration.Duration)(uv) } data, err := duration.Marshal(value) if err != nil { return nil, wrapMarshalError(err, "marshal error") } return data, nil } func unmarshalDuration(data []byte, value any) error { switch uv := value.(type) { case *Duration: value = (*duration.Duration)(uv) case **Duration: if uv == nil { value = (**duration.Duration)(nil) } else { value = (**duration.Duration)(unsafe.Pointer(uv)) } } err := duration.Unmarshal(data, value) if err != nil { return wrapUnmarshalError(err, "unmarshal error") } return nil } func writeCollectionSize(info CollectionType, n int, buf *bytes.Buffer) error { if n > math.MaxInt32 { return marshalErrorf("marshal: collection too large") } buf.WriteByte(byte(n >> 24)) buf.WriteByte(byte(n >> 16)) buf.WriteByte(byte(n >> 8)) buf.WriteByte(byte(n)) return nil } func marshalList(info TypeInfo, value any) ([]byte, error) { listInfo, ok := info.(CollectionType) if !ok { return nil, marshalErrorf("marshal: can not marshal non collection type into list") } if value == nil { return nil, nil } else if _, ok := value.(unsetColumn); ok { return nil, nil } rv := reflect.ValueOf(value) t := rv.Type() k := t.Kind() if k == reflect.Slice && rv.IsNil() { return nil, nil } switch k { case reflect.Slice, reflect.Array: buf := &bytes.Buffer{} n := rv.Len() if err := writeCollectionSize(listInfo, n, buf); err != nil { return nil, err } for i := 0; i < n; i++ { item, err := Marshal(listInfo.Elem, rv.Index(i).Interface()) if err != nil { return nil, err } itemLen := len(item) // Set the value to null for supported protocols if item == nil { itemLen = -1 } if err := writeCollectionSize(listInfo, itemLen, buf); err != nil { return nil, err } buf.Write(item) } return buf.Bytes(), nil case reflect.Map: elem := t.Elem() if elem.Kind() == reflect.Struct && elem.NumField() == 0 { rkeys := rv.MapKeys() keys := make([]any, len(rkeys)) for i := 0; i < len(keys); i++ { keys[i] = rkeys[i].Interface() } return marshalList(listInfo, keys) } } return nil, marshalErrorf("can not marshal %T into %s", value, info) } func readCollectionSize(info CollectionType, data []byte) (size, read int, err error) { if len(data) < 4 { return 0, 0, unmarshalErrorf("unmarshal list: unexpected eof") } size = int(int32(data[0])<<24 | int32(data[1])<<16 | int32(data[2])<<8 | int32(data[3])) read = 4 return } func unmarshalList(info TypeInfo, data []byte, value any) error { listInfo, ok := info.(CollectionType) if !ok { return unmarshalErrorf("unmarshal: can not unmarshal none collection type into list") } rv := reflect.ValueOf(value) if rv.Kind() != reflect.Ptr { return unmarshalErrorf("can not unmarshal into non-pointer %T", value) } rv = rv.Elem() t := rv.Type() k := t.Kind() // Handle *any destination if k == reflect.Interface { if t.NumMethod() != 0 { return unmarshalErrorf("can not unmarshal into non-empty interface %T", value) } // Create a properly typed slice based on the element type elemGoType, err := goType(listInfo.Elem) if err != nil { return unmarshalErrorf("unmarshal list: cannot determine element type: %v", err) } t = reflect.SliceOf(elemGoType) k = reflect.Slice } switch k { case reflect.Slice, reflect.Array: if data == nil { if k == reflect.Array { return unmarshalErrorf("unmarshal list: can not store nil in array value") } if rv.IsNil() { return nil } rv.Set(reflect.Zero(t)) return nil } n, p, err := readCollectionSize(listInfo, data) if err != nil { return err } data = data[p:] if k == reflect.Array { if rv.Len() != n { return unmarshalErrorf("unmarshal list: array with wrong size") } } else { rv.Set(reflect.MakeSlice(t, n, n)) // If rv was an interface, get the underlying slice if rv.Kind() == reflect.Interface { rv = rv.Elem() } } for i := 0; i < n; i++ { m, p, err := readCollectionSize(listInfo, data) if err != nil { return err } data = data[p:] // In case m < 0, the value is null, and unmarshalData should be nil. var unmarshalData []byte if m >= 0 { if len(data) < m { return unmarshalErrorf("unmarshal list: unexpected eof") } unmarshalData = data[:m] data = data[m:] } if err := Unmarshal(listInfo.Elem, unmarshalData, rv.Index(i).Addr().Interface()); err != nil { return err } } return nil } return unmarshalErrorf("can not unmarshal %s into %T. Accepted types: *slice, *array, *any.", info, value) } func marshalVector(info VectorType, value any) ([]byte, error) { if value == nil { return nil, nil } else if _, ok := value.(unsetColumn); ok { return nil, nil } rv := reflect.ValueOf(value) t := rv.Type() k := t.Kind() if k == reflect.Slice && rv.IsNil() { return nil, nil } switch k { case reflect.Slice, reflect.Array: n := rv.Len() if n != info.Dimensions { return nil, marshalErrorf("expected vector with %d dimensions, received %d", info.Dimensions, n) } isLengthType := isVectorVariableLengthType(info.SubType) buf := &bytes.Buffer{} if !isLengthType { if elemSize := vectorFixedElemSize(info.SubType); elemSize > 0 { if needed := int64(n) * int64(elemSize); needed > 0 && needed <= math.MaxInt32 { buf.Grow(int(needed)) } } } for i := 0; i < n; i++ { item, err := Marshal(info.SubType, rv.Index(i).Interface()) if err != nil { return nil, err } if isLengthType { writeUnsignedVInt(buf, uint64(len(item))) } buf.Write(item) } return buf.Bytes(), nil } return nil, marshalErrorf("can not marshal %T into %s. Accepted types: slice, array.", value, info) } func unmarshalVector(info VectorType, data []byte, value any) error { rv := reflect.ValueOf(value) if rv.Kind() != reflect.Ptr { return unmarshalErrorf("can not unmarshal into non-pointer %T", value) } rv = rv.Elem() t := rv.Type() if t.Kind() == reflect.Interface { if t.NumMethod() != 0 { return unmarshalErrorf("can not unmarshal into non-empty interface %T", value) } t = reflect.TypeOf(info.Zero()) } k := t.Kind() switch k { case reflect.Slice, reflect.Array: if data == nil { if k == reflect.Array { return unmarshalErrorf("unmarshal vector: can not store nil in array value") } if rv.IsNil() { return nil } rv.Set(reflect.Zero(t)) return nil } if info.Dimensions == 0 { if len(data) > 0 { return unmarshalErrorf("unmarshal vector: %d bytes of data for 0-dimension vector", len(data)) } if k == reflect.Array { if rv.Len() != 0 { return unmarshalErrorf("unmarshal vector: array of size %d cannot store vector of 0 dimensions", rv.Len()) } } else if k == reflect.Slice { rv.Set(reflect.MakeSlice(t, 0, 0)) } return nil } if k == reflect.Array { if rv.Len() != info.Dimensions { return unmarshalErrorf("unmarshal vector: array of size %d cannot store vector of %d dimensions", rv.Len(), info.Dimensions) } } else { rv.Set(reflect.MakeSlice(t, info.Dimensions, info.Dimensions)) if rv.Kind() == reflect.Interface { rv = rv.Elem() } } elemSize := len(data) / info.Dimensions isLengthType := isVectorVariableLengthType(info.SubType) for i := 0; i < info.Dimensions; i++ { offset := 0 if isLengthType { m, p, err := readUnsignedVInt(data) if err != nil { return err } elemSize = int(m) offset = p } if offset > 0 { data = data[offset:] } var unmarshalData []byte if elemSize >= 0 { if len(data) < elemSize { return unmarshalErrorf("unmarshal vector: unexpected eof") } unmarshalData = data[:elemSize] data = data[elemSize:] } err := Unmarshal(info.SubType, unmarshalData, rv.Index(i).Addr().Interface()) if err != nil { return unmarshalErrorf("failed to unmarshal %s into %T: %s", info.SubType, unmarshalData, err.Error()) } } return nil } return unmarshalErrorf("can not unmarshal %s into %T. Accepted types: *slice, *array, *any.", info, value) } func vectorFixedElemSize(elemType TypeInfo) int { switch elemType.Type() { case TypeBoolean: return 1 case TypeInt, TypeFloat: return 4 case TypeBigInt, TypeDouble, TypeTimestamp: return 8 case TypeUUID, TypeTimeUUID: return 16 } return 0 } // isVectorVariableLengthType determines if a type requires explicit length serialization within a vector. // Variable-length types need their length encoded (as a vint prefix) before the actual data. // Fixed-length types don't require this prefix. // // This classification must match Cassandra's VectorType behavior. Cassandra's VectorType constructor // selects FixedLengthSerializer vs VariableLengthSerializer based on elementType.isValueLengthFixed(), // which checks whether the type overrides valueLengthIfFixed() to return something other than -1. // // Several types that are conceptually fixed-size do NOT override valueLengthIfFixed() in Cassandra // and are therefore treated as variable-length inside vectors on the wire: // - CounterColumnType (counter) — no valueLengthIfFixed() override // - ShortType (smallint) — no valueLengthIfFixed() override // - ByteType (tinyint) — no valueLengthIfFixed() override // - TimeType (time) — no valueLengthIfFixed() override // - SimpleDateType (date) — no valueLengthIfFixed() override // // gocql must match this to produce wire-compatible encoding, even though these types always // serialize to a known number of bytes. // // Reference: https://github.com/apache/cassandra/blob/trunk/src/java/org/apache/cassandra/db/marshal/VectorType.java func isVectorVariableLengthType(elemType TypeInfo) bool { switch elemType.Type() { case TypeVarchar, TypeAscii, TypeBlob, TypeText, TypeCounter, TypeDuration, TypeDate, TypeTime, TypeDecimal, TypeSmallInt, TypeTinyInt, TypeVarint, TypeInet, TypeList, TypeSet, TypeMap, TypeUDT, TypeTuple: return true case TypeCustom: if vecType, ok := elemType.(VectorType); ok { return isVectorVariableLengthType(vecType.SubType) } return true } return false } func writeUnsignedVInt(buf *bytes.Buffer, v uint64) { numBytes := computeUnsignedVIntSize(v) if numBytes <= 1 { buf.WriteByte(byte(v)) return } extraBytes := numBytes - 1 var tmp = make([]byte, numBytes) for i := extraBytes; i >= 0; i-- { tmp[i] = byte(v) v >>= 8 } tmp[0] |= byte(^(0xff >> uint(extraBytes))) buf.Write(tmp) } func readUnsignedVInt(data []byte) (uint64, int, error) { if len(data) <= 0 { return 0, 0, errors.New("unexpected eof") } firstByte := data[0] if firstByte&0x80 == 0 { return uint64(firstByte), 1, nil } numBytes := bits.LeadingZeros32(uint32(^firstByte)) - 24 ret := uint64(firstByte & (0xff >> uint(numBytes))) if len(data) < numBytes+1 { return 0, 0, fmt.Errorf("data expect to have %d bytes, but it has only %d", numBytes+1, len(data)) } for i := 0; i < numBytes; i++ { ret <<= 8 ret |= uint64(data[i+1] & 0xff) } return ret, numBytes + 1, nil } func computeUnsignedVIntSize(v uint64) int { lead0 := bits.LeadingZeros64(v) return (639 - lead0*9) >> 6 } func marshalMap(info TypeInfo, value any) ([]byte, error) { mapInfo, ok := info.(CollectionType) if !ok { return nil, marshalErrorf("marshal: can not marshal none collection type into map") } if value == nil { return nil, nil } else if _, ok := value.(unsetColumn); ok { return nil, nil } rv := reflect.ValueOf(value) t := rv.Type() if t.Kind() != reflect.Map { return nil, marshalErrorf("can not marshal %T into %s", value, info) } if rv.IsNil() { return nil, nil } buf := &bytes.Buffer{} n := rv.Len() if err := writeCollectionSize(mapInfo, n, buf); err != nil { return nil, err } keys := rv.MapKeys() for _, key := range keys { item, err := Marshal(mapInfo.Key, key.Interface()) if err != nil { return nil, err } itemLen := len(item) // Set the key to null for supported protocols if item == nil { itemLen = -1 } if err := writeCollectionSize(mapInfo, itemLen, buf); err != nil { return nil, err } buf.Write(item) item, err = Marshal(mapInfo.Elem, rv.MapIndex(key).Interface()) if err != nil { return nil, err } itemLen = len(item) // Set the value to null for supported protocols if item == nil { itemLen = -1 } if err := writeCollectionSize(mapInfo, itemLen, buf); err != nil { return nil, err } buf.Write(item) } return buf.Bytes(), nil } func unmarshalMap(info TypeInfo, data []byte, value any) error { mapInfo, ok := info.(CollectionType) if !ok { return unmarshalErrorf("unmarshal: can not unmarshal none collection type into map") } rv := reflect.ValueOf(value) if rv.Kind() != reflect.Ptr { return unmarshalErrorf("can not unmarshal into non-pointer %T", value) } rv = rv.Elem() t := rv.Type() // Handle *any destination if t.Kind() == reflect.Interface { if t.NumMethod() != 0 { return unmarshalErrorf("can not unmarshal into non-empty interface %T", value) } // Create a properly typed map based on the key and element types keyGoType, err := goType(mapInfo.Key) if err != nil { return unmarshalErrorf("unmarshal map: cannot determine key type: %v", err) } elemGoType, err := goType(mapInfo.Elem) if err != nil { return unmarshalErrorf("unmarshal map: cannot determine element type: %v", err) } t = reflect.MapOf(keyGoType, elemGoType) } if t.Kind() != reflect.Map { return unmarshalErrorf("can not unmarshal %s into %T. Accepted types: *map, *any.", info, value) } if data == nil { rv.Set(reflect.Zero(t)) return nil } n, p, err := readCollectionSize(mapInfo, data) if err != nil { return err } if n < 0 { return unmarshalErrorf("negative map size %d", n) } rv.Set(reflect.MakeMapWithSize(t, n)) // If rv was an interface, get the underlying map if rv.Kind() == reflect.Interface { rv = rv.Elem() } data = data[p:] for i := 0; i < n; i++ { m, p, err := readCollectionSize(mapInfo, data) if err != nil { return err } data = data[p:] key := reflect.New(t.Key()) // In case m < 0, the key is null, and unmarshalData should be nil. var unmarshalData []byte if m >= 0 { if len(data) < m { return unmarshalErrorf("unmarshal map: unexpected eof") } unmarshalData = data[:m] data = data[m:] } if err := Unmarshal(mapInfo.Key, unmarshalData, key.Interface()); err != nil { return err } m, p, err = readCollectionSize(mapInfo, data) if err != nil { return err } data = data[p:] val := reflect.New(t.Elem()) // In case m < 0, the value is null, and unmarshalData should be nil. unmarshalData = nil if m >= 0 { if len(data) < m { return unmarshalErrorf("unmarshal map: unexpected eof") } unmarshalData = data[:m] data = data[m:] } if err := Unmarshal(mapInfo.Elem, unmarshalData, val.Interface()); err != nil { return err } rv.SetMapIndex(key.Elem(), val.Elem()) } return nil } func marshalUUID(value any) ([]byte, error) { switch uv := value.(type) { case UUID: value = [16]byte(uv) case *UUID: value = (*[16]byte)(uv) } data, err := uuid.Marshal(value) if err != nil { return nil, wrapMarshalError(err, "marshal error") } return data, nil } func unmarshalUUID(data []byte, value any) error { switch uv := value.(type) { case *UUID: value = (*[16]byte)(uv) case **UUID: if uv == nil { value = (**[16]byte)(nil) } else { value = (**[16]byte)(unsafe.Pointer(uv)) } } err := uuid.Unmarshal(data, value) if err != nil { return wrapUnmarshalError(err, "unmarshal error") } return nil } func marshalTimeUUID(value any) ([]byte, error) { switch uv := value.(type) { case UUID: value = [16]byte(uv) case *UUID: value = (*[16]byte)(uv) } data, err := timeuuid.Marshal(value) if err != nil { return nil, wrapMarshalError(err, "marshal error") } return data, nil } func unmarshalTimeUUID(data []byte, value any) error { switch uv := value.(type) { case *UUID: value = (*[16]byte)(uv) case **UUID: if uv == nil { value = (**[16]byte)(nil) } else { value = (**[16]byte)(unsafe.Pointer(uv)) } } err := timeuuid.Unmarshal(data, value) if err != nil { return wrapUnmarshalError(err, "unmarshal error") } return nil } func marshalInet(value any) ([]byte, error) { data, err := inet.Marshal(value) if err != nil { return nil, wrapMarshalError(err, "marshal error") } return data, nil } func unmarshalInet(data []byte, value any) error { err := inet.Unmarshal(data, value) if err != nil { return wrapUnmarshalError(err, "unmarshal error") } return nil } func marshalTuple(info TypeInfo, value any) ([]byte, error) { tuple := info.(TupleTypeInfo) switch v := value.(type) { case unsetColumn: return nil, unmarshalErrorf("Invalid request: UnsetValue is unsupported for tuples") case []any: if len(v) != len(tuple.Elems) { return nil, unmarshalErrorf("cannont marshal tuple: wrong number of elements") } var buf []byte for i, elem := range v { if elem == nil { buf = appendIntNeg1(buf) continue } data, err := Marshal(tuple.Elems[i], elem) if err != nil { return nil, err } n := len(data) buf = appendInt(buf, int32(n)) buf = append(buf, data...) } return buf, nil } rv := reflect.ValueOf(value) t := rv.Type() k := t.Kind() switch k { case reflect.Struct: if v := t.NumField(); v != len(tuple.Elems) { return nil, marshalErrorf("can not marshal tuple into struct %v, not enough fields have %d need %d", t, v, len(tuple.Elems)) } var buf []byte for i, elem := range tuple.Elems { field := rv.Field(i) if field.Kind() == reflect.Ptr && field.IsNil() { buf = appendIntNeg1(buf) continue } data, err := Marshal(elem, field.Interface()) if err != nil { return nil, err } n := len(data) buf = appendInt(buf, int32(n)) buf = append(buf, data...) } return buf, nil case reflect.Slice, reflect.Array: size := rv.Len() if size != len(tuple.Elems) { return nil, marshalErrorf("can not marshal tuple into %v of length %d need %d elements", k, size, len(tuple.Elems)) } var buf []byte for i, elem := range tuple.Elems { item := rv.Index(i) if item.Kind() == reflect.Ptr && item.IsNil() { buf = appendIntNeg1(buf) continue } data, err := Marshal(elem, item.Interface()) if err != nil { return nil, err } n := len(data) buf = appendInt(buf, int32(n)) buf = append(buf, data...) } return buf, nil } return nil, marshalErrorf("cannot marshal %T into %s", value, tuple) } func readBytes(p []byte) ([]byte, []byte) { // TODO: really should use a framer size := readInt(p) p = p[4:] if size < 0 { return nil, p } return p[:size], p[size:] } // currently only support unmarshal into a list of values, this makes it possible // to support tuples without changing the query API. In the future this can be extend // to allow unmarshalling into custom tuple types. func unmarshalTuple(info TypeInfo, data []byte, value any) error { if v, ok := value.(Unmarshaler); ok { return v.UnmarshalCQL(info, data) } tuple := info.(TupleTypeInfo) switch v := value.(type) { case []any: for i, elem := range tuple.Elems { // each element inside data is a [bytes] var p []byte if len(data) >= 4 { p, data = readBytes(data) } err := Unmarshal(elem, p, v[i]) if err != nil { return err } } return nil } rv := reflect.ValueOf(value) if rv.Kind() != reflect.Ptr { return unmarshalErrorf("can not unmarshal into non-pointer %T", value) } rv = rv.Elem() t := rv.Type() k := t.Kind() switch k { case reflect.Struct: if v := t.NumField(); v != len(tuple.Elems) { return unmarshalErrorf("can not unmarshal tuple into struct %v, not enough fields have %d need %d", t, v, len(tuple.Elems)) } for i, elem := range tuple.Elems { var p []byte if len(data) >= 4 { p, data = readBytes(data) } v, err := elem.NewWithError() if err != nil { return err } if err := Unmarshal(elem, p, v); err != nil { return err } switch rv.Field(i).Kind() { case reflect.Ptr: if p != nil { rv.Field(i).Set(reflect.ValueOf(v)) } else { rv.Field(i).Set(reflect.Zero(reflect.TypeOf(v))) } default: rv.Field(i).Set(reflect.ValueOf(v).Elem()) } } return nil case reflect.Slice, reflect.Array: if k == reflect.Array { size := rv.Len() if size != len(tuple.Elems) { return unmarshalErrorf("can not unmarshal tuple into array of length %d need %d elements", size, len(tuple.Elems)) } } else { rv.Set(reflect.MakeSlice(t, len(tuple.Elems), len(tuple.Elems))) } for i, elem := range tuple.Elems { var p []byte if len(data) >= 4 { p, data = readBytes(data) } v, err := elem.NewWithError() if err != nil { return err } if err := Unmarshal(elem, p, v); err != nil { return err } switch rv.Index(i).Kind() { case reflect.Ptr: if p != nil { rv.Index(i).Set(reflect.ValueOf(v)) } else { rv.Index(i).Set(reflect.Zero(reflect.TypeOf(v))) } default: rv.Index(i).Set(reflect.ValueOf(v).Elem()) } } return nil } return unmarshalErrorf("cannot unmarshal %s into %T", info, value) } // UDTMarshaler is an interface which should be implemented by users wishing to // handle encoding UDT types to sent to Cassandra. Note: due to current implentations // methods defined for this interface must be value receivers not pointer receivers. type UDTMarshaler interface { // MarshalUDT will be called for each field in the the UDT returned by Cassandra, // the implementor should marshal the type to return by for example calling // Marshal. MarshalUDT(name string, info TypeInfo) ([]byte, error) } // UDTUnmarshaler should be implemented by users wanting to implement custom // UDT unmarshaling. type UDTUnmarshaler interface { // UnmarshalUDT will be called for each field in the UDT return by Cassandra, // the implementor should unmarshal the data into the value of their chosing, // for example by calling Unmarshal. // // The data []byte slice is only valid for the duration of the call. // The backing memory may be reused after the call returns. // Implementations that need to retain data must copy it. UnmarshalUDT(name string, info TypeInfo, data []byte) error } func marshalUDT(info TypeInfo, value any) ([]byte, error) { udt := info.(UDTTypeInfo) switch v := value.(type) { case Marshaler: return v.MarshalCQL(info) case unsetColumn: return nil, unmarshalErrorf("invalid request: UnsetValue is unsupported for user defined types") case UDTMarshaler: var buf []byte for _, e := range udt.Elements { data, err := v.MarshalUDT(e.Name, e.Type) if err != nil { return nil, err } buf = appendBytes(buf, data) } return buf, nil case map[string]any: var buf []byte for _, e := range udt.Elements { val, ok := v[e.Name] var data []byte if ok { var err error data, err = Marshal(e.Type, val) if err != nil { return nil, err } } buf = appendBytes(buf, data) } return buf, nil } k := reflect.ValueOf(value) if k.Kind() == reflect.Ptr { if k.IsNil() { return nil, marshalErrorf("cannot marshal %T into %s", value, info) } k = k.Elem() } if k.Kind() != reflect.Struct || !k.IsValid() { return nil, marshalErrorf("cannot marshal %T into %s", value, info) } fields := make(map[string]reflect.Value) t := reflect.TypeOf(value) for i := 0; i < t.NumField(); i++ { sf := t.Field(i) if tag := sf.Tag.Get("cql"); tag != "" { fields[tag] = k.Field(i) } } var buf []byte for _, e := range udt.Elements { f, ok := fields[e.Name] if !ok { f = k.FieldByName(e.Name) } var data []byte if f.IsValid() && f.CanInterface() { var err error data, err = Marshal(e.Type, f.Interface()) if err != nil { return nil, err } } buf = appendBytes(buf, data) } return buf, nil } func unmarshalUDT(info TypeInfo, data []byte, value any) error { switch v := value.(type) { case Unmarshaler: return v.UnmarshalCQL(info, data) case UDTUnmarshaler: udt := info.(UDTTypeInfo) for id, e := range udt.Elements { if len(data) == 0 { return nil } if len(data) < 4 { return unmarshalErrorf("can not unmarshal %s: field [%d]%s: unexpected eof", info, id, e.Name) } var p []byte p, data = readBytes(data) if err := v.UnmarshalUDT(e.Name, e.Type, p); err != nil { return err } } return nil case *map[string]any: udt := info.(UDTTypeInfo) rv := reflect.ValueOf(value) if rv.Kind() != reflect.Ptr { return unmarshalErrorf("can not unmarshal into non-pointer %T", value) } rv = rv.Elem() t := rv.Type() if t.Kind() != reflect.Map { return unmarshalErrorf("can not unmarshal %s into %T", info, value) } else if data == nil { rv.Set(reflect.Zero(t)) return nil } rv.Set(reflect.MakeMap(t)) m := *v for id, e := range udt.Elements { if len(data) == 0 { return nil } if len(data) < 4 { return unmarshalErrorf("can not unmarshal %s: field [%d]%s: unexpected eof", info, id, e.Name) } valType, err := goType(e.Type) if err != nil { return unmarshalErrorf("can not unmarshal %s: %v", info, err) } val := reflect.New(valType) var p []byte p, data = readBytes(data) if err := Unmarshal(e.Type, p, val.Interface()); err != nil { return err } m[e.Name] = val.Elem().Interface() } return nil } rv := reflect.ValueOf(value) if rv.Kind() != reflect.Ptr { return unmarshalErrorf("can not unmarshal into non-pointer %T", value) } k := rv.Elem() if k.Kind() != reflect.Struct || !k.IsValid() { return unmarshalErrorf("cannot unmarshal %s into %T", info, value) } if len(data) == 0 { if k.CanSet() { k.Set(reflect.Zero(k.Type())) } return nil } t := k.Type() fields := make(map[string]reflect.Value, t.NumField()) for i := 0; i < t.NumField(); i++ { sf := t.Field(i) if tag := sf.Tag.Get("cql"); tag != "" { fields[tag] = k.Field(i) } } udt := info.(UDTTypeInfo) for id, e := range udt.Elements { if len(data) == 0 { return nil } if len(data) < 4 { // UDT def does not match the column value return unmarshalErrorf("can not unmarshal %s: field [%d]%s: unexpected eof", info, id, e.Name) } var p []byte p, data = readBytes(data) f, ok := fields[e.Name] if !ok { f = k.FieldByName(e.Name) if f == emptyValue { //nolint:govet // no other way to do that // skip fields which exist in the UDT but not in // the struct passed in continue } } if !f.IsValid() || !f.CanAddr() { return unmarshalErrorf("cannot unmarshal %s into %T: field %v is not valid", info, value, e.Name) } fk := f.Addr().Interface() if err := Unmarshal(e.Type, p, fk); err != nil { return err } } return nil } // TypeInfo describes a Cassandra specific data type. type TypeInfo interface { Type() Type Version() byte Custom() string // NewWithError creates a pointer to an empty version of whatever type // is referenced by the TypeInfo receiver. // // If there is no corresponding Go type for the CQL type, NewWithError returns an error. NewWithError() (any, error) } type NativeType struct { //only used for TypeCustom custom string typ Type proto byte } func NewNativeType(proto byte, typ Type) NativeType { return NativeType{proto: proto, typ: typ, custom: ""} } func NewCustomType(proto byte, typ Type, custom string) NativeType { return NativeType{proto: proto, typ: typ, custom: custom} } func (t NativeType) NewWithError() (any, error) { // Fast path for common types to avoid reflection overhead switch t.typ { case TypeInt: return new(int), nil case TypeBigInt, TypeCounter: return new(int64), nil case TypeVarchar, TypeAscii, TypeText, TypeInet: return new(string), nil case TypeBoolean: return new(bool), nil case TypeFloat: return new(float32), nil case TypeDouble: return new(float64), nil case TypeTimestamp, TypeDate: return new(time.Time), nil case TypeUUID, TypeTimeUUID: return new(UUID), nil case TypeBlob: return new([]byte), nil case TypeSmallInt: return new(int16), nil case TypeTinyInt: return new(int8), nil case TypeTime: return new(time.Duration), nil case TypeDecimal: return new(*inf.Dec), nil case TypeVarint: return new(*big.Int), nil case TypeDuration: return new(Duration), nil } // Fallback to reflection for complex/custom types typ, err := goType(t) if err != nil { return nil, err } return reflect.New(typ).Interface(), nil } func (t NativeType) Type() Type { return t.typ } func (t NativeType) Version() byte { return t.proto } func (t NativeType) Custom() string { return t.custom } func (t NativeType) String() string { switch t.typ { case TypeCustom: return fmt.Sprintf("%s(%s)", t.typ, t.custom) default: return t.typ.String() } } func NewCollectionType(m NativeType, key, elem TypeInfo) CollectionType { return CollectionType{ NativeType: m, Key: key, Elem: elem, } } type CollectionType struct { // Key is used only for TypeMap Key TypeInfo // Elem is used for TypeMap, TypeList and TypeSet Elem TypeInfo NativeType } type VectorType struct { SubType TypeInfo NativeType Dimensions int } // Zero returns the zero value for the vector CQL type. func (v VectorType) Zero() any { t, e := v.SubType.NewWithError() if e != nil { return nil } return reflect.Zero(reflect.SliceOf(reflect.TypeOf(t))).Interface() } func (t CollectionType) NewWithError() (any, error) { // Fast path for common collection patterns switch t.typ { case TypeList, TypeSet: // Fast path for lists/sets of primitive types if nt, ok := t.Elem.(NativeType); ok { switch nt.typ { case TypeInt: return new([]int), nil case TypeBigInt, TypeCounter: return new([]int64), nil case TypeText, TypeVarchar, TypeAscii: return new([]string), nil case TypeBoolean: return new([]bool), nil case TypeFloat: return new([]float32), nil case TypeDouble: return new([]float64), nil case TypeUUID, TypeTimeUUID: return new([]UUID), nil case TypeTimestamp, TypeDate: return new([]time.Time), nil case TypeSmallInt: return new([]int16), nil case TypeTinyInt: return new([]int8), nil case TypeBlob: return new([][]byte), nil } } case TypeMap: // Fast path for maps with primitive key/value types if keyNT, keyOk := t.Key.(NativeType); keyOk { if valNT, valOk := t.Elem.(NativeType); valOk { // String keys are most common if keyNT.typ == TypeText || keyNT.typ == TypeVarchar { switch valNT.typ { case TypeInt: return new(map[string]int), nil case TypeBigInt: return new(map[string]int64), nil case TypeText, TypeVarchar: return new(map[string]string), nil case TypeBoolean: return new(map[string]bool), nil case TypeFloat: return new(map[string]float32), nil case TypeDouble: return new(map[string]float64), nil case TypeUUID: return new(map[string]UUID), nil } } // Int keys if keyNT.typ == TypeInt { switch valNT.typ { case TypeText, TypeVarchar: return new(map[int]string), nil case TypeInt: return new(map[int]int), nil case TypeFloat: return new(map[int]float32), nil } } } } } // Fallback to reflection for complex types typ, err := goType(t) if err != nil { return nil, err } return reflect.New(typ).Interface(), nil } func (t CollectionType) String() string { switch t.typ { case TypeMap: return fmt.Sprintf("%s(%s, %s)", t.typ, t.Key, t.Elem) case TypeList, TypeSet: return fmt.Sprintf("%s(%s)", t.typ, t.Elem) case TypeCustom: return fmt.Sprintf("%s(%s)", t.typ, t.custom) default: return t.typ.String() } } func NewTupleType(n NativeType, elems ...TypeInfo) TupleTypeInfo { return TupleTypeInfo{ NativeType: n, Elems: elems, } } type TupleTypeInfo struct { Elems []TypeInfo NativeType } func (t TupleTypeInfo) String() string { var buf bytes.Buffer buf.WriteString(fmt.Sprintf("%s(", t.typ)) for _, elem := range t.Elems { buf.WriteString(fmt.Sprintf("%s, ", elem)) } buf.Truncate(buf.Len() - 2) buf.WriteByte(')') return buf.String() } func (t TupleTypeInfo) NewWithError() (any, error) { // Tuples scan into *[]any — no reflection needed. return new([]any), nil } type UDTField struct { Type TypeInfo Name string } func NewUDTType(proto byte, name, keySpace string, elems ...UDTField) UDTTypeInfo { return UDTTypeInfo{ NativeType: NativeType{proto: proto, typ: TypeUDT, custom: ""}, Name: name, KeySpace: keySpace, Elements: elems, } } type UDTTypeInfo struct { KeySpace string Name string Elements []UDTField NativeType } func (t UDTTypeInfo) NewWithError() (any, error) { typ, err := goType(t) if err != nil { return nil, err } return reflect.New(typ).Interface(), nil } func (t UDTTypeInfo) String() string { buf := &bytes.Buffer{} fmt.Fprintf(buf, "%s.%s{", t.KeySpace, t.Name) first := true for _, e := range t.Elements { if !first { fmt.Fprint(buf, ",") } else { first = false } fmt.Fprintf(buf, "%s=%v", e.Name, e.Type) } fmt.Fprint(buf, "}") return buf.String() } // String returns a human readable name for the Cassandra datatype // described by t. // Type is the identifier of a Cassandra internal datatype. type Type int const ( TypeCustom Type = 0x0000 TypeAscii Type = 0x0001 TypeBigInt Type = 0x0002 TypeBlob Type = 0x0003 TypeBoolean Type = 0x0004 TypeCounter Type = 0x0005 TypeDecimal Type = 0x0006 TypeDouble Type = 0x0007 TypeFloat Type = 0x0008 TypeInt Type = 0x0009 TypeText Type = 0x000A TypeTimestamp Type = 0x000B TypeUUID Type = 0x000C TypeVarchar Type = 0x000D TypeVarint Type = 0x000E TypeTimeUUID Type = 0x000F TypeInet Type = 0x0010 TypeDate Type = 0x0011 TypeTime Type = 0x0012 TypeSmallInt Type = 0x0013 TypeTinyInt Type = 0x0014 TypeDuration Type = 0x0015 TypeList Type = 0x0020 TypeMap Type = 0x0021 TypeSet Type = 0x0022 TypeUDT Type = 0x0030 TypeTuple Type = 0x0031 ) // String returns the name of the identifier. func (t Type) String() string { switch t { case TypeCustom: return "custom" case TypeAscii: return "ascii" case TypeBigInt: return "bigint" case TypeBlob: return "blob" case TypeBoolean: return "boolean" case TypeCounter: return "counter" case TypeDecimal: return "decimal" case TypeDouble: return "double" case TypeFloat: return "float" case TypeInt: return "int" case TypeText: return "text" case TypeTimestamp: return "timestamp" case TypeUUID: return "uuid" case TypeVarchar: return "varchar" case TypeTimeUUID: return "timeuuid" case TypeInet: return "inet" case TypeDate: return "date" case TypeDuration: return "duration" case TypeTime: return "time" case TypeSmallInt: return "smallint" case TypeTinyInt: return "tinyint" case TypeList: return "list" case TypeMap: return "map" case TypeSet: return "set" case TypeVarint: return "varint" case TypeTuple: return "tuple" default: return fmt.Sprintf("unknown_type_%d", t) } } type MarshalError struct { cause error msg string } func (m MarshalError) Error() string { if m.cause != nil { return m.msg + ": " + m.cause.Error() } return m.msg } func (m MarshalError) Cause() error { return m.cause } func (m MarshalError) Unwrap() error { return m.cause } func marshalErrorf(format string, args ...any) MarshalError { return MarshalError{msg: fmt.Sprintf(format, args...)} } func wrapMarshalError(err error, msg string) MarshalError { return MarshalError{msg: msg, cause: err} } func wrapMarshalErrorf(err error, format string, a ...any) MarshalError { return MarshalError{msg: fmt.Sprintf(format, a...), cause: err} } type UnmarshalError struct { cause error msg string } func (m UnmarshalError) Error() string { if m.cause != nil { return m.msg + ": " + m.cause.Error() } return m.msg } func (m UnmarshalError) Cause() error { return m.cause } func (m UnmarshalError) Unwrap() error { return m.cause } func unmarshalErrorf(format string, args ...any) UnmarshalError { return UnmarshalError{msg: fmt.Sprintf(format, args...)} } func wrapUnmarshalError(err error, msg string) UnmarshalError { return UnmarshalError{msg: msg, cause: err} } func wrapUnmarshalErrorf(err error, format string, a ...any) UnmarshalError { return UnmarshalError{msg: fmt.Sprintf(format, a...), cause: err} } ================================================ FILE: marshal_test.go ================================================ //go:build unit // +build unit /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "bytes" "encoding/binary" "fmt" "math" "math/big" "net" "reflect" "strings" "testing" "gopkg.in/inf.v0" ) type AliasInt int type AliasUint uint type AliasUint8 uint8 type AliasUint16 uint16 type AliasUint32 uint32 type AliasUint64 uint64 var marshalTests = []struct { Info TypeInfo Data []byte Value any MarshalError error UnmarshalError error }{ { CollectionType{ NativeType: NativeType{proto: protoVersion3, typ: TypeList}, Elem: NativeType{proto: protoVersion3, typ: TypeInt}, }, []byte("\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x02"), func() *[]int { l := []int{1, 2} return &l }(), nil, nil, }, } var unmarshalTests = []struct { Info TypeInfo Data []byte Value any UnmarshalError error }{ { CollectionType{ NativeType: NativeType{proto: protoVersion3, typ: TypeList}, Elem: NativeType{proto: protoVersion3, typ: TypeInt}, }, []byte("\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00"), // truncated data func() *[]int { l := []int{1, 2} return &l }(), unmarshalErrorf("unmarshal list: unexpected eof"), }, } func decimalize(s string) *inf.Dec { i, _ := new(inf.Dec).SetString(s) return i } func bigintize(s string) *big.Int { i, _ := new(big.Int).SetString(s, 10) return i } func TestMarshal_Encode(t *testing.T) { t.Parallel() for i, test := range marshalTests { if test.MarshalError == nil { data, err := Marshal(test.Info, test.Value) if err != nil { t.Errorf("marshalTest[%d]: %v", i, err) continue } if !bytes.Equal(data, test.Data) { t.Errorf("marshalTest[%d]: expected %q, got %q (%#v)", i, test.Data, data, test.Value) } } else { if _, err := Marshal(test.Info, test.Value); err != test.MarshalError { t.Errorf("unmarshalTest[%d] (%v=>%t): %#v returned error %#v, want %#v.", i, test.Info, test.Value, test.Value, err, test.MarshalError) } } } } func TestMarshal_Decode(t *testing.T) { t.Parallel() for i, test := range marshalTests { if test.UnmarshalError == nil { v := reflect.New(reflect.TypeOf(test.Value)) err := Unmarshal(test.Info, test.Data, v.Interface()) if err != nil { t.Errorf("unmarshalTest[%d] (%v=>%T): %v", i, test.Info, test.Value, err) continue } if !reflect.DeepEqual(v.Elem().Interface(), test.Value) { t.Errorf("unmarshalTest[%d] (%v=>%T): expected %#v, got %#v.", i, test.Info, test.Value, test.Value, v.Elem().Interface()) } } else { if err := Unmarshal(test.Info, test.Data, test.Value); err != test.UnmarshalError { t.Errorf("unmarshalTest[%d] (%v=>%T): %#v returned error %#v, want %#v.", i, test.Info, test.Value, test.Value, err, test.UnmarshalError) } } } for i, test := range unmarshalTests { v := reflect.New(reflect.TypeOf(test.Value)) if test.UnmarshalError == nil { err := Unmarshal(test.Info, test.Data, v.Interface()) if err != nil { t.Errorf("unmarshalTest[%d] (%v=>%T): %v", i, test.Info, test.Value, err) continue } if !reflect.DeepEqual(v.Elem().Interface(), test.Value) { t.Errorf("unmarshalTest[%d] (%v=>%T): expected %#v, got %#v.", i, test.Info, test.Value, test.Value, v.Elem().Interface()) } } else { if err := Unmarshal(test.Info, test.Data, v.Interface()); err != test.UnmarshalError { t.Errorf("unmarshalTest[%d] (%v=>%T): %#v returned error %#v, want %#v.", i, test.Info, test.Value, test.Value, err, test.UnmarshalError) } } } } func equalStringPointerSlice(leftList, rightList []*string) bool { if len(leftList) != len(rightList) { return false } for index := range leftList { if !reflect.DeepEqual(rightList[index], leftList[index]) { return false } } return true } func TestMarshalList(t *testing.T) { t.Parallel() typeInfoV3 := CollectionType{ NativeType: NativeType{proto: protoVersion3, typ: TypeList}, Elem: NativeType{proto: protoVersion3, typ: TypeVarchar}, } type tc struct { typeInfo CollectionType input []*string expected []*string } valueA := "valueA" valueB := "valueB" valueEmpty := "" testCases := []tc{ { typeInfo: typeInfoV3, input: []*string{&valueEmpty}, expected: []*string{&valueEmpty}, }, { typeInfo: typeInfoV3, input: []*string{nil}, expected: []*string{nil}, }, { typeInfo: typeInfoV3, input: []*string{&valueA, nil, &valueB}, expected: []*string{&valueA, nil, &valueB}, }, } listDatas := [][]byte{} for _, c := range testCases { listData, marshalErr := Marshal(c.typeInfo, c.input) if nil != marshalErr { t.Errorf("Error marshal %+v of type %+v: %s", c.input, c.typeInfo, marshalErr) } listDatas = append(listDatas, listData) } outputLists := [][]*string{} var outputList []*string for i, listData := range listDatas { if unmarshalErr := Unmarshal(testCases[i].typeInfo, listData, &outputList); nil != unmarshalErr { t.Error(unmarshalErr) } resultList := []any{} for i := range outputList { if outputList[i] != nil { resultList = append(resultList, *outputList[i]) } else { resultList = append(resultList, nil) } } outputLists = append(outputLists, outputList) } for index, c := range testCases { outputList := outputLists[index] if !equalStringPointerSlice(c.expected, outputList) { t.Errorf("Lists %+v not equal to lists %+v, but should", c.expected, outputList) } } } type CustomString string func (c CustomString) MarshalCQL(info TypeInfo) ([]byte, error) { return []byte(strings.ToUpper(string(c))), nil } func (c *CustomString) UnmarshalCQL(info TypeInfo, data []byte) error { *c = CustomString(strings.ToLower(string(data))) return nil } type MyString string var typeLookupTest = []struct { TypeName string ExpectedType Type }{ {"AsciiType", TypeAscii}, {"LongType", TypeBigInt}, {"BytesType", TypeBlob}, {"BooleanType", TypeBoolean}, {"CounterColumnType", TypeCounter}, {"DecimalType", TypeDecimal}, {"DoubleType", TypeDouble}, {"FloatType", TypeFloat}, {"Int32Type", TypeInt}, {"DateType", TypeTimestamp}, {"TimestampType", TypeTimestamp}, {"UUIDType", TypeUUID}, {"UTF8Type", TypeVarchar}, {"IntegerType", TypeVarint}, {"TimeUUIDType", TypeTimeUUID}, {"InetAddressType", TypeInet}, {"MapType", TypeMap}, {"ListType", TypeList}, {"SetType", TypeSet}, {"unknown", TypeCustom}, {"ShortType", TypeSmallInt}, {"ByteType", TypeTinyInt}, } func testType(t *testing.T, cassType string, expectedType Type) { if computedType := getApacheCassandraType(apacheCassandraTypePrefix + cassType); computedType != expectedType { t.Errorf("Cassandra custom type lookup for %s failed. Expected %s, got %s.", cassType, expectedType.String(), computedType.String()) } } func TestLookupCassType(t *testing.T) { t.Parallel() for _, lookupTest := range typeLookupTest { testType(t, lookupTest.TypeName, lookupTest.ExpectedType) } } type MyPointerMarshaler struct{} func (m *MyPointerMarshaler) MarshalCQL(_ TypeInfo) ([]byte, error) { return []byte{42}, nil } func TestMarshalTuple(t *testing.T) { t.Parallel() info := TupleTypeInfo{ NativeType: NativeType{proto: protoVersion3, typ: TypeTuple}, Elems: []TypeInfo{ NativeType{proto: protoVersion3, typ: TypeVarchar}, NativeType{proto: protoVersion3, typ: TypeVarchar}, }, } stringToPtr := func(s string) *string { return &s } checkString := func(t *testing.T, exp string, got string) { if got != exp { t.Errorf("expected string to be %v, got %v", exp, got) } } type tupleStruct struct { A string B *string } var ( s1 *string s2 *string ) testCases := []struct { name string expected []byte value any checkValue any check func(*testing.T, any) }{ { name: "interface-slice:two-strings", expected: []byte("\x00\x00\x00\x03foo\x00\x00\x00\x03bar"), value: []any{"foo", "bar"}, checkValue: []any{&s1, &s2}, check: func(t *testing.T, v any) { checkString(t, "foo", *s1) checkString(t, "bar", *s2) }, }, { name: "interface-slice:one-string-one-nil-string", expected: []byte("\x00\x00\x00\x03foo\xff\xff\xff\xff"), value: []any{"foo", nil}, checkValue: []any{&s1, &s2}, check: func(t *testing.T, v any) { checkString(t, "foo", *s1) if s2 != nil { t.Errorf("expected string to be nil, got %v", *s2) } }, }, { name: "struct:two-strings", expected: []byte("\x00\x00\x00\x03foo\x00\x00\x00\x03bar"), value: tupleStruct{ A: "foo", B: stringToPtr("bar"), }, checkValue: &tupleStruct{}, check: func(t *testing.T, v any) { got := v.(*tupleStruct) if got.A != "foo" { t.Errorf("expected A string to be %v, got %v", "foo", got.A) } if got.B == nil { t.Errorf("expected B string to be %v, got nil", "bar") } if *got.B != "bar" { t.Errorf("expected B string to be %v, got %v", "bar", got.B) } }, }, { name: "struct:one-string-one-nil-string", expected: []byte("\x00\x00\x00\x03foo\xff\xff\xff\xff"), value: tupleStruct{A: "foo", B: nil}, checkValue: &tupleStruct{}, check: func(t *testing.T, v any) { got := v.(*tupleStruct) if got.A != "foo" { t.Errorf("expected A string to be %v, got %v", "foo", got.A) } if got.B != nil { t.Errorf("expected B string to be nil, got %v", *got.B) } }, }, { name: "arrayslice:two-strings", expected: []byte("\x00\x00\x00\x03foo\x00\x00\x00\x03bar"), value: [2]*string{ stringToPtr("foo"), stringToPtr("bar"), }, checkValue: &[2]*string{}, check: func(t *testing.T, v any) { got := v.(*[2]*string) checkString(t, "foo", *(got[0])) checkString(t, "bar", *(got[1])) }, }, { name: "arrayslice:one-string-one-nil-string", expected: []byte("\x00\x00\x00\x03foo\xff\xff\xff\xff"), value: [2]*string{ stringToPtr("foo"), nil, }, checkValue: &[2]*string{}, check: func(t *testing.T, v any) { got := v.(*[2]*string) checkString(t, "foo", *(got[0])) if got[1] != nil { t.Errorf("expected string to be nil, got %v", *got[1]) } }, }, } for i, tc := range testCases { t.Run(tc.name, func(t *testing.T) { data, err := Marshal(info, tc.value) if err != nil { t.Errorf("marshalTest[%d]: %v", i, err) return } if !bytes.Equal(data, tc.expected) { t.Errorf("marshalTest[%d]: expected %x, got %x", i, tc.expected, data) return } err = Unmarshal(info, data, tc.checkValue) if err != nil { t.Errorf("unmarshalTest[%d]: %v", i, err) return } tc.check(t, tc.checkValue) }) } } func TestUnmarshalTuple(t *testing.T) { t.Parallel() info := TupleTypeInfo{ NativeType: NativeType{proto: protoVersion3, typ: TypeTuple}, Elems: []TypeInfo{ NativeType{proto: protoVersion3, typ: TypeVarchar}, NativeType{proto: protoVersion3, typ: TypeVarchar}, }, } // As per the CQL spec, a tuple is a sequence of "bytes" values. // Here we encode a null value (length -1) and the "foo" string (length 3) data := []byte("\xff\xff\xff\xff\x00\x00\x00\x03foo") t.Run("struct-ptr", func(t *testing.T) { var tmp struct { A *string B *string } err := Unmarshal(info, data, &tmp) if err != nil { t.Errorf("unmarshalTest: %v", err) return } if tmp.A != nil || *tmp.B != "foo" { t.Errorf("unmarshalTest: expected [nil, foo], got [%v, %v]", *tmp.A, *tmp.B) } }) t.Run("struct-nonptr", func(t *testing.T) { var tmp struct { A string B string } err := Unmarshal(info, data, &tmp) if err != nil { t.Errorf("unmarshalTest: %v", err) return } if tmp.A != "" || tmp.B != "foo" { t.Errorf("unmarshalTest: expected [nil, foo], got [%v, %v]", tmp.A, tmp.B) } }) t.Run("array", func(t *testing.T) { var tmp [2]*string err := Unmarshal(info, data, &tmp) if err != nil { t.Errorf("unmarshalTest: %v", err) return } if tmp[0] != nil || *tmp[1] != "foo" { t.Errorf("unmarshalTest: expected [nil, foo], got [%v, %v]", *tmp[0], *tmp[1]) } }) t.Run("array-nonptr", func(t *testing.T) { var tmp [2]string err := Unmarshal(info, data, &tmp) if err != nil { t.Errorf("unmarshalTest: %v", err) return } if tmp[0] != "" || tmp[1] != "foo" { t.Errorf("unmarshalTest: expected [nil, foo], got [%v, %v]", tmp[0], tmp[1]) } }) } func TestMarshalUDTMap(t *testing.T) { t.Parallel() typeInfo := UDTTypeInfo{ KeySpace: "", Name: "xyz", Elements: []UDTField{ {Name: "x", Type: NativeType{proto: protoVersion3, typ: TypeInt}}, {Name: "y", Type: NativeType{proto: protoVersion3, typ: TypeInt}}, {Name: "z", Type: NativeType{proto: protoVersion3, typ: TypeInt}}, }, NativeType: NativeType{proto: protoVersion3, typ: TypeUDT}, } t.Run("partially bound", func(t *testing.T) { value := map[string]any{ "y": 2, "z": 3, } expected := []byte("\xff\xff\xff\xff\x00\x00\x00\x04\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00\x03") data, err := Marshal(typeInfo, value) if err != nil { t.Errorf("got error %#v", err) } if !bytes.Equal(data, expected) { t.Errorf("got value %x", data) } }) t.Run("partially bound from the beginning", func(t *testing.T) { value := map[string]any{ "x": 1, "y": 2, } expected := []byte("\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x02\xff\xff\xff\xff") data, err := Marshal(typeInfo, value) if err != nil { t.Errorf("got error %#v", err) } if !bytes.Equal(data, expected) { t.Errorf("got value %x", data) } }) t.Run("fully bound", func(t *testing.T) { value := map[string]any{ "x": 1, "y": 2, "z": 3, } expected := []byte("\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00\x03") data, err := Marshal(typeInfo, value) if err != nil { t.Errorf("got error %#v", err) } if !bytes.Equal(data, expected) { t.Errorf("got value %x", data) } }) } func TestMarshalUDTStruct(t *testing.T) { t.Parallel() typeInfo := UDTTypeInfo{ KeySpace: "", Name: "xyz", Elements: []UDTField{ {Name: "x", Type: NativeType{proto: protoVersion3, typ: TypeInt}}, {Name: "y", Type: NativeType{proto: protoVersion3, typ: TypeInt}}, {Name: "z", Type: NativeType{proto: protoVersion3, typ: TypeInt}}, }, NativeType: NativeType{proto: protoVersion3, typ: TypeUDT}, } type xyzStruct struct { X int32 `cql:"x"` Y int32 `cql:"y"` Z int32 `cql:"z"` } type xyStruct struct { X int32 `cql:"x"` Y int32 `cql:"y"` } type yzStruct struct { Y int32 `cql:"y"` Z int32 `cql:"z"` } t.Run("partially bound", func(t *testing.T) { value := yzStruct{ Y: 2, Z: 3, } expected := []byte("\xff\xff\xff\xff\x00\x00\x00\x04\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00\x03") data, err := Marshal(typeInfo, value) if err != nil { t.Errorf("got error %#v", err) } if !bytes.Equal(data, expected) { t.Errorf("got value %x", data) } }) t.Run("partially bound from the beginning", func(t *testing.T) { value := xyStruct{ X: 1, Y: 2, } expected := []byte("\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x02\xff\xff\xff\xff") data, err := Marshal(typeInfo, value) if err != nil { t.Errorf("got error %#v", err) } if !bytes.Equal(data, expected) { t.Errorf("got value %x", data) } }) t.Run("fully bound", func(t *testing.T) { value := xyzStruct{ X: 1, Y: 2, Z: 3, } expected := []byte("\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00\x03") data, err := Marshal(typeInfo, value) if err != nil { t.Errorf("got error %#v", err) } if !bytes.Equal(data, expected) { t.Errorf("got value %x", data) } }) } func TestMarshalNil(t *testing.T) { t.Parallel() types := []Type{ TypeAscii, TypeBlob, TypeBoolean, TypeBigInt, TypeCounter, TypeDecimal, TypeDouble, TypeFloat, TypeInt, TypeTimestamp, TypeUUID, TypeVarchar, TypeVarint, TypeTimeUUID, TypeInet, } for _, typ := range types { data, err := Marshal(NativeType{proto: protoVersion3, typ: typ}, nil) if err != nil { t.Errorf("unable to marshal nil %v: %v\n", typ, err) } else if data != nil { t.Errorf("expected to get nil byte for nil %v got % X", typ, data) } } } func TestUnmarshalInetCopyBytes(t *testing.T) { t.Parallel() data := []byte{127, 0, 0, 1} var ip net.IP if err := unmarshalInet(data, &ip); err != nil { t.Fatal(err) } copy(data, []byte{0xFF, 0xFF, 0xFF, 0xFF}) ip2 := net.IP(data) if !ip.Equal(net.IPv4(127, 0, 0, 1)) { t.Fatalf("IP memory shared with data: ip=%v ip2=%v", ip, ip2) } } func BenchmarkUnmarshalVarchar(b *testing.B) { b.ReportAllocs() src := make([]byte, 1024) dst := make([]byte, len(src)) b.ResetTimer() for i := 0; i < b.N; i++ { if err := unmarshalVarchar(src, &dst); err != nil { b.Fatal(err) } } } func TestReadCollectionSize(t *testing.T) { t.Parallel() listV3 := CollectionType{ NativeType: NativeType{proto: protoVersion3, typ: TypeList}, Elem: NativeType{proto: protoVersion3, typ: TypeVarchar}, } tests := []struct { name string info CollectionType data []byte isError bool expectedSize int }{ { name: "short read 0 proto 3", info: listV3, data: []byte{}, isError: true, }, { name: "short read 1 proto 3", info: listV3, data: []byte{0x01}, isError: true, }, { name: "short read 2 proto 3", info: listV3, data: []byte{0x01, 0x38}, isError: true, }, { name: "short read 3 proto 3", info: listV3, data: []byte{0x01, 0x38, 0x42}, isError: true, }, { name: "good read proto 3", info: listV3, data: []byte{0x01, 0x38, 0x42, 0x22}, expectedSize: 0x01384222, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { size, _, err := readCollectionSize(test.info, test.data) if test.isError { if err == nil { t.Fatal("Expected error, but it was nil") } } else { if err != nil { t.Fatalf("Expected no error, got %v", err) } if size != test.expectedSize { t.Fatalf("Expected size of %d, but got %d", test.expectedSize, size) } } }) } } func TestReadUnsignedVInt(t *testing.T) { tests := []struct { decodedInt uint64 encodedVint []byte }{ { decodedInt: 0, encodedVint: []byte{0}, }, { decodedInt: 100, encodedVint: []byte{100}, }, { decodedInt: 256000, encodedVint: []byte{195, 232, 0}, }, } for _, test := range tests { t.Run(fmt.Sprintf("%d", test.decodedInt), func(t *testing.T) { actual, _, err := readUnsignedVInt(test.encodedVint) if err != nil { t.Fatalf("Expected no error, got %v", err) } if actual != test.decodedInt { t.Fatalf("Expected %d, but got %d", test.decodedInt, actual) } }) } } func BenchmarkUnmarshalUUID(b *testing.B) { b.ReportAllocs() src := make([]byte, 16) dst := UUID{} b.ResetTimer() for i := 0; i < b.N; i++ { if err := unmarshalUUID(src, &dst); err != nil { b.Fatal(err) } } } func TestUnmarshalUDT(t *testing.T) { t.Parallel() info := UDTTypeInfo{ NativeType: NativeType{proto: protoVersion4, typ: TypeUDT}, Name: "myudt", KeySpace: "myks", Elements: []UDTField{ { Name: "first", Type: NativeType{proto: protoVersion4, typ: TypeAscii}, }, { Name: "second", Type: NativeType{proto: protoVersion4, typ: TypeSmallInt}, }, }, } data := bytesWithLength( // UDT bytesWithLength([]byte("Hello")), // first bytesWithLength([]byte("\x00\x2a")), // second ) value := map[string]any{} expectedErr := unmarshalErrorf("can not unmarshal into non-pointer map[string]interface {}") if err := Unmarshal(info, data, value); err != expectedErr { t.Errorf("(%v=>%T): %#v returned error %#v, want %#v.", info, value, value, err, expectedErr) } } // TestUnmarshalListIntoInterface tests that lists can be unmarshaled into *any // This is used by MapScan and SliceMap functions. func TestUnmarshalListIntoInterface(t *testing.T) { t.Parallel() // Create a list of ints: [1, 2] // Format: [list_size (4 bytes), element_length (4 bytes), element_data, ...] // Reference: line 63 shows format: \x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x02 data := []byte{ 0, 0, 0, 2, // list size: 2 elements 0, 0, 0, 4, // element 0 length: 4 bytes 0, 0, 0, 1, // element 0 value: 1 0, 0, 0, 4, // element 1 length: 4 bytes 0, 0, 0, 2, // element 1 value: 2 } info := CollectionType{ NativeType: NativeType{proto: protoVersion4, typ: TypeList}, Elem: NativeType{proto: protoVersion4, typ: TypeInt}, } var result any if err := Unmarshal(info, data, &result); err != nil { t.Fatalf("Unmarshal failed: %v", err) } // Verify the result is a []int slice, ok := result.([]int) if !ok { t.Fatalf("Expected []int, got %T", result) } if len(slice) != 2 { t.Fatalf("Expected 2 elements, got %d", len(slice)) } expected := []int{1, 2} for i, v := range expected { if slice[i] != v { t.Errorf("Element %d: expected %d, got %d", i, v, slice[i]) } } } // TestUnmarshalMapIntoInterface tests that maps can be unmarshaled into *any // This is used by MapScan and SliceMap functions. func TestUnmarshalMapIntoInterface(t *testing.T) { t.Parallel() // Create a map: {"a": 1, "b": 2} // Format: [map_size (4 bytes), key_length, key_data, value_length, value_data, ...] data := []byte{ 0, 0, 0, 2, // map size: 2 entries 0, 0, 0, 1, // key 0 length: 1 byte 'a', // key 0 value: "a" 0, 0, 0, 4, // value 0 length: 4 bytes 0, 0, 0, 1, // value 0: 1 0, 0, 0, 1, // key 1 length: 1 byte 'b', // key 1 value: "b" 0, 0, 0, 4, // value 1 length: 4 bytes 0, 0, 0, 2, // value 1: 2 } info := CollectionType{ NativeType: NativeType{proto: protoVersion4, typ: TypeMap}, Key: NativeType{proto: protoVersion4, typ: TypeVarchar}, Elem: NativeType{proto: protoVersion4, typ: TypeInt}, } var result any if err := Unmarshal(info, data, &result); err != nil { t.Fatalf("Unmarshal failed: %v", err) } // Verify the result is a map[string]int m, ok := result.(map[string]int) if !ok { t.Fatalf("Expected map[string]int, got %T", result) } if len(m) != 2 { t.Fatalf("Expected 2 entries, got %d", len(m)) } if m["a"] != 1 { t.Errorf("Expected m[\"a\"] = 1, got %d", m["a"]) } if m["b"] != 2 { t.Errorf("Expected m[\"b\"] = 2, got %d", m["b"]) } } // TestUnmarshalListWithVectorIntoInterface tests that lists containing vectors // can be unmarshaled into *any (issue #692) func TestUnmarshalListWithVectorIntoInterface(t *testing.T) { t.Parallel() // Create a list of vectors: [[1.0, 2.0], [3.0, 4.0]] // Vector elements are fixed-size floats (4 bytes each) // Format: [list_size (4 bytes), element_length, element_data (vector), ...] // Each vector is 8 bytes (2 floats * 4 bytes) var data []byte // List size: 2 vectors data = append(data, 0, 0, 0, 2) // Vector 1: [1.0, 2.0] data = append(data, 0, 0, 0, 8) // vector length: 8 bytes float1Bytes := make([]byte, 4) binary.BigEndian.PutUint32(float1Bytes, math.Float32bits(1.0)) data = append(data, float1Bytes...) float2Bytes := make([]byte, 4) binary.BigEndian.PutUint32(float2Bytes, math.Float32bits(2.0)) data = append(data, float2Bytes...) // Vector 2: [3.0, 4.0] data = append(data, 0, 0, 0, 8) // vector length: 8 bytes float3Bytes := make([]byte, 4) binary.BigEndian.PutUint32(float3Bytes, math.Float32bits(3.0)) data = append(data, float3Bytes...) float4Bytes := make([]byte, 4) binary.BigEndian.PutUint32(float4Bytes, math.Float32bits(4.0)) data = append(data, float4Bytes...) info := CollectionType{ NativeType: NativeType{proto: protoVersion4, typ: TypeList}, Elem: VectorType{ NativeType: NativeType{proto: protoVersion4, typ: TypeCustom, custom: apacheCassandraTypePrefix + "VectorType"}, SubType: NativeType{proto: protoVersion4, typ: TypeFloat}, Dimensions: 2, }, } var result any if err := Unmarshal(info, data, &result); err != nil { t.Fatalf("Unmarshal failed: %v", err) } // Verify the result is a [][]float32 slice, ok := result.([][]float32) if !ok { t.Fatalf("Expected [][]float32, got %T", result) } if len(slice) != 2 { t.Fatalf("Expected 2 elements, got %d", len(slice)) } if len(slice[0]) != 2 || slice[0][0] != 1.0 || slice[0][1] != 2.0 { t.Errorf("Expected slice[0] = [1.0, 2.0], got %v", slice[0]) } if len(slice[1]) != 2 || slice[1][0] != 3.0 || slice[1][1] != 4.0 { t.Errorf("Expected slice[1] = [3.0, 4.0], got %v", slice[1]) } } // bytesWithLength concatenates all data slices and prepends the total length as uint32. // The length does not count the size of the uint32 used for writing the size. func bytesWithLength(data ...[]byte) []byte { totalLen := 0 for i := range data { totalLen += len(data[i]) } if totalLen > math.MaxUint32 { panic("total length overflows") } ret := make([]byte, totalLen+4) binary.BigEndian.PutUint32(ret[:4], uint32(totalLen)) buf := ret[4:] for i := range data { n := copy(buf, data[i]) buf = buf[n:] } return ret } func TestUnmarshalVectorZeroDimensions(t *testing.T) { info := VectorType{ NativeType: NewCustomType(protoVersion4, TypeCustom, apacheCassandraTypePrefix+"VectorType"), SubType: NativeType{proto: protoVersion4, typ: TypeFloat}, Dimensions: 0, } t.Run("nil_data", func(t *testing.T) { var result []float32 if err := unmarshalVector(info, nil, &result); err != nil { t.Fatalf("unexpected error: %v", err) } }) t.Run("empty_data", func(t *testing.T) { var result []float32 if err := unmarshalVector(info, []byte{}, &result); err != nil { t.Fatalf("unexpected error: %v", err) } if result == nil { t.Fatal("expected non-nil empty slice") } if len(result) != 0 { t.Fatalf("expected len 0, got %d", len(result)) } }) t.Run("nonempty_data_errors", func(t *testing.T) { var result []float32 err := unmarshalVector(info, []byte{0x01, 0x02}, &result) if err == nil { t.Fatal("expected error for non-empty data with 0 dimensions") } if !strings.Contains(err.Error(), "0-dimension") { t.Fatalf("expected error mentioning 0-dimension, got: %v", err) } }) t.Run("empty_data_into_zero_length_array", func(t *testing.T) { var result [0]float32 if err := unmarshalVector(info, []byte{}, &result); err != nil { t.Fatalf("unexpected error: %v", err) } }) t.Run("empty_data_into_nonzero_length_array_errors", func(t *testing.T) { var result [5]float32 err := unmarshalVector(info, []byte{}, &result) if err == nil { t.Fatal("expected error for 0-dimension vector into non-zero-length array") } if !strings.Contains(err.Error(), "array of size 5") { t.Fatalf("expected error mentioning array size, got: %v", err) } }) t.Run("empty_data_into_interface", func(t *testing.T) { var result any if err := unmarshalVector(info, []byte{}, &result); err != nil { t.Fatalf("unexpected error: %v", err) } }) } // TestNativeNewWithErrorConsistentWithGoType verifies that the fast-path type mapping // in NativeType.NewWithError() stays consistent with the canonical goType() mapping. // This guards against future changes to one mapping that forget to update the other. func TestNativeNewWithErrorConsistentWithGoType(t *testing.T) { // All NativeType type codes that goType handles (excluding collection/tuple/UDT // which are separate TypeInfo implementations). nativeTypes := []Type{ TypeVarchar, TypeAscii, TypeText, TypeInet, TypeBigInt, TypeCounter, TypeTime, TypeTimestamp, TypeBlob, TypeBoolean, TypeFloat, TypeDouble, TypeInt, TypeSmallInt, TypeTinyInt, TypeDecimal, TypeUUID, TypeTimeUUID, TypeVarint, TypeDate, TypeDuration, } for _, typ := range nativeTypes { nt := NativeType{typ: typ, proto: protoVersion4} // Get the fast-path result from NewWithError fastVal, err := nt.NewWithError() if err != nil { t.Errorf("NewWithError(%s): unexpected error: %v", typ, err) continue } // Get the canonical type from goType canonicalType, err := goType(nt) if err != nil { t.Errorf("goType(%s): unexpected error: %v", typ, err) continue } // NewWithError returns a pointer (reflect.New(typ).Interface()), so the // underlying type is reflect.TypeOf(val).Elem() fastType := reflect.TypeOf(fastVal) if fastType.Kind() != reflect.Ptr { t.Errorf("NewWithError(%s): expected pointer, got %s", typ, fastType.Kind()) continue } fastElemType := fastType.Elem() if fastElemType != canonicalType { t.Errorf("NewWithError(%s) fast-path type %s does not match goType() canonical type %s", typ, fastElemType, canonicalType) } } } // TestCollectionNewWithErrorConsistentWithGoType verifies that the fast-path type mapping // in CollectionType.NewWithError() stays consistent with the canonical goType() mapping. func TestCollectionNewWithErrorConsistentWithGoType(t *testing.T) { elemTypes := []Type{ TypeInt, TypeBigInt, TypeCounter, TypeText, TypeVarchar, TypeAscii, TypeBoolean, TypeFloat, TypeDouble, TypeUUID, TypeTimeUUID, TypeTimestamp, TypeDate, TypeSmallInt, TypeTinyInt, TypeBlob, } // Test list and set types for _, collTyp := range []Type{TypeList, TypeSet} { for _, elemTyp := range elemTypes { ct := CollectionType{ NativeType: NativeType{typ: collTyp, proto: protoVersion4}, Elem: NativeType{typ: elemTyp, proto: protoVersion4}, } fastVal, err := ct.NewWithError() if err != nil { t.Errorf("NewWithError(%s<%s>): unexpected error: %v", collTyp, elemTyp, err) continue } canonicalType, err := goType(ct) if err != nil { t.Errorf("goType(%s<%s>): unexpected error: %v", collTyp, elemTyp, err) continue } fastType := reflect.TypeOf(fastVal) if fastType.Kind() != reflect.Ptr { t.Errorf("NewWithError(%s<%s>): expected pointer, got %s", collTyp, elemTyp, fastType.Kind()) continue } if fastType.Elem() != canonicalType { t.Errorf("NewWithError(%s<%s>) fast-path type %s does not match goType() canonical type %s", collTyp, elemTyp, fastType.Elem(), canonicalType) } } } // Test map types with common key/value combinations keyTypes := []Type{TypeText, TypeVarchar, TypeInt} valTypes := []Type{ TypeInt, TypeBigInt, TypeText, TypeVarchar, TypeBoolean, TypeFloat, TypeDouble, TypeUUID, } for _, keyTyp := range keyTypes { for _, valTyp := range valTypes { ct := CollectionType{ NativeType: NativeType{typ: TypeMap, proto: protoVersion4}, Key: NativeType{typ: keyTyp, proto: protoVersion4}, Elem: NativeType{typ: valTyp, proto: protoVersion4}, } fastVal, err := ct.NewWithError() if err != nil { t.Errorf("NewWithError(map<%s, %s>): unexpected error: %v", keyTyp, valTyp, err) continue } canonicalType, err := goType(ct) if err != nil { t.Errorf("goType(map<%s, %s>): unexpected error: %v", keyTyp, valTyp, err) continue } fastType := reflect.TypeOf(fastVal) if fastType.Kind() != reflect.Ptr { t.Errorf("NewWithError(map<%s, %s>): expected pointer, got %s", keyTyp, valTyp, fastType.Kind()) continue } if fastType.Elem() != canonicalType { t.Errorf("NewWithError(map<%s, %s>) fast-path type %s does not match goType() canonical type %s", keyTyp, valTyp, fastType.Elem(), canonicalType) } } } } ================================================ FILE: metadata_scylla.go ================================================ // Copyright (c) 2015 The gocql Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package gocql import ( "errors" "fmt" "maps" "reflect" "strings" "sync" "sync/atomic" "golang.org/x/sync/errgroup" "golang.org/x/sync/singleflight" frm "github.com/gocql/gocql/internal/frame" "github.com/gocql/gocql/tablets" ) // schema metadata for a keyspace type KeyspaceMetadata struct { StrategyOptions map[string]any Tables map[string]*TableMetadata Functions map[string]*FunctionMetadata Aggregates map[string]*AggregateMetadata Types map[string]*TypeMetadata Indexes map[string]*IndexMetadata Views map[string]*ViewMetadata tablesInvalidated map[string]struct{} Name string StrategyClass string CreateStmts string DurableWrites bool } // Clone returns a shallow copy of the keyspace metadata with // cloned Tables, Indexes, Views, and tablesInvalidated maps so that mutations // do not race with concurrent readers of the original. func (ks *KeyspaceMetadata) Clone() *KeyspaceMetadata { cloned := &KeyspaceMetadata{ Name: ks.Name, DurableWrites: ks.DurableWrites, StrategyClass: ks.StrategyClass, StrategyOptions: maps.Clone(ks.StrategyOptions), Tables: maps.Clone(ks.Tables), Functions: maps.Clone(ks.Functions), Aggregates: maps.Clone(ks.Aggregates), Types: maps.Clone(ks.Types), Indexes: maps.Clone(ks.Indexes), Views: maps.Clone(ks.Views), CreateStmts: ks.CreateStmts, } if ks.tablesInvalidated != nil { cloned.tablesInvalidated = maps.Clone(ks.tablesInvalidated) } return cloned } func (ks *KeyspaceMetadata) removeTableData(tableName string) { if ks.Tables != nil { delete(ks.Tables, tableName) } for name, idx := range ks.Indexes { if idx != nil && idx.TableName == tableName { delete(ks.Indexes, name) } } for name, view := range ks.Views { if view != nil && view.BaseTableName == tableName { delete(ks.Views, name) } } } func (ks *KeyspaceMetadata) invalidateTable(tableName string) { ks.removeTableData(tableName) if ks.tablesInvalidated == nil { ks.tablesInvalidated = make(map[string]struct{}) } ks.tablesInvalidated[tableName] = struct{}{} } func (ks *KeyspaceMetadata) removeTable(tableName string) { ks.removeTableData(tableName) if ks.tablesInvalidated != nil { delete(ks.tablesInvalidated, tableName) } } // schema metadata for a table (a.k.a. column family) type TableMetadata struct { Columns map[string]*ColumnMetadata Extensions map[string]any Keyspace string Name string PartitionKey []*ColumnMetadata ClusteringColumns []*ColumnMetadata OrderedColumns []string Flags []string Options TableMetadataOptions } type TableMetadataOptions struct { Caching map[string]string Compaction map[string]string Compression map[string]string CDC map[string]string SpeculativeRetry string Comment string Version string Partitioner string GcGraceSeconds int MaxIndexInterval int MemtableFlushPeriodInMs int MinIndexInterval int ReadRepairChance float64 BloomFilterFpChance float64 DefaultTimeToLive int DcLocalReadRepairChance float64 CrcCheckChance float64 InMemory bool } func (t *TableMetadataOptions) Equals(other *TableMetadataOptions) bool { if t == nil || other == nil { return t == other // Both must be nil to be equal } if t.BloomFilterFpChance != other.BloomFilterFpChance || t.Comment != other.Comment || t.CrcCheckChance != other.CrcCheckChance || t.DcLocalReadRepairChance != other.DcLocalReadRepairChance || t.DefaultTimeToLive != other.DefaultTimeToLive || t.GcGraceSeconds != other.GcGraceSeconds || t.MaxIndexInterval != other.MaxIndexInterval || t.MemtableFlushPeriodInMs != other.MemtableFlushPeriodInMs || t.MinIndexInterval != other.MinIndexInterval || t.ReadRepairChance != other.ReadRepairChance || t.SpeculativeRetry != other.SpeculativeRetry || t.InMemory != other.InMemory || t.Partitioner != other.Partitioner || t.Version != other.Version { return false } if !compareStringMaps(t.Caching, other.Caching) || !compareStringMaps(t.Compaction, other.Compaction) || !compareStringMaps(t.Compression, other.Compression) || !compareStringMaps(t.CDC, other.CDC) { return false } return true } type ViewMetadata struct { Columns map[string]*ColumnMetadata Extensions map[string]any WhereClause string BaseTableName string ID string KeyspaceName string BaseTableID string ViewName string OrderedColumns []string PartitionKey []*ColumnMetadata ClusteringColumns []*ColumnMetadata Options TableMetadataOptions DcLocalReadRepairChance float64 // After Scylla 4.2 by default read_repair turned off ReadRepairChance float64 // After Scylla 4.2 by default read_repair turned off IncludeAllColumns bool } type ColumnMetadata struct { Index ColumnIndexMetadata Keyspace string Table string Name string Type string ClusteringOrder string ComponentIndex int Kind ColumnKind Order ColumnOrder } func (c *ColumnMetadata) Equals(other *ColumnMetadata) bool { if c == nil || other == nil { return c == other } return c.Keyspace == other.Keyspace && c.Table == other.Table && c.Name == other.Name && c.ComponentIndex == other.ComponentIndex && c.Kind == other.Kind && c.Type == other.Type && c.ClusteringOrder == other.ClusteringOrder && c.Order == other.Order && c.Index.Equals(&other.Index) } // FunctionMetadata holds metadata for function constructs type FunctionMetadata struct { Keyspace string Name string Body string Language string ReturnType string ArgumentTypes []string ArgumentNames []string CalledOnNullInput bool } // AggregateMetadata holds metadata for aggregate constructs type AggregateMetadata struct { Keyspace string Name string InitCond string ReturnType string StateType string stateFunc string finalFunc string ArgumentTypes []string FinalFunc FunctionMetadata StateFunc FunctionMetadata } // TypeMetadata holds the metadata for views. type TypeMetadata struct { Keyspace string Name string FieldNames []string FieldTypes []string } type IndexMetadata struct { Name string KeyspaceName string TableName string // Name of corresponding view. Kind string Options map[string]string Columns map[string]*ColumnMetadata OrderedColumns []string PartitionKey []*ColumnMetadata ClusteringColumns []*ColumnMetadata } func (t *TableMetadata) Equals(other *TableMetadata) bool { if t == nil || other == nil { return t == other } if t.Keyspace != other.Keyspace || t.Name != other.Name { return false } if len(t.PartitionKey) != len(other.PartitionKey) || !compareColumnSlices(t.PartitionKey, other.PartitionKey) { return false } if len(t.ClusteringColumns) != len(other.ClusteringColumns) || !compareColumnSlices(t.ClusteringColumns, other.ClusteringColumns) { return false } if len(t.Columns) != len(other.Columns) || !compareColumnsMap(t.Columns, other.Columns) { return false } if len(t.OrderedColumns) != len(other.OrderedColumns) || !compareStringSlices(t.OrderedColumns, other.OrderedColumns) { return false } if !t.Options.Equals(&other.Options) { return false } if len(t.Flags) != len(other.Flags) || !compareStringSlices(t.Flags, other.Flags) { return false } if len(t.Extensions) != len(other.Extensions) || !compareInterfaceMaps(t.Extensions, other.Extensions) { return false } return true } func compareColumnSlices(a, b []*ColumnMetadata) bool { for i := range a { if !a[i].Equals(b[i]) { return false } } return true } func compareColumnsMap(a, b map[string]*ColumnMetadata) bool { for k, v := range a { otherValue, exists := b[k] if !exists || !v.Equals(otherValue) { return false } } return true } func compareStringSlices(a, b []string) bool { for i := range a { if a[i] != b[i] { return false } } return true } func compareStringMaps(a, b map[string]string) bool { if len(a) != len(b) { return false } for k, v := range a { if otherValue, exists := b[k]; !exists || v != otherValue { return false } } return true } func compareInterfaceMaps(a, b map[string]any) bool { if len(a) != len(b) { return false } for k, v := range a { otherValue, exists := b[k] if !exists || !reflect.DeepEqual(v, otherValue) { return false } } return true } // cowTabletList implements a copy on write keyspace metadata map, its equivalent type is map[string]*KeyspaceMetadata type cowKeyspaceMetadataMap struct { keyspaceMap atomic.Value mu sync.Mutex } func (c *cowKeyspaceMetadataMap) get() map[string]*KeyspaceMetadata { l, ok := c.keyspaceMap.Load().(map[string]*KeyspaceMetadata) if !ok { return nil } return l } func (c *cowKeyspaceMetadataMap) getKeyspace(keyspaceName string) (*KeyspaceMetadata, bool) { m, ok := c.keyspaceMap.Load().(map[string]*KeyspaceMetadata) if !ok { return nil, ok } val, ok := m[keyspaceName] return val, ok } func (c *cowKeyspaceMetadataMap) set(keyspaceName string, keyspaceMetadata *KeyspaceMetadata) bool { c.mu.Lock() defer c.mu.Unlock() m := c.get() newM := map[string]*KeyspaceMetadata{} for name, metadata := range m { newM[name] = metadata } newM[keyspaceName] = keyspaceMetadata c.keyspaceMap.Store(newM) return true } func (c *cowKeyspaceMetadataMap) invalidateTable(keyspaceName, tableName string) { c.updateKeyspace(keyspaceName, func(ks *KeyspaceMetadata) { ks.invalidateTable(tableName) }) } func (c *cowKeyspaceMetadataMap) removeKeyspace(keyspaceName string) { c.mu.Lock() defer c.mu.Unlock() m := c.get() newM := maps.Clone(m) delete(newM, keyspaceName) c.keyspaceMap.Store(newM) } // updateKeyspace atomically clones a keyspace's mutable maps, applies fn to // the clone, and publishes the result. This prevents data races between // concurrent readers and writers of the same KeyspaceMetadata. // Returns false if the keyspace was not found (no update applied). func (c *cowKeyspaceMetadataMap) updateKeyspace(keyspaceName string, fn func(ks *KeyspaceMetadata)) bool { c.mu.Lock() defer c.mu.Unlock() m := c.get() ks, ok := m[keyspaceName] if !ok || ks == nil { return false } cloned := ks.Clone() fn(cloned) newM := maps.Clone(m) newM[keyspaceName] = cloned c.keyspaceMap.Store(newM) return true } const ( IndexKindCustom = "CUSTOM" ) // the ordering of the column with regard to its comparator type ColumnOrder bool const ( ASC ColumnOrder = false DESC = true ) type ColumnIndexMetadata struct { Options map[string]any Name string Type string } func (c *ColumnIndexMetadata) Equals(other *ColumnIndexMetadata) bool { if c == nil || other == nil { return c == other } if c.Name != other.Name || c.Type != other.Type { return false } // Compare the Options map if len(c.Options) != len(other.Options) { return false } for k, v := range c.Options { otherValue, exists := other.Options[k] if !exists || !reflect.DeepEqual(v, otherValue) { return false } } return true } type ColumnKind int const ( ColumnUnkownKind ColumnKind = iota ColumnPartitionKey ColumnClusteringKey ColumnRegular ColumnCompact ColumnStatic ) func (c ColumnKind) String() string { switch c { case ColumnPartitionKey: return "partition_key" case ColumnClusteringKey: return "clustering_key" case ColumnRegular: return "regular" case ColumnCompact: return "compact" case ColumnStatic: return "static" default: return fmt.Sprintf("unknown_column_%d", c) } } func (c *ColumnKind) UnmarshalCQL(typ TypeInfo, p []byte) error { if typ.Type() != TypeVarchar { return unmarshalErrorf("unable to marshall %s into ColumnKind, expected Varchar", typ) } kind, err := columnKindFromSchema(string(p)) if err != nil { return err } *c = kind return nil } func columnKindFromSchema(kind string) (ColumnKind, error) { switch kind { case "partition_key": return ColumnPartitionKey, nil case "clustering_key", "clustering": return ColumnClusteringKey, nil case "regular": return ColumnRegular, nil case "compact_value": return ColumnCompact, nil case "static": return ColumnStatic, nil default: return -1, fmt.Errorf("unknown column kind: %q", kind) } } type Metadata struct { tabletsMetadata *tablets.CowTabletList keyspaceMetadata cowKeyspaceMetadataMap } // queries the cluster for schema information for a specific keyspace and for tablets type metadataDescriber struct { keyspaceGroup singleflight.Group tableGroup singleflight.Group session *Session metadata *Metadata // mu serialises refreshAllSchema calls so the snapshot-compare-refresh // cycle runs as an atomic batch. Individual keyspace/table refreshes // are deduplicated by the singleflight groups above and do NOT need // this lock. // // Lock ordering: s.mu → cowKeyspaceMetadataMap.mu (never reversed). mu sync.Mutex } // creates a session bound schema describer which will query and cache // keyspace metadata and tablets metadata func newMetadataDescriber(session *Session) *metadataDescriber { return &metadataDescriber{ session: session, metadata: &Metadata{ tabletsMetadata: tablets.NewCowTabletList(), }, } } func (s *metadataDescriber) getKeyspaceInternal(keyspaceName string) (metadata *KeyspaceMetadata, wasReloaded bool, err error) { var found bool metadata, found = s.metadata.keyspaceMetadata.getKeyspace(keyspaceName) if !found { wasReloaded = true err = s.deduplicatedRefreshKeyspace(keyspaceName) if err != nil { return metadata, wasReloaded, err } metadata, found = s.metadata.keyspaceMetadata.getKeyspace(keyspaceName) if !found { return nil, true, fmt.Errorf("keyspace %s: %w", keyspaceName, ErrNotFound) } } return metadata, wasReloaded, nil } func (s *metadataDescriber) GetKeyspace(keyspaceName string) (*KeyspaceMetadata, error) { metadata, _, err := s.getKeyspaceInternal(keyspaceName) return metadata, err } func tableNotFoundError(keyspaceName, tableName string) error { return fmt.Errorf("table %s.%s: %w", keyspaceName, tableName, ErrNotFound) } // getTableFromSnapshot resolves a table lookup against a keyspace snapshot. // It re-reads the latest published keyspace metadata before deciding that an // invalidated table still needs a refresh, which avoids duplicate refreshes // for callers holding a stale snapshot. func (s *metadataDescriber) getTableFromSnapshot( keyspaceName, tableName string, keyspaceMetadata *KeyspaceMetadata, wasReloaded bool, ) (tableMetadata *TableMetadata, refreshNeeded bool, err error) { if tableMetadata, found := keyspaceMetadata.Tables[tableName]; found { return tableMetadata, false, nil } if latestMetadata, found := s.metadata.keyspaceMetadata.getKeyspace(keyspaceName); found && latestMetadata != keyspaceMetadata { keyspaceMetadata = latestMetadata if tableMetadata, found := keyspaceMetadata.Tables[tableName]; found { return tableMetadata, false, nil } } if wasReloaded { return nil, false, tableNotFoundError(keyspaceName, tableName) } if _, ok := keyspaceMetadata.tablesInvalidated[tableName]; !ok { return nil, false, tableNotFoundError(keyspaceName, tableName) } return nil, true, nil } func (s *metadataDescriber) GetTable(keyspaceName, tableName string) (*TableMetadata, error) { keyspaceMetadata, wasReloaded, err := s.getKeyspaceInternal(keyspaceName) if err != nil { return nil, err } tableMetadata, refreshNeeded, err := s.getTableFromSnapshot(keyspaceName, tableName, keyspaceMetadata, wasReloaded) if err != nil { return nil, err } if !refreshNeeded { return tableMetadata, nil } err = s.deduplicatedRefreshTable(keyspaceName, tableName) if err != nil { return nil, err } keyspaceMetadata, found := s.metadata.keyspaceMetadata.getKeyspace(keyspaceName) if !found { return nil, tableNotFoundError(keyspaceName, tableName) } tableMetadata, found = keyspaceMetadata.Tables[tableName] if !found { return nil, tableNotFoundError(keyspaceName, tableName) } return tableMetadata, nil } func (s *metadataDescriber) getTablets() tablets.TabletInfoList { return s.metadata.tabletsMetadata.Get() } func (s *metadataDescriber) getTableTablets(keyspace, table string) tablets.TabletEntryList { return s.metadata.tabletsMetadata.GetTableTablets(keyspace, table) } func (s *metadataDescriber) forEachTablet(fn func(keyspace, table string, entries tablets.TabletEntryList) bool) { s.metadata.tabletsMetadata.ForEach(fn) } func (s *metadataDescriber) AddTablet(tablet tablets.TabletInfo) { s.metadata.tabletsMetadata.AddTablet(tablet) } // RemoveTabletsWithHost removes tablets that contains given host. // to be used outside the metadataDescriber func (s *metadataDescriber) RemoveTabletsWithHost(host *HostInfo) { s.metadata.tabletsMetadata.RemoveTabletsWithHost(tablets.HostUUID(host.hostUUID())) } // RemoveTabletsWithKeyspace removes tablets for given keyspace. // to be used outside the metadataDescriber func (s *metadataDescriber) RemoveTabletsWithKeyspace(keyspace string) { s.metadata.tabletsMetadata.RemoveTabletsWithKeyspace(keyspace) } // RemoveTabletsWithTable removes tablets for given table. // to be used outside the metadataDescriber func (s *metadataDescriber) RemoveTabletsWithTable(keyspace string, table string) { s.metadata.tabletsMetadata.RemoveTabletsWithTable(keyspace, table) } // invalidateKeyspaceSchema clears the cached keyspace metadata func (s *metadataDescriber) invalidateKeyspaceSchema(keyspaceName string) { s.metadata.keyspaceMetadata.removeKeyspace(keyspaceName) } func (s *metadataDescriber) invalidateTableSchema(keyspaceName, tableName string) { s.metadata.keyspaceMetadata.invalidateTable(keyspaceName, tableName) } // deduplicatedRefreshKeyspace collapses concurrent refreshKeyspaceSchema calls // for the same keyspace into a single in-flight operation. func (s *metadataDescriber) deduplicatedRefreshKeyspace(keyspaceName string) error { _, err, _ := s.keyspaceGroup.Do(keyspaceName, func() (any, error) { return nil, s.refreshKeyspaceSchema(keyspaceName) }) return err } // deduplicatedRefreshTable collapses concurrent refreshTableSchema calls // for the same keyspace/table into a single in-flight operation. func (s *metadataDescriber) deduplicatedRefreshTable(keyspaceName, tableName string) error { key := keyspaceName + "\x00" + tableName _, err, _ := s.tableGroup.Do(key, func() (any, error) { return nil, s.refreshTableSchema(keyspaceName, tableName) }) return err } func (s *metadataDescriber) refreshAllSchema() error { // mu serialises concurrent refreshAllSchema calls so each one sees a // consistent snapshot before deciding what changed. Individual keyspace // refreshes inside the loop go through singleflight, so two overlapping // refreshAllSchema calls will not duplicate network queries — the second // caller blocks on mu while the first finishes. s.mu.Lock() defer s.mu.Unlock() copiedMap := make(map[string]*KeyspaceMetadata) for key, value := range s.metadata.keyspaceMetadata.get() { if value != nil { copiedMap[key] = value.Clone() } else { copiedMap[key] = nil } } for keyspaceName, metadata := range copiedMap { // Route through singleflight to dedup concurrent refreshes. err := s.deduplicatedRefreshKeyspace(keyspaceName) if errors.Is(err, ErrKeyspaceDoesNotExist) { s.invalidateKeyspaceSchema(keyspaceName) s.RemoveTabletsWithKeyspace(keyspaceName) continue } else if err != nil { return err } updatedMetadata, err := s.GetKeyspace(keyspaceName) if err != nil { return err } if !compareInterfaceMaps(metadata.StrategyOptions, updatedMetadata.StrategyOptions) { s.RemoveTabletsWithKeyspace(keyspaceName) continue } for tableName, tableMetadata := range metadata.Tables { if updatedTableMetadata, ok := updatedMetadata.Tables[tableName]; !ok || !tableMetadata.Equals(updatedTableMetadata) { s.RemoveTabletsWithTable(keyspaceName, tableName) } } } return nil } // forcibly updates the current KeyspaceMetadata held by the schema describer // for a given named keyspace. // // All system schema queries are issued concurrently since none of them // depend on each other's results. The results are only combined in // compileMetadata after all queries complete. func (s *metadataDescriber) refreshKeyspaceSchema(keyspaceName string) error { var ( keyspace *KeyspaceMetadata tables []TableMetadata columns []ColumnMetadata functions []FunctionMetadata aggregates []AggregateMetadata types []TypeMetadata indexes []IndexMetadata views []ViewMetadata createStmts []byte ) // Each goroutine writes to its own dedicated variable, so no // synchronisation is needed beyond errgroup itself. var g errgroup.Group g.Go(func() error { var err error keyspace, err = getKeyspaceMetadata(s.session, keyspaceName) return err }) g.Go(func() error { var err error tables, err = getTableMetadata(s.session, keyspaceName) return err }) g.Go(func() error { var err error columns, err = getColumnMetadata(s.session, keyspaceName) return err }) g.Go(func() error { var err error functions, err = getFunctionsMetadata(s.session, keyspaceName) return err }) g.Go(func() error { var err error aggregates, err = getAggregatesMetadata(s.session, keyspaceName) return err }) g.Go(func() error { var err error types, err = getTypeMetadata(s.session, keyspaceName) return err }) g.Go(func() error { var err error indexes, err = getIndexMetadata(s.session, keyspaceName) return err }) g.Go(func() error { var err error views, err = getViewMetadata(s.session, keyspaceName) return err }) g.Go(func() error { var err error createStmts, err = getCreateStatements(s.session, keyspaceName) return err }) if err := g.Wait(); err != nil { return err } compileMetadata(keyspace, tables, columns, functions, aggregates, types, indexes, views, createStmts) s.metadata.keyspaceMetadata.set(keyspaceName, keyspace) return nil } func (s *metadataDescriber) refreshTableSchema(keyspaceName, tableName string) error { _, found := s.metadata.keyspaceMetadata.getKeyspace(keyspaceName) if !found { return s.deduplicatedRefreshKeyspace(keyspaceName) } // Perform network queries outside the lock. tables, err := getTableMetadataByName(s.session, keyspaceName, tableName) if err != nil { return err } columns, err := getColumnMetadataByTable(s.session, keyspaceName, tableName) if err != nil { return err } indexes, err := getIndexMetadataByTable(s.session, keyspaceName, tableName) if err != nil { return err } views, err := getViewMetadataByTable(s.session, keyspaceName, tableName) if err != nil { return err } // Atomically clone-and-swap the keyspace metadata to avoid data races // with concurrent readers. applied := s.metadata.keyspaceMetadata.updateKeyspace(keyspaceName, func(ks *KeyspaceMetadata) { if len(tables) == 0 { ks.removeTable(tableName) } else { compileTableMetadata(ks, tables, columns, indexes, views) if ks.tablesInvalidated != nil { delete(ks.tablesInvalidated, tableName) } } }) if !applied { // Keyspace was removed between the initial check and the update. // Fall back to a full keyspace refresh to recover. return s.deduplicatedRefreshKeyspace(keyspaceName) } return nil } // "compiles" derived information about keyspace, table, and column metadata // for a keyspace from the basic queried metadata objects returned by // getKeyspaceMetadata, getTableMetadata, and getColumnMetadata respectively; // Links the metadata objects together and derives the column composition of // the partition key and clustering key for a table. func compileMetadata( keyspace *KeyspaceMetadata, tables []TableMetadata, columns []ColumnMetadata, functions []FunctionMetadata, aggregates []AggregateMetadata, types []TypeMetadata, indexes []IndexMetadata, views []ViewMetadata, createStmts []byte, ) { keyspace.Tables = make(map[string]*TableMetadata) for i := range tables { tables[i].Columns = make(map[string]*ColumnMetadata) keyspace.Tables[tables[i].Name] = &tables[i] } keyspace.Functions = make(map[string]*FunctionMetadata, len(functions)) for i := range functions { keyspace.Functions[functions[i].Name] = &functions[i] } keyspace.Aggregates = make(map[string]*AggregateMetadata, len(aggregates)) for _, aggregate := range aggregates { aggregate.FinalFunc = *keyspace.Functions[aggregate.finalFunc] aggregate.StateFunc = *keyspace.Functions[aggregate.stateFunc] keyspace.Aggregates[aggregate.Name] = &aggregate } keyspace.Types = make(map[string]*TypeMetadata, len(types)) for i := range types { keyspace.Types[types[i].Name] = &types[i] } keyspace.Indexes = make(map[string]*IndexMetadata, len(indexes)) for i := range indexes { indexes[i].Columns = make(map[string]*ColumnMetadata) keyspace.Indexes[indexes[i].Name] = &indexes[i] } keyspace.Views = make(map[string]*ViewMetadata, len(views)) for i := range views { v := &views[i] if _, ok := keyspace.Indexes[strings.TrimSuffix(v.ViewName, "_index")]; ok { continue } v.Columns = make(map[string]*ColumnMetadata) keyspace.Views[v.ViewName] = v } // add columns from the schema data for i := range columns { col := &columns[i] col.Order = ASC if col.ClusteringOrder == "desc" { col.Order = DESC } table, ok := keyspace.Tables[col.Table] if !ok { // If column owned by a table that the table name ends with `_index` // suffix then the table is a view corresponding to some index. if indexName, found := strings.CutSuffix(col.Table, "_index"); found { ix, ok := keyspace.Indexes[indexName] if ok { ix.Columns[col.Name] = col ix.OrderedColumns = append(ix.OrderedColumns, col.Name) continue } } view, ok := keyspace.Views[col.Table] if !ok { // if the schema is being updated we will race between seeing // the metadata be complete. Potentially we should check for // schema versions before and after reading the metadata and // if they dont match try again. continue } view.Columns[col.Name] = col view.OrderedColumns = append(view.OrderedColumns, col.Name) continue } table.Columns[col.Name] = col table.OrderedColumns = append(table.OrderedColumns, col.Name) } for i := range tables { t := &tables[i] t.PartitionKey, t.ClusteringColumns, t.OrderedColumns = compileColumns(t.Columns, t.OrderedColumns) } for i := range views { v := &views[i] v.PartitionKey, v.ClusteringColumns, v.OrderedColumns = compileColumns(v.Columns, v.OrderedColumns) } for i := range indexes { ix := &indexes[i] ix.PartitionKey, ix.ClusteringColumns, ix.OrderedColumns = compileColumns(ix.Columns, ix.OrderedColumns) } keyspace.CreateStmts = string(createStmts) } func compileTableMetadata( keyspace *KeyspaceMetadata, tables []TableMetadata, columns []ColumnMetadata, indexes []IndexMetadata, views []ViewMetadata, ) { if keyspace.Tables == nil { keyspace.Tables = make(map[string]*TableMetadata) } for i := range tables { tables[i].Columns = make(map[string]*ColumnMetadata) keyspace.Tables[tables[i].Name] = &tables[i] } if keyspace.Indexes == nil { keyspace.Indexes = make(map[string]*IndexMetadata) } for name, ix := range keyspace.Indexes { for i := range tables { if ix.TableName == tables[i].Name { delete(keyspace.Indexes, name) } } } for i := range indexes { indexes[i].Columns = make(map[string]*ColumnMetadata) keyspace.Indexes[indexes[i].Name] = &indexes[i] } if keyspace.Views == nil { keyspace.Views = make(map[string]*ViewMetadata) } for name, v := range keyspace.Views { for i := range tables { if v.BaseTableName == tables[i].Name { delete(keyspace.Views, name) } } } for i := range views { v := &views[i] if _, ok := keyspace.Indexes[strings.TrimSuffix(v.ViewName, "_index")]; ok { continue } v.Columns = make(map[string]*ColumnMetadata) keyspace.Views[v.ViewName] = v } for i := range columns { col := &columns[i] col.Order = ASC if col.ClusteringOrder == "desc" { col.Order = DESC } table, ok := keyspace.Tables[col.Table] if !ok { if indexName, found := strings.CutSuffix(col.Table, "_index"); found { ix, ok := keyspace.Indexes[indexName] if ok { ix.Columns[col.Name] = col ix.OrderedColumns = append(ix.OrderedColumns, col.Name) continue } } view, ok := keyspace.Views[col.Table] if !ok { continue } view.Columns[col.Name] = col view.OrderedColumns = append(view.OrderedColumns, col.Name) continue } table.Columns[col.Name] = col table.OrderedColumns = append(table.OrderedColumns, col.Name) } for i := range tables { t := &tables[i] t.PartitionKey, t.ClusteringColumns, t.OrderedColumns = compileColumns(t.Columns, t.OrderedColumns) } for i := range views { v := &views[i] v.PartitionKey, v.ClusteringColumns, v.OrderedColumns = compileColumns(v.Columns, v.OrderedColumns) } for i := range indexes { ix := &indexes[i] ix.PartitionKey, ix.ClusteringColumns, ix.OrderedColumns = compileColumns(ix.Columns, ix.OrderedColumns) } } func compileColumns(columns map[string]*ColumnMetadata, orderedColumns []string) ( partitionKey, clusteringColumns []*ColumnMetadata, sortedColumns []string) { clusteringColumnCount := componentColumnCountOfType(columns, ColumnClusteringKey) clusteringColumns = make([]*ColumnMetadata, clusteringColumnCount) partitionKeyCount := componentColumnCountOfType(columns, ColumnPartitionKey) partitionKey = make([]*ColumnMetadata, partitionKeyCount) var otherColumns []string for _, columnName := range orderedColumns { column := columns[columnName] if column.Kind == ColumnPartitionKey { partitionKey[column.ComponentIndex] = column } else if column.Kind == ColumnClusteringKey { clusteringColumns[column.ComponentIndex] = column } else { otherColumns = append(otherColumns, columnName) } } sortedColumns = orderedColumns[:0] for _, pk := range partitionKey { sortedColumns = append(sortedColumns, pk.Name) } for _, ck := range clusteringColumns { sortedColumns = append(sortedColumns, ck.Name) } for _, oc := range otherColumns { sortedColumns = append(sortedColumns, oc) } return } // returns the count of coluns with the given "kind" value. func componentColumnCountOfType(columns map[string]*ColumnMetadata, kind ColumnKind) int { maxComponentIndex := -1 for _, column := range columns { if column.Kind == kind && column.ComponentIndex > maxComponentIndex { maxComponentIndex = column.ComponentIndex } } return maxComponentIndex + 1 } // query for keyspace metadata in the system_schema.keyspaces func getKeyspaceMetadata(session *Session, keyspaceName string) (*KeyspaceMetadata, error) { if !session.useSystemSchema { return nil, ErrKeyspaceDoesNotExist } keyspace := &KeyspaceMetadata{Name: keyspaceName} const stmt = `SELECT durable_writes, replication FROM system_schema.keyspaces WHERE keyspace_name = ?` var replication map[string]string iter := session.control.querySystem(stmt, keyspaceName) if iter.NumRows() == 0 { iter.Close() return nil, ErrKeyspaceDoesNotExist } iter.Scan(&keyspace.DurableWrites, &replication) err := iter.Close() if err != nil { return nil, fmt.Errorf("error querying keyspace schema: %v", err) } keyspace.StrategyClass = replication["class"] delete(replication, "class") keyspace.StrategyOptions = make(map[string]any, len(replication)) for k, v := range replication { keyspace.StrategyOptions[k] = v } return keyspace, nil } // query for table metadata in the system_schema.tables, and system_schema.scylla_tables // if connected to ScyllaDB func getTableMetadata(session *Session, keyspaceName string) ([]TableMetadata, error) { if !session.useSystemSchema { return nil, nil } stmt := `SELECT * FROM system_schema.tables WHERE keyspace_name = ?` iter := session.control.querySystem(stmt, keyspaceName) var tables []TableMetadata table := TableMetadata{Keyspace: keyspaceName} for iter.MapScan(map[string]any{ "table_name": &table.Name, "bloom_filter_fp_chance": &table.Options.BloomFilterFpChance, "caching": &table.Options.Caching, "comment": &table.Options.Comment, "compaction": &table.Options.Compaction, "compression": &table.Options.Compression, "crc_check_chance": &table.Options.CrcCheckChance, "default_time_to_live": &table.Options.DefaultTimeToLive, "gc_grace_seconds": &table.Options.GcGraceSeconds, "max_index_interval": &table.Options.MaxIndexInterval, "memtable_flush_period_in_ms": &table.Options.MemtableFlushPeriodInMs, "min_index_interval": &table.Options.MinIndexInterval, "speculative_retry": &table.Options.SpeculativeRetry, "flags": &table.Flags, "extensions": &table.Extensions, }) { tables = append(tables, table) table = TableMetadata{Keyspace: keyspaceName} } err := iter.Close() if err != nil && err != ErrNotFound { return nil, fmt.Errorf("error querying table schema: %v", err) } conn := session.getConn() if conn == nil || !conn.isScyllaConn() { return tables, nil } // Fetch all ScyllaDB-specific table properties in a single query // instead of issuing one query per table (N+1 elimination). stmt = `SELECT * FROM system_schema.scylla_tables WHERE keyspace_name = ?` iter = session.control.querySystem(stmt, keyspaceName) scyllaOpts := make(map[string]TableMetadataOptions, len(tables)) var opts TableMetadataOptions var tblName string for iter.MapScan(map[string]any{ "table_name": &tblName, "cdc": &opts.CDC, "in_memory": &opts.InMemory, "partitioner": &opts.Partitioner, "version": &opts.Version, }) { scyllaOpts[tblName] = opts opts = TableMetadataOptions{} tblName = "" } if err := iter.Close(); err != nil && err != ErrNotFound { return nil, fmt.Errorf("error querying scylla table schema: %v", err) } for i, t := range tables { if sopts, ok := scyllaOpts[t.Name]; ok { tables[i].Options.CDC = sopts.CDC tables[i].Options.InMemory = sopts.InMemory tables[i].Options.Partitioner = sopts.Partitioner tables[i].Options.Version = sopts.Version } } return tables, nil } func getTableMetadataByName(session *Session, keyspaceName, tableName string) ([]TableMetadata, error) { if !session.useSystemSchema { return nil, nil } stmt := `SELECT * FROM system_schema.tables WHERE keyspace_name = ? AND table_name = ?` iter := session.control.querySystem(stmt, keyspaceName, tableName) var tables []TableMetadata table := TableMetadata{Keyspace: keyspaceName} for iter.MapScan(map[string]any{ "table_name": &table.Name, "bloom_filter_fp_chance": &table.Options.BloomFilterFpChance, "caching": &table.Options.Caching, "comment": &table.Options.Comment, "compaction": &table.Options.Compaction, "compression": &table.Options.Compression, "crc_check_chance": &table.Options.CrcCheckChance, "default_time_to_live": &table.Options.DefaultTimeToLive, "gc_grace_seconds": &table.Options.GcGraceSeconds, "max_index_interval": &table.Options.MaxIndexInterval, "memtable_flush_period_in_ms": &table.Options.MemtableFlushPeriodInMs, "min_index_interval": &table.Options.MinIndexInterval, "speculative_retry": &table.Options.SpeculativeRetry, "flags": &table.Flags, "extensions": &table.Extensions, }) { tables = append(tables, table) table = TableMetadata{Keyspace: keyspaceName} } err := iter.Close() if err != nil && err != ErrNotFound { return nil, fmt.Errorf("error querying table schema: %w", err) } if conn := session.getConn(); conn == nil || !conn.isScyllaConn() { return tables, nil } stmt = `SELECT * FROM system_schema.scylla_tables WHERE keyspace_name = ? AND table_name = ?` for i, t := range tables { iter := session.control.querySystem(stmt, keyspaceName, t.Name) table := TableMetadata{} if iter.MapScan(map[string]any{ "cdc": &table.Options.CDC, "in_memory": &table.Options.InMemory, "partitioner": &table.Options.Partitioner, "version": &table.Options.Version, }) { tables[i].Options.CDC = table.Options.CDC tables[i].Options.Version = table.Options.Version tables[i].Options.Partitioner = table.Options.Partitioner tables[i].Options.InMemory = table.Options.InMemory } if err := iter.Close(); err != nil && err != ErrNotFound { return nil, fmt.Errorf("error querying scylla table schema: %w", err) } } return tables, nil } // columnMetadataColumns lists the columns consumed by getColumnMetadata and // getColumnMetadataByTable. Selecting only these columns (instead of SELECT *) // avoids deserializing unused fields such as keyspace_name (already known from // the WHERE clause) and column_name_bytes (ScyllaDB-specific), which can add // over 50 KB of wasted payload per keyspace with 80+ tables. const columnMetadataColumns = `table_name, column_name, clustering_order, type, kind, position` func getColumnMetadataByTable(session *Session, keyspaceName, tableName string) ([]ColumnMetadata, error) { const stmt = `SELECT ` + columnMetadataColumns + ` FROM system_schema.columns WHERE keyspace_name = ? AND table_name = ?` var columns []ColumnMetadata iter := session.control.querySystem(stmt, keyspaceName, tableName) column := ColumnMetadata{Keyspace: keyspaceName} for iter.MapScan(map[string]any{ "table_name": &column.Table, "column_name": &column.Name, "clustering_order": &column.ClusteringOrder, "type": &column.Type, "kind": &column.Kind, "position": &column.ComponentIndex, }) { columns = append(columns, column) column = ColumnMetadata{Keyspace: keyspaceName} } if err := iter.Close(); err != nil && err != ErrNotFound { return nil, fmt.Errorf("error querying column schema: %w", err) } return columns, nil } func getIndexMetadataByTable(session *Session, keyspaceName, tableName string) ([]IndexMetadata, error) { if !session.useSystemSchema { return nil, nil } const stmt = `SELECT * FROM system_schema.indexes WHERE keyspace_name = ? AND table_name = ?` var indexes []IndexMetadata index := IndexMetadata{} iter := session.control.querySystem(stmt, keyspaceName, tableName) for iter.MapScan(map[string]any{ "index_name": &index.Name, "keyspace_name": &index.KeyspaceName, "table_name": &index.TableName, "kind": &index.Kind, "options": &index.Options, }) { indexes = append(indexes, index) index = IndexMetadata{} } if err := iter.Close(); err != nil { return nil, err } return indexes, nil } func getViewMetadataByTable(session *Session, keyspaceName, tableName string) ([]ViewMetadata, error) { if !session.useSystemSchema { return nil, nil } stmt := `SELECT * FROM system_schema.views WHERE keyspace_name = ? AND base_table_name = ? ALLOW FILTERING` iter := session.control.querySystem(stmt, keyspaceName, tableName) var views []ViewMetadata view := ViewMetadata{KeyspaceName: keyspaceName} for iter.MapScan(map[string]any{ "id": &view.ID, "view_name": &view.ViewName, "base_table_id": &view.BaseTableID, "base_table_name": &view.BaseTableName, "include_all_columns": &view.IncludeAllColumns, "where_clause": &view.WhereClause, "bloom_filter_fp_chance": &view.Options.BloomFilterFpChance, "caching": &view.Options.Caching, "comment": &view.Options.Comment, "compaction": &view.Options.Compaction, "compression": &view.Options.Compression, "crc_check_chance": &view.Options.CrcCheckChance, "default_time_to_live": &view.Options.DefaultTimeToLive, "gc_grace_seconds": &view.Options.GcGraceSeconds, "max_index_interval": &view.Options.MaxIndexInterval, "memtable_flush_period_in_ms": &view.Options.MemtableFlushPeriodInMs, "min_index_interval": &view.Options.MinIndexInterval, "speculative_retry": &view.Options.SpeculativeRetry, "extensions": &view.Extensions, "dclocal_read_repair_chance": &view.DcLocalReadRepairChance, "read_repair_chance": &view.ReadRepairChance, }) { views = append(views, view) view = ViewMetadata{KeyspaceName: keyspaceName} } err := iter.Close() if err != nil && err != ErrNotFound { return nil, fmt.Errorf("error querying view schema: %w", err) } return views, nil } // query for column metadata in the system_schema.columns func getColumnMetadata(session *Session, keyspaceName string) ([]ColumnMetadata, error) { const stmt = `SELECT ` + columnMetadataColumns + ` FROM system_schema.columns WHERE keyspace_name = ?` var columns []ColumnMetadata iter := session.control.querySystem(stmt, keyspaceName) column := ColumnMetadata{Keyspace: keyspaceName} for iter.MapScan(map[string]any{ "table_name": &column.Table, "column_name": &column.Name, "clustering_order": &column.ClusteringOrder, "type": &column.Type, "kind": &column.Kind, "position": &column.ComponentIndex, }) { columns = append(columns, column) column = ColumnMetadata{Keyspace: keyspaceName} } if err := iter.Close(); err != nil && err != ErrNotFound { return nil, fmt.Errorf("error querying column schema: %v", err) } return columns, nil } // query for type metadata in the system_schema.types func getTypeMetadata(session *Session, keyspaceName string) ([]TypeMetadata, error) { if !session.useSystemSchema { return nil, nil } stmt := `SELECT * FROM system_schema.types WHERE keyspace_name = ?` iter := session.control.querySystem(stmt, keyspaceName) var types []TypeMetadata tm := TypeMetadata{Keyspace: keyspaceName} for iter.MapScan(map[string]any{ "type_name": &tm.Name, "field_names": &tm.FieldNames, "field_types": &tm.FieldTypes, }) { types = append(types, tm) tm = TypeMetadata{Keyspace: keyspaceName} } if err := iter.Close(); err != nil { return nil, err } return types, nil } // query for function metadata in the system_schema.functions func getFunctionsMetadata(session *Session, keyspaceName string) ([]FunctionMetadata, error) { if !session.hasAggregatesAndFunctions || !session.useSystemSchema { return nil, nil } stmt := `SELECT * FROM system_schema.functions WHERE keyspace_name = ?` var functions []FunctionMetadata function := FunctionMetadata{Keyspace: keyspaceName} iter := session.control.querySystem(stmt, keyspaceName) for iter.MapScan(map[string]any{ "function_name": &function.Name, "argument_types": &function.ArgumentTypes, "argument_names": &function.ArgumentNames, "body": &function.Body, "called_on_null_input": &function.CalledOnNullInput, "language": &function.Language, "return_type": &function.ReturnType, }) { functions = append(functions, function) function = FunctionMetadata{Keyspace: keyspaceName} } if err := iter.Close(); err != nil { return nil, err } return functions, nil } // query for aggregate metadata in the system_schema.aggregates func getAggregatesMetadata(session *Session, keyspaceName string) ([]AggregateMetadata, error) { if !session.hasAggregatesAndFunctions || !session.useSystemSchema { return nil, nil } const stmt = `SELECT * FROM system_schema.aggregates WHERE keyspace_name = ?` var aggregates []AggregateMetadata aggregate := AggregateMetadata{Keyspace: keyspaceName} iter := session.control.querySystem(stmt, keyspaceName) for iter.MapScan(map[string]any{ "aggregate_name": &aggregate.Name, "argument_types": &aggregate.ArgumentTypes, "final_func": &aggregate.finalFunc, "initcond": &aggregate.InitCond, "return_type": &aggregate.ReturnType, "state_func": &aggregate.stateFunc, "state_type": &aggregate.StateType, }) { aggregates = append(aggregates, aggregate) aggregate = AggregateMetadata{Keyspace: keyspaceName} } if err := iter.Close(); err != nil { return nil, err } return aggregates, nil } // query for index metadata in the system_schema.indexes func getIndexMetadata(session *Session, keyspaceName string) ([]IndexMetadata, error) { if !session.useSystemSchema { return nil, nil } const stmt = `SELECT * FROM system_schema.indexes WHERE keyspace_name = ?` var indexes []IndexMetadata index := IndexMetadata{} iter := session.control.querySystem(stmt, keyspaceName) for iter.MapScan(map[string]any{ "index_name": &index.Name, "keyspace_name": &index.KeyspaceName, "table_name": &index.TableName, "kind": &index.Kind, "options": &index.Options, }) { indexes = append(indexes, index) index = IndexMetadata{} } if err := iter.Close(); err != nil { return nil, err } return indexes, nil } // get create statements for the keyspace func getCreateStatements(session *Session, keyspaceName string) ([]byte, error) { if !session.useSystemSchema { return nil, nil } iter := session.control.query(fmt.Sprintf(`DESCRIBE KEYSPACE %s WITH INTERNALS`, keyspaceName)) var createStatements []string var stmt string for iter.Scan(nil, nil, nil, &stmt) { if stmt == "" { continue } createStatements = append(createStatements, stmt) } if err := iter.Close(); err != nil { if errFrame, ok := err.(frm.ErrorFrame); ok && errFrame.Code == ErrCodeSyntax { // DESCRIBE KEYSPACE is not supported on older versions of Cassandra and Scylla // For such case schema statement is going to be recreated on the client side return nil, nil } return nil, fmt.Errorf("error querying keyspace schema: %v", err) } return []byte(strings.Join(createStatements, "\n")), nil } // query for view metadata in the system_schema.views func getViewMetadata(session *Session, keyspaceName string) ([]ViewMetadata, error) { if !session.useSystemSchema { return nil, nil } stmt := `SELECT * FROM system_schema.views WHERE keyspace_name = ?` iter := session.control.querySystem(stmt, keyspaceName) var views []ViewMetadata view := ViewMetadata{KeyspaceName: keyspaceName} for iter.MapScan(map[string]any{ "id": &view.ID, "view_name": &view.ViewName, "base_table_id": &view.BaseTableID, "base_table_name": &view.BaseTableName, "include_all_columns": &view.IncludeAllColumns, "where_clause": &view.WhereClause, "bloom_filter_fp_chance": &view.Options.BloomFilterFpChance, "caching": &view.Options.Caching, "comment": &view.Options.Comment, "compaction": &view.Options.Compaction, "compression": &view.Options.Compression, "crc_check_chance": &view.Options.CrcCheckChance, "default_time_to_live": &view.Options.DefaultTimeToLive, "gc_grace_seconds": &view.Options.GcGraceSeconds, "max_index_interval": &view.Options.MaxIndexInterval, "memtable_flush_period_in_ms": &view.Options.MemtableFlushPeriodInMs, "min_index_interval": &view.Options.MinIndexInterval, "speculative_retry": &view.Options.SpeculativeRetry, "extensions": &view.Extensions, "dclocal_read_repair_chance": &view.DcLocalReadRepairChance, "read_repair_chance": &view.ReadRepairChance, }) { views = append(views, view) view = ViewMetadata{KeyspaceName: keyspaceName} } err := iter.Close() if err != nil && err != ErrNotFound { return nil, fmt.Errorf("error querying view schema: %v", err) } return views, nil } ================================================ FILE: metadata_scylla_test.go ================================================ //go:build unit // +build unit // Copyright (c) 2015 The gocql Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package gocql import ( "testing" ) func TestGetKeyspaceMetadataMissingKeyspaceClosesIter(t *testing.T) { t.Parallel() framer := &trackingMockFramer{} session := &Session{ useSystemSchema: true, control: &systemSchemaTestControl{ iter: &Iter{framer: framer}, }, } _, err := getKeyspaceMetadata(session, "missing_keyspace") if err != ErrKeyspaceDoesNotExist { t.Fatalf("getKeyspaceMetadata() error = %v, want %v", err, ErrKeyspaceDoesNotExist) } if !framer.released { t.Fatal("expected iterator framer to be released on missing keyspace") } } // Tests metadata "compilation" from example data which might be returned // from metadata schema queries (see getKeyspaceMetadata, getTableMetadata, and getColumnMetadata) func TestCompileMetadata(t *testing.T) { t.Parallel() keyspace := &KeyspaceMetadata{ Name: "V2Keyspace", } tables := []TableMetadata{ { Keyspace: "V2Keyspace", Name: "Table1", }, { Keyspace: "V2Keyspace", Name: "Table2", }, } columns := []ColumnMetadata{ { Keyspace: "V2Keyspace", Table: "Table1", Name: "KEY1", Kind: ColumnPartitionKey, ComponentIndex: 0, Type: "text", }, { Keyspace: "V2Keyspace", Table: "Table1", Name: "Key1", Kind: ColumnPartitionKey, ComponentIndex: 0, Type: "text", }, { Keyspace: "V2Keyspace", Table: "Table2", Name: "Column1", Kind: ColumnPartitionKey, ComponentIndex: 0, Type: "text", }, { Keyspace: "V2Keyspace", Table: "Table2", Name: "Column2", Kind: ColumnClusteringKey, ComponentIndex: 0, Type: "text", }, { Keyspace: "V2Keyspace", Table: "Table2", Name: "Column3", Kind: ColumnClusteringKey, ComponentIndex: 1, Type: "text", ClusteringOrder: "desc", }, { Keyspace: "V2Keyspace", Table: "Table2", Name: "Column4", Kind: ColumnRegular, Type: "text", }, { Keyspace: "V2Keyspace", Table: "view", Name: "ColReg", Kind: ColumnRegular, Type: "text", }, { Keyspace: "V2Keyspace", Table: "view", Name: "ColCK", Kind: ColumnClusteringKey, Type: "text", }, { Keyspace: "V2Keyspace", Table: "view", Name: "ColPK", Kind: ColumnPartitionKey, Type: "text", }, { Keyspace: "V2Keyspace", Table: "buckets_by_owner_index", Name: "idx_token", Kind: ColumnClusteringKey, ComponentIndex: 0, Type: "bigint", ClusteringOrder: "asc", }, { Keyspace: "V2Keyspace", Table: "buckets_by_owner_index", Name: "name", Kind: ColumnClusteringKey, ComponentIndex: 1, Type: "text", ClusteringOrder: "asc", }, { Keyspace: "V2Keyspace", Table: "buckets_by_owner_index", Name: "owner", Kind: ColumnPartitionKey, Type: "text", }, } // Consider an index by column `owner` on the base table `buckets` with // partition key `name`. // // CREATE INDEX buckets_by_owner ON stg_msk_a.buckets(owner); indexes := []IndexMetadata{ {Name: "buckets_by_owner"}, } views := []ViewMetadata{ { KeyspaceName: "V2Keyspace", ViewName: "view", }, { KeyspaceName: "V2Keyspace", ViewName: "buckets_by_owner_index", }, } compileMetadata(keyspace, tables, columns, nil, nil, nil, indexes, views, nil) assertKeyspaceMetadata( t, keyspace, &KeyspaceMetadata{ Name: "V2Keyspace", Views: map[string]*ViewMetadata{ "view": { PartitionKey: []*ColumnMetadata{ { Name: "ColPK", Type: "text", }, }, ClusteringColumns: []*ColumnMetadata{ { Name: "ColCK", Type: "text", }, }, OrderedColumns: []string{ "ColPK", "ColCK", "ColReg", }, Columns: map[string]*ColumnMetadata{ "ColPK": { Name: "ColPK", Kind: ColumnPartitionKey, Type: "text", }, "ColCK": { Name: "ColCK", Kind: ColumnClusteringKey, Type: "text", }, "ColReg": { Name: "ColReg", Kind: ColumnRegular, Type: "text", }, }, }, }, Tables: map[string]*TableMetadata{ "Table1": { PartitionKey: []*ColumnMetadata{ { Name: "Key1", Type: "text", }, }, ClusteringColumns: []*ColumnMetadata{}, Columns: map[string]*ColumnMetadata{ "KEY1": { Name: "KEY1", Type: "text", Kind: ColumnPartitionKey, }, "Key1": { Name: "Key1", Type: "text", Kind: ColumnPartitionKey, }, }, OrderedColumns: []string{ "Key1", }, }, "Table2": { PartitionKey: []*ColumnMetadata{ { Name: "Column1", Type: "text", }, }, ClusteringColumns: []*ColumnMetadata{ { Name: "Column2", Type: "text", Order: ASC, }, { Name: "Column3", Type: "text", Order: DESC, }, }, Columns: map[string]*ColumnMetadata{ "Column1": { Name: "Column1", Type: "text", Kind: ColumnPartitionKey, }, "Column2": { Name: "Column2", Type: "text", Order: ASC, Kind: ColumnClusteringKey, }, "Column3": { Name: "Column3", Type: "text", Order: DESC, Kind: ColumnClusteringKey, }, "Column4": { Name: "Column4", Type: "text", Kind: ColumnRegular, }, }, OrderedColumns: []string{ "Column1", "Column2", "Column3", "Column4", }, }, }, Indexes: map[string]*IndexMetadata{ "buckets_by_owner": { Name: "buckets_by_owner", TableName: "buckets_by_owner_index", PartitionKey: []*ColumnMetadata{ {Name: "owner", Type: "text"}, }, ClusteringColumns: []*ColumnMetadata{ {Name: "idx_token", Type: "bigint"}, {Name: "name", Type: "text"}, }, OrderedColumns: []string{ "owner", "idx_token", "name", }, Columns: map[string]*ColumnMetadata{ "owner": { Name: "owner", Type: "text", Kind: ColumnPartitionKey, }, "idx_token": { Name: "idx_token", Type: "bigint", Order: ASC, Kind: ColumnClusteringKey, }, "name": { Name: "name", Type: "text", Order: ASC, Kind: ColumnClusteringKey, }, }, }, }, }, ) } func assertPartitionKey(t *testing.T, keyspaceName, tableName string, actual, expected []*ColumnMetadata) { if len(expected) != len(actual) { t.Errorf("Expected len(%s.Tables[%s].PartitionKey) to be %v but was %v", keyspaceName, tableName, len(expected), len(actual)) } else { for i := range expected { if expected[i].Name != actual[i].Name { t.Errorf("Expected %s.Tables[%s].PartitionKey[%d].Name to be '%v' but was '%v'", keyspaceName, tableName, i, expected[i].Name, actual[i].Name) } if keyspaceName != actual[i].Keyspace { t.Errorf("Expected %s.Tables[%s].PartitionKey[%d].Keyspace to be '%v' but was '%v'", keyspaceName, tableName, i, keyspaceName, actual[i].Keyspace) } if tableName != actual[i].Table { t.Errorf("Expected %s.Tables[%s].PartitionKey[%d].Table to be '%v' but was '%v'", keyspaceName, tableName, i, tableName, actual[i].Table) } if expected[i].Type != actual[i].Type { t.Errorf("Expected %s.Tables[%s].PartitionKey[%d].Type.Type to be %v but was %v", keyspaceName, tableName, i, expected[i].Type, actual[i].Type) } if i != actual[i].ComponentIndex { t.Errorf("Expected %s.Tables[%s].PartitionKey[%d].ComponentIndex to be %v but was %v", keyspaceName, tableName, i, i, actual[i].ComponentIndex) } if ColumnPartitionKey != actual[i].Kind { t.Errorf("Expected %s.Tables[%s].PartitionKey[%d].Kind to be '%v' but was '%v'", keyspaceName, tableName, i, ColumnPartitionKey, actual[i].Kind) } } } } func assertClusteringColumns(t *testing.T, keyspaceName, tableName string, actual, expected []*ColumnMetadata) { if len(expected) != len(actual) { t.Errorf("Expected len(%s.Tables[%s].ClusteringColumns) to be %v but was %v", keyspaceName, tableName, len(expected), len(actual)) } else { for i := range expected { if actual[i] == nil { t.Fatalf("Unexpected nil value: %s.Tables[%s].ClusteringColumns[%d]", keyspaceName, tableName, i) } if expected[i].Name != actual[i].Name { t.Errorf("Expected %s.Tables[%s].ClusteringColumns[%d].Name to be '%v' but was '%v'", keyspaceName, tableName, i, expected[i].Name, actual[i].Name) } if keyspaceName != actual[i].Keyspace { t.Errorf("Expected %s.Tables[%s].ClusteringColumns[%d].Keyspace to be '%v' but was '%v'", keyspaceName, tableName, i, keyspaceName, actual[i].Keyspace) } if tableName != actual[i].Table { t.Errorf("Expected %s.Tables[%s].ClusteringColumns[%d].Table to be '%v' but was '%v'", keyspaceName, tableName, i, tableName, actual[i].Table) } if expected[i].Type != actual[i].Type { t.Errorf("Expected %s.Tables[%s].ClusteringColumns[%d].Type.Type to be %v but was %v", keyspaceName, tableName, i, expected[i].Type, actual[i].Type) } if i != actual[i].ComponentIndex { t.Errorf("Expected %s.Tables[%s].ClusteringColumns[%d].ComponentIndex to be %v but was %v", keyspaceName, tableName, i, i, actual[i].ComponentIndex) } if expected[i].Order != actual[i].Order { t.Errorf("Expected %s.Tables[%s].ClusteringColumns[%d].Order to be %v but was %v", keyspaceName, tableName, i, expected[i].Order, actual[i].Order) } if ColumnClusteringKey != actual[i].Kind { t.Errorf("Expected %s.Tables[%s].ClusteringColumns[%d].Kind to be '%v' but was '%v'", keyspaceName, tableName, i, ColumnClusteringKey, actual[i].Kind) } } } } func assertColumns(t *testing.T, keyspaceName, tableName string, actual, expected map[string]*ColumnMetadata) { if len(expected) != len(actual) { eKeys := make([]string, 0, len(expected)) for key := range expected { eKeys = append(eKeys, key) } aKeys := make([]string, 0, len(actual)) for key := range actual { aKeys = append(aKeys, key) } t.Errorf("Expected len(%s.Tables[%s].Columns) to be %v (keys:%v) but was %v (keys:%v)", keyspaceName, tableName, len(expected), eKeys, len(actual), aKeys) } else { for keyC := range expected { ec := expected[keyC] ac, found := actual[keyC] if !found { t.Errorf("Expected %s.Tables[%s].Columns[%s] but was not found", keyspaceName, tableName, keyC) } else { if keyC != ac.Name { t.Errorf("Expected %s.Tables[%s].Columns[%s].Name to be '%v' but was '%v'", keyspaceName, tableName, keyC, keyC, tableName) } if keyspaceName != ac.Keyspace { t.Errorf("Expected %s.Tables[%s].Columns[%s].Keyspace to be '%v' but was '%v'", keyspaceName, tableName, keyC, keyspaceName, ac.Keyspace) } if tableName != ac.Table { t.Errorf("Expected %s.Tables[%s].Columns[%s].Table to be '%v' but was '%v'", keyspaceName, tableName, keyC, tableName, ac.Table) } if ec.Type != ac.Type { t.Errorf("Expected %s.Tables[%s].Columns[%s].Type.Type to be %v but was %v", keyspaceName, tableName, keyC, ec.Type, ac.Type) } if ec.Order != ac.Order { t.Errorf("Expected %s.Tables[%s].Columns[%s].Order to be %v but was %v", keyspaceName, tableName, keyC, ec.Order, ac.Order) } if ec.Kind != ac.Kind { t.Errorf("Expected %s.Tables[%s].Columns[%s].Kind to be '%v' but was '%v'", keyspaceName, tableName, keyC, ec.Kind, ac.Kind) } } } } } func assertOrderedColumns(t *testing.T, keyspaceName, tableName string, actual, expected []string) { if len(expected) != len(actual) { t.Errorf("Expected len(%s.Tables[%s].OrderedColumns to be %v but was %v", keyspaceName, tableName, len(expected), len(actual)) } else { for i, eoc := range expected { aoc := actual[i] if eoc != aoc { t.Errorf("Expected %s.Tables[%s].OrderedColumns[%d] to be %s, but was %s", keyspaceName, tableName, i, eoc, aoc) } } } } func assertTableMetadata(t *testing.T, keyspaceName string, actual, expected map[string]*TableMetadata) { if len(expected) != len(actual) { t.Errorf("Expected len(%s.Tables) to be %v but was %v", keyspaceName, len(expected), len(actual)) } for keyT := range expected { et := expected[keyT] at, found := actual[keyT] if !found { t.Errorf("Expected %s.Tables[%s] but was not found", keyspaceName, keyT) } else { if keyT != at.Name { t.Errorf("Expected %s.Tables[%s].Name to be %v but was %v", keyspaceName, keyT, keyT, at.Name) } assertPartitionKey(t, keyspaceName, keyT, at.PartitionKey, et.PartitionKey) assertClusteringColumns(t, keyspaceName, keyT, at.ClusteringColumns, et.ClusteringColumns) assertColumns(t, keyspaceName, keyT, at.Columns, et.Columns) assertOrderedColumns(t, keyspaceName, keyT, at.OrderedColumns, et.OrderedColumns) } } } func assertViewsMetadata(t *testing.T, keyspaceName string, actual, expected map[string]*ViewMetadata) { if len(expected) != len(actual) { t.Errorf("Expected len(%s.Views) to be %v but was %v", keyspaceName, len(expected), len(actual)) } for keyT := range expected { et := expected[keyT] at, found := actual[keyT] if !found { t.Errorf("Expected %s.Views[%s] but was not found", keyspaceName, keyT) } else { if keyT != at.ViewName { t.Errorf("Expected %s.Views[%s].Name to be %v but was %v", keyspaceName, keyT, keyT, at.ViewName) } assertPartitionKey(t, keyspaceName, keyT, at.PartitionKey, et.PartitionKey) assertClusteringColumns(t, keyspaceName, keyT, at.ClusteringColumns, et.ClusteringColumns) assertColumns(t, keyspaceName, keyT, at.Columns, et.Columns) assertOrderedColumns(t, keyspaceName, keyT, at.OrderedColumns, et.OrderedColumns) } } } func assertIndicesMetadata(t *testing.T, keyspaceName string, actual, expected map[string]*IndexMetadata) { if len(expected) != len(actual) { t.Errorf("Expected len(%s.Indexes) to be %v but was %v", keyspaceName, len(expected), len(actual)) } for key := range expected { viewName := key + "_index" et := expected[key] at, found := actual[key] if !found { t.Errorf("Expected %s.Indexes[%s] but was not found", keyspaceName, key) } else { if et.Name != at.Name { t.Errorf("Expected %s.Indexes[%s].Name to be %v but was %v", keyspaceName, key, et.Name, at.Name) } assertPartitionKey(t, keyspaceName, viewName, at.PartitionKey, et.PartitionKey) assertClusteringColumns(t, keyspaceName, viewName, at.ClusteringColumns, et.ClusteringColumns) assertColumns(t, keyspaceName, viewName, at.Columns, et.Columns) assertOrderedColumns(t, keyspaceName, viewName, at.OrderedColumns, et.OrderedColumns) } } } // Helper function for asserting that actual metadata returned was as expected func assertKeyspaceMetadata(t *testing.T, actual, expected *KeyspaceMetadata) { assertTableMetadata(t, expected.Name, actual.Tables, expected.Tables) assertViewsMetadata(t, expected.Name, actual.Views, expected.Views) assertIndicesMetadata(t, expected.Name, actual.Indexes, expected.Indexes) } ================================================ FILE: policies.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2012, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql // This file will be the future home for more policies import ( "context" "errors" "fmt" "math" "math/rand" randv2 "math/rand/v2" "sync" "sync/atomic" "time" ) // cowHostList implements a copy on write host list, its equivalent type is []*HostInfo type cowHostList struct { list atomic.Value mu sync.Mutex } func (c *cowHostList) String() string { return fmt.Sprintf("%+v", c.get()) } func (c *cowHostList) get() []*HostInfo { // TODO(zariel): should we replace this with []*HostInfo? l, ok := c.list.Load().(*[]*HostInfo) if !ok { return nil } return *l } // add will add a host if it not already in the list func (c *cowHostList) add(host *HostInfo) bool { c.mu.Lock() l := c.get() if n := len(l); n == 0 { l = []*HostInfo{host} } else { newL := make([]*HostInfo, n+1) for i := 0; i < n; i++ { if host.Equal(l[i]) { c.mu.Unlock() return false } newL[i] = l[i] } newL[n] = host l = newL } c.list.Store(&l) c.mu.Unlock() return true } func (c *cowHostList) remove(host *HostInfo) bool { c.mu.Lock() l := c.get() size := len(l) if size == 0 { c.mu.Unlock() return false } found := false newL := make([]*HostInfo, 0, size) for i := 0; i < len(l); i++ { if !l[i].Equal(host) { newL = append(newL, l[i]) } else { found = true } } if !found { c.mu.Unlock() return false } newL = newL[: size-1 : size-1] c.list.Store(&newL) c.mu.Unlock() return true } // RetryableQuery is an interface that represents a query or batch statement that // exposes the correct functions for the retry policy logic to evaluate correctly. type RetryableQuery interface { Attempts() int SetConsistency(c Consistency) GetConsistency() Consistency Context() context.Context } type RetryType uint16 const ( Retry RetryType = 0x00 // retry on same connection RetryNextHost RetryType = 0x01 // retry on another connection Ignore RetryType = 0x02 // same as Rethrow Rethrow RetryType = 0x03 // raise error and stop retrying ) // ErrUnknownRetryType is returned if the retry policy returns a retry type // unknown to the query executor. var ErrUnknownRetryType = errors.New("unknown retry type returned by retry policy") // RetryPolicy interface is used by gocql to determine if a query can be attempted // again after a retryable error has been received. The interface allows gocql // users to implement their own logic to determine if a query can be attempted // again. // // See SimpleRetryPolicy as an example of implementing and using a RetryPolicy // interface. type RetryPolicy interface { Attempt(RetryableQuery) bool GetRetryType(error) RetryType } // LWTRetryPolicy is a similar interface to RetryPolicy // If a query is recognized as an LWT query and its RetryPolicy satisfies this // interface, then this interface will be used instead of RetryPolicy. type LWTRetryPolicy interface { AttemptLWT(RetryableQuery) bool GetRetryTypeLWT(error) RetryType } // SimpleRetryPolicy has simple logic for attempting a query a fixed number of times. // // See below for examples of usage: // // //Assign to the cluster // cluster.RetryPolicy = &gocql.SimpleRetryPolicy{NumRetries: 3} // // //Assign to a query // query.RetryPolicy(&gocql.SimpleRetryPolicy{NumRetries: 1}) type SimpleRetryPolicy struct { NumRetries int // Number of times to retry a query } // Attempt tells gocql to attempt the query again based on query.Attempts being less // than the NumRetries defined in the policy. func (s *SimpleRetryPolicy) Attempt(q RetryableQuery) bool { return q.Attempts() <= s.NumRetries } func (s *SimpleRetryPolicy) AttemptLWT(q RetryableQuery) bool { return s.Attempt(q) } func (s *SimpleRetryPolicy) GetRetryType(err error) RetryType { var executedErr *QueryError if errors.As(err, &executedErr) && executedErr.PotentiallyExecuted() && !executedErr.IsIdempotent() { return Rethrow } return RetryNextHost } // Retrying on a different host is fine for normal (non-LWT) queries, // but in case of LWTs it will cause Paxos contention and possibly // even timeouts if other clients send statements touching the same // partition to the original node at the same time. func (s *SimpleRetryPolicy) GetRetryTypeLWT(err error) RetryType { var executedErr *QueryError if errors.As(err, &executedErr) && executedErr.PotentiallyExecuted() && !executedErr.IsIdempotent() { return Rethrow } return Retry } // ExponentialBackoffRetryPolicy sleeps between attempts type ExponentialBackoffRetryPolicy struct { NumRetries int Min, Max time.Duration } func (e *ExponentialBackoffRetryPolicy) Attempt(q RetryableQuery) bool { if q.Attempts() > e.NumRetries { return false } time.Sleep(e.napTime(q.Attempts())) return true } func (e *ExponentialBackoffRetryPolicy) AttemptLWT(q RetryableQuery) bool { return e.Attempt(q) } // used to calculate exponentially growing time func getExponentialTime(min time.Duration, max time.Duration, attempts int) time.Duration { if min <= 0 { min = 100 * time.Millisecond } if max <= 0 { max = 10 * time.Second } minFloat := float64(min) napDuration := minFloat * math.Pow(2, float64(attempts-1)) // add some jitter napDuration += rand.Float64()*minFloat - (minFloat / 2) if napDuration > float64(max) { return time.Duration(max) } return time.Duration(napDuration) } func (e *ExponentialBackoffRetryPolicy) GetRetryType(err error) RetryType { var executedErr *QueryError if errors.As(err, &executedErr) && executedErr.PotentiallyExecuted() && !executedErr.IsIdempotent() { return Rethrow } return RetryNextHost } // Retrying on a different host is fine for normal (non-LWT) queries, // but in case of LWTs it will cause Paxos contention and possibly // even timeouts if other clients send statements touching the same // partition to the original node at the same time. func (e *ExponentialBackoffRetryPolicy) GetRetryTypeLWT(err error) RetryType { var executedErr *QueryError if errors.As(err, &executedErr) && executedErr.PotentiallyExecuted() && !executedErr.IsIdempotent() { return Rethrow } return Retry } // DowngradingConsistencyRetryPolicy: Next retry will be with the next consistency level // provided in the slice // // On a read timeout: the operation is retried with the next provided consistency // level. // // On a write timeout: if the operation is an :attr:`~.UNLOGGED_BATCH` // and at least one replica acknowledged the write, the operation is // retried with the next consistency level. Furthermore, for other // write types, if at least one replica acknowledged the write, the // timeout is ignored. // // On an unavailable exception: if at least one replica is alive, the // operation is retried with the next provided consistency level. type DowngradingConsistencyRetryPolicy struct { ConsistencyLevelsToTry []Consistency } func (d *DowngradingConsistencyRetryPolicy) Attempt(q RetryableQuery) bool { currentAttempt := q.Attempts() if currentAttempt > len(d.ConsistencyLevelsToTry) { return false } else if currentAttempt > 0 { q.SetConsistency(d.ConsistencyLevelsToTry[currentAttempt-1]) } return true } func (d *DowngradingConsistencyRetryPolicy) GetRetryType(err error) RetryType { var executedErr *QueryError if errors.As(err, &executedErr) { err = executedErr.err if executedErr.PotentiallyExecuted() && !executedErr.IsIdempotent() { return Rethrow } } switch t := err.(type) { case *RequestErrUnavailable: if t.Alive > 0 { return Retry } return Rethrow case *RequestErrWriteTimeout: if t.WriteType == "SIMPLE" || t.WriteType == "BATCH" || t.WriteType == "COUNTER" { if t.Received > 0 { return Ignore } return Rethrow } if t.WriteType == "UNLOGGED_BATCH" { return Retry } return Rethrow case *RequestErrReadTimeout: return Retry default: return RetryNextHost } } func (e *ExponentialBackoffRetryPolicy) napTime(attempts int) time.Duration { return getExponentialTime(e.Min, e.Max, attempts) } type HostStateNotifier interface { AddHost(host *HostInfo) RemoveHost(host *HostInfo) HostUp(host *HostInfo) HostDown(host *HostInfo) } type KeyspaceUpdateEvent struct { Keyspace string Change string } type HostTierer interface { // HostTier returns an integer specifying how far a host is from the client. // Tier must start at 0. // The value is used to prioritize closer hosts during host selection. // For example this could be: // 0 - local rack, 1 - local DC, 2 - remote DC // or: // 0 - local DC, 1 - remote DC HostTier(host *HostInfo) uint // This function returns the maximum possible host tier MaxHostTier() uint } // HostSelectionPolicy is an interface for selecting // the most appropriate host to execute a given query. // HostSelectionPolicy instances cannot be shared between sessions. type HostSelectionPolicy interface { HostStateNotifier SetPartitioner KeyspaceChanged(KeyspaceUpdateEvent) Init(*Session) // Reset is opprotunity to reset HostSelectionPolicy if Session initilization failed and we want to // call HostSelectionPolicy.Init() again with new Session Reset() IsLocal(host *HostInfo) bool // Pick returns an iteration function over selected hosts. // Multiple attempts of a single query execution won't call the returned NextHost function concurrently, // so it's safe to have internal state without additional synchronization as long as every call to Pick returns // a different instance of NextHost. Pick(ExecutableQuery) NextHost // IsOperational checks if host policy can properly work with given Session/Cluster/ClusterConfig IsOperational(*Session) error } // SelectedHost is an interface returned when picking a host from a host // selection policy. type SelectedHost interface { Info() *HostInfo Token() Token Mark(error) } type selectedHost struct { info *HostInfo token Token } func (host selectedHost) Info() *HostInfo { return host.info } func (host selectedHost) Token() Token { return host.token } func (host selectedHost) Mark(err error) {} func newSingleHost(info *HostInfo, maxRetries byte, retryDelay time.Duration) *singleHost { return &singleHost{info: info, maxRetries: maxRetries, delay: retryDelay} } type singleHost struct { info *HostInfo delay time.Duration retry byte maxRetries byte } func (s *singleHost) selectHost() SelectedHost { if s.retry >= s.maxRetries { return nil } if s.retry > 0 && s.delay > 0 { time.Sleep(s.delay) } s.retry++ return s } func (s singleHost) Info() *HostInfo { return s.info } func (s singleHost) Token() Token { return nil } func (s singleHost) Mark(error) {} // NextHost is an iteration function over picked hosts type NextHost func() SelectedHost // RoundRobinHostPolicy is a round-robin load balancing policy, where each host // is tried sequentially for each query. func RoundRobinHostPolicy() HostSelectionPolicy { return &roundRobinHostPolicy{} } type roundRobinHostPolicy struct { hosts cowHostList lastUsedHostIdx uint64 } func (r *roundRobinHostPolicy) IsLocal(*HostInfo) bool { return true } func (r *roundRobinHostPolicy) KeyspaceChanged(KeyspaceUpdateEvent) {} func (r *roundRobinHostPolicy) SetPartitioner(partitioner string) {} func (r *roundRobinHostPolicy) Init(*Session) {} func (r *roundRobinHostPolicy) Reset() {} func (r *roundRobinHostPolicy) IsOperational(*Session) error { return nil } func (r *roundRobinHostPolicy) Pick(qry ExecutableQuery) NextHost { nextStartOffset := atomic.AddUint64(&r.lastUsedHostIdx, 1) return roundRobbin(int(nextStartOffset), r.hosts.get()) } func (r *roundRobinHostPolicy) AddHost(host *HostInfo) { r.hosts.add(host) } func (r *roundRobinHostPolicy) RemoveHost(host *HostInfo) { r.hosts.remove(host) } func (r *roundRobinHostPolicy) HostUp(host *HostInfo) { r.AddHost(host) } func (r *roundRobinHostPolicy) HostDown(host *HostInfo) { r.RemoveHost(host) } func ShuffleReplicas() func(*tokenAwareHostPolicy) { return func(t *tokenAwareHostPolicy) { t.shuffleReplicas = true } } func DontShuffleReplicas() func(*tokenAwareHostPolicy) { return func(t *tokenAwareHostPolicy) { t.shuffleReplicas = false } } // AvoidSlowReplicas enabled avoiding slow replicas // // TokenAwareHostPolicy normally does not check how busy replica is, with avoidSlowReplicas enabled it avoids replicas // if they have equal or more than MAX_IN_FLIGHT_THRESHOLD requests in flight func AvoidSlowReplicas(max_in_flight_threshold int) func(policy *tokenAwareHostPolicy) { return func(t *tokenAwareHostPolicy) { t.avoidSlowReplicas = true MAX_IN_FLIGHT_THRESHOLD = max_in_flight_threshold } } // NonLocalReplicasFallback enables fallback to replicas that are not considered local. // // TokenAwareHostPolicy used with DCAwareHostPolicy fallback first selects replicas by partition key in local DC, then // falls back to other nodes in the local DC. Enabling NonLocalReplicasFallback causes TokenAwareHostPolicy // to first select replicas by partition key in local DC, then replicas by partition key in remote DCs and fall back // to other nodes in local DC. func NonLocalReplicasFallback() func(policy *tokenAwareHostPolicy) { return func(t *tokenAwareHostPolicy) { t.nonLocalReplicasFallback = true } } // TokenAwareHostPolicy is a token aware host selection policy, where hosts are // selected based on the partition key, so queries are sent to the host which // owns the partition. Fallback is used when routing information is not available. func TokenAwareHostPolicy(fallback HostSelectionPolicy, opts ...func(*tokenAwareHostPolicy)) HostSelectionPolicy { p := &tokenAwareHostPolicy{ fallback: fallback, shuffleReplicas: true, } for _, opt := range opts { opt(p) } return p } // clusterMeta holds metadata about cluster topology. // It is used inside atomic.Value and shallow copies are used when replacing it, // so fields should not be modified in-place. Instead, to modify a field a copy of the field should be made // and the pointer in clusterMeta updated to point to the new value. type clusterMeta struct { // replicas is map[keyspace]map[token]hosts replicas map[string]tokenRingReplicas tokenRing *tokenRing } var MAX_IN_FLIGHT_THRESHOLD int = 10 type tokenAwareHostPolicy struct { fallback HostSelectionPolicy // atomic store for *clusterMeta metadata atomic.Value logger StdLogger getKeyspaceMetadata func(keyspace string) (*KeyspaceMetadata, error) getKeyspaceName func() string hosts cowHostList partitioner string // mu protects writes to hosts, partitioner, metadata. // reads can be unlocked as long as they are not used for updating state later. mu sync.Mutex shuffleReplicas bool nonLocalReplicasFallback bool avoidSlowReplicas bool } func (t *tokenAwareHostPolicy) Init(s *Session) { t.mu.Lock() defer t.mu.Unlock() if t.getKeyspaceMetadata != nil { // Init was already called. // See https://github.com/scylladb/gocql/issues/94. panic("sharing token aware host selection policy between sessions is not supported") } t.getKeyspaceMetadata = func(keyspace string) (*KeyspaceMetadata, error) { if keyspace == "" { return nil, ErrNoKeyspace } return s.metadataDescriber.GetKeyspace(keyspace) } t.getKeyspaceName = func() string { return s.cfg.Keyspace } t.logger = s.logger } func (t *tokenAwareHostPolicy) Reset() { t.mu.Lock() defer t.mu.Unlock() // Sharing token aware host selection policy between sessions is not supported // but session initialization can failed for some reasons. So in our application // may be we want to create new session again. // Reset method should be called in Session.Close method t.getKeyspaceMetadata = nil t.getKeyspaceName = nil t.logger = nil } func (t *tokenAwareHostPolicy) IsOperational(session *Session) error { return t.fallback.IsOperational(session) } func (t *tokenAwareHostPolicy) IsLocal(host *HostInfo) bool { return t.fallback.IsLocal(host) } func (t *tokenAwareHostPolicy) KeyspaceChanged(update KeyspaceUpdateEvent) { t.mu.Lock() defer t.mu.Unlock() meta := t.getMetadataForUpdate() t.updateReplicas(meta, update.Keyspace) t.metadata.Store(meta) } // updateReplicas updates replicas in clusterMeta. // It must be called with t.mu mutex locked. // meta must not be nil and it's replicas field will be updated. func (t *tokenAwareHostPolicy) updateReplicas(meta *clusterMeta, keyspace string) { if keyspace == "" || meta == nil || meta.tokenRing == nil { return } newReplicas := make(map[string]tokenRingReplicas, len(meta.replicas)) ks, err := t.getKeyspaceMetadata(keyspace) if err == nil { strat := getStrategy(ks, t.logger) if strat != nil { newReplicas[keyspace] = strat.replicaMap(meta.tokenRing) } } for ks, replicas := range meta.replicas { if ks != keyspace { newReplicas[ks] = replicas } } meta.replicas = newReplicas } func (t *tokenAwareHostPolicy) SetPartitioner(partitioner string) { t.mu.Lock() defer t.mu.Unlock() if t.partitioner != partitioner { t.fallback.SetPartitioner(partitioner) t.partitioner = partitioner meta := t.getMetadataForUpdate() meta.resetTokenRing(t.partitioner, t.hosts.get(), t.logger) t.updateReplicas(meta, t.getKeyspaceName()) t.metadata.Store(meta) } } func (t *tokenAwareHostPolicy) AddHost(host *HostInfo) { t.mu.Lock() if t.hosts.add(host) { meta := t.getMetadataForUpdate() meta.resetTokenRing(t.partitioner, t.hosts.get(), t.logger) t.updateReplicas(meta, t.getKeyspaceName()) t.metadata.Store(meta) } t.mu.Unlock() t.fallback.AddHost(host) } func (t *tokenAwareHostPolicy) AddHosts(hosts []*HostInfo) { t.mu.Lock() for _, host := range hosts { t.hosts.add(host) } meta := t.getMetadataForUpdate() meta.resetTokenRing(t.partitioner, t.hosts.get(), t.logger) t.updateReplicas(meta, t.getKeyspaceName()) t.metadata.Store(meta) t.mu.Unlock() for _, host := range hosts { t.fallback.AddHost(host) } } func (t *tokenAwareHostPolicy) RemoveHost(host *HostInfo) { t.mu.Lock() if t.hosts.remove(host) { meta := t.getMetadataForUpdate() meta.resetTokenRing(t.partitioner, t.hosts.get(), t.logger) t.updateReplicas(meta, t.getKeyspaceName()) t.metadata.Store(meta) } t.mu.Unlock() t.fallback.RemoveHost(host) } func (t *tokenAwareHostPolicy) HostUp(host *HostInfo) { t.fallback.HostUp(host) } func (t *tokenAwareHostPolicy) HostDown(host *HostInfo) { t.fallback.HostDown(host) } // getMetadataReadOnly returns current cluster metadata. // Metadata uses copy on write, so the returned value should be only used for reading. // To obtain a copy that could be updated, use getMetadataForUpdate instead. func (t *tokenAwareHostPolicy) getMetadataReadOnly() *clusterMeta { meta, _ := t.metadata.Load().(*clusterMeta) return meta } // getMetadataForUpdate returns clusterMeta suitable for updating. // It is a SHALLOW copy of current metadata in case it was already set or new empty clusterMeta otherwise. // This function should be called with t.mu mutex locked and the mutex should not be released before // storing the new metadata. func (t *tokenAwareHostPolicy) getMetadataForUpdate() *clusterMeta { metaReadOnly := t.getMetadataReadOnly() meta := new(clusterMeta) if metaReadOnly != nil { *meta = *metaReadOnly } return meta } // resetTokenRing creates a new tokenRing. // It must be called with t.mu locked. func (m *clusterMeta) resetTokenRing(partitioner string, hosts []*HostInfo, logger StdLogger) { if partitioner == "" { // partitioner not yet set return } // create a new token ring tokenRing, err := newTokenRing(partitioner, hosts) if err != nil { logger.Printf("Unable to update the token ring due to error: %s", err) return } // replace the token ring m.tokenRing = tokenRing } // hostSet is a small set optimized for tracking hosts returned by the // token-aware iterator. Uses an inline array for RF <= 9 (3 DCs × RF=3), // spilling to a map for larger replica sets. type hostSet struct { overflow map[*HostInfo]struct{} arr [9]*HostInfo n int } func (s *hostSet) add(h *HostInfo) { if s.n < len(s.arr) { s.arr[s.n] = h s.n++ return } if s.overflow == nil { s.overflow = make(map[*HostInfo]struct{}) for i := range s.n { s.overflow[s.arr[i]] = struct{}{} } } s.overflow[h] = struct{}{} } func (s *hostSet) contains(h *HostInfo) bool { if s.overflow != nil { _, ok := s.overflow[h] return ok } for i := range s.n { if s.arr[i] == h { return true } } return false } // shuffleHostsInPlace shuffles the given slice in-place using math/rand/v2. func shuffleHostsInPlace(hosts []*HostInfo) { randv2.Shuffle(len(hosts), func(i, j int) { hosts[i], hosts[j] = hosts[j], hosts[i] }) } // partitionHealthy performs an in-place stable partition of replicas, moving // healthy (non-busy) hosts to the front while preserving relative order. func partitionHealthy(replicas []*HostInfo, s *Session) { n := len(replicas) if n <= 1 { return } // Snapshot IsBusy to avoid TOCTOU races between counting and placement. var busyBuf [9]bool var busy []bool if n <= len(busyBuf) { busy = busyBuf[:n] } else { busy = make([]bool, n) } healthyCount := 0 for i, h := range replicas { busy[i] = h.IsBusy(s) if !busy[i] { healthyCount++ } } if healthyCount == 0 || healthyCount == n { return // all same category, nothing to do } var buf [9]*HostInfo var tmp []*HostInfo if n <= len(buf) { tmp = buf[:n] } else { tmp = make([]*HostInfo, n) } copy(tmp, replicas) hi, ui := 0, healthyCount for i, h := range tmp { if !busy[i] { replicas[hi] = h hi++ } else { replicas[ui] = h ui++ } } } func (t *tokenAwareHostPolicy) Pick(qry ExecutableQuery) NextHost { if qry == nil { return t.fallback.Pick(qry) } routingKey, err := qry.GetRoutingKey() if err != nil { return t.fallback.Pick(qry) } else if routingKey == nil { return t.fallback.Pick(qry) } meta := t.getMetadataReadOnly() if meta == nil || meta.tokenRing == nil { return t.fallback.Pick(qry) } partitioner := qry.GetCustomPartitioner() if partitioner == nil { partitioner = meta.tokenRing.partitioner } token := partitioner.Hash(routingKey) tokenCasted, isInt64Token := token.(int64Token) var replicas []*HostInfo if session := qry.GetSession(); session != nil && session.tabletsRoutingV1 && isInt64Token { tabletReplicas := session.findTabletReplicasUnsafeForToken(qry.Keyspace(), qry.Table(), int64(tokenCasted)) if len(tabletReplicas) != 0 { hosts := t.hosts.get() for _, replica := range tabletReplicas { for _, host := range hosts { if host.hostId == UUID(replica.HostUUIDValue()) { replicas = append(replicas, host) break } } } } } if len(replicas) == 0 { ht := meta.replicas[qry.Keyspace()].replicasFor(token) if ht != nil { needsMutation := t.shuffleReplicas || t.avoidSlowReplicas if needsMutation { replicas = make([]*HostInfo, len(ht.hosts)) copy(replicas, ht.hosts) } else { // Zero-copy: replicas must not be mutated below unless needsMutation is true. replicas = ht.hosts } } } if len(replicas) == 0 { host, _ := meta.tokenRing.GetHostForToken(token) replicas = []*HostInfo{host} } if t.shuffleReplicas && !qry.IsLWT() && len(replicas) > 1 { shuffleHostsInPlace(replicas) } if s := qry.GetSession(); s != nil && !qry.IsLWT() && t.avoidSlowReplicas { partitionHealthy(replicas, s) } var ( fallbackIter NextHost i, j, k int remote [][]*HostInfo tierer HostTierer tiererOk bool maxTier uint ) if tierer, tiererOk = t.fallback.(HostTierer); tiererOk { maxTier = tierer.MaxHostTier() } else { maxTier = 1 } if t.nonLocalReplicasFallback { remote = make([][]*HostInfo, maxTier) } var used hostSet return func() SelectedHost { for i < len(replicas) { h := replicas[i] i++ var tier uint if tiererOk { tier = tierer.HostTier(h) } else if t.fallback.IsLocal(h) { tier = 0 } else { tier = 1 } if tier != 0 { if t.nonLocalReplicasFallback { remote[tier-1] = append(remote[tier-1], h) } continue } if h.IsUp() { used.add(h) return selectedHost{info: h, token: token} } } if t.nonLocalReplicasFallback { for j < len(remote) && k < len(remote[j]) { h := remote[j][k] k++ if k >= len(remote[j]) { j++ k = 0 } if h.IsUp() { used.add(h) return selectedHost{info: h, token: token} } } } if fallbackIter == nil { // fallback fallbackIter = t.fallback.Pick(qry) } // filter the token aware selected hosts from the fallback hosts for fallbackHost := fallbackIter(); fallbackHost != nil; fallbackHost = fallbackIter() { if !used.contains(fallbackHost.Info()) { used.add(fallbackHost.Info()) return fallbackHost } } return nil } } type dcAwareRR struct { local string localHosts cowHostList remoteHosts cowHostList lastUsedHostIdx uint64 disableDCFailover bool } type dcFailoverDisabledPolicy interface { setDCFailoverDisabled() } type dcAwarePolicyOption func(p dcFailoverDisabledPolicy) func HostPolicyOptionDisableDCFailover(p dcFailoverDisabledPolicy) { p.setDCFailoverDisabled() } // DCAwareRoundRobinPolicy is a host selection policies which will prioritize and // return hosts which are in the local datacentre before returning hosts in all // other datercentres func DCAwareRoundRobinPolicy(localDC string, opts ...dcAwarePolicyOption) HostSelectionPolicy { p := &dcAwareRR{local: localDC, disableDCFailover: false} for _, opt := range opts { opt(p) } return p } func (d *dcAwareRR) setDCFailoverDisabled() { d.disableDCFailover = true } func (d *dcAwareRR) Init(*Session) {} func (d *dcAwareRR) Reset() {} func (d *dcAwareRR) KeyspaceChanged(KeyspaceUpdateEvent) {} func (d *dcAwareRR) SetPartitioner(p string) {} func (d *dcAwareRR) IsOperational(session *Session) error { if session.cfg.disableInit || session.cfg.disableControlConn { return nil } hosts := session.hostSource.getHostsList() for _, host := range hosts { if !session.cfg.filterHost(host) && host.DataCenter() == d.local { // Policy can work properly only if there is at least one host from target DC // No need to check host status, since it could be down due to the outage // We only need to make sure that policy is not misconfigured with wrong DC return nil } } return fmt.Errorf("gocql: datacenter %s in the policy was not found in the topology - probable DC aware policy misconfiguration", d.local) } func (d *dcAwareRR) IsLocal(host *HostInfo) bool { return host.DataCenter() == d.local } func (d *dcAwareRR) AddHost(host *HostInfo) { if d.IsLocal(host) { d.localHosts.add(host) } else { d.remoteHosts.add(host) } } func (d *dcAwareRR) RemoveHost(host *HostInfo) { if d.IsLocal(host) { d.localHosts.remove(host) } else { d.remoteHosts.remove(host) } } func (d *dcAwareRR) HostUp(host *HostInfo) { d.AddHost(host) } func (d *dcAwareRR) HostDown(host *HostInfo) { d.RemoveHost(host) } // This function is supposed to be called in a fashion // roundRobbin(offset, hostsPriority1, hostsPriority2, hostsPriority3 ... ) // // E.g. for DC-naive strategy: // roundRobbin(offset, allHosts) // // For tiered and DC-aware strategy: // roundRobbin(offset, localHosts, remoteHosts) func roundRobbin(shift int, hosts ...[]*HostInfo) NextHost { currentLayer := 0 currentlyObserved := 0 return func() SelectedHost { // iterate over layers for { if currentLayer == len(hosts) { return nil } currentLayerSize := len(hosts[currentLayer]) // iterate over hosts within a layer for { currentlyObserved++ if currentlyObserved > currentLayerSize { currentLayer++ currentlyObserved = 0 break } h := hosts[currentLayer][(shift+currentlyObserved)%currentLayerSize] if h.IsUp() { return selectedHost{info: h} } } } } } func (d *dcAwareRR) Pick(q ExecutableQuery) NextHost { nextStartOffset := atomic.AddUint64(&d.lastUsedHostIdx, 1) if d.disableDCFailover { return roundRobbin(int(nextStartOffset), d.localHosts.get()) } return roundRobbin(int(nextStartOffset), d.localHosts.get(), d.remoteHosts.get()) } // RackAwareRoundRobinPolicy is a host selection policies which will prioritize and // return hosts which are in the local rack, before hosts in the local datacenter but // a different rack, before hosts in all other datacenters type rackAwareRR struct { localDC string localRack string hosts []cowHostList // lastUsedHostIdx keeps the index of the last used host. // It is accessed atomically and needs to be aligned to 64 bits, so we // keep it first in the struct. Do not move it or add new struct members // before it. lastUsedHostIdx uint64 disableDCFailover bool } func RackAwareRoundRobinPolicy(localDC string, localRack string, opts ...dcAwarePolicyOption) HostSelectionPolicy { p := &rackAwareRR{localDC: localDC, localRack: localRack, hosts: make([]cowHostList, 3), disableDCFailover: false} for _, opt := range opts { opt(p) } return p } func (d *rackAwareRR) Init(*Session) {} func (d *rackAwareRR) Reset() {} func (d *rackAwareRR) KeyspaceChanged(KeyspaceUpdateEvent) {} func (d *rackAwareRR) SetPartitioner(p string) {} func (d *rackAwareRR) IsOperational(session *Session) error { if session.cfg.disableInit || session.cfg.disableControlConn { return nil } hosts := session.hostSource.getHostsList() for _, host := range hosts { if !session.cfg.filterHost(host) && host.DataCenter() == d.localDC && host.Rack() == d.localRack { // Policy can work properly only if there is at least one host from target DC+Rack // No need to check host status, since it could be down due to the outage // We only need to make sure that policy is not misconfigured with wrong DC+Rack return nil } } return fmt.Errorf("gocql: rack %s/%s was not found in the topology - probable Rack aware policy misconfiguration", d.localDC, d.localRack) } func (d *rackAwareRR) MaxHostTier() uint { return 2 } func (d *rackAwareRR) setDCFailoverDisabled() { d.disableDCFailover = true } func (d *rackAwareRR) HostTier(host *HostInfo) uint { if host.DataCenter() == d.localDC { if host.Rack() == d.localRack { return 0 } else { return 1 } } else { return 2 } } func (d *rackAwareRR) IsLocal(host *HostInfo) bool { return d.HostTier(host) == 0 } func (d *rackAwareRR) AddHost(host *HostInfo) { dist := d.HostTier(host) d.hosts[dist].add(host) } func (d *rackAwareRR) RemoveHost(host *HostInfo) { dist := d.HostTier(host) d.hosts[dist].remove(host) } func (d *rackAwareRR) HostUp(host *HostInfo) { d.AddHost(host) } func (d *rackAwareRR) HostDown(host *HostInfo) { d.RemoveHost(host) } func (d *rackAwareRR) Pick(q ExecutableQuery) NextHost { nextStartOffset := atomic.AddUint64(&d.lastUsedHostIdx, 1) if d.disableDCFailover { return roundRobbin(int(nextStartOffset), d.hosts[0].get(), d.hosts[1].get()) } return roundRobbin(int(nextStartOffset), d.hosts[0].get(), d.hosts[1].get(), d.hosts[2].get()) } // ReadyPolicy defines a policy for when a HostSelectionPolicy can be used. After // each host connects during session initialization, the Ready method will be // called. If you only need a single Host to be up you can wrap a // HostSelectionPolicy policy with SingleHostReadyPolicy. type ReadyPolicy interface { Ready() bool } // SingleHostReadyPolicy wraps a HostSelectionPolicy and returns Ready after a // single host has been added via HostUp func SingleHostReadyPolicy(p HostSelectionPolicy) *singleHostReadyPolicy { return &singleHostReadyPolicy{ HostSelectionPolicy: p, } } type singleHostReadyPolicy struct { HostSelectionPolicy ready bool readyMux sync.Mutex } func (s *singleHostReadyPolicy) HostUp(host *HostInfo) { s.HostSelectionPolicy.HostUp(host) s.readyMux.Lock() s.ready = true s.readyMux.Unlock() } func (s *singleHostReadyPolicy) Ready() bool { s.readyMux.Lock() ready := s.ready s.readyMux.Unlock() if !ready { return false } // in case the wrapped policy is also a ReadyPolicy, defer to that if rdy, ok := s.HostSelectionPolicy.(ReadyPolicy); ok { return rdy.Ready() } return true } // ConvictionPolicy interface is used by gocql to determine if a host should be // marked as DOWN based on the error and host info type ConvictionPolicy interface { // Implementations should return `true` if the host should be convicted, `false` otherwise. AddFailure(error error, host *HostInfo) bool // Implementations should clear out any convictions or state regarding the host. Reset(host *HostInfo) } // SimpleConvictionPolicy implements a ConvictionPolicy which convicts all hosts // regardless of error type SimpleConvictionPolicy struct{} func (e *SimpleConvictionPolicy) AddFailure(error error, host *HostInfo) bool { return true } func (e *SimpleConvictionPolicy) Reset(host *HostInfo) {} // ReconnectionPolicy interface is used by gocql to determine if reconnection // can be attempted after connection error. The interface allows gocql users // to implement their own logic to determine how to attempt reconnection. type ReconnectionPolicy interface { GetInterval(currentRetry int) time.Duration GetMaxRetries() int } // NoReconnectionPolicy is a policy to have no retry. // // Examples of usage: // // cluster.InitialReconnectionPolicy = &NoReconnectionPolicy{} type NoReconnectionPolicy struct { } func (c *NoReconnectionPolicy) GetInterval(currentRetry int) time.Duration { return time.Duration(0) } func (c *NoReconnectionPolicy) GetMaxRetries() int { return 1 } // ConstantReconnectionPolicy has simple logic for returning a fixed reconnection interval. // // Examples of usage: // // cluster.ReconnectionPolicy = &gocql.ConstantReconnectionPolicy{MaxRetries: 10, Interval: 8 * time.Second} type ConstantReconnectionPolicy struct { MaxRetries int Interval time.Duration } func (c *ConstantReconnectionPolicy) GetInterval(currentRetry int) time.Duration { return c.Interval } func (c *ConstantReconnectionPolicy) GetMaxRetries() int { return c.MaxRetries } // ExponentialReconnectionPolicy returns a growing reconnection interval. type ExponentialReconnectionPolicy struct { MaxRetries int InitialInterval time.Duration MaxInterval time.Duration } func (e *ExponentialReconnectionPolicy) GetInterval(currentRetry int) time.Duration { max := e.MaxInterval if max < e.InitialInterval { max = math.MaxInt16 * time.Second } return getExponentialTime(e.InitialInterval, max, currentRetry) } func (e *ExponentialReconnectionPolicy) GetMaxRetries() int { return e.MaxRetries } type SpeculativeExecutionPolicy interface { Attempts() int Delay() time.Duration } type NonSpeculativeExecution struct{} // defaultNonSpecExec is a package-level singleton that avoids allocating a new // NonSpeculativeExecution every time a Query or Batch is initialised. var defaultNonSpecExec SpeculativeExecutionPolicy = &NonSpeculativeExecution{} func (sp NonSpeculativeExecution) Attempts() int { return 0 } // No additional attempts func (sp NonSpeculativeExecution) Delay() time.Duration { return 1 } // The delay. Must be positive to be used in a ticker. type SimpleSpeculativeExecution struct { NumAttempts int TimeoutDelay time.Duration } func (sp *SimpleSpeculativeExecution) Attempts() int { return sp.NumAttempts } func (sp *SimpleSpeculativeExecution) Delay() time.Duration { return sp.TimeoutDelay } ================================================ FILE: policies_bench_test.go ================================================ //go:build unit // +build unit package gocql import ( "fmt" "math" "net" "runtime" "testing" "github.com/gocql/gocql/tablets" ) // setupTabletAwareBench creates a tokenAwareHostPolicy with the given number // of hosts and tablets, wired up to a mock session with tabletsRoutingV1. // It returns the policy, session, and a slice of pre-built queries that hit // different tokens spread across the tablet range. func setupTabletAwareBench(b *testing.B, numHosts, numTablets, rf int) (HostSelectionPolicy, *Session, []*Query) { b.Helper() const keyspace = "benchks" const table = "benchtbl" policy := TokenAwareHostPolicy(RoundRobinHostPolicy()) policyInternal := policy.(*tokenAwareHostPolicy) policyInternal.getKeyspaceName = func() string { return keyspace } policyInternal.getKeyspaceMetadata = func(ks string) (*KeyspaceMetadata, error) { return &KeyspaceMetadata{ Name: keyspace, StrategyClass: "SimpleStrategy", StrategyOptions: map[string]any{ "class": "SimpleStrategy", "replication_factor": rf, }, }, nil } // Create hosts with binary UUIDs (matching what tablets use). hosts := make([]*HostInfo, numHosts) for i := range hosts { hosts[i] = &HostInfo{ hostId: tUUID(i), connectAddress: net.IPv4(10, 0, byte(i>>8), byte(i)), tokens: []string{fmt.Sprintf("%d", int64(math.MinInt64)+int64(i)*100)}, } policy.AddHost(hosts[i]) } policy.SetPartitioner("Murmur3Partitioner") policy.KeyspaceChanged(KeyspaceUpdateEvent{Keyspace: keyspace}) // Set up mock session with tablet routing. ctrl := &schemaDataMock{knownKeyspaces: map[string][]tableInfo{}} s := newSchemaEventTestSession(ctrl, &trackingPolicy{}, "") s.useSystemSchema = true s.isInitialized = true s.tabletsRoutingV1 = true // Build tablets covering the full token range, each with `rf` replicas // drawn round-robin from the host list. step := uint64(math.MaxUint64) / uint64(numTablets) firstToken := int64(math.MinInt64) tabletList := make(tablets.TabletInfoList, numTablets) for i := 0; i < numTablets; i++ { lastToken := firstToken + int64(step) if i == numTablets-1 { lastToken = math.MaxInt64 } reps := make([][]any, rf) for r := 0; r < rf; r++ { hostIdx := (i + r) % numHosts reps[r] = []any{hosts[hostIdx].hostId, 0} } ti, err := tablets.TabletInfoBuilder{ KeyspaceName: keyspace, TableName: table, FirstToken: firstToken, LastToken: lastToken, Replicas: reps, }.Build() if err != nil { b.Fatal(err) } tabletList[i] = ti firstToken = lastToken } s.metadataDescriber.metadata.tabletsMetadata.BulkAddTablets(tabletList) s.metadataDescriber.metadata.tabletsMetadata.Flush() // Pre-build queries that hit evenly spaced tokens. const numQueries = 256 queries := make([]*Query, numQueries) tokenStep := uint64(math.MaxUint64) / uint64(numQueries) for i := range queries { token := int64(math.MinInt64) + int64(uint64(i)*tokenStep) + 1 queries[i] = &Query{ routingInfo: &queryRoutingInfo{ keyspace: keyspace, table: table, partitioner: fixedInt64Partitioner(token), }, session: s, } queries[i].getKeyspace = func() string { return keyspace } queries[i].routingKey = []byte("key") } return policy, s, queries } // BenchmarkTabletAwarePick benchmarks the full tokenAwareHostPolicy.Pick() // path with tablet routing, varying the number of hosts in the cluster. // This measures the O(RF * H) host resolution loop in Pick(). func BenchmarkTabletAwarePick(b *testing.B) { for _, numHosts := range []int{10, 50, 100} { b.Run(fmt.Sprintf("Hosts%d", numHosts), func(b *testing.B) { const numTablets = 10000 const rf = 3 policy, s, queries := setupTabletAwareBench(b, numHosts, numTablets, rf) defer s.Close() runtime.GC() b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { qry := queries[i%len(queries)] iter := policy.Pick(qry) // Consume the first host (happy path — one NextHost call). h := iter() if h == nil { b.Fatal("Pick returned nil on first call") } } }) } } // BenchmarkTabletAwarePickAllReplicas benchmarks exhausting all replicas // from Pick(), simulating the retry/unhappy path. func BenchmarkTabletAwarePickAllReplicas(b *testing.B) { for _, numHosts := range []int{10, 50, 100} { b.Run(fmt.Sprintf("Hosts%d", numHosts), func(b *testing.B) { const numTablets = 10000 const rf = 3 policy, s, queries := setupTabletAwareBench(b, numHosts, numTablets, rf) defer s.Close() runtime.GC() b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { qry := queries[i%len(queries)] iter := policy.Pick(qry) // Exhaust all hosts from the iterator. for h := iter(); h != nil; h = iter() { } } }) } } // BenchmarkHostIdComparison is a micro-benchmark for isolated host-ID // comparisons: string==string (current) baseline. func BenchmarkHostIdComparison(b *testing.B) { id1 := "00000000-0000-0000-0000-000000000001" id2 := "00000000-0000-0000-0000-000000000001" b.Run("StringEqual", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { if id1 != id2 { b.Fatal("should be equal") } } }) // Benchmark with UUIDs for comparison (what the proposed change uses). uuid1 := UUID{} uuid1[15] = 1 uuid2 := UUID{} uuid2[15] = 1 b.Run("UUIDEqual", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { if uuid1 != uuid2 { b.Fatal("should be equal") } } }) } ================================================ FILE: policies_integration_test.go ================================================ //go:build integration // +build integration package gocql import ( "context" "testing" "time" ) // Check if session fail to start if DC name provided in the policy is wrong func TestDCValidationTokenAware(t *testing.T) { t.Parallel() cluster := createCluster() fallback := DCAwareRoundRobinPolicy("WRONG_DC") cluster.PoolConfig.HostSelectionPolicy = TokenAwareHostPolicy(fallback) _, err := cluster.CreateSession() if err == nil { t.Fatal("createSession was expected to fail with wrong DC name provided.") } } func TestDCValidationDCAware(t *testing.T) { t.Parallel() cluster := createCluster() cluster.PoolConfig.HostSelectionPolicy = DCAwareRoundRobinPolicy("WRONG_DC") _, err := cluster.CreateSession() if err == nil { t.Fatal("createSession was expected to fail with wrong DC name provided.") } } func TestDCValidationRackAware(t *testing.T) { t.Parallel() cluster := createCluster() cluster.PoolConfig.HostSelectionPolicy = RackAwareRoundRobinPolicy("WRONG_DC", "RACK") _, err := cluster.CreateSession() if err == nil { t.Fatal("createSession was expected to fail with wrong DC name provided.") } } func TestTokenAwareHostPolicy(t *testing.T) { t.Parallel() t.Run("keyspace", func(t *testing.T) { ks := testKeyspaceName(t) createKeyspace(t, createCluster(), ks, false) policy := TokenAwareHostPolicy(RoundRobinHostPolicy()) tokenPolicy := policy.(*tokenAwareHostPolicy) cluster := createCluster() cluster.Keyspace = ks cluster.PoolConfig.HostSelectionPolicy = policy testIfPolicyInitializedProperly(t, cluster, tokenPolicy) }) t.Run("no-keyspace", func(t *testing.T) { policy := TokenAwareHostPolicy(RoundRobinHostPolicy()) tokenPolicy := policy.(*tokenAwareHostPolicy) cluster := createCluster() cluster.PoolConfig.HostSelectionPolicy = policy testIfPolicyInitializedProperly(t, cluster, tokenPolicy) }) } func testIfPolicyInitializedProperly(t *testing.T, cluster *ClusterConfig, policy *tokenAwareHostPolicy) { _, err := cluster.CreateSession() if err != nil { t.Fatalf("faled to create session: %v", err) } md := policy.getMetadataReadOnly() if md == nil { t.Fatalf("tokenAwareHostPolicy has no metadata") } if len(md.tokenRing.tokens) == 0 { t.Fatalf("tokenAwareHostPolicy metadata has no tokens") } if len(md.tokenRing.hosts) == 0 { t.Fatalf("tokenAwareHostPolicy metadata has no hosts") } if md.tokenRing.partitioner == nil { t.Fatalf("tokenAwareHostPolicy metadata has no partitioner") } if cluster.Keyspace != "" { if len(md.replicas[cluster.Keyspace]) == 0 { t.Fatalf("tokenAwareHostPolicy metadata has no replicas in target keyspace") } } } // TestNoHangAllHostsDown ensures that when all hosts are down, the query execution does not hang. // WARNING: This test must NOT use t.Parallel(). It sets ALL hosts to NodeDown state, // which mutates shared HostInfo objects visible to all concurrent sessions. // //nolint:paralleltest // mutates shared HostInfo state (sets all hosts to NodeDown) func TestNoHangAllHostsDown(t *testing.T) { cluster := createCluster() session := createSessionFromCluster(cluster, t) hosts := session.GetHosts() dc := hosts[0].DataCenter() rack := hosts[0].Rack() session.Close() policies := []HostSelectionPolicy{ DCAwareRoundRobinPolicy(dc), DCAwareRoundRobinPolicy(dc, HostPolicyOptionDisableDCFailover), TokenAwareHostPolicy(DCAwareRoundRobinPolicy(dc)), TokenAwareHostPolicy(DCAwareRoundRobinPolicy(dc, HostPolicyOptionDisableDCFailover)), RackAwareRoundRobinPolicy(dc, rack), RackAwareRoundRobinPolicy(dc, rack, HostPolicyOptionDisableDCFailover), TokenAwareHostPolicy(RackAwareRoundRobinPolicy(dc, rack)), TokenAwareHostPolicy(RackAwareRoundRobinPolicy(dc, rack, HostPolicyOptionDisableDCFailover)), nil, } for _, policy := range policies { cluster = createCluster() cluster.PoolConfig.HostSelectionPolicy = policy session = createSessionFromCluster(cluster, t) hosts = session.GetHosts() // simulating hosts are down for _, host := range hosts { pool, _ := session.pool.getPoolByHostID(host.HostID()) pool.host.setState(NodeDown) if policy != nil { policy.AddHost(host) } } ctx, cancel := context.WithTimeout(context.Background(), 12*time.Second) defer cancel() _ = session.Query("SELECT host_id FROM system.local").WithContext(ctx).Exec() if ctx.Err() != nil { t.Errorf("policy %T should be no hangups when all hosts are down", policy) } // remove all host except one if policy != nil { for i, host := range hosts { if i != 0 { policy.RemoveHost(host) } } } ctx, cancel2 := context.WithTimeout(context.Background(), 12*time.Second) defer cancel2() _ = session.Query("SELECT host_id FROM system.local").WithContext(ctx).Exec() if ctx.Err() != nil { t.Errorf("policy %T should be no hangups when all hosts are down", policy) } session.Close() } } ================================================ FILE: policies_test.go ================================================ // Copyright (c) 2015 The gocql Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build unit // +build unit /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "errors" "fmt" "net" "sort" "strings" "testing" "time" "github.com/gocql/gocql/internal/tests" "github.com/gocql/gocql/tablets" "github.com/google/go-cmp/cmp" ) // tUUID returns a deterministic UUID for testing. Byte 0 is always set to // a non-zero sentinel (0xFE) so that even tUUID(0) is distinguishable from // the zero UUID, and the last two bytes encode n. func tUUID(n int) UUID { var u UUID u[0] = 0xFE u[14] = byte(n >> 8) u[15] = byte(n) return u } // tID returns the string representation of tUUID(n), suitable for passing to // expectHosts and other string-based comparisons. func tID(n int) string { return tUUID(n).String() } // Tests of the round-robin host selection policy implementation func TestRoundRobbin(t *testing.T) { t.Parallel() policy := RoundRobinHostPolicy() hosts := [...]*HostInfo{ {hostId: tUUID(0), connectAddress: net.IPv4(0, 0, 0, 1)}, {hostId: tUUID(1), connectAddress: net.IPv4(0, 0, 0, 2)}, } for _, host := range hosts { policy.AddHost(host) } got := make(map[UUID]bool) it := policy.Pick(nil) for h := it(); h != nil; h = it() { id := h.Info().hostId if got[id] { t.Fatalf("got duplicate host: %v", id) } got[id] = true } if len(got) != len(hosts) { t.Fatalf("expected %d hosts got %d", len(hosts), len(got)) } } func TestRoundRobbinSameConnectAddress(t *testing.T) { t.Parallel() policy := RoundRobinHostPolicy() hosts := [...]*HostInfo{ {hostId: tUUID(0), connectAddress: net.IPv4(0, 0, 0, 1), port: 9042}, {hostId: tUUID(1), connectAddress: net.IPv4(0, 0, 0, 1), port: 9043}, } for _, host := range hosts { policy.AddHost(host) } got := make(map[UUID]bool) it := policy.Pick(nil) for h := it(); h != nil; h = it() { id := h.Info().hostId if got[id] { t.Fatalf("got duplicate host: %v", id) } got[id] = true } if len(got) != len(hosts) { t.Fatalf("expected %d hosts got %d", len(hosts), len(got)) } } // Tests of the token-aware host selection policy implementation with a // round-robin host selection policy fallback. func TestHostPolicy_TokenAware_SimpleStrategy(t *testing.T) { t.Parallel() const keyspace = "myKeyspace" policy := TokenAwareHostPolicy(RoundRobinHostPolicy()) policyInternal := policy.(*tokenAwareHostPolicy) policyInternal.getKeyspaceName = func() string { return keyspace } policyInternal.getKeyspaceMetadata = func(ks string) (*KeyspaceMetadata, error) { return nil, errors.New("not initalized") } query := &Query{routingInfo: &queryRoutingInfo{}} query.getKeyspace = func() string { return keyspace } iter := policy.Pick(nil) if iter == nil { t.Fatal("host iterator was nil") } actual := iter() if actual != nil { t.Fatalf("expected nil from iterator, but was %v", actual) } // set the hosts hosts := [...]*HostInfo{ {hostId: tUUID(0), connectAddress: net.IPv4(10, 0, 0, 1), tokens: []string{"00"}}, {hostId: tUUID(1), connectAddress: net.IPv4(10, 0, 0, 2), tokens: []string{"25"}}, {hostId: tUUID(2), connectAddress: net.IPv4(10, 0, 0, 3), tokens: []string{"50"}}, {hostId: tUUID(3), connectAddress: net.IPv4(10, 0, 0, 4), tokens: []string{"75"}}, } for _, host := range &hosts { policy.AddHost(host) } policy.SetPartitioner("OrderedPartitioner") policyInternal.getKeyspaceMetadata = func(keyspaceName string) (*KeyspaceMetadata, error) { if keyspaceName != keyspace { return nil, fmt.Errorf("unknown keyspace: %s", keyspaceName) } return &KeyspaceMetadata{ Name: keyspace, StrategyClass: "SimpleStrategy", StrategyOptions: map[string]any{ "class": "SimpleStrategy", "replication_factor": 2, }, }, nil } policy.KeyspaceChanged(KeyspaceUpdateEvent{Keyspace: keyspace}) // The SimpleStrategy above should generate the following replicas. // It's handy to have as reference here. tests.AssertDeepEqual(t, "replicas", map[string]tokenRingReplicas{ "myKeyspace": { {orderedToken("00"), []*HostInfo{hosts[0], hosts[1]}}, {orderedToken("25"), []*HostInfo{hosts[1], hosts[2]}}, {orderedToken("50"), []*HostInfo{hosts[2], hosts[3]}}, {orderedToken("75"), []*HostInfo{hosts[3], hosts[0]}}, }, }, policyInternal.getMetadataReadOnly().replicas) // now the token ring is configured query.RoutingKey([]byte("20")) iter = policy.Pick(query) // shuffling is enabled by default, expecfing expectHosts(t, "hosts[0]", iter, tID(1), tID(2)) // then rest of the hosts expectHosts(t, "rest", iter, tID(0), tID(3)) expectNoMoreHosts(t, iter) } func TestHostPolicy_TokenAware_LWT_DisablesHostShuffling(t *testing.T) { t.Parallel() tests := map[string]struct { hosts []*HostInfo routingKey string lwt bool shuffle bool want []string }{ "token 08 shuffling configured": {hosts: []*HostInfo{ {hostId: tUUID(0), connectAddress: net.IPv4(10, 0, 0, 1), tokens: []string{"00", "10", "20"}}, {hostId: tUUID(1), connectAddress: net.IPv4(10, 0, 0, 3), tokens: []string{"25", "35", "45"}}, {hostId: tUUID(2), connectAddress: net.IPv4(10, 0, 0, 2), tokens: []string{"00", "10", "20"}}, {hostId: tUUID(3), connectAddress: net.IPv4(10, 0, 0, 4), tokens: []string{"25", "35", "45"}}, {hostId: tUUID(4), connectAddress: net.IPv4(10, 0, 0, 3), tokens: []string{"50", "60", "70"}}, {hostId: tUUID(5), connectAddress: net.IPv4(10, 0, 0, 4), tokens: []string{"50", "60", "70"}}, }, routingKey: "8", lwt: true, shuffle: true, want: []string{tID(0), tID(2), tID(3), tID(4), tID(5), tID(1)}}, "token 08 shuffling not configured": {hosts: []*HostInfo{ {hostId: tUUID(0), connectAddress: net.IPv4(10, 0, 0, 1), tokens: []string{"00", "10", "20"}}, {hostId: tUUID(1), connectAddress: net.IPv4(10, 0, 0, 3), tokens: []string{"25", "35", "45"}}, {hostId: tUUID(2), connectAddress: net.IPv4(10, 0, 0, 2), tokens: []string{"00", "10", "20"}}, {hostId: tUUID(3), connectAddress: net.IPv4(10, 0, 0, 4), tokens: []string{"25", "35", "45"}}, {hostId: tUUID(4), connectAddress: net.IPv4(10, 0, 0, 3), tokens: []string{"50", "60", "70"}}, {hostId: tUUID(5), connectAddress: net.IPv4(10, 0, 0, 4), tokens: []string{"50", "60", "70"}}, }, routingKey: "8", lwt: true, shuffle: false, want: []string{tID(0), tID(2), tID(3), tID(4), tID(5), tID(1)}}, "token 30 shuffling configured": {hosts: []*HostInfo{ {hostId: tUUID(0), connectAddress: net.IPv4(10, 0, 0, 1), tokens: []string{"00", "10", "20"}}, {hostId: tUUID(1), connectAddress: net.IPv4(10, 0, 0, 3), tokens: []string{"25", "35", "45"}}, {hostId: tUUID(2), connectAddress: net.IPv4(10, 0, 0, 2), tokens: []string{"00", "10", "20"}}, {hostId: tUUID(3), connectAddress: net.IPv4(10, 0, 0, 4), tokens: []string{"25", "35", "45"}}, {hostId: tUUID(4), connectAddress: net.IPv4(10, 0, 0, 3), tokens: []string{"50", "60", "70"}}, {hostId: tUUID(5), connectAddress: net.IPv4(10, 0, 0, 4), tokens: []string{"50", "60", "70"}}, }, routingKey: "30", lwt: true, shuffle: true, want: []string{tID(1), tID(3), tID(2), tID(4), tID(5), tID(0)}}, "token 30 shuffling not configured": {hosts: []*HostInfo{ {hostId: tUUID(0), connectAddress: net.IPv4(10, 0, 0, 1), tokens: []string{"00", "10", "20"}}, {hostId: tUUID(1), connectAddress: net.IPv4(10, 0, 0, 3), tokens: []string{"25", "35", "45"}}, {hostId: tUUID(2), connectAddress: net.IPv4(10, 0, 0, 2), tokens: []string{"00", "10", "20"}}, {hostId: tUUID(3), connectAddress: net.IPv4(10, 0, 0, 4), tokens: []string{"25", "35", "45"}}, {hostId: tUUID(4), connectAddress: net.IPv4(10, 0, 0, 3), tokens: []string{"50", "60", "70"}}, {hostId: tUUID(5), connectAddress: net.IPv4(10, 0, 0, 4), tokens: []string{"50", "60", "70"}}, }, routingKey: "30", lwt: true, shuffle: false, want: []string{tID(1), tID(3), tID(2), tID(4), tID(5), tID(0)}}, "token 55 shuffling configured": {hosts: []*HostInfo{ {hostId: tUUID(0), connectAddress: net.IPv4(10, 0, 0, 1), tokens: []string{"00", "10", "20"}}, {hostId: tUUID(1), connectAddress: net.IPv4(10, 0, 0, 3), tokens: []string{"25", "35", "45"}}, {hostId: tUUID(2), connectAddress: net.IPv4(10, 0, 0, 2), tokens: []string{"00", "10", "20"}}, {hostId: tUUID(3), connectAddress: net.IPv4(10, 0, 0, 4), tokens: []string{"25", "35", "45"}}, {hostId: tUUID(4), connectAddress: net.IPv4(10, 0, 0, 3), tokens: []string{"50", "60", "70"}}, {hostId: tUUID(5), connectAddress: net.IPv4(10, 0, 0, 4), tokens: []string{"50", "60", "70"}}, }, routingKey: "55", lwt: true, shuffle: true, want: []string{tID(4), tID(5), tID(2), tID(3), tID(0), tID(1)}}, "token 55 shuffling not configured": {hosts: []*HostInfo{ {hostId: tUUID(0), connectAddress: net.IPv4(10, 0, 0, 1), tokens: []string{"00", "10", "20"}}, {hostId: tUUID(1), connectAddress: net.IPv4(10, 0, 0, 3), tokens: []string{"25", "35", "45"}}, {hostId: tUUID(2), connectAddress: net.IPv4(10, 0, 0, 2), tokens: []string{"00", "10", "20"}}, {hostId: tUUID(3), connectAddress: net.IPv4(10, 0, 0, 4), tokens: []string{"25", "35", "45"}}, {hostId: tUUID(4), connectAddress: net.IPv4(10, 0, 0, 3), tokens: []string{"50", "60", "70"}}, {hostId: tUUID(5), connectAddress: net.IPv4(10, 0, 0, 4), tokens: []string{"50", "60", "70"}}, }, routingKey: "55", lwt: true, shuffle: false, want: []string{tID(4), tID(5), tID(2), tID(3), tID(0), tID(1)}}, } const keyspace = "myKeyspace" for name, tc := range tests { t.Run(name, func(t *testing.T) { policy := createPolicy(keyspace, tc.shuffle) for _, host := range tc.hosts { policy.AddHost(host) } query := &Query{ routingKey: []byte(tc.routingKey), routingInfo: &queryRoutingInfo{lwt: tc.lwt}, } query.getKeyspace = func() string { return keyspace } iter := policy.Pick(query) var hostIds []string for host := iter(); host != nil; host = iter() { hostIds = append(hostIds, host.Info().HostID()) } if diff := cmp.Diff(hostIds, tc.want); diff != "" { t.Errorf("expected %s, got %s, diff %s", tc.want, hostIds, diff) } }) } } func createPolicy(keyspace string, shuffle bool) HostSelectionPolicy { policy := TokenAwareHostPolicy(RoundRobinHostPolicy()) policyInternal := policy.(*tokenAwareHostPolicy) policyInternal.getKeyspaceName = func() string { return keyspace } policyInternal.getKeyspaceMetadata = func(ks string) (*KeyspaceMetadata, error) { return nil, errors.New("not initalized") } policy.SetPartitioner("OrderedPartitioner") policyInternal.getKeyspaceMetadata = func(keyspaceName string) (*KeyspaceMetadata, error) { if keyspaceName != keyspace { return nil, fmt.Errorf("unknown keyspace: %s", keyspaceName) } return &KeyspaceMetadata{ Name: keyspace, StrategyClass: "SimpleStrategy", StrategyOptions: map[string]any{ "class": "SimpleStrategy", "replication_factor": 2, }, }, nil } policyInternal.shuffleReplicas = shuffle policy.KeyspaceChanged(KeyspaceUpdateEvent{Keyspace: keyspace}) return policy } func TestHostPolicy_RoundRobin_NilHostInfo(t *testing.T) { t.Parallel() policy := RoundRobinHostPolicy() host := &HostInfo{hostId: tUUID(1)} policy.AddHost(host) iter := policy.Pick(nil) next := iter() if next == nil { t.Fatal("got nil host") } else if v := next.Info(); v == nil { t.Fatal("got nil HostInfo") } else if v.HostID() != host.HostID() { t.Fatalf("expected host %v got %v", host, v) } next = iter() if next != nil { t.Errorf("expected to get nil host got %+v", next) if next.Info() == nil { t.Fatalf("HostInfo is nil") } } } func TestHostPolicy_TokenAware_NilHostInfo(t *testing.T) { t.Parallel() policy := TokenAwareHostPolicy(RoundRobinHostPolicy()) policyInternal := policy.(*tokenAwareHostPolicy) policyInternal.getKeyspaceName = func() string { return "myKeyspace" } policyInternal.getKeyspaceMetadata = func(ks string) (*KeyspaceMetadata, error) { return nil, errors.New("not initialized") } hosts := [...]*HostInfo{ {connectAddress: net.IPv4(10, 0, 0, 0), tokens: []string{"00"}}, {connectAddress: net.IPv4(10, 0, 0, 1), tokens: []string{"25"}}, {connectAddress: net.IPv4(10, 0, 0, 2), tokens: []string{"50"}}, {connectAddress: net.IPv4(10, 0, 0, 3), tokens: []string{"75"}}, } for _, host := range hosts { policy.AddHost(host) } policy.SetPartitioner("OrderedPartitioner") query := &Query{routingInfo: &queryRoutingInfo{}} query.getKeyspace = func() string { return "myKeyspace" } query.RoutingKey([]byte("20")) iter := policy.Pick(query) next := iter() if next == nil { t.Fatal("got nil host") } else if v := next.Info(); v == nil { t.Fatal("got nil HostInfo") } else if !v.ConnectAddress().Equal(hosts[1].ConnectAddress()) { t.Fatalf("expected peer 1 got %v", v.ConnectAddress()) } // Empty the hosts to trigger the panic when using the fallback. for _, host := range hosts { policy.RemoveHost(host) } next = iter() if next != nil { t.Errorf("expected to get nil host got %+v", next) if next.Info() == nil { t.Fatalf("HostInfo is nil") } } } func TestCOWList_Add(t *testing.T) { t.Parallel() var cow cowHostList toAdd := [...]net.IP{net.IPv4(10, 0, 0, 1), net.IPv4(10, 0, 0, 2), net.IPv4(10, 0, 0, 3)} for _, addr := range toAdd { if !cow.add(&HostInfo{connectAddress: addr}) { t.Fatal("did not add peer which was not in the set") } } hosts := cow.get() if len(hosts) != len(toAdd) { t.Fatalf("expected to have %d hosts got %d", len(toAdd), len(hosts)) } set := make(map[string]bool) for _, host := range hosts { set[string(host.ConnectAddress())] = true } for _, addr := range toAdd { if !set[string(addr)] { t.Errorf("addr was not in the host list: %q", addr) } } } // TestSimpleRetryPolicy makes sure that we only allow 1 + numRetries attempts func TestSimpleRetryPolicy(t *testing.T) { t.Parallel() q := &Query{routingInfo: &queryRoutingInfo{}} // this should allow a total of 3 tries. rt := &SimpleRetryPolicy{NumRetries: 2} regular_error := errors.New("regular error") qe1 := &QueryError{ err: errors.New("connection error"), potentiallyExecuted: false, isIdempotent: false, } qe2 := &QueryError{ err: errors.New("timeout error"), potentiallyExecuted: true, isIdempotent: true, } qe3 := &QueryError{ err: errors.New("write timeout"), potentiallyExecuted: true, isIdempotent: false, } cases := []struct { attempts int allow bool err error retryType RetryType LWTRetryType RetryType }{ {0, true, qe1, RetryNextHost, Retry}, {1, true, qe2, RetryNextHost, Retry}, {2, true, qe3, Rethrow, Rethrow}, {3, false, regular_error, RetryNextHost, Retry}, {4, false, regular_error, RetryNextHost, Retry}, {5, false, regular_error, RetryNextHost, Retry}, } for _, c := range cases { q.metrics = preFilledQueryMetrics(map[UUID]*hostMetrics{TimeUUID(): {Attempts: c.attempts}}) if c.retryType != rt.GetRetryType(c.err) { t.Fatalf("retry type for %v should be %v", c.err, c.retryType) } if c.LWTRetryType != rt.GetRetryTypeLWT(c.err) { t.Fatalf("LWT retry type for %v should be %v", c.err, c.LWTRetryType) } if c.allow && !rt.Attempt(q) { t.Fatalf("should allow retry after %d attempts", c.attempts) } if !c.allow && rt.Attempt(q) { t.Fatalf("should not allow retry after %d attempts", c.attempts) } } } func TestLWTSimpleRetryPolicy(t *testing.T) { t.Parallel() ebrp := &SimpleRetryPolicy{NumRetries: 2} // Verify that SimpleRetryPolicy implements both interfaces var _ RetryPolicy = ebrp var lwt_rt LWTRetryPolicy = ebrp tests.AssertEqual(t, "retry type of LWT policy", lwt_rt.GetRetryTypeLWT(nil), Retry) } func TestExponentialBackoffPolicy(t *testing.T) { t.Parallel() // test with defaults sut := &ExponentialBackoffRetryPolicy{NumRetries: 2} regular_error := errors.New("regular error") qe1 := &QueryError{ err: errors.New("connection error"), potentiallyExecuted: false, isIdempotent: false, } qe2 := &QueryError{ err: errors.New("timeout error"), potentiallyExecuted: true, isIdempotent: true, } qe3 := &QueryError{ err: errors.New("write timeout"), potentiallyExecuted: true, isIdempotent: false, } cases := []struct { attempts int delay time.Duration err error retryType RetryType LWTRetryType RetryType }{ {1, 100 * time.Millisecond, qe1, RetryNextHost, Retry}, {2, (2) * 100 * time.Millisecond, qe2, RetryNextHost, Retry}, {3, (2 * 2) * 100 * time.Millisecond, qe3, Rethrow, Rethrow}, {4, (2 * 2 * 2) * 100 * time.Millisecond, regular_error, RetryNextHost, Retry}, } for _, c := range cases { if c.retryType != sut.GetRetryType(c.err) { t.Fatalf("retry type for %v should be %v", c.err, c.retryType) } if c.LWTRetryType != sut.GetRetryTypeLWT(c.err) { t.Fatalf("LWT retry type for %v should be %v", c.err, c.LWTRetryType) } // test 100 times for each case for i := 0; i < 100; i++ { d := sut.napTime(c.attempts) if d < c.delay-(100*time.Millisecond)/2 { t.Fatalf("Delay %d less than jitter min of %d", d, c.delay-100*time.Millisecond/2) } if d > c.delay+(100*time.Millisecond)/2 { t.Fatalf("Delay %d greater than jitter max of %d", d, c.delay+100*time.Millisecond/2) } } } } func TestLWTExponentialBackoffPolicy(t *testing.T) { t.Parallel() ebrp := &ExponentialBackoffRetryPolicy{NumRetries: 2} // Verify that ExponentialBackoffRetryPolicy implements both interfaces var _ RetryPolicy = ebrp var lwt_rt LWTRetryPolicy = ebrp tests.AssertEqual(t, "retry type of LWT policy", lwt_rt.GetRetryTypeLWT(nil), Retry) } func TestDowngradingConsistencyRetryPolicy(t *testing.T) { t.Parallel() q := &Query{cons: LocalQuorum, routingInfo: &queryRoutingInfo{}} rewt0 := &RequestErrWriteTimeout{ Received: 0, WriteType: "SIMPLE", } rewt1 := &RequestErrWriteTimeout{ Received: 1, WriteType: "BATCH", } rewt2 := &RequestErrWriteTimeout{ WriteType: "UNLOGGED_BATCH", } rert := &RequestErrReadTimeout{} reu0 := &RequestErrUnavailable{ Alive: 0, } reu1 := &RequestErrUnavailable{ Alive: 1, } // this should allow a total of 3 tries. consistencyLevels := []Consistency{Three, Two, One} rt := &DowngradingConsistencyRetryPolicy{ConsistencyLevelsToTry: consistencyLevels} cases := []struct { attempts int allow bool err error retryType RetryType }{ {0, true, rewt0, Rethrow}, {3, true, rewt1, Ignore}, {1, true, rewt2, Retry}, {2, true, rert, Retry}, {4, false, reu0, Rethrow}, {16, false, reu1, Retry}, } for _, c := range cases { q.metrics = preFilledQueryMetrics(map[UUID]*hostMetrics{TimeUUID(): {Attempts: c.attempts}}) if c.retryType != rt.GetRetryType(c.err) { t.Fatalf("retry type should be %v", c.retryType) } if c.allow && !rt.Attempt(q) { t.Fatalf("should allow retry after %d attempts", c.attempts) } if !c.allow && rt.Attempt(q) { t.Fatalf("should not allow retry after %d attempts", c.attempts) } } } // expectHosts makes sure that the next len(hostIDs) returned from iter is a permutation of hostIDs. func expectHosts(t *testing.T, msg string, iter NextHost, hostIDs ...string) { t.Helper() expectedHostIDs := make(map[string]struct{}, len(hostIDs)) for i := range hostIDs { expectedHostIDs[hostIDs[i]] = struct{}{} } expectedStr := func() string { keys := make([]string, 0, len(expectedHostIDs)) for k := range expectedHostIDs { keys = append(keys, k) } sort.Strings(keys) return strings.Join(keys, ", ") } for len(expectedHostIDs) > 0 { host := iter() if host == nil || host.Info() == nil { t.Fatalf("%s: expected hostID one of {%s}, but got nil", msg, expectedStr()) } hostID := host.Info().HostID() if _, ok := expectedHostIDs[hostID]; !ok { t.Fatalf("%s: expected host ID one of {%s}, but got %s", msg, expectedStr(), hostID) } delete(expectedHostIDs, hostID) } } func expectNoMoreHosts(t *testing.T, iter NextHost) { t.Helper() host := iter() if host == nil { // success return } info := host.Info() if info == nil { t.Fatalf("expected no more hosts, but got host with nil Info()") return } t.Fatalf("expected no more hosts, but got %s", info.HostID()) } func TestHostPolicy_DCAwareRR(t *testing.T) { t.Parallel() p := DCAwareRoundRobinPolicy("local") hosts := [...]*HostInfo{ {hostId: tUUID(0), connectAddress: net.ParseIP("10.0.0.1"), dataCenter: "local"}, {hostId: tUUID(1), connectAddress: net.ParseIP("10.0.0.2"), dataCenter: "local"}, {hostId: tUUID(2), connectAddress: net.ParseIP("10.0.0.3"), dataCenter: "remote"}, {hostId: tUUID(3), connectAddress: net.ParseIP("10.0.0.4"), dataCenter: "remote"}, } for _, host := range hosts { p.AddHost(host) } got := make(map[UUID]bool, len(hosts)) var dcs []string it := p.Pick(nil) for h := it(); h != nil; h = it() { id := h.Info().hostId dc := h.Info().dataCenter if got[id] { t.Fatalf("got duplicate host %s", id) } got[id] = true dcs = append(dcs, dc) } if len(got) != len(hosts) { t.Fatalf("expected %d hosts got %d", len(hosts), len(got)) } var remote bool for _, dc := range dcs { if dc == "local" { if remote { t.Fatalf("got local dc after remote: %v", dcs) } } else { remote = true } } } func TestHostPolicy_DCAwareRR_disableDCFailover(t *testing.T) { t.Parallel() p := DCAwareRoundRobinPolicy("local", HostPolicyOptionDisableDCFailover) hosts := [...]*HostInfo{ {hostId: tUUID(0), connectAddress: net.ParseIP("10.0.0.1"), dataCenter: "local"}, {hostId: tUUID(1), connectAddress: net.ParseIP("10.0.0.2"), dataCenter: "local"}, {hostId: tUUID(2), connectAddress: net.ParseIP("10.0.0.3"), dataCenter: "remote"}, {hostId: tUUID(3), connectAddress: net.ParseIP("10.0.0.4"), dataCenter: "remote"}, } for _, host := range hosts { p.AddHost(host) } got := make(map[UUID]bool, len(hosts)) var dcs []string it := p.Pick(nil) for h := it(); h != nil; h = it() { id := h.Info().hostId dc := h.Info().dataCenter if got[id] { t.Fatalf("got duplicate host %s", id) } got[id] = true dcs = append(dcs, dc) } if len(got) != 2 { t.Fatalf("expected %d hosts got %d", 2, len(got)) } for _, dc := range dcs { if dc == "remote" { t.Fatalf("got remote dc but failover was diabled") } } } // Tests of the token-aware host selection policy implementation with a // DC aware round-robin host selection policy fallback // with {"class": "NetworkTopologyStrategy", "a": 1, "b": 1, "c": 1} replication. func TestHostPolicy_TokenAware(t *testing.T) { t.Parallel() const keyspace = "myKeyspace" policy := TokenAwareHostPolicy(DCAwareRoundRobinPolicy("local")) policyInternal := policy.(*tokenAwareHostPolicy) policyInternal.getKeyspaceName = func() string { return keyspace } policyInternal.getKeyspaceMetadata = func(ks string) (*KeyspaceMetadata, error) { return nil, errors.New("not initialized") } query := &Query{routingInfo: &queryRoutingInfo{}} query.getKeyspace = func() string { return keyspace } iter := policy.Pick(nil) if iter == nil { t.Fatal("host iterator was nil") } actual := iter() if actual != nil { t.Fatalf("expected nil from iterator, but was %v", actual) } // set the hosts hosts := [...]*HostInfo{ {hostId: tUUID(0), connectAddress: net.IPv4(10, 0, 0, 1), tokens: []string{"05"}, dataCenter: "remote1"}, {hostId: tUUID(1), connectAddress: net.IPv4(10, 0, 0, 2), tokens: []string{"10"}, dataCenter: "local"}, {hostId: tUUID(2), connectAddress: net.IPv4(10, 0, 0, 3), tokens: []string{"15"}, dataCenter: "remote2"}, {hostId: tUUID(3), connectAddress: net.IPv4(10, 0, 0, 4), tokens: []string{"20"}, dataCenter: "remote1"}, {hostId: tUUID(4), connectAddress: net.IPv4(10, 0, 0, 5), tokens: []string{"25"}, dataCenter: "local"}, {hostId: tUUID(5), connectAddress: net.IPv4(10, 0, 0, 6), tokens: []string{"30"}, dataCenter: "remote2"}, {hostId: tUUID(6), connectAddress: net.IPv4(10, 0, 0, 7), tokens: []string{"35"}, dataCenter: "remote1"}, {hostId: tUUID(7), connectAddress: net.IPv4(10, 0, 0, 8), tokens: []string{"40"}, dataCenter: "local"}, {hostId: tUUID(8), connectAddress: net.IPv4(10, 0, 0, 9), tokens: []string{"45"}, dataCenter: "remote2"}, {hostId: tUUID(9), connectAddress: net.IPv4(10, 0, 0, 10), tokens: []string{"50"}, dataCenter: "remote1"}, {hostId: tUUID(10), connectAddress: net.IPv4(10, 0, 0, 11), tokens: []string{"55"}, dataCenter: "local"}, {hostId: tUUID(11), connectAddress: net.IPv4(10, 0, 0, 12), tokens: []string{"60"}, dataCenter: "remote2"}, } for _, host := range hosts { policy.AddHost(host) } // the token ring is not setup without the partitioner, but the fallback // should work if actual := policy.Pick(nil)(); actual == nil { t.Fatal("expected to get host from fallback got nil") } query.RoutingKey([]byte("30")) if actual := policy.Pick(query)(); actual == nil { t.Fatal("expected to get host from fallback got nil") } policy.SetPartitioner("OrderedPartitioner") policyInternal.getKeyspaceMetadata = func(keyspaceName string) (*KeyspaceMetadata, error) { if keyspaceName != keyspace { return nil, fmt.Errorf("unknown keyspace: %s", keyspaceName) } return &KeyspaceMetadata{ Name: keyspace, StrategyClass: "NetworkTopologyStrategy", StrategyOptions: map[string]any{ "class": "NetworkTopologyStrategy", "local": 1, "remote1": 1, "remote2": 1, }, }, nil } policy.KeyspaceChanged(KeyspaceUpdateEvent{Keyspace: "myKeyspace"}) // The NetworkTopologyStrategy above should generate the following replicas. // It's handy to have as reference here. tests.AssertDeepEqual(t, "replicas", map[string]tokenRingReplicas{ "myKeyspace": { {orderedToken("05"), []*HostInfo{hosts[0], hosts[1], hosts[2]}}, {orderedToken("10"), []*HostInfo{hosts[1], hosts[2], hosts[3]}}, {orderedToken("15"), []*HostInfo{hosts[2], hosts[3], hosts[4]}}, {orderedToken("20"), []*HostInfo{hosts[3], hosts[4], hosts[5]}}, {orderedToken("25"), []*HostInfo{hosts[4], hosts[5], hosts[6]}}, {orderedToken("30"), []*HostInfo{hosts[5], hosts[6], hosts[7]}}, {orderedToken("35"), []*HostInfo{hosts[6], hosts[7], hosts[8]}}, {orderedToken("40"), []*HostInfo{hosts[7], hosts[8], hosts[9]}}, {orderedToken("45"), []*HostInfo{hosts[8], hosts[9], hosts[10]}}, {orderedToken("50"), []*HostInfo{hosts[9], hosts[10], hosts[11]}}, {orderedToken("55"), []*HostInfo{hosts[10], hosts[11], hosts[0]}}, {orderedToken("60"), []*HostInfo{hosts[11], hosts[0], hosts[1]}}, }, }, policyInternal.getMetadataReadOnly().replicas) // now the token ring is configured query.RoutingKey([]byte("23")) iter = policy.Pick(query) // first should be host with matching token from the local DC expectHosts(t, "matching token from local DC", iter, tID(4)) // next are in non-deterministic order expectHosts(t, "rest", iter, tID(0), tID(1), tID(2), tID(3), tID(5), tID(6), tID(7), tID(8), tID(9), tID(10), tID(11)) expectNoMoreHosts(t, iter) } // Tests of the token-aware host selection policy implementation with a // DC aware round-robin host selection policy fallback // with {"class": "NetworkTopologyStrategy", "a": 2, "b": 2, "c": 2} replication. func TestHostPolicy_TokenAware_NetworkStrategy(t *testing.T) { t.Parallel() const keyspace = "myKeyspace" policy := TokenAwareHostPolicy(DCAwareRoundRobinPolicy("local"), NonLocalReplicasFallback(), DontShuffleReplicas()) policyInternal := policy.(*tokenAwareHostPolicy) policyInternal.getKeyspaceName = func() string { return keyspace } policyInternal.getKeyspaceMetadata = func(ks string) (*KeyspaceMetadata, error) { return nil, errors.New("not initialized") } query := &Query{routingInfo: &queryRoutingInfo{}} query.getKeyspace = func() string { return keyspace } iter := policy.Pick(nil) if iter == nil { t.Fatal("host iterator was nil") } actual := iter() if actual != nil { t.Fatalf("expected nil from iterator, but was %v", actual) } // set the hosts hosts := [...]*HostInfo{ {hostId: tUUID(0), connectAddress: net.IPv4(10, 0, 0, 1), tokens: []string{"05"}, dataCenter: "remote1"}, {hostId: tUUID(1), connectAddress: net.IPv4(10, 0, 0, 2), tokens: []string{"10"}, dataCenter: "local"}, {hostId: tUUID(2), connectAddress: net.IPv4(10, 0, 0, 3), tokens: []string{"15"}, dataCenter: "remote2"}, {hostId: tUUID(3), connectAddress: net.IPv4(10, 0, 0, 4), tokens: []string{"20"}, dataCenter: "remote1"}, // 1 {hostId: tUUID(4), connectAddress: net.IPv4(10, 0, 0, 5), tokens: []string{"25"}, dataCenter: "local"}, // 2 {hostId: tUUID(5), connectAddress: net.IPv4(10, 0, 0, 6), tokens: []string{"30"}, dataCenter: "remote2"}, // 3 {hostId: tUUID(6), connectAddress: net.IPv4(10, 0, 0, 7), tokens: []string{"35"}, dataCenter: "remote1"}, // 4 {hostId: tUUID(7), connectAddress: net.IPv4(10, 0, 0, 8), tokens: []string{"40"}, dataCenter: "local"}, // 5 {hostId: tUUID(8), connectAddress: net.IPv4(10, 0, 0, 9), tokens: []string{"45"}, dataCenter: "remote2"}, // 6 {hostId: tUUID(9), connectAddress: net.IPv4(10, 0, 0, 10), tokens: []string{"50"}, dataCenter: "remote1"}, {hostId: tUUID(10), connectAddress: net.IPv4(10, 0, 0, 11), tokens: []string{"55"}, dataCenter: "local"}, {hostId: tUUID(11), connectAddress: net.IPv4(10, 0, 0, 12), tokens: []string{"60"}, dataCenter: "remote2"}, } for _, host := range hosts { policy.AddHost(host) } policy.SetPartitioner("OrderedPartitioner") policyInternal.getKeyspaceMetadata = func(keyspaceName string) (*KeyspaceMetadata, error) { if keyspaceName != keyspace { return nil, fmt.Errorf("unknown keyspace: %s", keyspaceName) } return &KeyspaceMetadata{ Name: keyspace, StrategyClass: "NetworkTopologyStrategy", StrategyOptions: map[string]any{ "class": "NetworkTopologyStrategy", "local": 2, "remote1": 2, "remote2": 2, }, }, nil } policy.KeyspaceChanged(KeyspaceUpdateEvent{Keyspace: keyspace}) // The NetworkTopologyStrategy above should generate the following replicas. // It's handy to have as reference here. tests.AssertDeepEqual(t, "replicas", map[string]tokenRingReplicas{ keyspace: { {orderedToken("05"), []*HostInfo{hosts[0], hosts[1], hosts[2], hosts[3], hosts[4], hosts[5]}}, {orderedToken("10"), []*HostInfo{hosts[1], hosts[2], hosts[3], hosts[4], hosts[5], hosts[6]}}, {orderedToken("15"), []*HostInfo{hosts[2], hosts[3], hosts[4], hosts[5], hosts[6], hosts[7]}}, {orderedToken("20"), []*HostInfo{hosts[3], hosts[4], hosts[5], hosts[6], hosts[7], hosts[8]}}, {orderedToken("25"), []*HostInfo{hosts[4], hosts[5], hosts[6], hosts[7], hosts[8], hosts[9]}}, {orderedToken("30"), []*HostInfo{hosts[5], hosts[6], hosts[7], hosts[8], hosts[9], hosts[10]}}, {orderedToken("35"), []*HostInfo{hosts[6], hosts[7], hosts[8], hosts[9], hosts[10], hosts[11]}}, {orderedToken("40"), []*HostInfo{hosts[7], hosts[8], hosts[9], hosts[10], hosts[11], hosts[0]}}, {orderedToken("45"), []*HostInfo{hosts[8], hosts[9], hosts[10], hosts[11], hosts[0], hosts[1]}}, {orderedToken("50"), []*HostInfo{hosts[9], hosts[10], hosts[11], hosts[0], hosts[1], hosts[2]}}, {orderedToken("55"), []*HostInfo{hosts[10], hosts[11], hosts[0], hosts[1], hosts[2], hosts[3]}}, {orderedToken("60"), []*HostInfo{hosts[11], hosts[0], hosts[1], hosts[2], hosts[3], hosts[4]}}, }, }, policyInternal.getMetadataReadOnly().replicas) // now the token ring is configured query.RoutingKey([]byte("18")) iter = policy.Pick(query) // first should be hosts with matching token from the local DC expectHosts(t, "matching token from local DC", iter, tID(4), tID(7)) // rest should be hosts with matching token from remote DCs expectHosts(t, "matching token from remote DCs", iter, tID(3), tID(5), tID(6), tID(8)) // followed by other hosts expectHosts(t, "rest", iter, tID(0), tID(1), tID(2), tID(9), tID(10), tID(11)) expectNoMoreHosts(t, iter) } func TestHostPolicy_RackAwareRR(t *testing.T) { t.Parallel() p := RackAwareRoundRobinPolicy("local", "b") hosts := [...]*HostInfo{ {hostId: tUUID(0), connectAddress: net.ParseIP("10.0.0.1"), dataCenter: "local", rack: "a"}, {hostId: tUUID(1), connectAddress: net.ParseIP("10.0.0.2"), dataCenter: "local", rack: "a"}, {hostId: tUUID(2), connectAddress: net.ParseIP("10.0.0.3"), dataCenter: "local", rack: "b"}, {hostId: tUUID(3), connectAddress: net.ParseIP("10.0.0.4"), dataCenter: "local", rack: "b"}, {hostId: tUUID(4), connectAddress: net.ParseIP("10.0.0.5"), dataCenter: "remote", rack: "a"}, {hostId: tUUID(5), connectAddress: net.ParseIP("10.0.0.6"), dataCenter: "remote", rack: "a"}, {hostId: tUUID(6), connectAddress: net.ParseIP("10.0.0.7"), dataCenter: "remote", rack: "b"}, {hostId: tUUID(7), connectAddress: net.ParseIP("10.0.0.8"), dataCenter: "remote", rack: "b"}, } for _, host := range hosts { p.AddHost(host) } it := p.Pick(nil) // Must start with rack-local hosts expectHosts(t, "rack-local hosts", it, tID(3), tID(2)) // Then dc-local hosts expectHosts(t, "dc-local hosts", it, tID(0), tID(1)) // Then the remote hosts expectHosts(t, "remote hosts", it, tID(4), tID(5), tID(6), tID(7)) expectNoMoreHosts(t, it) } // Tests of the token-aware host selection policy implementation with a // DC & Rack aware round-robin host selection policy fallback func TestHostPolicy_TokenAware_RackAware(t *testing.T) { t.Parallel() const keyspace = "myKeyspace" policy := TokenAwareHostPolicy(RackAwareRoundRobinPolicy("local", "b")) policyWithFallback := TokenAwareHostPolicy(RackAwareRoundRobinPolicy("local", "b"), NonLocalReplicasFallback()) policyInternal := policy.(*tokenAwareHostPolicy) policyInternal.getKeyspaceName = func() string { return keyspace } policyInternal.getKeyspaceMetadata = func(ks string) (*KeyspaceMetadata, error) { return nil, errors.New("not initialized") } policyWithFallbackInternal := policyWithFallback.(*tokenAwareHostPolicy) policyWithFallbackInternal.getKeyspaceName = policyInternal.getKeyspaceName policyWithFallbackInternal.getKeyspaceMetadata = policyInternal.getKeyspaceMetadata query := &Query{routingInfo: &queryRoutingInfo{}} query.getKeyspace = func() string { return keyspace } iter := policy.Pick(nil) if iter == nil { t.Fatal("host iterator was nil") } actual := iter() if actual != nil { t.Fatalf("expected nil from iterator, but was %v", actual) } // set the hosts hosts := [...]*HostInfo{ {hostId: tUUID(0), connectAddress: net.IPv4(10, 0, 0, 1), tokens: []string{"05"}, dataCenter: "remote", rack: "a"}, {hostId: tUUID(1), connectAddress: net.IPv4(10, 0, 0, 2), tokens: []string{"10"}, dataCenter: "remote", rack: "b"}, {hostId: tUUID(2), connectAddress: net.IPv4(10, 0, 0, 3), tokens: []string{"15"}, dataCenter: "local", rack: "a"}, {hostId: tUUID(3), connectAddress: net.IPv4(10, 0, 0, 4), tokens: []string{"20"}, dataCenter: "local", rack: "b"}, {hostId: tUUID(4), connectAddress: net.IPv4(10, 0, 0, 5), tokens: []string{"25"}, dataCenter: "remote", rack: "a"}, {hostId: tUUID(5), connectAddress: net.IPv4(10, 0, 0, 6), tokens: []string{"30"}, dataCenter: "remote", rack: "b"}, {hostId: tUUID(6), connectAddress: net.IPv4(10, 0, 0, 7), tokens: []string{"35"}, dataCenter: "local", rack: "a"}, {hostId: tUUID(7), connectAddress: net.IPv4(10, 0, 0, 8), tokens: []string{"40"}, dataCenter: "local", rack: "b"}, {hostId: tUUID(8), connectAddress: net.IPv4(10, 0, 0, 9), tokens: []string{"45"}, dataCenter: "remote", rack: "a"}, {hostId: tUUID(9), connectAddress: net.IPv4(10, 0, 0, 10), tokens: []string{"50"}, dataCenter: "remote", rack: "b"}, {hostId: tUUID(10), connectAddress: net.IPv4(10, 0, 0, 11), tokens: []string{"55"}, dataCenter: "local", rack: "a"}, {hostId: tUUID(11), connectAddress: net.IPv4(10, 0, 0, 12), tokens: []string{"60"}, dataCenter: "local", rack: "b"}, } for _, host := range hosts { policy.AddHost(host) policyWithFallback.AddHost(host) } // the token ring is not setup without the partitioner, but the fallback // should work if actual := policy.Pick(nil)(); actual == nil { t.Fatal("expected to get host from fallback got nil") } query.RoutingKey([]byte("30")) if actual := policy.Pick(query)(); actual == nil { t.Fatal("expected to get host from fallback got nil") } policy.SetPartitioner("OrderedPartitioner") policyWithFallback.SetPartitioner("OrderedPartitioner") policyInternal.getKeyspaceMetadata = func(keyspaceName string) (*KeyspaceMetadata, error) { if keyspaceName != keyspace { return nil, fmt.Errorf("unknown keyspace: %s", keyspaceName) } return &KeyspaceMetadata{ Name: keyspace, StrategyClass: "NetworkTopologyStrategy", StrategyOptions: map[string]any{ "class": "NetworkTopologyStrategy", "local": 2, "remote": 2, }, }, nil } policyWithFallbackInternal.getKeyspaceMetadata = policyInternal.getKeyspaceMetadata policy.KeyspaceChanged(KeyspaceUpdateEvent{Keyspace: "myKeyspace"}) policyWithFallback.KeyspaceChanged(KeyspaceUpdateEvent{Keyspace: "myKeyspace"}) // The NetworkTopologyStrategy above should generate the following replicas. // It's handy to have as reference here. tests.AssertDeepEqual(t, "replicas", map[string]tokenRingReplicas{ "myKeyspace": { {orderedToken("05"), []*HostInfo{hosts[0], hosts[1], hosts[2], hosts[3]}}, {orderedToken("10"), []*HostInfo{hosts[1], hosts[2], hosts[3], hosts[4]}}, {orderedToken("15"), []*HostInfo{hosts[2], hosts[3], hosts[4], hosts[5]}}, {orderedToken("20"), []*HostInfo{hosts[3], hosts[4], hosts[5], hosts[6]}}, {orderedToken("25"), []*HostInfo{hosts[4], hosts[5], hosts[6], hosts[7]}}, {orderedToken("30"), []*HostInfo{hosts[5], hosts[6], hosts[7], hosts[8]}}, {orderedToken("35"), []*HostInfo{hosts[6], hosts[7], hosts[8], hosts[9]}}, {orderedToken("40"), []*HostInfo{hosts[7], hosts[8], hosts[9], hosts[10]}}, {orderedToken("45"), []*HostInfo{hosts[8], hosts[9], hosts[10], hosts[11]}}, {orderedToken("50"), []*HostInfo{hosts[9], hosts[10], hosts[11], hosts[0]}}, {orderedToken("55"), []*HostInfo{hosts[10], hosts[11], hosts[0], hosts[1]}}, {orderedToken("60"), []*HostInfo{hosts[11], hosts[0], hosts[1], hosts[2]}}, }, }, policyInternal.getMetadataReadOnly().replicas) query.RoutingKey([]byte("23")) // now the token ring is configured // Test the policy with fallback iter = policyWithFallback.Pick(query) // first should be host with matching token from the local DC & rack expectHosts(t, "matching token from local DC and local rack", iter, tID(7)) // next should be host with matching token from local DC and other rack expectHosts(t, "matching token from local DC and non-local rack", iter, tID(6)) // next should be hosts with matching token from other DC, in any order expectHosts(t, "matching token from non-local DC", iter, tID(4), tID(5)) // then the local DC & rack that didn't match the token expectHosts(t, "non-matching token from local DC and local rack", iter, tID(3), tID(11)) // then the local DC & other rack that didn't match the token expectHosts(t, "non-matching token from local DC and non-local rack", iter, tID(2), tID(10)) // finally, the other DC that didn't match the token expectHosts(t, "non-matching token from non-local DC", iter, tID(0), tID(1), tID(8), tID(9)) expectNoMoreHosts(t, iter) // Test the policy without fallback iter = policy.Pick(query) // first should be host with matching token from the local DC & Rack expectHosts(t, "matching token from local DC and local rack", iter, tID(7)) // next should be the other two hosts from local DC & rack expectHosts(t, "non-matching token local DC and local rack", iter, tID(3), tID(11)) // then the three hosts from the local DC but other rack expectHosts(t, "local DC, non-local rack", iter, tID(2), tID(6), tID(10)) // then the 6 hosts from the other DC expectHosts(t, "non-local DC", iter, tID(0), tID(1), tID(4), tID(5), tID(8), tID(9)) expectNoMoreHosts(t, iter) } func TestHostPolicy_TokenAware_Issue1274(t *testing.T) { t.Parallel() policy := TokenAwareHostPolicy(DCAwareRoundRobinPolicy("local")) policyInternal := policy.(*tokenAwareHostPolicy) policyInternal.getKeyspaceName = func() string { return "myKeyspace" } policyInternal.getKeyspaceMetadata = func(ks string) (*KeyspaceMetadata, error) { return nil, errors.New("not initialized") } query := &Query{routingInfo: &queryRoutingInfo{}} query.getKeyspace = func() string { return "myKeyspace" } iter := policy.Pick(nil) if iter == nil { t.Fatal("host iterator was nil") } actual := iter() if actual != nil { t.Fatalf("expected nil from iterator, but was %v", actual) } // set the hosts hosts := [...]*HostInfo{ {hostId: tUUID(0), connectAddress: net.IPv4(10, 0, 0, 1), tokens: []string{"05"}, dataCenter: "remote1"}, {hostId: tUUID(1), connectAddress: net.IPv4(10, 0, 0, 2), tokens: []string{"10"}, dataCenter: "local"}, {hostId: tUUID(2), connectAddress: net.IPv4(10, 0, 0, 3), tokens: []string{"15"}, dataCenter: "remote2"}, {hostId: tUUID(3), connectAddress: net.IPv4(10, 0, 0, 4), tokens: []string{"20"}, dataCenter: "remote1"}, {hostId: tUUID(4), connectAddress: net.IPv4(10, 0, 0, 5), tokens: []string{"25"}, dataCenter: "local"}, {hostId: tUUID(5), connectAddress: net.IPv4(10, 0, 0, 6), tokens: []string{"30"}, dataCenter: "remote2"}, {hostId: tUUID(6), connectAddress: net.IPv4(10, 0, 0, 7), tokens: []string{"35"}, dataCenter: "remote1"}, {hostId: tUUID(7), connectAddress: net.IPv4(10, 0, 0, 8), tokens: []string{"40"}, dataCenter: "local"}, {hostId: tUUID(8), connectAddress: net.IPv4(10, 0, 0, 9), tokens: []string{"45"}, dataCenter: "remote2"}, {hostId: tUUID(9), connectAddress: net.IPv4(10, 0, 0, 10), tokens: []string{"50"}, dataCenter: "remote1"}, {hostId: tUUID(10), connectAddress: net.IPv4(10, 0, 0, 11), tokens: []string{"55"}, dataCenter: "local"}, {hostId: tUUID(11), connectAddress: net.IPv4(10, 0, 0, 12), tokens: []string{"60"}, dataCenter: "remote2"}, } policy.SetPartitioner("OrderedPartitioner") policyInternal.getKeyspaceMetadata = func(keyspaceName string) (*KeyspaceMetadata, error) { if keyspaceName != "myKeyspace" { return nil, fmt.Errorf("unknown keyspace: %s", keyspaceName) } return &KeyspaceMetadata{ Name: "myKeyspace", StrategyClass: "NetworkTopologyStrategy", StrategyOptions: map[string]any{ "class": "NetworkTopologyStrategy", "local": 1, "remote1": 1, "remote2": 1, }, }, nil } policy.KeyspaceChanged(KeyspaceUpdateEvent{Keyspace: "myKeyspace"}) cancel := make(chan struct{}) // now the token ring is configured for _, host := range hosts { host := host go func() { for { select { case <-cancel: return default: policy.AddHost(host) policy.RemoveHost(host) } } }() } time.Sleep(100 * time.Millisecond) close(cancel) } func TestTokenAwarePolicyReset(t *testing.T) { t.Parallel() policy := TokenAwareHostPolicy( RackAwareRoundRobinPolicy("local", "b"), NonLocalReplicasFallback(), ) policyInternal := policy.(*tokenAwareHostPolicy) if policyInternal.fallback == nil { t.Fatal("fallback is nil") } if !policyInternal.nonLocalReplicasFallback { t.Fatal("nonLocalReplicasFallback is false") } policy.Init(&Session{logger: &defaultLogger{}}) if policyInternal.getKeyspaceMetadata == nil { t.Fatal("keyspace metatadata fn is nil") } if policyInternal.getKeyspaceName == nil { t.Fatal("keyspace name fn is nil") } if policyInternal.logger == nil { t.Fatal("logger is nil") } // Reset - should reset fields that were set in Init policy.Reset() if policyInternal.fallback == nil { // we don't touch fallback t.Fatal("fallback is nil") } if !policyInternal.nonLocalReplicasFallback { // we don't touch nonLocalReplicasFallback t.Fatal("nonLocalReplicasFallback is false") } if policyInternal.getKeyspaceMetadata != nil { t.Fatal("keyspace metatadata fn is not nil") } if policyInternal.getKeyspaceName != nil { t.Fatal("keyspace name fn is not nil") } if policyInternal.logger != nil { t.Fatal("logger is nil") } } func TestTokenAwareHostPolicyTabletPath(t *testing.T) { t.Parallel() t.Run("HappyPath", func(t *testing.T) { t.Parallel() const keyspace = "testks" const table = "testtbl" policy := TokenAwareHostPolicy(RoundRobinHostPolicy()) policyInternal := policy.(*tokenAwareHostPolicy) policyInternal.getKeyspaceName = func() string { return keyspace } policyInternal.getKeyspaceMetadata = func(ks string) (*KeyspaceMetadata, error) { return nil, errors.New("not initialized") } host1 := &HostInfo{hostId: tUUID(1), connectAddress: net.IPv4(10, 0, 0, 1), tokens: []string{"-6148914691236517206"}} host2 := &HostInfo{hostId: tUUID(2), connectAddress: net.IPv4(10, 0, 0, 2), tokens: []string{"0"}} host3 := &HostInfo{hostId: tUUID(3), connectAddress: net.IPv4(10, 0, 0, 3), tokens: []string{"6148914691236517206"}} policy.AddHost(host1) policy.AddHost(host2) policy.AddHost(host3) policy.SetPartitioner("Murmur3Partitioner") policyInternal.getKeyspaceMetadata = func(ks string) (*KeyspaceMetadata, error) { return &KeyspaceMetadata{ Name: keyspace, StrategyClass: "SimpleStrategy", StrategyOptions: map[string]any{ "class": "SimpleStrategy", "replication_factor": 1, }, }, nil } policy.KeyspaceChanged(KeyspaceUpdateEvent{Keyspace: keyspace}) ctrl := &schemaDataMock{knownKeyspaces: map[string][]tableInfo{}} s := newSchemaEventTestSessionWithMock(ctrl) defer s.Close() s.isInitialized = true s.tabletsRoutingV1 = true t1, err := tablets.TabletInfoBuilder{ KeyspaceName: keyspace, TableName: table, FirstToken: -9223372036854775808, LastToken: 0, Replicas: [][]any{{host2.hostId, 0}}, }.Build() if err != nil { t.Fatal(err) } t2, err := tablets.TabletInfoBuilder{ KeyspaceName: keyspace, TableName: table, FirstToken: 0, LastToken: 9223372036854775807, Replicas: [][]any{{host3.hostId, 0}}, }.Build() if err != nil { t.Fatal(err) } s.metadataDescriber.AddTablet(t1) s.metadataDescriber.AddTablet(t2) s.metadataDescriber.metadata.tabletsMetadata.Flush() query := &Query{ routingInfo: &queryRoutingInfo{ keyspace: keyspace, table: table, partitioner: fixedInt64Partitioner(-42), }, session: s, } query.getKeyspace = func() string { return keyspace } query.routingKey = []byte("anything") iter := policy.Pick(query) first := iter() if first == nil || first.Info() == nil { t.Fatal("expected a host from tablet path, got nil") } if first.Info().HostID() != tID(2) { t.Fatalf("expected host tUUID(2) from tablet path, got %s", first.Info().HostID()) } query2 := &Query{ routingInfo: &queryRoutingInfo{ keyspace: keyspace, table: table, partitioner: fixedInt64Partitioner(42), }, session: s, } query2.getKeyspace = func() string { return keyspace } query2.routingKey = []byte("anything") iter2 := policy.Pick(query2) first2 := iter2() if first2 == nil || first2.Info() == nil { t.Fatal("expected a host from tablet path, got nil") } if first2.Info().HostID() != tID(3) { t.Fatalf("expected host tUUID(3) from tablet path, got %s", first2.Info().HostID()) } }) } type fixedInt64Partitioner int64 func (f fixedInt64Partitioner) Name() string { return "FixedInt64Partitioner" } func (f fixedInt64Partitioner) Hash([]byte) Token { return int64Token(f) } func (f fixedInt64Partitioner) ParseString(s string) Token { return parseInt64Token(s) } func TestHostSetInline(t *testing.T) { var s hostSet hosts := make([]*HostInfo, 9) for i := range hosts { hosts[i] = &HostInfo{} s.add(hosts[i]) } // All 9 should be tracked inline (no overflow map). if s.overflow != nil { t.Fatal("expected inline-only storage for 9 hosts") } for i, h := range hosts { if !s.contains(h) { t.Fatalf("host %d not found in inline set", i) } } // Unknown host should not be found. if s.contains(&HostInfo{}) { t.Fatal("unexpected contains=true for unknown host") } } func TestHostSetOverflow(t *testing.T) { var s hostSet hosts := make([]*HostInfo, 15) // exceeds inline capacity of 9 for i := range hosts { hosts[i] = &HostInfo{} s.add(hosts[i]) } // Should have spilled to map. if s.overflow == nil { t.Fatal("expected overflow map for 15 hosts") } // Every host must be found, including those added before and after spill. for i, h := range hosts { if !s.contains(h) { t.Fatalf("host %d not found after overflow", i) } } // Unknown host should not be found. if s.contains(&HostInfo{}) { t.Fatal("unexpected contains=true for unknown host in overflow mode") } } func TestHostSetOverflowPreservesInlineEntries(t *testing.T) { var s hostSet // Fill inline storage exactly. inline := make([]*HostInfo, 9) for i := range inline { inline[i] = &HostInfo{} s.add(inline[i]) } // Add one more to trigger spill. extra := &HostInfo{} s.add(extra) if s.overflow == nil { t.Fatal("expected overflow map after 10th add") } // Inline entries must be findable via the map path. for i, h := range inline { if !s.contains(h) { t.Fatalf("inline host %d lost after spill", i) } } if !s.contains(extra) { t.Fatal("extra host not found after spill") } } ================================================ FILE: prepared_cache.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "bytes" "sync" "github.com/gocql/gocql/internal/lru" ) const defaultMaxPreparedStmts = 1000 // stmtCacheKey is a composite key for the prepared statement cache. // Using a struct avoids the string concatenation allocation that occurred // on every query and fixes the theoretical key collision bug where // different (hostID, keyspace, statement) tuples could produce the same // concatenated string. type stmtCacheKey struct { hostID string keyspace string statement string } // preparedLRU is the prepared statement cache type preparedLRU struct { lru *lru.Cache[stmtCacheKey] mu sync.Mutex } func (p *preparedLRU) clear() { p.mu.Lock() defer p.mu.Unlock() for p.lru.Len() > 0 { p.lru.RemoveOldest() } } func (p *preparedLRU) add(key stmtCacheKey, val *inflightPrepare) { p.mu.Lock() defer p.mu.Unlock() p.lru.Add(key, val) } func (p *preparedLRU) remove(key stmtCacheKey) bool { p.mu.Lock() defer p.mu.Unlock() return p.lru.Remove(key) } func (p *preparedLRU) execIfMissing(key stmtCacheKey, fn func(cache *lru.Cache[stmtCacheKey]) *inflightPrepare) (*inflightPrepare, bool) { p.mu.Lock() defer p.mu.Unlock() val, ok := p.lru.Get(key) if ok { return val.(*inflightPrepare), true } return fn(p.lru), false } // keyFor constructs a zero-allocation composite cache key from the given // components. The returned struct references the original strings without // copying, so no heap allocation occurs. func (p *preparedLRU) keyFor(hostID, keyspace, statement string) stmtCacheKey { return stmtCacheKey{ hostID: hostID, keyspace: keyspace, statement: statement, } } func (p *preparedLRU) evictPreparedID(key stmtCacheKey, id []byte) { p.mu.Lock() defer p.mu.Unlock() val, ok := p.lru.Get(key) if !ok { return } ifp, ok := val.(*inflightPrepare) if !ok { return } select { case <-ifp.done: if bytes.Equal(id, ifp.preparedStatment.id) { p.lru.Remove(key) } default: } } ================================================ FILE: query_error_test.go ================================================ //go:build unit // +build unit package gocql import ( "errors" "testing" "time" ) func TestQueryError_PotentiallyExecuted(t *testing.T) { t.Parallel() tests := []struct { name string potentiallyExecuted bool expected bool }{ { name: "potentially executed true", potentiallyExecuted: true, expected: true, }, { name: "potentially executed false", potentiallyExecuted: false, expected: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { qErr := &QueryError{ err: errors.New("test error"), potentiallyExecuted: tt.potentiallyExecuted, } got := qErr.PotentiallyExecuted() if got != tt.expected { t.Fatalf("QueryError.PotentiallyExecuted() = %v, expected %v", got, tt.expected) } }) } } func TestQueryError_IsIdempotent(t *testing.T) { t.Parallel() tests := []struct { name string isIdempotent bool expected bool }{ { name: "idempotent true", isIdempotent: true, expected: true, }, { name: "idempotent false", isIdempotent: false, expected: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { qErr := &QueryError{ err: errors.New("test error"), isIdempotent: tt.isIdempotent, } got := qErr.IsIdempotent() if got != tt.expected { t.Errorf("QueryError.IsIdempotent() = %v, expected %v", got, tt.expected) } }) } } func TestQueryError_Error(t *testing.T) { t.Parallel() tests := []struct { name string err error potentiallyExecuted bool timeout time.Duration inFlight int expected string }{ { name: "with potentially executed true", err: errors.New("connection error"), potentiallyExecuted: true, expected: "connection error (potentially executed: true)", }, { name: "with potentially executed false", err: errors.New("syntax error"), potentiallyExecuted: false, expected: "syntax error (potentially executed: false)", }, { name: "with timeout", err: ErrTimeoutNoResponse, potentiallyExecuted: true, timeout: 11 * time.Second, inFlight: 42, expected: "gocql: no response received from cassandra within timeout period (timeout: 11s, in-flight: 42) (potentially executed: true)", }, { name: "with zero timeout omits timeout", err: errors.New("some error"), potentiallyExecuted: false, timeout: 0, expected: "some error (potentially executed: false)", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { qErr := &QueryError{ err: tt.err, potentiallyExecuted: tt.potentiallyExecuted, timeout: tt.timeout, inFlight: tt.inFlight, } got := qErr.Error() if got != tt.expected { t.Errorf("QueryError.Error() = %v, expected %v", got, tt.expected) } }) } } ================================================ FILE: query_executor.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "context" "errors" "fmt" "sync" "time" ) type ExecutableQuery interface { borrowForExecution() // Used to ensure that the query stays alive for lifetime of a particular execution goroutine. releaseAfterExecution() // Used when a goroutine finishes its execution attempts, either with ok result or an error. execute(ctx context.Context, conn *Conn) *Iter attempt(keyspace string, end, start time.Time, iter *Iter, host *HostInfo) retryPolicy() RetryPolicy speculativeExecutionPolicy() SpeculativeExecutionPolicy GetRoutingKey() ([]byte, error) Keyspace() string Table() string IsIdempotent() bool IsLWT() bool GetCustomPartitioner() Partitioner GetHostID() string withContext(context.Context) ExecutableQuery RetryableQuery GetSession() *Session } type queryExecutor struct { pool *policyConnPool policy HostSelectionPolicy } func (q *queryExecutor) attemptQuery(ctx context.Context, qry ExecutableQuery, conn *Conn) *Iter { start := time.Now() iter := qry.execute(ctx, conn) end := time.Now() qry.attempt(q.pool.keyspace, end, start, iter, conn.host) return iter } func (q *queryExecutor) speculate(ctx context.Context, qry ExecutableQuery, sp SpeculativeExecutionPolicy, hostIter NextHost, results chan *Iter) *Iter { ticker := time.NewTicker(sp.Delay()) defer ticker.Stop() for i := 0; i < sp.Attempts(); i++ { select { case <-ticker.C: qry.borrowForExecution() // ensure liveness in case of executing Query to prevent races with Query.Release(). go q.run(ctx, qry, hostIter, results) case <-ctx.Done(): return &Iter{err: ctx.Err()} case iter := <-results: return iter } } return nil } func (q *queryExecutor) executeQuery(qry ExecutableQuery) (*Iter, error) { var hostIter NextHost // check if the hostID is specified for the query, // if true - the query execute at the specified host. // if false - the query execute at the host picked by HostSelectionPolicy if hostID := qry.GetHostID(); hostID != "" { pool, ok := q.pool.getPoolByHostID(hostID) if !ok { // if the specified host ID have no connection pool we return error return nil, fmt.Errorf("query is targeting unknown host id %s: %w", hostID, ErrNoPool) } else if pool.Size() == 0 { // if the pool have no connection we return error return nil, fmt.Errorf("query is targeting host id %s that driver is not connected to: %w", hostID, ErrNoConnectionsInPool) } hostIter = newSingleHost(pool.host, 5, 200*time.Millisecond).selectHost } else { hostIter = q.policy.Pick(qry) } // check if the query is not marked as idempotent, if // it is, we force the policy to NonSpeculative sp := qry.speculativeExecutionPolicy() if qry.GetHostID() != "" || !qry.IsIdempotent() || sp.Attempts() == 0 { return q.do(qry.Context(), qry, hostIter), nil } // When speculative execution is enabled, we could be accessing the host iterator from multiple goroutines below. // To ensure we don't call it concurrently, we wrap the returned NextHost function here to synchronize access to it. var mu sync.Mutex origHostIter := hostIter hostIter = func() SelectedHost { mu.Lock() defer mu.Unlock() return origHostIter() } ctx, cancel := context.WithCancel(qry.Context()) defer cancel() results := make(chan *Iter, 1) // Launch the main execution qry.borrowForExecution() // ensure liveness in case of executing Query to prevent races with Query.Release(). go q.run(ctx, qry, hostIter, results) // The speculative executions are launched _in addition_ to the main // execution, on a timer. So Speculation{2} would make 3 executions running // in total. if iter := q.speculate(ctx, qry, sp, hostIter, results); iter != nil { return iter, nil } select { case iter := <-results: return iter, nil case <-ctx.Done(): return &Iter{err: ctx.Err()}, nil } } func (q *queryExecutor) do(ctx context.Context, qry ExecutableQuery, hostIter NextHost) *Iter { rt := qry.retryPolicy() if rt == nil { rt = &SimpleRetryPolicy{NumRetries: 3} } lwtRT, isRTSupportsLWT := rt.(LWTRetryPolicy) var getShouldRetry func(qry RetryableQuery) bool var getRetryType func(error) RetryType if isRTSupportsLWT && qry.IsLWT() { getShouldRetry = lwtRT.AttemptLWT getRetryType = lwtRT.GetRetryTypeLWT } else { getShouldRetry = rt.Attempt getRetryType = rt.GetRetryType } var potentiallyExecuted bool execute := func(qry ExecutableQuery, selectedHost SelectedHost) (iter *Iter, retry RetryType) { host := selectedHost.Info() if host == nil || !host.IsUp() { return &Iter{ err: &QueryError{ err: ErrHostDown, potentiallyExecuted: potentiallyExecuted, }, }, RetryNextHost } pool, ok := q.pool.getPool(host) if !ok { return &Iter{ err: &QueryError{ err: ErrNoPool, potentiallyExecuted: potentiallyExecuted, }, }, RetryNextHost } conn := pool.Pick(selectedHost.Token(), qry) if conn == nil { return &Iter{ err: &QueryError{ err: ErrNoConnectionsInPool, potentiallyExecuted: potentiallyExecuted, }, }, RetryNextHost } iter = q.attemptQuery(ctx, qry, conn) iter.host = selectedHost.Info() // Update host if iter.err == nil { return iter, RetryType(255) } switch { case errors.Is(iter.err, context.Canceled), errors.Is(iter.err, context.DeadlineExceeded): selectedHost.Mark(nil) potentiallyExecuted = true retry = Rethrow default: selectedHost.Mark(iter.err) retry = RetryType(255) // Don't enforce retry and get it from retry policy } var qErr *QueryError if errors.As(iter.err, &qErr) { potentiallyExecuted = potentiallyExecuted || qErr.PotentiallyExecuted() qErr.potentiallyExecuted = potentiallyExecuted qErr.isIdempotent = qry.IsIdempotent() iter.err = qErr } else { iter.err = &QueryError{ err: iter.err, potentiallyExecuted: potentiallyExecuted, isIdempotent: qry.IsIdempotent(), } } return iter, retry } var lastErr error selectedHost := hostIter() for selectedHost != nil { iter, retryType := execute(qry, selectedHost) if iter.err == nil { return iter } lastErr = iter.err // Exit if retry policy decides to not retry anymore if retryType == RetryType(255) { if !getShouldRetry(qry) { return iter } retryType = getRetryType(iter.err) } // If query is unsuccessful, check the error with RetryPolicy to retry switch retryType { case Retry: iter.finalize(true) // retry on the same host continue case Rethrow, Ignore: return iter case RetryNextHost: iter.finalize(true) // retry on the next host selectedHost = hostIter() continue default: // Undefined? Return nil and error, this will panic in the requester return &Iter{err: ErrUnknownRetryType} } } if lastErr != nil { return &Iter{err: lastErr} } return &Iter{err: ErrNoConnections} } func (q *queryExecutor) run(ctx context.Context, qry ExecutableQuery, hostIter NextHost, results chan<- *Iter) { iter := q.do(ctx, qry, hostIter) select { case results <- iter: case <-ctx.Done(): iter.discard() } qry.releaseAfterExecution() } ================================================ FILE: recreate.go ================================================ // Copyright (C) 2017 ScyllaDB package gocql import ( "encoding/binary" "encoding/json" "fmt" "io" "sort" "strconv" "strings" "text/template" ) // ToCQL returns a CQL query that ca be used to recreate keyspace with all // user defined types, tables, indexes, functions, aggregates and views associated // with this keyspace. func (ks *KeyspaceMetadata) ToCQL() (string, error) { // Be aware that `CreateStmts` is not only a cache for ToCQL, // but it also can be populated from response to `DESCRIBE KEYSPACE %s WITH INTERNALS` if len(ks.CreateStmts) != 0 { return ks.CreateStmts, nil } var sb strings.Builder if err := ks.keyspaceToCQL(&sb); err != nil { return "", err } sortedTypes := ks.typesSortedTopologically() for _, tm := range sortedTypes { if err := ks.userTypeToCQL(&sb, tm); err != nil { return "", err } } for _, tm := range ks.Tables { if err := ks.tableToCQL(&sb, ks.Name, tm); err != nil { return "", err } } for _, im := range ks.Indexes { if err := ks.indexToCQL(&sb, im); err != nil { return "", err } } for _, fm := range ks.Functions { if err := ks.functionToCQL(&sb, ks.Name, fm); err != nil { return "", err } } for _, am := range ks.Aggregates { if err := ks.aggregateToCQL(&sb, am); err != nil { return "", err } } for _, vm := range ks.Views { if err := ks.viewToCQL(&sb, vm); err != nil { return "", err } } ks.CreateStmts = sb.String() return ks.CreateStmts, nil } func (ks *KeyspaceMetadata) typesSortedTopologically() []*TypeMetadata { sortedTypes := make([]*TypeMetadata, 0, len(ks.Types)) for _, tm := range ks.Types { sortedTypes = append(sortedTypes, tm) } sort.Slice(sortedTypes, func(i, j int) bool { for _, ft := range sortedTypes[j].FieldTypes { if strings.Contains(ft, sortedTypes[i].Name) { return true } } return false }) return sortedTypes } var tableCQLTemplate = template.Must(template.New("table"). Funcs(map[string]any{ "escape": cqlHelpers.escape, "tableColumnToCQL": cqlHelpers.tableColumnToCQL, "tablePropertiesToCQL": cqlHelpers.tablePropertiesToCQL, }). Parse(` CREATE TABLE {{ .KeyspaceName }}.{{ .Tm.Name }} ( {{ tableColumnToCQL .Tm }} ) WITH {{ tablePropertiesToCQL .Tm.ClusteringColumns .Tm.Options .Tm.Extensions }}; `)) func (ks *KeyspaceMetadata) tableToCQL(w io.Writer, kn string, tm *TableMetadata) error { if err := tableCQLTemplate.Execute(w, map[string]any{ "Tm": tm, "KeyspaceName": kn, }); err != nil { return err } return nil } var functionTemplate = template.Must(template.New("functions"). Funcs(map[string]any{ "escape": cqlHelpers.escape, "zip": cqlHelpers.zip, "stripFrozen": cqlHelpers.stripFrozen, }). Parse(` CREATE FUNCTION {{ .keyspaceName }}.{{ .fm.Name }} ( {{- range $i, $args := zip .fm.ArgumentNames .fm.ArgumentTypes }} {{- if ne $i 0 }}, {{ end }} {{- (index $args 0) }} {{ stripFrozen (index $args 1) }} {{- end -}}) {{ if .fm.CalledOnNullInput }}CALLED{{ else }}RETURNS NULL{{ end }} ON NULL INPUT RETURNS {{ .fm.ReturnType }} LANGUAGE {{ .fm.Language }} AS $${{ .fm.Body }}$$; `)) func (ks *KeyspaceMetadata) functionToCQL(w io.Writer, keyspaceName string, fm *FunctionMetadata) error { if err := functionTemplate.Execute(w, map[string]any{ "fm": fm, "keyspaceName": keyspaceName, }); err != nil { return err } return nil } var viewTemplate = template.Must(template.New("views"). Funcs(map[string]any{ "zip": cqlHelpers.zip, "partitionKeyString": cqlHelpers.partitionKeyString, "tablePropertiesToCQL": cqlHelpers.tablePropertiesToCQL, }). Parse(` CREATE MATERIALIZED VIEW {{ .vm.KeyspaceName }}.{{ .vm.ViewName }} AS SELECT {{ if .vm.IncludeAllColumns }}*{{ else }} {{- range $i, $col := .vm.OrderedColumns }} {{- if ne $i 0 }}, {{ end }} {{ $col }} {{- end }} {{- end }} FROM {{ .vm.KeyspaceName }}.{{ .vm.BaseTableName }} WHERE {{ .vm.WhereClause }} PRIMARY KEY ({{ partitionKeyString .vm.PartitionKey .vm.ClusteringColumns }}) WITH {{ tablePropertiesToCQL .vm.ClusteringColumns .vm.Options .vm.Extensions }}; `)) func (ks *KeyspaceMetadata) viewToCQL(w io.Writer, vm *ViewMetadata) error { if err := viewTemplate.Execute(w, map[string]any{ "vm": vm, }); err != nil { return err } return nil } var aggregatesTemplate = template.Must(template.New("aggregate"). Funcs(map[string]any{ "stripFrozen": cqlHelpers.stripFrozen, }). Parse(` CREATE AGGREGATE {{ .Keyspace }}.{{ .Name }}( {{- range $i, $arg := .ArgumentTypes }} {{- if ne $i 0 }}, {{ end }} {{ stripFrozen $arg }} {{- end -}}) SFUNC {{ .StateFunc.Name }} STYPE {{ stripFrozen .StateType }} {{- if ne .FinalFunc.Name "" }} FINALFUNC {{ .FinalFunc.Name }} {{- end -}} {{- if ne .InitCond "" }} INITCOND {{ .InitCond }} {{- end -}} ; `)) func (ks *KeyspaceMetadata) aggregateToCQL(w io.Writer, am *AggregateMetadata) error { if err := aggregatesTemplate.Execute(w, am); err != nil { return err } return nil } var typeCQLTemplate = template.Must(template.New("types"). Funcs(map[string]any{ "zip": cqlHelpers.zip, }). Parse(` CREATE TYPE {{ .Keyspace }}.{{ .Name }} ( {{- range $i, $fields := zip .FieldNames .FieldTypes }} {{- if ne $i 0 }},{{ end }} {{ index $fields 0 }} {{ index $fields 1 }} {{- end }} ); `)) func (ks *KeyspaceMetadata) userTypeToCQL(w io.Writer, tm *TypeMetadata) error { if err := typeCQLTemplate.Execute(w, tm); err != nil { return err } return nil } func (ks *KeyspaceMetadata) indexToCQL(w io.Writer, im *IndexMetadata) error { // Scylla doesn't support any custom indexes if im.Kind == IndexKindCustom { return nil } options := im.Options indexTarget := options["target"] // secondary index si := struct { ClusteringKeys []string `json:"ck"` PartitionKeys []string `json:"pk"` }{} if err := json.Unmarshal([]byte(indexTarget), &si); err == nil { indexTarget = fmt.Sprintf("(%s), %s", strings.Join(si.PartitionKeys, ","), strings.Join(si.ClusteringKeys, ","), ) } _, err := fmt.Fprintf(w, "\nCREATE INDEX %s ON %s.%s (%s);\n", im.Name, im.KeyspaceName, im.TableName, indexTarget, ) if err != nil { return err } return nil } var keyspaceCQLTemplate = template.Must(template.New("keyspace"). Funcs(map[string]any{ "escape": cqlHelpers.escape, "fixStrategy": cqlHelpers.fixStrategy, }). Parse(`CREATE KEYSPACE {{ .Name }} WITH replication = { 'class': {{ escape ( fixStrategy .StrategyClass) }} {{- range $key, $value := .StrategyOptions }}, {{ escape $key }}: {{ escape $value }} {{- end }} }{{ if not .DurableWrites }} AND durable_writes = 'false'{{ end }}; `)) func (ks *KeyspaceMetadata) keyspaceToCQL(w io.Writer) error { if err := keyspaceCQLTemplate.Execute(w, ks); err != nil { return err } return nil } func contains(in []string, v string) bool { for _, e := range in { if e == v { return true } } return false } type toCQLHelpers struct{} var cqlHelpers = toCQLHelpers{} func (h toCQLHelpers) zip(a []string, b []string) [][]string { m := make([][]string, len(a)) for i := range a { m[i] = []string{a[i], b[i]} } return m } func (h toCQLHelpers) escape(e any) string { switch v := e.(type) { case int, float64: return fmt.Sprint(v) case bool: if v { return "true" } return "false" case string: return "'" + strings.ReplaceAll(v, "'", "''") + "'" case []byte: return string(v) } return "" } func (h toCQLHelpers) stripFrozen(v string) string { return strings.TrimSuffix(strings.TrimPrefix(v, "frozen<"), ">") } func (h toCQLHelpers) fixStrategy(v string) string { return strings.TrimPrefix(v, "org.apache.cassandra.locator.") } func (h toCQLHelpers) fixQuote(v string) string { return strings.ReplaceAll(v, `"`, `'`) } func (h toCQLHelpers) tableOptionsToCQL(ops TableMetadataOptions) ([]string, error) { opts := map[string]any{ "bloom_filter_fp_chance": ops.BloomFilterFpChance, "comment": ops.Comment, "crc_check_chance": ops.CrcCheckChance, "default_time_to_live": ops.DefaultTimeToLive, "gc_grace_seconds": ops.GcGraceSeconds, "max_index_interval": ops.MaxIndexInterval, "memtable_flush_period_in_ms": ops.MemtableFlushPeriodInMs, "min_index_interval": ops.MinIndexInterval, "speculative_retry": ops.SpeculativeRetry, } var err error opts["caching"], err = json.Marshal(ops.Caching) if err != nil { return nil, err } opts["compaction"], err = json.Marshal(ops.Compaction) if err != nil { return nil, err } opts["compression"], err = json.Marshal(ops.Compression) if err != nil { return nil, err } cdc, err := json.Marshal(ops.CDC) if err != nil { return nil, err } if string(cdc) != "null" { opts["cdc"] = cdc } if ops.InMemory { opts["in_memory"] = ops.InMemory } out := make([]string, 0, len(opts)) for key, opt := range opts { out = append(out, fmt.Sprintf("%s = %s", key, h.fixQuote(h.escape(opt)))) } sort.Strings(out) return out, nil } func (h toCQLHelpers) tableExtensionsToCQL(extensions map[string]any) ([]string, error) { exts := map[string]any{} if blob, ok := extensions["scylla_encryption_options"]; ok { encOpts := &scyllaEncryptionOptions{} if err := encOpts.UnmarshalBinary(blob.([]byte)); err != nil { return nil, err } var err error exts["scylla_encryption_options"], err = json.Marshal(encOpts) if err != nil { return nil, err } } out := make([]string, 0, len(exts)) for key, ext := range exts { out = append(out, fmt.Sprintf("%s = %s", key, h.fixQuote(h.escape(ext)))) } sort.Strings(out) return out, nil } func (h toCQLHelpers) tablePropertiesToCQL(cks []*ColumnMetadata, opts TableMetadataOptions, extensions map[string]any) (string, error) { var sb strings.Builder var properties []string if len(cks) > 0 { var inner []string for _, col := range cks { inner = append(inner, fmt.Sprintf("%s %s", col.Name, col.ClusteringOrder)) } properties = append(properties, fmt.Sprintf("CLUSTERING ORDER BY (%s)", strings.Join(inner, ", "))) } options, err := h.tableOptionsToCQL(opts) if err != nil { return "", err } properties = append(properties, options...) exts, err := h.tableExtensionsToCQL(extensions) if err != nil { return "", err } properties = append(properties, exts...) sb.WriteString(strings.Join(properties, "\n AND ")) return sb.String(), nil } func (h toCQLHelpers) tableColumnToCQL(tm *TableMetadata) string { var sb strings.Builder var columns []string for _, cn := range tm.OrderedColumns { cm := tm.Columns[cn] column := fmt.Sprintf("%s %s", cn, cm.Type) if cm.Kind == ColumnStatic { column += " static" } columns = append(columns, column) } if len(tm.PartitionKey) == 1 && len(tm.ClusteringColumns) == 0 && len(columns) > 0 { columns[0] += " PRIMARY KEY" } sb.WriteString(strings.Join(columns, ",\n ")) if len(tm.PartitionKey) > 1 || len(tm.ClusteringColumns) > 0 { sb.WriteString(",\n PRIMARY KEY (") sb.WriteString(h.partitionKeyString(tm.PartitionKey, tm.ClusteringColumns)) sb.WriteRune(')') } return sb.String() } func (h toCQLHelpers) partitionKeyString(pks, cks []*ColumnMetadata) string { var sb strings.Builder if len(pks) > 1 { sb.WriteRune('(') for i, pk := range pks { if i != 0 { sb.WriteString(", ") } sb.WriteString(pk.Name) } sb.WriteRune(')') } else { sb.WriteString(pks[0].Name) } if len(cks) > 0 { sb.WriteString(", ") for i, ck := range cks { if i != 0 { sb.WriteString(", ") } sb.WriteString(ck.Name) } } return sb.String() } type scyllaEncryptionOptions struct { CipherAlgorithm string `json:"cipher_algorithm"` KeyProvider string `json:"key_provider"` SecretKeyFile string `json:"secret_key_file"` SecretKeyStrength int `json:"secret_key_strength"` } // UnmarshalBinary deserializes blob into scyllaEncryptionOptions. // Format: // - 4 bytes - size of KV map // Size times: // - 4 bytes - length of key // - len_of_key bytes - key // - 4 bytes - length of value // - len_of_value bytes - value func (enc *scyllaEncryptionOptions) UnmarshalBinary(data []byte) error { size := binary.LittleEndian.Uint32(data[0:4]) m := make(map[string]string, size) off := uint32(4) for i := uint32(0); i < size; i++ { keyLen := binary.LittleEndian.Uint32(data[off : off+4]) off += 4 key := string(data[off : off+keyLen]) off += keyLen valueLen := binary.LittleEndian.Uint32(data[off : off+4]) off += 4 value := string(data[off : off+valueLen]) off += valueLen m[key] = value } enc.CipherAlgorithm = m["cipher_algorithm"] enc.KeyProvider = m["key_provider"] enc.SecretKeyFile = m["secret_key_file"] if secretKeyStrength, ok := m["secret_key_strength"]; ok { sks, err := strconv.Atoi(secretKeyStrength) if err != nil { return err } enc.SecretKeyStrength = sks } return nil } ================================================ FILE: recreate_test.go ================================================ //go:build integration // +build integration // Copyright (C) 2017 ScyllaDB package gocql import ( "context" "encoding/json" "flag" "fmt" "os" "regexp" "sort" "strings" "testing" "github.com/google/go-cmp/cmp" frm "github.com/gocql/gocql/internal/frame" ) var updateGolden = flag.Bool("update-golden", false, "update golden files") func TestRecreateSchema(t *testing.T) { t.Parallel() failsOnOldScylla := false if *flagDistribution == "scylla" && flagCassVersion.Before(2024, 0, 0) { failsOnOldScylla = true } session := createSessionFromClusterTabletsDisabled(createCluster(), t) defer session.Close() getStmtFromCluster := isDescribeKeyspaceSupported(t, session) tabletsAutoEnabled := isTabletsSupported() && isTabletsAutoEnabled() tcs := []struct { Name string FixedKeyspace string // original keyspace name used in .cql files FailWithTablets bool Input string Golden string }{ { Name: "Keyspace", FixedKeyspace: "gocqlx_keyspace", Input: "testdata/recreate/keyspace.cql", Golden: "testdata/recreate/keyspace_golden.cql", }, { Name: "Table", FixedKeyspace: "gocqlx_table", Input: "testdata/recreate/table.cql", Golden: "testdata/recreate/table_golden.cql", }, { Name: "Materialized Views", FixedKeyspace: "gocqlx_mv", FailWithTablets: failsOnOldScylla, Input: "testdata/recreate/materialized_views.cql", Golden: "testdata/recreate/materialized_views_golden.cql", }, { Name: "Index", FixedKeyspace: "gocqlx_idx", FailWithTablets: failsOnOldScylla, Input: "testdata/recreate/index.cql", Golden: "testdata/recreate/index_golden.cql", }, { Name: "Secondary Index", FixedKeyspace: "gocqlx_sec_idx", FailWithTablets: failsOnOldScylla, Input: "testdata/recreate/secondary_index.cql", Golden: "testdata/recreate/secondary_index_golden.cql", }, { Name: "UDT", FixedKeyspace: "gocqlx_udt", Input: "testdata/recreate/udt.cql", Golden: "testdata/recreate/udt_golden.cql", }, { Name: "Aggregates", FixedKeyspace: "gocqlx_aggregates", Input: "testdata/recreate/aggregates.cql", Golden: "testdata/recreate/aggregates_golden.cql", }, } for i := range tcs { test := tcs[i] t.Run(test.Name, func(t *testing.T) { if test.Name == "UDT" && *flagDistribution == "scylla" && flagCassVersion.Major == 2024 && flagCassVersion.Minor == 1 { t.Skip("Doesn't work properly on Scylla 2024.1 due to https://github.com/scylladb/scylladb/issues/26761") } // Generate a unique keyspace name to avoid collisions under parallel execution. // Replace the fixed keyspace name in CQL input/golden files with the unique name. ks := testKeyspaceName(t) cleanup(t, session, ks) in, err := os.ReadFile(test.Input) if err != nil { t.Fatal(err) } // Substitute the fixed keyspace name in the CQL input with the unique name. inStr := strings.ReplaceAll(string(in), test.FixedKeyspace, ks) queries := trimQueries(strings.Split(inStr, ";")) for _, q := range queries { qr := session.Query(q, nil) err = qr.Exec() if err != nil { break } qr.Release() } err = session.AwaitSchemaAgreement(context.Background()) if err != nil { t.Fatal("failed to await for schema agreement", err) } err = session.metadataDescriber.refreshKeyspaceSchema(ks) if err != nil { t.Fatal("failed to read schema for keyspace", err) } if tabletsAutoEnabled && test.FailWithTablets { if err == nil { t.Errorf("did not get expected error or tablets") } else if strings.Contains(err.Error(), "not supported") && strings.Contains(err.Error(), "tablets") { return } else { t.Fatal("query failed with unexpected error", err) } } else if err != nil { t.Fatal("invalid input query", err) } km, err := session.KeyspaceMetadata(ks) if err != nil { t.Fatal("dump schema", err) } dump, err := km.ToCQL() if err != nil { t.Fatal("recreate schema", err) } dump = trimSchema(dump) // Normalize the dump back to the fixed keyspace name for comparison with golden files. dump = strings.ReplaceAll(dump, ks, test.FixedKeyspace) var golden []byte if getStmtFromCluster { golden, err = getCreateStatements(session, ks) if err != nil { t.Fatal(err) } golden = []byte(trimSchema(string(golden))) // Normalize from the cluster's unique keyspace name back to fixed name. golden = []byte(strings.ReplaceAll(string(golden), ks, test.FixedKeyspace)) } else { if *updateGolden { if err := os.WriteFile(test.Golden, []byte(dump), 0644); err != nil { t.Fatal(err) } } golden, err = os.ReadFile(test.Golden) if err != nil { t.Fatal(err) } golden = []byte(trimSchema(string(golden))) } goldenQueries := trimQueries(sortQueries(strings.Split(string(golden), ";"))) dumpQueries := trimQueries(sortQueries(strings.Split(dump, ";"))) if len(goldenQueries) != len(dumpQueries) { t.Fatalf("Expected len(dumpQueries) to be %d, got %d", len(goldenQueries), len(dumpQueries)) } // Compare with golden for i, dq := range dumpQueries { gq := goldenQueries[i] if diff := cmp.Diff(gq, dq); diff != "" { t.Errorf("dumpQueries[%d] diff\n%s", i, diff) } } // Exec dumped queries to check if they are CQL-correct. // Substitute the fixed keyspace name back to the unique name for execution. cleanup(t, session, ks) session.metadataDescriber.invalidateKeyspaceSchema(ks) for _, q := range trimQueries(strings.Split(strings.ReplaceAll(dump, test.FixedKeyspace, ks), ";")) { qr := session.Query(q, nil) if err := qr.Exec(); err != nil { t.Fatal("invalid dump query", q, err) } qr.Release() } // Check if new dump is the same as previous err = session.AwaitSchemaAgreement(context.Background()) if err != nil { t.Fatal("failed to await for schema agreement", err) } err = session.metadataDescriber.refreshKeyspaceSchema(ks) if err != nil { t.Fatal("failed to read schema for keyspace", err) } km, err = session.KeyspaceMetadata(ks) if err != nil { t.Fatal("dump schema", err) } secondDump, err := km.ToCQL() if err != nil { t.Fatal("recreate schema", err) } secondDump = trimSchema(secondDump) // Normalize the second dump back to fixed keyspace name for comparison. secondDump = strings.ReplaceAll(secondDump, ks, test.FixedKeyspace) secondDumpQueries := trimQueries(sortQueries(strings.Split(secondDump, ";"))) if !cmp.Equal(secondDumpQueries, dumpQueries) { t.Errorf("first dump and second one differs: %s", cmp.Diff(secondDumpQueries, dumpQueries)) } }) } } func isDescribeKeyspaceSupported(t *testing.T, s *Session) bool { t.Helper() err := s.control.query(fmt.Sprintf(`DESCRIBE KEYSPACE system WITH INTERNALS`)).Close() if err != nil { if errFrame, ok := err.(frm.ErrorFrame); ok && errFrame.Code == ErrCodeSyntax { // DESCRIBE KEYSPACE is not supported on older versions of Cassandra and Scylla // For such case schema statement is going to be recreated on the client side return false } t.Fatalf("error querying keyspace schema: %v", err) } return true } func TestScyllaEncryptionOptionsUnmarshaller(t *testing.T) { t.Parallel() const ( input = "testdata/recreate/scylla_encryption_options.bin" golden = "testdata/recreate/scylla_encryption_options_golden.json" ) inputBuf, err := os.ReadFile(input) if err != nil { t.Fatal(err) } goldenBuf, err := os.ReadFile(golden) if err != nil { t.Fatal(err) } goldenOpts := &scyllaEncryptionOptions{} if err := json.Unmarshal(goldenBuf, goldenOpts); err != nil { t.Fatal(err) } opts := &scyllaEncryptionOptions{} if err := opts.UnmarshalBinary(inputBuf); err != nil { t.Error(err) } if !cmp.Equal(goldenOpts, opts) { t.Error(cmp.Diff(goldenOpts, opts)) } } func cleanup(t *testing.T, session *Session, keyspace string) { qr := session.Query(`DROP KEYSPACE IF EXISTS ` + keyspace) if err := qr.Exec(); err != nil { t.Fatalf("unable to drop keyspace: %v", err) } qr.Release() } func sortQueries(in []string) []string { q := trimQueries(in) sort.Strings(q) return q } func trimQueries(in []string) []string { queries := in[:0] for _, q := range in { q = strings.TrimSpace(q) if q == "" { continue } if len(q) != 0 { queries = append(queries, q) } } return queries } var schemaVersion = regexp.MustCompile(` WITH ID = [0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}[ \t\n]+AND`) func trimSchema(s string) string { // Remove temporary items from the scheme, in particular schema version: // ) WITH ID = cf0364d0-3b85-11ef-b79d-80a2ee1928c0 return strings.ReplaceAll(schemaVersion.ReplaceAllString(s, " WITH"), "\n\n", "\n") } ================================================ FILE: renovate.json ================================================ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ "config:recommended" ], "prConcurrentLimit": 2 } ================================================ FILE: ring_describer.go ================================================ package gocql import ( "context" "fmt" "sync" ) // Polls system.peers at a specific interval to find new hosts type ringDescriber struct { control controlConnection logger StdLogger cfg *ClusterConfig // hosts are the set of all hosts in the cassandra ring that we know of. // key of map is host_id. hosts map[string]*HostInfo // hostIPToUUID maps host native address to host_id. hostIPToUUID map[string]string prevPartitioner string prevHosts []*HostInfo mu sync.RWMutex } func (r *ringDescriber) setControlConn(c controlConnection) { r.mu.Lock() defer r.mu.Unlock() r.control = c } // Ask the control node for the local host info func (r *ringDescriber) getLocalHostInfo(conn ConnInterface) (*HostInfo, error) { iter := conn.querySystem(context.TODO(), qrySystemLocal) if iter == nil { return nil, errNoControl } host, err := hostInfoFromIter(iter, r.cfg.Port) if err != nil { return nil, fmt.Errorf("could not retrieve local host info: %w", err) } return host, nil } // Ask the control node for host info on all it's known peers func (r *ringDescriber) getClusterPeerInfo(localHost *HostInfo, c ConnInterface) ([]*HostInfo, error) { var iter *Iter if c.getIsSchemaV2() { iter = c.querySystem(context.TODO(), qrySystemPeersV2) } else { iter = c.querySystem(context.TODO(), qrySystemPeers) } if iter == nil { return nil, errNoControl } defer iter.Close() rows, err := iter.SliceMap() if err != nil { // TODO(zariel): make typed error return nil, fmt.Errorf("unable to fetch peer host info: %s", err) } return getPeersFromQuerySystemPeers(rows, r.cfg.Port, r.logger) } func getPeersFromQuerySystemPeers(querySystemPeerRows []map[string]any, defaultPort int, logger StdLogger) ([]*HostInfo, error) { var peers []*HostInfo for _, row := range querySystemPeerRows { // extract all available info about the peer host, err := hostInfoFromMap(row, defaultPort) if err != nil { return nil, err } else if !isValidPeer(host) { // If it's not a valid peer logger.Printf("Found invalid peer '%s' "+ "Likely due to a gossip or snitch issue, this host will be ignored", host) continue } else if isZeroToken(host) { continue } peers = append(peers, host) } return peers, nil } // Return true if the host is a valid peer func isValidPeer(host *HostInfo) bool { return !(len(host.RPCAddress()) == 0 || host.hostId.IsEmpty() || host.dataCenter == "" || host.rack == "") } func isZeroToken(host *HostInfo) bool { return len(host.tokens) == 0 } // GetHostsFromSystem returns a list of hosts found via queries to system.local and system.peers func (r *ringDescriber) GetHostsFromSystem() ([]*HostInfo, string, error) { r.mu.Lock() defer r.mu.Unlock() if r.control == nil { return r.prevHosts, r.prevPartitioner, errNoControl } ch := r.control.getConn() localHost, err := r.getLocalHostInfo(ch.conn) if err != nil { return r.prevHosts, r.prevPartitioner, err } peerHosts, err := r.getClusterPeerInfo(localHost, ch.conn) if err != nil { return r.prevHosts, r.prevPartitioner, err } var hosts []*HostInfo if !isZeroToken(localHost) { hosts = []*HostInfo{localHost} } hosts = append(hosts, peerHosts...) var partitioner string if len(hosts) > 0 { partitioner = hosts[0].Partitioner() } r.prevHosts = hosts r.prevPartitioner = partitioner return hosts, partitioner, nil } func (r *ringDescriber) getHostByIP(ip string) (*HostInfo, bool) { r.mu.RLock() defer r.mu.RUnlock() hi, ok := r.hostIPToUUID[ip] return r.hosts[hi], ok } func (r *ringDescriber) getHost(hostID string) *HostInfo { r.mu.RLock() host := r.hosts[hostID] r.mu.RUnlock() return host } func (r *ringDescriber) getHostsList() []*HostInfo { r.mu.RLock() hosts := make([]*HostInfo, 0, len(r.hosts)) for _, host := range r.hosts { hosts = append(hosts, host) } r.mu.RUnlock() return hosts } func (r *ringDescriber) getHostsMap() map[string]*HostInfo { r.mu.RLock() hosts := make(map[string]*HostInfo, len(r.hosts)) for k, v := range r.hosts { hosts[k] = v } r.mu.RUnlock() return hosts } func (r *ringDescriber) addOrUpdate(host *HostInfo) *HostInfo { if existingHost, ok := r.addHostIfMissing(host); ok { existingHost.update(host) host = existingHost } return host } func (r *ringDescriber) addHostIfMissing(host *HostInfo) (*HostInfo, bool) { if !validIpAddr(host.ConnectAddress()) { panic(fmt.Sprintf("invalid host: %v", host)) } hostID := host.HostID() r.mu.Lock() if r.hosts == nil { r.hosts = make(map[string]*HostInfo) } if r.hostIPToUUID == nil { r.hostIPToUUID = make(map[string]string) } existing, ok := r.hosts[hostID] if !ok { r.hosts[hostID] = host r.hostIPToUUID[host.nodeToNodeAddress().String()] = hostID existing = host } r.mu.Unlock() return existing, ok } func (r *ringDescriber) removeHost(hostID string) bool { r.mu.Lock() if r.hosts == nil { r.hosts = make(map[string]*HostInfo) } if r.hostIPToUUID == nil { r.hostIPToUUID = make(map[string]string) } h, ok := r.hosts[hostID] if ok { delete(r.hostIPToUUID, h.nodeToNodeAddress().String()) } delete(r.hosts, hostID) r.mu.Unlock() return ok } ================================================ FILE: ring_describer_test.go ================================================ //go:build unit // +build unit package gocql import ( "context" "fmt" "net" "testing" "time" "github.com/gocql/gocql/internal/tests" "github.com/gocql/gocql/internal/tests/mock" ) func TestGetClusterPeerInfoZeroToken(t *testing.T) { t.Parallel() schema_version1 := ParseUUIDMust("af810386-a694-11ef-81fa-3aea73156247") peersRows := []map[string]any{ { "data_center": "datacenter1", "host_id": ParseUUIDMust("b2035fd9-e0ca-4857-8c45-e63c00fb7c43"), "peer": "127.0.0.3", "preferred_ip": "127.0.0.3", "rack": "rack1", "release_version": "3.0.8", "rpc_address": "127.0.0.3", "schema_version": schema_version1, "tokens": []string{"-1296227678594315580994457470329811265"}, }, { "data_center": "datacenter1", "host_id": ParseUUIDMust("4b21ee4c-acea-4267-8e20-aaed5361a0dd"), "peer": "127.0.0.2", "preferred_ip": "127.0.0.2", "rack": "rack1", "release_version": "3.0.8", "rpc_address": "127.0.0.2", "schema_version": schema_version1, "tokens": []string{"-1129762924682054333"}, }, { "data_center": "datacenter2", "host_id": ParseUUIDMust("dfef4a22-b8d8-47e9-aee5-8c19d4b7a9e3"), "peer": "127.0.0.5", "preferred_ip": "127.0.0.5", "rack": "rack1", "release_version": "3.0.8", "rpc_address": "127.0.0.5", "schema_version": schema_version1, "tokens": []string{}, }, } var logger StdLogger t.Run("OmitOneZeroTokenNode", func(t *testing.T) { peers, err := getPeersFromQuerySystemPeers( peersRows, 9042, logger, ) if err != nil { t.Fatalf("unable to get peers: %v", err) } tests.AssertEqual(t, "peers length", 2, len(peers)) }) t.Run("NoZeroTokenNodes", func(t *testing.T) { peersRows[2]["tokens"] = []string{"-1129762924682054333"} peers, err := getPeersFromQuerySystemPeers( peersRows, 9042, logger, ) if err != nil { t.Fatalf("unable to get peers: %v", err) } tests.AssertEqual(t, "peers length", 3, len(peers)) }) } type mockConnection struct{} func (*mockConnection) Close() {} func (*mockConnection) exec(ctx context.Context, req frameBuilder, tracer Tracer, requestTimeout time.Duration) (*framer, error) { return nil, nil } func (*mockConnection) awaitSchemaAgreement(ctx context.Context) error { return nil } func (*mockConnection) executeQuery(ctx context.Context, qry *Query) *Iter { return nil } var systemLocalResultMetadata = resultMetadata{ flags: 0, pagingState: []byte{}, actualColCount: 18, columns: []ColumnInfo{{ Keyspace: "system", Table: "local", Name: "key", TypeInfo: NativeType{proto: protoVersion4, typ: TypeVarchar}, }, { Keyspace: "system", Table: "local", Name: "bootstrapped", TypeInfo: NativeType{proto: protoVersion4, typ: TypeVarchar}, }, { Keyspace: "system", Table: "local", Name: "broadcast_address", TypeInfo: NativeType{proto: protoVersion4, typ: TypeInet}, }, { Keyspace: "system", Table: "local", Name: "cluster_name", TypeInfo: NativeType{proto: protoVersion4, typ: TypeVarchar}, }, { Keyspace: "system", Table: "local", Name: "cql_version", TypeInfo: NativeType{proto: protoVersion4, typ: TypeVarchar}, }, { Keyspace: "system", Table: "local", Name: "data_center", TypeInfo: NativeType{proto: protoVersion4, typ: TypeVarchar}, }, { Keyspace: "system", Table: "local", Name: "gossip_generation", TypeInfo: NativeType{proto: protoVersion4, typ: TypeInt}, }, { Keyspace: "system", Table: "local", Name: "host_id", TypeInfo: NativeType{proto: protoVersion4, typ: TypeUUID}, }, { Keyspace: "system", Table: "local", Name: "listen_address", TypeInfo: NativeType{proto: protoVersion4, typ: TypeInet}, }, { Keyspace: "system", Table: "local", Name: "native_protocol_version", TypeInfo: NativeType{proto: protoVersion4, typ: TypeVarchar}, }, { Keyspace: "system", Table: "local", Name: "partitioner", TypeInfo: NativeType{proto: protoVersion4, typ: TypeVarchar}, }, { Keyspace: "system", Table: "local", Name: "rack", TypeInfo: NativeType{proto: protoVersion4, typ: TypeVarchar}, }, { Keyspace: "system", Table: "local", Name: "release_version", TypeInfo: NativeType{proto: protoVersion4, typ: TypeVarchar}, }, { Keyspace: "system", Table: "local", Name: "rpc_address", TypeInfo: NativeType{proto: protoVersion4, typ: TypeInet}, }, { Keyspace: "system", Table: "local", Name: "schema_version", TypeInfo: NativeType{proto: protoVersion4, typ: TypeUUID}, }, { Keyspace: "system", Table: "local", Name: "supported_features", TypeInfo: NativeType{proto: protoVersion4, typ: TypeVarchar}, }, { Keyspace: "system", Table: "local", Name: "tokens", TypeInfo: CollectionType{ NativeType: NativeType{proto: protoVersion4, typ: TypeSet}, Elem: NativeType{proto: protoVersion4, typ: TypeVarchar}, }, }, { Keyspace: "system", Table: "local", Name: "truncated_at", TypeInfo: CollectionType{ NativeType: NativeType{proto: protoVersion4, typ: TypeMap}, Key: NativeType{proto: protoVersion4, typ: TypeUUID}, Elem: NativeType{proto: protoVersion4, typ: TypeBlob}, }, }}, } var systemPeersResultMetadata = resultMetadata{ flags: 0, pagingState: []byte{}, actualColCount: 10, columns: []ColumnInfo{{ Keyspace: "system", Table: "local", Name: "peer", TypeInfo: NativeType{proto: protoVersion4, typ: TypeInet}, }, { Keyspace: "system", Table: "local", Name: "data_center", TypeInfo: NativeType{proto: protoVersion4, typ: TypeVarchar}, }, { Keyspace: "system", Table: "local", Name: "host_id", TypeInfo: NativeType{proto: protoVersion4, typ: TypeUUID}, }, { Keyspace: "system", Table: "local", Name: "preferred_ip", TypeInfo: NativeType{proto: protoVersion4, typ: TypeInet}, }, { Keyspace: "system", Table: "local", Name: "rack", TypeInfo: NativeType{proto: protoVersion4, typ: TypeVarchar}, }, { Keyspace: "system", Table: "local", Name: "release_version", TypeInfo: NativeType{proto: protoVersion4, typ: TypeVarchar}, }, { Keyspace: "system", Table: "local", Name: "rpc_address", TypeInfo: NativeType{proto: protoVersion4, typ: TypeInet}, }, { Keyspace: "system", Table: "local", Name: "schema_version", TypeInfo: NativeType{proto: protoVersion4, typ: TypeUUID}, }, { Keyspace: "system", Table: "local", Name: "supported_features", TypeInfo: NativeType{proto: protoVersion4, typ: TypeVarchar}, }, { Keyspace: "system", Table: "local", Name: "tokens", TypeInfo: CollectionType{ NativeType: NativeType{proto: protoVersion4, typ: TypeSet}, Elem: NativeType{proto: protoVersion4, typ: TypeVarchar}, }, }}, } func (*mockConnection) querySystem(ctx context.Context, query string, values ...any) *Iter { localData := []any{"local", "COMPLETED", net.IPv4(192, 168, 100, 12), "", "3.3.1", "datacenter1", 1733834239, ParseUUIDMust("045859a7-6b9f-4efd-a5e7-acd64a295e13"), net.IPv4(192, 168, 100, 12), "4", "org.apache.cassandra.dht.Murmur3Partitioner", "rack1", "3.0.8", net.IPv4(192, 168, 100, 12), ParseUUIDMust("daf4df2c-b708-11ef-5c25-3004361afd71"), "", []string{}, map[UUID]byte{}} peerData1 := []any{net.IPv4(192, 168, 100, 13), "datacenter1", ParseUUIDMust("b953309f-6e68-41f2-baf5-0e60da317a9c"), net.IP{}, "rack1", "3.0.8", net.IPv4(192, 168, 100, 13), ParseUUIDMust("b6ed5bde-b318-11ef-8f58-aeba19e31273"), "", []string{"-1032311531684407545", "-1112089412567859825"}} peerData2 := []any{net.IPv4(192, 168, 100, 14), "datacenter1", ParseUUIDMust("8269e111-ea38-44bd-a73f-9d3d12cfaf78"), net.IP{}, "rack1", "3.0.8", net.IPv4(192, 168, 100, 14), ParseUUIDMust("b6ed5bde-b318-11ef-8f58-aeba19e31273"), "", []string{}} if query == "SELECT * FROM system.local WHERE key='local'" { return &Iter{ meta: systemLocalResultMetadata, framer: &mock.MockFramer{Data: marshalMetadataMust(systemLocalResultMetadata, localData)}, numRows: 1, next: nil, } } else if query == "SELECT * FROM system.peers" { return &Iter{ meta: systemPeersResultMetadata, framer: &mock.MockFramer{Data: append(marshalMetadataMust(systemPeersResultMetadata, peerData1), marshalMetadataMust(systemPeersResultMetadata, peerData2)...)}, numRows: 2, next: nil, } } return nil } func (*mockConnection) getIsSchemaV2() bool { return false } func (*mockConnection) setSchemaV2(s bool) {} func (*mockConnection) getScyllaSupported() ScyllaConnectionFeatures { return ScyllaConnectionFeatures{} } type mockControlConn struct{} func (m *mockControlConn) querySystem(statement string, values ...any) (iter *Iter) { return nil } func (m *mockControlConn) reconnect() error { return nil } func (m *mockControlConn) getConn() *connHost { return &connHost{ conn: &mockConnection{}, host: &HostInfo{}, } } func (m *mockControlConn) awaitSchemaAgreement() error { return nil } func (m *mockControlConn) query(statement string, values ...any) (iter *Iter) { return nil } func (m *mockControlConn) discoverProtocol(hosts []*HostInfo) (int, error) { return 0, nil } func (m *mockControlConn) connect(hosts []*HostInfo) error { return nil } func (m *mockControlConn) close() {} func (m *mockControlConn) getSession() *Session { return nil } func marshalMetadataMust(metadata resultMetadata, data []any) [][]byte { if len(metadata.columns) != len(data) { panic("metadata length mismatch") } res := make([][]byte, len(metadata.columns)) for id, col := range metadata.columns { var err error value := data[id] res[id], err = Marshal(col.TypeInfo, value) if err != nil { panic(fmt.Sprintf("unable to marshal column %d: %v", id, err)) } } return res } type trackingRingConnection struct { iter *Iter schemaV2 bool } func (*trackingRingConnection) Close() {} func (*trackingRingConnection) exec(context.Context, frameBuilder, Tracer, time.Duration) (*framer, error) { return nil, nil } func (*trackingRingConnection) awaitSchemaAgreement(context.Context) error { return nil } func (*trackingRingConnection) executeQuery(context.Context, *Query) *Iter { return nil } func (c *trackingRingConnection) querySystem(context.Context, string, ...any) *Iter { return c.iter } func (c *trackingRingConnection) getIsSchemaV2() bool { return c.schemaV2 } func (*trackingRingConnection) setSchemaV2(bool) {} func (*trackingRingConnection) getScyllaSupported() ScyllaConnectionFeatures { return ScyllaConnectionFeatures{} } func TestMockGetHostsFromSystem(t *testing.T) { t.Parallel() r := &ringDescriber{control: &mockControlConn{}, cfg: &ClusterConfig{}} hosts, _, err := r.GetHostsFromSystem() if err != nil { t.Fatalf("unable to get hosts: %v", err) } // local host and one of the peers are zero token so only one peer should be returned with 2 tokens tests.AssertEqual(t, "hosts length", 1, len(hosts)) tests.AssertEqual(t, "host token length", 2, len(hosts[0].tokens)) } func TestRingDescriberGetClusterPeerInfoClosesIter(t *testing.T) { t.Parallel() row := []any{ net.IPv4(192, 168, 100, 13), "datacenter1", ParseUUIDMust("b953309f-6e68-41f2-baf5-0e60da317a9c"), net.IP{}, "rack1", "3.0.8", net.IPv4(192, 168, 100, 13), ParseUUIDMust("b6ed5bde-b318-11ef-8f58-aeba19e31273"), "", []string{"-1032311531684407545"}, } framer := &trackingMockFramer{ MockFramer: mock.MockFramer{Data: marshalMetadataMust(systemPeersResultMetadata, row)}, } r := &ringDescriber{cfg: &ClusterConfig{}} peers, err := r.getClusterPeerInfo(&HostInfo{}, &trackingRingConnection{ iter: &Iter{ meta: systemPeersResultMetadata, framer: framer, numRows: 1, }, }) if err != nil { t.Fatalf("unexpected error: %v", err) } if len(peers) != 1 { t.Fatalf("expected 1 peer, got %d", len(peers)) } if !framer.released { t.Fatal("expected iterator framer to be released") } } func TestRing_AddHostIfMissing_Missing(t *testing.T) { t.Parallel() ring := &ringDescriber{} host := &HostInfo{hostId: MustRandomUUID(), connectAddress: net.IPv4(1, 1, 1, 1)} h1, ok := ring.addHostIfMissing(host) if ok { t.Fatal("host was reported as already existing") } else if !h1.Equal(host) { t.Fatalf("hosts not equal that are returned %v != %v", h1, host) } else if h1 != host { t.Fatalf("returned host same pointer: %p != %p", h1, host) } } func TestRing_AddHostIfMissing_Existing(t *testing.T) { t.Parallel() ring := &ringDescriber{} host := &HostInfo{hostId: MustRandomUUID(), connectAddress: net.IPv4(1, 1, 1, 1)} ring.addHostIfMissing(host) h2 := &HostInfo{hostId: host.hostId, connectAddress: net.IPv4(2, 2, 2, 2)} h1, ok := ring.addHostIfMissing(h2) if !ok { t.Fatal("host was not reported as already existing") } else if !h1.Equal(host) { t.Fatalf("hosts not equal that are returned %v != %v", h1, host) } else if h1 != host { t.Fatalf("returned host same pointer: %p != %p", h1, host) } } ================================================ FILE: schema_queries_test.go ================================================ //go:build integration // +build integration package gocql import ( "testing" "github.com/gocql/gocql/internal/tests" ) func TestSchemaQueries(t *testing.T) { t.Parallel() cluster := createCluster() fallback := RoundRobinHostPolicy() cluster.PoolConfig.HostSelectionPolicy = TokenAwareHostPolicy(fallback) session := createSessionFromCluster(cluster, t) defer session.Close() keyspaceMetadata, err := session.metadataDescriber.GetKeyspace("gocql_test") if err != nil { t.Fatal("unable to get keyspace metadata for keyspace: ", err) } tests.AssertTrue(t, "keyspace present in metadataDescriber", keyspaceMetadata.Name == "gocql_test") } ================================================ FILE: scylla.go ================================================ package gocql import ( "context" "crypto/tls" "errors" "fmt" "math" "net" "strconv" "strings" "sync/atomic" "syscall" "time" "github.com/gocql/gocql/internal/debug" ) // ScyllaFeatures represents Scylla connection options as sent in SUPPORTED // frame. // FIXME: Should also follow `cqlProtocolExtension` interface. type ScyllaConnectionFeatures struct { ScyllaHostFeatures // Comes from SCYLLA_SHARD shard int } func (f ScyllaConnectionFeatures) Shard() int { return f.shard } type ScyllaHostFeatures struct { // Comes from SCYLLA_PARTITIONER partitioner string // Comes from SCYLLA_SHARDING_ALGORITHM shardingAlgorithm string // Comes from SCYLLA_NR_SHARDS nrShards int // Comes from SCYLLA_SHARDING_IGNORE_MSB msbIgnore uint64 // Comes from SCYLLA_LWT_ADD_METADATA_MARK.LWT_OPTIMIZATION_META_BIT_MASK lwtFlagMask int // Comes from SCYLLA_RATE_LIMIT_ERROR.ERROR_CODE rateLimitErrorCode int // Comes from SCYLLA_SHARD_AWARE_PORT shardAwarePort uint16 // Comes from SCYLLA_SHARD_AWARE_PORT_SSL shardAwarePortTLS uint16 // Comes from SCYLLA_USE_METADATA_ID // Signals that host supports proper prepared statement metadata invalidation read more at: // https://github.com/scylladb/scylladb/issues/20860 // https://github.com/scylladb/scylladb/pull/23292 isMetadataIDSupported bool } func (f ScyllaHostFeatures) IsPresent() bool { return f.nrShards != 0 } func (f ScyllaHostFeatures) Partitioner() string { return f.partitioner } func (f ScyllaHostFeatures) ShardingAlgorithm() string { return f.shardingAlgorithm } func (f ScyllaHostFeatures) ShardsCount() int { return f.nrShards } func (f ScyllaHostFeatures) MSBIgnore() uint64 { return f.msbIgnore } func (f ScyllaHostFeatures) LWTFlagMask() int { return f.lwtFlagMask } func (f ScyllaHostFeatures) ShardAwarePort() uint16 { return f.shardAwarePort } func (f ScyllaHostFeatures) ShardAwarePortTLS() uint16 { return f.shardAwarePortTLS } func (f ScyllaHostFeatures) RateLimitErrorCode() int { return f.rateLimitErrorCode } func (f ScyllaHostFeatures) IsMetadataIDSupported() bool { return f.isMetadataIDSupported } // CQL Protocol extension interface for Scylla. // Each extension is identified by a name and defines a way to serialize itself // in STARTUP message payload. type cqlProtocolExtension interface { name() string serialize() map[string]string } func findCQLProtoExtByName(exts []cqlProtocolExtension, name string) cqlProtocolExtension { for i := range exts { if exts[i].name() == name { return exts[i] } } return nil } // Top-level keys used for serialization/deserialization of CQL protocol // extensions in SUPPORTED/STARTUP messages. // Each key identifies a single extension. const ( lwtAddMetadataMarkKey = "SCYLLA_LWT_ADD_METADATA_MARK" rateLimitError = "SCYLLA_RATE_LIMIT_ERROR" tabletsRoutingV1 = "TABLETS_ROUTING_V1" ) // "tabletsRoutingV1" CQL Protocol Extension. // This extension, if enabled (properly negotiated), allows Scylla server // to send a tablet information in `custom_payload`. // // Implements cqlProtocolExtension interface. type tabletsRoutingV1Ext struct { } var _ cqlProtocolExtension = &tabletsRoutingV1Ext{} // Factory function to deserialize and create an `tabletsRoutingV1Ext` instance // from SUPPORTED message payload. func newTabletsRoutingV1Ext(supported map[string][]string) *tabletsRoutingV1Ext { if _, found := supported[tabletsRoutingV1]; found { return &tabletsRoutingV1Ext{} } return nil } func (ext *tabletsRoutingV1Ext) serialize() map[string]string { return map[string]string{ tabletsRoutingV1: "", } } func (ext *tabletsRoutingV1Ext) name() string { return tabletsRoutingV1 } // "Rate limit" CQL Protocol Extension. // This extension, if enabled (properly negotiated), allows Scylla server // to send a special kind of error. // // Implements cqlProtocolExtension interface. type rateLimitExt struct { rateLimitErrorCode int } var _ cqlProtocolExtension = &rateLimitExt{} // Factory function to deserialize and create an `rateLimitExt` instance // from SUPPORTED message payload. func newRateLimitExt(supported map[string][]string, logger StdLogger) *rateLimitExt { const rateLimitErrorCode = "ERROR_CODE" if v, found := supported[rateLimitError]; found { for i := range v { splitVal := strings.Split(v[i], "=") if splitVal[0] == rateLimitErrorCode { var ( err error errorCode int ) if errorCode, err = strconv.Atoi(splitVal[1]); err != nil { if debug.Enabled { logger.Printf("scylla: failed to parse %s value %v: %s", rateLimitErrorCode, splitVal[1], err) return nil } } return &rateLimitExt{ rateLimitErrorCode: errorCode, } } } } return nil } func (ext *rateLimitExt) serialize() map[string]string { return map[string]string{ rateLimitError: "", } } func (ext *rateLimitExt) name() string { return rateLimitError } // "LWT prepared statements metadata mark" CQL Protocol Extension. // This extension, if enabled (properly negotiated), allows Scylla server // to set a special bit in prepared statements metadata, which would indicate // whether the statement at hand is LWT statement or not. // // This is further used to consistently choose primary replicas in a predefined // order for these queries, which can reduce contention over hot keys and thus // increase LWT performance. // // Implements cqlProtocolExtension interface. type lwtAddMetadataMarkExt struct { lwtOptMetaBitMask int } var _ cqlProtocolExtension = &lwtAddMetadataMarkExt{} // Factory function to deserialize and create an `lwtAddMetadataMarkExt` instance // from SUPPORTED message payload. func newLwtAddMetaMarkExt(supported map[string][]string, logger StdLogger) *lwtAddMetadataMarkExt { const lwtOptMetaBitMaskKey = "LWT_OPTIMIZATION_META_BIT_MASK" if v, found := supported[lwtAddMetadataMarkKey]; found { for i := range v { splitVal := strings.Split(v[i], "=") if splitVal[0] == lwtOptMetaBitMaskKey { var ( err error bitMask int ) if bitMask, err = strconv.Atoi(splitVal[1]); err != nil { if debug.Enabled { logger.Printf("scylla: failed to parse %s value %v: %s", lwtOptMetaBitMaskKey, splitVal[1], err) return nil } } return &lwtAddMetadataMarkExt{ lwtOptMetaBitMask: bitMask, } } } } return nil } func (ext *lwtAddMetadataMarkExt) serialize() map[string]string { return map[string]string{ lwtAddMetadataMarkKey: fmt.Sprintf("LWT_OPTIMIZATION_META_BIT_MASK=%d", ext.lwtOptMetaBitMask), } } func (ext *lwtAddMetadataMarkExt) name() string { return lwtAddMetadataMarkKey } func parseSupported(supported map[string][]string, logger StdLogger) ScyllaConnectionFeatures { const ( scyllaShard = "SCYLLA_SHARD" scyllaNrShards = "SCYLLA_NR_SHARDS" scyllaPartitioner = "SCYLLA_PARTITIONER" scyllaShardingAlgorithm = "SCYLLA_SHARDING_ALGORITHM" scyllaShardingIgnoreMSB = "SCYLLA_SHARDING_IGNORE_MSB" scyllaShardAwarePort = "SCYLLA_SHARD_AWARE_PORT" scyllaShardAwarePortSSL = "SCYLLA_SHARD_AWARE_PORT_SSL" scyllaUseMetadataID = "SCYLLA_USE_METADATA_ID" ) var ( si ScyllaConnectionFeatures err error ) if s, ok := supported[scyllaShard]; ok { if si.shard, err = strconv.Atoi(s[0]); err != nil { if debug.Enabled { logger.Printf("scylla: failed to parse %s value %v: %s", scyllaShard, s, err) } } } if s, ok := supported[scyllaNrShards]; ok { if si.nrShards, err = strconv.Atoi(s[0]); err != nil { if debug.Enabled { logger.Printf("scylla: failed to parse %s value %v: %s", scyllaNrShards, s, err) } } } if s, ok := supported[scyllaShardingIgnoreMSB]; ok { if si.msbIgnore, err = strconv.ParseUint(s[0], 10, 64); err != nil { if debug.Enabled { logger.Printf("scylla: failed to parse %s value %v: %s", scyllaShardingIgnoreMSB, s, err) } } } if s, ok := supported[scyllaPartitioner]; ok { si.partitioner = s[0] } if s, ok := supported[scyllaShardingAlgorithm]; ok { si.shardingAlgorithm = s[0] } if s, ok := supported[scyllaShardAwarePort]; ok { if shardAwarePort, err := strconv.ParseUint(s[0], 10, 16); err != nil { if debug.Enabled { logger.Printf("scylla: failed to parse %s value %v: %s", scyllaShardAwarePort, s, err) } } else { si.shardAwarePort = uint16(shardAwarePort) } } if s, ok := supported[scyllaShardAwarePortSSL]; ok { if shardAwarePortTLS, err := strconv.ParseUint(s[0], 10, 16); err != nil { if debug.Enabled { logger.Printf("scylla: failed to parse %s value %v: %s", scyllaShardAwarePortSSL, s, err) } } else { si.shardAwarePortTLS = uint16(shardAwarePortTLS) } } if lwtInfo := newLwtAddMetaMarkExt(supported, logger); lwtInfo != nil { si.lwtFlagMask = lwtInfo.lwtOptMetaBitMask } if rateLimitInfo := newRateLimitExt(supported, logger); rateLimitInfo != nil { si.rateLimitErrorCode = rateLimitInfo.rateLimitErrorCode } if _, ok := supported[scyllaUseMetadataID]; ok { si.isMetadataIDSupported = true } if si.partitioner != "org.apache.cassandra.dht.Murmur3Partitioner" || si.shardingAlgorithm != "biased-token-round-robin" || si.nrShards == 0 || si.msbIgnore == 0 { if debug.Enabled { logger.Printf("scylla: unsupported sharding configuration, partitioner=%s, algorithm=%s, no_shards=%d, msb_ignore=%d", si.partitioner, si.shardingAlgorithm, si.nrShards, si.msbIgnore) } return ScyllaConnectionFeatures{} } return si } func parseCQLProtocolExtensions(supported map[string][]string, logger StdLogger) []cqlProtocolExtension { exts := []cqlProtocolExtension{} lwtExt := newLwtAddMetaMarkExt(supported, logger) if lwtExt != nil { exts = append(exts, lwtExt) } rateLimitExt := newRateLimitExt(supported, logger) if rateLimitExt != nil { exts = append(exts, rateLimitExt) } tabletsExt := newTabletsRoutingV1Ext(supported) if tabletsExt != nil { exts = append(exts, tabletsExt) } return exts } // isScyllaConn checks if conn is suitable for scyllaConnPicker. func (c *Conn) isScyllaConn() bool { return c.getScyllaSupported().nrShards != 0 } // scyllaConnPicker is a specialised ConnPicker that selects connections based // on token trying to get connection to a shard containing the given token. // A list of excess connections is maintained to allow for lazy closing of // connections to already opened shards. Keeping excess connections open helps // reaching equilibrium faster since the likelihood of hitting the same shard // decreases with the number of connections to the shard. // // scyllaConnPicker keeps track of the details about the shard-aware port. // When used as a Dialer, it connects to the shard-aware port instead of the // regular port (if the node supports it). For each subsequent connection // it tries to make, the shard that it aims to connect to is chosen // in a round-robin fashion. type scyllaConnPicker struct { logger StdLogger // disableShardAwarePortUntil is used to temporarily disable new connections to the shard-aware port temporarily disableShardAwarePortUntil *atomic.Value address string excessConns []*Conn conns []*Conn nrShards int pos uint64 lastAttemptedShard int msbIgnore uint64 nrConns int excessConnsLimitRate float32 hostId UUID shardAwarePortDisabled bool } func newScyllaConnPicker(conn *Conn, logger StdLogger) *scyllaConnPicker { addr := conn.Address() if conn.scyllaSupported.nrShards == 0 { panic(fmt.Sprintf("scylla: %s not a sharded connection", addr)) } if debug.Enabled { logger.Printf("scylla: %s new conn picker sharding options %+v", addr, conn.scyllaSupported) } return &scyllaConnPicker{ address: addr, hostId: conn.host.hostId, nrShards: conn.scyllaSupported.nrShards, msbIgnore: conn.scyllaSupported.msbIgnore, lastAttemptedShard: 0, shardAwarePortDisabled: conn.session.cfg.DisableShardAwarePort, logger: logger, excessConnsLimitRate: conn.session.cfg.MaxExcessShardConnectionsRate, disableShardAwarePortUntil: new(atomic.Value), } } func (p *scyllaConnPicker) Pick(t Token, qry ExecutableQuery) *Conn { if len(p.conns) == 0 { return nil } if t == nil { return p.leastBusyConn() } mmt, ok := t.(int64Token) // double check if that's murmur3 token if !ok { return nil } idx := -1 outer: for _, conn := range p.conns { if conn == nil { continue } if qry != nil && conn.isTabletSupported() { for _, replica := range conn.session.findTabletReplicasUnsafeForToken(qry.Keyspace(), qry.Table(), int64(mmt)) { if UUID(replica.HostUUIDValue()) == p.hostId { idx = replica.ShardID() break outer } } } break } if idx == -1 { idx = p.shardOf(mmt) } if c := p.conns[idx]; c != nil { // We have this shard's connection // so let's give it to the caller. // But only if it's not loaded too much and load is well distributed. if qry != nil && qry.IsLWT() { return c } return p.maybeReplaceWithLessBusyConnection(c) } return p.leastBusyConn() } func (p *scyllaConnPicker) maybeReplaceWithLessBusyConnection(c *Conn) *Conn { if !isHeavyLoaded(c) { return c } alternative := p.leastBusyConn() if alternative == nil || alternative.AvailableStreams()*120 > c.AvailableStreams()*100 { return c } else { return alternative } } func isHeavyLoaded(c *Conn) bool { return c.streams.NumStreams/2 > c.AvailableStreams() } func (p *scyllaConnPicker) leastBusyConn() *Conn { var ( leastBusyConn *Conn streamsAvailable int ) idx := int(atomic.AddUint64(&p.pos, 1)) // find the conn which has the most available streams, this is racy for i := range p.conns { if conn := p.conns[(idx+i)%len(p.conns)]; conn != nil { if streams := conn.AvailableStreams(); streams > streamsAvailable { leastBusyConn = conn streamsAvailable = streams } } } return leastBusyConn } func (p *scyllaConnPicker) shardOf(token int64Token) int { shards := uint64(p.nrShards) z := uint64(token+math.MinInt64) << p.msbIgnore lo := z & 0xffffffff hi := (z >> 32) & 0xffffffff mul1 := lo * shards mul2 := hi * shards sum := (mul1 >> 32) + mul2 return int(sum >> 32) } func (p *scyllaConnPicker) Put(conn *Conn) error { var ( nrShards = conn.scyllaSupported.nrShards shard = conn.scyllaSupported.shard ) if nrShards == 0 { return errors.New("server reported that it has no shards") } if nrShards != p.nrShards { if debug.Enabled { p.logger.Printf("scylla: %s shard count changed from %d to %d, rebuilding connection pool", p.address, p.nrShards, nrShards) } p.handleShardCountChange(conn, nrShards) } else if nrShards != len(p.conns) { conns := p.conns p.conns = make([]*Conn, nrShards) copy(p.conns, conns) } if c := p.conns[shard]; c != nil { if conn.isShardAware { // A connection made to the shard-aware port resulted in duplicate // connection to the same shard being made. Because this is never // intentional, it suggests that a NAT or AddressTranslator // changes the source port along the way, therefore we can't trust // the shard-aware port to return connection to the shard // that we requested. Fall back to non-shard-aware port for some time. p.logger.Printf( "scylla: connection to shard-aware address %s resulted in wrong shard being assigned; please check that you are not behind a NAT or AddressTranslater which changes source ports; falling back to non-shard-aware port for %v", p.address, scyllaShardAwarePortFallbackDuration, ) until := time.Now().Add(scyllaShardAwarePortFallbackDuration) p.disableShardAwarePortUntil.Store(until) return fmt.Errorf("connection landed on %d shard that already has connection", shard) } else { p.excessConns = append(p.excessConns, conn) if debug.Enabled { p.logger.Printf("scylla: %s put shard %d excess connection total: %d missing: %d excess: %d", p.address, shard, p.nrConns, p.nrShards-p.nrConns, len(p.excessConns)) } } } else { p.conns[shard] = conn p.nrConns++ if debug.Enabled { p.logger.Printf("scylla: %s put shard %d connection total: %d missing: %d", p.address, shard, p.nrConns, p.nrShards-p.nrConns) } } if p.shouldCloseExcessConns() { p.closeExcessConns() } return nil } func (p *scyllaConnPicker) handleShardCountChange(newConn *Conn, newShardCount int) { oldShardCount := p.nrShards oldConns := make([]*Conn, len(p.conns)) copy(oldConns, p.conns) if debug.Enabled { p.logger.Printf("scylla: %s handling shard topology change from %d to %d", p.address, oldShardCount, newShardCount) } newConns := make([]*Conn, newShardCount) var toClose []*Conn migratedCount := 0 for i, conn := range oldConns { if conn == nil { continue } if i < newShardCount { newConns[i] = conn migratedCount++ } else { toClose = append(toClose, conn) } } p.nrShards = newShardCount p.msbIgnore = newConn.scyllaSupported.msbIgnore p.conns = newConns p.nrConns = migratedCount p.lastAttemptedShard = 0 if len(toClose) > 0 { go closeConns(toClose...) } if debug.Enabled { p.logger.Printf("scylla: %s migrated %d/%d connections to new shard topology, closing %d excess connections", p.address, migratedCount, len(oldConns), len(toClose)) } } func (p *scyllaConnPicker) shouldCloseExcessConns() bool { if p.nrConns >= p.nrShards { return true } return len(p.excessConns) > int(p.excessConnsLimitRate*float32(p.nrShards)) } func (p *scyllaConnPicker) GetConnectionCount() int { return p.nrConns } func (p *scyllaConnPicker) GetExcessConnectionCount() int { return len(p.excessConns) } func (p *scyllaConnPicker) GetShardCount() int { return p.nrShards } func (p *scyllaConnPicker) Remove(conn *Conn) { shard := conn.scyllaSupported.shard if conn.scyllaSupported.nrShards == 0 { // It is possible for Remove to be called before the connection is added to the pool. // Ignoring these connections here is safe. if debug.Enabled { p.logger.Printf("scylla: %s has unknown sharding state, ignoring it", p.address) } return } if debug.Enabled { p.logger.Printf("scylla: %s remove shard %d connection", p.address, shard) } if p.conns[shard] != nil { p.conns[shard] = nil p.nrConns-- } } func (p *scyllaConnPicker) InFlight() int { result := 0 for _, conn := range p.conns { if conn != nil { result = result + (conn.streams.InUse()) } } return result } func (p *scyllaConnPicker) Size() (int, int) { return p.nrConns, p.nrShards - p.nrConns } func (p *scyllaConnPicker) Close() { p.closeConns() p.closeExcessConns() } func (p *scyllaConnPicker) closeConns() { if len(p.conns) == 0 { if debug.Enabled { p.logger.Printf("scylla: %s no connections to close", p.address) } return } conns := p.conns p.conns = nil p.nrConns = 0 if debug.Enabled { p.logger.Printf("scylla: %s closing %d connections", p.address, len(conns)) } go closeConns(conns...) } func (p *scyllaConnPicker) closeExcessConns() { if len(p.excessConns) == 0 { if debug.Enabled { p.logger.Printf("scylla: %s no excess connections to close", p.address) } return } conns := p.excessConns p.excessConns = nil if debug.Enabled { p.logger.Printf("scylla: %s closing %d excess connections", p.address, len(conns)) } go closeConns(conns...) } // Closing must be done outside of hostConnPool lock. If holding a lock // a deadlock can occur when closing one of the connections returns error on close. // See scylladb/gocql#53. func closeConns(conns ...*Conn) { for _, conn := range conns { if conn != nil { conn.Close() } } } // NextShard returns the shardID to connect to. // nrShard specifies how many shards the host has. // If nrShards is zero, the caller shouldn't use shard-aware port. func (p *scyllaConnPicker) NextShard() (shardID, nrShards int) { if p.shardAwarePortDisabled { return 0, 0 } disableUntil, _ := p.disableShardAwarePortUntil.Load().(time.Time) if time.Now().Before(disableUntil) { // There is suspicion that the shard-aware-port is not reachable // or misconfigured, fall back to the non-shard-aware port return 0, 0 } // Find the shard without a connection // It's important to start counting from 1 here because we want // to consider the next shard after the previously attempted one for i := 1; i <= p.nrShards; i++ { shardID := (p.lastAttemptedShard + i) % p.nrShards if p.conns == nil || p.conns[shardID] == nil { p.lastAttemptedShard = shardID return shardID, p.nrShards } } // We did not find an unallocated shard // We will dial the non-shard-aware port return 0, 0 } // ShardDialer is like HostDialer but is shard-aware. // If the driver wants to connect to a specific shard, it will call DialShard, // otherwise it will call DialHost. type ShardDialer interface { HostDialer // DialShard establishes a connection to the specified shard ID out of nrShards. // The returned connection must be directly usable for CQL protocol, // specifically DialShard is responsible also for setting up the TLS session if needed. DialShard(ctx context.Context, host *HostInfo, shardID, nrShards int) (*DialedHost, error) } // A dialer which dials a particular shard type scyllaDialer struct { dialer Dialer logger StdLogger tlsConfig *tls.Config cfg *ClusterConfig } const scyllaShardAwarePortFallbackDuration time.Duration = 5 * time.Minute func (sd *scyllaDialer) DialHost(ctx context.Context, host *HostInfo) (*DialedHost, error) { ip := host.ConnectAddress() port := host.Port() if !validIpAddr(ip) { return nil, fmt.Errorf("host missing connect ip address: %v", ip) } else if port == 0 { return nil, fmt.Errorf("host missing port: %v", port) } addr := net.JoinHostPort(ip.String(), strconv.Itoa(port)) translatedInfo := host.getTranslatedConnectionInfo() if translatedInfo != nil { addr = translatedInfo.CQL.ToNetAddr() } conn, err := sd.dialer.DialContext(ctx, "tcp", addr) if err != nil { return nil, err } return WrapTLS(ctx, conn, addr, sd.tlsConfig) } func (sd *scyllaDialer) DialShard(ctx context.Context, host *HostInfo, shardID, nrShards int) (*DialedHost, error) { ip := host.ConnectAddress() port := host.Port() if !validIpAddr(ip) { return nil, fmt.Errorf("host missing connect ip address: %v", ip) } else if port == 0 { return nil, fmt.Errorf("host missing port: %v", port) } iter := newScyllaPortIterator(shardID, nrShards) addr := net.JoinHostPort(ip.String(), strconv.Itoa(port)) shardAwareAddr := "" translatedInfo := host.getTranslatedConnectionInfo() if translatedInfo != nil { addr = translatedInfo.CQL.ToNetAddr() if sd.tlsConfig != nil { if translatedInfo.ShardAwareTLS.IsValid() { shardAwareAddr = translatedInfo.ShardAwareTLS.ToNetAddr() } } else if translatedInfo.ShardAware.IsValid() { shardAwareAddr = translatedInfo.ShardAware.ToNetAddr() } } if debug.Enabled { sd.logger.Printf("scylla: connecting to shard %d", shardID) } conn, err := sd.dialShardAware(ctx, addr, shardAwareAddr, iter) if err != nil { return nil, err } return WrapTLS(ctx, conn, addr, sd.tlsConfig) } func (sd *scyllaDialer) dialShardAware(ctx context.Context, addr, shardAwareAddr string, iter *scyllaPortIterator) (net.Conn, error) { for { port, ok := iter.Next() if !ok { // We exhausted ports to connect from. Try the non-shard-aware port. return sd.dialer.DialContext(ctx, "tcp", addr) } ctxWithPort := context.WithValue(ctx, scyllaSourcePortCtx{}, port) conn, err := sd.dialer.DialContext(ctxWithPort, "tcp", shardAwareAddr) if isLocalAddrInUseErr(err) { // This indicates that the source port is already in use // We can immediately retry with another source port for this shard continue } else if err != nil { conn, err := sd.dialer.DialContext(ctx, "tcp", addr) if err == nil { // We failed to connect to the shard-aware port, but succeeded // in connecting to the non-shard-aware port. This might // indicate that the shard-aware port is just not reachable, // but we may also be unlucky and the node became reachable // just after we tried the first connection. // We can't avoid false positives here, so I'm putting it // behind a debug flag. if debug.Enabled { sd.logger.Printf( "scylla: %s couldn't connect to shard-aware address while the non-shard-aware address %s is available; this might be an issue with ", addr, shardAwareAddr, ) } } return conn, err } return conn, err } } // ErrScyllaSourcePortAlreadyInUse An error value which can returned from // a custom dialer implementation to indicate that the requested source port // to dial from is already in use var ErrScyllaSourcePortAlreadyInUse = errors.New("scylla: source port is already in use") func isLocalAddrInUseErr(err error) bool { return errors.Is(err, syscall.EADDRINUSE) || errors.Is(err, ErrScyllaSourcePortAlreadyInUse) } // ScyllaShardAwareDialer wraps a net.Dialer, but uses a source port specified by gocql when connecting. // // Unlike in the case standard native transport ports, gocql can choose which shard will handle // a new connection by connecting from a specific source port. If you are using your own net.Dialer // in ClusterConfig, you can use ScyllaShardAwareDialer to "upgrade" it so that it connects // from the source port chosen by gocql. // // Please note that ScyllaShardAwareDialer overwrites the LocalAddr field in order to choose // the right source port for connection. type ScyllaShardAwareDialer struct { net.Dialer } func (d *ScyllaShardAwareDialer) DialContext(ctx context.Context, network, addr string) (conn net.Conn, err error) { sourcePort := ScyllaGetSourcePort(ctx) if sourcePort == 0 { return d.Dialer.DialContext(ctx, network, addr) } dialerWithLocalAddr := d.Dialer dialerWithLocalAddr.LocalAddr, err = net.ResolveTCPAddr(network, fmt.Sprintf(":%d", sourcePort)) if err != nil { return nil, err } return dialerWithLocalAddr.DialContext(ctx, network, addr) } type scyllaPortIterator struct { currentPort int shardCount int } const ( scyllaPortBasedBalancingMin = 0x8000 scyllaPortBasedBalancingMax = 0xFFFF ) func newScyllaPortIterator(shardID, shardCount int) *scyllaPortIterator { if shardCount == 0 { panic("shardCount cannot be 0") } // Find the smallest port p such that p >= min and p % shardCount == shardID port := scyllaPortBasedBalancingMin - scyllaShardForSourcePort(scyllaPortBasedBalancingMin, shardCount) + shardID if port < scyllaPortBasedBalancingMin { port += shardCount } return &scyllaPortIterator{ currentPort: port, shardCount: shardCount, } } func (spi *scyllaPortIterator) Next() (uint16, bool) { if spi == nil { return 0, false } p := spi.currentPort if p > scyllaPortBasedBalancingMax { return 0, false } spi.currentPort += spi.shardCount return uint16(p), true } func scyllaShardForSourcePort(sourcePort uint16, shardCount int) int { return int(sourcePort) % shardCount } type scyllaSourcePortCtx struct{} // ScyllaGetSourcePort returns the source port that should be used when connecting to a node. // // Unlike in the case standard native transport ports, gocql can choose which shard will handle // a new connection at the shard-aware port by connecting from a specific source port. Therefore, // if you are using a custom Dialer and your nodes expose shard-aware ports, your dialer should // use the source port specified by gocql. // // If this function returns 0, then your dialer can use any source port. // // If you aren't using a custom dialer, gocql will use a default one which uses appropriate source port. // If you are using net.Dialer, consider wrapping it in a gocql.ScyllaShardAwareDialer. func ScyllaGetSourcePort(ctx context.Context) uint16 { sourcePort, _ := ctx.Value(scyllaSourcePortCtx{}).(uint16) return sourcePort } // Returns a partitioner specific to the table, or "nil" // if the cluster-global partitioner should be used func scyllaGetTablePartitioner(session *Session, keyspaceName, tableName string) (Partitioner, error) { isCdc, err := scyllaIsCdcTable(session, keyspaceName, tableName) if err != nil { return nil, err } if isCdc { return scyllaCDCPartitioner{logger: &defaultLogger{}}, nil } return nil, nil } ================================================ FILE: scylla_cdc.go ================================================ package gocql import ( "encoding/binary" "math" "strings" "github.com/gocql/gocql/internal/debug" ) // cdc partitioner const ( scyllaCDCPartitionerName = "CDCPartitioner" scyllaCDCPartitionerFullName = "com.scylladb.dht.CDCPartitioner" scyllaCDCPartitionKeyLength = 16 scyllaCDCVersionMask = 0x0F scyllaCDCMinSupportedVersion = 1 scyllaCDCMaxSupportedVersion = 1 scyllaCDCMinToken = int64Token(math.MinInt64) scyllaCDCLogTableNameSuffix = "_scylla_cdc_log" scyllaCDCExtensionName = "cdc" ) type scyllaCDCPartitioner struct { logger StdLogger } var _ Partitioner = scyllaCDCPartitioner{logger: &defaultLogger{}} func (p scyllaCDCPartitioner) Name() string { return scyllaCDCPartitionerName } func (p scyllaCDCPartitioner) Hash(partitionKey []byte) Token { if len(partitionKey) < 8 { // The key is too short to extract any sensible token, // so return the min token instead if debug.Enabled { p.logger.Printf("scylla: cdc partition key too short: %d < 8", len(partitionKey)) } return scyllaCDCMinToken } upperQword := binary.BigEndian.Uint64(partitionKey[0:]) if debug.Enabled { // In debug mode, do some more checks if len(partitionKey) != scyllaCDCPartitionKeyLength { // The token has unrecognized format, but the first quadword // should be the token value that we want p.logger.Printf("scylla: wrong size of cdc partition key: %d", len(partitionKey)) } lowerQword := binary.BigEndian.Uint64(partitionKey[8:]) version := lowerQword & scyllaCDCVersionMask if version < scyllaCDCMinSupportedVersion || version > scyllaCDCMaxSupportedVersion { // We don't support this version yet, // the token may be wrong p.logger.Printf( "scylla: unsupported version: %d is not in range [%d, %d]", version, scyllaCDCMinSupportedVersion, scyllaCDCMaxSupportedVersion, ) } } return int64Token(upperQword) } func (p scyllaCDCPartitioner) ParseString(str string) Token { return parseInt64Token(str) } func scyllaIsCdcTable(session *Session, keyspaceName, tableName string) (bool, error) { if !strings.HasSuffix(tableName, scyllaCDCLogTableNameSuffix) { // Not a CDC table, use the default partitioner return false, nil } // Check if the table has the CDC partitioner set. tableMeta, err := session.TableMetadata(keyspaceName, tableName) if err != nil { return false, err } return tableMeta.Options.Partitioner == scyllaCDCPartitionerFullName, nil } ================================================ FILE: scylla_shard_aware_port_common_test.go ================================================ //go:build integration || unit // +build integration unit package gocql import ( "context" "fmt" "net" "strconv" "sync" "testing" "time" ) type makeClusterTestFunc func() *ClusterConfig func testShardAwarePortNoReconnections(t *testing.T, makeCluster makeClusterTestFunc) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() wg := &sync.WaitGroup{} // Initialize 10 sessions in parallel. // If shard-aware port is used and configured properly, we should get // a connection to each shard without any retries. // For each host, there should be N-1 connections to the special port. var errs []error var errLock sync.Mutex pushErr := func(err error) { errLock.Lock() errs = append(errs, err) errLock.Unlock() } // Run 10 sessions in parallel for i := 0; i < 10; i++ { wg.Add(1) go func() { defer wg.Done() // Each session gets a separate configuration, because we need to have // separate connection listeners - we need to differentiate connections // made for each session separately dialer := newLoggingTestDialer() cluster := makeCluster() cluster.PoolConfig.HostSelectionPolicy = TokenAwareHostPolicy(RoundRobinHostPolicy()) cluster.Dialer = dialer useTLS := cluster.SslOpts != nil sess, err := cluster.CreateSession() if err != nil { cancel() return } defer sess.Close() if err := waitUntilPoolsStopFilling(ctx, sess, 10*time.Second); err != nil { cancel() return } hosts := sess.hostSource.getHostsList() for _, host := range hosts { t.Logf("checking host %s:%d hostID: %q", host.ConnectAddress(), host.Port(), host.hostId) hostPool, ok := sess.pool.getPool(host) if !ok { pushErr(fmt.Errorf("host %q hostID not found", host.hostname)) return } shardAwarePort := getShardAwarePort(hostPool, useTLS) if shardAwarePort == 0 { // Shard aware port was not exposed by the host t.Log("the host does not expose a shard-aware port, skipping") continue } // Verify that we have a sharded connPicker shardedPicker, ok := hostPool.connPicker.(*scyllaConnPicker) if !ok { t.Errorf("not a sharded connection") continue } numberOfShards := shardedPicker.nrShards // Verify that there were no duplicate connections to the same shard // Make sure that we didn't connect to the same shard twice // There should be numberOfShards-1 connections to the new port events := dialer.events[host.ConnectAddress().String()] shardAwareConnectionCount := 0 shardsConnected := make(map[int]testConnectionEvent) for _, evt := range events { if evt.destinationPort != shardAwarePort { continue } shardAwareConnectionCount++ shard := scyllaShardForSourcePort(evt.sourcePort, numberOfShards) if oldEvt, hasShard := shardsConnected[shard]; hasShard { t.Errorf("there was more than one connection to the shard aware port from the same shard (shard %d, port %d and %d)", shard, oldEvt.sourcePort, evt.sourcePort) } shardsConnected[shard] = evt } if shardAwareConnectionCount != numberOfShards-1 { t.Errorf("expected %d connections to the shard aware port, but got %d", numberOfShards-1, shardAwareConnectionCount) } } return }() } wg.Wait() for _, err := range errs { t.Error(err.Error()) } } func testShardAwarePortMaliciousNAT(t *testing.T, makeCluster makeClusterTestFunc) { cluster := makeCluster() cluster.PoolConfig.HostSelectionPolicy = TokenAwareHostPolicy(RoundRobinHostPolicy()) cluster.Dialer = &sourcePortOffByOneTestDialer{} sess, err := cluster.CreateSession() if err != nil { t.Fatalf("an error occurred while creating a session: %s", err) } defer sess.Close() // In this situation we are guaranteed that the connection will miss one // shard at this point. The first connection receives a random shard, // then we establish N-1 connections, targeting remaining shards. // Because the malicious port translator shifts the port by one, // one shard will be missed (if the host has more than one shard). // Retry until we establish one connection per shard for { if err := waitUntilPoolsStopFilling(context.Background(), sess, 10*time.Second); err != nil { t.Fatal(err) } if checkIfPoolsAreFull(sess) { break } triggerPoolsRefill(sess) } } func testShardAwarePortUnreachable(t *testing.T, makeCluster makeClusterTestFunc) { cluster := makeCluster() cluster.PoolConfig.HostSelectionPolicy = TokenAwareHostPolicy(RoundRobinHostPolicy()) cluster.Dialer = &allowOnlyNonShardAwarePortDialer{allowedPort: getClusterPort(cluster)} sess, err := cluster.CreateSession() if err != nil { t.Fatalf("an error occurred while creating a session: %s", err) } defer sess.Close() // In this situation, the connecting to the shard-aware port will fail, // but connections to the non-shard-aware port will succeed. This test // checks that we detect that the shard-aware-port is unreachable and // we fall back to the old port. // Retry until we establish one connection per shard for { if err := waitUntilPoolsStopFilling(context.Background(), sess, 10*time.Second); err != nil { t.Fatal(err) } if checkIfPoolsAreFull(sess) { break } triggerPoolsRefill(sess) } } func testShardAwarePortUnusedIfNotEnabled(t *testing.T, makeCluster makeClusterTestFunc) { dialer := newLoggingTestDialer() cluster := makeCluster() cluster.PoolConfig.HostSelectionPolicy = TokenAwareHostPolicy(RoundRobinHostPolicy()) // Explicitly disable the shard aware port cluster.DisableShardAwarePort = true cluster.Dialer = dialer useTLS := cluster.SslOpts != nil sess, err := cluster.CreateSession() if err != nil { t.Fatalf("an error occurred while creating a session: %s", err) } defer sess.Close() if err := waitUntilPoolsStopFilling(context.Background(), sess, 10*time.Second); err != nil { t.Fatal(err) } hosts := sess.hostSource.getHostsList() for _, host := range hosts { t.Logf("checking host %s", host.hostname) hostPool, _ := sess.pool.getPool(host) shardAwarePort := getShardAwarePort(hostPool, useTLS) if shardAwarePort == 0 { // Shard aware port was not exposed by the host t.Log("the host does not expose a shard-aware port, skipping") continue } events, _ := dialer.events[host.ConnectAddress().String()] for _, evt := range events { if evt.destinationPort == shardAwarePort { t.Error("there was an attempt to connect to a shard aware port, but the configuration does not allow that") } } } } func getShardAwarePort(pool *hostConnPool, useTLS bool) uint16 { if useTLS { return pool.Host().ScyllaShardAwarePortTLS() } return pool.Host().ScyllaShardAwarePort() } func triggerPoolsRefill(sess *Session) { hosts := sess.hostSource.getHostsList() for _, host := range hosts { hostPool, _ := sess.pool.getPool(host) go hostPool.fill_debounce() } } func waitUntilPoolsStopFilling(ctx context.Context, sess *Session, timeout time.Duration) error { deadline := time.After(timeout) for !checkIfPoolsStoppedFilling(sess) { select { case <-ctx.Done(): return ctx.Err() case <-deadline: return fmt.Errorf("failed to fill all connection pools in %s", timeout) case <-time.After(250 * time.Millisecond): continue } } return nil } func checkIfPoolsStoppedFilling(sess *Session) bool { hosts := sess.hostSource.getHostsList() for _, host := range hosts { hostPool, _ := sess.pool.getPool(host) hostPool.mu.Lock() isFilling := hostPool.filling hostPool.mu.Unlock() if isFilling { return false } } return true } func checkIfPoolsAreFull(sess *Session) bool { hosts := sess.hostSource.getHostsList() for _, host := range hosts { hostPool, _ := sess.pool.getPool(host) hostPool.mu.Lock() _, remaining := hostPool.connPicker.Size() hostPool.mu.Unlock() if remaining > 0 { return false } } return true } func getClusterPort(cluster *ClusterConfig) uint16 { _, portStr, _ := net.SplitHostPort(cluster.Hosts[0]) port, _ := strconv.Atoi(portStr) if port == 0 { // Assume default if it's not explicitly specified return 9042 } return uint16(port) } type sourcePortOffByOneTestDialer struct { ScyllaShardAwareDialer } func (spobo *sourcePortOffByOneTestDialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) { // Simulate a NAT that always increases the source port by 1 // This should always result in wrong shard being assigned if host // has more than one shard. sourcePort := ScyllaGetSourcePort(ctx) if sourcePort > 0 { sourcePort++ } newCtx := context.WithValue(ctx, scyllaSourcePortCtx{}, sourcePort) return spobo.ScyllaShardAwareDialer.DialContext(newCtx, network, addr) } type allowOnlyNonShardAwarePortDialer struct { net.Dialer allowedPort uint16 } func (aonsa *allowOnlyNonShardAwarePortDialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) { // Simulate a network configuration which allows connections to the // non-shard-aware port, but not to the shard-aware one. _, targetPort, _ := net.SplitHostPort(addr) if targetPort != strconv.Itoa(int(aonsa.allowedPort)) { return nil, fmt.Errorf("allowOnlyNonShardAwarePortDialer: tried to connect to port %s, but only %d is allowed", targetPort, aonsa.allowedPort) } return aonsa.Dialer.DialContext(ctx, network, addr) } type loggingTestDialer struct { ScyllaShardAwareDialer events map[string][]testConnectionEvent mu sync.Mutex } type testConnectionEvent struct { sourcePort, destinationPort uint16 } func newLoggingTestDialer() *loggingTestDialer { return &loggingTestDialer{ events: make(map[string][]testConnectionEvent), } } func (ltd *loggingTestDialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) { sourcePort := ScyllaGetSourcePort(ctx) ipaddr, destinationPortStr, err := net.SplitHostPort(addr) if err != nil { return nil, err } destinationPort, err := strconv.ParseUint(destinationPortStr, 10, 16) if err != nil { return nil, err } conn, err := ltd.ScyllaShardAwareDialer.DialContext(ctx, network, addr) if err == nil { ltd.mu.Lock() defer ltd.mu.Unlock() evt := testConnectionEvent{ sourcePort: sourcePort, destinationPort: uint16(destinationPort), } ltd.events[ipaddr] = append(ltd.events[ipaddr], evt) } return conn, err } ================================================ FILE: scylla_shard_aware_port_integration_test.go ================================================ //go:build integration // +build integration package gocql import "testing" func TestShardAwarePortIntegrationNoReconnections(t *testing.T) { t.Parallel() testShardAwarePortNoReconnections(t, func() *ClusterConfig { c := createCluster() c.Port = 9042 return c }) } func TestShardAwarePortIntegrationMaliciousNAT(t *testing.T) { t.Parallel() testShardAwarePortMaliciousNAT(t, func() *ClusterConfig { c := createCluster() c.Port = 9042 return c }) } func TestShardAwarePortIntegrationUnreachable(t *testing.T) { t.Parallel() testShardAwarePortUnreachable(t, func() *ClusterConfig { c := createCluster() c.Port = 9042 return c }) } func TestShardAwarePortIntegrationUnusedIfNotEnabled(t *testing.T) { t.Parallel() testShardAwarePortUnusedIfNotEnabled(t, func() *ClusterConfig { c := createCluster() c.Port = 9042 return c }) } ================================================ FILE: scylla_shard_aware_port_mocked_test.go ================================================ //go:build unit // +build unit package gocql import ( "context" "math/rand" "net" "strconv" "sync/atomic" "testing" "time" ) const testShardCount = 3 func TestShardAwarePortMockedNoReconnections(t *testing.T) { t.Parallel() testWithAndWithoutTLS(t, testShardAwarePortNoReconnections) } func TestShardAwarePortMockedMaliciousNAT(t *testing.T) { t.Parallel() testWithAndWithoutTLS(t, testShardAwarePortMaliciousNAT) } func TestShardAwarePortMockedUnreachable(t *testing.T) { t.Parallel() testWithAndWithoutTLS(t, testShardAwarePortUnreachable) } func TestShardAwarePortMockedUnusedIfNotEnabled(t *testing.T) { t.Parallel() testWithAndWithoutTLS(t, testShardAwarePortUnusedIfNotEnabled) } func testWithAndWithoutTLS(t *testing.T, test func(t *testing.T, makeCluster makeClusterTestFunc)) { t.Run("without TLS", func(t *testing.T) { makeCluster, stop := startServerWithShardAwarePort(t, false) defer stop() test(t, makeCluster) }) t.Run("with TLS", func(t *testing.T) { makeCluster, stop := startServerWithShardAwarePort(t, true) defer stop() test(t, makeCluster) }) } func startServerWithShardAwarePort(t testing.TB, useTLS bool) (makeCluster func() *ClusterConfig, stop func()) { var shardAwarePort uint32 shardAwarePortKey := "SCYLLA_SHARD_AWARE_PORT" if useTLS { shardAwarePortKey = "SCYLLA_SHARD_AWARE_PORT_SSL" } regularSupportedFactory := func(conn net.Conn) map[string][]string { // Assign a random shard. Although Scylla uses a deterministic algorithm // for assigning shards, the driver doesn't have enough information // to determine which shard will be assigned - therefore, from its // perspective, it's practically random. saPort := int(atomic.LoadUint32(&shardAwarePort)) t.Log("Connecting to the regular port") shardID := rand.Intn(testShardCount) supported := getStandardScyllaExtensions(shardID, testShardCount) supported[shardAwarePortKey] = []string{strconv.Itoa(saPort)} return supported } shardAwareSupportedFactory := func(conn net.Conn) map[string][]string { // Shard ID depends on the source port. saPort := int(atomic.LoadUint32(&shardAwarePort)) t.Log("Connecting to the shard-aware port") port := mustParsePortFromAddr(conn.RemoteAddr().String()) shardID := scyllaShardForSourcePort(port, testShardCount) supported := getStandardScyllaExtensions(shardID, testShardCount) supported[shardAwarePortKey] = []string{strconv.Itoa(saPort)} return supported } makeServer := func(factory testSupportedFactory) *TestServer { if useTLS { return NewSSLTestServerWithSupportedFactory(t, defaultProto, context.Background(), factory) } return NewTestServerWithAddressAndSupportedFactory("127.0.0.1:0", t, defaultProto, context.Background(), factory) } srvRegular := makeServer(regularSupportedFactory) srvShardAware := makeServer(shardAwareSupportedFactory) saPort := mustParsePortFromAddr(srvShardAware.Address) atomic.StoreUint32(&shardAwarePort, uint32(saPort)) t.Logf("regular port address: %s, shard aware port address: %s", srvRegular.Address, srvShardAware.Address) makeCluster = func() *ClusterConfig { var cluster *ClusterConfig if useTLS { cluster = createTestSslCluster(srvRegular.Address, defaultProto, false) // Give a long timeout. For some reason, closing tls connections // result in an i/o timeout error, and this mitigates this problem. cluster.Timeout = 1 * time.Minute } else { cluster = testCluster(defaultProto, srvRegular.Address) } return cluster } stop = func() { srvRegular.Stop() srvShardAware.Stop() } return makeCluster, stop } func mustParsePortFromAddr(addr string) uint16 { _, portStr, err := net.SplitHostPort(addr) if err != nil { panic(err) } port, err := strconv.ParseUint(portStr, 10, 16) if err != nil { panic(err) } return uint16(port) } func getStandardScyllaExtensions(shardID, shardCount int) map[string][]string { return map[string][]string{ "SCYLLA_SHARD": []string{strconv.Itoa(shardID)}, "SCYLLA_NR_SHARDS": []string{strconv.Itoa(shardCount)}, "SCYLLA_PARTITIONER": []string{"org.apache.cassandra.dht.Murmur3Partitioner"}, "SCYLLA_SHARDING_ALGORITHM": []string{"biased-token-round-robin"}, "SCYLLA_SHARDING_IGNORE_MSB": []string{"12"}, } } ================================================ FILE: scylla_test.go ================================================ //go:build unit // +build unit package gocql import ( "context" "fmt" "math" "net" "runtime" "sync" "sync/atomic" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/gocql/gocql/internal/streams" ) func TestScyllaConnPickerPickNilToken(t *testing.T) { t.Parallel() s := scyllaConnPicker{ nrShards: 4, msbIgnore: 12, } t.Run("no conns", func(t *testing.T) { s.conns = []*Conn{{ streams: streams.New(), }} if s.Pick(Token(nil), nil) != s.conns[0] { t.Fatal("expected connection") } }) t.Run("one shard", func(t *testing.T) { s.conns = []*Conn{{ streams: streams.New(), }} if s.Pick(Token(nil), nil) != s.conns[0] { t.Fatal("expected connection") } }) t.Run("multiple shards", func(t *testing.T) { s.conns = []*Conn{nil, { streams: streams.New(), }} if s.Pick(Token(nil), nil) != s.conns[1] { t.Fatal("expected connection") } if s.Pick(Token(nil), nil) != s.conns[1] { t.Fatal("expected connection") } }) t.Run("multiple shards no conns", func(t *testing.T) { s.conns = []*Conn{nil, nil} if s.Pick(Token(nil), nil) != nil { t.Fatal("expected nil") } if s.Pick(Token(nil), nil) != nil { t.Fatal("expected nil") } }) } func hammerConnPicker(t *testing.T, wg *sync.WaitGroup, s *scyllaConnPicker, loops int) { t.Helper() for i := 0; i < loops; i++ { if c := s.Pick(nil, nil); c == nil { t.Error("unexpected nil") } } wg.Done() } func TestScyllaConnPickerHammerPickNilToken(t *testing.T) { t.Parallel() s := scyllaConnPicker{ nrShards: 4, msbIgnore: 12, } s.conns = make([]*Conn, 100) for i := range s.conns { if i%7 == 0 { continue } s.conns[i] = &Conn{ streams: streams.New(), } } n := runtime.GOMAXPROCS(0) loops := 10000 / n var wg sync.WaitGroup wg.Add(n) for i := 0; i < n; i++ { go hammerConnPicker(t, &wg, &s, loops) } wg.Wait() } func TestScyllaConnPickerRemove(t *testing.T) { t.Parallel() s := scyllaConnPicker{ nrShards: 4, msbIgnore: 12, } conn := mockConn(0) s.Put(conn) s.Put(mockConn(1)) if s.nrConns != 2 { t.Error("added 2 connections, expected connection count to be 2") } s.Remove(conn) if s.nrConns != 1 { t.Errorf("removed 1 connection, expected connection count to be 1 but was %d", s.nrConns) } if s.conns[0] != nil { t.Errorf("Expected %v to be removed from it's position", conn) } } func TestScyllaConnPickerShardOf(t *testing.T) { t.Parallel() s := scyllaConnPicker{ nrShards: 4, msbIgnore: 12, } for _, test := range scyllaShardOfTests { if shard := s.shardOf(int64Token(test.token)); shard != test.shard { t.Errorf("wrong scylla shard calculated for token %d, expected %d, got %d", test.token, test.shard, shard) } } } func TestScyllaRandomConnPicker(t *testing.T) { t.Parallel() t.Run("max iterations", func(t *testing.T) { s := &scyllaConnPicker{ nrShards: 4, msbIgnore: 12, pos: math.MaxUint64, conns: []*Conn{nil, mockConn(1)}, } if s.Pick(Token(nil), nil) == nil { t.Fatal("expected connection") } }) t.Run("async access of max iterations", func(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() s := &scyllaConnPicker{ nrShards: 4, msbIgnore: 12, pos: math.MaxUint64, conns: []*Conn{nil, mockConn(1)}, } var wg sync.WaitGroup connCh := make(chan *Conn, 9) for i := 0; i < 3; i++ { wg.Add(1) go func() { defer wg.Done() for i := 0; i < 3; i++ { select { case connCh <- s.Pick(Token(nil), nil): case <-ctx.Done(): } } }() } wg.Wait() close(connCh) if s.pos != 8 { t.Fatalf("expected position to be 8 | actual %d", s.pos) } if len(connCh) != 9 { t.Fatalf("expected 9 connection picks, got %d", len(connCh)) } for conn := range connCh { if conn == nil { t.Fatal("expected connection, got nil") } } }) } func TestScyllaRateLimitingExtParsing(t *testing.T) { t.Parallel() t.Run("init framer without cql extensions", func(t *testing.T) { // mock connection without cql extensions, expected to have the `rateLimitingErrorCode` // field set to 0 (default, signifying no code) conn := mockConn(0) f := newFramerWithExts(conn.compressor, conn.version, conn.cqlProtoExts, conn.logger) if f.rateLimitingErrorCode != 0 { t.Error("expected to have rateLimitingErrorCode set to 0 (no code) after framer init") } }) const mockCode = 42 t.Run("init framer with cql extensions", func(t *testing.T) { // create a mock connection, add `lwt` cql protocol extension to it, // ensure that framer recognizes this extension and adjusts appropriately conn := mockConn(0) conn.cqlProtoExts = []cqlProtocolExtension{ &rateLimitExt{ rateLimitErrorCode: mockCode, }, } framerWithRateLimitExt := newFramerWithExts(conn.compressor, conn.version, conn.cqlProtoExts, conn.logger) if framerWithRateLimitExt.rateLimitingErrorCode != mockCode { t.Error("expected to have rateLimitingErrorCode set to mockCode after framer init") } }) } func TestScyllaLWTExtParsing(t *testing.T) { t.Parallel() t.Run("init framer without cql extensions", func(t *testing.T) { // mock connection without cql extensions, expected not to have // the `flagLWT` field being set in the framer created out of it conn := mockConn(0) f := newFramerWithExts(conn.compressor, conn.version, conn.cqlProtoExts, conn.logger) if f.flagLWT != 0 { t.Error("expected to have LWT flag uninitialized after framer init") } }) t.Run("init framer with cql extensions", func(t *testing.T) { // create a mock connection, add `lwt` cql protocol extension to it, // ensure that framer recognizes this extension and adjusts appropriately conn := mockConn(0) conn.cqlProtoExts = []cqlProtocolExtension{ &lwtAddMetadataMarkExt{ lwtOptMetaBitMask: 1, }, } framerWithLwtExt := newFramerWithExts(conn.compressor, conn.version, conn.cqlProtoExts, conn.logger) if framerWithLwtExt.flagLWT == 0 { t.Error("expected to have LWT flag to be set after framer init") } }) } func TestScyllaPortIterator(t *testing.T) { t.Parallel() for _shardCount := 1; _shardCount <= 64; _shardCount++ { shardCount := _shardCount t.Run(fmt.Sprintf("shard count %d", shardCount), func(t *testing.T) { for shardID := 0; shardID < shardCount; shardID++ { // Count by brute force ports that can be used to connect to requested shard expectedPortCount := 0 for i := scyllaPortBasedBalancingMin; i <= scyllaPortBasedBalancingMax; i++ { if i%shardCount == shardID { expectedPortCount++ } } // Enumerate all ports using the port iterator and assert various things iterator := newScyllaPortIterator(shardID, shardCount) actualPortCount := 0 previousPort := 0 for { portU16, ok := iterator.Next() if !ok { break } port := int(portU16) if port < scyllaPortBasedBalancingMin || port > scyllaPortBasedBalancingMax { t.Errorf("expected port %d generated from iterator to be in range [%d..%d]", port, scyllaPortBasedBalancingMin, scyllaPortBasedBalancingMax) } if port <= previousPort { t.Errorf("expected port %d generated from iterator to be larger than the previous generated port %d", port, previousPort) } actualShardOfPort := scyllaShardForSourcePort(portU16, shardCount) if actualShardOfPort != shardID { t.Errorf("expected port %d returned from iterator to belong to shard %d, but belongs to %d", port, shardID, actualShardOfPort) } previousPort = port actualPortCount++ } if expectedPortCount != actualPortCount { t.Errorf("expected port iterator to generate %d ports, but got %d", expectedPortCount, actualPortCount) } } }) } } func TestScyllaConnPickerHandleShardCountChange(t *testing.T) { tests := []struct { name string initialShards int newShards int initialConns []int // shard IDs of initial connections expectedMigrated int expectedClosed int }{ { name: "shard increase from 4 to 8", initialShards: 4, newShards: 8, initialConns: []int{0, 2, 3}, expectedMigrated: 3, // All initial connections survive expectedClosed: 0, }, { name: "shard decrease from 8 to 4", initialShards: 8, newShards: 4, initialConns: []int{0, 2, 5, 7}, expectedMigrated: 2, // Only shards 0, 2 survive expectedClosed: 2, // Shards 5, 7 get closed }, { name: "no change same count", initialShards: 8, newShards: 8, initialConns: []int{1, 3, 5}, expectedMigrated: 4, // All initial connections survive + new one expectedClosed: 0, }, { name: "massive decrease from 16 to 2", initialShards: 16, newShards: 2, initialConns: []int{0, 1, 5, 8, 12, 15}, expectedMigrated: 2, // Only shards 0, 1 survive expectedClosed: 4, // Shards 5, 8, 12, 15 get closed }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() logger := &testLogger{} picker := &scyllaConnPicker{ logger: logger, disableShardAwarePortUntil: new(atomic.Value), hostId: tUUID(99), address: "192.168.1.1:9042", // Regular port conns: make([]*Conn, tt.initialShards), excessConns: make([]*Conn, 0), nrShards: tt.initialShards, msbIgnore: 12, nrConns: 0, pos: 0, lastAttemptedShard: 0, shardAwarePortDisabled: false, excessConnsLimitRate: 0.1, } picker.disableShardAwarePortUntil.Store(time.Time{}) var connectionsToCheck []*Conn // Add initial connections for _, shardID := range tt.initialConns { conn := mockConnForPicker(shardID, tt.initialShards) err := picker.Put(conn) require.NoError(t, err) if shardID >= tt.newShards { connectionsToCheck = append(connectionsToCheck, conn) } } // Verify initial state assert.Equal(t, tt.initialShards, picker.nrShards) assert.Equal(t, len(tt.initialConns), picker.nrConns) // Execute topology change newConn := mockConnForPicker(0, tt.newShards) err := picker.Put(newConn) require.NoError(t, err) // Allow background goroutine to complete time.Sleep(50 * time.Millisecond) // Verify new topology assert.Equal(t, tt.newShards, picker.nrShards) assert.Equal(t, len(picker.conns), tt.newShards) // Count migrated connections migratedCount := 0 for _, conn := range picker.conns { if conn != nil { migratedCount++ } } assert.Equal(t, tt.expectedMigrated, migratedCount) // Verify connections that should be closed are actually closed closedCount := 0 for _, conn := range connectionsToCheck { if conn.Closed() { closedCount++ } } assert.Equal(t, tt.expectedClosed, closedCount, "Expected %d connections to be closed, but %d were closed", tt.expectedClosed, closedCount) }) } } func mockConn(shard int) *Conn { return &Conn{ streams: streams.New(), scyllaSupported: ScyllaConnectionFeatures{ shard: shard, ScyllaHostFeatures: ScyllaHostFeatures{ nrShards: 4, msbIgnore: 12, partitioner: "org.apache.cassandra.dht.Murmur3Partitioner", shardingAlgorithm: "biased-token-round-robin", }, }, } } func mockConnForPicker(shard, nrShards int) *Conn { ctx, cancel := context.WithCancel(context.Background()) conn1, conn2 := net.Pipe() _ = conn2.Close() return &Conn{ scyllaSupported: ScyllaConnectionFeatures{ shard: shard, ScyllaHostFeatures: ScyllaHostFeatures{ nrShards: nrShards, msbIgnore: 12, }, }, conn: conn1, addr: fmt.Sprintf("192.168.1.%d:9042", shard+1), closed: false, mu: sync.Mutex{}, logger: &testLogger{}, ctx: ctx, cancel: cancel, calls: make(map[int]*callReq), streams: streams.New(), } } ================================================ FILE: scylla_tokens_test.go ================================================ package gocql var scyllaShardOfTests = []struct { token int64 shard int }{ {-9219783007514621794, 3}, {-9218910161940551519, 3}, {-9217148724365525195, 1}, {-9215501124608928187, 2}, {-9212118992469829075, 1}, {-9212030144112870790, 2}, {-9211258281348643746, 2}, {-9210074338140524409, 3}, {-9209452951616763088, 0}, {-9209003334210078074, 0}, {-9208107013070640916, 1}, {-9206869989664156496, 2}, {-9203470406378964547, 1}, {-9202339907670320874, 2}, {-9202275438383790249, 2}, {-9200541087111340967, 0}, {-9198480270279608398, 2}, {-9196697686091266284, 3}, {-9196174198992597486, 0}, {-9194615357326961625, 1}, {-9193113491748958345, 2}, {-9192459871981514371, 3}, {-9190526603941545655, 1}, {-9189326718574265489, 2}, {-9187895885180493903, 3}, {-9186904031908562648, 0}, {-9185241105530461414, 1}, {-9185031262400478274, 2}, {-9184265387969214261, 2}, {-9180945271818496043, 1}, {-9177781200259901224, 0}, {-9175479359685319229, 2}, {-9174998497001176631, 2}, {-9172370230482781505, 1}, {-9168648833848204743, 0}, {-9167865830883491911, 1}, {-9165003895616112063, 3}, {-9163092042366917964, 1}, {-9159429206437382746, 0}, {-9156628358613672159, 3}, {-9145990555828476571, 0}, {-9145984070346097828, 0}, {-9145653916006550014, 1}, {-9145227631306399119, 1}, {-9144242661317651188, 2}, {-9142801888302425882, 3}, {-9141024881712510010, 1}, {-9135654074943507558, 1}, {-9134471177307058759, 2}, {-9133450519921711534, 3}, {-9132179582023424885, 0}, {-9128845389688502876, 3}, {-9126855626742569135, 1}, {-9126468196331425497, 2}, {-9125869882319112283, 2}, {-9122205444002044894, 1}, {-9122124704753907281, 1}, {-9119852417382055127, 3}, {-9108996115070661611, 1}, {-9105962620655078102, 0}, {-9104488397628566176, 1}, {-9102490257548627547, 3}, {-9100044309397920724, 1}, {-9097647687687441987, 3}, {-9096130690687366280, 1}, {-9094167670298826740, 2}, {-9092647747436307833, 0}, {-9090605585241100926, 1}, {-9090460421272311919, 2}, {-9089682013175894585, 2}, {-9086591152376149795, 1}, {-9086156579028515398, 1}, {-9085700796937931307, 2}, {-9084840568084277047, 3}, {-9083314180003896592, 0}, {-9083218787485222603, 0}, {-9079203666276610372, 0}, {-9077619300681531189, 1}, {-9073899858137616676, 0}, {-9072429865350033657, 2}, {-9070732684693488973, 3}, {-9068162850065914690, 1}, {-9066106911671959188, 3}, {-9065794362167854621, 3}, {-9060720433166640362, 0}, {-9059058429610502260, 1}, {-9057041435787997979, 3}, {-9055571171737281638, 1}, {-9053550620084185626, 2}, {-9051961176178065334, 0}, {-9050949256565904929, 1}, {-9044743511154156117, 2}, {-9034146976767032545, 0}, {-9032837933556123687, 1}, {-9030843123924115008, 3}, {-9028318934664121759, 1}, {-9027195046628036333, 2}, {-9025877019451973201, 3}, {-9021845321518921582, 2}, {-9021684946663807180, 3}, {-9021391864262786543, 3}, {-9018654448679225135, 1}, {-9017910576289779427, 2}, {-9011578186521064224, 0}, {-9010196012589019855, 1}, {-9008364600031197426, 2}, {-9007249462924898062, 3}, {-9001909576140939925, 0}, {-9001857208489190644, 0}, {-9001835025464037958, 0}, {-9001169953992264486, 1}, {-9000459115829895779, 1}, {-8997727458472373131, 0}, {-8997672619639292323, 0}, {-8996600295949411295, 1}, {-8995689744426462869, 2}, {-8994008005758461826, 3}, {-8992681528412498651, 0}, {-8991768123515142274, 1}, {-8991467277747639021, 1}, {-8990822821394141367, 2}, {-8989729945845236311, 3}, {-8988845307229810281, 0}, {-8988427316811599555, 0}, {-8987983753017434450, 1}, {-8983115793326873493, 1}, {-8983086844895242739, 1}, {-8981890906573758340, 2}, {-8980395476532088132, 3}, {-8975145032863711407, 0}, {-8974746983465009593, 0}, {-8974164694278947968, 1}, {-8973772633339379555, 1}, {-8972366641844733980, 2}, {-8970803836946395296, 0}, {-8970535559841294174, 0}, {-8968974983101949757, 1}, {-8968892069542755690, 2}, {-8960650631469933104, 1}, {-8959255750778599523, 2}, {-8953396776625136216, 3}, {-8951615565843131215, 1}, {-8951102612976094316, 1}, {-8949963041853689354, 2}, {-8949810590745335399, 2}, {-8949784640726681190, 2}, {-8949334117415806646, 3}, {-8946966673935406601, 1}, {-8946767485005137880, 1}, {-8945963928449701913, 2}, {-8945371869540957686, 2}, {-8945239009942620356, 3}, {-8945116731660105709, 3}, {-8944475579543785027, 3}, {-8943692667418059849, 0}, {-8942901061978944084, 1}, {-8939748931550317608, 3}, {-8938679124732642340, 0}, {-8937971959266143183, 1}, {-8937104296003173554, 2}, {-8934283921670948718, 0}, {-8933054975554443806, 1}, {-8931482356358984226, 3}, {-8928959646490609172, 1}, {-8928387227904079120, 1}, {-8927549807600016124, 2}, {-8926697058461788553, 3}, {-8925747283359504181, 0}, {-8923380316857811053, 2}, {-8922582611698436293, 3}, {-8918171797824175287, 3}, {-8915814954952854897, 1}, {-8912752475837100265, 3}, {-8912540227104816085, 0}, {-8911066216677875757, 1}, {-8910551173422668769, 1}, {-8908802930504231128, 3}, {-8904966686288057759, 2}, {-8904372279862096262, 3}, {-8903824986915019470, 3}, {-8903360949336071968, 0}, {-8898438179411912073, 0}, {-8896914538224349415, 1}, {-8896360492911583677, 2}, {-8895591046743457495, 3}, {-8891175580066418563, 3}, {-8890180610327932722, 3}, {-8888744564811465701, 1}, {-8888564657618562037, 1}, {-8886682084755450990, 3}, {-8882606779991583263, 2}, {-8882062035164834986, 3}, {-8877728006448550298, 2}, {-8876462831121647619, 0}, {-8873585190160663864, 2}, {-8869785478182536414, 2}, {-8868029989929923849, 3}, {-8865831648690043653, 1}, {-8860825946126019428, 2}, {-8859003956954505643, 3}, {-8858001676873636536, 0}, {-8854951543355195101, 3}, {-8849938990466738405, 3}, {-8849704314664442652, 3}, {-8847866554259590809, 1}, {-8846974795410482266, 2}, {-8845685019202984672, 3}, {-8844882533362836650, 0}, {-8844659956697746942, 0}, {-8844257545131977123, 0}, {-8843034454487328123, 1}, {-8842601616960620517, 2}, {-8839039456611521855, 1}, {-8838859987399394830, 1}, {-8833124622276138633, 2}, {-8830032519419637331, 1}, {-8822439709392298791, 0}, {-8821427899115331156, 0}, {-8819951120330048172, 2}, {-8815270273792620404, 2}, {-8813833124794343910, 3}, {-8812664555648833579, 0}, {-8811727670611115180, 1}, {-8808672535964284764, 0}, {-8808601300750787698, 0}, {-8808031375963918595, 0}, {-8807968659296814722, 0}, {-8806675296706545492, 2}, {-8806101794329977984, 2}, {-8801970776860053540, 2}, {-8798628438609891883, 1}, {-8789553083015140754, 1}, {-8789494705227999890, 1}, {-8787034000357658753, 3}, {-8775385252264980252, 1}, {-8772832906275798096, 0}, {-8772495734273833210, 0}, {-8772174643258493699, 0}, {-8771653192088662075, 1}, {-8769354943203848641, 3}, {-8768569886866708171, 3}, {-8766495561833593642, 1}, {-8765537918602003531, 2}, {-8764303545778534735, 3}, {-8761220513767182658, 2}, {-8761164659607525373, 2}, {-8758942732260072898, 0}, {-8756882226765508222, 2}, {-8755213741187638266, 3}, {-8755016822125610320, 3}, {-8753980144030598881, 0}, {-8751047520592856827, 3}, {-8750693121820192812, 3}, {-8749876757270066354, 0}, {-8749082587872467859, 1}, {-8747183699980377729, 2}, {-8746837533061351316, 3}, {-8745902267294206290, 0}, {-8744901735518734872, 0}, {-8744279366062525256, 1}, {-8744242917179719505, 1}, {-8742589525335533309, 3}, {-8738918780723998982, 2}, {-8738640371071776390, 2}, {-8738320438179690851, 2}, {-8737913853561915873, 3}, {-8737654491999137154, 3}, {-8736425500288678266, 0}, {-8735511428551356372, 1}, {-8734226567915098893, 2}, {-8732656541702377155, 3}, {-8730780887984762083, 1}, {-8730032953181465675, 2}, {-8729728248134888651, 2}, {-8729537205320247895, 2}, {-8726631609258350604, 1}, {-8719673675991649792, 3}, {-8715942829879108231, 2}, {-8713817866637997581, 0}, {-8713235141188388676, 1}, {-8710026033658477134, 3}, {-8709455175357331570, 0}, {-8708478959256766071, 1}, {-8705845965690512004, 3}, {-8702764679476038089, 2}, {-8702294914617418886, 2}, {-8698219858997879183, 2}, {-8695842967577853796, 0}, {-8694289930557945258, 1}, {-8693343273833565723, 2}, {-8691799375689065662, 0}, {-8690008299103932739, 1}, {-8686822069518232256, 0}, {-8686553803468375282, 0}, {-8684269390224206829, 2}, {-8681804423163734411, 1}, {-8677003794839760523, 1}, {-8672349742562912316, 1}, {-8669333074300031356, 0}, {-8668870770756856653, 0}, {-8668772057914638282, 0}, {-8661513751550755684, 3}, {-8660407108660952693, 0}, {-8654356242011138044, 1}, {-8653688090610516442, 1}, {-8650756654208074358, 0}, {-8649474919300467994, 1}, {-8643829250806418031, 2}, {-8642446847437341342, 3}, {-8640485967118393069, 1}, {-8638398587362786176, 3}, {-8634498893535145030, 3}, {-8631793409250861568, 1}, {-8630678691436498925, 2}, {-8629806435278869430, 3}, {-8628844787156112881, 0}, {-8628451325709326965, 0}, {-8627445306253582442, 1}, {-8625210573491404264, 3}, {-8624038236276653656, 0}, {-8623365200431438942, 0}, {-8622885554884634024, 1}, {-8622176728713857632, 1}, {-8622087440407858116, 2}, {-8620819812742745793, 3}, {-8619341306075018491, 0}, {-8618367675718400367, 1}, {-8617788353003901138, 1}, {-8616669637118265485, 2}, {-8613485937573063009, 1}, {-8613196177022776082, 1}, {-8609180518710886544, 1}, {-8604283912744699815, 1}, {-8601757538878082634, 0}, {-8599932959975673411, 1}, {-8598683624458050255, 2}, {-8597972294187660525, 3}, {-8590473406867427513, 2}, {-8583864590204372642, 3}, {-8582803546476954912, 0}, {-8582588166754404233, 1}, {-8582515637205675089, 1}, {-8579055303790739373, 0}, {-8577547012485896127, 1}, {-8573937138034522235, 0}, {-8573655228765444718, 1}, {-8573379270252509593, 1}, {-8572086417016232524, 2}, {-8568585774495497786, 1}, {-8568438303436956086, 1}, {-8567484190854003453, 2}, {-8566370499098902907, 3}, {-8565099365536511691, 0}, {-8564295532138797541, 1}, {-8562432275188785766, 3}, {-8561569892018217133, 3}, {-8560590287561398043, 0}, {-8560347848166038849, 0}, {-8559319822564321006, 1}, {-8557058741648472552, 3}, {-8555874984190908568, 0}, {-8554443619930992800, 2}, {-8553004642340115293, 3}, {-8552467776436301709, 3}, {-8551711348753821057, 0}, {-8551649181508365590, 0}, {-8549496230554455523, 2}, {-8549167782466536956, 2}, {-8548665128497669648, 3}, {-8547692886936802752, 0}, {-8545970590581923360, 1}, {-8542036410629753097, 1}, {-8540465467042529887, 2}, {-8539889868690973983, 3}, {-8539791775427497845, 3}, {-8539582955068356044, 3}, {-8539073847134826047, 3}, {-8533287058408907839, 0}, {-8533083406846856996, 1}, {-8532946484795827615, 1}, {-8532203920559643847, 1}, {-8530587345278308289, 3}, {-8524837578608761942, 0}, {-8521118904139218716, 3}, {-8520455435863883396, 0}, {-8519137813822130633, 1}, {-8514806405479319244, 1}, {-8514796186603935400, 1}, {-8514050721846210610, 2}, {-8513853483467452066, 2}, {-8512439189184951872, 3}, {-8509523127630632020, 2}, {-8506436841734745781, 0}, {-8505035946016406028, 2}, {-8504262047033039069, 2}, {-8502506709139732540, 0}, {-8501410389981439153, 1}, {-8500949573468944775, 1}, {-8499332519511648440, 3}, {-8498559063575674910, 3}, {-8497974368011647622, 0}, {-8496648776118834308, 1}, {-8495045114587314715, 2}, {-8490103713184203144, 3}, {-8485461275243533430, 3}, {-8484091542298091675, 0}, {-8483698769767406782, 0}, {-8482007217409229133, 2}, {-8481522505374571945, 2}, {-8480329620741332569, 3}, {-8479540725713627483, 0}, {-8475279295355581427, 0}, {-8473828066928014798, 1}, {-8473751669843379927, 1}, {-8471514019531691804, 3}, {-8470694603663626540, 0}, {-8469899105534108482, 1}, {-8467346370116500732, 3}, {-8465046400200259596, 1}, {-8464728709759645268, 1}, {-8463572192211102153, 2}, {-8459795533268608644, 2}, {-8459785543572838456, 2}, {-8459280090157509505, 2}, {-8458458385762615566, 3}, {-8455325930966454216, 2}, {-8454960839471478619, 2}, {-8449043774552522263, 3}, {-8445506221730907556, 2}, {-8441400010740778171, 2}, {-8440771700544956085, 3}, {-8440688495120230302, 3}, {-8435070196955213200, 0}, {-8434907410219775733, 0}, {-8432371535523876839, 2}, {-8432363831625304734, 2}, {-8431798484932836316, 3}, {-8428774134953824393, 1}, {-8421099371731028715, 0}, {-8419354185222706444, 2}, {-8417120049028743807, 0}, {-8416497296990146903, 0}, {-8415683328128979423, 1}, {-8415127921297226828, 1}, {-8413083173323803051, 3}, {-8412069940792355445, 0}, {-8411382930996905407, 1}, {-8409784240205182299, 2}, {-8408490719920727576, 3}, {-8405009540046335716, 2}, {-8403634009763728108, 0}, {-8402123526929444170, 1}, {-8401598368850656636, 1}, {-8399748517516631169, 3}, {-8398145746429616858, 0}, {-8395999254791712562, 2}, {-8395260530766916784, 3}, {-8394478841990517560, 0}, {-8388413583856715057, 1}, {-8387672882787740834, 2}, {-8387168430284168819, 2}, {-8387094803912313492, 2}, {-8387074512141759261, 2}, {-8384893706871858624, 0}, {-8383985495630390913, 1}, {-8383894456009207223, 1}, {-8382573019622317730, 2}, {-8377619854364117982, 3}, {-8376488801924003757, 0}, {-8373913399574645799, 2}, {-8373419214044244059, 2}, {-8369032425975313013, 2}, {-8368549845846954257, 3}, {-8367858159285207899, 3}, {-8367610095872028000, 0}, {-8367549184173066531, 0}, {-8367120991245542759, 0}, {-8364321713298490471, 2}, {-8361884287202140375, 1}, {-8358860115010827712, 3}, {-8357430617580290143, 1}, {-8353180418110558552, 0}, {-8352763372689441011, 1}, {-8351537537669063019, 2}, {-8351006652984738107, 2}, {-8350024552130708141, 3}, {-8346545786916311365, 2}, {-8345540821347024540, 3}, {-8342112292946974880, 2}, {-8339040728487871104, 1}, {-8339005856507062736, 1}, {-8338854521362905181, 1}, {-8338094180190166070, 2}, {-8336580678769266918, 3}, {-8336299032910419213, 3}, {-8336051024809654132, 0}, {-8334422891159978946, 1}, {-8326290797705283744, 0}, {-8325342960557061012, 1}, {-8323042734129659215, 3}, {-8322671511628216748, 3}, {-8320255622010060895, 2}, {-8319596953646915440, 2}, {-8317456214049413758, 0}, {-8316690175436868029, 1}, {-8316652874708819614, 1}, {-8316316880915081047, 1}, {-8316197264741883055, 1}, {-8315477624831318816, 2}, {-8315169655343619250, 2}, {-8313298616084598611, 0}, {-8312195898846365046, 1}, {-8309808854855876136, 3}, {-8308355491273903660, 0}, {-8307566336394387808, 1}, {-8305759486226031547, 3}, {-8305220119003647557, 3}, {-8304589256831767464, 0}, {-8303614282647025208, 0}, {-8303110787469750596, 1}, {-8301969265617118971, 2}, {-8301431163645948800, 2}, {-8298736404649928953, 1}, {-8296627456093501180, 3}, {-8296449349543571877, 3}, {-8294875504603859921, 0}, {-8294723474216249353, 0}, {-8290965418788732930, 0}, {-8290644590637034734, 0}, {-8289033544838143166, 1}, {-8288998612620410954, 1}, {-8287215240968950425, 3}, {-8283449255035247719, 2}, {-8282375366710057449, 3}, {-8278828103680226284, 2}, {-8278552617328622388, 3}, {-8275995731875641701, 1}, {-8273111408424141355, 0}, {-8273052696125238590, 0}, {-8270766545201551571, 2}, {-8270259233929505281, 2}, {-8266783678487798757, 1}, {-8265748113882696357, 2}, {-8265640029804085770, 2}, {-8264960494676468461, 3}, {-8264586923374603149, 3}, {-8262958171224427210, 1}, {-8256103142222743001, 3}, {-8255461772141702609, 3}, {-8247061437346180334, 3}, {-8243938201190679083, 1}, {-8239704930383216851, 1}, {-8237233854167132587, 3}, {-8234515644965821638, 2}, {-8233213657486196085, 3}, {-8231943422673463045, 0}, {-8223303916461868061, 0}, {-8222108409741375147, 1}, {-8221546357842636370, 1}, {-8221528922157970495, 1}, {-8220999818478773849, 2}, {-8220598297367534642, 2}, {-8220195410468954025, 2}, {-8218810134276172915, 0}, {-8218424901606405428, 0}, {-8215586791933333747, 3}, {-8211197630521909247, 2}, {-8209093341571548698, 0}, {-8207766192162305292, 2}, {-8205119319121463289, 0}, {-8203698733632199750, 1}, {-8203628927328766437, 1}, {-8201925780717530320, 3}, {-8200136513863707575, 0}, {-8194683581531803428, 1}, {-8193982101216933743, 2}, {-8193667489132494997, 2}, {-8193572047985582129, 2}, {-8192911466999677781, 3}, {-8192185008131646249, 3}, {-8189951415467077951, 1}, {-8187130076508936924, 0}, {-8186116272708412686, 1}, {-8183440858276076728, 3}, {-8180952284103274279, 1}, {-8179380470230486498, 3}, {-8173807306347547330, 0}, {-8172171197867775285, 1}, {-8169425467099585043, 0}, {-8169203381757641145, 0}, {-8167003379306200572, 2}, {-8166168691558738770, 2}, {-8166007710971503662, 3}, {-8165476724478035500, 3}, {-8163128774244699711, 1}, {-8161078427841652309, 3}, {-8160686924256522793, 3}, {-8160514661736768058, 0}, {-8158364274569363963, 1}, {-8157976470872947127, 2}, {-8157736753134290423, 2}, {-8157491123616025879, 2}, {-8150186264403995132, 1}, {-8149758591835234130, 1}, {-8149619838857454522, 1}, {-8149479037624232307, 1}, {-8149128021065051280, 2}, {-8145324027774362378, 1}, {-8144644007658534241, 2}, {-8143188420527983546, 3}, {-8143018037088803741, 3}, {-8142247589362881634, 0}, {-8138337625745378051, 3}, {-8136807372149818542, 1}, {-8129864702238010737, 3}, {-8126755579614410814, 1}, {-8126246140550618846, 2}, {-8124207955818292845, 0}, {-8120287786937763361, 3}, {-8118807516152413619, 1}, {-8114065141929769781, 1}, {-8113731542609340577, 1}, {-8110437254484803155, 0}, {-8106964496292752676, 3}, {-8104651571181294011, 1}, {-8100584857928968960, 1}, {-8099009219653496977, 2}, {-8098588230467470125, 3}, {-8095667600947641688, 1}, {-8095646378979037890, 1}, {-8092954289066200413, 0}, {-8092390129237554639, 0}, {-8088876253718557598, 3}, {-8088315559490257673, 0}, {-8087702294524216978, 0}, {-8083355931455517436, 0}, {-8082489000653926150, 1}, {-8082402818656404057, 1}, {-8081170769789281576, 2}, {-8078553735343112288, 0}, {-8077785987856351995, 1}, {-8070500606084551350, 3}, {-8070472479818134131, 3}, {-8066238707937535916, 3}, {-8065037361862400737, 0}, {-8064375597626074579, 1}, {-8061820775890735860, 3}, {-8059336090042517652, 1}, {-8059141974754427524, 2}, {-8053099187153857152, 3}, {-8046902469358790329, 0}, {-8040120546131891062, 2}, {-8039724830444517412, 3}, {-8034405218402590186, 0}, {-8032449261037029458, 1}, {-8032110605872969129, 2}, {-8029781628709824769, 0}, {-8027126256806052396, 2}, {-8026293319067632665, 3}, {-8025484020483209259, 3}, {-8024352892742505070, 0}, {-8024334150394593621, 0}, {-8023957809019968725, 1}, {-8023821807599215005, 1}, {-8021355074387850438, 3}, {-8018626820857744053, 2}, {-8017799262965255787, 2}, {-8013498413404766176, 2}, {-8012823788799540889, 3}, {-8010064929144775679, 1}, {-8006140495059837586, 1}, {-8004190488731998497, 2}, {-8002689860111014626, 0}, {-8002248753638091732, 0}, {-8000716191578504492, 1}, {-7998073064202605212, 0}, {-7997327973990558837, 0}, {-7995870780743388171, 2}, {-7992635613610837962, 1}, {-7988744458505091489, 0}, {-7987324815915822515, 1}, {-7985447422447494243, 3}, {-7985345534775578535, 3}, {-7981832206554147257, 2}, {-7980324694072893774, 0}, {-7976693287690656996, 3}, {-7975542422893311089, 0}, {-7973687030757825828, 1}, {-7973325363128557171, 2}, {-7970852764926200285, 0}, {-7970779282316506155, 0}, {-7968119469071268318, 2}, {-7965989310863216311, 0}, {-7962496574859087105, 3}, {-7961483364245672667, 0}, {-7960817023828069909, 1}, {-7960506093750069850, 1}, {-7955570704870779330, 2}, {-7954374529903310952, 3}, {-7953885461175197051, 3}, {-7953460103581126297, 3}, {-7953216249345286292, 0}, {-7952267944930043572, 0}, {-7951693035429606922, 1}, {-7950834041268878582, 2}, {-7949668023231451945, 3}, {-7947703822977921021, 1}, {-7946337409887484401, 2}, {-7945733347521513181, 2}, {-7944717781978222404, 3}, {-7942115815306396558, 1}, {-7941983985633065542, 2}, {-7941843043654188132, 2}, {-7941343652311220382, 2}, {-7940325285073302361, 3}, {-7934926690315343476, 0}, {-7931301959496434245, 3}, {-7930364097488798670, 0}, {-7927881575703674630, 2}, {-7921856201819807125, 3}, {-7917286439997527769, 0}, {-7914934565301762430, 2}, {-7914573765007474392, 2}, {-7912899376876339458, 3}, {-7910233769622991782, 2}, {-7907820214829005224, 0}, {-7906132536562408211, 1}, {-7904941402001275627, 3}, {-7904609972982593784, 3}, {-7902099888536578567, 1}, {-7897873526219194099, 1}, {-7895495464357695402, 3}, {-7892643771871154539, 1}, {-7891931494989106148, 2}, {-7885804659777714470, 3}, {-7884919004001330791, 0}, {-7884227075765356984, 1}, {-7882930431715808577, 2}, {-7882360420749882932, 3}, {-7879697982714656294, 1}, {-7878453878110052646, 2}, {-7877784811230391590, 3}, {-7876701405943677217, 0}, {-7876380466207432883, 0}, {-7875027439717362859, 1}, {-7874288201479223924, 2}, {-7872978173701415611, 3}, {-7871455228270757474, 0}, {-7869873280293211507, 2}, {-7866644670530561229, 1}, {-7865772788087043991, 1}, {-7862753807120225300, 0}, {-7861682998465459389, 1}, {-7860156530441649360, 2}, {-7854826274888546347, 3}, {-7853115693683699219, 1}, {-7851825953188822446, 2}, {-7850384381874396299, 3}, {-7848053055769255628, 1}, {-7846180831178163940, 3}, {-7844132380570303554, 1}, {-7839383618088726663, 1}, {-7838450008418096385, 2}, {-7835340979145260523, 0}, {-7834879838205002274, 1}, {-7834721550147569919, 1}, {-7834159489789374325, 1}, {-7828174358691711687, 3}, {-7827111423911915825, 0}, {-7825160405744827929, 1}, {-7823568714666188974, 3}, {-7823058064208708539, 3}, {-7820971560922921379, 1}, {-7818149409220416537, 0}, {-7817760746492805061, 0}, {-7816876533723557700, 1}, {-7814774301286679390, 3}, {-7813939127787407977, 3}, {-7813280774364608175, 0}, {-7811921678218430528, 1}, {-7809380002083508101, 3}, {-7808618676106873172, 0}, {-7807966406443809495, 1}, {-7807205876735161744, 1}, {-7797259105003095572, 2}, {-7796260449738116705, 3}, {-7794080179707686963, 1}, {-7793932296539560961, 1}, {-7793848964995052033, 1}, {-7793594124935415606, 1}, {-7793551395791736688, 1}, {-7792490438861861870, 2}, {-7785423911667768423, 1}, {-7784021490804660866, 2}, {-7782249671568911276, 3}, {-7781671380889357378, 0}, {-7777371381864307889, 0}, {-7774595997679591041, 2}, {-7774050549619834739, 3}, {-7773872054281516820, 3}, {-7772185972351577978, 0}, {-7771665432510293098, 1}, {-7771631245946568521, 1}, {-7769194870688400314, 3}, {-7765928522741201423, 2}, {-7765598300688973433, 2}, {-7763635279921807562, 0}, {-7763144956411445346, 0}, {-7762201638516290911, 1}, {-7761785350382611535, 2}, {-7755387254545997473, 3}, {-7753034197013421892, 1}, {-7751727826668026344, 3}, {-7749964060672122974, 0}, {-7748177469919360596, 2}, {-7747654907169657295, 2}, {-7746399002542767188, 3}, {-7746011137284640072, 0}, {-7745643539098322464, 0}, {-7740095797463983663, 1}, {-7739841299645566948, 1}, {-7738547231463112062, 2}, {-7738348936092451899, 2}, {-7737579143497774513, 3}, {-7737197715356175867, 3}, {-7733099578905213766, 3}, {-7731801278750838612, 0}, {-7731116229990556253, 1}, {-7730537662479118831, 1}, {-7730072839208012435, 2}, {-7728952079476078551, 3}, {-7728408242308346087, 3}, {-7724697852490240089, 3}, {-7721183435990539102, 2}, {-7716331903207141841, 2}, {-7716105577982902248, 2}, {-7713832375844466161, 0}, {-7713295027695448756, 1}, {-7711940686791312023, 2}, {-7710538587176049956, 3}, {-7709938987330016866, 0}, {-7706049385623308933, 3}, {-7704050807940704589, 1}, {-7703879123810870944, 1}, {-7702255845777537611, 3}, {-7698466250930323173, 2}, {-7697127437072468925, 3}, {-7694845365599072732, 1}, {-7693280720373436622, 2}, {-7692064139413784352, 0}, {-7691066508329632009, 0}, {-7690089177007586387, 1}, {-7685515592646238795, 1}, {-7684868564941164525, 2}, {-7681549608396735037, 1}, {-7681289924393628930, 1}, {-7680942099333869506, 1}, {-7680562744198021819, 2}, {-7680484778832324747, 2}, {-7680139717364205342, 2}, {-7675511291550547936, 2}, {-7674684868966459365, 3}, {-7672355118140828776, 1}, {-7671980573437355806, 1}, {-7670292711882707405, 3}, {-7669383304241768918, 0}, {-7668634370684207969, 0}, {-7668427521231152733, 1}, {-7667462791902095893, 1}, {-7665995510776444737, 3}, {-7663721130251120816, 1}, {-7663407716123317373, 1}, {-7660690089505698137, 3}, {-7658609580293159254, 1}, {-7658273149305138922, 2}, {-7657968495827113939, 2}, {-7656743483920160316, 3}, {-7655086853913074549, 0}, {-7652275185585428739, 3}, {-7651663563074173674, 3}, {-7649572935555404193, 1}, {-7647463282213790976, 3}, {-7647311758400010833, 3}, {-7646657388757320970, 0}, {-7643726882669486253, 3}, {-7643269654907408171, 3}, {-7642573395645746580, 0}, {-7641624701402297523, 0}, {-7633534238732450383, 0}, {-7632810725834693743, 0}, {-7632727583406990655, 0}, {-7629886053918178666, 3}, {-7622408165253721598, 1}, {-7620359729475626044, 3}, {-7619544826747441500, 0}, {-7618005168644775859, 1}, {-7611569210046022125, 3}, {-7610085201496075116, 0}, {-7607227581626135569, 3}, {-7604891437199856147, 1}, {-7604237573224217213, 2}, {-7602474169368385043, 3}, {-7599588765998697843, 2}, {-7598670484928833357, 3}, {-7596312723512963087, 1}, {-7596301585837983948, 1}, {-7591901425454689020, 1}, {-7591265201391449981, 1}, {-7590741430834099423, 2}, {-7589981075046979219, 2}, {-7589082418510972320, 3}, {-7588297545645205845, 0}, {-7587779253892574044, 0}, {-7586266067489064939, 2}, {-7578991577855542511, 0}, {-7566915388914894643, 3}, {-7566520002942106585, 3}, {-7566101983133902182, 3}, {-7560757067526793099, 0}, {-7560471121158384855, 0}, {-7560470657637083585, 0}, {-7556116364839694042, 0}, {-7552641539528901402, 3}, {-7552544479599556095, 3}, {-7551731016763091421, 0}, {-7548497617522355640, 3}, {-7547859112955371680, 0}, {-7545569852061893506, 2}, {-7543420314744007932, 0}, {-7535171381064191416, 3}, {-7534673974804393795, 3}, {-7533569887213953281, 0}, {-7533360556034376736, 1}, {-7532241961069512317, 2}, {-7526696533756584508, 2}, {-7521825462681864943, 3}, {-7521593636298229120, 3}, {-7518312055483287728, 2}, {-7517858198600330972, 2}, {-7515979944547978395, 0}, {-7512507461475127024, 3}, {-7508954644057070829, 2}, {-7507849086826712203, 3}, {-7506489082667012668, 0}, {-7503949509002093288, 3}, {-7501855559511602182, 1}, {-7500356053507891509, 2}, {-7499356407379847183, 3}, {-7497081829842644178, 1}, {-7497013943899383700, 1}, {-7496990568775955103, 1}, {-7495223944998901987, 2}, {-7494550171770361109, 3}, {-7494064536477347636, 3}, {-7492868655702746123, 0}, {-7489002208588113683, 0}, {-7488439262166615771, 0}, {-7487740895096503232, 1}, {-7484047186343380074, 0}, {-7481917754636737579, 2}, {-7479234415595162360, 1}, {-7477376951330018236, 2}, {-7476336351020487694, 3}, {-7475996308745057900, 3}, {-7475248318862206229, 0}, {-7469381848148914946, 1}, {-7466962550721270949, 0}, {-7462418328782223524, 0}, {-7460634311665536834, 1}, {-7457605305917013309, 0}, {-7456970692117565630, 0}, {-7451095759082818745, 2}, {-7450172384100755149, 2}, {-7450086782004343641, 2}, {-7449927587636755434, 3}, {-7445219205161511012, 3}, {-7444462906014988720, 3}, {-7438299805042817268, 1}, {-7436814199680356289, 2}, {-7436255053152730801, 3}, {-7435787148500924165, 3}, {-7435174776550970408, 0}, {-7434557089201418806, 0}, {-7432982383438286781, 2}, {-7432906420609666755, 2}, {-7432844809800011307, 2}, {-7429020473927981688, 1}, {-7427768596567275769, 2}, {-7424267790237246093, 1}, {-7422741547464078852, 3}, {-7422723616938787317, 3}, {-7421008072013168983, 0}, {-7418529895755143096, 3}, {-7416826662577041073, 0}, {-7413741776763884829, 3}, {-7413335510583257715, 3}, {-7408828329958861081, 3}, {-7403601833953004855, 0}, {-7400760265995484294, 2}, {-7398057061005167831, 1}, {-7397642702856021659, 1}, {-7394602946214729100, 0}, {-7394522806892590377, 0}, {-7391905752806225901, 2}, {-7386573594494892035, 3}, {-7385372136438356481, 0}, {-7384857237623907443, 0}, {-7384319973339250282, 1}, {-7384221104548414778, 1}, {-7383355554840233492, 2}, {-7381877812575104441, 3}, {-7380933923067355735, 0}, {-7377024704699264999, 3}, {-7376718558837285694, 0}, {-7376657206155865939, 0}, {-7371657027925604611, 0}, {-7371170921961388748, 1}, {-7367556751295685508, 0}, {-7366599053879878519, 1}, {-7365533437689267431, 2}, {-7357769122981123216, 0}, {-7356524743152382329, 2}, {-7351086221196239200, 2}, {-7350174473442059218, 3}, {-7347139605035414656, 2}, {-7346556748293810686, 2}, {-7339950496735599076, 0}, {-7339326132986362114, 1}, {-7337693661483376532, 2}, {-7335882684517088008, 0}, {-7334146427657464301, 1}, {-7334075305089381704, 2}, {-7331263471527853572, 0}, {-7328516542055458577, 2}, {-7325077059887644351, 2}, {-7324232570291438735, 2}, {-7321556248582414990, 1}, {-7316302795767765118, 1}, {-7308050018745371185, 1}, {-7306032579581698770, 2}, {-7305257421776272638, 3}, {-7304557467583338379, 0}, {-7303294128599744041, 1}, {-7299462945423595155, 0}, {-7298403559371907041, 1}, {-7297647431181106659, 2}, {-7296976238423384452, 2}, {-7290440884884584716, 0}, {-7286310379290086957, 0}, {-7283845012868637984, 2}, {-7283748467267468512, 2}, {-7282933452920384904, 3}, {-7282882213175962113, 3}, {-7282726466488193668, 3}, {-7282649474331948995, 3}, {-7282216763919916004, 0}, {-7278744586710677386, 3}, {-7277905979007774889, 3}, {-7273849588486140939, 3}, {-7273708428338148277, 3}, {-7271599211692026140, 1}, {-7271278502222449881, 1}, {-7265630799292543245, 2}, {-7262214302212097483, 1}, {-7258924430515878578, 0}, {-7258341144218849815, 1}, {-7255035402635558476, 0}, {-7253311941672531862, 1}, {-7253110720377960232, 1}, {-7250050917922664961, 0}, {-7248647522678551327, 1}, {-7246717854580392576, 3}, {-7243051044161949858, 2}, {-7242905632840755339, 3}, {-7238758885705931359, 2}, {-7237292616476807856, 3}, {-7231501938654758539, 1}, {-7231486162736521500, 1}, {-7231462416148937458, 1}, {-7229454270309182650, 2}, {-7228551434945991524, 3}, {-7227234249068800594, 0}, {-7225669950936650344, 2}, {-7223418401567346313, 0}, {-7222808170590301362, 0}, {-7221004787933409463, 2}, {-7214749678489349850, 0}, {-7209393644421858129, 0}, {-7207691134757919293, 2}, {-7205597440305934506, 0}, {-7204227410191801032, 1}, {-7200565058834996949, 0}, {-7199273504671936358, 1}, {-7197281336986766950, 3}, {-7196877669425387864, 3}, {-7194950831844046343, 1}, {-7192886698799613238, 3}, {-7190591846886263164, 1}, {-7190413781197847668, 1}, {-7186777650291192945, 0}, {-7186162404463873368, 1}, {-7184657777827709219, 2}, {-7183761432055077457, 3}, {-7183284297892615766, 3}, {-7182896287045052491, 0}, {-7179981051467129472, 2}, {-7175305387402304924, 3}, {-7174507628514245047, 3}, {-7173895258267958047, 0}, {-7173444954313401290, 0}, {-7173326451538569253, 0}, {-7172947036856277462, 1}, {-7170799601780680947, 3}, {-7170787011754503264, 3}, {-7170723543925850895, 3}, {-7167739093618756438, 1}, {-7164546404973077237, 0}, {-7164451266049387909, 0}, {-7164374129055247405, 0}, {-7162572829968807800, 2}, {-7161393507582785731, 3}, {-7161179619534233588, 3}, {-7160316710125094997, 0}, {-7158151898532794741, 2}, {-7156678131449587982, 3}, {-7155956544587615449, 0}, {-7155444042079914075, 0}, {-7155433816363925663, 0}, {-7154355310132502764, 1}, {-7152785470957936838, 3}, {-7150789376019568667, 0}, {-7149507981894962189, 1}, {-7149392681350978944, 2}, {-7147366867943006415, 3}, {-7147240747177985569, 3}, {-7139848333144823921, 2}, {-7139608418394846766, 2}, {-7139131737791243843, 3}, {-7138124335193217774, 0}, {-7134221825764623244, 3}, {-7133731041475885991, 3}, {-7130160008508673468, 3}, {-7129086363622898385, 0}, {-7126632274838341040, 2}, {-7126629832257075332, 2}, {-7125697875181621568, 3}, {-7121563629031344122, 2}, {-7117615918298289600, 2}, {-7116928599625160236, 2}, {-7116522127744902326, 3}, {-7115663873485689946, 0}, {-7115023936172587141, 0}, {-7113178507723511270, 2}, {-7112861987496530169, 2}, {-7109992380766067596, 1}, {-7108809445447329207, 2}, {-7108754721650855371, 2}, {-7105735950686544948, 0}, {-7104032785860948829, 2}, {-7103704764580401125, 2}, {-7100369672270449867, 1}, {-7099384693957328672, 2}, {-7097252315782952082, 0}, {-7094568751480404166, 2}, {-7090988674857736112, 1}, {-7090618429146118706, 2}, {-7090055669493233434, 2}, {-7087977425336368518, 0}, {-7084788152046588315, 3}, {-7081837350751310482, 2}, {-7080816226208123532, 2}, {-7073774124269649069, 1}, {-7067570781685192808, 2}, {-7067092685997808537, 3}, {-7066682616614494386, 3}, {-7065828702461009970, 0}, {-7064527941369366415, 1}, {-7062264674312153974, 3}, {-7061196050477031074, 0}, {-7058856521768192874, 2}, {-7058170064942405879, 3}, {-7057343502674040662, 3}, {-7056080956168825000, 0}, {-7054217101155765915, 2}, {-7053026116236305029, 3}, {-7050388470879780610, 1}, {-7050068659621904364, 2}, {-7047788252254184504, 0}, {-7044566070593902579, 3}, {-7041520375533868531, 1}, {-7039565572552413074, 3}, {-7038515241722310587, 0}, {-7038046786757607254, 0}, {-7037255905809320951, 1}, {-7036560852421995331, 2}, {-7034673341431048482, 3}, {-7034193572155587960, 0}, {-7032941450655817785, 1}, {-7026989704396571067, 2}, {-7022837130142310158, 2}, {-7022750172422450180, 2}, {-7021521067927726436, 3}, {-7021286551888271850, 3}, {-7020804438073087736, 0}, {-7019096248039430202, 1}, {-7015177723741709715, 1}, {-7012752879079057654, 3}, {-7010186207550039178, 1}, {-7009747483944313205, 2}, {-7009020201051453396, 2}, {-7007132800397069053, 0}, {-7007001094496596446, 0}, {-7006497400750396348, 0}, {-6998767601890521924, 3}, {-6993306866832654932, 0}, {-6991444975733544978, 2}, {-6991186466584406387, 2}, {-6990962638370173482, 2}, {-6987291144830204080, 2}, {-6985591466354193759, 3}, {-6983976584358990546, 0}, {-6983531924416226728, 1}, {-6981877696337775221, 2}, {-6978908133159843540, 1}, {-6977444310291547672, 2}, {-6977028095309674060, 3}, {-6975584108393929539, 0}, {-6972644089185790661, 3}, {-6972259927345838412, 3}, {-6972004782936715847, 3}, {-6970867686287908840, 0}, {-6969870711992353933, 1}, {-6968783043651178551, 2}, {-6967299681242205982, 3}, {-6966384669914971543, 0}, {-6964755158194156885, 2}, {-6961720774803234500, 0}, {-6961641437890217240, 0}, {-6961517782218093253, 0}, {-6961469917897685172, 0}, {-6960725118479664210, 1}, {-6956419355754849549, 1}, {-6953989651934172472, 3}, {-6951274487698281227, 2}, {-6950171709337752837, 3}, {-6950000658174796331, 3}, {-6949158367877261371, 3}, {-6947746032680390427, 1}, {-6946725152830415331, 2}, {-6945242062240517469, 3}, {-6943441574797612512, 0}, {-6941856327972159009, 2}, {-6941377050327390181, 2}, {-6939946814247903714, 0}, {-6934565593884852335, 0}, {-6933204182963180839, 2}, {-6931050389490557231, 3}, {-6930812536426342199, 0}, {-6924310740580351990, 1}, {-6923227897201298409, 2}, {-6921985847822447357, 0}, {-6921860225947264735, 0}, {-6919744354322491337, 2}, {-6917373244500615974, 0}, {-6915858189103129671, 1}, {-6915052870012368694, 2}, {-6913400606665765821, 3}, {-6907503500910832172, 0}, {-6904878688066422647, 3}, {-6902849012335252068, 1}, {-6902288212182879230, 1}, {-6900170369213001226, 3}, {-6899467874873806359, 0}, {-6888743429760184621, 1}, {-6884866039735341988, 1}, {-6882856472575557905, 2}, {-6880879564552318441, 0}, {-6875791902404425353, 1}, {-6870575731095334169, 1}, {-6868407865188205743, 3}, {-6868265883684808629, 3}, {-6867377613317140587, 0}, {-6866182931887709300, 1}, {-6865513466846010928, 2}, {-6865401455876770805, 2}, {-6865057393455238633, 2}, {-6864700673968546779, 2}, {-6864207506670221508, 3}, {-6863277907951517867, 0}, {-6862062847142697450, 1}, {-6860906693413172948, 2}, {-6859497996029273613, 3}, {-6858064002933077311, 0}, {-6857050327266745189, 1}, {-6849039686762461107, 0}, {-6844907218643915246, 0}, {-6844394904837766950, 0}, {-6838743321761321902, 1}, {-6838585598965727961, 2}, {-6838048013292878356, 2}, {-6834046751483542391, 2}, {-6833680150854797160, 2}, {-6832087776190079215, 3}, {-6830716738502255278, 1}, {-6830328417467790861, 1}, {-6830012044549900414, 1}, {-6828207144802685998, 3}, {-6828087243069919038, 3}, {-6828064420747915164, 3}, {-6827238943304168747, 0}, {-6827151486528237204, 0}, {-6826073336896416334, 1}, {-6825752175434553702, 1}, {-6821157867965945495, 1}, {-6820761116525759498, 1}, {-6819229804648378826, 3}, {-6818312533517750143, 0}, {-6815126457389527682, 2}, {-6812645139602253043, 1}, {-6812497847266549486, 1}, {-6811320387455758249, 2}, {-6809996512675770261, 3}, {-6808562785420373011, 0}, {-6808225072569239527, 1}, {-6807305782631681189, 1}, {-6806459857822381122, 2}, {-6806418841020236219, 2}, {-6805081141332636843, 3}, {-6803741024943841129, 1}, {-6799383075546240611, 0}, {-6796480424201323230, 3}, {-6786455746626068843, 0}, {-6786126735777108974, 0}, {-6786090945480774262, 0}, {-6785265254524789424, 1}, {-6781943141017326846, 0}, {-6781352357061165548, 0}, {-6775919642850837836, 1}, {-6773984304220719101, 3}, {-6771914178185133715, 1}, {-6771330334984705183, 1}, {-6768452040300922908, 0}, {-6767274134170451117, 1}, {-6764669514921254403, 3}, {-6762098567347597396, 2}, {-6758373873301264852, 1}, {-6758060122668637663, 1}, {-6757220109891788205, 2}, {-6756228706178354511, 3}, {-6755090411825565343, 0}, {-6753150292571846119, 1}, {-6752511061798576539, 2}, {-6751886343274326098, 3}, {-6751007667347572737, 3}, {-6748851544696594100, 1}, {-6748718347421941309, 1}, {-6744087546846332053, 2}, {-6743377419958495680, 2}, {-6742673255926970001, 3}, {-6742643286169076485, 3}, {-6741096085435582261, 0}, {-6739735813616739415, 1}, {-6739527784091321069, 2}, {-6737868873389138832, 3}, {-6737704631000508705, 3}, {-6734446102347509321, 2}, {-6734335707818795683, 2}, {-6731981768517727835, 0}, {-6731197932316706765, 1}, {-6729829327957380214, 2}, {-6724517494527357612, 3}, {-6721306548855749173, 2}, {-6718320174832526973, 0}, {-6718309834221153142, 0}, {-6716342147434951176, 2}, {-6714393889281358615, 0}, {-6713325845803953102, 1}, {-6710648244041542975, 3}, {-6707360887093597324, 2}, {-6704490925642341814, 1}, {-6703766776590934004, 1}, {-6700613299245164135, 0}, {-6698069856676001280, 2}, {-6696552701277188729, 0}, {-6695909936695340826, 0}, {-6695264219163230084, 1}, {-6694029941279898028, 2}, {-6693465998643829650, 3}, {-6690548925577575225, 1}, {-6690149474014233837, 1}, {-6689073478480157408, 2}, {-6687985977460349245, 3}, {-6685999510281546149, 1}, {-6685874464742504110, 1}, {-6678726624917026287, 0}, {-6676803490694526992, 1}, {-6671541703464584373, 2}, {-6668770708523915103, 0}, {-6667726463429914635, 1}, {-6665762395429790059, 3}, {-6664224759161908177, 0}, {-6662649181191020617, 2}, {-6662519435036539932, 2}, {-6661682318922512712, 3}, {-6656644431104706414, 3}, {-6656210220711588090, 0}, {-6650743839450723177, 0}, {-6648464291737180836, 2}, {-6647877811951456276, 3}, {-6645513639367983993, 1}, {-6641941821865538954, 0}, {-6641938732358325937, 0}, {-6640867343256435757, 1}, {-6635782576346717906, 2}, {-6634810562207235046, 3}, {-6634245382486847694, 3}, {-6634233744230884953, 3}, {-6624655203701066328, 0}, {-6623716524238618439, 0}, {-6622123801617431633, 2}, {-6620807532144591504, 3}, {-6618419636726524936, 1}, {-6614493161702618060, 1}, {-6611221479997965440, 0}, {-6610604726748851579, 0}, {-6610326174492154726, 0}, {-6601689533908283254, 0}, {-6599683001267326539, 2}, {-6596613318960784443, 1}, {-6595968431576836144, 1}, {-6595807175416576179, 1}, {-6591911974989665251, 1}, {-6590549034643371619, 2}, {-6584895884231782271, 3}, {-6582663666016741344, 1}, {-6579872493153739339, 3}, {-6579673860271064709, 0}, {-6576945804156556718, 2}, {-6575161425452188343, 0}, {-6573514003557299939, 1}, {-6573472058693474801, 1}, {-6573178458348024664, 1}, {-6564822788685245206, 1}, {-6560246201045120730, 1}, {-6559572378420388655, 1}, {-6559548584970578201, 1}, {-6558767320798555083, 2}, {-6557989010547115623, 3}, {-6557553469111505721, 3}, {-6556215174673457652, 0}, {-6555593195312602850, 1}, {-6553868239610059926, 2}, {-6551529453795565863, 1}, {-6550010798354129967, 2}, {-6543908798129291680, 3}, {-6539803717113268273, 3}, {-6537740417101318899, 1}, {-6537193185950883220, 1}, {-6532437887837570130, 2}, {-6532284323463464748, 2}, {-6530339250394354597, 3}, {-6529552848909839327, 0}, {-6527890982526054651, 2}, {-6514496966741365937, 1}, {-6513949888608640963, 2}, {-6513006183815677580, 3}, {-6512299129690871716, 3}, {-6510706840231257734, 1}, {-6509878219692902761, 2}, {-6506434651450759056, 1}, {-6505588609690363566, 1}, {-6495722111972982560, 2}, {-6492973044843929692, 1}, {-6489301408111198524, 0}, {-6488868472483888979, 0}, {-6487574502802186627, 1}, {-6487019461031460914, 2}, {-6486642147506089756, 2}, {-6483255432186999194, 1}, {-6479855438827011179, 0}, {-6477150953757928682, 3}, {-6474143845585541711, 1}, {-6473151545612167794, 2}, {-6469780010792248766, 1}, {-6469688380321372178, 1}, {-6468576221078796776, 2}, {-6467669790966208698, 3}, {-6466853857690627436, 0}, {-6464013001673729165, 2}, {-6463308317603382013, 3}, {-6462822105872595979, 3}, {-6462305212495918847, 0}, {-6460283860747384252, 2}, {-6458610451591925600, 3}, {-6458172755152091211, 3}, {-6457560571002518887, 0}, {-6455374513303479280, 2}, {-6441583908814349281, 2}, {-6438797140142098624, 1}, {-6438191977637515788, 1}, {-6436637831158602014, 3}, {-6435534578399239442, 0}, {-6433925332139169421, 1}, {-6427522423272258632, 3}, {-6425710773899196642, 0}, {-6424525607544936108, 1}, {-6421691077905628627, 0}, {-6414185287718883405, 3}, {-6413919981551186809, 3}, {-6410270650915812607, 2}, {-6409319759830000755, 3}, {-6407976732258260134, 0}, {-6407726134414296387, 0}, {-6404095862905492112, 0}, {-6403787025100544374, 0}, {-6399269499806112567, 0}, {-6399167395579320661, 0}, {-6396938554982307105, 2}, {-6395403482056156891, 3}, {-6393565848182836569, 1}, {-6390882098664662563, 3}, {-6390713399501304652, 3}, {-6390101771116126458, 0}, {-6389705280449443210, 0}, {-6388641200704259072, 1}, {-6386497522653060497, 3}, {-6384433584579720933, 1}, {-6383767549989322911, 2}, {-6383695550516410472, 2}, {-6382595738687190995, 3}, {-6380896045986351480, 0}, {-6379283844457683693, 2}, {-6379233215573147775, 2}, {-6378283198058326751, 2}, {-6377883899103070823, 3}, {-6373771883741652598, 2}, {-6370989835511065229, 1}, {-6370638315408038455, 1}, {-6370465553823215475, 1}, {-6369253187726445226, 2}, {-6367979327009290100, 0}, {-6364189278105479874, 3}, {-6364104244996347062, 3}, {-6363170425827513544, 0}, {-6361030363968743328, 2}, {-6360516836927124196, 2}, {-6359197031449257286, 3}, {-6357329054072592106, 1}, {-6355698737721326690, 3}, {-6353642844296568655, 0}, {-6353531582000570842, 0}, {-6352545141053791594, 1}, {-6351390344091978544, 2}, {-6350339354909484178, 3}, {-6349488165822347131, 0}, {-6347872654346857262, 1}, {-6345411045377788997, 0}, {-6343660744343987405, 1}, {-6341372715775164483, 3}, {-6341035230576411873, 0}, {-6339356166360898552, 1}, {-6338344571697806676, 2}, {-6336890949883822487, 3}, {-6334992048030130908, 1}, {-6333004421750264353, 3}, {-6332255304185599836, 3}, {-6332140489315477382, 3}, {-6329256495565683968, 2}, {-6327686587849709293, 3}, {-6326705343439634894, 0}, {-6325935326519630349, 1}, {-6325449458020786074, 1}, {-6321777179853981135, 1}, {-6319878259995297030, 2}, {-6319691025550655159, 2}, {-6318912020475470463, 3}, {-6315191691319460556, 2}, {-6313608889880960650, 0}, {-6313453937726583872, 0}, {-6311331489376513851, 2}, {-6310510034542565920, 3}, {-6310404506171627205, 3}, {-6309062842064382514, 0}, {-6303271995453261706, 1}, {-6302600461933577047, 2}, {-6300981843966988086, 3}, {-6300022183960029724, 0}, {-6297865868408307913, 2}, {-6297622299168358335, 2}, {-6293637609629269943, 2}, {-6288078627657537049, 3}, {-6285802677032078429, 1}, {-6285540904821833991, 1}, {-6282528885081137808, 3}, {-6279454455201726952, 2}, {-6279262802875204128, 2}, {-6274296627173458305, 3}, {-6270118237034163192, 3}, {-6269179109184062310, 3}, {-6268834376601993570, 0}, {-6262149255782260224, 2}, {-6256540974046911482, 3}, {-6254764454434098499, 0}, {-6253127462655508581, 2}, {-6251306665038730498, 3}, {-6250910687019869619, 0}, {-6249621277049346546, 1}, {-6247739019258957913, 2}, {-6244343187543884992, 1}, {-6243308664502318579, 2}, {-6241975194137073476, 0}, {-6241965000484191778, 0}, {-6239394481677494936, 2}, {-6238473051891572305, 3}, {-6238275162470059171, 3}, {-6233575572051838656, 3}, {-6233469821617458215, 3}, {-6232954253297445887, 0}, {-6231669275785178623, 1}, {-6230933852448506461, 1}, {-6228759665193379801, 3}, {-6226623937508448776, 1}, {-6223336308353154382, 0}, {-6222461407729743106, 1}, {-6221707394449098291, 2}, {-6215382281023038255, 3}, {-6215070955868281266, 3}, {-6210171661706761351, 0}, {-6206596060561340488, 3}, {-6206560478041763945, 3}, {-6204180010887807428, 1}, {-6201590192064311674, 3}, {-6198164392899410886, 2}, {-6197327109545488740, 3}, {-6197304492206684586, 3}, {-6195568298487616494, 1}, {-6195293972815193047, 1}, {-6195056070214948518, 1}, {-6192791429212434030, 3}, {-6186761957109079921, 1}, {-6183843028537268270, 3}, {-6176518773136480216, 2}, {-6175479418043578545, 3}, {-6175475562242912924, 3}, {-6174413546799114308, 0}, {-6171357350974313784, 2}, {-6170965414410863148, 3}, {-6167954887735159285, 1}, {-6164991567962109851, 0}, {-6164979487814790739, 0}, {-6161771262210994536, 3}, {-6161496085196157736, 3}, {-6160889756828249303, 0}, {-6160685397509095714, 0}, {-6160643328094543049, 0}, {-6159195509202095473, 1}, {-6158165270327779039, 2}, {-6155669438294406169, 0}, {-6155060509751376575, 1}, {-6153877848675576817, 2}, {-6150839721267825568, 0}, {-6150106051366801621, 1}, {-6149554400743443952, 2}, {-6146165778212168607, 1}, {-6144592814422786180, 2}, {-6143343459808537923, 3}, {-6141964760556938144, 0}, {-6139557678947072980, 2}, {-6139217231456176166, 3}, {-6137886572258149084, 0}, {-6137147525405124488, 1}, {-6136104566669857742, 2}, {-6134116505493838215, 3}, {-6134041888089849335, 3}, {-6129444909104237756, 3}, {-6128028614455371050, 1}, {-6123600302638150943, 1}, {-6122709240341326439, 1}, {-6120817785555186756, 3}, {-6116637049332014288, 3}, {-6116198226289167461, 3}, {-6114956508545185121, 0}, {-6114763798028913394, 0}, {-6114454093866253722, 1}, {-6112175535451687045, 3}, {-6108679509749399381, 2}, {-6107332951254750790, 3}, {-6102909007684646798, 3}, {-6102335724670652872, 0}, {-6097950572081657206, 3}, {-6095679246504270354, 1}, {-6095440000555220397, 2}, {-6093726008280628528, 3}, {-6093424013863286745, 3}, {-6092369347969426688, 0}, {-6091441118021768450, 1}, {-6090848490068329202, 2}, {-6090542148318706660, 2}, {-6090094990406064871, 2}, {-6087717619147488443, 1}, {-6084059528307391595, 0}, {-6078109322464198937, 1}, {-6077621399284344570, 1}, {-6076795525182041517, 2}, {-6073138584397374586, 1}, {-6070555857643945479, 0}, {-6070466010001683347, 0}, {-6067791711758164636, 2}, {-6067211875992808044, 3}, {-6066299331195078942, 0}, {-6064577008390968780, 1}, {-6060376249554002166, 1}, {-6059769807045895755, 1}, {-6052302564438415117, 0}, {-6052172502907770525, 0}, {-6050776280744008102, 1}, {-6050367673603093691, 2}, {-6049460540333895800, 2}, {-6047757054387388809, 0}, {-6046422559751190956, 1}, {-6035748971603842799, 3}, {-6032263610660198426, 2}, {-6026530539461404790, 3}, {-6025377825780538247, 0}, {-6021559468462908647, 3}, {-6021095565774817083, 0}, {-6019447253941324155, 1}, {-6018575894188502206, 2}, {-6017622394028991004, 3}, {-6016903506663549638, 3}, {-6015339382035474874, 1}, {-6007394627291377405, 0}, {-6004522141235558329, 2}, {-6004205340176528865, 3}, {-6001500310255415235, 1}, {-6001477337263005838, 1}, {-6000964336112028681, 2}, {-6000803121987176268, 2}, {-5997240089258847870, 1}, {-5995824515505548093, 2}, {-5994910184412586510, 3}, {-5993591923551292682, 0}, {-5991727320260368035, 2}, {-5987139798144393854, 2}, {-5983753909578336571, 1}, {-5981763345755523663, 3}, {-5981732692970168162, 3}, {-5980859052850917182, 3}, {-5975754728999844233, 0}, {-5975202785036719524, 0}, {-5971968271713170716, 3}, {-5968242418118831247, 3}, {-5967158257899347127, 0}, {-5964244406161037070, 2}, {-5962070818752329190, 0}, {-5961683655490074031, 0}, {-5961292286393492860, 1}, {-5959905247456295278, 2}, {-5955576833281232025, 2}, {-5954718536480195669, 3}, {-5953595230854016883, 0}, {-5948722947920353055, 0}, {-5948590176018945102, 0}, {-5947726679925042183, 1}, {-5946049414066205564, 2}, {-5945589939151508007, 3}, {-5945286909460386592, 3}, {-5942561737736782132, 1}, {-5938715201559427227, 1}, {-5937961136312120320, 2}, {-5937664473184663856, 2}, {-5934863386777334045, 0}, {-5933746884292946225, 1}, {-5928999763670681271, 1}, {-5927238655873720297, 3}, {-5914001391538883327, 3}, {-5910108228262783873, 2}, {-5910070035238383301, 2}, {-5905613608056516201, 2}, {-5905204170605625904, 3}, {-5901732494563660346, 2}, {-5901697403238152621, 2}, {-5900649047715306855, 3}, {-5900221707795305519, 3}, {-5899623831633158157, 0}, {-5899245390688017471, 0}, {-5895293550285184718, 3}, {-5890793062525243333, 3}, {-5888535120336897741, 1}, {-5887091014153440542, 3}, {-5887077264733057906, 3}, {-5887059288424105279, 3}, {-5884957082729718630, 1}, {-5884885966623076788, 1}, {-5883543343336310577, 2}, {-5882649532375577578, 3}, {-5880958654863311066, 0}, {-5880886073335081746, 0}, {-5877697706312135521, 3}, {-5877072052452534503, 0}, {-5869317614783131409, 2}, {-5868057940995810756, 0}, {-5859954025123610707, 3}, {-5859807342507156009, 3}, {-5858780058499612615, 0}, {-5858052149125301197, 1}, {-5851612021392286828, 2}, {-5850325694538373424, 3}, {-5848785670364660740, 1}, {-5846121880585994243, 3}, {-5843186203125985279, 2}, {-5841338347125806970, 3}, {-5840664452801973116, 0}, {-5840088897429328297, 0}, {-5839710988038317101, 1}, {-5839619008701717869, 1}, {-5839018459562026115, 1}, {-5838977031672241708, 1}, {-5837923235960690720, 2}, {-5837850648565674641, 2}, {-5832105622417378933, 0}, {-5829322055872919791, 2}, {-5829161992951060435, 2}, {-5828716266408076202, 3}, {-5824518420828278330, 2}, {-5820881797831749827, 2}, {-5816822613542206529, 1}, {-5814705123892063743, 3}, {-5813275254012145731, 0}, {-5809821811632482967, 3}, {-5808797853830028762, 0}, {-5808392680720087885, 1}, {-5808117208878705901, 1}, {-5806941399783369191, 2}, {-5806828944967857068, 2}, {-5806662241152748420, 2}, {-5806310465280737457, 2}, {-5805485177440891929, 3}, {-5804203232077319375, 0}, {-5803805048179761437, 1}, {-5802312341917665909, 2}, {-5800236586540978139, 0}, {-5800076104539027983, 0}, {-5799184096267164481, 1}, {-5797848800657749201, 2}, {-5794654896467148854, 1}, {-5794063584458818684, 1}, {-5792850457182940850, 2}, {-5787199053170677427, 3}, {-5786543547059476633, 0}, {-5786295362185140183, 0}, {-5780497518834877549, 1}, {-5780063746529401023, 2}, {-5780058343428484579, 2}, {-5779646526432301479, 2}, {-5779546203690935175, 2}, {-5778349572657904941, 3}, {-5777344207402541420, 0}, {-5776209475474932732, 1}, {-5774416178082571764, 3}, {-5774315939910588213, 3}, {-5773004096694998527, 0}, {-5770594945753361230, 2}, {-5769764604273889726, 3}, {-5768468573610928610, 0}, {-5766505170344286613, 2}, {-5762970578609068064, 1}, {-5762614110878601368, 1}, {-5752917436558829739, 2}, {-5751533182213976090, 3}, {-5743987862639610532, 2}, {-5742837527832852499, 3}, {-5742728304324450446, 3}, {-5742255313089622305, 3}, {-5740729277847591049, 1}, {-5740222729565681701, 1}, {-5738288317901621193, 3}, {-5737287393675878901, 0}, {-5736240877935235106, 1}, {-5733084185873698453, 3}, {-5728728612870908779, 3}, {-5727619925034594113, 0}, {-5722079581828740718, 1}, {-5716308923666156515, 2}, {-5714724113611357071, 0}, {-5714615541660346081, 0}, {-5713760406579185550, 1}, {-5707492524814606446, 2}, {-5700031596634870440, 1}, {-5698448041167822394, 2}, {-5697682397915502201, 3}, {-5694890177208537614, 1}, {-5694856321071229986, 1}, {-5692566283031219515, 3}, {-5691036158511379954, 1}, {-5689631257399651273, 2}, {-5689558007939473990, 2}, {-5689182779320343522, 2}, {-5686658793116455398, 1}, {-5684349833650478597, 3}, {-5683671591248112646, 3}, {-5680850554012438089, 2}, {-5677376187118919562, 1}, {-5675243393592861519, 3}, {-5674365969210543707, 0}, {-5669553431493780222, 0}, {-5663725347997841118, 1}, {-5663512103798184096, 1}, {-5663309836005992930, 1}, {-5660048603298790214, 0}, {-5657101388407893123, 3}, {-5656680936784549584, 3}, {-5656263270511225191, 0}, {-5653353707532285591, 2}, {-5649438177728197194, 2}, {-5647434114998721183, 0}, {-5646770445409016928, 0}, {-5646767989091546476, 0}, {-5643255548055204267, 3}, {-5641600146342662464, 1}, {-5637701053242426906, 0}, {-5635786868678388544, 2}, {-5633097462745768955, 0}, {-5631160285944104406, 2}, {-5629662934957765368, 3}, {-5626397083165812896, 2}, {-5621258638282690649, 3}, {-5621127974436018505, 3}, {-5620452694640365554, 0}, {-5619810903102968134, 0}, {-5617960707478384152, 2}, {-5617324249957241321, 2}, {-5616563630546386704, 3}, {-5614695161768747634, 1}, {-5614343715206873111, 1}, {-5613571559372950390, 2}, {-5612144261475393456, 3}, {-5611745035151004312, 3}, {-5611009341948329515, 0}, {-5609561769425814737, 1}, {-5609361431272501848, 1}, {-5607382463416843021, 3}, {-5606542653449815364, 0}, {-5605042492256938836, 1}, {-5604614461401967622, 2}, {-5604444713787379404, 2}, {-5602764933133368940, 3}, {-5599340665484058919, 2}, {-5599294684613758921, 2}, {-5596462766476707763, 1}, {-5596438330801042302, 1}, {-5595126004979014460, 2}, {-5594701303999037494, 2}, {-5591638296503961268, 1}, {-5587199406160075695, 1}, {-5585723929151322045, 2}, {-5580736176425647338, 3}, {-5579598886738537741, 0}, {-5579444472052314707, 0}, {-5578556051772988908, 1}, {-5576620690357831727, 2}, {-5575769257558458467, 3}, {-5570741777297255304, 0}, {-5569514042049213472, 1}, {-5568500797983381038, 2}, {-5567751603197764136, 2}, {-5567359688058368077, 3}, {-5564794563757944818, 1}, {-5562580791154953715, 3}, {-5560452780724481908, 1}, {-5558355547799087007, 3}, {-5557448687536128796, 3}, {-5557276036715867181, 0}, {-5556910763995764145, 0}, {-5554566470569962024, 2}, {-5553479587129446960, 3}, {-5552955413790145431, 3}, {-5550065975063008592, 2}, {-5548705048683266731, 3}, {-5543468891435970734, 0}, {-5542636836385482086, 1}, {-5540362191832701619, 3}, {-5539527087083434287, 3}, {-5530942149214946570, 3}, {-5527744795508802908, 2}, {-5524796414288371854, 0}, {-5524541366996154603, 1}, {-5523919793741503564, 1}, {-5522456330938970917, 3}, {-5521943171148311155, 3}, {-5521536460591708952, 3}, {-5517288244340479241, 3}, {-5515851662681028422, 0}, {-5515831076577235427, 0}, {-5515633070474136197, 1}, {-5515199962948010682, 1}, {-5514870345574072232, 1}, {-5514613066503085021, 2}, {-5514215298502191579, 2}, {-5512440634747795171, 3}, {-5512003267608309385, 0}, {-5507076511698703305, 0}, {-5504358124972858655, 3}, {-5503377443106243352, 0}, {-5502319138122552501, 0}, {-5502191975923970390, 1}, {-5500544832648779103, 2}, {-5499292636277080474, 3}, {-5499080112007462990, 3}, {-5496000756057725416, 2}, {-5494726634517981713, 3}, {-5493333829325368753, 0}, {-5492073147545914215, 2}, {-5491841562115827740, 2}, {-5489199369077314999, 0}, {-5488934768323399579, 0}, {-5487952731275160550, 1}, {-5487136893580856547, 2}, {-5485345106935442275, 0}, {-5481007267789029779, 3}, {-5478490562174259867, 2}, {-5476576912801499208, 3}, {-5472250321127637490, 3}, {-5471332534680759777, 0}, {-5468111483789349336, 3}, {-5466349205228466317, 0}, {-5466013031666420801, 1}, {-5463785630593359906, 3}, {-5463242731657101181, 3}, {-5461515911566278676, 1}, {-5459527425136614317, 2}, {-5454575132172821077, 3}, {-5450483394940942029, 2}, {-5449248427097354491, 0}, {-5448445717926354533, 0}, {-5448183344898469783, 1}, {-5447646263958951996, 1}, {-5442524195361505386, 2}, {-5440678581814775902, 3}, {-5440309246548971917, 0}, {-5438099174503930974, 1}, {-5437873074248408236, 2}, {-5436824012974022621, 3}, {-5436611961162705839, 3}, {-5435792376054095623, 0}, {-5433613781658773038, 1}, {-5432462295380878414, 3}, {-5432114204577588111, 3}, {-5431072992975085575, 0}, {-5430140834124040603, 1}, {-5422607874532281563, 3}, {-5419856609020354342, 2}, {-5419053686406675431, 2}, {-5418122542875603493, 3}, {-5418041515056817603, 3}, {-5416497630528714230, 1}, {-5416034870762205398, 1}, {-5411283789767690529, 1}, {-5409977454231509655, 2}, {-5407619944238414627, 1}, {-5405128988855145566, 3}, {-5399425154680423251, 0}, {-5398251006076562168, 1}, {-5396646468148662242, 2}, {-5393418496119004816, 1}, {-5390963412000624836, 3}, {-5390601925913933087, 0}, {-5386601830652639078, 3}, {-5384411677445067841, 1}, {-5383352037660172997, 2}, {-5380215922526749815, 1}, {-5374689224512951539, 2}, {-5374107232570356222, 2}, {-5372439262726269948, 0}, {-5372411674999096397, 0}, {-5371327678932016869, 1}, {-5368625300074697211, 3}, {-5366691332889381881, 1}, {-5366439598704547871, 1}, {-5364292145142915311, 3}, {-5362071638590124707, 1}, {-5360426712748302184, 2}, {-5359232005084796593, 0}, {-5356051159561591235, 2}, {-5355835024518793238, 3}, {-5354880558566612764, 3}, {-5354406834793543347, 0}, {-5351441552593394734, 2}, {-5350982773747000918, 3}, {-5344853047422093147, 0}, {-5343978277108675528, 1}, {-5342731969020123767, 2}, {-5340578321255665712, 0}, {-5339278877302920460, 1}, {-5337756457703186169, 3}, {-5332747985549737410, 3}, {-5331925210594118629, 0}, {-5328631463982503305, 3}, {-5327869734693528484, 3}, {-5327839656105672510, 3}, {-5326415971987506540, 1}, {-5325720175558380174, 1}, {-5323187287929944734, 0}, {-5322900966699956089, 0}, {-5322300808657613268, 0}, {-5320595427506558879, 2}, {-5318521916100024569, 0}, {-5317433666105507102, 1}, {-5317126802133407007, 1}, {-5307573677060359199, 1}, {-5306922994580998318, 2}, {-5306794349527260446, 2}, {-5305127458695560497, 0}, {-5297708183304154525, 2}, {-5297282118865418393, 3}, {-5297245636533346789, 3}, {-5294848948258846675, 1}, {-5293377031823166388, 2}, {-5291797599243872284, 3}, {-5291760462792277868, 3}, {-5288619989468578322, 2}, {-5284459505981439747, 2}, {-5284354102624275531, 2}, {-5282708820311100933, 0}, {-5278604717646940931, 3}, {-5277069649499464211, 1}, {-5276664321970943111, 1}, {-5274386457344274680, 3}, {-5273808349476238502, 3}, {-5273075611705304211, 0}, {-5271390040461561671, 2}, {-5270963193282499097, 2}, {-5270726720487222345, 2}, {-5267239218325224507, 1}, {-5265118169892000174, 3}, {-5265016110310601865, 3}, {-5264041159625350106, 0}, {-5263244051079102379, 1}, {-5262762141804440042, 1}, {-5262683775438557520, 1}, {-5258307907599402484, 1}, {-5257646681173119148, 2}, {-5256988356335551296, 2}, {-5255670804996085146, 0}, {-5254783394915826106, 0}, {-5254424419428673227, 1}, {-5251885543289955947, 3}, {-5248263410612557441, 2}, {-5245928255340910715, 0}, {-5241084954485676321, 0}, {-5239249045238660587, 2}, {-5238855867206833270, 2}, {-5237988022502100812, 3}, {-5236537867664629478, 1}, {-5236444219007459153, 1}, {-5233406080267388751, 3}, {-5232412598627684909, 0}, {-5231816320156232231, 1}, {-5221707034393984796, 2}, {-5220986182173293448, 2}, {-5220165873326677824, 3}, {-5215223138473580380, 3}, {-5212751681284358775, 2}, {-5212305281791657985, 2}, {-5211614086919348430, 3}, {-5211307721706029108, 3}, {-5209975805866958972, 0}, {-5209199170859139265, 1}, {-5206164747817021090, 3}, {-5204441667730518496, 1}, {-5204235901132667876, 1}, {-5203491954606281743, 2}, {-5202127912086710904, 3}, {-5200816742750491295, 0}, {-5199699222754213082, 1}, {-5198857509880348751, 2}, {-5197236321445898576, 3}, {-5196223118779185603, 0}, {-5196121090566806152, 0}, {-5195791591533267993, 1}, {-5194688130367392229, 2}, {-5193709875431218533, 3}, {-5193357943657938637, 3}, {-5192903605668416798, 3}, {-5191917709250527934, 0}, {-5184757118606011039, 3}, {-5184530317961488766, 3}, {-5183884636334254102, 3}, {-5183617010069245198, 0}, {-5180404946730959781, 2}, {-5179487393384558857, 3}, {-5176504716103879365, 2}, {-5174875226115557661, 3}, {-5173912931405489553, 0}, {-5171717320508064790, 2}, {-5171629777865883349, 2}, {-5171555043511855039, 2}, {-5170616653714730441, 3}, {-5169934740335330344, 0}, {-5167540477009665325, 2}, {-5161686639220067415, 3}, {-5161049479861756704, 0}, {-5157642182853009198, 3}, {-5156937362304975751, 3}, {-5156275646299893794, 0}, {-5155481074623917332, 1}, {-5149400603777938286, 2}, {-5148434599804172606, 3}, {-5146328674912038004, 1}, {-5142223214106967298, 0}, {-5140570578771181976, 2}, {-5139282656038280568, 3}, {-5137338804828268449, 1}, {-5136436034318817205, 1}, {-5135947475024812391, 2}, {-5125648645737328630, 3}, {-5123088821064099475, 1}, {-5121725746378458737, 2}, {-5118586581467447316, 1}, {-5114662081166532752, 1}, {-5109454754791282233, 1}, {-5105732514881254310, 1}, {-5105558910145551546, 1}, {-5103408411524981692, 3}, {-5100878244403619134, 1}, {-5100852449300312276, 1}, {-5100510705547514636, 1}, {-5095502385501718017, 2}, {-5095307809472325888, 2}, {-5094395489932226499, 3}, {-5094264456747936197, 3}, {-5094033370584215789, 3}, {-5091736688906945885, 1}, {-5088434469575865230, 0}, {-5085542453411885095, 3}, {-5084604466143069318, 3}, {-5083655156822667384, 0}, {-5079161466599766061, 0}, {-5077437972755116566, 2}, {-5074682300026209687, 0}, {-5073614622058833252, 1}, {-5073402585731890632, 1}, {-5073397737337874058, 1}, {-5070088429523050068, 0}, {-5068861855813154374, 1}, {-5068526843484069453, 2}, {-5066589709579414792, 3}, {-5060428630355414206, 1}, {-5060080781628701567, 1}, {-5060013472184425604, 1}, {-5055272552173607211, 2}, {-5054876211334751308, 2}, {-5053050689410555081, 3}, {-5051377301356016614, 1}, {-5043530886164321276, 0}, {-5043153527465775731, 0}, {-5041292650184788926, 2}, {-5037219543135336145, 2}, {-5033656962231141962, 1}, {-5032527916254818814, 2}, {-5032036757213758275, 2}, {-5030558856659131498, 3}, {-5029920112752322857, 0}, {-5020700312773736984, 0}, {-5016742905311933110, 0}, {-5015228222576151951, 1}, {-5014363174803746991, 2}, {-5013811634212997403, 2}, {-5010703442906645260, 1}, {-5009849043302403088, 2}, {-5009254016366942159, 2}, {-5007799251163635980, 0}, {-5007124619047970829, 0}, {-5006682216699136691, 1}, {-5004874386266052595, 2}, {-5000710896363955743, 2}, {-5000152308381522707, 2}, {-4998938400332066905, 0}, {-4996823334878306523, 1}, {-4996654215152623560, 2}, {-4991197589422879648, 2}, {-4988658350545544010, 1}, {-4983332920183961152, 1}, {-4982546690130916642, 2}, {-4982516479631140380, 2}, {-4981146103902529891, 3}, {-4980841468236328214, 0}, {-4976592492689997952, 3}, {-4976224437144196852, 0}, {-4975939219453018600, 0}, {-4972898029745948170, 3}, {-4971424511400114418, 0}, {-4969740291049685087, 1}, {-4969463949686710398, 2}, {-4969104315186509326, 2}, {-4966534857378424546, 0}, {-4966119218957518266, 1}, {-4964534536534549854, 2}, {-4963785652766867162, 3}, {-4962050875221106380, 0}, {-4959516320610841244, 3}, {-4956542545304898984, 1}, {-4955487920482744449, 2}, {-4954927891204721414, 3}, {-4953924679062632002, 0}, {-4951866248219015491, 1}, {-4951139231763874703, 2}, {-4949088353158080713, 0}, {-4946518029393824446, 2}, {-4944839588321918816, 0}, {-4941981821602892394, 2}, {-4932940324188180605, 2}, {-4926535798589625391, 0}, {-4926241335036264832, 0}, {-4925796587969193277, 1}, {-4924055798812744989, 2}, {-4919035218919208669, 3}, {-4918052235596060978, 3}, {-4917809685617656211, 0}, {-4917638975846390902, 0}, {-4917300107565742122, 0}, {-4917236602780645529, 0}, {-4916794223449858962, 1}, {-4911516589511161655, 1}, {-4910949219842705334, 2}, {-4910536147546886229, 2}, {-4908522960974967869, 0}, {-4906960218854491162, 1}, {-4904781830609650647, 3}, {-4903242827307200629, 1}, {-4896860683801786083, 2}, {-4896849754059983666, 2}, {-4894851229303410597, 0}, {-4891519093649931897, 3}, {-4888023992652221449, 2}, {-4887959752256510216, 2}, {-4887845318213502508, 2}, {-4885287953892531707, 0}, {-4883218843925761175, 2}, {-4881051016984397884, 0}, {-4879942821936229373, 1}, {-4879611786541315667, 2}, {-4877921182374090868, 3}, {-4874249212053665464, 2}, {-4874010361430588785, 3}, {-4872777162774248744, 0}, {-4871447711271581343, 1}, {-4871249571359782266, 1}, {-4870834360041101797, 1}, {-4865863835263933873, 2}, {-4865371389809966768, 2}, {-4862210694030833877, 1}, {-4858825736074737297, 0}, {-4858542213461876478, 0}, {-4857362727681001546, 1}, {-4854678498559698043, 0}, {-4851869817782247538, 2}, {-4847344244667648284, 2}, {-4845607813370061168, 0}, {-4843180828489849300, 2}, {-4842097842146962106, 3}, {-4841333732663314815, 0}, {-4841181207222055129, 0}, {-4839108153486289381, 2}, {-4837883279450790537, 3}, {-4837233048180842088, 3}, {-4835218748423840413, 1}, {-4833232644757996837, 3}, {-4830051133950141363, 2}, {-4826898855822236082, 0}, {-4826430511057931074, 1}, {-4826338070554219785, 1}, {-4825953256895355413, 1}, {-4825448586273649995, 2}, {-4821862325318471663, 1}, {-4819781065049956145, 3}, {-4818995353087626309, 3}, {-4813557317925471817, 0}, {-4812124958503027704, 1}, {-4811813066355538293, 2}, {-4807910218652461766, 1}, {-4807852449082845473, 1}, {-4806514943625509940, 2}, {-4805045245139102066, 0}, {-4803995691261591303, 1}, {-4803349985737015151, 1}, {-4800382969258635060, 0}, {-4798424048143666412, 2}, {-4797610444716158293, 2}, {-4797306662990335749, 3}, {-4794717831080081187, 1}, {-4793296205530171295, 2}, {-4791593016730960067, 0}, {-4791238708597264878, 0}, {-4789904410265471880, 1}, {-4779178343139787664, 3}, {-4778209828139427195, 0}, {-4775539693400245240, 2}, {-4774959687383610891, 2}, {-4773429019313660707, 0}, {-4769260692642680202, 0}, {-4766621310274825876, 2}, {-4762900661303769103, 1}, {-4758378996349173503, 1}, {-4754620928613649830, 1}, {-4754253937633976765, 1}, {-4747191700190733942, 3}, {-4746604330404571649, 0}, {-4746505594134442713, 0}, {-4741431591025034463, 0}, {-4739912967485081963, 2}, {-4739329085425870364, 2}, {-4738550323190747036, 3}, {-4737433913749659540, 0}, {-4737304670691166223, 0}, {-4736983633384435377, 0}, {-4734750563932957673, 2}, {-4733910172381620349, 3}, {-4733891583519598782, 3}, {-4733634774104906672, 3}, {-4733398939432662353, 3}, {-4732593032021858686, 0}, {-4730914899414744944, 2}, {-4728172259949389069, 0}, {-4727916498288597347, 0}, {-4727794693550724911, 0}, {-4726019226877706201, 2}, {-4725270935207140997, 3}, {-4723748945063187964, 0}, {-4722442779976302420, 1}, {-4721401549406389817, 2}, {-4720994846808957936, 2}, {-4720488453028895900, 3}, {-4719258288844216881, 0}, {-4717733386118379464, 1}, {-4716253367680249664, 3}, {-4715725848594724738, 3}, {-4710702916417813035, 0}, {-4710572361354397696, 0}, {-4709966675297352985, 0}, {-4708118120562827197, 2}, {-4707537429285632316, 2}, {-4705983913402524320, 0}, {-4705231169545999098, 0}, {-4704116416622451339, 1}, {-4702351735373991496, 3}, {-4701764280626417302, 3}, {-4698084369789267227, 3}, {-4695585675625201225, 1}, {-4694258396396986106, 2}, {-4690731007560645374, 1}, {-4690556995731549923, 1}, {-4688769720405001308, 3}, {-4684708095985816942, 3}, {-4684695029068869546, 3}, {-4684222485141860970, 3}, {-4681833310692867186, 1}, {-4680548677201305450, 2}, {-4680172177871606383, 3}, {-4678873362263130599, 0}, {-4674869417196444743, 3}, {-4674691099535765984, 0}, {-4673302528767406827, 1}, {-4672561038115523082, 1}, {-4671871945764623680, 2}, {-4670567886345620379, 3}, {-4667562882376350140, 2}, {-4667218722195082952, 2}, {-4662697209525852183, 2}, {-4662603989406886509, 2}, {-4659994131661891267, 1}, {-4656801354467441996, 3}, {-4654294432280869953, 2}, {-4654182173899701813, 2}, {-4653872321981084756, 2}, {-4652775573311524477, 3}, {-4648450953659344637, 3}, {-4647612781712755563, 0}, {-4646710187752899228, 0}, {-4645655625434552997, 1}, {-4645511024212851929, 1}, {-4643689013147431986, 3}, {-4639266646006595743, 3}, {-4637174299809092594, 1}, {-4633421647102713624, 0}, {-4632200829506322140, 1}, {-4627572914254313882, 1}, {-4623208620257384437, 1}, {-4622800900009632030, 2}, {-4615250638068998557, 0}, {-4613178334429917610, 2}, {-4610620133616642135, 0}, {-4610599795335092897, 0}, {-4610101323551528906, 1}, {-4609292032814548307, 2}, {-4606415062729030268, 0}, {-4604984331714684278, 1}, {-4603074222154359762, 3}, {-4599861807933115141, 2}, {-4597127249293679259, 0}, {-4595436456670287526, 2}, {-4592246642337519804, 1}, {-4589533449883538396, 3}, {-4588611536874075013, 0}, {-4588074070662873769, 0}, {-4587388476261305620, 1}, {-4585542934115940376, 3}, {-4582697147304426976, 1}, {-4581661831705042623, 2}, {-4580663703985051255, 3}, {-4579027551067414344, 1}, {-4576363354771071998, 3}, {-4576222456131952665, 3}, {-4574208681095108402, 1}, {-4571466779238782088, 3}, {-4570026187497261953, 1}, {-4565633717702658059, 0}, {-4565116358035735954, 1}, {-4564612545758619460, 1}, {-4562770914024123658, 3}, {-4562721811059824378, 3}, {-4561785873721971802, 0}, {-4561363598780301789, 0}, {-4559446090756373250, 2}, {-4558459138600834922, 3}, {-4556377361319183276, 1}, {-4554040251095883995, 3}, {-4553689820529357924, 3}, {-4551779164546056142, 1}, {-4549076572087129824, 3}, {-4547688629031365870, 0}, {-4546864579398325329, 1}, {-4543521680766897536, 0}, {-4542595122528538331, 1}, {-4542499078834861537, 1}, {-4541033932054777737, 2}, {-4537150516675728094, 2}, {-4536517903068610811, 2}, {-4526526068701337004, 3}, {-4522753693990510577, 2}, {-4522598948090011960, 3}, {-4522056921300507992, 3}, {-4519353631048453476, 2}, {-4519284779918148163, 2}, {-4519200920148599965, 2}, {-4518972852354251300, 2}, {-4518842920533332219, 2}, {-4518650484171332393, 2}, {-4517868548629276189, 3}, {-4514547131282243799, 2}, {-4514245341945593208, 2}, {-4508803205300584625, 3}, {-4499081433439364845, 0}, {-4497674587365452438, 1}, {-4492906579517039220, 1}, {-4491939675940901343, 2}, {-4491719280731191636, 2}, {-4488548387505360223, 1}, {-4486174306773576719, 3}, {-4484678596657481726, 0}, {-4483794462233584317, 1}, {-4483785032915471680, 1}, {-4477356668672593201, 3}, {-4476297685227427284, 0}, {-4474103850173054957, 2}, {-4471701795992391617, 0}, {-4467340103054293189, 0}, {-4467336203569939194, 0}, {-4464214863794381138, 2}, {-4463667901673107188, 3}, {-4459910984800214053, 2}, {-4455401865063209534, 2}, {-4451217145206705311, 2}, {-4450487182305093856, 3}, {-4448475363628171969, 0}, {-4448266344021483674, 1}, {-4447350998887115028, 1}, {-4446410810705290341, 2}, {-4446201414591381807, 2}, {-4445606005343463931, 3}, {-4444055855116269395, 0}, {-4443502030650246051, 1}, {-4443248050314220796, 1}, {-4441092155114467044, 3}, {-4437801878704656530, 2}, {-4437007420935676978, 3}, {-4434175212668565119, 1}, {-4433163933883236209, 2}, {-4432302371416702621, 3}, {-4431351660348213619, 0}, {-4428078134594632225, 3}, {-4427712353279057290, 3}, {-4427588386238766636, 3}, {-4427107421599904597, 3}, {-4426770926047084271, 0}, {-4417600275683112731, 0}, {-4417309497663099476, 0}, {-4417273361583245543, 0}, {-4414758944446414549, 2}, {-4411455567527596217, 1}, {-4407583084517622177, 1}, {-4403085833655918309, 1}, {-4400821712403147787, 3}, {-4396360821975859528, 3}, {-4393519688682690056, 1}, {-4390174909945848982, 0}, {-4390025216756011970, 0}, {-4388650160949606616, 2}, {-4385559562871266124, 0}, {-4379930584539173362, 1}, {-4379185727254278211, 2}, {-4377144299298821192, 0}, {-4376687615902103553, 0}, {-4375651041726419862, 1}, {-4373344997905034567, 3}, {-4372897892747331138, 0}, {-4371744299990688347, 1}, {-4370917529886439699, 1}, {-4368295940950441666, 0}, {-4361490933227191698, 2}, {-4361394730163158793, 2}, {-4360064118806649535, 3}, {-4357937955991924021, 1}, {-4353325903928971697, 1}, {-4350942178059917901, 3}, {-4348766228246209535, 1}, {-4347615193014774506, 2}, {-4345765545292538456, 0}, {-4344732026205427574, 1}, {-4344419041661215904, 1}, {-4343750233159048866, 1}, {-4342335719565939093, 3}, {-4338969298166626168, 2}, {-4338821378092888407, 2}, {-4331815229664943834, 0}, {-4330670095648685953, 1}, {-4327544673578189891, 0}, {-4325133168399894989, 2}, {-4319071977015497829, 3}, {-4317710526289596374, 1}, {-4315574563852770765, 2}, {-4313927721056181069, 0}, {-4310197755365846068, 3}, {-4307955227299174590, 1}, {-4304851935210358239, 0}, {-4304439646632382700, 0}, {-4304301207698444358, 1}, {-4303204856120571019, 1}, {-4301502797839848070, 3}, {-4300504505501978335, 0}, {-4300160892676710139, 0}, {-4299927082893888286, 0}, {-4299832988622941551, 0}, {-4299028748738986908, 1}, {-4297759236630095600, 2}, {-4297320465257783603, 3}, {-4294373277123326663, 1}, {-4293191114563369416, 2}, {-4292820624629565952, 3}, {-4291905700340272690, 0}, {-4291239224720770335, 0}, {-4290883653258986699, 0}, {-4290362251207186563, 1}, {-4287936228176707415, 3}, {-4284999263751335604, 2}, {-4283278235954268913, 3}, {-4281057395518207394, 1}, {-4280489890656454178, 2}, {-4280202669899116527, 2}, {-4277415148129936043, 0}, {-4274026569779475298, 3}, {-4273107572553001980, 0}, {-4271950217132835218, 1}, {-4270350403991943761, 3}, {-4268315464212555340, 0}, {-4268138816781976260, 1}, {-4267726277151817515, 1}, {-4266515200836801306, 2}, {-4261366394722917510, 3}, {-4258837939710953673, 1}, {-4258270982360468124, 1}, {-4256063556976463363, 3}, {-4255668840409032550, 0}, {-4255638825816421901, 0}, {-4251869571228206080, 3}, {-4248753146841169456, 2}, {-4243101971442096350, 3}, {-4242760677387880385, 3}, {-4242305407455055640, 0}, {-4242101396975959924, 0}, {-4241561317899344279, 0}, {-4236321747242487876, 1}, {-4234724936049240980, 2}, {-4233863356371244139, 3}, {-4233431554033538798, 3}, {-4232842794859698268, 0}, {-4231716155975095872, 1}, {-4231021345161535354, 2}, {-4225661690744912155, 2}, {-4224943814131016378, 3}, {-4223122339307918910, 1}, {-4222267140063604901, 1}, {-4221629084319753063, 2}, {-4221057327769686285, 2}, {-4218754759885102818, 0}, {-4215196884482189181, 0}, {-4204935900211475651, 1}, {-4203323572718160470, 2}, {-4196555312996833875, 0}, {-4195445419333092387, 1}, {-4191928069932749580, 0}, {-4191506859048771468, 1}, {-4190717390187902342, 1}, {-4190120933356584045, 2}, {-4189742212418008565, 2}, {-4189278360872181855, 3}, {-4189098064378700010, 3}, {-4189062136462818778, 3}, {-4188870437180598080, 3}, {-4187023270060251009, 1}, {-4186833421425795310, 1}, {-4180565984494264806, 2}, {-4178907430303257312, 0}, {-4177920382258414955, 1}, {-4176956983774723121, 2}, {-4176948525797914138, 2}, {-4173700745718737263, 1}, {-4170646438718103830, 3}, {-4160436777582262202, 0}, {-4159715318569072964, 1}, {-4157945119369451038, 3}, {-4157887224649636337, 3}, {-4153605021112475459, 2}, {-4152560725523245576, 3}, {-4150673962603509365, 1}, {-4149750525713339994, 2}, {-4141922532432088677, 1}, {-4140958344663356700, 2}, {-4138874891612903687, 3}, {-4137256525278690375, 1}, {-4136275267952281716, 2}, {-4133254802538490627, 0}, {-4130954407709287987, 2}, {-4130193969752965711, 3}, {-4129954525232364292, 3}, {-4126676448278269567, 2}, {-4126649819573295910, 2}, {-4126022319757150219, 3}, {-4117478652966258207, 2}, {-4112494578058335835, 3}, {-4109558497847283361, 1}, {-4108999530852690122, 2}, {-4107409498130039330, 3}, {-4105712505779530935, 1}, {-4100882093134726913, 1}, {-4096974618499175749, 1}, {-4090795883503224198, 2}, {-4089890186851727381, 3}, {-4087423745282220155, 1}, {-4086055416696102866, 2}, {-4083373563175178379, 1}, {-4083163041990028050, 1}, {-4080872684343453264, 3}, {-4079715645315677776, 0}, {-4079433759635812581, 0}, {-4078373898870909190, 1}, {-4077896571028456547, 2}, {-4073167242895079479, 2}, {-4073076567385986951, 2}, {-4068459702867266788, 2}, {-4059894304085030305, 2}, {-4059677101225892588, 2}, {-4059451909711416421, 2}, {-4056696195379453294, 0}, {-4053465730322555749, 3}, {-4052844207042649723, 0}, {-4052181449728743725, 0}, {-4048212021062351146, 0}, {-4046921116820503312, 1}, {-4039190470714210410, 0}, {-4037225144879793641, 2}, {-4034334875957649468, 0}, {-4031837968928549064, 3}, {-4031367018381678751, 3}, {-4030890765945694337, 3}, {-4028682177843946708, 1}, {-4027702632847308982, 2}, {-4025687850568739708, 0}, {-4025015917345141601, 1}, {-4023141977444151869, 2}, {-4022992180654516720, 2}, {-4017659838436031667, 3}, {-4015645187168478732, 1}, {-4013548278083218596, 3}, {-4012429591227980114, 0}, {-4010630261469503347, 1}, {-4009078060533900972, 3}, {-4006825819617497234, 1}, {-4006467550079862064, 1}, {-4004470591263374033, 3}, {-3997956394132363479, 1}, {-3995335879432697408, 3}, {-3994665980755925788, 0}, {-3994009643922001454, 0}, {-3992096059762536243, 2}, {-3988936855091629080, 1}, {-3987790902105876413, 2}, {-3985743255213422837, 3}, {-3982443398310480624, 2}, {-3977406941901566518, 3}, {-3976196008357446480, 0}, {-3975688262285995778, 0}, {-3974009097065662970, 2}, {-3973615685383493036, 2}, {-3972627677769518485, 3}, {-3964654878906885299, 2}, {-3961521658154015719, 1}, {-3960218524112440332, 2}, {-3947706325254027443, 1}, {-3947044209485412800, 2}, {-3946845662259312188, 2}, {-3946752530248974545, 2}, {-3946726374625185903, 2}, {-3944348713026804818, 0}, {-3941370931176305364, 3}, {-3940804639175720640, 3}, {-3936419407847602605, 3}, {-3932708474861041482, 3}, {-3932408532646671665, 3}, {-3931601567313153666, 0}, {-3930501556214092823, 1}, {-3929651659870777663, 1}, {-3929593208529092257, 1}, {-3925890761287323678, 1}, {-3925760531953829583, 1}, {-3923797792409501443, 2}, {-3922645388544992630, 3}, {-3920651380743710045, 1}, {-3920518588836006508, 1}, {-3919896655614933618, 2}, {-3918952642576587725, 3}, {-3917982227897728604, 0}, {-3914719156036376534, 3}, {-3911737843708429833, 1}, {-3909445695983139426, 3}, {-3907891795395515714, 1}, {-3907371012693288386, 1}, {-3906851181590454148, 2}, {-3905097212338945156, 3}, {-3901110048445832368, 3}, {-3899995630568833247, 0}, {-3896710635716910813, 3}, {-3896137430667412352, 3}, {-3893349189582953959, 2}, {-3887191153531825259, 3}, {-3882110846608098066, 3}, {-3881936367027667277, 0}, {-3881548493247531224, 0}, {-3878030389052407256, 3}, {-3875078776692649664, 2}, {-3870873335596276974, 1}, {-3865268631032820428, 2}, {-3865158825222636626, 3}, {-3863902491904934154, 0}, {-3863343039378540029, 0}, {-3859462584613684179, 0}, {-3859160025432150604, 0}, {-3853372674280219518, 1}, {-3852191315790415688, 2}, {-3850181778053110089, 0}, {-3850039277879998009, 0}, {-3848215945990212029, 2}, {-3847658431616730953, 2}, {-3847114432168708931, 3}, {-3845874285069279250, 0}, {-3841936817396857991, 3}, {-3840903107156260910, 0}, {-3840866887006051362, 0}, {-3840508224779668867, 0}, {-3836182662852761251, 0}, {-3834029713142847278, 2}, {-3833625370742996598, 3}, {-3831598919814223983, 0}, {-3831419523526231787, 1}, {-3829746373712532045, 2}, {-3827486589728217862, 0}, {-3824747940283909239, 2}, {-3823354711099620962, 0}, {-3818937968248472959, 0}, {-3818708837457895937, 0}, {-3818617610388909737, 0}, {-3818327790062440044, 0}, {-3817352330446250177, 1}, {-3817212587026512720, 1}, {-3810338444535036275, 3}, {-3807535259076845296, 2}, {-3806622383909582641, 3}, {-3802561550704444403, 2}, {-3802268439002071529, 2}, {-3801009773909343550, 0}, {-3798504134210806581, 2}, {-3797481071386508797, 3}, {-3793938017067969976, 2}, {-3791609596159981991, 0}, {-3790248883256837452, 1}, {-3788920491993865323, 2}, {-3784549378444147694, 2}, {-3783102854818228214, 3}, {-3780208376111087297, 2}, {-3776500871258140115, 1}, {-3775372266411640179, 2}, {-3772988298449024198, 0}, {-3771441800630258237, 2}, {-3771173093867029902, 2}, {-3768406467549387382, 0}, {-3766440169414022031, 2}, {-3763557199532202812, 1}, {-3757770410038772221, 2}, {-3757382991731304877, 2}, {-3757313687557725022, 2}, {-3755555916617884865, 0}, {-3755210973340753899, 0}, {-3753241624802660985, 2}, {-3750173037840668314, 1}, {-3747868018731742202, 3}, {-3737915017325335522, 0}, {-3737674853963626806, 0}, {-3733791714789696032, 3}, {-3732254071027355960, 1}, {-3731251881087956386, 1}, {-3729717476870947675, 3}, {-3727255257665134604, 1}, {-3725837184622941878, 2}, {-3724869856450962728, 3}, {-3722260448113296229, 1}, {-3718210330048510601, 1}, {-3717924030743621062, 1}, {-3715285310683183290, 0}, {-3710734326747495379, 0}, {-3705340478030227643, 0}, {-3703891554627211559, 2}, {-3701836228696541005, 0}, {-3701549597637477241, 0}, {-3693357103559930697, 3}, {-3692347733187309808, 0}, {-3691242679305435502, 1}, {-3688557835757145555, 3}, {-3684778967206358669, 3}, {-3684227970320085677, 3}, {-3684198182839632523, 3}, {-3684129629353653488, 3}, {-3682562626841939283, 1}, {-3679041728093413613, 0}, {-3677390189902236104, 1}, {-3676805645451162764, 2}, {-3676596760113800761, 2}, {-3675022415601315465, 3}, {-3670702411074082301, 3}, {-3670549181759777779, 3}, {-3669328046939491203, 0}, {-3668780196083141076, 1}, {-3667796854335873812, 2}, {-3665056123456277927, 0}, {-3664989070885080130, 0}, {-3663076268648970233, 2}, {-3662714748005113234, 2}, {-3661763166663553972, 3}, {-3661258261691777727, 0}, {-3660465958855695750, 0}, {-3656103552599866830, 0}, {-3655405725905958684, 1}, {-3655092331148011998, 1}, {-3651612326628463358, 0}, {-3649661966402759569, 2}, {-3647650046651736562, 0}, {-3647314406368777422, 0}, {-3645855594149553334, 1}, {-3644667639156250902, 2}, {-3642185541196793718, 1}, {-3642146686459422430, 1}, {-3641393954985786133, 1}, {-3637701899508375059, 1}, {-3634987398027286519, 3}, {-3634633362529068333, 3}, {-3634083373066101786, 0}, {-3633566944729543736, 0}, {-3623869800086531774, 1}, {-3620607443584454007, 0}, {-3620334976389475052, 0}, {-3617673901042492536, 2}, {-3615623573200245763, 0}, {-3606463025141286711, 0}, {-3606028262075046779, 1}, {-3604290616869646380, 2}, {-3601525103400866280, 1}, {-3600714113938931997, 1}, {-3598778365507195688, 3}, {-3598097016299385573, 0}, {-3596545459665962878, 1}, {-3596051237381896662, 2}, {-3595607972434835807, 2}, {-3594959553849002071, 3}, {-3592505226021320157, 1}, {-3591228033666188028, 2}, {-3586730428507504952, 2}, {-3585621855113336780, 3}, {-3585531322210372445, 3}, {-3584618061560391210, 0}, {-3583793249753093765, 0}, {-3582132723143897358, 2}, {-3580111592589829488, 0}, {-3577863947036314627, 2}, {-3575753539964566976, 0}, {-3575562966952762376, 0}, {-3575230846670000212, 0}, {-3575217734905003875, 0}, {-3573068427164585730, 2}, {-3572641870364943167, 2}, {-3568416852313692157, 2}, {-3567428945704071328, 3}, {-3564764681305495647, 1}, {-3564580183380486994, 2}, {-3564560653413030197, 2}, {-3563841352760753092, 2}, {-3561181797126166537, 1}, {-3558768808113426905, 3}, {-3556776655430198469, 0}, {-3556198222251093700, 1}, {-3555120438553611784, 2}, {-3551910284819165679, 1}, {-3551758456176501420, 1}, {-3551601877709528504, 1}, {-3551519734355853838, 1}, {-3550824131267686051, 2}, {-3550051674696333793, 2}, {-3548619712167122188, 0}, {-3545242467215591922, 3}, {-3536795760782084425, 2}, {-3531632801863993822, 3}, {-3531573496007403061, 3}, {-3531182831500027599, 3}, {-3528246068657230264, 2}, {-3526978752157748379, 3}, {-3526856949879886652, 3}, {-3526290033169052333, 0}, {-3523275212513553601, 2}, {-3521123935893694563, 0}, {-3521099994116048911, 0}, {-3518220866453423698, 3}, {-3515719960042198778, 1}, {-3515332735340211251, 1}, {-3514949422883979425, 2}, {-3514144482741915879, 2}, {-3512488385783285732, 0}, {-3509388025195512766, 3}, {-3503715075844167889, 0}, {-3502625201554596047, 1}, {-3501959013470401823, 1}, {-3501824729107532246, 1}, {-3501525591674374417, 2}, {-3499874069742834327, 3}, {-3498967899681044491, 0}, {-3490410408895038823, 3}, {-3490122393352105490, 0}, {-3488770483245217874, 1}, {-3488441676400534025, 1}, {-3487108689109952362, 2}, {-3487096694279133354, 2}, {-3485092775508295834, 0}, {-3479258797548920484, 1}, {-3478912114492015582, 2}, {-3477806127219942756, 3}, {-3476984015727581167, 3}, {-3476425065361281372, 0}, {-3475293848099064983, 1}, {-3474818665183036127, 1}, {-3473723081572526220, 2}, {-3472690823324859637, 3}, {-3471454426308390293, 0}, {-3471186103366315916, 0}, {-3467156631916511943, 0}, {-3466872754954560381, 0}, {-3466626492200126086, 1}, {-3466534988804924454, 1}, {-3459975566886031299, 2}, {-3456776956616433374, 1}, {-3455866806647757474, 2}, {-3454515189897539714, 3}, {-3450866503380392142, 3}, {-3450111067052383597, 3}, {-3446663453374307723, 2}, {-3443308880832320543, 1}, {-3441701061315523653, 3}, {-3439683767828247785, 0}, {-3437193246933548974, 3}, {-3435687215249201245, 0}, {-3435647926065263842, 0}, {-3432817169758780417, 3}, {-3429680916004916257, 1}, {-3429586264177573903, 1}, {-3428754660785313001, 2}, {-3425648344964249934, 1}, {-3424818652830634891, 2}, {-3424769411899272309, 2}, {-3424569213836991991, 2}, {-3419601802268133055, 2}, {-3417812579738055912, 0}, {-3417031228649657947, 1}, {-3415631573433393132, 2}, {-3415054115816335037, 2}, {-3408976057367425622, 0}, {-3407078349390933291, 1}, {-3405233504637255529, 3}, {-3401716171101597763, 2}, {-3399851518650429477, 0}, {-3396783453795273693, 3}, {-3396661321827641447, 3}, {-3395328450948455539, 0}, {-3394510743450546462, 1}, {-3388968870557406156, 1}, {-3386834539694802770, 3}, {-3386602780530994262, 0}, {-3385930866817031311, 0}, {-3381798303721707389, 0}, {-3379045812693669927, 2}, {-3378562070000774436, 3}, {-3377050221254756625, 0}, {-3374149965185588595, 3}, {-3372486101827579970, 0}, {-3372065746597623858, 1}, {-3370818462985142769, 2}, {-3370434919986754362, 2}, {-3367663948892971812, 0}, {-3355978702487126680, 3}, {-3352541153272338166, 2}, {-3351276513764932882, 3}, {-3348814805862882392, 1}, {-3344033789111870793, 1}, {-3340407044087364195, 1}, {-3340321472963939727, 1}, {-3333379280756722214, 3}, {-3332797455289724293, 3}, {-3332384951891485988, 0}, {-3331355163318940516, 1}, {-3328617007707819800, 3}, {-3327427469768426787, 0}, {-3325527183165443649, 2}, {-3324569367862140704, 3}, {-3324209036873770198, 3}, {-3321740770911166597, 1}, {-3321060258427533947, 2}, {-3319091365273759708, 0}, {-3318721963639951930, 0}, {-3315011169039261364, 3}, {-3312517882134904308, 1}, {-3307139524247224030, 2}, {-3304989087415578646, 0}, {-3304558421153774586, 0}, {-3303980324087273672, 1}, {-3301951682089751074, 3}, {-3301795439968858151, 3}, {-3297013927583781784, 3}, {-3295314009565962732, 1}, {-3293579635464589912, 2}, {-3292906284502253524, 3}, {-3291092821923909755, 0}, {-3288274383337205058, 3}, {-3280848613358378899, 2}, {-3280538524002610156, 2}, {-3274696652326161192, 3}, {-3270373439314657469, 3}, {-3270120075578989114, 3}, {-3268953453169295248, 0}, {-3267428355727710463, 1}, {-3265577930308573655, 3}, {-3261040052295910161, 3}, {-3260560440021876552, 0}, {-3255619226375307094, 0}, {-3254187120342866805, 1}, {-3254052403079175996, 1}, {-3253267128125167392, 2}, {-3249646011836043835, 1}, {-3248645513157513748, 2}, {-3246801173833094893, 0}, {-3245988122985274141, 0}, {-3245630880978383934, 1}, {-3243958383319994803, 2}, {-3243468377085879355, 3}, {-3241550456322569743, 0}, {-3241441706509455798, 1}, {-3240564475537303571, 1}, {-3240369188784665646, 1}, {-3238097112742850511, 3}, {-3236456091159518317, 1}, {-3234909254933017289, 2}, {-3232311644671573092, 1}, {-3230664728808407550, 2}, {-3229706326498129479, 3}, {-3228530513831255234, 0}, {-3226228805335983411, 2}, {-3219245238835681625, 0}, {-3217222613906242169, 2}, {-3216086005702890550, 3}, {-3215274298416678785, 0}, {-3214472757930364746, 0}, {-3214050544002490581, 1}, {-3213879476412530721, 1}, {-3212981100756133514, 2}, {-3210528520471277806, 0}, {-3210454216905244546, 0}, {-3208305553533761849, 2}, {-3207188577808510646, 3}, {-3199555118010160927, 2}, {-3198269440142673193, 3}, {-3198106800486363012, 3}, {-3195524141222673715, 1}, {-3193749624361171911, 3}, {-3192509972666121344, 0}, {-3190453011880384893, 2}, {-3189094893150428389, 3}, {-3188257537860388152, 0}, {-3187637342371237068, 0}, {-3187093202113223168, 1}, {-3185671838408002047, 2}, {-3182189875086726974, 1}, {-3180867327701891940, 2}, {-3172515683366594041, 2}, {-3169504194940221721, 0}, {-3167718672128395622, 2}, {-3165154888494269488, 0}, {-3162540706059349183, 3}, {-3161529410792012243, 3}, {-3161116258779731243, 0}, {-3160142397786391907, 1}, {-3159867125050712343, 1}, {-3159220132029675555, 2}, {-3157884745462274915, 3}, {-3157415840296545541, 3}, {-3155382289675741388, 1}, {-3153897171957731208, 2}, {-3148370026417268254, 3}, {-3147773520358304241, 0}, {-3147472979918559585, 0}, {-3147246887211194130, 0}, {-3146294492146933944, 1}, {-3143913372815119405, 3}, {-3141389625800121176, 1}, {-3140137126562110275, 2}, {-3137619231645727542, 1}, {-3137116633909332704, 1}, {-3136520158053355330, 2}, {-3131608258298651445, 2}, {-3126325270458551326, 3}, {-3123363788132291544, 1}, {-3122802400862241017, 2}, {-3122291597050858986, 2}, {-3118996283541541055, 1}, {-3117656168243177884, 2}, {-3117308297640582642, 3}, {-3114545715534720612, 1}, {-3113720043967491140, 2}, {-3113454606644625252, 2}, {-3109688182898815920, 2}, {-3108653345976578385, 2}, {-3105674842501390249, 1}, {-3104632055148420124, 2}, {-3103061709869921882, 3}, {-3102727835206858005, 0}, {-3101494146307049608, 1}, {-3099552025829625036, 3}, {-3095220004843598559, 2}, {-3094631008242885900, 3}, {-3093579945035760087, 0}, {-3093546285880642726, 0}, {-3091448159939143791, 2}, {-3088559383659129995, 0}, {-3084073240044805028, 0}, {-3082751232336528177, 1}, {-3082718710090373868, 1}, {-3070440986312371031, 0}, {-3066283459562763102, 0}, {-3065469364052149342, 1}, {-3062310534560370810, 0}, {-3060399571900493299, 1}, {-3058907577907580042, 3}, {-3053933383542777318, 3}, {-3050931945152186216, 2}, {-3050705676232618819, 2}, {-3048365360927019001, 0}, {-3048181812089092560, 0}, {-3045765304802213051, 2}, {-3044155756817350001, 0}, {-3043382581128414301, 0}, {-3042223425772217827, 1}, {-3042198820370562562, 1}, {-3039677709134737180, 0}, {-3038054118138634996, 1}, {-3036963718801302257, 2}, {-3035132353153526594, 0}, {-3035087924518426939, 0}, {-3034513849749441163, 0}, {-3033577503049565179, 1}, {-3032128820756774983, 2}, {-3028087917408702857, 2}, {-3027056759318988003, 3}, {-3026368494770050903, 0}, {-3024721786713613081, 1}, {-3021990736514891644, 3}, {-3021299044027774262, 0}, {-3016381891277178911, 0}, {-3016088645897321699, 1}, {-3016019010263860657, 1}, {-3015065975932513766, 2}, {-3013325663745935157, 3}, {-3009999693529812750, 2}, {-3009962242305222554, 2}, {-3009874410369691472, 2}, {-3007833970348172929, 0}, {-3007655667382102099, 0}, {-3007556621048251005, 0}, {-3004695844023007931, 3}, {-3004537491915297049, 3}, {-3003025485163478589, 0}, {-3002356125129148167, 1}, {-3000355705646074427, 3}, {-3000169638802716243, 3}, {-2995925710307321221, 3}, {-2995465124179858689, 3}, {-2995085254816452439, 3}, {-2994787389200555039, 0}, {-2989119741996774661, 1}, {-2986135871263991702, 3}, {-2985909310218282962, 3}, {-2985406829667914796, 0}, {-2985376992899876618, 0}, {-2985130939934459879, 0}, {-2984817815354149766, 0}, {-2983977701424150580, 1}, {-2983251700710829977, 2}, {-2980292768352466115, 0}, {-2979610368382278511, 1}, {-2979031493721559096, 2}, {-2978886170372167481, 2}, {-2975384931016922414, 1}, {-2975109778822127624, 1}, {-2974200026559050122, 2}, {-2973029401577758713, 3}, {-2971792396654030652, 0}, {-2971058436056497229, 1}, {-2970276288698213383, 1}, {-2969472269800168954, 2}, {-2969136585714306617, 2}, {-2968834007207290171, 3}, {-2967988359986239606, 3}, {-2966185045600388538, 1}, {-2961472984067599286, 1}, {-2959085327792863127, 3}, {-2958944455508102573, 3}, {-2958495265318181711, 0}, {-2957922919879320296, 0}, {-2957557020063116264, 1}, {-2957015000160562380, 1}, {-2956168349987879576, 2}, {-2955106407496392412, 3}, {-2953993001876355519, 0}, {-2951949824205549444, 2}, {-2949213252895512776, 0}, {-2946259390648114489, 3}, {-2945629509688367419, 3}, {-2944936352116364376, 0}, {-2944401406669179149, 0}, {-2944192370739715913, 1}, {-2943926592078462731, 1}, {-2939317781372906190, 1}, {-2938140639969569739, 2}, {-2934876358760987525, 1}, {-2934841802736090239, 1}, {-2931391638305133402, 0}, {-2930448426002858687, 1}, {-2930317885884522603, 1}, {-2929256417971226185, 2}, {-2929025091375555793, 2}, {-2928186182154178024, 3}, {-2925823064813362016, 1}, {-2925303788667834248, 1}, {-2925185240161647580, 1}, {-2924585279928243595, 2}, {-2919033765628898750, 3}, {-2918565801795507000, 3}, {-2916799155289074409, 1}, {-2912931812013164185, 0}, {-2912667633493585245, 1}, {-2910677004888599760, 2}, {-2910288593546875789, 3}, {-2909696952091197708, 3}, {-2906974674261915332, 2}, {-2902868075379588331, 1}, {-2902467355927090088, 2}, {-2901605971634111574, 2}, {-2897604126597717645, 2}, {-2894095634806670193, 1}, {-2891600009458392643, 3}, {-2891352482100609400, 3}, {-2889494847559623685, 1}, {-2888174525434464067, 2}, {-2887146977075886385, 3}, {-2886085511150327026, 0}, {-2885036440653568397, 1}, {-2884228525355842379, 2}, {-2882759764482151938, 3}, {-2881054469868882030, 1}, {-2877132323400118979, 0}, {-2876778202845575521, 0}, {-2876357884749357003, 1}, {-2876243126834354171, 1}, {-2875881354414516993, 1}, {-2875204273229100110, 2}, {-2875053674100041780, 2}, {-2873080573678951971, 0}, {-2871878150272344861, 1}, {-2871761014479493540, 1}, {-2871056718164024161, 1}, {-2868786030339209610, 0}, {-2867758880701820873, 0}, {-2866593548503039738, 1}, {-2850436040329760435, 0}, {-2844922368624790244, 1}, {-2842173086214465716, 3}, {-2841625417469392906, 0}, {-2840348335916305209, 1}, {-2840181641247245099, 1}, {-2836103537117477124, 1}, {-2835447800309966707, 1}, {-2834635073309116078, 2}, {-2831748460490928780, 0}, {-2831457722830338420, 1}, {-2830286156200643082, 2}, {-2827549927003228241, 0}, {-2825647522513387962, 2}, {-2821799917296329275, 1}, {-2820356457120821336, 3}, {-2814448907684594333, 0}, {-2814172722797196808, 0}, {-2814026979740686048, 0}, {-2813808066037064123, 0}, {-2812923699110768422, 1}, {-2812037930643576018, 2}, {-2811281472468335628, 3}, {-2810847788188549431, 3}, {-2809939049776530220, 0}, {-2808692452338734567, 1}, {-2807522188789370463, 2}, {-2807424403492885266, 2}, {-2806904044473713807, 2}, {-2804768431466270348, 0}, {-2801417231179511683, 3}, {-2801211363558834416, 0}, {-2797715671839862080, 3}, {-2791770266713470940, 0}, {-2791318679785403711, 0}, {-2788775053267039611, 3}, {-2788434084269317380, 3}, {-2788249579427109645, 3}, {-2784304661527651503, 3}, {-2782602654385281736, 0}, {-2778591368205867845, 0}, {-2775510863635900297, 2}, {-2772480735206589687, 1}, {-2772471558308158138, 1}, {-2771435658939308309, 2}, {-2770750621739066375, 3}, {-2769666064965534382, 0}, {-2767693470449517633, 1}, {-2765843796276346110, 3}, {-2765518096898429709, 3}, {-2764986878473405609, 0}, {-2764668695089193409, 0}, {-2763894460595384595, 1}, {-2763744741608549278, 1}, {-2763126352734798464, 1}, {-2762483522631819856, 2}, {-2761687475643139997, 3}, {-2761344446665085919, 3}, {-2760310857239253481, 0}, {-2757451406478904925, 2}, {-2757311354829701692, 3}, {-2752909251566610889, 2}, {-2752102007595781595, 3}, {-2750590661340944924, 0}, {-2746907375151349632, 0}, {-2744787791958448000, 2}, {-2742432918660599673, 0}, {-2734521183332627859, 3}, {-2734172452662118168, 3}, {-2726978072428881370, 1}, {-2726544204970191583, 2}, {-2722026758628418674, 2}, {-2721695134105521871, 2}, {-2721227536919337220, 3}, {-2720458003988110516, 3}, {-2720157737819232648, 0}, {-2719248914510631491, 0}, {-2716431663907121759, 3}, {-2711031758245518374, 0}, {-2709509947593878201, 1}, {-2709356882553420218, 1}, {-2708991041802633143, 1}, {-2706013986291972030, 0}, {-2704815631074933283, 1}, {-2704812533221111541, 1}, {-2701559381723346853, 0}, {-2701227746563741300, 0}, {-2699451087634401104, 2}, {-2696746493502475357, 0}, {-2693430577424558072, 3}, {-2690793838798990786, 2}, {-2689414004795868341, 3}, {-2686335605283876475, 2}, {-2685020173153700010, 3}, {-2684359891153436597, 3}, {-2683020834458716398, 0}, {-2677257878216839285, 2}, {-2677253822965273990, 2}, {-2674385071050339127, 0}, {-2669405136231004174, 1}, {-2668096356389519994, 2}, {-2667310263111273119, 2}, {-2665770371230506131, 0}, {-2665562977789950484, 0}, {-2664078597450848025, 1}, {-2663862630109708625, 2}, {-2662070733667730874, 3}, {-2659479873634149716, 1}, {-2659421580781462996, 1}, {-2658265902642449454, 2}, {-2655833677725392164, 1}, {-2653793725666412432, 2}, {-2651417337147943553, 1}, {-2650707952314243342, 1}, {-2646504561842795329, 1}, {-2643765842016028969, 3}, {-2643271409307202084, 0}, {-2641793006224554219, 1}, {-2641494727422285946, 1}, {-2640299308249964941, 2}, {-2638474632880490386, 0}, {-2633253853983562740, 1}, {-2631957486563225609, 2}, {-2631827224509519683, 2}, {-2628238168044982436, 1}, {-2626088472731423583, 3}, {-2623510749398306210, 1}, {-2623507700814777949, 1}, {-2622030844209432632, 3}, {-2620804688109058566, 0}, {-2612980313345756424, 3}, {-2612491947283313294, 3}, {-2611987143115430356, 0}, {-2611930154962527621, 0}, {-2610131962043282043, 1}, {-2609488693451151947, 2}, {-2606421844193792590, 1}, {-2605512685515091320, 1}, {-2605066603016655746, 2}, {-2603439783253443967, 3}, {-2602157392147837405, 0}, {-2601884862810405303, 1}, {-2601726880141406840, 1}, {-2599607694937117446, 3}, {-2598780688510716158, 3}, {-2598464089607860397, 0}, {-2596894513350812598, 1}, {-2594837831141368556, 3}, {-2592879915624126391, 1}, {-2592839555654494078, 1}, {-2592648380431487301, 1}, {-2591412302181975023, 2}, {-2590671814904257939, 3}, {-2588532071686642362, 0}, {-2587979063926703402, 1}, {-2587546593967157789, 1}, {-2583859643274105804, 1}, {-2582957789639975204, 1}, {-2578460845274948580, 1}, {-2577454251284469592, 2}, {-2576731839920939528, 3}, {-2575709256143108457, 0}, {-2573492847310102784, 2}, {-2570463657489891643, 0}, {-2570295161905284703, 1}, {-2570034565937227758, 1}, {-2569728728174668592, 1}, {-2568084386103904875, 3}, {-2563202483826193329, 3}, {-2556890261577588100, 1}, {-2556291594037786151, 1}, {-2556249788321562086, 1}, {-2554738226859100049, 2}, {-2554726948432886464, 2}, {-2554375420730259082, 3}, {-2552973254121783266, 0}, {-2549790095232338506, 3}, {-2549131592259074007, 3}, {-2544429160103377085, 0}, {-2542204822121035160, 2}, {-2541937435593124584, 2}, {-2541904243463923379, 2}, {-2540999445248396437, 3}, {-2540756610458846291, 3}, {-2536950132192244438, 2}, {-2529588574935931893, 1}, {-2528543268206744059, 2}, {-2527542245091545958, 3}, {-2520773472914853889, 1}, {-2520494934520288794, 1}, {-2519971037715482051, 1}, {-2519721592688257769, 2}, {-2517148216631492904, 0}, {-2510717586694072561, 2}, {-2508914672754067796, 3}, {-2506983947392144823, 1}, {-2505569289864295943, 2}, {-2502640347343931415, 1}, {-2493929424080577156, 0}, {-2492663436810150711, 2}, {-2492557429029320646, 2}, {-2490860029693268907, 3}, {-2490539671891250844, 3}, {-2489160173565555203, 1}, {-2488970317392650908, 1}, {-2487172360506888366, 2}, {-2484972712628037609, 0}, {-2483938747057027028, 1}, {-2483765992321433167, 1}, {-2479954096852560112, 1}, {-2479545945226287108, 1}, {-2477041915025336753, 3}, {-2475854487857967818, 0}, {-2475583354970251669, 1}, {-2475429375189018484, 1}, {-2473057620187280888, 3}, {-2472806670717769836, 3}, {-2470570833337184798, 1}, {-2469095645915998745, 3}, {-2466137118790664850, 1}, {-2462472373490037680, 0}, {-2460717584131255467, 2}, {-2458751987429028564, 0}, {-2457371398211384388, 1}, {-2453403623771049237, 0}, {-2452001652401666686, 2}, {-2451761939127976070, 2}, {-2451027032526555593, 3}, {-2450472822117757904, 3}, {-2448637985279892652, 1}, {-2445829905964552172, 3}, {-2441113679860449361, 3}, {-2437466089971476711, 3}, {-2437363183150595342, 3}, {-2436926209927488215, 3}, {-2436684224810308055, 3}, {-2436014030076034526, 0}, {-2435261912172079556, 1}, {-2432335430744949835, 3}, {-2431540776211677806, 0}, {-2430773053521267509, 1}, {-2430320630082785781, 1}, {-2426430528563765517, 0}, {-2423985596817697146, 3}, {-2423335654357458541, 3}, {-2420409292182096959, 2}, {-2419530576701002007, 3}, {-2415764723491766066, 2}, {-2413247246157920033, 0}, {-2412294328410363639, 1}, {-2409804716312225570, 3}, {-2409616212921387479, 3}, {-2408206260232246513, 1}, {-2407528963112167410, 1}, {-2405462066239409862, 3}, {-2405030140472630425, 3}, {-2401358100142493613, 3}, {-2400299222327285946, 0}, {-2400054072475383755, 0}, {-2393510691250235963, 2}, {-2393062422701534566, 2}, {-2392291701786707761, 3}, {-2391567887120316420, 3}, {-2387803325444302159, 3}, {-2387798921219048278, 3}, {-2382635096349054712, 3}, {-2378123669179878196, 3}, {-2375288823730654623, 2}, {-2370338837053606078, 2}, {-2367322814450039676, 1}, {-2366039479091575566, 2}, {-2364858613365894831, 3}, {-2364557571549175480, 3}, {-2364276823956302531, 0}, {-2364202514752516204, 0}, {-2362800393992846908, 1}, {-2359794378307663322, 0}, {-2359194611534084229, 0}, {-2358642987132463338, 1}, {-2358394947916532067, 1}, {-2356745649482943695, 2}, {-2351410673105450190, 3}, {-2349953547325948950, 0}, {-2348915884660651169, 1}, {-2348520874838231421, 2}, {-2347763199285130931, 2}, {-2347608901841483902, 2}, {-2346650695679353494, 3}, {-2345438402707671605, 0}, {-2344890772806313083, 1}, {-2344008371097415587, 2}, {-2342236413769668944, 3}, {-2340902108506463950, 0}, {-2336557099005459749, 0}, {-2334864576100618300, 2}, {-2334567840303958770, 2}, {-2333171380648613565, 3}, {-2332797567736410094, 0}, {-2332452771379379317, 0}, {-2326881073667208359, 1}, {-2325221339677018822, 2}, {-2324963065733339176, 3}, {-2324296564662216125, 3}, {-2322474442384912906, 1}, {-2321646740569667768, 1}, {-2315662514143358923, 3}, {-2313663455636384906, 1}, {-2312983150592732472, 1}, {-2309436993511593956, 0}, {-2306346631960459674, 3}, {-2303951546040142087, 1}, {-2302475115125550159, 2}, {-2302173474816029425, 3}, {-2298945058561512710, 2}, {-2292127761772858435, 0}, {-2289895894048857468, 2}, {-2289448300200132791, 2}, {-2287598839947545796, 0}, {-2287370246140490934, 0}, {-2286070584288675652, 1}, {-2285717769717121975, 1}, {-2283974089932545025, 3}, {-2283534842932894702, 3}, {-2278644570841188880, 0}, {-2276962788116117924, 1}, {-2276918984111311755, 1}, {-2273555296203508850, 0}, {-2273129003658115849, 1}, {-2271894262228176093, 2}, {-2271434335987435542, 2}, {-2269490570227783645, 0}, {-2269267201043165988, 0}, {-2267837084103038835, 1}, {-2267546619270730573, 2}, {-2266381087296221357, 3}, {-2264590300188505277, 0}, {-2264034311573219335, 1}, {-2260538635420795596, 0}, {-2260399765831207912, 0}, {-2260111136639590274, 0}, {-2259407577075134292, 1}, {-2258940458046940672, 1}, {-2255683614229295145, 0}, {-2251778530403867941, 0}, {-2251118757187501568, 0}, {-2249571901714279622, 1}, {-2246818409949561067, 0}, {-2246231317019685410, 0}, {-2242931196513977463, 3}, {-2240518190840678540, 2}, {-2238160449036530410, 0}, {-2238072987832679324, 0}, {-2237959045764371301, 0}, {-2237652779337160948, 0}, {-2237539355295038106, 0}, {-2237037299910945657, 1}, {-2236843357771721212, 1}, {-2235988601281361544, 2}, {-2230269330653699961, 3}, {-2227593600515311823, 1}, {-2226202820425534042, 2}, {-2218838641266795362, 1}, {-2213565825338923107, 1}, {-2209542968262942413, 1}, {-2209048459249732751, 1}, {-2208887266432228304, 2}, {-2207394337060051276, 3}, {-2206420529994797030, 0}, {-2197907156764602539, 3}, {-2196846097327658992, 0}, {-2196246508661402006, 1}, {-2194159296655350408, 3}, {-2190081407205509116, 2}, {-2189353681212350467, 3}, {-2188827729859097094, 3}, {-2188206200284358975, 0}, {-2187672233148801599, 0}, {-2187562275560676549, 1}, {-2187269745230633973, 1}, {-2186296730848403666, 2}, {-2185626685292721891, 2}, {-2184261868447329991, 3}, {-2180146640829917379, 3}, {-2179585624819235644, 0}, {-2178943331147685730, 0}, {-2178464304235614195, 1}, {-2177815283452065000, 1}, {-2174968374336463268, 0}, {-2173900982644262860, 1}, {-2173745866080571812, 1}, {-2168866195085667593, 1}, {-2168389958926116066, 2}, {-2168357533157689284, 2}, {-2167707770783977991, 2}, {-2164044237336388750, 1}, {-2160999103065661289, 0}, {-2158447437837599140, 2}, {-2154292854395452429, 2}, {-2151599740303759032, 0}, {-2149382042069993022, 2}, {-2148953861311305472, 3}, {-2148698104931987848, 3}, {-2148112978694481096, 0}, {-2147680066767827196, 0}, {-2146997980960737122, 1}, {-2144650084507999880, 3}, {-2143325876430900376, 0}, {-2142839851831308106, 0}, {-2142251962803039670, 1}, {-2142168437921494952, 1}, {-2140593741715450337, 2}, {-2140162815703417510, 3}, {-2137246168928895700, 1}, {-2133823962143610931, 0}, {-2132703895584727152, 1}, {-2132321366588068045, 2}, {-2132179081571537488, 2}, {-2131362256163948007, 2}, {-2130778459546605117, 3}, {-2127893530648175973, 2}, {-2126982857092730068, 2}, {-2126570221295548900, 3}, {-2126372785527825876, 3}, {-2124378884240244012, 1}, {-2120829748743551728, 0}, {-2120105962060951732, 0}, {-2120089749864045610, 0}, {-2117009309659868707, 3}, {-2116442265483090548, 0}, {-2113857355933120795, 2}, {-2110414466527627175, 1}, {-2106041704660502944, 1}, {-2105629668702761656, 1}, {-2104717372700672449, 2}, {-2103440551476621601, 3}, {-2103359812954352049, 3}, {-2102090581730588687, 0}, {-2099558733259532086, 3}, {-2098631832592787072, 0}, {-2092767992028643996, 1}, {-2086585612680309883, 2}, {-2083014707116449149, 1}, {-2082234143181941548, 2}, {-2075578911078594652, 0}, {-2075236028193731601, 0}, {-2074849443572064608, 1}, {-2074500803028978729, 1}, {-2074291420915547905, 1}, {-2073782254202379797, 2}, {-2073640161144212708, 2}, {-2070416218094267614, 1}, {-2066244733166682653, 0}, {-2065945631444149730, 1}, {-2065816100031120881, 1}, {-2065367547164317761, 1}, {-2063066970021814280, 3}, {-2057715467631369210, 0}, {-2055678213387146389, 2}, {-2054425647941599057, 3}, {-2052509234494603248, 1}, {-2051540835092421323, 1}, {-2047540345802755738, 1}, {-2045145553822751633, 3}, {-2044168067324692852, 0}, {-2041297476165374960, 2}, {-2041153306318831295, 3}, {-2040998281588441000, 3}, {-2039140054526868281, 0}, {-2038639960848531541, 1}, {-2034894975105011740, 0}, {-2034263994484506584, 1}, {-2030730410709817375, 0}, {-2027266012545195185, 3}, {-2024932223989693975, 1}, {-2018225850289290900, 3}, {-2017642800488073115, 3}, {-2014492897832630471, 2}, {-2012440653942637790, 0}, {-2010466790131887032, 2}, {-2003796251298459922, 0}, {-2003687295190569078, 0}, {-2003290280934108382, 0}, {-1999065736768996681, 0}, {-1998664787827093786, 0}, {-1998658185151237190, 0}, {-1998006241169437750, 1}, {-1992862111869029652, 1}, {-1992552636093442893, 2}, {-1988596050595588632, 1}, {-1985905916303620287, 0}, {-1985243493435551548, 0}, {-1984089034476011526, 1}, {-1982446095162266356, 3}, {-1979042922149102403, 2}, {-1974300474725133899, 2}, {-1972234929885691391, 0}, {-1969501859756236498, 2}, {-1963128678804256834, 0}, {-1960541645596629114, 2}, {-1959665170338960642, 3}, {-1959042834804288103, 0}, {-1958317717095864498, 0}, {-1956572540252483451, 2}, {-1948056953503939124, 1}, {-1947965609073498075, 1}, {-1947775742597290580, 2}, {-1938211927945504317, 2}, {-1937059331288790967, 3}, {-1935755071906302991, 0}, {-1934233713184352765, 2}, {-1932322598268362288, 3}, {-1932160284409598705, 3}, {-1930524203030790570, 1}, {-1930400927565874732, 1}, {-1929100604147673969, 2}, {-1929014292818998275, 2}, {-1928880041489040166, 2}, {-1926237471365538009, 1}, {-1925866440768481944, 1}, {-1924919078948974188, 2}, {-1921046174871286486, 1}, {-1920170160639724076, 2}, {-1918071207995313420, 0}, {-1913851401702855063, 0}, {-1913575068283535842, 0}, {-1912772188273378171, 1}, {-1912194742595567221, 1}, {-1912159111584559214, 1}, {-1910996253744891098, 2}, {-1910687496623733176, 2}, {-1907559923903002166, 1}, {-1906362554936993600, 2}, {-1905564681716128912, 3}, {-1903289686061509354, 1}, {-1900089356707402545, 0}, {-1899774909554183123, 0}, {-1899005973765148518, 1}, {-1897031735968314296, 3}, {-1896674211807173063, 3}, {-1896373754261125209, 3}, {-1895927701202612382, 0}, {-1895912572536160990, 0}, {-1892915180655558698, 2}, {-1892722070949314246, 2}, {-1892702771721787932, 2}, {-1891214090940897197, 0}, {-1890730517298219501, 0}, {-1890340822608936081, 1}, {-1888301461224304007, 2}, {-1888120051044284019, 3}, {-1887898543783613101, 3}, {-1886804392633259315, 0}, {-1886360411030179843, 0}, {-1883655437934737036, 2}, {-1882054408396715960, 0}, {-1879597933982587266, 2}, {-1878494770591697774, 3}, {-1878081758349364047, 3}, {-1877428361111421756, 0}, {-1876006441412808642, 1}, {-1875909827106035810, 1}, {-1875171502966337475, 2}, {-1873473599933351724, 0}, {-1871404493179677068, 1}, {-1865138975261346060, 3}, {-1863332318657203145, 1}, {-1862299952057358843, 1}, {-1861469782185774242, 2}, {-1860659425200769019, 3}, {-1859574769497374283, 0}, {-1858454353806900987, 1}, {-1857382477123995478, 2}, {-1857360875346156082, 2}, {-1856330511325720903, 3}, {-1856181385788934415, 3}, {-1854493237484912544, 0}, {-1854466982102865542, 0}, {-1850766465229825122, 0}, {-1849623939966392797, 1}, {-1848048114159325844, 2}, {-1843934712774367925, 2}, {-1843618648373916943, 2}, {-1840977839819247764, 0}, {-1833188255869195087, 3}, {-1831068751191439147, 1}, {-1830642009290768934, 2}, {-1828892330421349941, 3}, {-1826988244448057128, 1}, {-1824951082240923237, 3}, {-1824869417389715773, 3}, {-1823816214574931290, 0}, {-1822181949417990712, 1}, {-1821852977674989408, 1}, {-1820464635726684841, 3}, {-1817352914908967107, 1}, {-1816419573671727699, 2}, {-1815588381446658795, 3}, {-1814915237237971627, 0}, {-1814105236715409039, 0}, {-1808307347573127347, 1}, {-1808040454012366204, 2}, {-1805799861060122608, 0}, {-1805423197207748539, 0}, {-1804793000334610134, 1}, {-1803732116860232196, 1}, {-1799240499836713697, 1}, {-1796372868237264224, 0}, {-1792996523942718741, 3}, {-1792617508371055034, 3}, {-1792249732543921618, 0}, {-1790675281054148868, 1}, {-1787882135061100367, 0}, {-1787632938159903226, 0}, {-1784229534574110459, 3}, {-1783829371775155162, 3}, {-1783017331429437242, 0}, {-1781919842759101092, 1}, {-1779844930087365741, 3}, {-1779071391116587009, 3}, {-1778993948930827177, 3}, {-1777712912186282667, 1}, {-1776832295367414331, 1}, {-1775081382941827680, 3}, {-1775068639283931764, 3}, {-1773093141839940101, 1}, {-1770582882602923096, 3}, {-1765987271807056595, 3}, {-1764203793285297595, 1}, {-1762849408292986841, 2}, {-1762579735139109116, 2}, {-1760031513045871638, 0}, {-1758402347258004054, 2}, {-1755867854034167015, 0}, {-1755197594740793689, 1}, {-1752867402584856984, 3}, {-1751203181164321659, 0}, {-1749531068569060412, 2}, {-1746388478437347786, 0}, {-1746163625865976983, 1}, {-1745355841458698153, 1}, {-1745161422465154799, 1}, {-1743290637506269044, 3}, {-1743000379198439020, 3}, {-1741027670220260900, 1}, {-1740509364539265718, 2}, {-1739237417399340898, 3}, {-1738871792183849957, 3}, {-1736734728712005655, 1}, {-1736257160434343463, 1}, {-1734839074553675408, 3}, {-1734605702515050498, 3}, {-1733473721879637883, 0}, {-1731987872898592864, 1}, {-1727739427050789107, 1}, {-1727652161941793587, 1}, {-1727293510164503507, 1}, {-1727162561878255052, 1}, {-1724404612416028431, 0}, {-1722524894756293483, 2}, {-1719309985251970216, 0}, {-1718992537710772484, 1}, {-1715881090607322711, 3}, {-1715384981194759232, 0}, {-1714179046357577000, 1}, {-1710731568862121365, 0}, {-1709430540415023211, 1}, {-1703641985898630462, 2}, {-1693926576021654720, 3}, {-1693523448777815659, 3}, {-1692340115173495330, 0}, {-1691746622763476746, 1}, {-1680258347308247770, 3}, {-1679313913073914417, 0}, {-1679131447123428458, 0}, {-1674358545206413156, 0}, {-1670869536098236151, 3}, {-1670699006914452166, 0}, {-1668343002958002830, 2}, {-1665852231236116178, 0}, {-1664185483327441597, 1}, {-1663743278102555502, 2}, {-1663378417784252575, 2}, {-1661511824664119347, 0}, {-1659961907848432286, 1}, {-1658467318361508662, 2}, {-1650814621618233035, 1}, {-1649674584801489828, 2}, {-1647761775726938901, 0}, {-1645097598965550290, 2}, {-1644317587157914688, 3}, {-1642468211353558340, 1}, {-1641303804254275062, 2}, {-1638749362147741438, 0}, {-1638392507967679416, 0}, {-1633989416563684334, 0}, {-1631704072566276741, 2}, {-1630607867523115358, 3}, {-1627299494934524248, 2}, {-1627194249203659720, 2}, {-1626056433241636478, 3}, {-1621991081661240609, 3}, {-1621085018682709855, 0}, {-1619377313320485674, 1}, {-1615026744031117009, 1}, {-1614568699164127899, 1}, {-1614497073291791832, 2}, {-1611643432082702196, 0}, {-1608099982411886934, 3}, {-1607170760890700760, 0}, {-1605032997743499044, 2}, {-1603969215275282995, 3}, {-1601042909382343769, 1}, {-1598891279397659633, 3}, {-1598788545964291928, 3}, {-1597455479019942120, 1}, {-1597320620601795776, 1}, {-1596114996776120447, 2}, {-1593323235421682050, 0}, {-1591708216857693180, 2}, {-1588989238592335672, 0}, {-1586251731860542940, 3}, {-1584156435961454604, 0}, {-1582536824893245425, 2}, {-1578903591526917177, 1}, {-1577748931079384631, 2}, {-1577193525516580968, 3}, {-1574259629920283241, 1}, {-1572803616465303540, 3}, {-1569029150689579426, 2}, {-1563790370335052111, 3}, {-1563188125699306320, 3}, {-1562809307787587839, 3}, {-1562751739078108004, 3}, {-1560182473401270394, 2}, {-1559848111411980933, 2}, {-1557315843310898722, 0}, {-1556221261721911328, 1}, {-1554080502714318972, 3}, {-1553530575504181180, 0}, {-1553232530992959658, 0}, {-1551267162716394812, 2}, {-1549277539320998527, 3}, {-1548035660739683959, 1}, {-1545452979887143084, 3}, {-1542627790006228968, 1}, {-1537728697664832951, 2}, {-1534951466587859449, 0}, {-1533077901592371382, 2}, {-1532642884956928868, 2}, {-1529485069253607924, 1}, {-1527802401599544087, 3}, {-1527055371436496762, 3}, {-1524349928567974230, 2}, {-1523973936653775684, 2}, {-1518444121745917668, 3}, {-1517605853758432920, 0}, {-1512843435409066707, 0}, {-1512825500314012716, 0}, {-1512287684307957039, 0}, {-1512196060281246254, 0}, {-1510465954280237946, 2}, {-1510444808308034173, 2}, {-1506915753189261675, 1}, {-1506763572283983646, 1}, {-1506160871283510880, 2}, {-1505544264098902823, 2}, {-1505304159737148308, 3}, {-1501896388474219075, 2}, {-1501010683791516153, 2}, {-1500929039144384021, 2}, {-1498441681979226385, 1}, {-1498047809429285021, 1}, {-1494113340401610555, 0}, {-1494043790921563066, 1}, {-1488614560406335935, 1}, {-1487404051367138156, 2}, {-1484210710786972740, 1}, {-1479842367472189224, 1}, {-1474815918600291735, 2}, {-1473425692407981733, 3}, {-1471473622549831966, 1}, {-1468030945382313744, 0}, {-1465974463340554483, 1}, {-1465224092611572552, 2}, {-1464973780294624678, 2}, {-1464817042583527930, 2}, {-1462795292621763301, 0}, {-1462049816624651316, 1}, {-1461280565681122556, 2}, {-1455947091304858026, 2}, {-1454162783543057607, 0}, {-1452346188197411134, 2}, {-1449827072838993314, 0}, {-1448531631099878625, 1}, {-1446112453325041567, 3}, {-1445943711418849100, 3}, {-1444490488567062739, 1}, {-1440616756658800055, 0}, {-1440201595199290569, 0}, {-1439164830506807663, 1}, {-1433741524132007675, 2}, {-1433534058769220936, 2}, {-1429446709107605997, 2}, {-1427755346720345444, 3}, {-1425878819485331282, 1}, {-1416089963963272278, 2}, {-1416076589999394053, 2}, {-1414480315518395611, 3}, {-1408905663687901977, 0}, {-1406681831609473672, 2}, {-1406360353076550129, 2}, {-1404309548662345170, 0}, {-1403136838690136074, 1}, {-1399940900690963084, 0}, {-1398678919921721829, 1}, {-1398611792395107567, 1}, {-1398314707890919864, 2}, {-1396248357767677591, 3}, {-1393479670595026840, 2}, {-1391592344885264932, 0}, {-1385871943806910318, 1}, {-1384952133416615764, 1}, {-1383631863631062324, 3}, {-1380724317621571561, 1}, {-1378913085668019563, 3}, {-1378000885520110991, 0}, {-1375408316901699687, 2}, {-1373732083342434939, 3}, {-1369078717692394053, 0}, {-1368854053212096521, 0}, {-1367844497320252663, 1}, {-1366902003403090714, 1}, {-1362623426886902239, 1}, {-1361778231459323052, 2}, {-1361247149346162270, 2}, {-1357344194246196682, 2}, {-1354413656017715812, 1}, {-1354164235284583129, 1}, {-1349663819090533573, 1}, {-1347100097576767756, 3}, {-1346383750776461839, 0}, {-1338850645441468878, 2}, {-1338796224011019793, 2}, {-1333917434186680394, 3}, {-1331856836553673957, 1}, {-1329101611515589465, 3}, {-1328428949757312575, 0}, {-1327935382244843453, 0}, {-1325168693740124782, 3}, {-1325152932883192573, 3}, {-1323724782579371354, 0}, {-1320801626707566503, 2}, {-1320304058393305082, 3}, {-1320155345480619674, 3}, {-1319774336460305454, 3}, {-1319654097239040179, 3}, {-1317279198152880086, 2}, {-1315697465148623624, 3}, {-1315595291204178765, 3}, {-1314642015978588599, 0}, {-1314506396527354713, 0}, {-1302076497759566196, 3}, {-1301146133434801499, 0}, {-1299101443779584876, 2}, {-1298781291506435097, 2}, {-1285654953148083366, 2}, {-1284984431032969819, 2}, {-1283664301162782501, 3}, {-1281460906375894565, 1}, {-1277957415329035942, 0}, {-1276350644752352739, 2}, {-1271007628369261666, 3}, {-1270748765140350517, 3}, {-1267775765877796973, 1}, {-1265793239351691300, 3}, {-1265025951471657763, 0}, {-1263155161144952833, 2}, {-1262194379779126736, 2}, {-1261763548157370125, 3}, {-1261214427717145557, 3}, {-1258575267463600936, 2}, {-1258352383571134350, 2}, {-1253530287168879212, 2}, {-1252944263126520647, 3}, {-1252712739372795816, 3}, {-1252002263984871304, 3}, {-1246575837405344446, 0}, {-1245576336262250434, 1}, {-1242970933191876325, 0}, {-1242134053646083860, 0}, {-1241838785875603366, 1}, {-1240924489907435312, 1}, {-1239531207691803586, 3}, {-1234980902435470782, 3}, {-1233470947513703105, 0}, {-1224699012311108401, 0}, {-1224000270275042778, 0}, {-1221843336532441859, 2}, {-1221328215574000509, 3}, {-1220678477799309883, 3}, {-1218549083573929154, 1}, {-1217090296851042491, 3}, {-1216647231488414561, 3}, {-1213069060332460176, 2}, {-1212722424675087943, 2}, {-1210749641592010249, 0}, {-1209451658367509130, 1}, {-1207985712205088473, 3}, {-1207548938640541284, 3}, {-1205926746069531541, 0}, {-1201533665340011682, 0}, {-1201504451431647717, 0}, {-1201273777071664066, 1}, {-1201080066455059843, 1}, {-1199249073256660406, 2}, {-1198831181223038346, 3}, {-1198637581310715623, 3}, {-1197508199353274230, 0}, {-1195426106362627613, 2}, {-1195090046047014218, 2}, {-1193746069172769025, 3}, {-1191798131784745810, 1}, {-1190858883595967526, 2}, {-1190107657111481907, 2}, {-1186441402359507445, 2}, {-1185776498237769292, 2}, {-1184434569986778375, 0}, {-1183873305629144242, 0}, {-1181277879191692978, 2}, {-1179630796588659498, 0}, {-1177151819143762662, 2}, {-1176774698107529431, 2}, {-1175526857543141289, 3}, {-1174300356546669860, 1}, {-1171152141296850958, 3}, {-1169977416524822488, 0}, {-1168846865328009227, 1}, {-1163257714797357812, 2}, {-1160243713204167242, 1}, {-1158639737153591325, 2}, {-1157036866770926600, 0}, {-1155232900193319944, 1}, {-1152967204838276266, 3}, {-1152868737732334136, 0}, {-1152802578545715236, 0}, {-1152684150090300048, 0}, {-1152359548242977740, 0}, {-1150686407875479255, 1}, {-1150296582975165507, 2}, {-1150264431439526532, 2}, {-1150030117026759518, 2}, {-1147316199121276500, 0}, {-1145217570441118699, 2}, {-1145074827508615066, 2}, {-1144859008037402187, 3}, {-1143261189509486067, 0}, {-1142560685692396912, 1}, {-1140740218452445296, 2}, {-1140471338776748020, 3}, {-1139474671028372331, 3}, {-1139266097077022096, 0}, {-1138484053216818838, 0}, {-1137014555010663959, 2}, {-1136979698434165329, 2}, {-1128109384607248838, 2}, {-1127717459979696323, 2}, {-1126530448459170425, 3}, {-1125328110991572224, 0}, {-1123927515042473937, 1}, {-1123278971731887438, 2}, {-1123009921528778662, 2}, {-1118693171689001704, 2}, {-1117206558756077661, 3}, {-1116797137764417425, 0}, {-1116663361230920869, 0}, {-1113158324599555738, 3}, {-1111781222046260423, 0}, {-1108304800518597836, 3}, {-1107066522854398543, 0}, {-1101625406492723498, 1}, {-1100656754750990607, 2}, {-1098224416273206884, 0}, {-1097301031065938614, 1}, {-1094942599387816303, 3}, {-1093945354052422448, 0}, {-1093929716827351380, 0}, {-1091068625720540039, 2}, {-1090707405519744506, 3}, {-1089289024529580524, 0}, {-1087995967921116164, 1}, {-1087275644534610604, 2}, {-1087242979198940329, 2}, {-1085614910931006866, 3}, {-1075385491503010818, 0}, {-1074668554203872265, 1}, {-1073235046345540556, 2}, {-1070423312756455567, 1}, {-1069673373482873561, 1}, {-1068100500782301346, 3}, {-1066510686810337201, 0}, {-1063735478041201793, 3}, {-1060698767693734800, 1}, {-1059596041075332457, 2}, {-1058218048005603894, 0}, {-1051854066379282369, 1}, {-1051251684367976510, 2}, {-1051150292398920338, 2}, {-1051002128331815853, 2}, {-1050432403394780831, 3}, {-1048441364038490135, 0}, {-1046018898404202211, 2}, {-1045852427070619530, 3}, {-1045253478913356452, 3}, {-1043490112080498771, 1}, {-1040506675782319462, 3}, {-1039555660142716775, 0}, {-1039365871498463897, 0}, {-1038507094029118666, 1}, {-1024137151284929107, 2}, {-1021341092316011443, 0}, {-1020699409736068844, 1}, {-1020185170929668224, 1}, {-1017177323443070327, 0}, {-1014646946621792220, 2}, {-1012382214490360397, 0}, {-1011781922916624888, 1}, {-1010362432658746493, 2}, {-1009453752036546769, 3}, {-1009087513563874359, 3}, {-1005257144232960511, 3}, {-1004665102592750919, 3}, {-1003536152561585952, 0}, {-1000141238090522809, 3}, {-997995573337511703, 1}, {-995019871775582865, 0}, {-994062941789339149, 1}, {-993222845684429133, 1}, {-992653181499890078, 2}, {-987599969184458926, 2}, {-986399483459998572, 3}, {-986382211788141931, 3}, {-983253585597138587, 2}, {-981129884115140304, 0}, {-978888221685878702, 2}, {-977683012077508982, 3}, {-977080948432618714, 0}, {-977050006895902886, 0}, {-972876779519100960, 3}, {-971638704204878139, 1}, {-971530851882327796, 1}, {-971456383110996773, 1}, {-969239839137818859, 3}, {-967825744430733346, 0}, {-967728501516303499, 0}, {-967425539375673921, 0}, {-963754516077969798, 0}, {-962762924286725255, 0}, {-962651811879179709, 0}, {-961907766411954570, 1}, {-960419142166668073, 2}, {-959014489259360762, 0}, {-958738910333608519, 0}, {-956512461201067389, 2}, {-952693488331882972, 1}, {-951579551112524673, 2}, {-950590931691416163, 3}, {-948815683450387798, 1}, {-948500683717338961, 1}, {-946272134678290441, 3}, {-944331205686484850, 1}, {-941228152756570819, 0}, {-940498784430490362, 0}, {-940141377166661318, 0}, {-937512914028603620, 3}, {-937018491241707600, 3}, {-936260470151143969, 0}, {-932830596804011644, 3}, {-931589898911148069, 0}, {-924415414736346231, 2}, {-920727616950795670, 2}, {-920140388996040072, 2}, {-918608439276856107, 0}, {-915409309132928352, 2}, {-915291862740106742, 3}, {-914273139509149148, 3}, {-913828857994003705, 0}, {-913422721190430947, 0}, {-913214085702998812, 0}, {-908823184859081942, 0}, {-907860667411174299, 1}, {-907117004909382785, 2}, {-905477140002459244, 3}, {-903719116778878193, 1}, {-899419123396695724, 1}, {-899009618672977627, 1}, {-895346832758020062, 0}, {-891642641951171181, 0}, {-891605919111554641, 0}, {-891501614146508201, 0}, {-886519233923207175, 0}, {-886127866988687889, 0}, {-884591073667069665, 2}, {-881764523398330524, 0}, {-877365872829187835, 0}, {-876715568781609829, 1}, {-875648607130754806, 2}, {-875108816988003375, 2}, {-872578781267656666, 0}, {-872137386690888106, 1}, {-871681486727994380, 1}, {-871456056532746440, 1}, {-868951014232923967, 0}, {-862064264817027743, 2}, {-857775020482326402, 2}, {-855651265983115465, 0}, {-851358961765885186, 3}, {-850797503307583747, 0}, {-850276587889890853, 0}, {-849718435948130611, 1}, {-848074892985352431, 2}, {-847975243000832844, 2}, {-844047641076089562, 2}, {-843297192462526062, 3}, {-841388159977631827, 0}, {-841088622394001755, 0}, {-841006501537323183, 1}, {-840406953743301563, 1}, {-839839195792572660, 2}, {-839168098500359551, 2}, {-835292887720171328, 2}, {-830789226864780038, 2}, {-830498671011261396, 2}, {-828106487136060559, 0}, {-827749382739086071, 0}, {-825347295361382392, 2}, {-823087301619710795, 0}, {-821676159540573804, 2}, {-818771859446853983, 0}, {-814151344242355196, 0}, {-814005593591987023, 1}, {-813342644116700931, 1}, {-807207866519130243, 3}, {-805822858464796655, 0}, {-805214755764999948, 0}, {-804617920795327793, 1}, {-802693145930005592, 3}, {-801981287171221196, 3}, {-801757062392514851, 3}, {-795543541632209843, 1}, {-795416522810450163, 1}, {-793247799558849163, 3}, {-792965992507521994, 3}, {-790224977923007852, 2}, {-790102614526281936, 2}, {-789936867880901767, 2}, {-789010614468653769, 3}, {-787891129623668480, 0}, {-784992424788800285, 2}, {-782349593170855666, 1}, {-781575004087050824, 1}, {-779477504752744724, 3}, {-775470887151333436, 3}, {-774104534141819447, 0}, {-770427949396936848, 3}, {-769801292173358406, 0}, {-769759231140445305, 0}, {-769141004179211242, 0}, {-766250072124318694, 3}, {-765172561144749439, 0}, {-765051748074516957, 0}, {-762859989423626158, 2}, {-761979729736011117, 3}, {-758103253018524508, 2}, {-756895270274011853, 3}, {-753650760793606515, 2}, {-753040700087578141, 3}, {-753036260583658578, 3}, {-752769468622619709, 3}, {-751720132915383628, 0}, {-749221757880928815, 2}, {-747370348387618091, 0}, {-743510257069913444, 3}, {-741972051330918179, 0}, {-732777874819585469, 1}, {-732527415795266136, 1}, {-728908313856330296, 0}, {-726325844279290769, 2}, {-726104670247783274, 3}, {-725916525071468705, 3}, {-721080886055739804, 3}, {-716173786562910616, 3}, {-711418142795894312, 0}, {-708707312265689755, 2}, {-708259336741294331, 2}, {-708209601443501034, 2}, {-707413840356596211, 3}, {-704264816257841785, 2}, {-700501842376859299, 1}, {-699924121618666580, 2}, {-699173510278521294, 3}, {-694928025106217093, 2}, {-689186831618837430, 3}, {-687133535904964464, 1}, {-686933357352386113, 1}, {-680274134582167252, 3}, {-679246917476623699, 0}, {-678118811503123850, 1}, {-677177035019724642, 2}, {-676979480903665150, 2}, {-676033229760784567, 3}, {-674043833559041960, 1}, {-669793974453348022, 1}, {-667044712082598034, 3}, {-666762408152965524, 3}, {-665993192882545941, 0}, {-663289209930603601, 2}, {-660937286264411051, 0}, {-658584783947766860, 3}, {-658286583263627520, 3}, {-655954128371037376, 1}, {-655494942366869739, 1}, {-654522755499703383, 2}, {-651988276856973182, 0}, {-645865271396655468, 2}, {-640094885109482048, 3}, {-638192256750016889, 1}, {-638030128936957155, 1}, {-637791841615950901, 1}, {-629173266594458701, 1}, {-629051840281723418, 1}, {-628872808572439065, 1}, {-628405696243610091, 1}, {-626765933131104163, 3}, {-626149818514139261, 3}, {-625777811831726968, 0}, {-625739085672544325, 0}, {-618704114558502395, 2}, {-617955489350255873, 3}, {-617504493190557679, 3}, {-614050578974305642, 2}, {-606091791930651899, 1}, {-602186949834657433, 1}, {-600915681010340203, 2}, {-598528765384049157, 0}, {-595726228236097271, 2}, {-594748086612544774, 3}, {-594489435270597066, 3}, {-593130964790886442, 1}, {-590775394569444171, 3}, {-589026533038643231, 0}, {-588965009055505994, 0}, {-587650125902095591, 2}, {-586722688440250625, 2}, {-586708867127744681, 2}, {-586514475303424365, 3}, {-586204668459208778, 3}, {-585096967734484344, 0}, {-583906160172242099, 1}, {-582036368839819185, 3}, {-579181055705552183, 1}, {-578449113031338860, 2}, {-577998860602676434, 2}, {-577750678648786787, 2}, {-574205306189701593, 2}, {-571083669644024950, 0}, {-569902880434305106, 1}, {-567721443821300591, 3}, {-567500972540005687, 3}, {-567015540306850344, 0}, {-566350456914480747, 0}, {-565220067356568000, 1}, {-562983565687368755, 3}, {-561188847121447964, 1}, {-560142088855490684, 2}, {-560019466163975205, 2}, {-557644782841535115, 0}, {-556003840926558196, 2}, {-555639950092640668, 2}, {-555628050309182846, 2}, {-554326076964927066, 3}, {-552069022449147648, 1}, {-548798741259206860, 0}, {-547118646773306259, 2}, {-541640602847650161, 2}, {-540363744289044139, 0}, {-540236694393729254, 0}, {-536097879657316840, 3}, {-534399299648268268, 1}, {-532715588273947737, 2}, {-532157493440787893, 3}, {-529580886942148797, 1}, {-529399673591367197, 1}, {-526974151612197803, 3}, {-521900291291646808, 0}, {-521819338262241021, 0}, {-521777273466472192, 0}, {-521590533477234680, 0}, {-519421720958313954, 2}, {-517610583928040616, 0}, {-514030935887180362, 3}, {-513040944932576616, 0}, {-512661081567630200, 0}, {-510936796703106027, 2}, {-510436188550830240, 2}, {-507209922248318590, 1}, {-501816739816444981, 2}, {-499795352490996691, 0}, {-497772499581118605, 1}, {-495750013236888658, 3}, {-494552530806590575, 0}, {-490620724022037065, 0}, {-489368915436843794, 1}, {-487802567388282187, 2}, {-485840197008653553, 0}, {-483219462271033024, 2}, {-481470925307898641, 0}, {-480316717575630103, 1}, {-476875037956864526, 0}, {-476855407287041063, 0}, {-475599560221382704, 1}, {-475360359861770502, 1}, {-474665027162709122, 2}, {-473271248998237036, 3}, {-473169797920193246, 3}, {-472599117440972782, 0}, {-469494751331826278, 3}, {-469085281818431464, 3}, {-468596155884519221, 3}, {-467329399381280503, 0}, {-460810142657282082, 2}, {-457048210915036106, 2}, {-452354747180591056, 2}, {-452071264969598975, 2}, {-450421382449634808, 3}, {-448160776526584579, 1}, {-446067958794031360, 3}, {-444163390544203835, 1}, {-443238259674941121, 2}, {-438107416680541341, 2}, {-436558849784123331, 0}, {-433318055686475087, 3}, {-430472767797237460, 1}, {-430047833052471868, 2}, {-429951509704472173, 2}, {-427611592361889162, 0}, {-426525328389663639, 1}, {-425670994943984117, 1}, {-423758837370222832, 3}, {-422831163432379469, 0}, {-420356808984731478, 2}, {-415152776690618958, 3}, {-412491629245583811, 1}, {-412411955806795936, 1}, {-409194516914747059, 0}, {-408054142151004596, 1}, {-405916381053817988, 3}, {-402680400405748153, 2}, {-402294563122108593, 2}, {-402179730513070908, 2}, {-401317018934295792, 3}, {-401120271507893491, 3}, {-400201166720684612, 0}, {-398507410050414140, 2}, {-394794716949370121, 1}, {-393986612633286205, 2}, {-393355342249438647, 2}, {-392993602474464002, 2}, {-392906005314377954, 3}, {-388985867040381446, 2}, {-383211370617934898, 3}, {-382521476925762340, 0}, {-382423754079888716, 0}, {-376103656281112226, 1}, {-373734314020955645, 0}, {-371766073836396390, 1}, {-371753564795024950, 1}, {-367578753006445182, 1}, {-364396238653707811, 0}, {-364248120747934747, 0}, {-362809164837844933, 1}, {-361653515061631252, 2}, {-361108568338612868, 3}, {-358351927013351817, 1}, {-355963252458851750, 3}, {-354222795473148829, 1}, {-351461639432972239, 3}, {-349813745284870510, 1}, {-343171624986920150, 3}, {-340305343239490768, 1}, {-339853116163933114, 2}, {-338274602863681392, 3}, {-337947739068872473, 3}, {-336821261629352001, 0}, {-335468982235920052, 2}, {-333748092272552657, 3}, {-332161111509311172, 0}, {-330504950989170206, 2}, {-329656477330667493, 3}, {-327470139832544282, 1}, {-326799867474199465, 1}, {-321291470808127794, 2}, {-320486708517407841, 3}, {-317801693422775921, 1}, {-314172743928834217, 0}, {-314145463593925842, 0}, {-313250437698246935, 1}, {-309089415574316096, 1}, {-307306264328335331, 3}, {-307198390607013151, 3}, {-307141340924384126, 3}, {-304015893526921200, 1}, {-301740446419748943, 0}, {-299794238108725721, 1}, {-299578168054249600, 1}, {-299139383163409969, 2}, {-299018411687511723, 2}, {-294416114236761611, 2}, {-294241495009912632, 2}, {-290496769689193011, 1}, {-290172184000345249, 2}, {-288785721342621054, 3}, {-283860555331182376, 3}, {-282102785892339796, 1}, {-280730676104291787, 2}, {-277889098791410023, 1}, {-272579376031201559, 1}, {-270944517927142521, 3}, {-268040845003362199, 1}, {-267907505272959767, 2}, {-266142010797821901, 3}, {-264999559122421420, 0}, {-264495840778440365, 1}, {-263372665235036844, 2}, {-261555233229301282, 3}, {-259253317087742609, 1}, {-250345527471428046, 1}, {-249463868468920988, 2}, {-247365580122209507, 0}, {-245906337401114133, 1}, {-245847644647976388, 1}, {-237421649820761541, 1}, {-236103314090079636, 2}, {-234003737024208362, 0}, {-232564882469286066, 1}, {-232553029963256329, 1}, {-229732231136376743, 3}, {-227165961174207581, 2}, {-225940683867680844, 3}, {-223231591414754686, 1}, {-220858213162678124, 3}, {-220853209766062543, 3}, {-218814151780565259, 1}, {-217415483528589551, 2}, {-215019501764532499, 1}, {-212800859907387909, 2}, {-211316482699402036, 0}, {-209838200620880590, 1}, {-206047194985799654, 0}, {-205688478318626438, 1}, {-204797643058289232, 2}, {-204723507707766497, 2}, {-204509901656493691, 2}, {-203815543341826767, 2}, {-201560454886547082, 0}, {-201395833392055979, 1}, {-200187547955074750, 2}, {-198697659860506397, 3}, {-198213893087575061, 3}, {-195609007205124389, 2}, {-194502032274121086, 3}, {-193870337344881833, 3}, {-192780734242323592, 0}, {-191823931213441261, 1}, {-190926944869205505, 2}, {-188723776699310893, 0}, {-187892840754218842, 1}, {-187666886727938348, 1}, {-185642768172442400, 3}, {-182931870224192662, 1}, {-182613927073960835, 1}, {-178023573015689523, 1}, {-177442369411702452, 2}, {-175800235966354569, 3}, {-174250492565661475, 1}, {-172507369656689546, 2}, {-171426858473823448, 3}, {-169724297451530417, 1}, {-169621763899538875, 1}, {-169463994805073541, 1}, {-164493060619583776, 1}, {-164314379825977151, 2}, {-163105021764425780, 3}, {-162148505303414039, 3}, {-161680126255521039, 0}, {-161377740959848933, 0}, {-160842573018558141, 1}, {-160549406095001530, 1}, {-159125751571007457, 2}, {-158282582276624510, 3}, {-157864933296445077, 3}, {-156931981352873519, 0}, {-156169512602506618, 1}, {-155711526586381950, 1}, {-153013985105339652, 0}, {-152841809345263249, 0}, {-152545681642528496, 0}, {-152468721982189271, 0}, {-151612077042964049, 1}, {-149699492631760347, 3}, {-145240311186288542, 3}, {-137875643124074655, 1}, {-136823741788358717, 2}, {-133267543280976868, 1}, {-132716169076251081, 2}, {-131760647534116592, 2}, {-131605007050047232, 3}, {-131583060548710765, 3}, {-131420798435651764, 3}, {-130657697238337884, 3}, {-129438970951617485, 1}, {-126551225829717195, 3}, {-123764423966618857, 2}, {-123463349719919011, 2}, {-120526517138463402, 0}, {-119559634183805982, 1}, {-119312023456581357, 2}, {-119018545082410367, 2}, {-112095669382329211, 0}, {-110358033321145423, 1}, {-107892908511118517, 0}, {-104219854386612346, 3}, {-101498572469197163, 1}, {-100951912144139858, 2}, {-99139936086429966, 3}, {-94995291919087899, 3}, {-88711943388874637, 1}, {-83668165339614550, 1}, {-82371603702022797, 2}, {-80304589910404741, 0}, {-80175028528078733, 0}, {-80020596677198829, 0}, {-75944638922506117, 0}, {-70499300361242469, 1}, {-70487716349055786, 1}, {-69769017131629845, 2}, {-68557372089363563, 3}, {-67678228637283364, 3}, {-66685435314917788, 0}, {-65983712739073161, 1}, {-65667333597769575, 1}, {-63148605363145979, 3}, {-61144679699915150, 1}, {-61096754689315132, 1}, {-59693224507000408, 2}, {-59073855366602705, 3}, {-57865695274931102, 0}, {-56208138632121421, 2}, {-55283652474376003, 2}, {-52328287156369476, 1}, {-52147832854641180, 1}, {-51902409972188429, 1}, {-50340608507081554, 3}, {-49827275952989029, 3}, {-47620112558228260, 1}, {-41709503107598606, 2}, {-38949378385075562, 1}, {-30772445739016921, 0}, {-24055876245573933, 2}, {-23787344794631260, 2}, {-20011310888904549, 2}, {-19801451591297434, 2}, {-19388754982815970, 2}, {-18291953055118581, 3}, {-9772655057286938, 3}, {-5233917887925356, 3}, {-3790811379916698, 0}, {-1161327067926650, 2}, {337365398009672, 0}, {1835083890227297, 1}, {3479365334002559, 3}, {3620624420154906, 3}, {5016580628498636, 0}, {5947199420413706, 1}, {12406422862851764, 3}, {13457394691850640, 3}, {13759266764875114, 0}, {16943233056723959, 3}, {17811771696258454, 3}, {18297061267893592, 0}, {19122717499513976, 0}, {19313208863719195, 1}, {21288269071442912, 2}, {22158555735764685, 3}, {22876217507758888, 0}, {24132517986348450, 1}, {27516514484999216, 0}, {36270947124910996, 0}, {37871400534593637, 1}, {38721528907814858, 2}, {42003086096185126, 1}, {42368431559075436, 1}, {45472263415666160, 0}, {50499560488193998, 0}, {52886549432698387, 2}, {54314460065931021, 0}, {58150087335711583, 3}, {58572384606175640, 0}, {65693968761035428, 2}, {65775041077610528, 2}, {67458091619110080, 3}, {69293445680159260, 1}, {71942130426308810, 3}, {73391310423337311, 1}, {73831204758445523, 1}, {74080918672923083, 1}, {74811359757052203, 2}, {75509257701993107, 3}, {75622999261034037, 3}, {76424973408800670, 3}, {77227795434592890, 0}, {77347453074835314, 0}, {78622928595414301, 1}, {80837329384614668, 3}, {84934713532593143, 3}, {85712118963198663, 0}, {85798179858718218, 0}, {86634496166944612, 0}, {87007479623289741, 1}, {88033852397144842, 2}, {89765411247192320, 3}, {90526299466570760, 0}, {91098822676962048, 0}, {92043614588135058, 1}, {97541202511934322, 2}, {100138151257549750, 0}, {101131555154966248, 1}, {101271567986559024, 1}, {102358211025551975, 2}, {103188636542860506, 3}, {103233761839887273, 3}, {105215639123603095, 1}, {105247898612236993, 1}, {109334461514774551, 1}, {113940269966747855, 1}, {114981392801355984, 2}, {115789089383228434, 2}, {117025225880114358, 3}, {120330458503131262, 2}, {123082950455315200, 1}, {123130503269026471, 1}, {123455633768048520, 1}, {123487307600089941, 1}, {124480779319895711, 2}, {124544085092507842, 2}, {125302152732348184, 3}, {127312691055572717, 1}, {127978711203399929, 1}, {129686417761587636, 3}, {130060335428695763, 3}, {133197585235411031, 2}, {133276733357110320, 2}, {133890687739758641, 2}, {134082388779593758, 3}, {136525485375924584, 1}, {137711180325059355, 2}, {137852534869027802, 2}, {138758511575589621, 3}, {143927313191213067, 3}, {145566948387415811, 1}, {146043500758882627, 1}, {146239434715307010, 1}, {147656975457446594, 3}, {147924765446833171, 3}, {148622882929551227, 0}, {153094423748990272, 3}, {155376204296061914, 2}, {156113834385102974, 2}, {156692738160586768, 3}, {158452049235133439, 0}, {159238969879886275, 1}, {159474651983725180, 1}, {161593434730501659, 3}, {162357121275283036, 0}, {163014677410648780, 0}, {165609063072009543, 3}, {165683438545279851, 3}, {167331385283237955, 0}, {167384008766142825, 0}, {169664180767084950, 2}, {170638000774456803, 3}, {171312239643798338, 0}, {171913194746292340, 0}, {175173259055422937, 3}, {180406214755895441, 0}, {180763204734487516, 0}, {181715623314219477, 1}, {183531592189566333, 3}, {183617125152929884, 3}, {185117872141862078, 0}, {186994595414036126, 2}, {188561368309958610, 3}, {193906087462276248, 0}, {194685332682032855, 0}, {196764857437706914, 2}, {197286788100342599, 3}, {197927461559872336, 3}, {201156120159567349, 2}, {205781740550325879, 2}, {207407626016646402, 0}, {211442421345845297, 3}, {213056095944584527, 1}, {214590221976256938, 2}, {216447722602413405, 0}, {217379821904314596, 1}, {217396611170985405, 1}, {218469539110662847, 2}, {219452310774567477, 2}, {224041321812828923, 2}, {224325018177633051, 3}, {224864726707847341, 3}, {228147416769297532, 2}, {230403592908769907, 0}, {230910819710191369, 1}, {232292276743311520, 2}, {234873315809482405, 0}, {236048318327160595, 1}, {238108428351258060, 3}, {238597963220579951, 3}, {239349792036227692, 0}, {239363709735580268, 0}, {240118175049334032, 1}, {240183135090482759, 1}, {240818536923214964, 1}, {242398233326733860, 3}, {244051970029844984, 0}, {244653387233514407, 1}, {245785646003188762, 2}, {245997232532844693, 2}, {251407823474830080, 3}, {253520698967646545, 1}, {254927734982673366, 2}, {256155467344147576, 3}, {258103410624047043, 1}, {261762849724297777, 0}, {261800098297632583, 0}, {266525457107874383, 0}, {266715605587420697, 0}, {266926087904139703, 1}, {267095478455447841, 1}, {270358505600711663, 0}, {272185316158276275, 1}, {272254647515368888, 1}, {273803672914640681, 3}, {274365244835760293, 3}, {276174609308984620, 1}, {276672097945894600, 1}, {278615615452077094, 3}, {280306586670564258, 0}, {282088369226218327, 2}, {284662740045013911, 0}, {285441240227703147, 1}, {288867677693729617, 0}, {289228213040561409, 0}, {291516313259642017, 2}, {291846502005767527, 3}, {296190498504846331, 3}, {303374530070102574, 1}, {304417433989457422, 2}, {304553619725871452, 2}, {306541890774176457, 0}, {306606584236184948, 0}, {309995557562527642, 3}, {315608229279517988, 0}, {319559866097311520, 3}, {319631962641262886, 3}, {321596525596966368, 1}, {322230631275722738, 2}, {323500563420915007, 3}, {333253501526819559, 3}, {336966636538642288, 3}, {338322513685889094, 0}, {340815447648883515, 2}, {342808204456495847, 0}, {344363442083183372, 1}, {346996834423003137, 0}, {352050173411363008, 0}, {354396912301694851, 2}, {355987470783715289, 0}, {358981084325456297, 2}, {359296461283412462, 3}, {362307894109184756, 1}, {362622696874572589, 2}, {364857249575627077, 0}, {366923925676297763, 1}, {368039661304060109, 2}, {369660033030395664, 0}, {373111689511787776, 3}, {374168726287392416, 0}, {374675543762855577, 0}, {374749841501219463, 0}, {374876330850033157, 0}, {375480926451248840, 1}, {380589540610900499, 2}, {380891117707254721, 2}, {384578381819882997, 1}, {386951757158120423, 3}, {390895102354125171, 3}, {391503694742400100, 3}, {392399536927565061, 0}, {392581604898486237, 0}, {393456580623424521, 1}, {396061521001819786, 3}, {397073904761201461, 0}, {401505357222421447, 0}, {402070157707995986, 1}, {404421684825651952, 3}, {404868283939984213, 3}, {410252855556188539, 0}, {415539346022180878, 1}, {416032636133761818, 1}, {416720033476621147, 2}, {419686382925774771, 0}, {421035787703398002, 1}, {423821949166429788, 0}, {425257284077656004, 1}, {425787377679108388, 2}, {426431974320414067, 2}, {427565442753143553, 3}, {428534378636662958, 0}, {430098637932406443, 2}, {432683879540471111, 0}, {439696993716723735, 2}, {440823819967060343, 3}, {441378230138224914, 0}, {441411349356125535, 0}, {441457730413500510, 0}, {442191421312351195, 0}, {446727068500127107, 0}, {450474396458754002, 0}, {451656144049885095, 1}, {454405977678828412, 3}, {463535568942521956, 3}, {465735389253857123, 1}, {466631422970127920, 2}, {467870374922948736, 3}, {468115237644725800, 3}, {472369124048495753, 3}, {478290019479375202, 0}, {480892195976386872, 3}, {484326175962445163, 2}, {486365072260726313, 3}, {487428300374279401, 0}, {488441932932924225, 1}, {490710029825349556, 3}, {492056482150412530, 1}, {492561297140740547, 1}, {492661594913071611, 1}, {496162732100763266, 0}, {496776120299488581, 1}, {496886168345737807, 1}, {496944015270898995, 1}, {500933203559990987, 0}, {500944429247223971, 0}, {501233423162925204, 1}, {502032307740418321, 1}, {504835160439230767, 0}, {507177783332912449, 2}, {509217252508558304, 0}, {511469944185190158, 2}, {516837030458121969, 3}, {521925448109788045, 3}, {523908475000446487, 1}, {524951016726211808, 2}, {528761500497662498, 1}, {530386852072226610, 3}, {531210891015723707, 3}, {531381629741544799, 3}, {531980235083892768, 0}, {533683300186882498, 2}, {536185626940071484, 0}, {536311087107268072, 0}, {539163924238980929, 2}, {542062452577694166, 1}, {543793928320028734, 2}, {544294882063238804, 3}, {544520562553653066, 3}, {545586338841603045, 0}, {547798002764098876, 2}, {549084177158104777, 3}, {549773321515881448, 0}, {551694351184988551, 2}, {552222230753806118, 2}, {553862270397822915, 3}, {554181802875127421, 0}, {554372274652098251, 0}, {554613564275538672, 0}, {555666710752354453, 1}, {555715937471231784, 1}, {556751813715743981, 2}, {557889252770278838, 3}, {558888658462600978, 0}, {560679865241411885, 1}, {560821241126552884, 2}, {560883684809180368, 2}, {561230303704181607, 2}, {561299513492190691, 2}, {561885852477666278, 3}, {562270698796260005, 3}, {563097743169968319, 0}, {566339162869987490, 3}, {567423005556333805, 3}, {569017886015882980, 1}, {574668401802865252, 2}, {576556559454682664, 0}, {578869688591670360, 2}, {579253224818210416, 2}, {581292904207481109, 0}, {583940302802572395, 2}, {586447346893402641, 0}, {588074243027949133, 2}, {589185089630973955, 3}, {589982625287611892, 0}, {592246222838598113, 2}, {594144723906925562, 3}, {597441805673619566, 2}, {598160347685025644, 3}, {599132234851720624, 0}, {600173457889012769, 1}, {602543600201171412, 3}, {603374183917318025, 3}, {603659977879060406, 0}, {605210368520521965, 1}, {605775777190750153, 2}, {610139844400468507, 1}, {610826417599315750, 2}, {611450649805029532, 3}, {612538775579738495, 0}, {612582765730350509, 0}, {614277641753975970, 1}, {614318399645036152, 1}, {618794779997173932, 1}, {620387221110690724, 3}, {621485790692477322, 3}, {621955212201632827, 0}, {625473651597746964, 3}, {625687413740554443, 3}, {626636361892924637, 0}, {628814986250236191, 2}, {630159090784017938, 3}, {631267881297488377, 0}, {635276331565324742, 0}, {637313904146438327, 2}, {641485642622129968, 1}, {642425508859726606, 2}, {644060931085521154, 0}, {646770974357644912, 2}, {647305426889022548, 2}, {656242490540023065, 2}, {656391054161585752, 2}, {656391515615580827, 2}, {656526856410007710, 3}, {657268804481801140, 3}, {657311090456727485, 3}, {662003116984943022, 3}, {665340772538023648, 2}, {666010218889008350, 3}, {669956786553297627, 3}, {671730026270554187, 0}, {672030839275917387, 0}, {673620386362313513, 2}, {675056178048049936, 3}, {675854453175001376, 0}, {676687808166080642, 1}, {678599629935822487, 2}, {680445808036521720, 0}, {680467331078527998, 0}, {681967482214983606, 1}, {683755129463380331, 3}, {686398273273041108, 1}, {690124000269978738, 0}, {691252595381399909, 1}, {692294179800897334, 2}, {692444220637995183, 3}, {692644109289117284, 3}, {695168590355871183, 1}, {698916452129699483, 0}, {706461694073357290, 3}, {707535734886170166, 0}, {707711359274050554, 0}, {709688946484886124, 2}, {714400186386000571, 2}, {715892538453103234, 3}, {716342211815932418, 0}, {717654477562387405, 1}, {717690824093382499, 1}, {718495496558931256, 2}, {718719347157522770, 2}, {719916909403588123, 3}, {721411054070512211, 0}, {721724488120173520, 1}, {723201529057232872, 2}, {723504070534086590, 2}, {723631146530792730, 2}, {725626380395340423, 0}, {726651803167631282, 1}, {727221375081964863, 1}, {727449677177446247, 2}, {738914743692549036, 0}, {739463808082457414, 0}, {745308808409863365, 1}, {745519897470161955, 2}, {745841945296493090, 2}, {748623699537764331, 0}, {749095217428427498, 1}, {755642148242486948, 3}, {755681272866141856, 3}, {755726243276216906, 3}, {757425635468859837, 0}, {761253702785240269, 0}, {763392550528597251, 2}, {765708529370205869, 0}, {766925528466831056, 1}, {768783710055022593, 2}, {772555732763092154, 2}, {774599870418544994, 3}, {774756648255550280, 0}, {778985717569275446, 3}, {779502023171210622, 0}, {791075276130010146, 2}, {792700662267082069, 0}, {792907708367489662, 0}, {793428309236441723, 0}, {793815434919859989, 1}, {795600785879462404, 2}, {797294204554062394, 0}, {797500947937199223, 0}, {804391514991094773, 2}, {804555761589970083, 2}, {805356410022526770, 3}, {811407746428888897, 0}, {812814452165280173, 1}, {813090728865908337, 2}, {813818575717250876, 2}, {814055479504359623, 3}, {817684401878026413, 2}, {819021066271244200, 3}, {819025671360939567, 3}, {819428459126520906, 3}, {821149774937924658, 1}, {824725906020094214, 0}, {827601593006385334, 3}, {828473548921635080, 3}, {829354102130592378, 0}, {835352941317790791, 1}, {839256216425754304, 1}, {839503776742338198, 1}, {839509316896647706, 1}, {840288121521900184, 2}, {840602289338043168, 2}, {841248540516677308, 3}, {842319433657874318, 0}, {843624166482924976, 1}, {848900957253965811, 1}, {848996694706215701, 2}, {850624083803699649, 3}, {851862610389870785, 0}, {854477707945585227, 2}, {857098812080930139, 1}, {858271201847869287, 2}, {858333992539229269, 2}, {859220453935501236, 3}, {859378934821172184, 3}, {859958068469395300, 3}, {860685480897895971, 0}, {861106151214369387, 0}, {861385654789185798, 1}, {863783611178845441, 3}, {864186408820718161, 3}, {864861725479381028, 0}, {875193440960042854, 1}, {875443999704706258, 1}, {876262774402417068, 2}, {878521627235279817, 0}, {879196032588393852, 0}, {879582419694004114, 1}, {881254706009257711, 2}, {882227072856718050, 3}, {882598261022221814, 3}, {883386231004650614, 0}, {883711822615720872, 0}, {884169628483225217, 1}, {885632651921176341, 2}, {888545003464860710, 1}, {892883629136550867, 1}, {893248822015722249, 1}, {895320277096324620, 3}, {896171356496767050, 3}, {899131368000361909, 2}, {902133821765135673, 1}, {902551065938994026, 1}, {904600659399261346, 3}, {905138511533056316, 3}, {906237837296102071, 0}, {908961080316058823, 3}, {911259355609173590, 1}, {915701087126056860, 1}, {925712127334585174, 2}, {925935340277953126, 2}, {928489657013724568, 0}, {929850752341248740, 1}, {933205556752153021, 0}, {933757557593860159, 1}, {934547105018699582, 2}, {936550465252300093, 3}, {938094085229677744, 1}, {941078103132403724, 3}, {944202864752274667, 2}, {946573670118938997, 0}, {947280115192488894, 1}, {948953595599416460, 2}, {952687947479188423, 2}, {953801513254440744, 3}, {959084680228600613, 3}, {961080277765178814, 1}, {961392430845232914, 1}, {962301735301218435, 2}, {963606859868551133, 3}, {963733347542747706, 3}, {967509558281347036, 3}, {969469723070082297, 1}, {970659689666235934, 2}, {971574222404564943, 2}, {977688457852410587, 0}, {977725620600488043, 0}, {977891417952875348, 0}, {978776852477553230, 1}, {980812901908535790, 3}, {982060729801511801, 0}, {986123554861458148, 3}, {988480590191737773, 1}, {989690216748963637, 3}, {989739120193804727, 3}, {990211916120153389, 3}, {990366869750361980, 3}, {993067487641579939, 2}, {993258725591061993, 2}, {994251186392734587, 3}, {996323108122612311, 0}, {997094925100662137, 1}, {998424394203610944, 2}, {1002957240404445546, 2}, {1004611925672609513, 0}, {1005958133829829425, 1}, {1015303344578987207, 1}, {1015386475308003467, 1}, {1022965241305208866, 0}, {1024824968912746329, 2}, {1024933666632532595, 2}, {1025119456145766159, 2}, {1025802827985629416, 3}, {1028205664953556204, 1}, {1028747619818027600, 1}, {1029293419077591562, 2}, {1032204017291942790, 0}, {1032746349041337455, 1}, {1040562332555460063, 0}, {1041185087788456918, 0}, {1050390365100800311, 0}, {1050906963526943019, 1}, {1054599031332574628, 0}, {1055544259175965535, 1}, {1058939659577574223, 0}, {1061009742995815545, 2}, {1061937175004805191, 3}, {1062065604962796920, 3}, {1063988680536248228, 1}, {1064849522879585016, 1}, {1065471006309631872, 2}, {1065572022482167790, 2}, {1067489266685813773, 0}, {1068459445868032838, 0}, {1068824419602182764, 1}, {1069524643026181952, 1}, {1070955674568086375, 3}, {1075257707763902225, 3}, {1075274683913189606, 3}, {1078394678117926137, 1}, {1080511552817548049, 3}, {1080828374048567057, 3}, {1083285357439896990, 2}, {1085682992432990874, 0}, {1089021699717885159, 3}, {1095757044016364031, 1}, {1096785435765046959, 2}, {1096976902901982192, 2}, {1097196481343585659, 2}, {1098600186416161004, 3}, {1098971261621454594, 0}, {1101574533919586285, 2}, {1103540907477374444, 0}, {1104458717241815284, 0}, {1105716337238831748, 2}, {1107422145359961753, 3}, {1109695895264635363, 1}, {1109881179802283759, 1}, {1112172449402580714, 3}, {1112780658531095458, 0}, {1112842949509741101, 0}, {1112998305966503665, 0}, {1113042365779287127, 0}, {1113836218512251067, 1}, {1114071050576470904, 1}, {1123959651517445868, 2}, {1125695354111055758, 3}, {1126032946029330507, 0}, {1126630237720891742, 0}, {1129976329047212134, 3}, {1133252146556133090, 2}, {1140539915437060710, 1}, {1143627388781178725, 3}, {1144177852994490447, 0}, {1145556519401723288, 1}, {1147708699943533897, 3}, {1149158270324655872, 0}, {1149259869158207995, 0}, {1151195231591349858, 2}, {1153364818832711412, 0}, {1155035678806979322, 1}, {1155221942760407103, 2}, {1156092594311720747, 2}, {1161917366711740583, 3}, {1162466388048355522, 0}, {1162477377748463608, 0}, {1165449813666723245, 3}, {1166272088269700786, 3}, {1167384217230804995, 0}, {1171761989772819802, 0}, {1172643630703075609, 1}, {1178966281490397405, 3}, {1182216014295731409, 2}, {1189676940708782892, 0}, {1190362085911399083, 1}, {1190892817511656345, 1}, {1190951433392755456, 1}, {1191953785197365653, 2}, {1195709260847563914, 2}, {1198837813983177895, 0}, {1200078427691073336, 1}, {1200930084466958572, 2}, {1205775714778668544, 2}, {1206507629391066206, 3}, {1207484797551178924, 0}, {1208660885988600982, 1}, {1210375749568901747, 3}, {1210734825750224520, 3}, {1215056712177857526, 3}, {1215729586770795501, 3}, {1217088958690918882, 0}, {1217098229391488706, 1}, {1217610351396086751, 1}, {1218443577247217823, 2}, {1219148923633096877, 2}, {1221418521687777852, 0}, {1222548357771775685, 1}, {1224424985739225678, 3}, {1228491195436101511, 3}, {1228993611345388479, 3}, {1236175304256914233, 1}, {1237331744326455050, 2}, {1239968328321956612, 1}, {1239986683838370037, 1}, {1241529130044755830, 2}, {1242024406094218682, 3}, {1243032970746089399, 0}, {1247548208502798631, 0}, {1250803680837412187, 2}, {1251115187899783177, 3}, {1251289280768314584, 3}, {1255186395986322203, 2}, {1255804769636616025, 3}, {1257623871650212619, 0}, {1259606906059702628, 2}, {1260280508665885255, 3}, {1263647334904245808, 2}, {1271213949009556448, 1}, {1272056078013317084, 1}, {1272092814980824469, 1}, {1273353301268581483, 2}, {1275169597522355515, 0}, {1278365862400192935, 3}, {1279798977652505305, 0}, {1282461421617204637, 3}, {1282693600841221350, 3}, {1283918961184995170, 0}, {1286532647834460686, 2}, {1287354075783632460, 3}, {1288133411826919595, 0}, {1289063085949241225, 0}, {1293656137109978348, 0}, {1293732643300419368, 1}, {1296400053830805208, 3}, {1296653902803072700, 3}, {1298276198882856715, 1}, {1300408848478546790, 2}, {1302015108993484551, 0}, {1304432326433248958, 2}, {1304588447361659055, 2}, {1306169389037231067, 0}, {1307776333242619523, 1}, {1308900095114529310, 2}, {1312266611820928577, 1}, {1318936579949486103, 3}, {1321812264368916196, 2}, {1324220586312640505, 0}, {1325540251631451370, 1}, {1327529584372033689, 3}, {1328855678098796757, 0}, {1329737504329581185, 1}, {1335175570311062918, 1}, {1339196388087145225, 1}, {1342880564241768251, 0}, {1345926424411966953, 3}, {1347428924487193261, 0}, {1347650998776027241, 0}, {1348917497307860143, 2}, {1350140107075203804, 3}, {1352305920148433607, 1}, {1353726696190194404, 2}, {1355081412324984098, 3}, {1358248941840879590, 2}, {1364409949186090703, 3}, {1367310795188030679, 2}, {1368817758288194117, 3}, {1370123731962541445, 0}, {1371150172551586333, 1}, {1372856580863141020, 3}, {1373051972406704766, 3}, {1373574806823150328, 3}, {1378050135726228161, 3}, {1380259432538718654, 1}, {1381239029642314879, 2}, {1382417967811923706, 3}, {1384236866527101123, 1}, {1384267543871591392, 1}, {1387178854716121004, 0}, {1387397682995301202, 0}, {1394553221563288201, 2}, {1398383378572317671, 2}, {1399792476358252050, 3}, {1401418393805019626, 0}, {1402117131624322900, 1}, {1402879261428322998, 2}, {1403500865619565599, 2}, {1404682894575373777, 3}, {1404806749482079980, 3}, {1406103043401372665, 0}, {1409276309328636773, 3}, {1410668021684646948, 0}, {1413972876688782008, 3}, {1414346690926109613, 0}, {1414928731747542872, 0}, {1416356195229226315, 1}, {1416504293928159276, 2}, {1417189303368880684, 2}, {1420600548151686806, 1}, {1424964360742966635, 1}, {1427522872414722372, 3}, {1431883904866309910, 3}, {1434363926078047252, 1}, {1436417281956555896, 3}, {1441626700086218192, 0}, {1442879627917665690, 1}, {1443818553270615499, 2}, {1444328484773338167, 2}, {1445543524566241608, 3}, {1450125052617487218, 3}, {1451172824164603565, 0}, {1451563952130535978, 1}, {1451564548536605484, 1}, {1454078886646757143, 3}, {1454606448084823166, 3}, {1455229561651469301, 0}, {1455376008647936190, 0}, {1457864801814536524, 2}, {1458260161054384761, 3}, {1460974090967837316, 1}, {1462482973820468724, 2}, {1462678069761419850, 3}, {1463249364939387841, 3}, {1464285581219537996, 0}, {1469435370711233007, 1}, {1471237502676188461, 2}, {1472107924069382547, 3}, {1474215929724959893, 1}, {1481518018950793244, 3}, {1482972720172878059, 1}, {1485242396008881197, 3}, {1485271721606994716, 3}, {1486276198525983554, 0}, {1488661914267733065, 2}, {1490063910931054670, 3}, {1490076144037419359, 3}, {1491693315398379687, 0}, {1493930193617735057, 2}, {1495024674561591180, 3}, {1497244913564476265, 1}, {1503076669528790858, 3}, {1514318061541755182, 0}, {1514825661871054352, 1}, {1515254787027393594, 1}, {1519313438571860645, 1}, {1527623267955373855, 0}, {1529800249090541766, 2}, {1531466454557379986, 0}, {1538024964073812442, 2}, {1538305633461972611, 2}, {1538420655160639269, 2}, {1540776694169428903, 0}, {1541257968642167797, 0}, {1541423753282510129, 1}, {1542256467787600283, 1}, {1542859235863580185, 2}, {1544038191617474684, 3}, {1544869788517694714, 0}, {1546856695214145176, 1}, {1548798594257444732, 3}, {1549309662595324668, 0}, {1556792364019312007, 2}, {1557581171296230318, 3}, {1559474092243938650, 1}, {1561179576101225726, 2}, {1562267537644065439, 3}, {1562964254403371708, 0}, {1563485741465461080, 0}, {1567146212701192752, 3}, {1568228349793808253, 0}, {1569039468256256562, 1}, {1569907446894739842, 2}, {1582523014668799630, 1}, {1582934409324620386, 1}, {1589234639648872732, 3}, {1592150242269670743, 2}, {1594340452056863036, 0}, {1596045719363032197, 1}, {1597289653962341551, 2}, {1598695035110472052, 3}, {1599576409118296865, 0}, {1599852532912659023, 0}, {1601509798243168151, 2}, {1605422202025241430, 1}, {1609763743127140922, 1}, {1611532661557778301, 3}, {1612074294355160128, 3}, {1612364221800206638, 0}, {1616918073161810848, 0}, {1618313144051551912, 1}, {1621300337206651673, 0}, {1621584661605830430, 0}, {1624261576589018403, 2}, {1625991241159469153, 0}, {1629449308394550959, 3}, {1631280909760658597, 0}, {1631569970688956569, 1}, {1631981332465356379, 1}, {1635396583089667468, 0}, {1636190769590009107, 1}, {1636331153397267766, 1}, {1637321391733825015, 2}, {1642157344648127337, 2}, {1645963756409166326, 1}, {1647876473784447240, 3}, {1649364507682110219, 0}, {1654943980327399792, 1}, {1655875831343947428, 2}, {1657005815545785647, 3}, {1663427164777559453, 1}, {1664757933041586988, 2}, {1665187587282623339, 2}, {1665798635809173785, 3}, {1676223687738663955, 0}, {1676576784339641631, 1}, {1677713773937543754, 2}, {1679605669637525582, 3}, {1680113678999614981, 0}, {1683114076245164749, 2}, {1684883469382854259, 0}, {1685413085635042295, 0}, {1685636095767489365, 1}, {1687960475414833568, 3}, {1688370358417382632, 3}, {1688916584333141057, 0}, {1689051313101462494, 0}, {1690061097090002333, 1}, {1690594162622540928, 1}, {1690674022256346341, 1}, {1691204926006975703, 2}, {1693473161040945604, 0}, {1696109183115885576, 2}, {1697562721464187664, 3}, {1698135496890692840, 0}, {1699114282818758467, 1}, {1700510067828427876, 2}, {1700514329078996739, 2}, {1701319489031149236, 3}, {1704291712998945652, 1}, {1704839074194920736, 2}, {1705240537533440738, 2}, {1706111423045900983, 3}, {1706118807388848261, 3}, {1710906493552684399, 3}, {1712650752558735340, 1}, {1713721224870738440, 2}, {1715062791537722713, 3}, {1715325788738796308, 3}, {1717569349928912994, 1}, {1722462405438661312, 1}, {1724069996931357019, 3}, {1726695619902932254, 1}, {1732086766094171258, 2}, {1743456421918443792, 0}, {1743919402225504573, 0}, {1744421355624303707, 1}, {1745478346290347545, 2}, {1749350372373441488, 1}, {1751643584586604687, 3}, {1752522540869410670, 0}, {1753545218943935422, 1}, {1753966982237063458, 1}, {1754341998378326322, 2}, {1754611850019271074, 2}, {1754733336441909569, 2}, {1755754271040716255, 3}, {1756675024418822077, 0}, {1757286671389717993, 0}, {1758880903514880813, 2}, {1759218920988292014, 2}, {1762008893023215563, 0}, {1762657471677942089, 1}, {1763984786690737875, 2}, {1765669432992014693, 0}, {1767668741349669867, 2}, {1768934859242607960, 3}, {1772154580420775975, 1}, {1773288756206266287, 2}, {1774457240205898202, 0}, {1777429312201247201, 2}, {1778557028194634154, 3}, {1780836547286869999, 1}, {1783041429131962847, 3}, {1783886775919968269, 0}, {1786432829603837256, 2}, {1789144601278502758, 1}, {1791840132956910642, 3}, {1792623996530670744, 0}, {1793963495385336209, 1}, {1794559612137751486, 1}, {1795121974086810382, 2}, {1797071271526879895, 0}, {1798228779848436515, 1}, {1800989224958926691, 3}, {1808323284489291203, 2}, {1809794557324136694, 3}, {1812860392084017672, 2}, {1813175122091933019, 2}, {1815005475251028405, 0}, {1815835533330475455, 0}, {1817513196550999235, 2}, {1822986072271453107, 3}, {1823729927791719392, 3}, {1824026578138183898, 0}, {1834516432588114945, 1}, {1836674077893607520, 3}, {1838460038563224348, 0}, {1839190532130847758, 1}, {1840847394184144433, 3}, {1845449733049728993, 3}, {1845812409014775527, 3}, {1850197817508690483, 3}, {1850234532054411344, 3}, {1850295190310069365, 3}, {1850489936829771291, 3}, {1854780136859916723, 3}, {1855000518509739830, 3}, {1855277795554843688, 3}, {1857091294769821287, 1}, {1860348451187850559, 0}, {1868540031370038668, 3}, {1873009988545680134, 3}, {1873578126483341325, 0}, {1874079854203843783, 0}, {1877537482152735761, 3}, {1877578913641367408, 3}, {1884954267934626405, 2}, {1885247068441945414, 2}, {1886595055962931020, 3}, {1888420607623793057, 1}, {1889042245272934087, 1}, {1890850676047892556, 3}, {1895587662480657826, 3}, {1895795960219106941, 3}, {1896313334551158327, 0}, {1897257524438295283, 1}, {1902610113851977991, 1}, {1903751297035882168, 2}, {1905593435891678536, 0}, {1906062485406968362, 0}, {1908972624752386690, 3}, {1909416093687517012, 3}, {1910502098048984037, 0}, {1913063781993275919, 3}, {1913739619631529557, 3}, {1914784270455993301, 0}, {1916323113388973127, 2}, {1916691605488781670, 2}, {1917536737215622089, 3}, {1920852431231078915, 2}, {1921157041622943651, 2}, {1921245036485593838, 2}, {1922217526967870291, 3}, {1923492356840387470, 0}, {1924914014411934442, 1}, {1926075864558563321, 2}, {1927706707312966382, 0}, {1927829596674533023, 0}, {1929517350415868639, 1}, {1929925893751789604, 2}, {1930385169294511045, 2}, {1931843700584769296, 3}, {1932327528167692711, 0}, {1932463622659522205, 0}, {1933308397046355035, 1}, {1934682859155462898, 2}, {1937239815325678608, 0}, {1940449000618018928, 3}, {1942075580176447377, 0}, {1944659937569877049, 3}, {1946481587806225383, 0}, {1946646054528896893, 0}, {1946762991795262422, 1}, {1947652641404803424, 1}, {1948879964504746475, 2}, {1951421442121855393, 1}, {1956294880541934048, 1}, {1959092776460949437, 0}, {1959388868105189237, 0}, {1959924580575848220, 0}, {1961532401774648744, 2}, {1962722263775069931, 3}, {1965360840295464456, 1}, {1966627651009049412, 2}, {1967544277611116532, 3}, {1967618869007279712, 3}, {1969789791121359174, 1}, {1973522326953101916, 0}, {1974119876556401010, 1}, {1977827082687434826, 0}, {1979474482463219887, 2}, {1982111314202016634, 0}, {1982778271857132210, 1}, {1988018297876870438, 1}, {1990204698749745565, 3}, {1991073766335982620, 0}, {1994032527043715463, 3}, {1994818308703674801, 3}, {1995061051082325577, 3}, {1997113348408442310, 1}, {1999078881456502566, 3}, {2000394933619766711, 0}, {2001649524261917768, 1}, {2002220664428654473, 2}, {2010755535977040659, 1}, {2011611258746293387, 2}, {2017059831021637818, 3}, {2017361971194385422, 3}, {2018600927603271389, 0}, {2019018434705797619, 1}, {2019767793884827698, 1}, {2023856286326487762, 1}, {2025476593669189764, 2}, {2026248167944608660, 3}, {2027230776019468468, 0}, {2028336964576645850, 1}, {2029726497975634795, 2}, {2030121649743892300, 3}, {2032427348072681901, 1}, {2033859291499173203, 2}, {2037364473579107846, 1}, {2037731274149687583, 1}, {2038909866534233882, 2}, {2042947124590187001, 2}, {2045126817847517348, 0}, {2045608862713241073, 0}, {2047470413794678109, 2}, {2047526262087379413, 2}, {2048149343483780953, 3}, {2050724059043279348, 1}, {2051573162235110655, 2}, {2052292098375195633, 2}, {2055159237887132923, 1}, {2055935761964482527, 2}, {2057351520428769677, 3}, {2057968463543241521, 3}, {2060458934549147135, 2}, {2061021775852935788, 2}, {2062030053579463457, 3}, {2062466841257187767, 3}, {2062958173290906988, 0}, {2063770370078969743, 0}, {2064812986130755772, 1}, {2065009300959985242, 2}, {2068222904914639301, 0}, {2070047604173407488, 2}, {2071368820688747993, 3}, {2072234148640698596, 0}, {2072745192389883601, 0}, {2073538256022502110, 1}, {2074645637035817522, 2}, {2076413005833620879, 0}, {2078985226503809537, 2}, {2079729471625994340, 3}, {2080637927402485838, 3}, {2081350534423472833, 0}, {2085756405478394474, 0}, {2086000883676150572, 0}, {2089245836534955715, 3}, {2089353920849553554, 3}, {2089793043006101152, 0}, {2094163138138120504, 3}, {2095619800171161797, 1}, {2097741731246290187, 3}, {2099310436397692136, 0}, {2099759775829597507, 0}, {2100671181442074773, 1}, {2102243580106409111, 3}, {2102458379346049041, 3}, {2108040728240666622, 0}, {2108630806204455492, 0}, {2111506113223537678, 3}, {2113609219352315034, 1}, {2114358141468174562, 1}, {2114446445393174009, 2}, {2115495849030367870, 2}, {2115835362754613108, 3}, {2117449501413282848, 0}, {2118425314004844420, 1}, {2122540397581077008, 1}, {2124412794412385511, 2}, {2124760302722833621, 3}, {2125392675097197794, 3}, {2125540223666299288, 3}, {2132067412399680819, 1}, {2133790780277651741, 3}, {2133824145787987614, 3}, {2137711397017115015, 2}, {2137785611590843728, 2}, {2141088885788980201, 1}, {2142691165443140155, 3}, {2144758273000772011, 0}, {2145747129304148247, 1}, {2146987675126980373, 2}, {2152877175356655509, 0}, {2154770295320264124, 1}, {2155969914125641507, 2}, {2156571083762545331, 3}, {2160904111632154517, 3}, {2162854547064751339, 1}, {2163756529306353658, 1}, {2164289638652118406, 2}, {2165498862684946919, 3}, {2167255763453934226, 0}, {2169277897073662877, 2}, {2171016813670421951, 0}, {2171395783435526075, 0}, {2173277617212055909, 2}, {2177911771237386394, 2}, {2178724703213895395, 3}, {2181335670246505309, 1}, {2185005309073901372, 0}, {2185067350786174253, 0}, {2190703127780560028, 1}, {2194465897849084042, 1}, {2196759984502755922, 3}, {2198012497691454757, 0}, {2198738782269933396, 0}, {2199547456013532204, 1}, {2201207357179402530, 3}, {2203106756112700978, 0}, {2203353888936245906, 0}, {2207063012462218110, 0}, {2207467518381119303, 0}, {2208121136162133502, 1}, {2210655146895577907, 3}, {2213680692102252663, 2}, {2214710054544757991, 3}, {2215568872963026502, 3}, {2216271935634098934, 0}, {2217996514155256405, 1}, {2220946260301041719, 0}, {2221527757268392661, 1}, {2227522142602323759, 2}, {2228398927083210419, 3}, {2228747564084586798, 3}, {2233044195768004797, 3}, {2234559833801756429, 0}, {2234726675445484237, 0}, {2234928115260701999, 1}, {2238752974981530712, 0}, {2238801118171206190, 0}, {2241120924748579541, 2}, {2243142255386054977, 0}, {2244071294080495237, 1}, {2245377241549771619, 2}, {2248114341304047594, 0}, {2250380280226333243, 2}, {2251708078441275933, 3}, {2253454098611817689, 1}, {2253991235881223902, 1}, {2255741720806302139, 3}, {2257557576713061641, 1}, {2259501396667277667, 2}, {2264241181899746198, 3}, {2264749090329145115, 3}, {2266012157266024767, 0}, {2268462385797138312, 2}, {2273029876326768443, 2}, {2274777688076642652, 0}, {2276824438056155480, 2}, {2278519180919047240, 3}, {2279023830500096268, 0}, {2282213406662803980, 3}, {2282273797294037842, 3}, {2282976378897651913, 3}, {2283504484428765791, 0}, {2291546503161845062, 3}, {2291572111288603505, 3}, {2292409904601552080, 0}, {2293113280352873494, 0}, {2294122475708981176, 1}, {2294234201942509742, 1}, {2296279427432905932, 3}, {2298861869255406419, 1}, {2299216679127802466, 2}, {2299757314951607684, 2}, {2303303803946912624, 1}, {2303905763870364788, 2}, {2305786485934368835, 3}, {2307091314388146200, 1}, {2307165296271925968, 1}, {2314769323078349734, 3}, {2323324255863457448, 3}, {2325797337688793315, 1}, {2328086925039006711, 3}, {2328689956156821661, 0}, {2329107869150758172, 0}, {2329624156079839349, 1}, {2329668520797451478, 1}, {2330630090949070351, 2}, {2331290873189060905, 2}, {2331712960409419724, 2}, {2336835159949449510, 3}, {2339438951562803527, 1}, {2341552435338439783, 3}, {2342666698370816160, 0}, {2346375421856884525, 0}, {2348335974982666089, 1}, {2350079285614523595, 3}, {2351673035477543941, 0}, {2353236410327271024, 2}, {2353334692556161997, 2}, {2354847158095309636, 3}, {2357503292681763718, 1}, {2358962825949288247, 3}, {2362012222781705014, 1}, {2362192470525375733, 2}, {2363548503534177471, 3}, {2364964208926739349, 0}, {2364984617763080065, 0}, {2366292172941057674, 1}, {2369233438093683600, 0}, {2369661455122381900, 0}, {2373288232406530506, 3}, {2374846948125402513, 1}, {2376964498278064864, 3}, {2381247964487272646, 2}, {2382338625359378117, 3}, {2386580687654686883, 3}, {2387304192572085767, 0}, {2391914954532445471, 0}, {2394058413024949413, 2}, {2395585502358415412, 3}, {2397902786314999500, 1}, {2401452743152199348, 0}, {2403815083795836298, 3}, {2404491811373910779, 3}, {2405011667061971612, 0}, {2406196883562974963, 1}, {2406471351344320384, 1}, {2406871345258971158, 1}, {2408429439933862720, 3}, {2414862267573462573, 0}, {2420068100786391340, 1}, {2421568807384469382, 2}, {2422547369165411062, 3}, {2424066531948784486, 1}, {2426577126045088353, 3}, {2428836792551506375, 1}, {2429276828787153458, 1}, {2430529664464207989, 2}, {2430896623150313760, 3}, {2431367072608388491, 3}, {2431683881917828749, 3}, {2431879362670788688, 3}, {2432283942664407013, 0}, {2432326168490032839, 0}, {2433954011467600146, 1}, {2434199041146110021, 2}, {2434466184696633782, 2}, {2435773018803773701, 3}, {2436923351665174906, 0}, {2437808689521092135, 1}, {2438823054316013357, 2}, {2438946896501257890, 2}, {2440906430462170926, 3}, {2444598071916309839, 3}, {2445093168862303470, 3}, {2451147558538712382, 1}, {2455583005749532232, 0}, {2455662914092736100, 1}, {2456020378434434293, 1}, {2462724369151433378, 3}, {2462834811602523909, 3}, {2463424786223129095, 3}, {2465307774524670296, 1}, {2465760423296170162, 2}, {2466122210378917951, 2}, {2467044285986958421, 3}, {2470904541582627672, 2}, {2471999810922273791, 3}, {2477639286958802538, 0}, {2478525812782775062, 1}, {2483718328863080542, 1}, {2496360792245722041, 1}, {2497143467543750984, 1}, {2499884280123589992, 0}, {2500028924844180328, 0}, {2500283785880651154, 0}, {2501454724139367664, 1}, {2504448627158541538, 0}, {2504847052864715234, 0}, {2505551581785959673, 1}, {2508933052051901263, 0}, {2509655595311508378, 1}, {2510664853925284771, 1}, {2514838403717412536, 1}, {2515940280332096506, 2}, {2517591514635144927, 0}, {2520497157330179474, 2}, {2527061984955192280, 0}, {2529680322869109038, 2}, {2529790129620983307, 2}, {2530203331469325359, 3}, {2530355359206443934, 3}, {2534200853592486176, 2}, {2534303282622218582, 2}, {2535164582684345706, 3}, {2535366566823422233, 3}, {2535705539768053478, 0}, {2535775646972511588, 0}, {2536153696112825851, 0}, {2538618081453212263, 2}, {2542816730339936264, 2}, {2545627011064020299, 0}, {2546462063511371390, 1}, {2547037579654373658, 2}, {2549750940817655706, 0}, {2549778925621307832, 0}, {2553498590878748960, 3}, {2556144963661568652, 2}, {2558463272079048142, 0}, {2558723170797460449, 0}, {2559228007059623930, 1}, {2562486897432441957, 3}, {2562770906079014119, 0}, {2564817434086127843, 2}, {2564951538593907755, 2}, {2566222442735478578, 3}, {2567517694216761028, 0}, {2568015560315586994, 0}, {2569320940991805551, 2}, {2569420726395644236, 2}, {2570441294823249065, 3}, {2574434797060318240, 2}, {2578241662485981646, 1}, {2579545784127663565, 3}, {2580461106497514381, 3}, {2581695239002458057, 1}, {2581909222804939588, 1}, {2583007097462047814, 2}, {2587112489049674511, 1}, {2590170021285918711, 0}, {2593640489718234785, 3}, {2595624544247016648, 1}, {2596351564679594002, 2}, {2599759378792195557, 1}, {2601165307394814877, 2}, {2601815154755466835, 2}, {2605598839509508371, 2}, {2606692450807190811, 3}, {2610951885650626733, 2}, {2614692013380081203, 2}, {2616826221426928716, 0}, {2617690121682005379, 0}, {2622490204131674973, 1}, {2625575217743043684, 3}, {2634461472579444998, 3}, {2635815124457594140, 1}, {2636194650119474953, 1}, {2638253349147643366, 3}, {2647035632560182683, 3}, {2648058577250918701, 3}, {2648950295752540988, 0}, {2650074027117480961, 1}, {2652049559061213827, 3}, {2653202925648673114, 0}, {2655671937238131365, 2}, {2657520682757578172, 0}, {2666212218483767809, 0}, {2667137763686944838, 0}, {2669102904289985338, 2}, {2674475996987390602, 3}, {2675585244415597173, 0}, {2677199513735423099, 1}, {2679566211267948232, 3}, {2680416926292157197, 0}, {2680528703268640208, 0}, {2680811156203768409, 1}, {2680915574305481678, 1}, {2683456476753756613, 3}, {2685623252105967824, 1}, {2686359949176722197, 1}, {2690961240338209707, 2}, {2691181709153216317, 2}, {2691711657162475044, 2}, {2693526104072385190, 0}, {2694010528783099020, 0}, {2695083755138402305, 1}, {2697674984229467416, 0}, {2700734775711949772, 2}, {2705807250171775702, 3}, {2706057281606222296, 3}, {2712266804197956772, 0}, {2713623080116796607, 2}, {2719198033500690999, 3}, {2723450134372576974, 2}, {2723909033824677888, 3}, {2724287676937014168, 3}, {2724387710015603466, 3}, {2724759159169259940, 0}, {2737261301435517004, 3}, {2738823627901346124, 0}, {2741398628174060450, 2}, {2743994062301557593, 1}, {2749376327638446782, 1}, {2750862636124883597, 3}, {2751251495790745295, 3}, {2753197909957120256, 1}, {2754185900235614523, 2}, {2756310561073741291, 0}, {2759431311846195548, 2}, {2760133346949424209, 3}, {2761617674770708877, 0}, {2765733986876946854, 0}, {2766679642997634862, 1}, {2767197662113150553, 1}, {2767771878270971276, 2}, {2769522714752031731, 3}, {2771175863079776853, 1}, {2776622740325377108, 2}, {2780015829871747136, 1}, {2780054321163820754, 1}, {2780430617716026729, 1}, {2780633109268615078, 1}, {2782880515675979817, 3}, {2784433032889329891, 1}, {2785103624996640646, 1}, {2786997073576183085, 3}, {2789885466669654417, 1}, {2790457233879669706, 2}, {2791090177013426788, 2}, {2791209212034089992, 3}, {2794486033072035993, 2}, {2794974019087075836, 2}, {2799025748014484496, 2}, {2804071962427612307, 2}, {2805510684508922545, 3}, {2805529119175269965, 3}, {2806001319051376402, 0}, {2807444582417683367, 1}, {2807991225952968108, 1}, {2808402241710171507, 2}, {2808512605043590064, 2}, {2808867127988664760, 2}, {2808907409659557936, 2}, {2809766678327956313, 3}, {2810116705398371018, 3}, {2810632065720403207, 0}, {2812722291926807750, 2}, {2813517957255188132, 2}, {2814838091645615265, 0}, {2814864010004493567, 0}, {2815094356895474885, 0}, {2817919826003254115, 2}, {2818515408154101807, 3}, {2819245983651230926, 3}, {2819285527875791334, 0}, {2821516635140975869, 2}, {2825282157329577481, 1}, {2827040147131093658, 2}, {2829356330824726587, 0}, {2830433674949438859, 1}, {2833367299815299138, 0}, {2833879988422799546, 0}, {2833986686334959987, 1}, {2836343550366458287, 3}, {2836393609560694794, 3}, {2836910113093083321, 3}, {2838919278929826733, 1}, {2839035817307118471, 1}, {2840406940647032152, 2}, {2844615017042294501, 2}, {2845888405618126475, 3}, {2847788564242379871, 1}, {2849686826586096145, 3}, {2851615259847550363, 0}, {2852163895967989861, 1}, {2853285246650312372, 2}, {2856555073458733324, 1}, {2861278927883096247, 1}, {2865144123732194841, 0}, {2868019371222510553, 3}, {2868326802248882075, 3}, {2870357086968320399, 1}, {2874302010494084279, 0}, {2875038447036413171, 1}, {2876873153177572860, 3}, {2879928191030473629, 1}, {2880549659795797383, 2}, {2882142330292643005, 3}, {2883235815820045521, 0}, {2884016388364485602, 1}, {2888979797852181944, 1}, {2890472934831498655, 3}, {2890803238165579622, 3}, {2894403265184929473, 2}, {2898718228184616335, 2}, {2900447284570352596, 0}, {2900718917138543594, 0}, {2901012764493815344, 0}, {2901286729252361434, 0}, {2906222920763114369, 1}, {2906291151471163111, 1}, {2907468202673010362, 2}, {2908752424625161566, 3}, {2910603749229313474, 1}, {2910715460713191325, 1}, {2911182164286716385, 1}, {2914531349475574883, 0}, {2914897181900369267, 0}, {2915779556717987728, 1}, {2916878765340049397, 2}, {2921726276898584519, 3}, {2922223716818812445, 3}, {2923187916591191368, 0}, {2930898086084142673, 3}, {2935453779507915759, 3}, {2940394065321057825, 3}, {2942249056508750688, 1}, {2944988364721981116, 3}, {2946215746790627520, 0}, {2946234501266189024, 0}, {2947762591807146427, 2}, {2950441601736726626, 0}, {2953590053689388406, 3}, {2955083219635037676, 0}, {2956369695319909764, 1}, {2957187483705351084, 2}, {2958383540293237445, 3}, {2959248584834778099, 0}, {2959983536238765557, 0}, {2960089759082884888, 1}, {2960687625428737204, 1}, {2962170707839957521, 2}, {2962497156445981797, 3}, {2965958019637853471, 2}, {2967337895372970972, 3}, {2967813052208817537, 3}, {2968644679462056262, 0}, {2969122328473140919, 1}, {2969887650706214656, 1}, {2970262718495597768, 2}, {2975454360642530618, 2}, {2978567385517976844, 1}, {2979472944889523117, 2}, {2980443149539340869, 3}, {2981032829354224147, 3}, {2981053333496081480, 3}, {2982941799858270243, 1}, {2983276221309770349, 1}, {2984039284535689107, 2}, {2984359113848658706, 2}, {2985519757225120843, 3}, {2985774419674241746, 3}, {2992670909726052831, 2}, {2994380778306300579, 3}, {2994472560058517684, 3}, {2994929426316354364, 0}, {2995351020165597450, 0}, {2996400569037062551, 1}, {2996460494598025727, 1}, {2996946015951864503, 1}, {2997121096690421793, 1}, {2998675450639619662, 3}, {2999777588514915565, 0}, {3001013256844921801, 1}, {3001470156336614070, 1}, {3005280564450022975, 1}, {3005412098938467178, 1}, {3007773003675651623, 3}, {3010330133131778156, 1}, {3012142671577145491, 3}, {3014803894911548373, 1}, {3017075361970622737, 3}, {3018861256983852596, 1}, {3022615273023457947, 0}, {3023045781943514175, 1}, {3023208805483103069, 1}, {3023843951636426506, 1}, {3025073437638076106, 2}, {3025271286408490614, 2}, {3025793919253347393, 3}, {3026667474164639726, 0}, {3026817131257607710, 0}, {3026903947826973844, 0}, {3033255605659601991, 2}, {3033782327290942742, 2}, {3035085710878911110, 3}, {3036996057423969516, 1}, {3039219789250287978, 3}, {3040265781862434326, 0}, {3041613303364073358, 1}, {3046274336834390459, 1}, {3049325758373576761, 0}, {3049675863200393531, 0}, {3050953930570256518, 1}, {3051150236398028196, 1}, {3053184759058143180, 3}, {3057326349519192987, 3}, {3061310152151845604, 2}, {3062147342587982776, 3}, {3062285889017609928, 3}, {3064506194388069949, 1}, {3064764910448561709, 2}, {3064931524548739208, 2}, {3066475996325074886, 3}, {3066687788824831459, 3}, {3067162026500984106, 0}, {3070166643663101695, 2}, {3071448976285722904, 3}, {3072978486010636476, 1}, {3073165006734413870, 1}, {3073335772734840531, 1}, {3074524141632442126, 2}, {3075576965655105265, 3}, {3079680861869243315, 3}, {3079824004392180327, 3}, {3079929730930687472, 3}, {3080090586309517552, 3}, {3084063011640714994, 3}, {3086895493337929669, 1}, {3088326304239481997, 2}, {3091964165303019487, 2}, {3092515539583624002, 2}, {3092640703412310837, 2}, {3092720873175528484, 2}, {3092779397784027407, 2}, {3097122257072133677, 2}, {3099999133873938068, 1}, {3107145493620563786, 3}, {3108966224162852822, 1}, {3116350475142345464, 3}, {3117050603386086052, 0}, {3118262049827060432, 1}, {3118784934153119073, 2}, {3120117449135127598, 3}, {3121383291838196664, 0}, {3123576538713838778, 2}, {3127449853255374688, 1}, {3127679911412836645, 1}, {3129172992179304375, 3}, {3130338849051603144, 0}, {3131305273839156054, 1}, {3134253197458850223, 3}, {3136914495729410564, 2}, {3137515800845216379, 2}, {3137786968001789387, 2}, {3140985220404782640, 1}, {3141689570859898046, 2}, {3143468150308699157, 3}, {3148802513167201267, 0}, {3149509305275409516, 1}, {3150327677611071457, 2}, {3150341634535621630, 2}, {3151374456746403597, 2}, {3154665029035864193, 1}, {3154973020790924649, 2}, {3155002465120584160, 2}, {3155788632933354250, 2}, {3156731606556372716, 3}, {3163758970508620307, 1}, {3164468701080362455, 2}, {3170784045582909952, 0}, {3175263316462616166, 0}, {3176841324862592322, 1}, {3176851520232777914, 1}, {3178880178851110560, 3}, {3178890916332075338, 3}, {3183536397839160912, 3}, {3183569897407931926, 3}, {3184184069617293743, 0}, {3186950263224909931, 2}, {3188323067916717418, 3}, {3190893012851929825, 2}, {3193648970489939740, 0}, {3195941797030440996, 2}, {3199120263433932749, 1}, {3201877037286286734, 3}, {3202043479278758297, 3}, {3202208009093727212, 0}, {3204922450327338704, 2}, {3207386309853249556, 0}, {3208589314514124571, 1}, {3211999944184082143, 0}, {3217733251093768338, 1}, {3217863986972508187, 2}, {3218548089531638887, 2}, {3220804580361818619, 0}, {3221802948653988300, 1}, {3222063330534997880, 1}, {3222175500858351947, 1}, {3224465617760416517, 3}, {3224741691711482653, 0}, {3226563848048972505, 1}, {3228029113764896033, 3}, {3231465918077371556, 2}, {3231571389956958076, 2}, {3234974950106046903, 1}, {3235267709230474881, 1}, {3242634779375207474, 0}, {3244287871640915516, 1}, {3244964180557920894, 2}, {3246011120346063165, 3}, {3249690133482337629, 2}, {3253049886197329853, 1}, {3257072904735968364, 0}, {3257317678812554333, 1}, {3257768525261421241, 1}, {3258376282833299565, 2}, {3259233422798502340, 2}, {3260429946026406252, 3}, {3263546291230689838, 2}, {3264284458288962534, 3}, {3264424389909851169, 3}, {3264578604791845705, 3}, {3265895083621174065, 0}, {3266946485108558087, 1}, {3269417575203973678, 3}, {3273830015016831383, 3}, {3275732804591488302, 1}, {3277082567430551111, 2}, {3279515506911227209, 0}, {3284828307592935660, 1}, {3287870391570586907, 0}, {3292344906188978752, 0}, {3293947362427444949, 1}, {3294454008376456139, 2}, {3297882651585997978, 1}, {3298238903573193961, 1}, {3301289118056825704, 0}, {3301782510674936808, 0}, {3306536363753618949, 0}, {3307962975880785118, 2}, {3309285424640852885, 3}, {3309416672368239775, 3}, {3310437209857045449, 0}, {3313776886417598794, 3}, {3314281500545949801, 3}, {3317021177907224951, 2}, {3319622102877662317, 0}, {3319676467645533751, 0}, {3319970391256777243, 0}, {3320530057836912424, 1}, {3320960569131767991, 1}, {3322142703911304233, 2}, {3323008055807222291, 3}, {3324083974684121063, 0}, {3326427649153763488, 2}, {3328030213895667979, 3}, {3328140311310404631, 3}, {3329125301053562422, 0}, {3330847793778759595, 2}, {3331517044972778552, 2}, {3332452810496252574, 3}, {3334527675420580836, 1}, {3336096106867739679, 3}, {3336599700766073286, 3}, {3337249569639359343, 0}, {3338368369527709137, 1}, {3340053641981531746, 2}, {3341357743610388613, 3}, {3342148444878768725, 0}, {3343490232046443767, 1}, {3348871922887773525, 2}, {3350795023322554551, 0}, {3350890578639598952, 0}, {3358825200737704089, 3}, {3359625966394357101, 3}, {3365163691435824575, 0}, {3365623598786557873, 1}, {3369291342486901160, 0}, {3371015855601698125, 2}, {3372799154120294706, 3}, {3375172859758531368, 1}, {3377570736592840034, 3}, {3378900635555028143, 1}, {3382771364660397555, 0}, {3384278816025362963, 1}, {3384641555434123017, 2}, {3385927231462661363, 3}, {3385949748923062607, 3}, {3393511459427227979, 2}, {3394040364420833623, 2}, {3395176423875915810, 3}, {3400231132561870717, 0}, {3400654021045008797, 0}, {3402341723441174246, 1}, {3404167802524527383, 3}, {3405336529876290803, 0}, {3405549463917682984, 0}, {3406912909405219078, 1}, {3408834567672756721, 3}, {3410789605807983473, 1}, {3412253279524323644, 2}, {3414696525182993885, 0}, {3415060565186217713, 1}, {3418288057764216389, 0}, {3419316785903181906, 0}, {3419575153848582609, 1}, {3420991500973281217, 2}, {3421687766899199032, 3}, {3424568666608217341, 1}, {3425085947139233646, 2}, {3425106180157587034, 2}, {3426456360631049002, 3}, {3427401221269977567, 0}, {3427768995971014979, 0}, {3434914334740252543, 2}, {3442580573368532953, 1}, {3443415068849031212, 2}, {3444094278596479740, 2}, {3444268318713026918, 3}, {3445625117847349025, 0}, {3446088173854709479, 0}, {3448719174844355383, 3}, {3450621967679654082, 0}, {3455797360821141357, 1}, {3457091088867165690, 2}, {3457586404617178462, 2}, {3458315490264497140, 3}, {3458458015549211121, 3}, {3459191261743684297, 0}, {3459812483789493723, 0}, {3460061258007799893, 1}, {3460265397357412575, 1}, {3463767737635578020, 0}, {3467805865330742488, 0}, {3468681945290060782, 0}, {3468737288955317317, 0}, {3469795510945911509, 1}, {3471423705702659702, 3}, {3476312622997853824, 3}, {3477410019648264994, 0}, {3477442615928188451, 0}, {3478241422217936377, 1}, {3479735472453172523, 2}, {3482746684043594291, 1}, {3487995151857953274, 1}, {3488086052715463409, 2}, {3492740592602224243, 2}, {3494214930778272897, 3}, {3494428041786844621, 3}, {3494880947890395274, 0}, {3497604170893077495, 2}, {3497612405961978004, 2}, {3498569149403497938, 3}, {3501177282332406663, 1}, {3502323868188590458, 2}, {3504541985946816063, 0}, {3506358164898880496, 2}, {3507120463825948685, 2}, {3507732774077893748, 3}, {3510114892549258397, 1}, {3511106316788034793, 2}, {3514080040669417175, 1}, {3516646378455763007, 3}, {3516761896967631485, 3}, {3520854102340628868, 3}, {3520872192915917837, 3}, {3521257899633950205, 3}, {3521799362172050991, 3}, {3521852862802554421, 0}, {3522319553184296812, 0}, {3522696854222685961, 0}, {3524061387476484150, 1}, {3525260012624043263, 3}, {3525563889804129061, 3}, {3526417797738331074, 0}, {3530369890416864029, 3}, {3531255192550073268, 0}, {3531323920441231508, 0}, {3532111012207150269, 1}, {3532469600858993458, 1}, {3533335534300421405, 2}, {3534488379889405903, 3}, {3534504829654522014, 3}, {3535492279485348316, 0}, {3536400960578622726, 0}, {3538541575764076013, 2}, {3538939524801473512, 3}, {3540177800567489843, 0}, {3540291248898659226, 0}, {3543192317036527904, 2}, {3544040957624287579, 3}, {3544554587749245559, 0}, {3544944537238854167, 0}, {3546557558454058285, 1}, {3546998724591072949, 2}, {3547523196152872568, 2}, {3549861946200661824, 0}, {3553208949221422214, 3}, {3556054382896127550, 2}, {3557522066982752521, 3}, {3559263536239483440, 1}, {3559541691150269773, 1}, {3563137547515512016, 0}, {3563311411162191670, 0}, {3563457774152001005, 0}, {3563947446204916589, 1}, {3564735890730929393, 2}, {3565822656754228038, 3}, {3567166437103167732, 0}, {3570762708270278732, 3}, {3570988386760280138, 3}, {3571774117613710771, 0}, {3576140393212308006, 0}, {3579151368734717565, 2}, {3580275710537049939, 3}, {3584478334247225873, 3}, {3587470442257930096, 2}, {3590523500237223815, 1}, {3592539750235322553, 2}, {3593230499398004079, 3}, {3593822167955022995, 3}, {3599281523282481104, 0}, {3600518568259788129, 1}, {3608881755988378458, 1}, {3609227581859355055, 1}, {3610894554733909060, 3}, {3611679908339769462, 3}, {3613260097952833779, 1}, {3620098203098193944, 3}, {3620596341949629820, 3}, {3623291613527916602, 2}, {3626929346831188746, 1}, {3627481885228464690, 1}, {3628040400799658137, 2}, {3630328156596193780, 0}, {3631914826509687130, 1}, {3633063731971052520, 2}, {3633897910461780026, 3}, {3635811329352621842, 1}, {3636390896730554893, 1}, {3640766542788071497, 1}, {3642602882574037270, 3}, {3643492202426060648, 0}, {3645794409631552373, 2}, {3648330362918116727, 0}, {3649116146636274587, 1}, {3649785396446530538, 1}, {3650442172715673365, 2}, {3651371035634788826, 3}, {3658519123974785665, 1}, {3660095251041707355, 2}, {3660796605905121511, 3}, {3661865313874199816, 0}, {3663458205346560006, 1}, {3668206424626899983, 2}, {3668452545570955132, 2}, {3670167886812139369, 3}, {3674431379564295101, 3}, {3676736434189457996, 1}, {3679463588532074771, 0}, {3680430992460167359, 0}, {3682985586739338276, 3}, {3683026768694760047, 3}, {3683183701083191683, 3}, {3692426893503511525, 3}, {3692956231688524469, 0}, {3693923710225235172, 0}, {3699095987413124564, 1}, {3699108661131349678, 1}, {3701289680420196718, 3}, {3702383771433211743, 0}, {3704197338834317936, 1}, {3704325018567667916, 2}, {3706287250482303233, 3}, {3708224727088848632, 1}, {3710094639260022653, 3}, {3711228216373241105, 0}, {3711787365308887563, 0}, {3717079396006304506, 1}, {3717346412895298497, 1}, {3720408511379922161, 0}, {3725677000565467539, 1}, {3727307155600968656, 2}, {3729045703710123409, 0}, {3729406694865154095, 0}, {3733670582342095796, 0}, {3737282002778387339, 3}, {3738806909016626924, 0}, {3738888816087743813, 0}, {3740111730958133259, 1}, {3740485131202212521, 2}, {3740953439630422273, 2}, {3741668280832208194, 3}, {3742049504614130172, 3}, {3744422277423831786, 1}, {3745470634921412838, 2}, {3747403294562400056, 0}, {3750042998345869908, 2}, {3750823044385427937, 3}, {3754014957565525773, 2}, {3754339837234337643, 2}, {3757846239892471449, 1}, {3760789624501693217, 0}, {3761875988909903864, 1}, {3762603033372856812, 1}, {3764587415682140748, 3}, {3767982218425468500, 2}, {3769684809007969085, 0}, {3775166842895598343, 1}, {3775371198888880116, 1}, {3775400080492610631, 1}, {3775624659603382373, 1}, {3782164914503545999, 3}, {3794594537772422114, 2}, {3796099210206534219, 3}, {3800356556378005398, 3}, {3807897159754889665, 2}, {3809204305155973345, 3}, {3809863304156015307, 3}, {3811873633409373068, 1}, {3813276357869736000, 2}, {3814012717524745174, 3}, {3816076643826914771, 1}, {3816321983009539824, 1}, {3816765179413625270, 1}, {3820483357394399656, 1}, {3820695838052232588, 1}, {3822842191150026769, 3}, {3827250750161666405, 3}, {3829563167393779045, 1}, {3830773796201512734, 2}, {3833787416136649737, 1}, {3834459321133540598, 1}, {3834491402049297702, 1}, {3835646448997603415, 2}, {3835736964506116520, 2}, {3837450667218903460, 0}, {3839005380043059441, 1}, {3839405360082607448, 2}, {3842991141959275361, 1}, {3843708474933466400, 1}, {3844175977720473233, 2}, {3845511580298498800, 3}, {3847319562720531361, 1}, {3847370602713004898, 1}, {3849041616232956592, 2}, {3851821461774294693, 1}, {3852902096373246345, 2}, {3853399314309319060, 2}, {3853963984663747560, 3}, {3854414633075004229, 3}, {3854961727627545794, 3}, {3855391086496690429, 0}, {3858417253466079056, 2}, {3867703381053458367, 3}, {3871141862912096701, 2}, {3881667645729113184, 3}, {3882715310440714530, 0}, {3886348859361293381, 3}, {3886462746493074033, 3}, {3886983501134915989, 0}, {3887530602515149862, 0}, {3888645901922483869, 1}, {3890192598422858353, 3}, {3893158542707915774, 1}, {3895052363097057093, 3}, {3896279843382136853, 0}, {3899178627199881866, 3}, {3902857616982333808, 2}, {3903348986409735079, 2}, {3904616851077318955, 3}, {3907411251170194821, 2}, {3908067384187416216, 3}, {3910172642246414953, 0}, {3911924442046280011, 2}, {3914629055443757067, 0}, {3914687928472992962, 0}, {3915912422497280120, 2}, {3918504009612176256, 0}, {3919713094923188547, 1}, {3921867695903152677, 3}, {3923036737577991382, 0}, {3925389614098068805, 2}, {3929983933420866047, 2}, {3930663389065252136, 3}, {3934664741955250253, 2}, {3935379837280669818, 3}, {3939738952946211468, 3}, {3941266145607839873, 0}, {3942653437261378352, 1}, {3942708050817898237, 1}, {3943331085977611435, 2}, {3955235529576855139, 0}, {3962171787912742985, 3}, {3962267080775814446, 3}, {3965775119979934618, 2}, {3966694967605869539, 3}, {3967292051942296453, 3}, {3967871692469729144, 0}, {3971298435888564339, 3}, {3973581327224630979, 1}, {3974649540307096128, 2}, {3975172206678360143, 2}, {3976946633654649803, 0}, {3979489501819051454, 2}, {3982940468398677194, 1}, {3983472013755653788, 2}, {3988711897561767956, 2}, {3989574836468683891, 3}, {3990491472899213363, 0}, {3991687508973326983, 1}, {3994882358972358038, 0}, {3995282693026336612, 0}, {3998112572292518263, 3}, {3999174617671892594, 3}, {4000335446025022173, 1}, {4000677556064600331, 1}, {4004863465709064415, 1}, {4007356622358193867, 3}, {4007620176263083653, 3}, {4008049298037574045, 3}, {4008135238438316185, 3}, {4008596704999700118, 0}, {4014570779590664441, 1}, {4017207421232736338, 3}, {4018794596916066769, 1}, {4019786401907810189, 2}, {4023399780843775178, 1}, {4023579473392880122, 1}, {4025916246056896634, 3}, {4026611831864293788, 0}, {4029463273814040416, 2}, {4029769970549945375, 3}, {4030047188732658749, 3}, {4031461840136407195, 0}, {4034108831927568507, 3}, {4036140174675410377, 0}, {4036155043838823339, 0}, {4037488136120735940, 2}, {4039748562278911826, 0}, {4039839263981285036, 0}, {4042809266750045821, 2}, {4047002183599186241, 2}, {4051144076385966950, 2}, {4051435199394590559, 2}, {4053542738079040904, 0}, {4058641639347956663, 0}, {4058710371274696507, 0}, {4058953711288785976, 1}, {4059753249276406084, 1}, {4061732836629713871, 3}, {4065316560054005162, 2}, {4067049258094733719, 0}, {4069687774164023258, 2}, {4069726820510509076, 2}, {4070421742064056072, 3}, {4071026963584354399, 3}, {4072992750323761485, 1}, {4073398000013760870, 1}, {4074013023834326537, 2}, {4074358061401992581, 2}, {4075444716783500002, 3}, {4078067350932430715, 2}, {4079407433748321684, 3}, {4080362368825921658, 0}, {4080605510419996680, 0}, {4081577243356923571, 1}, {4081874992284134063, 1}, {4084159049673583340, 3}, {4085808899332415373, 0}, {4086070055667105127, 1}, {4086293576162062623, 1}, {4087383513506933787, 2}, {4090509023216334693, 1}, {4091331940996397706, 1}, {4092103645012042064, 2}, {4093524614129681152, 3}, {4094524895025015503, 0}, {4097200583223325082, 3}, {4098028207903386869, 3}, {4098535237370634151, 0}, {4099718473540772968, 1}, {4100052832006923140, 1}, {4100403733343314546, 1}, {4102246936373802832, 3}, {4103950165083988717, 1}, {4105446093666545097, 2}, {4105781610309485995, 2}, {4105912217244982945, 2}, {4107340692516268735, 0}, {4107404229534147611, 0}, {4107779968603309180, 0}, {4108494353370581022, 1}, {4112560867431172737, 0}, {4112969548700678556, 1}, {4114828487522653933, 2}, {4117413154263071090, 0}, {4127147208675095402, 1}, {4129594543959819157, 3}, {4132654503788589657, 2}, {4132847311328934341, 2}, {4136976049689221447, 2}, {4137093739393576753, 2}, {4137802831106456963, 3}, {4140384341325907953, 1}, {4148711550192472662, 0}, {4151842681389864831, 3}, {4155098726177504188, 2}, {4155162701325730854, 2}, {4162488291467794839, 1}, {4168433306041676279, 2}, {4168524701517771289, 2}, {4169637528288810948, 3}, {4175420030711711663, 0}, {4176113290521033807, 1}, {4181557178749791542, 1}, {4181638102234558992, 2}, {4183031819903524480, 3}, {4190822072781723144, 2}, {4194226935635648454, 1}, {4195843299843838210, 2}, {4196152880012939252, 2}, {4198610512390402121, 1}, {4198652634018520191, 1}, {4198806991931071556, 1}, {4199356891124370137, 1}, {4201440921743049408, 3}, {4201737956141639634, 3}, {4202204022206657675, 0}, {4203563950705319014, 1}, {4207864467824639439, 1}, {4211973036218958877, 0}, {4212538061537490787, 1}, {4212570490280843406, 1}, {4213066816651251479, 1}, {4213114454162833008, 1}, {4214280081409801576, 3}, {4221678943584874984, 1}, {4222863083323635825, 2}, {4223434480951563634, 3}, {4225151885415047278, 0}, {4231675061215499455, 2}, {4232075085936644864, 2}, {4234685188140782266, 1}, {4234977875860653851, 1}, {4239498270479015899, 1}, {4240717939192446187, 2}, {4241272655392087042, 3}, {4242745513879421346, 0}, {4245606366034171577, 2}, {4246360038993955761, 3}, {4247485632252233198, 0}, {4247821529223044905, 0}, {4255093375872483992, 3}, {4258333482713322347, 2}, {4260100378124976925, 3}, {4260235115626198195, 3}, {4262880255963671183, 2}, {4263873765372520803, 3}, {4264202171752493827, 3}, {4265915327220129986, 0}, {4266571832959345212, 1}, {4268401498199734875, 3}, {4268683392432019129, 3}, {4271018635259475028, 1}, {4271317270611693194, 1}, {4274070304884805399, 0}, {4277500833365948321, 3}, {4279815704399828246, 1}, {4282067632568351563, 3}, {4286456726370828605, 3}, {4286484836527869207, 3}, {4286567281180745730, 3}, {4290590974711478182, 2}, {4293270974448917560, 1}, {4294130091132237411, 1}, {4294644967413544768, 2}, {4294689139559561361, 2}, {4294975134159802258, 2}, {4295884438938417808, 3}, {4298098431456382531, 1}, {4303123581155419075, 1}, {4304879529194916096, 3}, {4305700249485256639, 0}, {4305801574933914853, 0}, {4307948391858959988, 2}, {4308395744793787909, 2}, {4311478765271984555, 1}, {4312364037786171261, 2}, {4312737232224553726, 2}, {4314512480330162710, 0}, {4315614309488988836, 1}, {4315645344355216048, 1}, {4318492003438926187, 3}, {4318863376591322449, 3}, {4319056282139368728, 0}, {4320398825752592450, 1}, {4320641382345463203, 1}, {4320910175804898776, 1}, {4321813541271190397, 2}, {4322176341878479004, 2}, {4326866713461145269, 3}, {4328069235021348020, 0}, {4330020623329261664, 1}, {4333303690604123667, 0}, {4337180967959206113, 0}, {4337202360298294847, 0}, {4337645417391057264, 0}, {4338593314990170992, 1}, {4339299590257585119, 2}, {4341152643303837774, 3}, {4342823957303595245, 1}, {4345640021199834354, 3}, {4346965713544996044, 0}, {4346990655856082576, 0}, {4350928710000396476, 0}, {4356290058915506601, 1}, {4357166167545067111, 1}, {4358169467415338441, 2}, {4359511541240631231, 0}, {4360163811964981187, 0}, {4360465726695100726, 0}, {4365849463800639529, 1}, {4371531406331620622, 2}, {4371844763637625288, 2}, {4371910118609494561, 3}, {4372447903401004582, 3}, {4374934430987491529, 1}, {4379833353501708295, 2}, {4380506802599149948, 2}, {4380751983948700139, 2}, {4381809749507910219, 3}, {4382503413450104797, 0}, {4386885436289188920, 0}, {4387329278182876093, 0}, {4387334672402418589, 0}, {4387579971497194873, 0}, {4389497305771939934, 2}, {4389912366724602955, 3}, {4391955501222044874, 0}, {4394666730808823492, 3}, {4395195543059936138, 3}, {4397588700925961068, 1}, {4397893725148086245, 2}, {4398274354358792020, 2}, {4399272937683746226, 3}, {4401080892004746940, 0}, {4404673828024017676, 0}, {4404674177098943646, 0}, {4405800069647510650, 1}, {4406044491408168756, 1}, {4416117682778071794, 2}, {4417908078744575814, 3}, {4418212938605670879, 0}, {4420362459136515802, 2}, {4422746115213201426, 0}, {4423487070419591392, 0}, {4425311685012809366, 2}, {4428251222441512290, 1}, {4428421811957306371, 1}, {4429635069053893733, 2}, {4430918384357940506, 3}, {4430921023273731046, 3}, {4432441342157380838, 0}, {4434501036008037865, 2}, {4441834568409235516, 1}, {4442717115954061144, 1}, {4443395479346919445, 2}, {4445625198531294060, 0}, {4447140088244231077, 1}, {4452320211860523109, 2}, {4453426910232948821, 3}, {4454510055856860362, 0}, {4455518748157763437, 1}, {4461138832457625470, 2}, {4462394142030084413, 3}, {4464912847214848896, 1}, {4465307408552077066, 1}, {4466575070771838604, 3}, {4468636342493201051, 0}, {4470469924501424498, 2}, {4470808308182890245, 2}, {4472841647567749958, 0}, {4473023916199574949, 0}, {4476312986290230264, 3}, {4484094530921797029, 2}, {4485471962149390644, 3}, {4490011968133246644, 3}, {4491028635415076630, 0}, {4493810190645762231, 3}, {4494645612473207725, 0}, {4498203145894764222, 3}, {4499722267277331801, 0}, {4501648888377589489, 2}, {4504576325414883659, 0}, {4504781445951758814, 1}, {4505328504773043058, 1}, {4506063999763780684, 2}, {4506826908446397835, 2}, {4508496411470895032, 0}, {4508787705476765999, 0}, {4509612966313299122, 1}, {4509742270943717214, 1}, {4509861008909884804, 1}, {4510232384704750004, 1}, {4513642194594339997, 0}, {4517435895676201878, 0}, {4518990156846352197, 1}, {4522246016857748250, 0}, {4523242346400757752, 1}, {4526682621395233424, 0}, {4527371203522142219, 1}, {4528133704485662667, 1}, {4528237122279081609, 1}, {4528261756117349346, 1}, {4529055998164350569, 2}, {4530309512795405867, 3}, {4542378763557348020, 2}, {4545998597828667430, 1}, {4546337370802775832, 1}, {4551159572398093441, 2}, {4551428509467093765, 2}, {4552105221451826075, 3}, {4555898122075658922, 2}, {4557448425965485281, 3}, {4558387103419915204, 0}, {4560035818513021188, 2}, {4560832403453779258, 2}, {4566514606316688001, 3}, {4572945960485160758, 1}, {4573265524643710815, 1}, {4576985991534266370, 1}, {4578741505534189287, 2}, {4579516026675118940, 3}, {4580926831025299071, 0}, {4581307146170945933, 1}, {4582411051625008194, 1}, {4585644113427068340, 0}, {4591754433046665581, 2}, {4592809417129004420, 3}, {4596626538913327426, 2}, {4596842778895355516, 2}, {4598004149896252307, 3}, {4598596709426157489, 0}, {4603342802438875334, 0}, {4605461897371648319, 2}, {4606431397026319709, 3}, {4606667919645777725, 3}, {4607729303870605987, 0}, {4607742700442095049, 0}, {4609014250743618605, 1}, {4609115557529912027, 1}, {4609965960642071389, 2}, {4614525953016249244, 2}, {4615422750080385728, 3}, {4617870967148906005, 1}, {4623590127836296969, 2}, {4623861527357913475, 2}, {4625778785022473905, 0}, {4626083587949318407, 0}, {4626153638625696712, 0}, {4626541177050590883, 1}, {4627027997713994024, 1}, {4632330137991349005, 2}, {4632832204072700910, 2}, {4632965691878551148, 2}, {4634977905973810772, 0}, {4639184833692510328, 0}, {4640200910948042988, 1}, {4642082297628719040, 2}, {4642794063147231353, 3}, {4643695729006736183, 0}, {4645859158182311996, 2}, {4645935184879786362, 2}, {4646982108586023767, 3}, {4652909896747551924, 0}, {4654640801175269934, 2}, {4655096173434740867, 2}, {4656264876561753490, 3}, {4659658767762973267, 2}, {4659837368793564411, 2}, {4662466422842468219, 1}, {4662656414915949380, 1}, {4662823757669349174, 1}, {4663804752143940598, 2}, {4666224250217451623, 0}, {4666584102712571476, 0}, {4668763195823976134, 2}, {4671560191488344985, 1}, {4672606292076135622, 2}, {4674313909237939448, 3}, {4675303189195412883, 0}, {4679388320727791829, 0}, {4680640147777962699, 1}, {4681672918497503552, 2}, {4683063637475430161, 3}, {4683604576323107996, 3}, {4685148825761210619, 1}, {4685355580370409023, 1}, {4688174618514206676, 3}, {4691777993314033338, 3}, {4694676608878349271, 1}, {4696227134379354105, 3}, {4697795398838582577, 0}, {4697853389731267232, 0}, {4698012282164299400, 0}, {4699286635329755154, 1}, {4700985640211419295, 3}, {4701424914602714087, 3}, {4706454348405760990, 0}, {4708159633961387308, 1}, {4709047791322641225, 2}, {4710055268587892087, 3}, {4710356657637817554, 3}, {4710475172446328060, 3}, {4711102455189341275, 0}, {4711335470218805037, 0}, {4713338471426843886, 2}, {4714620120676009927, 3}, {4715217283852863572, 3}, {4721997034143454027, 1}, {4723827331775500204, 3}, {4725790797486714284, 1}, {4726308198697736594, 1}, {4730657650938952964, 1}, {4730676826326532743, 1}, {4731320002684578692, 2}, {4731411243179682880, 2}, {4732566604973505961, 3}, {4732837534729894939, 3}, {4734724237629032237, 1}, {4735598873606395713, 2}, {4736048498404477401, 2}, {4736873958708156464, 3}, {4750785486035266554, 3}, {4753264361840060391, 1}, {4755049775316837721, 3}, {4755565901877939278, 3}, {4759861770386782250, 3}, {4761322082113681058, 0}, {4761492625331305661, 1}, {4763398529909883960, 2}, {4763705824037745188, 3}, {4763799995967278652, 3}, {4763921138761791592, 3}, {4764423203653469544, 3}, {4764662223273860098, 3}, {4764724249766552002, 3}, {4771431246549649971, 1}, {4774082315795350689, 0}, {4775301829330002085, 1}, {4778660205365725418, 0}, {4784060845229932118, 1}, {4784771488476611570, 1}, {4785100707580612364, 2}, {4785135161137249400, 2}, {4790867956573360421, 3}, {4791484965290613382, 3}, {4791770612666791816, 3}, {4803883262772341180, 2}, {4804542519931337348, 3}, {4807193342499930919, 1}, {4810351814219213441, 0}, {4811326575884976869, 1}, {4811916457865324500, 1}, {4811951055614954591, 1}, {4814232727989294249, 3}, {4814765320965186006, 0}, {4816314067596903573, 1}, {4816499433967787512, 1}, {4818621667505205106, 3}, {4820801140120590588, 1}, {4822177363554112757, 2}, {4822563410741587479, 3}, {4822925749212740571, 3}, {4824993638457688971, 1}, {4826375050158826071, 2}, {4827135955581798500, 3}, {4827145232764294973, 3}, {4827710597206137509, 3}, {4827752650438645980, 3}, {4828147645412676478, 0}, {4828374200991313146, 0}, {4834338356026154578, 1}, {4836490692398687228, 3}, {4836642471471124607, 3}, {4840702622796026614, 3}, {4846845369697312836, 0}, {4847135386038226657, 1}, {4853398826803151846, 2}, {4856756360929629707, 1}, {4856862837566493800, 1}, {4865689445468590912, 1}, {4866255000342319343, 2}, {4867430443499164545, 3}, {4868405234108622042, 0}, {4871110459491023148, 2}, {4873891153684616151, 0}, {4877445711586267412, 0}, {4877865308237244713, 0}, {4883811170642746476, 1}, {4883987295373808754, 1}, {4884254600867910929, 2}, {4884826517972830460, 2}, {4886451157532784501, 0}, {4887237179728240206, 0}, {4887899728450826740, 1}, {4888552065955222106, 1}, {4889039581186323004, 2}, {4889388838852999771, 2}, {4889444339040233398, 2}, {4895748719320067500, 0}, {4897600432647424304, 1}, {4897965661593441979, 2}, {4900655692610992483, 0}, {4903326372140063612, 3}, {4904402148578853891, 3}, {4909533814277507741, 0}, {4911172522553355927, 1}, {4911296292641477437, 2}, {4914179048375045259, 0}, {4917043476129554431, 3}, {4917081505880857851, 3}, {4917816396729393466, 3}, {4918516731475821334, 0}, {4920556627127998010, 2}, {4927245708134209391, 0}, {4927537437400004580, 0}, {4930690386842076936, 3}, {4932662171743140088, 1}, {4935085422742336192, 3}, {4937071755239609763, 1}, {4938483907335880510, 2}, {4938998642446160421, 2}, {4939223474926943340, 2}, {4940619397068197921, 0}, {4941448395249681727, 0}, {4942228445088312775, 1}, {4947492273515807874, 2}, {4949052031132606117, 3}, {4951259319200620945, 1}, {4953374126295207645, 3}, {4954527202160740467, 0}, {4955439240988112042, 1}, {4955536471998953935, 1}, {4957934045209707462, 3}, {4957936744355999916, 3}, {4958177027904990201, 3}, {4960705398776100262, 1}, {4963334015510756465, 0}, {4964224426917209909, 1}, {4965186263719972797, 1}, {4967124260056354031, 3}, {4967719218548115658, 0}, {4969018299189815090, 1}, {4969301591451505233, 1}, {4969452548970307194, 1}, {4971125319468801164, 3}, {4975257627302405972, 2}, {4982300245606942924, 1}, {4983278286450825185, 2}, {4986386615437008783, 0}, {4986426006648018767, 0}, {4986573257038743121, 0}, {4987660877167053353, 1}, {4988901539269028919, 3}, {4989879236996068847, 3}, {4992840222235583342, 2}, {4993402647102982576, 3}, {4996330289558081631, 1}, {4997258442294642375, 2}, {4997913558819332340, 3}, {5002386213932090824, 3}, {5005306002763728037, 1}, {5006918986793807370, 3}, {5009320385667943976, 1}, {5011198940530855928, 2}, {5017959014693951694, 0}, {5019182003346871452, 1}, {5020549801909908937, 3}, {5020627306790702830, 3}, {5021826760299570324, 0}, {5022301246076898771, 0}, {5022557299624258526, 0}, {5025759456252832533, 3}, {5027276941892019879, 1}, {5027780260290056568, 1}, {5029275401699182514, 2}, {5030141205857508929, 3}, {5030808872929617636, 0}, {5032491047817258450, 1}, {5036114313678579516, 0}, {5041261757433008568, 1}, {5041969000933394975, 2}, {5044044228461491950, 0}, {5044332123083883064, 0}, {5045478692947684251, 1}, {5045837408496175829, 1}, {5049989617464209972, 1}, {5051299213018644107, 2}, {5051474465505738216, 2}, {5054458983929196605, 1}, {5055158178475434613, 1}, {5061172491247036491, 3}, {5061443207718496911, 3}, {5061506370787253855, 3}, {5064532705984935657, 2}, {5065223257927420641, 2}, {5065944527817500432, 3}, {5066667527574657971, 0}, {5068781183678639974, 1}, {5069167938285654289, 2}, {5072003657260887376, 0}, {5074604964083325216, 3}, {5074792888300783460, 3}, {5075443650483675072, 3}, {5075683239049511674, 0}, {5075843762173610541, 0}, {5076548910214537045, 0}, {5077257332259966629, 1}, {5079825478165990430, 3}, {5083596125992700219, 3}, {5084738784362190200, 0}, {5085215506052118728, 0}, {5085592541579854779, 0}, {5086113127873727514, 1}, {5086623420417937602, 1}, {5087413380788406742, 2}, {5088185476759662799, 3}, {5093596061254334995, 0}, {5095185892327720809, 1}, {5095256235272522505, 1}, {5100847701820752721, 2}, {5102404847767701675, 3}, {5107501425678891473, 0}, {5110092139593063625, 2}, {5110184317740855949, 2}, {5110470421819550906, 3}, {5110876266441521022, 3}, {5114055723007644212, 2}, {5114392665787098064, 2}, {5115288744682723611, 3}, {5117513801589728581, 1}, {5119825786931863673, 3}, {5119999924028115441, 3}, {5121417458675517519, 0}, {5123913157698447750, 2}, {5125305090342253986, 0}, {5129289734515056627, 3}, {5129782070235847765, 0}, {5132151549722367549, 2}, {5132309507049257444, 2}, {5132724110856492904, 2}, {5133726932296528298, 3}, {5137685453139314562, 3}, {5138481315496082845, 3}, {5139006657580811945, 0}, {5139920630388363235, 1}, {5141481437678832124, 2}, {5142813697692518431, 3}, {5147066129010480696, 3}, {5148231641765292900, 0}, {5148768339771467255, 1}, {5150084268544396173, 2}, {5152665162221002820, 0}, {5155665742012954969, 3}, {5156111835245822056, 3}, {5157386893157139071, 0}, {5160187386572538248, 3}, {5160229923167042013, 3}, {5162620815817568032, 1}, {5165512984063187190, 3}, {5166636191298090923, 0}, {5167345670581326328, 1}, {5169391259894938753, 3}, {5170130859547001180, 3}, {5170816192054543637, 0}, {5170975006998919100, 0}, {5171061510663414419, 0}, {5171200977532351160, 0}, {5171501683615437294, 1}, {5174172187388117374, 3}, {5174245592407126220, 3}, {5175328938201196362, 0}, {5176165713826472413, 1}, {5178457700345953931, 3}, {5181836564695499330, 2}, {5182126052869149839, 2}, {5182300501837375548, 2}, {5185616658080613864, 1}, {5185965562261976096, 2}, {5187821408630013099, 3}, {5188978468548117582, 0}, {5191290004517922537, 2}, {5194096108043317765, 1}, {5197244565029609609, 0}, {5197993986781284558, 0}, {5199367254834564646, 1}, {5200032066390826211, 2}, {5200469370597353979, 2}, {5202286657487295929, 0}, {5203335883917466884, 1}, {5203753286180234064, 1}, {5206635425062398492, 0}, {5208047849586124334, 1}, {5208176755076094513, 1}, {5208337061417025237, 1}, {5208862567796559524, 2}, {5210157180149027365, 3}, {5210630917530276905, 3}, {5212076795549357088, 1}, {5214378004406198025, 3}, {5214971047340771068, 3}, {5215489351996227030, 0}, {5216256677837851673, 0}, {5216557811577154934, 1}, {5217864211014532589, 2}, {5218304975230707610, 2}, {5220423579847634126, 0}, {5222117968085716350, 2}, {5223292073235656166, 3}, {5225087823073871263, 0}, {5225832811170381846, 1}, {5231308380820441885, 2}, {5231369518814927797, 2}, {5232521048456725176, 3}, {5238362466286834695, 0}, {5239505523496759622, 1}, {5241002751972814731, 2}, {5241482574188012448, 3}, {5247422817107148154, 0}, {5249540393303816406, 2}, {5250621709200175511, 3}, {5252042788550533595, 0}, {5253189622825989544, 1}, {5254264810680249608, 2}, {5257751805608968016, 1}, {5259685158061024500, 3}, {5261426710953031556, 1}, {5261978218402465607, 1}, {5264984245994912360, 0}, {5265876699330129961, 1}, {5267490243091689752, 2}, {5269824970691159395, 0}, {5270861814216687703, 1}, {5277904115728455574, 3}, {5278090506148678661, 3}, {5283412353181216262, 0}, {5283721431596032999, 0}, {5285747163236290906, 2}, {5288656209377196564, 1}, {5289629493076738098, 2}, {5293067387163208742, 1}, {5294220432506349521, 2}, {5298510533316376600, 2}, {5300625938308666333, 3}, {5302421023832977918, 1}, {5307384845725235700, 1}, {5308285099033996136, 2}, {5308477017995902336, 2}, {5309730369061303839, 3}, {5310262456090108628, 0}, {5310620293105169683, 0}, {5312803779880318313, 2}, {5313566716693168683, 3}, {5313980950867061955, 3}, {5314641904515467393, 0}, {5314863119542651747, 0}, {5315768645311989520, 1}, {5316598437438941386, 2}, {5318785750123503095, 0}, {5320333336971412686, 1}, {5320931671057094735, 1}, {5325469192603864870, 1}, {5326211632469556782, 2}, {5328081077282856667, 0}, {5330439121097336676, 2}, {5334546398074582023, 2}, {5334889134629760794, 2}, {5335313711731296734, 2}, {5337552475843548684, 0}, {5339237247565323941, 2}, {5339313204689921558, 2}, {5340052316165944993, 2}, {5340105747485270137, 2}, {5340754031208992697, 3}, {5340806515349917309, 3}, {5344003472635565642, 2}, {5346678715748914959, 0}, {5354076756077903475, 3}, {5355140252737527174, 0}, {5358091431815683117, 2}, {5358288507998569912, 3}, {5360472559932191114, 1}, {5361375617673564203, 1}, {5365298111426864003, 1}, {5366732353181449163, 2}, {5367477352308987192, 3}, {5367650967143077226, 3}, {5370015230213194185, 1}, {5372036858675718257, 3}, {5372175854099971837, 3}, {5373627574743775687, 0}, {5374760057685605024, 1}, {5378282967678948725, 0}, {5378713720892223612, 1}, {5381991033301799965, 0}, {5382587342637287980, 0}, {5383038597524815200, 1}, {5386986676364246694, 0}, {5387534825684117903, 1}, {5387816655688304549, 1}, {5388039705862531898, 1}, {5389147631384212546, 2}, {5396256934093195501, 0}, {5396594273001355862, 1}, {5397267601992050775, 1}, {5398821294500614516, 3}, {5398904296090869085, 3}, {5401922154093398942, 1}, {5402145050820902743, 2}, {5402625268775633201, 2}, {5411258977078864518, 2}, {5411430830797636639, 2}, {5411684524875350561, 2}, {5414611494982231140, 1}, {5416384295959621740, 2}, {5418147842493895598, 0}, {5423840223930386044, 1}, {5424704405629557023, 2}, {5424754465269744976, 2}, {5428150109722672297, 1}, {5431358152855351800, 0}, {5434083628447509688, 2}, {5438876701733939490, 2}, {5441197739472982802, 0}, {5444824850433653783, 3}, {5445773406457778279, 0}, {5451411693041324724, 1}, {5451498116519307453, 1}, {5454983624615005533, 0}, {5464324730613043688, 1}, {5464851355370116090, 1}, {5465963393053222041, 2}, {5467858766798773985, 0}, {5469698478261554546, 2}, {5470191125939500724, 2}, {5470329115833206971, 2}, {5472766592156284614, 0}, {5475113429636126331, 2}, {5475523126916735511, 3}, {5475757310035669107, 3}, {5476441358410004787, 0}, {5478217042504188081, 1}, {5483684178527121355, 2}, {5484009735751954875, 2}, {5484242430731999589, 2}, {5484581543861348811, 3}, {5487106108064320655, 1}, {5489456112044439842, 3}, {5493009156210739183, 2}, {5495704941419121487, 1}, {5496577539008792744, 1}, {5503554740685621784, 0}, {5505885652536540904, 2}, {5509229971908437103, 1}, {5511929706326463383, 3}, {5515742330999243810, 2}, {5525087477367550914, 3}, {5527022843348957122, 0}, {5527470044523789889, 1}, {5527686191628381440, 1}, {5528099273924769884, 1}, {5529762448605642876, 3}, {5534173125011668675, 3}, {5534736649456897768, 3}, {5535034658964436225, 0}, {5535694753985095986, 0}, {5536054957490105426, 1}, {5539298496204212382, 3}, {5539866376424053110, 0}, {5540629805963289781, 1}, {5543909360696220406, 3}, {5545692870339640299, 1}, {5545776115390534095, 1}, {5549652416340106599, 1}, {5550717519437583450, 2}, {5551952880451002991, 3}, {5552293080845556451, 3}, {5552500544518286006, 3}, {5557461581900923593, 0}, {5558647678584467055, 1}, {5561894926260306543, 3}, {5563402149727298772, 1}, {5564181368184751441, 1}, {5564272761655244547, 2}, {5565827131973446900, 3}, {5569932225605518378, 3}, {5570157314455677844, 3}, {5571180560343076075, 0}, {5574614822369682841, 3}, {5581187861763756627, 1}, {5588883247237350142, 3}, {5589989617017297855, 0}, {5591575530922043019, 2}, {5593248062062490204, 3}, {5594694671407749856, 1}, {5596453447708376055, 2}, {5599052917505667126, 0}, {5602788298316848210, 0}, {5603611641650957536, 1}, {5605261298933584824, 2}, {5605720598793172068, 2}, {5605990881456505001, 3}, {5609318103594359885, 2}, {5610112989782748853, 2}, {5614191066974593226, 2}, {5614916120490694090, 3}, {5616126918430448465, 0}, {5616982847073435450, 0}, {5618196078348891224, 1}, {5619273548659354372, 2}, {5620143520882968278, 3}, {5621044355402783562, 0}, {5628635039315296643, 3}, {5631357405729667701, 1}, {5632112822524222997, 2}, {5636552775428556408, 2}, {5642541513911093222, 3}, {5642560193812812134, 3}, {5643667115226238462, 0}, {5646150186849220744, 2}, {5646876774228107253, 3}, {5646885467038692598, 3}, {5650244400587544233, 2}, {5650708381527038012, 2}, {5659044649877083512, 2}, {5668884047615732234, 2}, {5672246409822670239, 1}, {5672803631571147574, 2}, {5673944371932224005, 3}, {5674814865818505418, 0}, {5674874101538950251, 0}, {5681000536612620681, 1}, {5682169763478627694, 2}, {5684840287572717555, 1}, {5689008616437926686, 0}, {5689299834122844892, 1}, {5690388833517576536, 2}, {5692784649816683588, 0}, {5694690511641025455, 1}, {5694706522518456466, 1}, {5695114308909491554, 2}, {5697820497935439887, 0}, {5702655393406857645, 0}, {5703557541438931407, 1}, {5705052394500430451, 3}, {5707490043659715344, 1}, {5708843082444023028, 2}, {5711080394427217024, 0}, {5711469012336954066, 0}, {5712205398085475358, 1}, {5713058750773305809, 2}, {5715346222313540162, 0}, {5719109514244952779, 3}, {5719399961600514596, 3}, {5724222551111960876, 0}, {5724808230358393468, 0}, {5726366895871925173, 2}, {5729353537456241534, 0}, {5732158411015517832, 3}, {5733001640670984283, 3}, {5733329722696273852, 0}, {5734148225387327587, 0}, {5734562419656389892, 1}, {5734564927161283671, 1}, {5734979283453716410, 1}, {5735023614813146351, 1}, {5735128837404022405, 1}, {5738987165214302334, 1}, {5745094748599987629, 2}, {5745879168965268052, 3}, {5747280852075057967, 0}, {5748765096341349284, 1}, {5749069486797737308, 2}, {5750321217558946957, 3}, {5750530948867933631, 3}, {5752377584195281679, 1}, {5754135142102698396, 2}, {5754141215120657404, 2}, {5754161032399784708, 2}, {5756577500464099055, 0}, {5760967949084190153, 0}, {5762216204706857136, 1}, {5763230390607302845, 2}, {5763636572391427847, 3}, {5764327681635464419, 3}, {5766201301595272499, 1}, {5770259951300036707, 1}, {5771499437167321993, 2}, {5773117727216912340, 3}, {5773197998879590541, 3}, {5774040740574203143, 0}, {5779684294013646436, 1}, {5780576653537512260, 2}, {5781058708920075948, 2}, {5781341806700341357, 2}, {5782846243911586692, 0}, {5783206880988196787, 0}, {5785370868086884710, 2}, {5790391197292669737, 2}, {5792228128150606682, 0}, {5795067060348336314, 3}, {5795950684759610669, 3}, {5801203864258835941, 0}, {5802462469982435629, 1}, {5802727632941258874, 1}, {5804218088454302267, 3}, {5811177927283699815, 1}, {5815119544485160615, 0}, {5817799680232837531, 3}, {5818367628875738393, 3}, {5821582883787141913, 2}, {5821798480688131836, 2}, {5823184859398895357, 0}, {5826293914945155216, 2}, {5830486920177450014, 2}, {5834478884249128812, 2}, {5838803962297500671, 1}, {5840109917744571347, 3}, {5841595600013439183, 0}, {5841624773115866106, 0}, {5842296351831473664, 1}, {5848329897597105938, 2}, {5849177798851483954, 3}, {5849988590903117786, 3}, {5852336778449893475, 1}, {5858946537431787868, 3}, {5859031802533991188, 3}, {5860127672264329149, 0}, {5861204710401776331, 1}, {5861655919086685061, 2}, {5863041279877354917, 3}, {5865076344787967516, 1}, {5865282376022291876, 1}, {5867725680010554018, 3}, {5868022036547348833, 3}, {5871523016211180167, 2}, {5871875652937347193, 3}, {5872175399176938256, 3}, {5875471472322896507, 2}, {5875998582143461504, 2}, {5878764945670735354, 1}, {5882544220237810885, 0}, {5883134132619865231, 1}, {5884244557096105407, 2}, {5884576639938545486, 2}, {5884770198902752062, 2}, {5886036649267748150, 3}, {5886429341456316798, 0}, {5888453648163016687, 1}, {5889677671655366065, 3}, {5891986365680277476, 1}, {5895898488114922923, 0}, {5896919425508176411, 1}, {5897010191228218112, 1}, {5898958420998408220, 3}, {5900438256841097508, 0}, {5900440729764803928, 0}, {5900644173091914339, 0}, {5905886090218860787, 1}, {5908061747521851771, 3}, {5908457648889689851, 3}, {5909825488629464766, 0}, {5910749027408295985, 1}, {5912593925142193100, 3}, {5915893265983788067, 2}, {5916690496427687937, 3}, {5917630859212994989, 3}, {5919881539868190462, 1}, {5919960865051000947, 1}, {5921533224426475625, 3}, {5921675359725231066, 3}, {5923509507998152543, 1}, {5923562550658557358, 1}, {5924086573840147598, 1}, {5925567521729145601, 2}, {5926277358860557767, 3}, {5926399781825307655, 3}, {5926695537077934893, 3}, {5927563035494783927, 0}, {5928398409709101067, 1}, {5930411513967899407, 3}, {5933544452357079463, 2}, {5934819353989008103, 3}, {5935518435633989961, 3}, {5937776929663843468, 1}, {5938903631748588984, 2}, {5939879043437390658, 3}, {5941107027876487545, 0}, {5942285387105998158, 1}, {5944461224572228857, 3}, {5944533998008963067, 3}, {5951451141452555345, 1}, {5952886658068805767, 3}, {5956046812507990479, 2}, {5958455145755976025, 0}, {5960875912967598168, 2}, {5963857618060953505, 0}, {5964332495315212369, 1}, {5965145966159559910, 2}, {5965401571834328643, 2}, {5965822791742173964, 2}, {5967488125545125351, 0}, {5967641626159858020, 0}, {5968995069020434004, 1}, {5972431745092005787, 0}, {5975127485867439603, 2}, {5975951468793138120, 3}, {5976490485567514829, 0}, {5977703805573813682, 1}, {5977825382621992762, 1}, {5979495468358239508, 2}, {5981316720147719291, 0}, {5981433242759101927, 0}, {5981481201971817455, 0}, {5983437098839967556, 2}, {5986004342540184458, 0}, {5986631055611506339, 1}, {5987538565381743512, 2}, {5991077970781953007, 1}, {5991579190797406686, 1}, {5994056894112965282, 3}, {5997876861350164350, 3}, {5999457689184080059, 0}, {5999794870051781393, 0}, {6003075537061947958, 3}, {6008373848958907892, 0}, {6009776305142720380, 1}, {6014072881293007974, 1}, {6015124335724434305, 2}, {6016856162373329938, 0}, {6023249228070662898, 1}, {6025076405471890982, 3}, {6025405556926247316, 3}, {6028364596262610309, 2}, {6029838592583645418, 3}, {6031967723784501006, 1}, {6032476336505668300, 1}, {6032596160577222731, 2}, {6033959993614341560, 3}, {6034902086591430202, 0}, {6038391263799673845, 3}, {6047935930327696175, 3}, {6047966067772297568, 3}, {6048068773693130223, 3}, {6051382165973076291, 2}, {6051477376189385506, 2}, {6051647781143817645, 2}, {6063992524488708148, 1}, {6065091895811047567, 2}, {6066870936876204501, 0}, {6068611349734545154, 2}, {6069399764936722621, 2}, {6071781873644733518, 0}, {6073405732130312254, 2}, {6077208026181981379, 1}, {6077521702592749061, 1}, {6082639332908688390, 2}, {6084996491991026937, 0}, {6085813761108972722, 1}, {6086235712301620506, 1}, {6090430536268187635, 1}, {6095079551468488019, 1}, {6102345850265102109, 3}, {6108315417651728414, 1}, {6109771431634001926, 2}, {6115134509772405659, 3}, {6116765195909036845, 0}, {6119191167328657795, 2}, {6119579316393674740, 3}, {6123774431894963545, 3}, {6124608172964355163, 3}, {6127153158731884302, 2}, {6131610501138562162, 1}, {6134187486777293836, 0}, {6134314300766418716, 0}, {6134400653315257343, 0}, {6136948087630233293, 2}, {6138403369537094202, 3}, {6140057245082557227, 1}, {6143632021610759489, 0}, {6147466985794885918, 0}, {6148036928326343247, 0}, {6148176598295051437, 0}, {6150072540825356051, 2}, {6155563179859386266, 3}, {6155897702451041573, 3}, {6156199747249882571, 3}, {6159876576253458546, 3}, {6161106184311731369, 0}, {6161112063000825194, 0}, {6165127559449762599, 3}, {6165210419450510560, 3}, {6167126982351788425, 1}, {6172829736563079578, 2}, {6172909545330958572, 2}, {6173258135680319539, 2}, {6177232914834115043, 2}, {6182353070948635511, 3}, {6188040208851326788, 0}, {6190047898087242296, 1}, {6190402662236539307, 2}, {6191736030350384664, 3}, {6192756744504177638, 0}, {6194387790775418325, 1}, {6200294301836121631, 2}, {6203970557641311367, 2}, {6204338953925081452, 2}, {6205215187428923981, 3}, {6209851116027589949, 3}, {6213495178446608696, 2}, {6213539664855418938, 2}, {6213760582930620451, 2}, {6214263173325033535, 3}, {6214860907720473405, 3}, {6215316143016206280, 0}, {6219705095347325001, 0}, {6220883700437834872, 1}, {6222519303659280032, 2}, {6225782284654721600, 1}, {6229425203757653430, 0}, {6231917861339168530, 3}, {6235143923732198996, 1}, {6235357697378193554, 2}, {6243123472568756928, 1}, {6243384755453250365, 1}, {6244903592279532795, 2}, {6247002265065472602, 0}, {6247674542187150856, 1}, {6254527648346406492, 3}, {6254935197330393670, 3}, {6256912335391495321, 1}, {6257418547801095851, 1}, {6258550220946004240, 2}, {6262617326664750512, 2}, {6265337058590076123, 0}, {6265499345599331933, 0}, {6267987406909863904, 3}, {6270588902191461484, 1}, {6271241683126966001, 1}, {6275337614212473581, 1}, {6275691143553196975, 1}, {6276238049166074060, 2}, {6276414091215806402, 2}, {6277455165838069356, 3}, {6280007987743022117, 1}, {6281869290951077282, 3}, {6286920648480758693, 3}, {6291064665695856366, 3}, {6293140157010510172, 1}, {6295153634661381476, 3}, {6295889714920073455, 3}, {6296259944339757264, 0}, {6297694989208149147, 1}, {6298451878271795410, 2}, {6300702901155699370, 0}, {6302959378814186636, 2}, {6303289194247064089, 2}, {6304036215107367687, 3}, {6304180685354229568, 3}, {6304860634716431833, 3}, {6310786657494619396, 1}, {6311267664765788452, 1}, {6311471374851890786, 1}, {6313753082039806849, 3}, {6314877998564939764, 0}, {6317173990983901503, 2}, {6318531582570286362, 3}, {6318735763405858808, 0}, {6327208352735702853, 3}, {6327461209077959096, 3}, {6328050678249600098, 0}, {6328640556688344140, 0}, {6329287246059299298, 1}, {6332931762125578266, 0}, {6338788246150630834, 1}, {6339332436125338553, 2}, {6339552262186734353, 2}, {6340165549676494714, 3}, {6345424278858511768, 3}, {6346161439984660045, 0}, {6347132025298623648, 1}, {6347317235792506193, 1}, {6348479691146191845, 2}, {6349814744430797613, 3}, {6351159001044564997, 0}, {6351404903750165989, 1}, {6354044112399122303, 3}, {6355084174957498977, 0}, {6356337941786855197, 1}, {6358128114482472442, 3}, {6364038900013539061, 0}, {6365304760698956368, 1}, {6365490670159653020, 1}, {6365975146866071978, 2}, {6366764058887095264, 2}, {6372633554289373704, 0}, {6373017170543749126, 0}, {6382987668969528334, 1}, {6384342465580185074, 2}, {6385612614133994521, 3}, {6390754474888739185, 0}, {6392613481708594538, 1}, {6394088785275140196, 3}, {6395088351674394165, 3}, {6395449304949476129, 0}, {6395526381257113789, 0}, {6403480026744478822, 3}, {6407742969376326161, 3}, {6407748328625641965, 3}, {6408039341317554191, 3}, {6411375575653125895, 2}, {6413487176261234586, 0}, {6416377191741043014, 2}, {6418473772287630058, 0}, {6420173698063757539, 2}, {6426223178894842892, 3}, {6430364776481296549, 3}, {6432212859772189168, 0}, {6432665909801428724, 1}, {6433060575139722432, 1}, {6433845591617665892, 2}, {6433847858658428903, 2}, {6439117613849223651, 3}, {6440769501979416210, 0}, {6446448118479560352, 1}, {6449242029365869333, 0}, {6451861772190237669, 2}, {6455554781665509847, 1}, {6458371606854624535, 0}, {6460382601597530030, 1}, {6460468494307623821, 2}, {6461969387707806485, 3}, {6464934952008544599, 2}, {6466353849823715358, 3}, {6467824141313664225, 0}, {6467859518052474985, 0}, {6468041038977561794, 0}, {6468539894193205668, 1}, {6469097553668056929, 1}, {6469842929497027370, 2}, {6470800581779840980, 3}, {6471777879989944167, 0}, {6476212172344459936, 0}, {6476510301989108412, 0}, {6482495367522857668, 1}, {6483281663848018009, 2}, {6483620731662674482, 2}, {6484004892088506310, 2}, {6486057519980161334, 0}, {6487683737924422245, 2}, {6487997743235837627, 2}, {6488675643744836451, 3}, {6491429418837563364, 1}, {6494027301439304772, 3}, {6495356252147892915, 1}, {6495660670364579430, 1}, {6496150217043857754, 1}, {6497374663506126127, 2}, {6500581737028138053, 1}, {6501826721167309978, 2}, {6503462129580500811, 0}, {6505108068106878116, 1}, {6509309368986578527, 1}, {6510956180808542130, 2}, {6518931419936010607, 1}, {6520600751076202295, 3}, {6527985924303547936, 2}, {6528703369887053040, 2}, {6531236654821947146, 0}, {6532234643741018032, 1}, {6534269957520710106, 3}, {6539954014528808682, 0}, {6539997341028816240, 0}, {6540172930042703891, 0}, {6542250843073855307, 2}, {6543910944682596200, 0}, {6544450762467484691, 0}, {6548215167569192562, 3}, {6550516805811294984, 2}, {6551305884531541725, 2}, {6553365778044715946, 0}, {6554544128231145118, 1}, {6554958188845837039, 1}, {6559151970632684225, 1}, {6559881835642967957, 2}, {6560017648844307004, 2}, {6561210495529215889, 3}, {6562319384115714703, 0}, {6570887929632950909, 0}, {6572130873226439976, 1}, {6574457238973492227, 3}, {6575604826054365000, 0}, {6576907269598258784, 1}, {6580829947241367053, 0}, {6583057682466835019, 2}, {6583436886249917290, 3}, {6585345545757085720, 0}, {6586538759272518384, 2}, {6586593063465539185, 2}, {6587701269019651503, 3}, {6589882056855857990, 0}, {6592290513260960794, 3}, {6592778596056533482, 3}, {6593816055611080311, 0}, {6594332904671476016, 0}, {6594595749475039055, 1}, {6597518441366298266, 3}, {6597556665498094721, 3}, {6597584186783312391, 3}, {6597598438219715445, 3}, {6599077620671871350, 1}, {6601281181601482005, 3}, {6601495475543548638, 3}, {6602192606518511600, 3}, {6603298061623698105, 0}, {6606759604426632276, 3}, {6607765128979025622, 0}, {6608510724790257085, 1}, {6609448262200057475, 2}, {6610166225087857678, 3}, {6611103914183686804, 3}, {6611559696264760600, 0}, {6613551438743261199, 2}, {6614587697959255231, 2}, {6615135800457248259, 3}, {6618136082274067543, 2}, {6618936114894621728, 2}, {6621087346703464851, 0}, {6624898969056576212, 0}, {6626748552710298833, 1}, {6626873139033783632, 1}, {6630493727057694906, 1}, {6631219579097519351, 1}, {6632445588045569957, 2}, {6636933745239403690, 2}, {6644767978969745184, 1}, {6647145021473000986, 3}, {6647487660255629863, 0}, {6651433721354068515, 3}, {6652267273357888469, 0}, {6653207797624116130, 1}, {6654995460779929909, 2}, {6659380794685690615, 2}, {6660927669970104245, 0}, {6662203287674694997, 1}, {6662714084989294155, 1}, {6664036881919519538, 2}, {6665215989353244102, 3}, {6665877352386445182, 0}, {6667100729189311142, 1}, {6671890554178081965, 1}, {6676942655145773373, 2}, {6677473693977314017, 2}, {6679568149966524458, 0}, {6680132605600041215, 1}, {6682185696023661728, 2}, {6682918577259585619, 3}, {6683494392105064449, 0}, {6686894214493403307, 3}, {6687835344397365981, 3}, {6687865944396060885, 0}, {6688526805765165174, 0}, {6690639712525564549, 2}, {6694635133914923803, 2}, {6695333324556035883, 2}, {6695678096430593124, 2}, {6696264161854610643, 3}, {6697929934631098206, 0}, {6698592544414388456, 1}, {6700335413607770750, 3}, {6701404741738755804, 0}, {6703225196536791996, 1}, {6705160525169732528, 3}, {6708759644857525705, 2}, {6711608163130472641, 1}, {6713813763704636578, 3}, {6714755817127414755, 3}, {6717018220341283065, 1}, {6720690402514811950, 1}, {6722150246009876005, 2}, {6723295749910667690, 3}, {6723721730541776127, 3}, {6727384882306888541, 3}, {6728066752261338427, 3}, {6728752938726286885, 0}, {6728780905164492206, 0}, {6729110101973475982, 0}, {6732301768602618645, 3}, {6733525801870766243, 0}, {6734310940621491651, 1}, {6736425787958235904, 3}, {6739054957632534211, 1}, {6740427543588866406, 2}, {6741354565349208059, 3}, {6742565357409368266, 0}, {6743361077598008674, 1}, {6750616850088853509, 3}, {6752026187796057243, 1}, {6753124560502613091, 1}, {6753269370634114232, 2}, {6754198366028747832, 2}, {6755705544634180287, 0}, {6758125802653040540, 2}, {6765696922558919483, 1}, {6767321517567487250, 2}, {6771281185099087456, 2}, {6772188647095675234, 2}, {6776371455752558421, 2}, {6777064052516098499, 3}, {6778947233728609393, 0}, {6780695881372496451, 2}, {6784478599185599894, 1}, {6786922396611765972, 3}, {6787111338529276268, 0}, {6788688208397593164, 1}, {6791075702297112545, 3}, {6797872609731343778, 1}, {6798010389213349124, 1}, {6801035063334417143, 0}, {6802483736108368654, 1}, {6803393609938389154, 2}, {6806503814488633889, 1}, {6808866330576144754, 3}, {6810584639803548389, 1}, {6812182102140845367, 2}, {6812890342201098953, 3}, {6814314430821799652, 0}, {6814482623607729294, 0}, {6816683091071279382, 2}, {6816860026843275861, 2}, {6817007918265302588, 2}, {6820522522910544850, 1}, {6821601391216486242, 2}, {6824235076006457579, 1}, {6830811579751924374, 2}, {6833682489435403224, 1}, {6833824694803940187, 1}, {6835536512041679926, 3}, {6836242331166410785, 3}, {6838184657405461716, 1}, {6840286870109587224, 3}, {6844202795544180284, 2}, {6844361489718293122, 3}, {6845341054127907539, 3}, {6846392402982579383, 0}, {6861239562394765963, 2}, {6865491212194485137, 1}, {6867450729920789292, 3}, {6868969042393466055, 0}, {6874643495759733482, 1}, {6875685699004955174, 2}, {6878696115042386533, 1}, {6879129824056958868, 1}, {6880635068513631658, 3}, {6882965098398331561, 1}, {6884162701291018287, 2}, {6889788661252486804, 3}, {6890892228019165723, 0}, {6892332220476511424, 1}, {6893106971966404234, 2}, {6894221354962172456, 3}, {6896735827746825260, 1}, {6898194410841208017, 2}, {6900779261232705109, 1}, {6901346070537206780, 1}, {6902017742415621221, 2}, {6902054849283586006, 2}, {6903197425397102486, 3}, {6903322212119345913, 3}, {6903438386933387315, 3}, {6903798721442365664, 3}, {6906976492484241699, 2}, {6907082225818826618, 2}, {6910138644331558936, 1}, {6910283610102036964, 1}, {6911534242619099897, 2}, {6914728625341019896, 1}, {6915021225271241590, 1}, {6917125994264349757, 3}, {6917870237627358305, 0}, {6918112376917038303, 0}, {6918366013419546390, 0}, {6919963461368826549, 2}, {6920051263156088230, 2}, {6920475083242951046, 2}, {6921310980554948555, 3}, {6922770820529218226, 0}, {6924260787226518972, 1}, {6928241615584285380, 1}, {6928478234842745003, 1}, {6929766733662645727, 2}, {6929989534315585593, 3}, {6930234159075987068, 3}, {6932794420811503340, 1}, {6934358934427842116, 2}, {6936780389476756271, 1}, {6937508073552055508, 1}, {6938372515710882062, 2}, {6941109171231709763, 0}, {6942805915844354326, 2}, {6943762346250726795, 3}, {6944500316115619299, 3}, {6948779285788493109, 3}, {6949432918879492730, 0}, {6954327906021638485, 0}, {6960117946768357389, 1}, {6960427156745953594, 2}, {6962282411017498113, 3}, {6965915839719520234, 2}, {6974540124911396009, 2}, {6975294715127385000, 3}, {6977247307767273835, 1}, {6977265916937695331, 1}, {6978295192930028604, 1}, {6980319128666416781, 3}, {6981872716714081277, 1}, {6982286752010508994, 1}, {6983820876336442304, 2}, {6984058398646912514, 3}, {6984433246166513497, 3}, {6986144720146655772, 0}, {6987964923555803968, 2}, {6988158263302920400, 2}, {6990371111415938791, 0}, {6991714888220760968, 1}, {6992823134322374992, 2}, {6998946183504526992, 0}, {7001301327074520702, 2}, {7001427934049115139, 2}, {7004943074148511173, 1}, {7005344199955618068, 1}, {7007531782531791260, 3}, {7013433913089786874, 1}, {7013660359284554411, 1}, {7018061137570227434, 1}, {7019167410475637663, 2}, {7020359324405856401, 3}, {7021238746980193221, 0}, {7023168536638914862, 1}, {7024926148044640952, 3}, {7031017602220309808, 0}, {7033400685464683640, 2}, {7034173210600912549, 3}, {7036478515432020726, 1}, {7036597359216271270, 1}, {7041155319222071786, 1}, {7043791495660968780, 0}, {7045870668790663315, 1}, {7047273848495108896, 3}, {7049099923383314296, 0}, {7050041168063665779, 1}, {7052355472085295025, 3}, {7052519164086853688, 3}, {7052565325474925091, 3}, {7052844633402620283, 0}, {7053622598273641699, 0}, {7054601105583186872, 1}, {7058599742353379340, 1}, {7062447948835092842, 0}, {7064286146105437853, 2}, {7064394616805395114, 2}, {7064944202465335759, 2}, {7064952825921069700, 2}, {7065476455957413848, 3}, {7065987284657550756, 3}, {7066278076896759570, 0}, {7071801491804119563, 1}, {7073192079694939763, 2}, {7084295117068442472, 0}, {7089303965668082514, 0}, {7090442245819037108, 1}, {7090890649216270393, 1}, {7091800773799401599, 2}, {7092461874792753304, 3}, {7093173105132434680, 0}, {7095566182179465279, 2}, {7095702131778021243, 2}, {7098178601251586829, 0}, {7099862990455358511, 1}, {7101053585573745315, 3}, {7103027081340390090, 0}, {7105324110230993503, 2}, {7105394272641749291, 2}, {7105744320170434115, 3}, {7106916584125047412, 0}, {7108603184994970302, 1}, {7110074794815049047, 3}, {7119378345542052422, 3}, {7120655015737065803, 0}, {7122646175965063552, 2}, {7123364808376610479, 2}, {7124661461133590789, 3}, {7124946103666626693, 0}, {7125095977738666891, 0}, {7125384924038271243, 0}, {7125940952594982585, 1}, {7126547711992422524, 1}, {7127774681272783785, 2}, {7128969656120329717, 3}, {7134333243636657799, 0}, {7135896737480962619, 1}, {7136902074504964746, 2}, {7139041838124169939, 0}, {7139478370992174694, 1}, {7142110783349586663, 3}, {7146308211326387385, 3}, {7149658409710091125, 2}, {7155335360813044004, 3}, {7163393164083175949, 2}, {7164952265600023573, 3}, {7168025695898353792, 2}, {7168130160027886120, 2}, {7169709315728876647, 3}, {7170870277106080415, 1}, {7171698304806323789, 1}, {7175107104187691807, 0}, {7176864863562416647, 2}, {7177319266616357498, 2}, {7178226904803203911, 3}, {7179565624328348226, 0}, {7179961509515838763, 1}, {7183506378306968474, 0}, {7184334163225346667, 0}, {7187204757923255264, 3}, {7188374961227716913, 0}, {7189355544912629138, 1}, {7189685262353961111, 1}, {7191881241846497627, 3}, {7193027733300463056, 0}, {7194536295825327391, 2}, {7194985784261031533, 2}, {7196470201113812026, 3}, {7197806261284467474, 0}, {7198352879225966467, 1}, {7200206507090296676, 3}, {7200361664008914655, 3}, {7201153796910597584, 3}, {7201575974044987427, 0}, {7202180134611819820, 0}, {7202570924120676906, 1}, {7204126629913410753, 2}, {7206905386711677650, 1}, {7208292598615153393, 2}, {7208856502996428926, 2}, {7209648521707408713, 3}, {7210229576238625932, 3}, {7210628618393637426, 0}, {7211986723926748547, 1}, {7215712733239957294, 0}, {7218873278686856379, 3}, {7219393958510681817, 0}, {7219680682165998798, 0}, {7221375503734142850, 1}, {7221919141618461319, 2}, {7222629197027679753, 2}, {7223209421064911282, 3}, {7225240017859523365, 1}, {7227374327375717145, 3}, {7228885388802091009, 0}, {7229623976457130652, 1}, {7229771440799825811, 1}, {7233599705422359159, 0}, {7236295874596800565, 3}, {7237957733188836615, 0}, {7238413782554188982, 1}, {7239097712875169141, 1}, {7239270257624363753, 1}, {7240776843671397080, 3}, {7248633343905641856, 2}, {7252551528077763742, 1}, {7257067421969924559, 1}, {7257999471252980417, 2}, {7261025840513502295, 1}, {7266316199927331899, 1}, {7266903213081534058, 2}, {7267856476405651302, 3}, {7269434110184785823, 0}, {7269868563076491509, 0}, {7272938235546774024, 3}, {7274307917466821142, 0}, {7275021190744928671, 1}, {7278336424371856510, 0}, {7278866640734801419, 0}, {7279653745853892362, 1}, {7280358552543519605, 2}, {7280856749579170805, 2}, {7281235708946845532, 3}, {7283929871426656989, 1}, {7287956661078160484, 1}, {7288682558444362985, 1}, {7288725358117308959, 1}, {7295186748708413175, 3}, {7295411429099009647, 3}, {7297900474404722507, 1}, {7299500175052133608, 3}, {7301112408297190440, 0}, {7302025001939906183, 1}, {7308448122173287170, 3}, {7312040765035319296, 2}, {7312168070320905349, 2}, {7312840938177699261, 3}, {7313787417623846931, 3}, {7314126598737812066, 0}, {7314305316987786237, 0}, {7316562600173631109, 2}, {7316973040316656489, 2}, {7320928064703134051, 2}, {7323171488422789044, 0}, {7327708731476913271, 0}, {7328403656896533466, 0}, {7330032302130196934, 2}, {7330944146100148831, 3}, {7330963716307289410, 3}, {7333830344899870083, 1}, {7334068083911983716, 1}, {7336432494229422124, 0}, {7341789377645603337, 0}, {7343575467015822305, 2}, {7347239504969594266, 1}, {7348230346633952577, 2}, {7349241713871838003, 3}, {7349509080699159596, 3}, {7350196715995343304, 0}, {7351116418752114848, 1}, {7354780518608274257, 0}, {7355339260645083026, 0}, {7357607957509610821, 2}, {7364472823391304124, 0}, {7367631014443237592, 3}, {7368016961128177490, 0}, {7369852078285730525, 1}, {7371383742260189342, 3}, {7372375369646376972, 3}, {7373003138115210561, 0}, {7379228672333209682, 2}, {7390589864994393234, 0}, {7391684575887756702, 1}, {7394073663540864511, 3}, {7395786617882768714, 0}, {7397049098057846028, 1}, {7397293237452388291, 2}, {7398488117637152561, 3}, {7401531579500151117, 1}, {7403370486281751861, 3}, {7403661802090299012, 3}, {7405320234200932966, 1}, {7408227722422288148, 3}, {7408592440427451153, 0}, {7409020824362372432, 0}, {7409521290932946537, 0}, {7409921781539621458, 1}, {7412762165528730729, 3}, {7412770047606544270, 3}, {7415959818851535873, 2}, {7416190936850734706, 2}, {7417792560505781181, 0}, {7420734259054336977, 2}, {7423137450690217676, 1}, {7424243105371924102, 2}, {7424722187067171475, 2}, {7425241802840101985, 2}, {7428621099896750673, 1}, {7430655249244235155, 3}, {7432268642192510184, 1}, {7432788943998390121, 1}, {7436258247402762339, 0}, {7436987560392188270, 1}, {7437709632165226978, 2}, {7437990485752451845, 2}, {7439189085888461008, 3}, {7439424701861229218, 3}, {7439875764032774174, 3}, {7442827645393067535, 2}, {7445746365484159330, 1}, {7445945997701075015, 1}, {7455395077332822751, 1}, {7455711159137506446, 2}, {7456543703610124212, 2}, {7456993575851823021, 3}, {7457673075235534113, 3}, {7458909869773902539, 0}, {7460005751295694055, 1}, {7463238873162541036, 0}, {7466888064101991020, 3}, {7467565986518722980, 0}, {7469539149218206237, 2}, {7471218314392972046, 3}, {7471427676523744959, 3}, {7475839232706068587, 3}, {7478195536928595989, 1}, {7479210621040232841, 2}, {7479764188711520656, 3}, {7481998999771059374, 1}, {7482997990506633994, 2}, {7484356765896361466, 3}, {7486386340404728192, 1}, {7487288570296382004, 2}, {7488365668922210884, 3}, {7490744772892375182, 1}, {7493435673354912311, 3}, {7494797619800367049, 0}, {7498497291659472813, 0}, {7499537676279182123, 0}, {7501851182521229852, 2}, {7502549730266342353, 3}, {7503397669176812596, 0}, {7504646752239251429, 1}, {7507167475991248089, 3}, {7507849424940725753, 0}, {7510890252866121269, 3}, {7512257993655592879, 0}, {7514830027188638334, 2}, {7515201663637533715, 2}, {7515800613550627514, 3}, {7519171246316366715, 2}, {7519204787539612918, 2}, {7520803781162979002, 3}, {7521915577811263064, 0}, {7522420879754112866, 1}, {7523001979036737432, 1}, {7524220499179743015, 2}, {7524257096244191954, 2}, {7524262656362780517, 2}, {7525161513121738447, 3}, {7525384991465306789, 3}, {7525693762054162095, 0}, {7525804604860049374, 0}, {7526207308150848816, 0}, {7526276945439953795, 0}, {7529423254268956083, 3}, {7530694134958348786, 0}, {7531958722824493877, 1}, {7535438747501653666, 0}, {7541663839312047462, 2}, {7548012888911632022, 3}, {7549054062615437255, 0}, {7556636951473808595, 3}, {7557494776210986959, 0}, {7559856193248004262, 2}, {7562381332488722906, 0}, {7563273342522911558, 1}, {7563316446082013456, 1}, {7563541689196308786, 1}, {7564042667975700684, 2}, {7568297265294929005, 1}, {7572711061405647226, 1}, {7574986525129671854, 3}, {7581162217598167913, 1}, {7581804508867389294, 1}, {7581927675910089808, 2}, {7582864254704168664, 2}, {7584597602392575590, 0}, {7586941089109921862, 2}, {7589467555657100834, 0}, {7594064531137424074, 0}, {7595687779929300358, 2}, {7597325528548056917, 3}, {7598224545919599078, 0}, {7602032796792896374, 3}, {7603135208899796422, 0}, {7605002061153211570, 2}, {7606538071756711856, 3}, {7610080492160398699, 3}, {7611708958842202712, 0}, {7616697451730582879, 0}, {7618476995187479398, 2}, {7620434768286963612, 0}, {7622299091237408309, 1}, {7628193369260722142, 3}, {7629642355145508873, 0}, {7630147525901331008, 0}, {7632184426473130631, 2}, {7633826188516882863, 0}, {7636153660322038675, 2}, {7636708588660666164, 2}, {7637449762267549991, 3}, {7638149053070171084, 0}, {7639273380053847724, 1}, {7645393233901876368, 2}, {7646192222623705183, 3}, {7647805648263210852, 0}, {7648445694823926647, 1}, {7650110799757109786, 2}, {7652533045352382061, 0}, {7654689226197494963, 2}, {7655895846812006245, 3}, {7656757486410079232, 0}, {7658637714416868424, 2}, {7659114414227472151, 2}, {7659190747591199755, 2}, {7659211961168155554, 2}, {7659881771467593434, 3}, {7659965379261088583, 3}, {7663432869594987511, 2}, {7663555093867406897, 2}, {7663830647764290931, 2}, {7667945174887062992, 2}, {7668298798740451921, 2}, {7669712721401839420, 0}, {7670097299873345458, 0}, {7672040023932327118, 2}, {7675843952489193310, 1}, {7677044163150681064, 2}, {7677757320459238313, 3}, {7678609718799028127, 3}, {7678628540505374498, 3}, {7681036646203293629, 2}, {7683177126248571812, 0}, {7685147536786511122, 1}, {7685491040834708987, 2}, {7685632478651800498, 2}, {7686157800065484724, 2}, {7686420229543185761, 2}, {7687183771696139583, 3}, {7687340176602374842, 3}, {7694125092571277285, 1}, {7694244283536735072, 1}, {7695582423516131386, 3}, {7696482424698998506, 3}, {7699443445059737843, 2}, {7700624525155729014, 3}, {7702363340767833046, 1}, {7702400849774105385, 1}, {7709905394052239019, 3}, {7711314621507022039, 1}, {7713000169299889739, 2}, {7713665210637642883, 3}, {7713735287283453950, 3}, {7717028668400501155, 2}, {7718439172730920981, 3}, {7719372945942160203, 0}, {7722013505397540712, 2}, {7722166155296996937, 2}, {7728628555655437625, 0}, {7729167210875532700, 0}, {7730887540482148756, 2}, {7732766727550172684, 0}, {7734618335535508664, 1}, {7735832410459952027, 2}, {7738380247857777778, 1}, {7738560876489338119, 1}, {7738865789466429036, 1}, {7739687571797392783, 2}, {7746988243378123128, 0}, {7751428670649677689, 0}, {7757386355416624973, 1}, {7759162310409033896, 3}, {7761767087771149001, 1}, {7761796199095889610, 1}, {7761844112910606231, 1}, {7766562184437244103, 2}, {7768072682167455325, 3}, {7768299659469800808, 3}, {7769531691621578276, 0}, {7771033327731920422, 2}, {7773602322060718678, 0}, {7775447509222303391, 1}, {7776073033296487292, 2}, {7777269599966622050, 3}, {7777608478693522503, 3}, {7784978665353641505, 2}, {7786671038905821035, 3}, {7787501422546285311, 0}, {7787825394145981362, 0}, {7788013780855444042, 1}, {7790128764198277724, 3}, {7793699494633816414, 2}, {7796508089859680180, 0}, {7798508262176956668, 2}, {7804245339745398730, 3}, {7804292828423376847, 3}, {7804697185817466663, 3}, {7806184518073542201, 1}, {7807193308434877287, 2}, {7810006636724842419, 0}, {7811606728357896063, 2}, {7814456200566403330, 0}, {7815010142181657955, 1}, {7816232931943818338, 2}, {7819249849757855277, 0}, {7820448160347709551, 1}, {7820738530412289482, 2}, {7822809523555648231, 0}, {7824365774894920217, 1}, {7826500289029853507, 3}, {7828301911132263813, 0}, {7829245629508416240, 1}, {7830105168492723735, 2}, {7830536893839575650, 2}, {7830555835118351487, 2}, {7836607938660911840, 0}, {7838498121769741893, 1}, {7838570772287921909, 2}, {7840193433173960272, 3}, {7840218237062559512, 3}, {7845977419549595339, 0}, {7851872040397647620, 1}, {7865802779016932570, 2}, {7866725776382550926, 3}, {7866739997801656088, 3}, {7868026101056726149, 0}, {7868785034312199345, 0}, {7871341695597306000, 3}, {7872692084102529250, 0}, {7872735095149838690, 0}, {7877098103765737445, 0}, {7878483534039941040, 1}, {7880657672791846765, 3}, {7884921840951818892, 3}, {7887893019310725469, 1}, {7888041909585437246, 1}, {7888568112933994110, 2}, {7892511457337125662, 1}, {7892585062413955993, 2}, {7892904780940957366, 2}, {7894355649186152220, 3}, {7894906962099957943, 0}, {7896866936128933995, 1}, {7897322321665698311, 2}, {7900675393672177992, 1}, {7902525274662638678, 2}, {7904776081144655982, 0}, {7905971573245125981, 1}, {7906429611623763098, 2}, {7908574732851492526, 0}, {7910397049559299062, 1}, {7910543121058072843, 1}, {7916900029627956377, 3}, {7918251789239587924, 0}, {7920395432722775052, 2}, {7923241027053446765, 1}, {7925996189143207295, 3}, {7930036644656234168, 3}, {7933541605213508048, 2}, {7935892591257799039, 0}, {7936846275621542535, 1}, {7937165684711102897, 1}, {7937522819004970784, 1}, {7938300659289518204, 2}, {7939810405693602405, 3}, {7941456982881705013, 1}, {7941473489857558922, 1}, {7943050077784833975, 2}, {7943590278347595839, 3}, {7943628515727020426, 3}, {7944644844553292248, 0}, {7952461408807785160, 3}, {7956051128385237182, 2}, {7959578859012041936, 1}, {7960748682733728124, 2}, {7960966384169805946, 2}, {7962802374442934622, 0}, {7963442496738206995, 0}, {7976949707798235096, 0}, {7976961532036847394, 0}, {7978659501208544399, 2}, {7979991647296020792, 3}, {7981785194652236377, 1}, {7983537489297478989, 2}, {7985440763466884054, 0}, {7986008985985153196, 1}, {7988746105053037247, 3}, {7991768502960008939, 2}, {7991769871323277036, 2}, {7992685670286689864, 2}, {7995029359670810079, 1}, {7997707579522562490, 3}, {7999187680449258857, 0}, {7999538436692306174, 1}, {8000579701042449737, 1}, {8001837399712796664, 3}, {8002115532738795782, 3}, {8002845887140222485, 3}, {8005434673764166358, 2}, {8006143067573694566, 2}, {8007912654530563351, 0}, {8008146817946886729, 0}, {8009056293937277215, 1}, {8013407540783403010, 1}, {8013722145101233616, 1}, {8014064930370196043, 1}, {8015257563164973674, 2}, {8015573398072388195, 3}, {8016315096859492355, 3}, {8017298722492154410, 0}, {8017570683142408687, 1}, {8018968190168991793, 2}, {8021871494822600548, 0}, {8024288693677815549, 3}, {8024754498264663892, 3}, {8026518538630837114, 0}, {8030122709039660892, 0}, {8033933975522942330, 3}, {8034283130585146137, 3}, {8037449966729422089, 2}, {8040049958223863035, 0}, {8040842597125808739, 1}, {8049030052757711178, 0}, {8049987854820755522, 1}, {8050558802346193395, 2}, {8050767334343762975, 2}, {8051180131220781164, 2}, {8052794010964433406, 0}, {8053176062063680388, 0}, {8057652967642298928, 0}, {8057762172349735371, 0}, {8058462327052922861, 1}, {8060353270406235677, 3}, {8060594257477753589, 3}, {8064900350891301971, 3}, {8068799182823062338, 2}, {8071217097716235170, 0}, {8071767619558522363, 1}, {8077479003342000764, 2}, {8077917963821992728, 2}, {8079851019091869509, 0}, {8083758699706230480, 3}, {8093389695832876496, 0}, {8093849070284364175, 0}, {8096378545251979595, 3}, {8097190511588148847, 3}, {8097731688522175882, 0}, {8098452001442713018, 0}, {8100005875358742155, 2}, {8104012627918881656, 1}, {8104044978575683630, 1}, {8105000221026489976, 2}, {8107003132196123439, 0}, {8112174023821410231, 1}, {8112391766500359021, 1}, {8114123380673531032, 2}, {8114220363642010454, 2}, {8115227508893082397, 3}, {8116085666296804061, 0}, {8116520938587518703, 0}, {8117192677013899845, 1}, {8119186999576360262, 3}, {8120606322757578876, 0}, {8121582467031152655, 1}, {8127278127031400694, 2}, {8148532541786155008, 1}, {8150408349802589233, 3}, {8150540179861677648, 3}, {8150767762100444853, 3}, {8152412964826781423, 0}, {8152990242110475952, 1}, {8153787183861665735, 2}, {8154414151803687635, 2}, {8155477488662249843, 3}, {8158011799424467301, 1}, {8159449394921010764, 3}, {8162742975497611968, 1}, {8164659839968985714, 3}, {8167597398835967075, 2}, {8169781764168957256, 0}, {8170702944460419345, 1}, {8172412269494145896, 2}, {8173827239868549200, 3}, {8174715496050449028, 0}, {8175040411359102000, 0}, {8175187430564476476, 1}, {8186231891014030513, 2}, {8186376010078994326, 2}, {8188134322433589614, 0}, {8188345717467169156, 0}, {8192429663926731500, 0}, {8192737022918494591, 0}, {8195232928372100184, 2}, {8196718743995802332, 0}, {8196905169693750275, 0}, {8198415869918801761, 1}, {8198691710932751020, 1}, {8200153012376469678, 3}, {8200745560817025027, 3}, {8204827305969967008, 3}, {8209042066664402518, 3}, {8209177014858882481, 3}, {8210418866863760924, 0}, {8213890257173031708, 3}, {8214344340547193955, 3}, {8215267037619602661, 0}, {8216504310108223117, 1}, {8216790780351461713, 1}, {8218262732660825538, 3}, {8218691563815976182, 3}, {8219033348783266781, 3}, {8219671150731276827, 0}, {8223253715000814767, 3}, {8223299554338339812, 3}, {8225034440210636948, 1}, {8225462898255113758, 1}, {8230689709517881055, 2}, {8231674002626480405, 3}, {8233231122304316262, 0}, {8234445411125250618, 1}, {8239757209519363373, 2}, {8242843673717123613, 1}, {8243329526725075127, 1}, {8243577931036888576, 1}, {8249610846206486299, 3}, {8249979185474747226, 3}, {8252216759801775061, 1}, {8253828339881301963, 2}, {8254217932357719749, 3}, {8254467331171144598, 3}, {8258253391793462438, 2}, {8259523488489007419, 3}, {8261738544790768524, 1}, {8264230449965386730, 0}, {8264570083381650043, 0}, {8267848172783910380, 3}, {8268339861393164434, 3}, {8269465132232115011, 0}, {8270451738745783659, 1}, {8271403501926865248, 2}, {8274364529672028137, 1}, {8275054463686978127, 1}, {8282933589830002528, 0}, {8283925976356639036, 1}, {8284820931879263419, 2}, {8284905071952546849, 2}, {8285594760221674332, 3}, {8288524003519431923, 1}, {8289509785634189113, 2}, {8294661371673442165, 3}, {8295840290968281616, 0}, {8296863956415889693, 1}, {8298002286009257927, 2}, {8300467309605536203, 0}, {8303998530869801891, 3}, {8304460675037950677, 3}, {8305635340369162653, 0}, {8308609594381779176, 3}, {8309060277817474292, 3}, {8311485755051557458, 2}, {8312390233679676667, 2}, {8312681205186471327, 3}, {8314442920082478415, 0}, {8314572585579527932, 0}, {8315968292214743930, 2}, {8317786831718601862, 3}, {8318749796678087881, 0}, {8321752454968208888, 3}, {8323210176224501595, 0}, {8323736503057789090, 0}, {8324689917188530009, 1}, {8326556165409224098, 3}, {8326850041978247691, 3}, {8327405947415640490, 0}, {8328030160131413359, 0}, {8329125965493322687, 1}, {8333983164812244028, 2}, {8335703151636916218, 3}, {8336524928779035034, 0}, {8340451866762529289, 3}, {8340466806472593292, 3}, {8340501542589775986, 3}, {8345443520379316135, 0}, {8346373975982879110, 1}, {8347442908956980596, 2}, {8348339482229214830, 2}, {8356367532342220296, 1}, {8356634985691482293, 2}, {8357309323187017107, 2}, {8357347202607980608, 2}, {8358093563230912442, 3}, {8362061795228126637, 3}, {8362636407604295011, 3}, {8363448751146694535, 0}, {8364094316323883521, 0}, {8364173254635099791, 0}, {8364786835497513340, 1}, {8365118422387876449, 1}, {8366608556645181664, 3}, {8366772594534571305, 3}, {8369708033552873953, 1}, {8370932988997749760, 2}, {8375282569020175184, 2}, {8375515940093304751, 2}, {8375537075439695812, 2}, {8376327975948524419, 3}, {8376912416720573217, 0}, {8377046140202950266, 0}, {8377645206378399231, 0}, {8379825368832325487, 2}, {8380001625780181272, 2}, {8380305185285746081, 3}, {8382239768817927538, 0}, {8385742438717847245, 0}, {8387198822739989280, 1}, {8387755233473232669, 1}, {8394104195021561325, 3}, {8394239784655208493, 3}, {8395087522858079634, 0}, {8397548768711828341, 2}, {8401012731861844796, 1}, {8405349729907987605, 1}, {8406533919303458484, 2}, {8408817949690271703, 0}, {8410348456151395456, 1}, {8410689808519203064, 2}, {8412111066543962230, 3}, {8413333178039810164, 0}, {8414024643162677267, 1}, {8416228101465709428, 3}, {8416562430108537754, 3}, {8417354384712485677, 0}, {8417361497177812416, 0}, {8417822991022764153, 0}, {8418858047195550638, 1}, {8420467047752901998, 2}, {8421156346043822582, 3}, {8423231468811497619, 1}, {8433903182351023529, 2}, {8435060098192054799, 3}, {8441895623112146792, 1}, {8442133403548049952, 2}, {8442352739916070476, 2}, {8442476405010842960, 2}, {8444726840151372871, 0}, {8446548310840205412, 2}, {8448394379130782129, 3}, {8448644485505501221, 3}, {8449972942657550434, 1}, {8450560086303552475, 1}, {8452285475470254786, 3}, {8454769060124752019, 1}, {8455429094869338842, 1}, {8455893963989903062, 2}, {8461369245586699838, 3}, {8462112073354606517, 3}, {8462519140778323204, 0}, {8463476691614102718, 1}, {8465112964251593447, 2}, {8466561070444308455, 3}, {8467552360600473088, 0}, {8469683935699482732, 2}, {8470076913949444496, 2}, {8471825296952223937, 0}, {8472779201767331645, 1}, {8474117312471289817, 2}, {8474738760877740811, 3}, {8477513015469080731, 1}, {8477624942326090040, 1}, {8480080070321033636, 3}, {8485644207730875954, 0}, {8486931316666653249, 1}, {8488493833253468072, 3}, {8489018748877322875, 3}, {8491365252673729907, 1}, {8496438962971037053, 2}, {8496832031861980794, 2}, {8496978267868974125, 2}, {8500553243240043440, 2}, {8502152999219918403, 3}, {8503669106105849810, 0}, {8506446547879894731, 3}, {8507004865978969668, 3}, {8508138533330561846, 0}, {8509042663487713835, 1}, {8512814699604634573, 0}, {8513531237152180855, 1}, {8514052162454177352, 1}, {8514659706212537498, 2}, {8521003260352419801, 0}, {8522154100195149199, 1}, {8533960865939415426, 3}, {8535809010608235857, 1}, {8537322905046267597, 2}, {8544122822063491271, 0}, {8544885153945275019, 1}, {8544911790224905489, 1}, {8545516859409474333, 1}, {8545775432839522402, 2}, {8546535698405135703, 2}, {8547008824798062105, 3}, {8547989919210321567, 0}, {8548076619168712509, 0}, {8550328057917393919, 2}, {8555278161020220667, 2}, {8556948632399490607, 0}, {8558580816609305738, 1}, {8561391129339216835, 0}, {8564523321601239224, 2}, {8566052373820288742, 0}, {8567541237672157618, 1}, {8571481787597486391, 1}, {8574151222711593392, 3}, {8576217433180579550, 1}, {8579242458966266897, 3}, {8580374922128271797, 0}, {8580761042547102222, 1}, {8582496345134531470, 2}, {8584418972140144704, 0}, {8585007933030237871, 1}, {8586049714738708795, 1}, {8590875638897003940, 2}, {8592939006759521450, 0}, {8594301318465192797, 1}, {8594642978790378262, 1}, {8595631671972097217, 2}, {8596665809222571807, 3}, {8597466005107645107, 0}, {8600538900061309458, 2}, {8609176568420185285, 2}, {8611217186722961122, 0}, {8611858997570073702, 0}, {8614101850578394994, 2}, {8615760036491987252, 0}, {8618040894131847401, 2}, {8620260678423448456, 0}, {8624209230042000497, 3}, {8627275366674772141, 2}, {8627718897629466854, 2}, {8628154862165225998, 3}, {8632710955286493506, 3}, {8633939701530389654, 0}, {8635277321471886835, 1}, {8635931891440204479, 2}, {8641417180659258718, 3}, {8642414224426603014, 0}, {8642866632967975820, 0}, {8644333995379892951, 1}, {8645604651829730437, 2}, {8647065247314230933, 0}, {8648038679748613307, 1}, {8651903194700517689, 0}, {8652532604961058742, 0}, {8656246648258445426, 0}, {8659442245666248376, 3}, {8660677265384323485, 0}, {8661303031436092144, 0}, {8665267678920996401, 0}, {8666246142891793156, 1}, {8667258594251879200, 2}, {8668095087516913662, 2}, {8677579648883816304, 3}, {8678839712041446770, 0}, {8679009859724199473, 0}, {8679017287582235659, 0}, {8683318678005844006, 0}, {8686027461725544893, 2}, {8687114629496478087, 3}, {8687207514058569133, 3}, {8692667920219205255, 0}, {8695981218061639335, 3}, {8696534312732566375, 0}, {8697249482057670604, 0}, {8698263393278667265, 1}, {8698847479686058492, 2}, {8699306102726856007, 2}, {8705513475384084192, 0}, {8706337311054859069, 0}, {8706816447812868578, 1}, {8707394252073633978, 1}, {8711033790046141813, 0}, {8713158956803646846, 2}, {8713261564830468140, 2}, {8714705443675092535, 0}, {8715602625519057054, 1}, {8718819306984602173, 3}, {8728621314047940311, 0}, {8740419509414506976, 3}, {8741901311570877859, 0}, {8744428362506489542, 2}, {8744943512276441891, 3}, {8746156782384384467, 0}, {8750183031380003170, 3}, {8752066522595958822, 1}, {8752219747961560742, 1}, {8753253583694883309, 2}, {8753824859801159210, 2}, {8754510758434088893, 3}, {8756440292573682494, 1}, {8759935557672630601, 0}, {8760642811714871451, 1}, {8761229024940490940, 1}, {8764890330690343460, 0}, {8766031941567711277, 1}, {8766958219444565781, 2}, {8768838417072501110, 0}, {8777931167855672272, 0}, {8780262070352282248, 2}, {8782431702771154648, 0}, {8785078652726854689, 2}, {8785860903439902006, 3}, {8788900704897876828, 2}, {8789185302371359190, 2}, {8792062934274831448, 0}, {8797497947866103029, 1}, {8799108171344063584, 3}, {8801117578161331203, 0}, {8801193891696580701, 1}, {8801400230069568872, 1}, {8801814367990732642, 1}, {8801969312011273550, 1}, {8801969573182581211, 1}, {8802151240149383774, 1}, {8802424396209908148, 2}, {8802561517891595564, 2}, {8803671413929043565, 3}, {8804673402816362738, 0}, {8806226928389521982, 1}, {8806851861152731451, 2}, {8808128126120113304, 3}, {8808336749445622050, 3}, {8809266294232745918, 0}, {8810202678525499747, 1}, {8810436672736251324, 1}, {8813990664419695628, 0}, {8815968119488177655, 2}, {8816338219606655388, 2}, {8816836837652793404, 2}, {8825221579567645309, 2}, {8825228288601331294, 2}, {8826482559944492341, 3}, {8828660755664910426, 1}, {8834663691614705767, 2}, {8837323267818495495, 1}, {8848273428886996454, 2}, {8849630815178195441, 0}, {8852769371258146283, 2}, {8857775086364991486, 3}, {8859294400788032917, 0}, {8861403251534004560, 2}, {8862507075241237299, 3}, {8862586135009330401, 3}, {8863967661557995554, 0}, {8865359161832520249, 2}, {8867174585918043423, 3}, {8867981532231442801, 0}, {8870645679389749242, 2}, {8873235671076387331, 1}, {8874525870670529516, 2}, {8876696455496022807, 0}, {8878325348555107882, 1}, {8880022518673358785, 3}, {8881617203953076633, 0}, {8886215292972159137, 0}, {8886677649613413294, 0}, {8890845524749336300, 0}, {8891534018190566141, 1}, {8892489602187080229, 2}, {8894496721366302695, 3}, {8896513886192874382, 1}, {8898944760335435201, 3}, {8899518244339025420, 0}, {8902904664419776119, 3}, {8903790054884701071, 0}, {8910598092665810040, 2}, {8911141245933353477, 2}, {8912263771313892241, 3}, {8915196926568706531, 2}, {8921198285112571587, 3}, {8926696357622658878, 0}, {8927909811206404135, 1}, {8928887319351489167, 2}, {8929202961991520410, 2}, {8930842243064825644, 0}, {8932372966673693913, 1}, {8932446420167856214, 1}, {8932665482582846150, 1}, {8933991808737496788, 2}, {8935863617989923389, 0}, {8936746675655021024, 1}, {8936934712517527121, 1}, {8937916709705154918, 2}, {8938490372212431025, 2}, {8942341754081012490, 2}, {8943202980815635900, 3}, {8948437094103829573, 3}, {8950376186315720732, 1}, {8950918074093999981, 2}, {8951771999109633241, 2}, {8952827279536411079, 3}, {8953262429739367710, 0}, {8954387075054690648, 1}, {8954777655204591729, 1}, {8956479164926963601, 2}, {8959778815082916266, 1}, {8963978940723666608, 1}, {8967106235025308817, 0}, {8968954275649960231, 2}, {8969510896854235994, 2}, {8971527407870203070, 0}, {8971713444374712178, 0}, {8972759444865536314, 1}, {8975326172970725217, 3}, {8975552259484029713, 3}, {8983209907300300359, 2}, {8983586831598409668, 3}, {8985279399773627595, 0}, {8986055034478589598, 1}, {8986265358844895175, 1}, {8989340846053888187, 0}, {8990792628231778499, 1}, {8991454223333596035, 2}, {8991525099440074826, 2}, {8993049957638156056, 3}, {8994398178395633426, 0}, {8995234369596067534, 1}, {8995270380879222110, 1}, {8996127038342030962, 2}, {8998768903758844477, 0}, {8999035118549396101, 0}, {8999467668100320477, 1}, {9001402465467466894, 2}, {9002631123861881623, 3}, {9004630863402561846, 1}, {9007014066314201619, 3}, {9008591676274473002, 1}, {9009865001362811260, 2}, {9011504264626994843, 3}, {9013142713823148190, 1}, {9014711772233205553, 2}, {9017328176781944395, 0}, {9017867778444839106, 1}, {9017984958609081444, 1}, {9019336897688741500, 2}, {9019395857951543452, 2}, {9020254073416955736, 3}, {9020257452404220067, 3}, {9020481443925005558, 3}, {9021434886084218241, 0}, {9023077193616750903, 2}, {9024814676469869818, 3}, {9026189346563417194, 0}, {9026308588793324014, 0}, {9026384078998165586, 1}, {9027712148946986885, 2}, {9029096887121237369, 3}, {9029384168847931836, 3}, {9033049595634697012, 2}, {9034147385043770367, 3}, {9036013522052612905, 1}, {9037204342890356043, 2}, {9037531161043446013, 2}, {9038853579939715426, 0}, {9040639381169079355, 1}, {9041058438875313629, 2}, {9041098587048874675, 2}, {9045207383281141015, 1}, {9053030264973621164, 0}, {9054248936407115313, 1}, {9054326231634226583, 1}, {9055911220672312967, 3}, {9056081229145736417, 3}, {9056093895932861461, 3}, {9061225196233628321, 3}, {9068211875715483742, 2}, {9070757410129033928, 0}, {9072540519157212402, 2}, {9073760565693700666, 3}, {9075736794111282292, 0}, {9075978049186446977, 1}, {9083745296129982860, 3}, {9087408943067785160, 3}, {9087731044895664529, 3}, {9094724960177121711, 1}, {9095189239094154798, 2}, {9095508647436673929, 2}, {9097475647951840428, 0}, {9098633317613921552, 1}, {9103362147068614817, 1}, {9105272495095028544, 3}, {9106461775371684260, 0}, {9108041077909203955, 1}, {9109156817871264837, 2}, {9111981028674696204, 1}, {9112036793732803789, 1}, {9119442121669333238, 3}, {9123558123814184573, 3}, {9124434217269606475, 0}, {9124586802063395064, 0}, {9129282860586328335, 0}, {9137210243440259473, 3}, {9138132364417684971, 0}, {9138671230689286115, 0}, {9146401162383718377, 3}, {9147047684882162431, 0}, {9151264726783959525, 3}, {9154610909476106534, 2}, {9155659481516160477, 3}, {9157825333315209398, 1}, {9161748311905994898, 1}, {9163541838275329407, 2}, {9165181996660680576, 0}, {9167018380406759413, 1}, {9169815834892129037, 0}, {9174955522866467270, 0}, {9183339094624243743, 0}, {9184303865258274310, 1}, {9185175068059017337, 2}, {9187829891919878632, 0}, {9191610275894508036, 3}, {9192147570164982437, 0}, {9194197254270776678, 2}, {9194306748219568751, 2}, {9198427616043000261, 1}, {9199485016162953999, 2}, {9200054798065318708, 3}, {9200563736728780744, 3}, {9200749901620093660, 3}, {9201200566037300421, 0}, {9203923190973454807, 2}, {9205657223992507821, 0}, {9205919749081689000, 0}, {9207233672484108073, 1}, {9208939405210219392, 3}, {9209692744597551944, 3}, {9222568625800748744, 3}, {9222582454147032830, 3}, } ================================================ FILE: scyllacloud/cluster.go ================================================ // Copyright (C) 2021 ScyllaDB package scyllacloud import ( "crypto/tls" "fmt" "net" "os" "sigs.k8s.io/yaml" "github.com/gocql/gocql" ) func NewCloudCluster(bundlePath string) (*gocql.ClusterConfig, error) { connConf := &ConnectionConfig{} bundleFile, err := os.ReadFile(bundlePath) if err != nil { return nil, fmt.Errorf("can't open bundle path: %w", err) } if err := yaml.Unmarshal(bundleFile, connConf); err != nil { return nil, fmt.Errorf("can't decode bundle file at %q: %w", bundlePath, err) } if _, ok := connConf.Contexts[connConf.CurrentContext]; !ok { return nil, fmt.Errorf("current context points to unknown context") } confContext := connConf.Contexts[connConf.CurrentContext] if _, ok := connConf.AuthInfos[confContext.AuthInfoName]; !ok { return nil, fmt.Errorf("context %q auth info points to unknown authinfo", connConf.CurrentContext) } if _, ok := connConf.Datacenters[confContext.DatacenterName]; !ok { return nil, fmt.Errorf("context %q datacenter points to unknown datacenter", connConf.CurrentContext) } authInfo := connConf.AuthInfos[confContext.AuthInfoName] caPool, err := connConf.GetRootCAPool() if err != nil { return nil, fmt.Errorf("can't create root CA pool: %w", err) } cc := gocql.NewCluster(connConf.GetInitialContactPoints()...) cc.Port = 443 // SslOpts are used only by establishing connection to initial contact points. // Skip verifying TLS if any of DC requires it. insecureSkipVerify := false for _, dc := range connConf.Datacenters { if dc.InsecureSkipTLSVerify { insecureSkipVerify = true break } } cc.SslOpts = &gocql.SslOptions{ // Set to false, always use value from tls.Config. EnableHostVerification: false, Config: &tls.Config{ RootCAs: caPool, GetClientCertificate: func(info *tls.CertificateRequestInfo) (*tls.Certificate, error) { return connConf.GetClientCertificate() }, InsecureSkipVerify: insecureSkipVerify, }, } dialer := cc.Dialer if dialer == nil { dialer = &net.Dialer{} } cc.HostDialer = NewSniHostDialer(connConf, dialer) cc.Authenticator = gocql.PasswordAuthenticator{Password: authInfo.Password, Username: authInfo.Username} if connConf.Parameters != nil { if connConf.Parameters.DefaultConsistency != "" { if !validateConsistency(connConf.Parameters.DefaultConsistency, allowedConsistencies) { return nil, fmt.Errorf("invalid value of default consistency %q, values can be one of: %v", connConf.Parameters.DefaultConsistency, allowedConsistencies) } if err := cc.Consistency.UnmarshalText([]byte(connConf.Parameters.DefaultConsistency)); err != nil { return nil, fmt.Errorf("unmarshal default consistency: %w", err) } } if connConf.Parameters.DefaultSerialConsistency != "" { if !validateConsistency(connConf.Parameters.DefaultSerialConsistency, allowedSerialConsistencies) { return nil, fmt.Errorf("invalid value of default serial consistency %q, values can be one of: %v", connConf.Parameters.DefaultSerialConsistency, allowedSerialConsistencies) } if err := cc.SerialConsistency.UnmarshalText([]byte(connConf.Parameters.DefaultSerialConsistency)); err != nil { return nil, fmt.Errorf("unmarshal default serial consistency: %w", err) } } } return cc, nil } func validateConsistency(c ConsistencyString, allowed []ConsistencyString) bool { for _, ac := range allowed { if ac == c { return true } } return false } var allowedSerialConsistencies = []ConsistencyString{ DefaultSerialConsistency, DefaultLocalSerialConsistency, } var allowedConsistencies = []ConsistencyString{ DefaultThreeConsistency, DefaultOneConsistency, DefaultTwoConsistency, DefaultAnyConsistency, DefaultQuorumConsistency, DefaultAllConsistency, DefaultLocalQuorumConsistency, DefaultEachQuorumConsistency, DefaultLocalOneConsistency, } ================================================ FILE: scyllacloud/config.go ================================================ package scyllacloud import ( "crypto/tls" "crypto/x509" "fmt" "os" ) type ConnectionConfig struct { // Datacenters is a map of referencable names to datacenter configs. Datacenters map[string]*Datacenter `json:"datacenters"` // AuthInfos is a map of referencable names to authentication configs. AuthInfos map[string]*AuthInfo `json:"authInfos"` // Contexts is a map of referencable names to context configs. Contexts map[string]*Context `json:"contexts"` Parameters *Parameters `json:"parameters,omitempty"` // Kind is a string value representing the REST resource this object represents. // Servers may infer this from the endpoint the client submits requests to. // In CamelCase. // +optional Kind string `json:"kind,omitempty"` // APIVersion defines the versioned schema of this representation of an object. // Servers should convert recognized schemas to the latest internal value, and // may reject unrecognized values // +optional APIVersion string `json:"apiVersion,omitempty"` CurrentContext string `json:"currentContext"` } type AuthInfo struct { // ClientCertificatePath is the path to a client cert file for TLS. ClientCertificatePath string `json:"clientCertificatePath,omitempty"` // ClientKeyPath is the path to a client key file for TLS. ClientKeyPath string `json:"clientKeyPath,omitempty"` // Username is the username for basic authentication to the Scylla cluster. Username string `json:"username,omitempty"` // Password is the password for basic authentication to the Scylla cluster Password string `json:"password,omitempty"` // ClientCertificateData contains PEM-encoded data from a client cert file for TLS. // Overrides ClientCertificatePath. ClientCertificateData []byte `json:"clientCertificateData,omitempty"` // ClientKeyData contains PEM-encoded data from a client key file for TLS. // Overrides ClientKeyPath. ClientKeyData []byte `json:"clientKeyData,omitempty"` } type Datacenter struct { // CertificateAuthorityPath is the path to a cert file for the certificate authority. CertificateAuthorityPath string `json:"certificateAuthorityPath,omitempty"` // Server is the initial contact point of the Scylla cluster. // Example: https://hostname:port Server string `json:"server"` // TLSServerName is used to check server certificates. // If TLSServerName is empty, the hostname used to contact the server is used. TLSServerName string `json:"tlsServerName,omitempty"` // NodeDomain the domain suffix that is concatenated with host_id of the node driver wants to connect to. // Example: host_id. NodeDomain string `json:"nodeDomain"` // ProxyURL is the URL to the proxy to be used for all requests made by this // client. URLs with "http", "https", and "socks5" schemes are supported. If // this configuration is not provided or the empty string, the client // attempts to construct a proxy configuration from http_proxy and // https_proxy environment variables. If these environment variables are not // set, the client does not attempt to proxy requests. // It is optional ProxyURL string `json:"proxyUrl,omitempty"` // CertificateAuthorityData contains PEM-encoded certificate authority certificates. Overrides CertificateAuthority. CertificateAuthorityData []byte `json:"certificateAuthorityData,omitempty"` // InsecureSkipTLSVerify skips the validity check for the server's certificate. This will make your HTTPS connections insecure. InsecureSkipTLSVerify bool `json:"insecureSkipTlsVerify,omitempty"` } type Context struct { // DatacenterName is the name of the datacenter for this context. DatacenterName string `json:"datacenterName"` // AuthInfoName is the name of the authInfo for this context. AuthInfoName string `json:"authInfoName"` } type Parameters struct { // DefaultConsistency is the default consistency level used for user queries. // +optional DefaultConsistency ConsistencyString `json:"defaultConsistency,omitempty"` // DefaultSerialConsistency is the default consistency level for the serial part of user queries. // +optional DefaultSerialConsistency ConsistencyString `json:"defaultSerialConsistency,omitempty"` } type ConsistencyString string // just AnyConsistency etc is better, but there's already SerialConsistency defined elsewhere. const ( DefaultAnyConsistency ConsistencyString = "ANY" DefaultOneConsistency ConsistencyString = "ONE" DefaultTwoConsistency ConsistencyString = "TWO" DefaultThreeConsistency ConsistencyString = "THREE" DefaultQuorumConsistency ConsistencyString = "QUORUM" DefaultAllConsistency ConsistencyString = "ALL" DefaultLocalQuorumConsistency ConsistencyString = "LOCAL_QUORUM" DefaultEachQuorumConsistency ConsistencyString = "EACH_QUORUM" DefaultSerialConsistency ConsistencyString = "SERIAL" DefaultLocalSerialConsistency ConsistencyString = "LOCAL_SERIAL" DefaultLocalOneConsistency ConsistencyString = "LOCAL_ONE" ) func (cc *ConnectionConfig) GetRootCAPool() (*x509.CertPool, error) { caPool := x509.NewCertPool() for dcName, dc := range cc.Datacenters { if len(dc.CertificateAuthorityData) == 0 && len(dc.CertificateAuthorityPath) == 0 { return nil, fmt.Errorf("datacenter %q does not include certificate authority", dcName) } caData, err := cc.getDataOrReadFile(dc.CertificateAuthorityData, dc.CertificateAuthorityPath) if err != nil { return nil, fmt.Errorf("can't read datacenter %q certificate authority file from %q: %w", dcName, dc.CertificateAuthorityPath, err) } caPool.AppendCertsFromPEM(caData) } return caPool, nil } func (cc *ConnectionConfig) GetDatacenterCAPool(datacenterName string) (*x509.CertPool, error) { dc, ok := cc.Datacenters[datacenterName] if !ok { return nil, fmt.Errorf("datacenter %q not found in cloud connection config", datacenterName) } caPool := x509.NewCertPool() if len(dc.CertificateAuthorityData) == 0 && len(dc.CertificateAuthorityPath) == 0 { return nil, fmt.Errorf("datacenter %q does not include certificate authority", datacenterName) } caData, err := cc.getDataOrReadFile(dc.CertificateAuthorityData, dc.CertificateAuthorityPath) if err != nil { return nil, fmt.Errorf("can't read datacenter %q certificate authority file from %q: %w", datacenterName, dc.CertificateAuthorityPath, err) } caPool.AppendCertsFromPEM(caData) return caPool, nil } func (cc *ConnectionConfig) GetInitialContactPoints() []string { hosts := make([]string, 0, len(cc.Datacenters)) for _, dc := range cc.Datacenters { hosts = append(hosts, dc.Server) } return hosts } func (cc *ConnectionConfig) getDataOrReadFile(data []byte, path string) ([]byte, error) { if len(data) == 0 { return os.ReadFile(path) } return data, nil } func (cc *ConnectionConfig) GetCurrentDatacenterConfig() (*Datacenter, error) { contextConf, err := cc.GetCurrentContextConfig() if err != nil { return nil, fmt.Errorf("can't get current context config: %w", err) } if len(contextConf.DatacenterName) == 0 { return nil, fmt.Errorf("datacenterName in current context can't be empty") } dcConf, ok := cc.Datacenters[contextConf.DatacenterName] if !ok { return nil, fmt.Errorf("datacenter %q does not exists", contextConf.DatacenterName) } return dcConf, nil } func (cc *ConnectionConfig) GetCurrentContextConfig() (*Context, error) { if len(cc.CurrentContext) == 0 { return nil, fmt.Errorf("current context can't be empty") } contextConf, ok := cc.Contexts[cc.CurrentContext] if !ok { return nil, fmt.Errorf("context %q does not exists", cc.CurrentContext) } return contextConf, nil } func (cc *ConnectionConfig) GetCurrentAuthInfo() (*AuthInfo, error) { contextConf, err := cc.GetCurrentContextConfig() if err != nil { return nil, fmt.Errorf("can't get current context config: %w", err) } if len(contextConf.AuthInfoName) == 0 { return nil, fmt.Errorf("authInfo in current context can't be empty") } authInfo, ok := cc.AuthInfos[contextConf.AuthInfoName] if !ok { return nil, fmt.Errorf("authInfo %q does not exists", contextConf.AuthInfoName) } return authInfo, nil } func (cc *ConnectionConfig) GetClientCertificate() (*tls.Certificate, error) { authInfo, err := cc.GetCurrentAuthInfo() if err != nil { return nil, fmt.Errorf("can't get current auth info: %w", err) } clientCert, err := cc.getDataOrReadFile(authInfo.ClientCertificateData, authInfo.ClientCertificatePath) if err != nil { return nil, fmt.Errorf("can't read client certificate: %w", err) } clientKey, err := cc.getDataOrReadFile(authInfo.ClientKeyData, authInfo.ClientKeyPath) if err != nil { return nil, fmt.Errorf("can't read client key: %w", err) } cert, err := tls.X509KeyPair(clientCert, clientKey) if err != nil { return nil, fmt.Errorf("can't create x509 pair: %w", err) } return &cert, nil } ================================================ FILE: scyllacloud/config_test.go ================================================ // Copyright (C) 2021 ScyllaDB //go:build unit // +build unit package scyllacloud import ( "fmt" "os" "reflect" "testing" "sigs.k8s.io/yaml" "github.com/gocql/gocql/internal/tests" "github.com/gocql/gocql" ) func TestCloudCluster(t *testing.T) { t.Parallel() var ( singleDCConfig = func() *ConnectionConfig { return &ConnectionConfig{ Datacenters: map[string]*Datacenter{ "dc-1": { CertificateAuthorityPath: "../testdata/pki/ca.crt", Server: "eu.cloud.scylladb.com", TLSServerName: "some-host", NodeDomain: "eu.cloud.scylladb.com", }, }, AuthInfos: map[string]*AuthInfo{ "ai-1": { Username: "username", Password: "password", ClientKeyPath: "../testdata/pki/gocql.key", ClientCertificatePath: "../testdata/pki/gocql.crt", }, }, Contexts: map[string]*Context{ "default-context": { AuthInfoName: "ai-1", DatacenterName: "dc-1", }, }, CurrentContext: "default-context", } } multiDCConfig = func() *ConnectionConfig { cc := singleDCConfig() cc.Datacenters["dc-2"] = &Datacenter{ CertificateAuthorityPath: "../testdata/pki/ca.crt", Server: "cloud.scylladb.com", TLSServerName: "some-host", NodeDomain: "cloud.scylladb.com", ProxyURL: "socks5://127.0.0.1:5215", } return cc } ) ts := []struct { name string createConfig func() (*ConnectionConfig, string) expectedError error verifyClusterConfig func(*testing.T, *ConnectionConfig, *gocql.ClusterConfig) }{ { name: "current context points to unknown context", createConfig: func() (*ConnectionConfig, string) { return writeCloudConnectionConfigToTemp(t, &ConnectionConfig{ CurrentContext: "unknown", }) }, expectedError: fmt.Errorf("current context points to unknown context"), }, { name: "context auth info points to unknown auth info", createConfig: func() (*ConnectionConfig, string) { return writeCloudConnectionConfigToTemp(t, &ConnectionConfig{ Contexts: map[string]*Context{ "default": { AuthInfoName: "unknown", }, }, CurrentContext: "default", }) }, expectedError: fmt.Errorf("context %q auth info points to unknown authinfo", "default"), }, { name: "context datacenter points to unknown datacenter", createConfig: func() (*ConnectionConfig, string) { return writeCloudConnectionConfigToTemp(t, &ConnectionConfig{ AuthInfos: map[string]*AuthInfo{ "default": {}, }, Contexts: map[string]*Context{ "default": { AuthInfoName: "default", DatacenterName: "unknown", }, }, CurrentContext: "default", }) }, expectedError: fmt.Errorf("context %q datacenter points to unknown datacenter", "default"), }, { name: "invalid default consistency", createConfig: func() (*ConnectionConfig, string) { cc := singleDCConfig() cc.Parameters = &Parameters{ DefaultConsistency: DefaultSerialConsistency, } return writeCloudConnectionConfigToTemp(t, cc) }, expectedError: fmt.Errorf("invalid value of default consistency %q, values can be one of: [THREE ONE TWO ANY QUORUM ALL LOCAL_QUORUM EACH_QUORUM LOCAL_ONE]", DefaultSerialConsistency), }, { name: "invalid default serial consistency", createConfig: func() (*ConnectionConfig, string) { cc := singleDCConfig() cc.Parameters = &Parameters{ DefaultSerialConsistency: DefaultQuorumConsistency, } return writeCloudConnectionConfigToTemp(t, cc) }, expectedError: fmt.Errorf("invalid value of default serial consistency %q, values can be one of: [SERIAL LOCAL_SERIAL]", DefaultQuorumConsistency), }, { name: "initial contact points are taken from all available datacenters", createConfig: func() (*ConnectionConfig, string) { return writeCloudConnectionConfigToTemp(t, multiDCConfig()) }, expectedError: nil, verifyClusterConfig: func(t *testing.T, connConfig *ConnectionConfig, config *gocql.ClusterConfig) { if len(connConfig.Datacenters) != len(config.Hosts) { t.Errorf("initial contact points does not use all datacenters") } }, }, { name: "certificate validation is off if any dc requires it", createConfig: func() (*ConnectionConfig, string) { cc := multiDCConfig() cc.Datacenters["dc-1"].InsecureSkipTLSVerify = true return writeCloudConnectionConfigToTemp(t, cc) }, expectedError: nil, verifyClusterConfig: func(t *testing.T, connConfig *ConnectionConfig, config *gocql.ClusterConfig) { if !config.SslOpts.Config.InsecureSkipVerify { t.Errorf("expected disabled certificate verification") } }, }, { name: "authentication credentials are taken from current auth info", createConfig: func() (*ConnectionConfig, string) { return writeCloudConnectionConfigToTemp(t, singleDCConfig()) }, expectedError: nil, verifyClusterConfig: func(t *testing.T, connConfig *ConnectionConfig, config *gocql.ClusterConfig) { authenticator, ok := config.Authenticator.(gocql.PasswordAuthenticator) if !ok { t.Errorf("expected PasswordAuthenticator, got %T", config.Authenticator) } currentContext := connConfig.Contexts[connConfig.CurrentContext] authInfo := connConfig.AuthInfos[currentContext.AuthInfoName] if authInfo.Username != authenticator.Username { t.Errorf("expected %q username, got %q", authInfo.Username, authenticator.Username) } if authInfo.Password != authenticator.Password { t.Errorf("expected %q password, got %q", authInfo.Password, authenticator.Password) } }, }, { name: "certificate and key data has priority over path to file containing it", createConfig: func() (*ConnectionConfig, string) { cc := singleDCConfig() caCert, err := os.ReadFile(cc.Datacenters["dc-1"].CertificateAuthorityPath) if err != nil { t.Fatal(err) } cc.Datacenters["dc-1"].CertificateAuthorityData = caCert cc.Datacenters["dc-1"].CertificateAuthorityPath = "/not-existing-path" clientKey, err := os.ReadFile(cc.AuthInfos["ai-1"].ClientKeyPath) if err != nil { t.Fatal(err) } cc.AuthInfos["ai-1"].ClientKeyData = clientKey cc.AuthInfos["ai-1"].ClientKeyPath = "/not-existing-path" clientCert, err := os.ReadFile(cc.AuthInfos["ai-1"].ClientCertificatePath) if err != nil { t.Fatal(err) } cc.AuthInfos["ai-1"].ClientCertificateData = clientCert cc.AuthInfos["ai-1"].ClientCertificatePath = "/not-existing-path" return writeCloudConnectionConfigToTemp(t, cc) }, expectedError: nil, }, } for i := range ts { test := ts[i] t.Run(test.name, func(t *testing.T) { t.Parallel() cc, path := test.createConfig() defer os.RemoveAll(path) cloudConfig, err := NewCloudCluster(path) if !tests.ErrEqual(err, test.expectedError) { t.Errorf("expected error %#v, got %#v", test.expectedError, err) } if test.verifyClusterConfig != nil { test.verifyClusterConfig(t, cc, cloudConfig) } }) } } func TestConnectionConfig_GetCurrentContextConfig(t *testing.T) { t.Parallel() tt := []struct { name string connConfig *ConnectionConfig expectedContext *Context expectedError error }{ { name: "empty current context", connConfig: &ConnectionConfig{ CurrentContext: "", }, expectedContext: nil, expectedError: fmt.Errorf("current context can't be empty"), }, { name: "not existing current context", connConfig: &ConnectionConfig{ CurrentContext: "not-existing-context", }, expectedContext: nil, expectedError: fmt.Errorf(`context "not-existing-context" does not exists`), }, { name: "context from current context is returned", connConfig: &ConnectionConfig{ Contexts: map[string]*Context{ "default": { AuthInfoName: "admin", DatacenterName: "us-east-1", }, }, CurrentContext: "default", }, expectedContext: &Context{ AuthInfoName: "admin", DatacenterName: "us-east-1", }, expectedError: nil, }, } for i := range tt { tc := tt[i] t.Run(tc.name, func(t *testing.T) { contextConf, err := tc.connConfig.GetCurrentContextConfig() if !tests.ErrEqual(err, tc.expectedError) { t.Errorf("expected error %#v, got %#v", tc.expectedError, err) } if !reflect.DeepEqual(tc.expectedContext, contextConf) { t.Errorf("expected context %#v, got %#v", tc.expectedContext, contextConf) } }) } } func TestConnectionConfig_GetCurrentAuthInfo(t *testing.T) { t.Parallel() tt := []struct { name string connConfig *ConnectionConfig expectedAuthInfo *AuthInfo expectedError error }{ { name: "empty current context", connConfig: &ConnectionConfig{ CurrentContext: "", }, expectedAuthInfo: nil, expectedError: fmt.Errorf("can't get current context config: %w", fmt.Errorf("current context can't be empty")), }, { name: "not existing current context", connConfig: &ConnectionConfig{ CurrentContext: "not-existing-context", }, expectedAuthInfo: nil, expectedError: fmt.Errorf("can't get current context config: %w", fmt.Errorf(`context "not-existing-context" does not exists`)), }, { name: "empty auth info name in current context", connConfig: &ConnectionConfig{ Contexts: map[string]*Context{ "default": { AuthInfoName: "", }, }, CurrentContext: "default", }, expectedAuthInfo: nil, expectedError: fmt.Errorf("authInfo in current context can't be empty"), }, { name: "not existing auth info name in current context", connConfig: &ConnectionConfig{ Contexts: map[string]*Context{ "default": { AuthInfoName: "not-existing-auth-info", }, }, CurrentContext: "default", }, expectedAuthInfo: nil, expectedError: fmt.Errorf(`authInfo "not-existing-auth-info" does not exists`), }, { name: "auth info from current context is returned", connConfig: &ConnectionConfig{ AuthInfos: map[string]*AuthInfo{ "admin": { ClientCertificatePath: "client-cert-path", ClientKeyPath: "client-key-path", Username: "username", Password: "password", }, }, Contexts: map[string]*Context{ "default": { AuthInfoName: "admin", }, }, CurrentContext: "default", }, expectedAuthInfo: &AuthInfo{ ClientCertificatePath: "client-cert-path", ClientKeyPath: "client-key-path", Username: "username", Password: "password", }, expectedError: nil, }, } for i := range tt { tc := tt[i] t.Run(tc.name, func(t *testing.T) { ai, err := tc.connConfig.GetCurrentAuthInfo() if !tests.ErrEqual(err, tc.expectedError) { t.Errorf("expected error %#v, got %#v", tc.expectedError, err) } if !reflect.DeepEqual(tc.expectedAuthInfo, ai) { t.Errorf("expected authInfo %#v, got %#v", tc.expectedAuthInfo, ai) } }) } } func TestConnectionConfig_GetCurrentDatacenterConfig(t *testing.T) { t.Parallel() tt := []struct { name string connConfig *ConnectionConfig expectedDatacenter *Datacenter expectedError error }{ { name: "empty current context", connConfig: &ConnectionConfig{ CurrentContext: "", }, expectedDatacenter: nil, expectedError: fmt.Errorf("can't get current context config: %w", fmt.Errorf("current context can't be empty")), }, { name: "not existing current context", connConfig: &ConnectionConfig{ CurrentContext: "not-existing-context", }, expectedDatacenter: nil, expectedError: fmt.Errorf("can't get current context config: %w", fmt.Errorf(`context "not-existing-context" does not exists`)), }, { name: "empty datacenter name in current context", connConfig: &ConnectionConfig{ Contexts: map[string]*Context{ "default": { DatacenterName: "", }, }, CurrentContext: "default", }, expectedDatacenter: nil, expectedError: fmt.Errorf("datacenterName in current context can't be empty"), }, { name: "not existing datacenter name in current context", connConfig: &ConnectionConfig{ Contexts: map[string]*Context{ "default": { DatacenterName: "not-existing-dc", }, }, CurrentContext: "default", }, expectedDatacenter: nil, expectedError: fmt.Errorf(`datacenter "not-existing-dc" does not exists`), }, { name: "datacenter from current context is returned", connConfig: &ConnectionConfig{ Datacenters: map[string]*Datacenter{ "us-east-1": { CertificateAuthorityPath: "path-to-ca-cert", Server: "server", TLSServerName: "tls-server-name", NodeDomain: "node-domain", InsecureSkipTLSVerify: true, ProxyURL: "proxy-url", }, }, Contexts: map[string]*Context{ "default": { DatacenterName: "us-east-1", }, }, CurrentContext: "default", }, expectedDatacenter: &Datacenter{ CertificateAuthorityPath: "path-to-ca-cert", Server: "server", TLSServerName: "tls-server-name", NodeDomain: "node-domain", InsecureSkipTLSVerify: true, ProxyURL: "proxy-url", }, expectedError: nil, }, } for i := range tt { tc := tt[i] t.Run(tc.name, func(t *testing.T) { dc, err := tc.connConfig.GetCurrentDatacenterConfig() if !tests.ErrEqual(err, tc.expectedError) { t.Errorf("expected error %#v, got %#v", tc.expectedError, err) } if !reflect.DeepEqual(tc.expectedDatacenter, dc) { t.Errorf("expected datacenter %v, got %v", tc.expectedDatacenter, dc) } }) } } func writeCloudConnectionConfigToTemp(t *testing.T, cc *ConnectionConfig) (*ConnectionConfig, string) { f, err := os.CreateTemp(os.TempDir(), "gocql-cloud-conn-config-") if err != nil { t.Fatal(err) } if err := f.Close(); err != nil { t.Fatal(err) } buf, err := yaml.Marshal(cc) if err != nil { t.Fatal(err) } if err := os.WriteFile(f.Name(), buf, 0600); err != nil { t.Fatal(err) } return cc, f.Name() } ================================================ FILE: scyllacloud/hostdialer.go ================================================ // Copyright (C) 2021 ScyllaDB package scyllacloud import ( "context" "crypto/tls" "fmt" "net" "net/url" "golang.org/x/net/proxy" "github.com/gocql/gocql" ) // SniHostDialer is able to dial particular host through SNI proxy. // TLS Config is build from ConnectionConfig based on datacenter where given node belongs. // SNI is constructed from host_id of a node, and NodeDomain taken from cloud config. type SniHostDialer struct { connConfig *ConnectionConfig dialer gocql.Dialer } func NewSniHostDialer(connConfig *ConnectionConfig, dialer gocql.Dialer) *SniHostDialer { return &SniHostDialer{ connConfig: connConfig, dialer: dialer, } } func (s *SniHostDialer) DialHost(ctx context.Context, host *gocql.HostInfo) (*gocql.DialedHost, error) { hostID := host.HostID() if len(hostID) == 0 { return s.dialInitialContactPoint(ctx) } dcName := host.DataCenter() dcConf, ok := s.connConfig.Datacenters[dcName] if !ok { return nil, fmt.Errorf("datacenter %q configuration not found in connection bundle", dcName) } dialer := s.dialer if len(dcConf.ProxyURL) != 0 { u, err := url.Parse(dcConf.ProxyURL) if err != nil { return nil, fmt.Errorf("can't parse proxy URL %q: %w", dcConf.ProxyURL, err) } d, err := proxy.FromURL(u, proxyDialerFunc(func(network, addr string) (net.Conn, error) { return dialer.DialContext(ctx, network, addr) })) if err != nil { return nil, fmt.Errorf("can't create proxy dialer: %w", err) } dialer = d.(proxy.ContextDialer) } sni := fmt.Sprintf("%s.%s", host.HostID(), dcConf.NodeDomain) clientCertificate, err := s.connConfig.GetClientCertificate() if err != nil { return nil, fmt.Errorf("can't get client certificate from configuration: %w", err) } ca, err := s.connConfig.GetDatacenterCAPool(dcName) if err != nil { return nil, fmt.Errorf("can't get root CA from configuration: %w", err) } return s.connect(ctx, dialer, dcConf.Server, &tls.Config{ ServerName: sni, RootCAs: ca, InsecureSkipVerify: dcConf.InsecureSkipTLSVerify, Certificates: []tls.Certificate{*clientCertificate}, }) } func (s *SniHostDialer) dialInitialContactPoint(ctx context.Context) (*gocql.DialedHost, error) { insecureSkipVerify := false for _, dc := range s.connConfig.Datacenters { if dc.InsecureSkipTLSVerify { insecureSkipVerify = true break } } clientCertificate, err := s.connConfig.GetClientCertificate() if err != nil { return nil, fmt.Errorf("can't get client certificate from configuration: %w", err) } ca, err := s.connConfig.GetRootCAPool() if err != nil { return nil, fmt.Errorf("can't get root CA from configuration: %w", err) } dcConf, err := s.connConfig.GetCurrentDatacenterConfig() if err != nil { return nil, fmt.Errorf("can't get current datacenter config: %w", err) } serverName := dcConf.NodeDomain if len(serverName) == 0 { serverName = dcConf.Server } return s.connect(ctx, s.dialer, dcConf.Server, &tls.Config{ ServerName: serverName, RootCAs: ca, InsecureSkipVerify: insecureSkipVerify, Certificates: []tls.Certificate{*clientCertificate}, }) } func (s *SniHostDialer) connect(ctx context.Context, dialer gocql.Dialer, server string, tlsConfig *tls.Config) (*gocql.DialedHost, error) { conn, err := dialer.DialContext(ctx, "tcp", server) if err != nil { return nil, fmt.Errorf("can't connect to %q: %w", server, err) } tconn := tls.Client(conn, tlsConfig) if err := tconn.HandshakeContext(ctx); err != nil { _ = conn.Close() return nil, fmt.Errorf("can't finish TLS handshake with server %q SNI %q: %w", server, tlsConfig.ServerName, err) } return &gocql.DialedHost{ Conn: tconn, DisableCoalesce: true, }, nil } type proxyDialerFunc func(network, addr string) (net.Conn, error) func (d proxyDialerFunc) Dial(network, addr string) (net.Conn, error) { return d(network, addr) } ================================================ FILE: scyllacloud/hostdialer_test.go ================================================ //go:build unit // +build unit package scyllacloud import ( "bytes" "context" "crypto/rand" "crypto/rsa" "crypto/tls" "crypto/x509" "crypto/x509/pkix" "encoding/pem" "fmt" "math/big" "net" "net/http/httptest" "os" "syscall" "testing" "time" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests" ) const ( testTimeout = time.Second ) func TestHostSNIDialer_InvalidConnectionConfig(t *testing.T) { t.Parallel() ctx, cancel := context.WithCancel(context.Background()) defer cancel() _, serverCertPem, clientCertPem, clientKeyPem, err := setupTLSServer(nil) if err != nil { t.Fatal(err) } dialer := &gocql.ScyllaShardAwareDialer{Dialer: net.Dialer{}} tt := []struct { name string connConfig *ConnectionConfig hostInfo *gocql.HostInfo expectedError error }{ { name: "empty current context", connConfig: func() *ConnectionConfig { cc := newBasicConnectionConf("127.0.0.1:9142", serverCertPem, clientCertPem, clientKeyPem) cc.CurrentContext = "" return cc }(), hostInfo: &gocql.HostInfo{}, expectedError: fmt.Errorf("can't get client certificate from configuration: %w", fmt.Errorf("can't get current auth info: %w", fmt.Errorf("can't get current context config: %w", fmt.Errorf("current context can't be empty")))), }, { name: "current context is unknown", connConfig: func() *ConnectionConfig { cc := newBasicConnectionConf("127.0.0.1:9142", serverCertPem, clientCertPem, clientKeyPem) cc.CurrentContext = "unknown-context" return cc }(), hostInfo: &gocql.HostInfo{}, expectedError: fmt.Errorf("can't get client certificate from configuration: %w", fmt.Errorf("can't get current auth info: %w", fmt.Errorf("can't get current context config: %w", fmt.Errorf(`context "unknown-context" does not exists`)))), }, { name: "unknown default authInfo", connConfig: func() *ConnectionConfig { cc := newBasicConnectionConf("127.0.0.1:9142", serverCertPem, clientCertPem, clientKeyPem) cc.Contexts[cc.CurrentContext].AuthInfoName = "unknown-authinfo" return cc }(), hostInfo: &gocql.HostInfo{}, expectedError: fmt.Errorf("can't get client certificate from configuration: %w", fmt.Errorf("can't get current auth info: %w", fmt.Errorf(`authInfo "unknown-authinfo" does not exists`))), }, { name: "empty client certificate", connConfig: newBasicConnectionConf("127.0.0.1:9142", serverCertPem, nil, clientKeyPem), hostInfo: &gocql.HostInfo{}, expectedError: fmt.Errorf("can't get client certificate from configuration: %w", fmt.Errorf("can't read client certificate: %w", &os.PathError{Op: "open", Path: "", Err: syscall.ENOENT})), }, { name: "empty client key", connConfig: newBasicConnectionConf("127.0.0.1:9142", serverCertPem, clientCertPem, nil), hostInfo: &gocql.HostInfo{}, expectedError: fmt.Errorf("can't get client certificate from configuration: %w", fmt.Errorf("can't read client key: %w", &os.PathError{Op: "open", Path: "", Err: syscall.ENOENT})), }, { name: "empty certificate authority", connConfig: newBasicConnectionConf("127.0.0.1:9142", nil, clientCertPem, clientKeyPem), hostInfo: &gocql.HostInfo{}, expectedError: fmt.Errorf("can't get root CA from configuration: %w", fmt.Errorf(`datacenter "us-east-1" does not include certificate authority`)), }, { name: "unknown default datacenter", connConfig: func() *ConnectionConfig { cc := newBasicConnectionConf("127.0.0.1:9142", serverCertPem, clientCertPem, clientKeyPem) cc.Contexts[cc.CurrentContext].DatacenterName = "unknown-datacenter" return cc }(), hostInfo: &gocql.HostInfo{}, expectedError: fmt.Errorf("can't get current datacenter config: %w", fmt.Errorf(`datacenter "unknown-datacenter" does not exists`)), }, { name: "unknown host datacenter", connConfig: newBasicConnectionConf("127.0.0.1:9142", serverCertPem, clientCertPem, clientKeyPem), hostInfo: func() *gocql.HostInfo { hi := gocql.HostInfoBuilder{ DataCenter: "unknown-datacenter", HostId: "a0000000-0000-0000-0000-000000000099", }.Build() return &hi }(), expectedError: fmt.Errorf(`datacenter "unknown-datacenter" configuration not found in connection bundle`), }, } for i := range tt { tc := tt[i] t.Run(tc.name, func(t *testing.T) { t.Parallel() hostDialer := NewSniHostDialer(tc.connConfig, dialer) _, err := hostDialer.DialHost(ctx, tc.hostInfo) if !tests.ErrEqual(err, tc.expectedError) { t.Errorf("expected error to be %#v, got %#v", tc.expectedError, err) } }) } } func TestHostSNIDialer_ServerNameIdentifiers(t *testing.T) { t.Parallel() tt := []struct { name string connConfig func(server string, serverCertPem, clientCertPem, clientKeyPem []byte) *ConnectionConfig hostInfo *gocql.HostInfo expectedSNI func(config *ConnectionConfig) string }{ { name: "node domain as SNI when host info is unknown", hostInfo: &gocql.HostInfo{}, connConfig: newBasicConnectionConf, expectedSNI: func(_ *ConnectionConfig) string { return "node.scylladb.com" }, }, { name: "server as SNI when host info is unknown and node domain is empty", hostInfo: &gocql.HostInfo{}, connConfig: func(server string, serverCertPem, clientCertPem, clientKeyPem []byte) *ConnectionConfig { cc := newBasicConnectionConf(server, serverCertPem, clientCertPem, clientKeyPem) dcConf := cc.Datacenters[cc.Contexts[cc.CurrentContext].DatacenterName] dcConf.NodeDomain = "" // Disable verification because serving cert isn't signed for IP address. dcConf.InsecureSkipTLSVerify = true return cc }, expectedSNI: func(cc *ConnectionConfig) string { return cc.Datacenters[cc.Contexts[cc.CurrentContext].DatacenterName].Server }, }, { name: "host SNI when host is known", hostInfo: func() *gocql.HostInfo { hi := gocql.HostInfoBuilder{ DataCenter: "us-east-1", HostId: "a0000000-0000-0000-0000-000000000001", }.Build() return &hi }(), connConfig: newBasicConnectionConf, expectedSNI: func(_ *ConnectionConfig) string { return "a0000000-0000-0000-0000-000000000001.node.scylladb.com" }, }, } for i := range tt { tc := tt[i] t.Run(tc.name, func(t *testing.T) { t.Parallel() ctx, cancel := context.WithTimeout(context.Background(), testTimeout) defer cancel() server, serverCertPem, clientCertPem, clientKeyPem, err := setupTLSServer([]string{"a0000000-0000-0000-0000-000000000001.node.scylladb.com", "node.scylladb.com"}) if err != nil { t.Fatal(err) } connectionStateCh := make(chan tls.ConnectionState, 1) server.TLS.VerifyConnection = func(state tls.ConnectionState) error { connectionStateCh <- state return nil } server.StartTLS() defer server.Close() dialer := &gocql.ScyllaShardAwareDialer{Dialer: net.Dialer{}} connConfig := tc.connConfig(server.Listener.Addr().String(), serverCertPem, clientCertPem, clientKeyPem) hostDialer := NewSniHostDialer(connConfig, dialer) _, err = hostDialer.DialHost(ctx, tc.hostInfo) if err != nil { t.Fatal(err) } select { case receivedState := <-connectionStateCh: expectedSNI := tc.expectedSNI(connConfig) if receivedState.ServerName != expectedSNI { t.Errorf("expected %q SNI, got %q", expectedSNI, receivedState.ServerName) } case <-ctx.Done(): t.Fatal("expected to receive connection, but timed out") } }) } } func setupTLSServer(dnsDomains []string) (*httptest.Server, []byte, []byte, []byte, error) { clientCert, clientKey, err := generateClientCert() if err != nil { return nil, nil, nil, nil, err } clientCertPem, err := encodeCertificates(clientCert) if err != nil { return nil, nil, nil, nil, err } clientKeyPem, err := encodePrivateKey(clientKey) if err != nil { return nil, nil, nil, nil, err } clientCAPool := x509.NewCertPool() clientCAPool.AppendCertsFromPEM(clientCertPem) serverCert, serverKey, err := generateServingCert(dnsDomains) if err != nil { return nil, nil, nil, nil, err } serverCertPem, err := encodeCertificates(serverCert) if err != nil { return nil, nil, nil, nil, err } serverKeyPem, err := encodePrivateKey(serverKey) if err != nil { return nil, nil, nil, nil, err } servingCert, err := tls.X509KeyPair(serverCertPem, serverKeyPem) if err != nil { return nil, nil, nil, nil, err } server := httptest.NewUnstartedServer(nil) server.TLS = &tls.Config{ Certificates: []tls.Certificate{servingCert}, ClientCAs: clientCAPool, ClientAuth: tls.RequestClientCert, } return server, serverCertPem, clientCertPem, clientKeyPem, nil } func newBasicConnectionConf(server string, serverCertPem, clientCertPem, clientKeyPem []byte) *ConnectionConfig { return &ConnectionConfig{ Datacenters: map[string]*Datacenter{ "us-east-1": { CertificateAuthorityData: serverCertPem, Server: server, NodeDomain: "node.scylladb.com", }, }, AuthInfos: map[string]*AuthInfo{ "admin": { ClientCertificateData: clientCertPem, ClientKeyData: clientKeyPem, }, }, Contexts: map[string]*Context{ "default": { DatacenterName: "us-east-1", AuthInfoName: "admin", }, }, CurrentContext: "default", } } func generateServingCert(dnsNames []string) (*x509.Certificate, *rsa.PrivateKey, error) { privateKey, err := rsa.GenerateKey(rand.Reader, 1028) if err != nil { return nil, nil, fmt.Errorf("can't generate private key: %w", err) } commonName := "serving-cert" cert, err := generateSelfSignedX509Certificate(commonName, []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, dnsNames, &privateKey.PublicKey, privateKey) if err != nil { return nil, nil, err } return cert, privateKey, nil } func generateClientCert() (*x509.Certificate, *rsa.PrivateKey, error) { privateKey, err := rsa.GenerateKey(rand.Reader, 1028) if err != nil { return nil, nil, fmt.Errorf("can't generate private key: %w", err) } commonName := "client" cert, err := generateSelfSignedX509Certificate(commonName, []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, nil, &privateKey.PublicKey, privateKey) if err != nil { return nil, nil, err } return cert, privateKey, nil } func generateSerialNumber() (*big.Int, error) { serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) if err != nil { return nil, err } return serialNumber, nil } func generateSelfSignedX509Certificate(cn string, extKeyUsage []x509.ExtKeyUsage, dnsNames []string, pub, priv any) (*x509.Certificate, error) { now := time.Now() serialNumber, err := generateSerialNumber() if err != nil { return nil, err } template := &x509.Certificate{ Subject: pkix.Name{ CommonName: cn, }, IsCA: false, KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, ExtKeyUsage: extKeyUsage, NotBefore: now.Add(-1 * time.Second), NotAfter: now.Add(time.Hour), SignatureAlgorithm: x509.SHA512WithRSA, BasicConstraintsValid: true, SerialNumber: serialNumber, DNSNames: dnsNames, } derBytes, err := x509.CreateCertificate(rand.Reader, template, template, pub, priv) if err != nil { return nil, fmt.Errorf("can't create certificate: %w", err) } certs, err := x509.ParseCertificates(derBytes) if err != nil { return nil, fmt.Errorf("can't parse der encoded certificate: %w", err) } if len(certs) != 1 { return nil, fmt.Errorf("expected to parse 1 certificate from der bytes but %d were present", len(certs)) } return certs[0], nil } func encodeCertificates(certificates ...*x509.Certificate) ([]byte, error) { buffer := bytes.Buffer{} for _, certificate := range certificates { err := pem.Encode(&buffer, &pem.Block{ Type: "CERTIFICATE", Bytes: certificate.Raw, }) if err != nil { return nil, fmt.Errorf("can't pem encode certificate: %w", err) } } return buffer.Bytes(), nil } func encodePrivateKey(key *rsa.PrivateKey) ([]byte, error) { buffer := bytes.Buffer{} err := pem.Encode(&buffer, &pem.Block{ Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key), }) if err != nil { return nil, fmt.Errorf("can't pem encode rsa private key: %w", err) } return buffer.Bytes(), nil } ================================================ FILE: serialization/ascii/marshal.go ================================================ package ascii import ( "reflect" ) func Marshal(value any) ([]byte, error) { switch v := value.(type) { case nil: return nil, nil case string: return EncString(v) case *string: return EncStringR(v) case []byte: return EncBytes(v) case *[]byte: return EncBytesR(v) default: // Custom types (type MyString string) can be serialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.ValueOf(value) if rv.Kind() != reflect.Ptr { return EncReflect(rv) } return EncReflectR(rv) } } ================================================ FILE: serialization/ascii/marshal_utils.go ================================================ package ascii import ( "fmt" "reflect" ) func EncString(v string) ([]byte, error) { return encString(v), nil } func EncStringR(v *string) ([]byte, error) { if v == nil { return nil, nil } return encString(*v), nil } func EncBytes(v []byte) ([]byte, error) { return v, nil } func EncBytesR(v *[]byte) ([]byte, error) { if v == nil { return nil, nil } return *v, nil } func EncReflect(v reflect.Value) ([]byte, error) { switch v.Kind() { case reflect.String: return encString(v.String()), nil case reflect.Slice: if v.Type().Elem().Kind() != reflect.Uint8 { return nil, fmt.Errorf("failed to marshal ascii: unsupported value type (%T)(%[1]v), supported types: ~string, ~[]byte, unsetColumn", v.Interface()) } return EncBytes(v.Bytes()) case reflect.Struct: if v.Type().String() == "gocql.unsetColumn" { return nil, nil } return nil, fmt.Errorf("failed to marshal ascii: unsupported value type (%T)(%[1]v), supported types: ~string, ~[]byte, unsetColumn", v.Interface()) default: return nil, fmt.Errorf("failed to marshal ascii: unsupported value type (%T)(%[1]v), supported types: ~string, ~[]byte, unsetColumn", v.Interface()) } } func EncReflectR(v reflect.Value) ([]byte, error) { if v.IsNil() { return nil, nil } return EncReflect(v.Elem()) } func encString(v string) []byte { if v == "" { return make([]byte, 0) } return []byte(v) } ================================================ FILE: serialization/ascii/unmarshal.go ================================================ package ascii import ( "fmt" "reflect" ) func Unmarshal(data []byte, value any) error { switch v := value.(type) { case nil: return nil case *string: return DecString(data, v) case **string: return DecStringR(data, v) case *[]byte: return DecBytes(data, v) case **[]byte: return DecBytesR(data, v) default: // Custom types (type MyString string) can be deserialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.ValueOf(value) rt := rv.Type() if rt.Kind() != reflect.Ptr { return fmt.Errorf("failed to unmarshal ascii: unsupported value type (%T)(%[1]v), supported types: ~string, ~[]byte", v) } if rt.Elem().Kind() != reflect.Ptr { return DecReflect(data, rv) } return DecReflectR(data, rv) } } ================================================ FILE: serialization/ascii/unmarshal_utils.go ================================================ package ascii import ( "fmt" "reflect" ) func errInvalidData(p []byte) error { for i := range p { if p[i] > 127 { return fmt.Errorf("failed to unmarshal ascii: invalid charester %s", string(p[i])) } } return nil } func errNilReference(v any) error { return fmt.Errorf("failed to unmarshal ascii: can not unmarshal into nil reference(%T)(%[1]v)", v) } func DecString(p []byte, v *string) error { if v == nil { return errNilReference(v) } *v = decString(p) return errInvalidData(p) } func DecStringR(p []byte, v **string) error { if v == nil { return errNilReference(v) } *v = decStringR(p) return errInvalidData(p) } func DecBytes(p []byte, v *[]byte) error { if v == nil { return errNilReference(v) } if p == nil { *v = nil return nil } if len(p) == 0 { *v = make([]byte, 0) return nil } *v = append((*v)[:0], p...) return errInvalidData(p) } func DecBytesR(p []byte, v **[]byte) error { if v == nil { return errNilReference(v) } *v = decBytesR(p) return errInvalidData(p) } func DecReflect(p []byte, v reflect.Value) error { if v.IsNil() { return errNilReference(v) } switch v = v.Elem(); v.Kind() { case reflect.String: v.SetString(decString(p)) case reflect.Slice: if v.Type().Elem().Kind() != reflect.Uint8 { return fmt.Errorf("failed to marshal ascii: unsupported value type (%T)(%[1]v), supported types: ~string, ~[]byte", v.Interface()) } v.SetBytes(decBytes(p)) default: return fmt.Errorf("failed to unmarshal ascii: unsupported value type (%T)(%[1]v), supported types: ~string, ~[]byte", v.Interface()) } return errInvalidData(p) } func DecReflectR(p []byte, v reflect.Value) error { if v.IsNil() { return errNilReference(v) } switch ev := v.Type().Elem().Elem(); ev.Kind() { case reflect.String: return decReflectStringR(p, v) case reflect.Slice: if ev.Elem().Kind() != reflect.Uint8 { return fmt.Errorf("failed to marshal ascii: unsupported value type (%T)(%[1]v), supported types: ~string, ~[]byte", v.Interface()) } return decReflectBytesR(p, v) default: return fmt.Errorf("failed to unmarshal ascii: unsupported value type (%T)(%[1]v), supported types: ~string, ~[]byte", v.Interface()) } } func decReflectStringR(p []byte, v reflect.Value) error { if len(p) == 0 { if p == nil { v.Elem().Set(reflect.Zero(v.Elem().Type())) } else { v.Elem().Set(reflect.New(v.Type().Elem().Elem())) } return nil } val := reflect.New(v.Type().Elem().Elem()) val.Elem().SetString(string(p)) v.Elem().Set(val) return errInvalidData(p) } func decReflectBytesR(p []byte, v reflect.Value) error { if len(p) == 0 { if p == nil { v.Elem().Set(reflect.Zero(v.Elem().Type())) } else { val := reflect.New(v.Type().Elem().Elem()) val.Elem().SetBytes(make([]byte, 0)) v.Elem().Set(val) } return nil } tmp := make([]byte, len(p)) copy(tmp, p) val := reflect.New(v.Type().Elem().Elem()) val.Elem().SetBytes(tmp) v.Elem().Set(val) return errInvalidData(p) } func decString(p []byte) string { if len(p) == 0 { return "" } return string(p) } func decStringR(p []byte) *string { if len(p) == 0 { if p == nil { return nil } return new(string) } tmp := string(p) return &tmp } func decBytes(p []byte) []byte { if len(p) == 0 { if p == nil { return nil } return make([]byte, 0) } tmp := make([]byte, len(p)) copy(tmp, p) return tmp } func decBytesR(p []byte) *[]byte { if len(p) == 0 { if p == nil { return nil } tmp := make([]byte, 0) return &tmp } tmp := make([]byte, len(p)) copy(tmp, p) return &tmp } ================================================ FILE: serialization/bigint/marshal.go ================================================ package bigint import ( "math/big" "reflect" ) func Marshal(value any) ([]byte, error) { switch v := value.(type) { case nil: return nil, nil case int8: return EncInt8(v) case int16: return EncInt16(v) case int32: return EncInt32(v) case int64: return EncInt64(v) case int: return EncInt(v) case uint8: return EncUint8(v) case uint16: return EncUint16(v) case uint32: return EncUint32(v) case uint64: return EncUint64(v) case uint: return EncUint(v) case big.Int: return EncBigInt(v) case string: return EncString(v) case *int8: return EncInt8R(v) case *int16: return EncInt16R(v) case *int32: return EncInt32R(v) case *int64: return EncInt64R(v) case *int: return EncIntR(v) case *uint8: return EncUint8R(v) case *uint16: return EncUint16R(v) case *uint32: return EncUint32R(v) case *uint64: return EncUint64R(v) case *uint: return EncUintR(v) case *big.Int: return EncBigIntR(v) case *string: return EncStringR(v) default: // Custom types (type MyInt int) can be serialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.TypeOf(value) if rv.Kind() != reflect.Ptr { return EncReflect(reflect.ValueOf(v)) } return EncReflectR(reflect.ValueOf(v)) } } ================================================ FILE: serialization/bigint/marshal_utils.go ================================================ package bigint import ( "fmt" "math/big" "reflect" "strconv" ) const supportedTypes = "~int8, ~int16, ~int32, ~int64, ~int, ~uint8, ~uint16, ~uint32, ~uint64, ~uint, ~string, big.Int" func EncInt8(v int8) ([]byte, error) { if v < 0 { return []byte{255, 255, 255, 255, 255, 255, 255, byte(v)}, nil } return []byte{0, 0, 0, 0, 0, 0, 0, byte(v)}, nil } func EncInt8R(v *int8) ([]byte, error) { if v == nil { return nil, nil } return EncInt8(*v) } func EncInt16(v int16) ([]byte, error) { if v < 0 { return []byte{255, 255, 255, 255, 255, 255, byte(v >> 8), byte(v)}, nil } return []byte{0, 0, 0, 0, 0, 0, byte(v >> 8), byte(v)}, nil } func EncInt16R(v *int16) ([]byte, error) { if v == nil { return nil, nil } return EncInt16(*v) } func EncInt32(v int32) ([]byte, error) { if v < 0 { return []byte{255, 255, 255, 255, byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)}, nil } return []byte{0, 0, 0, 0, byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)}, nil } func EncInt32R(v *int32) ([]byte, error) { if v == nil { return nil, nil } return EncInt32(*v) } func EncInt64(v int64) ([]byte, error) { return encInt64(v), nil } func EncInt64R(v *int64) ([]byte, error) { if v == nil { return nil, nil } return EncInt64(*v) } func EncInt(v int) ([]byte, error) { return []byte{byte(v >> 56), byte(v >> 48), byte(v >> 40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)}, nil } func EncIntR(v *int) ([]byte, error) { if v == nil { return nil, nil } return EncInt(*v) } func EncUint8(v uint8) ([]byte, error) { return []byte{0, 0, 0, 0, 0, 0, 0, v}, nil } func EncUint8R(v *uint8) ([]byte, error) { if v == nil { return nil, nil } return EncUint8(*v) } func EncUint16(v uint16) ([]byte, error) { return []byte{0, 0, 0, 0, 0, 0, byte(v >> 8), byte(v)}, nil } func EncUint16R(v *uint16) ([]byte, error) { if v == nil { return nil, nil } return EncUint16(*v) } func EncUint32(v uint32) ([]byte, error) { return []byte{0, 0, 0, 0, byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)}, nil } func EncUint32R(v *uint32) ([]byte, error) { if v == nil { return nil, nil } return EncUint32(*v) } func EncUint64(v uint64) ([]byte, error) { return []byte{byte(v >> 56), byte(v >> 48), byte(v >> 40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)}, nil } func EncUint64R(v *uint64) ([]byte, error) { if v == nil { return nil, nil } return EncUint64(*v) } func EncUint(v uint) ([]byte, error) { return []byte{byte(v >> 56), byte(v >> 48), byte(v >> 40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)}, nil } func EncUintR(v *uint) ([]byte, error) { if v == nil { return nil, nil } return EncUint(*v) } func EncBigInt(v big.Int) ([]byte, error) { if !v.IsInt64() { return nil, fmt.Errorf("failed to marshal bigint: value (%T)(%s) out of range", v, v.String()) } return encInt64(v.Int64()), nil } func EncBigIntR(v *big.Int) ([]byte, error) { if v == nil { return nil, nil } if !v.IsInt64() { return nil, fmt.Errorf("failed to marshal bigint: value (%T)(%s) out of range", v, v.String()) } return encInt64(v.Int64()), nil } func EncString(v string) ([]byte, error) { if v == "" { return nil, nil } n, err := strconv.ParseInt(v, 10, 64) if err != nil { return nil, fmt.Errorf("failed to marshal bigint: can not marshal (%T)(%[1]v) %s", v, err) } return encInt64(n), nil } func EncStringR(v *string) ([]byte, error) { if v == nil { return nil, nil } return EncString(*v) } func EncReflect(v reflect.Value) ([]byte, error) { switch v.Kind() { case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8: return EncInt64(v.Int()) case reflect.Uint, reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8: return EncUint64(v.Uint()) case reflect.String: val := v.String() if val == "" { return nil, nil } n, err := strconv.ParseInt(val, 10, 64) if err != nil { return nil, fmt.Errorf("failed to marshal bigint: can not marshal (%T)(%[1]v) %s", v.Interface(), err) } return encInt64(n), nil case reflect.Struct: if v.Type().String() == "gocql.unsetColumn" { return nil, nil } return nil, fmt.Errorf("failed to marshal bigint: unsupported value type (%T)(%[1]v), supported types: %s, unsetColumn", v.Interface(), supportedTypes) default: return nil, fmt.Errorf("failed to marshal bigint: unsupported value type (%T)(%[1]v), supported types: %s, unsetColumn", v.Interface(), supportedTypes) } } func EncReflectR(v reflect.Value) ([]byte, error) { if v.IsNil() { return nil, nil } return EncReflect(v.Elem()) } func encInt64(v int64) []byte { return []byte{byte(v >> 56), byte(v >> 48), byte(v >> 40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} } ================================================ FILE: serialization/bigint/unmarshal.go ================================================ package bigint import ( "fmt" "math/big" "reflect" ) func Unmarshal(data []byte, value any) error { switch v := value.(type) { case nil: return nil case *int8: return DecInt8(data, v) case *int16: return DecInt16(data, v) case *int32: return DecInt32(data, v) case *int64: return DecInt64(data, v) case *int: return DecInt(data, v) case *uint8: return DecUint8(data, v) case *uint16: return DecUint16(data, v) case *uint32: return DecUint32(data, v) case *uint64: return DecUint64(data, v) case *uint: return DecUint(data, v) case *big.Int: return DecBigInt(data, v) case *string: return DecString(data, v) case **int8: return DecInt8R(data, v) case **int16: return DecInt16R(data, v) case **int32: return DecInt32R(data, v) case **int64: return DecInt64R(data, v) case **int: return DecIntR(data, v) case **uint8: return DecUint8R(data, v) case **uint16: return DecUint16R(data, v) case **uint32: return DecUint32R(data, v) case **uint64: return DecUint64R(data, v) case **uint: return DecUintR(data, v) case **big.Int: return DecBigIntR(data, v) case **string: return DecStringR(data, v) default: // Custom types (type MyInt int) can be deserialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.ValueOf(value) rt := rv.Type() if rt.Kind() != reflect.Ptr { return fmt.Errorf("failed to unmarshal bigint: unsupported value type (%T)(%[1]v), supported types: %s", value, supportedTypes) } if rt.Elem().Kind() != reflect.Ptr { return DecReflect(data, rv) } return DecReflectR(data, rv) } } ================================================ FILE: serialization/bigint/unmarshal_utils.go ================================================ package bigint import ( "fmt" "math" "math/big" "reflect" "strconv" ) var errWrongDataLen = fmt.Errorf("failed to unmarshal bigint: the length of the data should be 0 or 8") func errNilReference(v any) error { return fmt.Errorf("failed to unmarshal bigint: can not unmarshal into nil reference (%T)(%[1]v))", v) } func DecInt8(p []byte, v *int8) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 8: val := decInt64(p) if val > math.MaxInt8 || val < math.MinInt8 { return fmt.Errorf("failed to unmarshal bigint: to unmarshal into int8, the data should be in the int8 range") } *v = int8(val) default: return errWrongDataLen } return nil } func DecInt8R(p []byte, v **int8) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(int8) } case 8: val := decInt64(p) if val > math.MaxInt8 || val < math.MinInt8 { return fmt.Errorf("failed to unmarshal bigint: to unmarshal into int8, the data should be in the int8 range") } tmp := int8(val) *v = &tmp default: return errWrongDataLen } return nil } func DecInt16(p []byte, v *int16) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 8: val := decInt64(p) if val > math.MaxInt16 || val < math.MinInt16 { return fmt.Errorf("failed to unmarshal bigint: to unmarshal into int16, the data should be in the int16 range") } *v = int16(val) default: return errWrongDataLen } return nil } func DecInt16R(p []byte, v **int16) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(int16) } case 8: val := decInt64(p) if val > math.MaxInt16 || val < math.MinInt16 { return fmt.Errorf("failed to unmarshal bigint: to unmarshal into int16, the data should be in the int16 range") } tmp := int16(val) *v = &tmp default: return errWrongDataLen } return nil } func DecInt32(p []byte, v *int32) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 8: val := decInt64(p) if val > math.MaxInt32 || val < math.MinInt32 { return fmt.Errorf("failed to unmarshal bigint: to unmarshal into int32, the data should be in the int32 range") } *v = int32(val) default: return errWrongDataLen } return nil } func DecInt32R(p []byte, v **int32) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(int32) } case 8: val := decInt64(p) if val > math.MaxInt32 || val < math.MinInt32 { return fmt.Errorf("failed to unmarshal bigint: to unmarshal into int32, the data should be in the int32 range") } tmp := int32(val) *v = &tmp default: return errWrongDataLen } return nil } func DecInt64(p []byte, v *int64) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 8: *v = decInt64(p) default: return errWrongDataLen } return nil } func DecInt64R(p []byte, v **int64) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(int64) } case 8: val := decInt64(p) *v = &val default: return errWrongDataLen } return nil } func DecInt(p []byte, v *int) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 8: *v = int(p[0])<<56 | int(p[1])<<48 | int(p[2])<<40 | int(p[3])<<32 | int(p[4])<<24 | int(p[5])<<16 | int(p[6])<<8 | int(p[7]) default: return errWrongDataLen } return nil } func DecIntR(p []byte, v **int) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(int) } case 8: val := int(p[0])<<56 | int(p[1])<<48 | int(p[2])<<40 | int(p[3])<<32 | int(p[4])<<24 | int(p[5])<<16 | int(p[6])<<8 | int(p[7]) *v = &val default: return errWrongDataLen } return nil } func DecUint8(p []byte, v *uint8) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 8: if p[0] != 0 || p[1] != 0 || p[2] != 0 || p[3] != 0 || p[4] != 0 || p[5] != 0 || p[6] != 0 { return fmt.Errorf("failed to unmarshal bigint: to unmarshal into uint8, the data should be in the uint8 range") } *v = p[7] default: return errWrongDataLen } return nil } func DecUint8R(p []byte, v **uint8) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(uint8) } case 8: if p[0] != 0 || p[1] != 0 || p[2] != 0 || p[3] != 0 || p[4] != 0 || p[5] != 0 || p[6] != 0 { return fmt.Errorf("failed to unmarshal bigint: to unmarshal into uint8, the data should be in the uint8 range") } val := p[7] *v = &val default: return errWrongDataLen } return nil } func DecUint16(p []byte, v *uint16) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 8: if p[0] != 0 || p[1] != 0 || p[2] != 0 || p[3] != 0 || p[4] != 0 || p[5] != 0 { return fmt.Errorf("failed to unmarshal bigint: to unmarshal into uint16, the data should be in the uint16 range") } *v = uint16(p[6])<<8 | uint16(p[7]) default: return errWrongDataLen } return nil } func DecUint16R(p []byte, v **uint16) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(uint16) } case 8: if p[0] != 0 || p[1] != 0 || p[2] != 0 || p[3] != 0 || p[4] != 0 || p[5] != 0 { return fmt.Errorf("failed to unmarshal bigint: to unmarshal into uint16, the data should be in the uint16 range") } val := uint16(p[6])<<8 | uint16(p[7]) *v = &val default: return errWrongDataLen } return nil } func DecUint32(p []byte, v *uint32) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 8: if p[0] != 0 || p[1] != 0 || p[2] != 0 || p[3] != 0 { return fmt.Errorf("failed to unmarshal bigint: to unmarshal into uint32, the data should be in the uint32 range") } *v = uint32(p[4])<<24 | uint32(p[5])<<16 | uint32(p[6])<<8 | uint32(p[7]) default: return errWrongDataLen } return nil } func DecUint32R(p []byte, v **uint32) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(uint32) } case 8: if p[0] != 0 || p[1] != 0 || p[2] != 0 || p[3] != 0 { return fmt.Errorf("failed to unmarshal bigint: to unmarshal into uint32, the data should be in the uint32 range") } val := uint32(p[4])<<24 | uint32(p[5])<<16 | uint32(p[6])<<8 | uint32(p[7]) *v = &val default: return errWrongDataLen } return nil } func DecUint64(p []byte, v *uint64) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 8: *v = decUint64(p) default: return errWrongDataLen } return nil } func DecUint64R(p []byte, v **uint64) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(uint64) } case 8: val := decUint64(p) *v = &val default: return errWrongDataLen } return nil } func DecUint(p []byte, v *uint) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 8: *v = uint(p[0])<<56 | uint(p[1])<<48 | uint(p[2])<<40 | uint(p[3])<<32 | uint(p[4])<<24 | uint(p[5])<<16 | uint(p[6])<<8 | uint(p[7]) default: return errWrongDataLen } return nil } func DecUintR(p []byte, v **uint) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(uint) } case 8: val := uint(p[0])<<56 | uint(p[1])<<48 | uint(p[2])<<40 | uint(p[3])<<32 | uint(p[4])<<24 | uint(p[5])<<16 | uint(p[6])<<8 | uint(p[7]) *v = &val default: return errWrongDataLen } return nil } func DecString(p []byte, v *string) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = "" } else { *v = "0" } case 8: *v = strconv.FormatInt(decInt64(p), 10) default: return errWrongDataLen } return nil } func DecStringR(p []byte, v **string) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { val := "0" *v = &val } case 8: val := strconv.FormatInt(decInt64(p), 10) *v = &val default: return errWrongDataLen } return nil } func DecBigInt(p []byte, v *big.Int) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: v.SetInt64(0) case 8: v.SetInt64(decInt64(p)) default: return errWrongDataLen } return nil } func DecBigIntR(p []byte, v **big.Int) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(big.Int) } case 8: *v = big.NewInt(decInt64(p)) default: return errWrongDataLen } return nil } func DecReflect(p []byte, v reflect.Value) error { if v.IsNil() { return fmt.Errorf("failed to unmarshal bigint: can not unmarshal into nil reference (%T)(%[1]v))", v.Interface()) } switch v = v.Elem(); v.Kind() { case reflect.Int8: return decReflectInt8(p, v) case reflect.Int16: return decReflectInt16(p, v) case reflect.Int32: return decReflectInt32(p, v) case reflect.Int64, reflect.Int: return decReflectInts(p, v) case reflect.Uint8: return decReflectUint8(p, v) case reflect.Uint16: return decReflectUint16(p, v) case reflect.Uint32: return decReflectUint32(p, v) case reflect.Uint64, reflect.Uint: return decReflectUints(p, v) case reflect.String: return decReflectString(p, v) default: return fmt.Errorf("failed to unmarshal bigint: unsupported value type (%T)(%[1]v), supported types: %s", v.Interface(), supportedTypes) } } func decReflectInt8(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetInt(0) case 8: val := decInt64(p) if val > math.MaxInt8 || val < math.MinInt8 { return fmt.Errorf("failed to unmarshal bigint: to unmarshal into %T, the data should be in the int8 range", v.Interface()) } v.SetInt(val) default: return errWrongDataLen } return nil } func decReflectInt16(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetInt(0) case 8: val := decInt64(p) if val > math.MaxInt16 || val < math.MinInt16 { return fmt.Errorf("failed to unmarshal bigint: to unmarshal into %T, the data should be in the int16 range", v.Interface()) } v.SetInt(val) default: return errWrongDataLen } return nil } func decReflectInt32(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetInt(0) case 8: val := decInt64(p) if val > math.MaxInt32 || val < math.MinInt32 { return fmt.Errorf("failed to unmarshal bigint: to unmarshal into %T, the data should be in the int32 range", v.Interface()) } v.SetInt(val) default: return errWrongDataLen } return nil } func decReflectInts(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetInt(0) case 8: v.SetInt(decInt64(p)) default: return errWrongDataLen } return nil } func decReflectUint8(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetUint(0) case 8: if p[0] != 0 || p[1] != 0 || p[2] != 0 || p[3] != 0 || p[4] != 0 || p[5] != 0 || p[6] != 0 { return fmt.Errorf("failed to unmarshal bigint: to unmarshal into %T, the data should be in the uint8 range", v.Interface()) } v.SetUint(uint64(p[7])) default: return errWrongDataLen } return nil } func decReflectUint16(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetUint(0) case 8: if p[0] != 0 || p[1] != 0 || p[2] != 0 || p[3] != 0 || p[4] != 0 || p[5] != 0 { return fmt.Errorf("failed to unmarshal bigint: to unmarshal into %T, the data should be in the uint16 range", v.Interface()) } v.SetUint(uint64(p[6])<<8 | uint64(p[7])) default: return errWrongDataLen } return nil } func decReflectUint32(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetUint(0) case 8: if p[0] != 0 || p[1] != 0 || p[2] != 0 || p[3] != 0 { return fmt.Errorf("failed to unmarshal bigint: to unmarshal into %T, the data should be in the uint32 range", v.Interface()) } v.SetUint(uint64(p[4])<<24 | uint64(p[5])<<16 | uint64(p[6])<<8 | uint64(p[7])) default: return errWrongDataLen } return nil } func decReflectUints(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetUint(0) case 8: v.SetUint(decUint64(p)) default: return errWrongDataLen } return nil } func decReflectString(p []byte, v reflect.Value) error { switch len(p) { case 0: if p == nil { v.SetString("") } else { v.SetString("0") } case 8: v.SetString(strconv.FormatInt(decInt64(p), 10)) default: return errWrongDataLen } return nil } func DecReflectR(p []byte, v reflect.Value) error { if v.IsNil() { return fmt.Errorf("failed to unmarshal bigint: can not unmarshal into nil reference (%T)(%[1]v)", v.Interface()) } switch v.Type().Elem().Elem().Kind() { case reflect.Int8: return decReflectInt8R(p, v) case reflect.Int16: return decReflectInt16R(p, v) case reflect.Int32: return decReflectInt32R(p, v) case reflect.Int64, reflect.Int: return decReflectIntsR(p, v) case reflect.Uint8: return decReflectUint8R(p, v) case reflect.Uint16: return decReflectUint16R(p, v) case reflect.Uint32: return decReflectUint32R(p, v) case reflect.Uint64, reflect.Uint: return decReflectUintsR(p, v) case reflect.String: return decReflectStringR(p, v) default: return fmt.Errorf("failed to unmarshal bigint: unsupported value type (%T)(%[1]v), supported types: %s", v.Interface(), supportedTypes) } } func decReflectInt8R(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) case 8: val := decInt64(p) if val > math.MaxInt8 || val < math.MinInt8 { return fmt.Errorf("failed to unmarshal bigint: to unmarshal into %T, the data should be in the int8 range", v.Interface()) } newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetInt(val) v.Elem().Set(newVal) default: return errWrongDataLen } return nil } func decReflectInt16R(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) case 8: val := decInt64(p) if val > math.MaxInt16 || val < math.MinInt16 { return fmt.Errorf("failed to unmarshal bigint: to unmarshal into %T, the data should be in the int16 range", v.Interface()) } newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetInt(val) v.Elem().Set(newVal) default: return errWrongDataLen } return nil } func decReflectInt32R(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) case 8: val := decInt64(p) if val > math.MaxInt32 || val < math.MinInt32 { return fmt.Errorf("failed to unmarshal bigint: to unmarshal into %T, the data should be in the int32 range", v.Interface()) } newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetInt(val) v.Elem().Set(newVal) default: return errWrongDataLen } return nil } func decReflectIntsR(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) case 8: val := reflect.New(v.Type().Elem().Elem()) val.Elem().SetInt(decInt64(p)) v.Elem().Set(val) default: return errWrongDataLen } return nil } func decReflectUint8R(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) case 8: newVal := reflect.New(v.Type().Elem().Elem()) if p[0] != 0 || p[1] != 0 || p[2] != 0 || p[3] != 0 || p[4] != 0 || p[5] != 0 || p[6] != 0 { return fmt.Errorf("failed to unmarshal bigint: to unmarshal into %T, the data should be in the uint8 range", v.Interface()) } newVal.Elem().SetUint(uint64(p[7])) v.Elem().Set(newVal) default: return errWrongDataLen } return nil } func decReflectUint16R(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) case 8: newVal := reflect.New(v.Type().Elem().Elem()) if p[0] != 0 || p[1] != 0 || p[2] != 0 || p[3] != 0 || p[4] != 0 || p[5] != 0 { return fmt.Errorf("failed to unmarshal bigint: to unmarshal into %T, the data should be in the uint16 range", v.Interface()) } newVal.Elem().SetUint(uint64(p[6])<<8 | uint64(p[7])) v.Elem().Set(newVal) default: return errWrongDataLen } return nil } func decReflectUint32R(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) case 8: newVal := reflect.New(v.Type().Elem().Elem()) if p[0] != 0 || p[1] != 0 || p[2] != 0 || p[3] != 0 { return fmt.Errorf("failed to unmarshal bigint: to unmarshal into %T, the data should be in the uint32 range", v.Interface()) } newVal.Elem().SetUint(uint64(p[4])<<24 | uint64(p[5])<<16 | uint64(p[6])<<8 | uint64(p[7])) v.Elem().Set(newVal) default: return errWrongDataLen } return nil } func decReflectUintsR(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) case 8: val := reflect.New(v.Type().Elem().Elem()) val.Elem().SetUint(decUint64(p)) v.Elem().Set(val) default: return errWrongDataLen } return nil } func decReflectStringR(p []byte, v reflect.Value) error { switch len(p) { case 0: var val reflect.Value if p == nil { val = reflect.Zero(v.Type().Elem()) } else { val = reflect.New(v.Type().Elem().Elem()) val.Elem().SetString("0") } v.Elem().Set(val) case 8: val := reflect.New(v.Type().Elem().Elem()) val.Elem().SetString(strconv.FormatInt(decInt64(p), 10)) v.Elem().Set(val) default: return errWrongDataLen } return nil } func decReflectNullableR(p []byte, v reflect.Value) reflect.Value { if p == nil { return reflect.Zero(v.Elem().Type()) } return reflect.New(v.Type().Elem().Elem()) } func decInt64(p []byte) int64 { return int64(p[0])<<56 | int64(p[1])<<48 | int64(p[2])<<40 | int64(p[3])<<32 | int64(p[4])<<24 | int64(p[5])<<16 | int64(p[6])<<8 | int64(p[7]) } func decUint64(p []byte) uint64 { return uint64(p[0])<<56 | uint64(p[1])<<48 | uint64(p[2])<<40 | uint64(p[3])<<32 | uint64(p[4])<<24 | uint64(p[5])<<16 | uint64(p[6])<<8 | uint64(p[7]) } ================================================ FILE: serialization/blob/marshal.go ================================================ package blob import ( "reflect" ) func Marshal(value any) ([]byte, error) { switch v := value.(type) { case nil: return nil, nil case string: return EncString(v) case *string: return EncStringR(v) case []byte: return EncBytes(v) case *[]byte: return EncBytesR(v) default: // Custom types (type MyString string) can be serialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.ValueOf(value) if rv.Kind() != reflect.Ptr { return EncReflect(rv) } return EncReflectR(rv) } } ================================================ FILE: serialization/blob/marshal_utils.go ================================================ package blob import ( "fmt" "reflect" ) func EncString(v string) ([]byte, error) { return encString(v), nil } func EncStringR(v *string) ([]byte, error) { if v == nil { return nil, nil } return encString(*v), nil } func EncBytes(v []byte) ([]byte, error) { return v, nil } func EncBytesR(v *[]byte) ([]byte, error) { if v == nil { return nil, nil } return *v, nil } func EncReflect(v reflect.Value) ([]byte, error) { switch v.Kind() { case reflect.String: return encString(v.String()), nil case reflect.Slice: if v.Type().Elem().Kind() != reflect.Uint8 { return nil, fmt.Errorf("failed to marshal blob: unsupported value type (%T)(%[1]v), supported types: ~string, ~[]byte, unsetColumn", v.Interface()) } return EncBytes(v.Bytes()) case reflect.Struct: if v.Type().String() == "gocql.unsetColumn" { return nil, nil } return nil, fmt.Errorf("failed to marshal blob: unsupported value type (%T)(%[1]v), supported types: ~string, ~[]byte, unsetColumn", v.Interface()) default: return nil, fmt.Errorf("failed to marshal blob: unsupported value type (%T)(%[1]v), supported types: ~string, ~[]byte, unsetColumn", v.Interface()) } } func EncReflectR(v reflect.Value) ([]byte, error) { if v.IsNil() { return nil, nil } return EncReflect(v.Elem()) } func encString(v string) []byte { if v == "" { return make([]byte, 0) } return []byte(v) } ================================================ FILE: serialization/blob/unmarshal.go ================================================ package blob import ( "fmt" "reflect" ) func Unmarshal(data []byte, value any) error { switch v := value.(type) { case nil: return nil case *string: return DecString(data, v) case **string: return DecStringR(data, v) case *[]byte: return DecBytes(data, v) case **[]byte: return DecBytesR(data, v) case *any: return DecInterface(data, v) default: // Custom types (type MyString string) can be deserialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.ValueOf(value) rt := rv.Type() if rt.Kind() != reflect.Ptr { return fmt.Errorf("failed to unmarshal blob: unsupported value type (%T)(%[1]v), supported types: ~string, ~[]byte", v) } if rt.Elem().Kind() != reflect.Ptr { return DecReflect(data, rv) } return DecReflectR(data, rv) } } ================================================ FILE: serialization/blob/unmarshal_utils.go ================================================ package blob import ( "fmt" "reflect" ) func errNilReference(v any) error { return fmt.Errorf("failed to unmarshal blob: can not unmarshal into nil reference(%T)(%[1]v)", v) } func DecString(p []byte, v *string) error { if v == nil { return errNilReference(v) } *v = decString(p) return nil } func DecStringR(p []byte, v **string) error { if v == nil { return errNilReference(v) } *v = decStringR(p) return nil } func DecBytes(p []byte, v *[]byte) error { if v == nil { return errNilReference(v) } if p == nil { *v = nil return nil } if len(p) == 0 { *v = make([]byte, 0) return nil } *v = append((*v)[:0], p...) return nil } func DecBytesR(p []byte, v **[]byte) error { if v == nil { return errNilReference(v) } *v = decBytesR(p) return nil } func DecInterface(p []byte, v *any) error { if v == nil { return errNilReference(v) } *v = decBytes(p) return nil } func DecReflect(p []byte, v reflect.Value) error { if v.IsNil() { return errNilReference(v) } switch v = v.Elem(); v.Kind() { case reflect.String: v.SetString(decString(p)) case reflect.Slice: if v.Type().Elem().Kind() != reflect.Uint8 { return fmt.Errorf("failed to marshal blob: unsupported value type (%T)(%[1]v), supported types: ~string, ~[]byte", v.Interface()) } v.SetBytes(decBytes(p)) case reflect.Interface: v.Set(reflect.ValueOf(decBytes(p))) default: return fmt.Errorf("failed to unmarshal blob: unsupported value type (%T)(%[1]v), supported types: ~string, ~[]byte", v.Interface()) } return nil } func DecReflectR(p []byte, v reflect.Value) error { if v.IsNil() { return errNilReference(v) } switch ev := v.Type().Elem().Elem(); ev.Kind() { case reflect.String: return decReflectStringR(p, v) case reflect.Slice: if ev.Elem().Kind() != reflect.Uint8 { return fmt.Errorf("failed to marshal blob: unsupported value type (%T)(%[1]v), supported types: ~string, ~[]byte", v.Interface()) } return decReflectBytesR(p, v) default: return fmt.Errorf("failed to unmarshal blob: unsupported value type (%T)(%[1]v), supported types: ~string, ~[]byte", v.Interface()) } } func decReflectStringR(p []byte, v reflect.Value) error { if len(p) == 0 { if p == nil { v.Elem().Set(reflect.Zero(v.Type().Elem())) } else { v.Elem().Set(reflect.New(v.Type().Elem().Elem())) } return nil } val := reflect.New(v.Type().Elem().Elem()) val.Elem().SetString(string(p)) v.Elem().Set(val) return nil } func decReflectBytesR(p []byte, v reflect.Value) error { if len(p) == 0 { if p == nil { v.Elem().Set(reflect.Zero(v.Elem().Type())) } else { val := reflect.New(v.Type().Elem().Elem()) val.Elem().SetBytes(make([]byte, 0)) v.Elem().Set(val) } return nil } tmp := make([]byte, len(p)) copy(tmp, p) val := reflect.New(v.Type().Elem().Elem()) val.Elem().SetBytes(tmp) v.Elem().Set(val) return nil } func decString(p []byte) string { if len(p) == 0 { return "" } return string(p) } func decStringR(p []byte) *string { if len(p) == 0 { if p == nil { return nil } return new(string) } tmp := string(p) return &tmp } func decBytes(p []byte) []byte { if len(p) == 0 { if p == nil { return nil } return make([]byte, 0) } tmp := make([]byte, len(p)) copy(tmp, p) return tmp } func decBytesR(p []byte) *[]byte { if len(p) == 0 { if p == nil { return nil } tmp := make([]byte, 0) return &tmp } tmp := make([]byte, len(p)) copy(tmp, p) return &tmp } ================================================ FILE: serialization/blob/unmarshal_utils_test.go ================================================ //go:build unit // +build unit package blob import ( "bytes" "testing" ) func TestDecBytesArrayBackedSlice(t *testing.T) { t.Parallel() data := []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C} var arr [12]byte slice := arr[:] if err := DecBytes(data, &slice); err != nil { t.Fatal(err) } if !bytes.Equal(arr[:], data) { t.Fatalf("expected underlying array to be %v, got %v", data, arr) } if !bytes.Equal(slice, data) { t.Fatalf("expected slice to be %v, got %v", data, slice) } } func TestDecBytesArrayBackedSliceViaUnmarshal(t *testing.T) { t.Parallel() type ObjectID [12]byte data := []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C} var id ObjectID pkSlice := id[:] if err := Unmarshal(data, &pkSlice); err != nil { t.Fatal(err) } if !bytes.Equal(id[:], data) { t.Fatalf("expected underlying array to be %v, got %v", data, id) } } func TestDecBytesNil(t *testing.T) { t.Parallel() existing := []byte{1, 2, 3} if err := DecBytes(nil, &existing); err != nil { t.Fatal(err) } if existing != nil { t.Fatalf("expected nil, got %v", existing) } } func TestDecBytesEmpty(t *testing.T) { t.Parallel() var dest []byte if err := DecBytes(make([]byte, 0), &dest); err != nil { t.Fatal(err) } if dest == nil { t.Fatal("expected non-nil empty slice for non-nil empty input") } if len(dest) != 0 { t.Fatalf("expected empty slice, got %v", dest) } } func TestDecBytesPreallocated(t *testing.T) { t.Parallel() data := []byte{0xAA, 0xBB, 0xCC} dest := make([]byte, 5) if err := DecBytes(data, &dest); err != nil { t.Fatal(err) } if !bytes.Equal(dest, data) { t.Fatalf("expected %v, got %v", data, dest) } } func TestDecBytesNilReference(t *testing.T) { t.Parallel() if err := DecBytes([]byte{1}, nil); err == nil { t.Fatal("expected error for nil reference") } } ================================================ FILE: serialization/boolean/marshal.go ================================================ package boolean import ( "reflect" ) func Marshal(value any) ([]byte, error) { switch v := value.(type) { case nil: return nil, nil case bool: return EncBool(v) case *bool: return EncBoolR(v) default: // Custom types (type MyBool bool) can be serialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.TypeOf(value) if rv.Kind() != reflect.Ptr { return EncReflect(reflect.ValueOf(v)) } return EncReflectR(reflect.ValueOf(v)) } } ================================================ FILE: serialization/boolean/marshal_utils.go ================================================ package boolean import ( "fmt" "reflect" ) func EncBool(v bool) ([]byte, error) { return encBool(v), nil } func EncBoolR(v *bool) ([]byte, error) { if v == nil { return nil, nil } return encBool(*v), nil } func EncReflect(v reflect.Value) ([]byte, error) { switch v.Kind() { case reflect.Bool: return encBool(v.Bool()), nil case reflect.Struct: if v.Type().String() == "gocql.unsetColumn" { return nil, nil } return nil, fmt.Errorf("failed to marshal boolean: unsupported value type (%T)(%[1]v), supported types: ~bool, unsetColumn", v.Interface()) default: return nil, fmt.Errorf("failed to marshal boolean: unsupported value type (%T)(%[1]v), supported types: ~bool, unsetColumn", v.Interface()) } } func EncReflectR(v reflect.Value) ([]byte, error) { if v.IsNil() { return nil, nil } return EncReflect(v.Elem()) } func encBool(v bool) []byte { if v { return []byte{1} } return []byte{0} } ================================================ FILE: serialization/boolean/unmarshal.go ================================================ package boolean import ( "fmt" "reflect" ) func Unmarshal(data []byte, value any) error { switch v := value.(type) { case nil: return nil case *bool: return DecBool(data, v) case **bool: return DecBoolR(data, v) default: // Custom types (type MyBool bool) can be deserialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.ValueOf(value) rt := rv.Type() if rt.Kind() != reflect.Ptr { return fmt.Errorf("failed to unmarshal boolean: unsupported value type (%T)(%[1]v)", v) } if rt.Elem().Kind() != reflect.Ptr { return DecReflect(data, rv) } return DecReflectR(data, rv) } } ================================================ FILE: serialization/boolean/unmarshal_utils.go ================================================ package boolean import ( "fmt" "reflect" ) var errWrongDataLen = fmt.Errorf("failed to unmarshal boolean: the length of the data should be 0 or 1") func errNilReference(v any) error { return fmt.Errorf("failed to unmarshal boolean: can not unmarshal into nil reference(%T)(%[1]v)", v) } func DecBool(p []byte, v *bool) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = false case 1: *v = decBool(p) default: return errWrongDataLen } return nil } func DecBoolR(p []byte, v **bool) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(bool) } case 1: val := decBool(p) *v = &val default: return errWrongDataLen } return nil } func DecReflect(p []byte, v reflect.Value) error { if v.IsNil() { return errNilReference(v) } switch v = v.Elem(); v.Kind() { case reflect.Bool: return decReflectBool(p, v) default: return fmt.Errorf("failed to unmarshal boolean: unsupported value type (%T)(%[1]v), supported types: ~bool", v.Interface()) } } func DecReflectR(p []byte, v reflect.Value) error { if v.IsNil() { return errNilReference(v) } switch v.Type().Elem().Elem().Kind() { case reflect.Bool: return decReflectBoolR(p, v) default: return fmt.Errorf("failed to unmarshal boolean: unsupported value type (%T)(%[1]v), supported types: ~bool", v.Interface()) } } func decReflectBool(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetBool(false) case 1: v.SetBool(decBool(p)) default: return errWrongDataLen } return nil } func decReflectBoolR(p []byte, v reflect.Value) error { switch len(p) { case 0: if p == nil { v.Elem().Set(reflect.Zero(v.Type().Elem())) } else { val := reflect.New(v.Type().Elem().Elem()) v.Elem().Set(val) } case 1: val := reflect.New(v.Type().Elem().Elem()) val.Elem().SetBool(decBool(p)) v.Elem().Set(val) default: return errWrongDataLen } return nil } func decBool(p []byte) bool { return p[0] != 0 } ================================================ FILE: serialization/counter/marshal.go ================================================ package counter import ( "math/big" "reflect" ) func Marshal(value any) ([]byte, error) { switch v := value.(type) { case nil: return nil, nil case int8: return EncInt8(v) case int16: return EncInt16(v) case int32: return EncInt32(v) case int64: return EncInt64(v) case int: return EncInt(v) case uint8: return EncUint8(v) case uint16: return EncUint16(v) case uint32: return EncUint32(v) case uint64: return EncUint64(v) case uint: return EncUint(v) case big.Int: return EncBigInt(v) case string: return EncString(v) case *int8: return EncInt8R(v) case *int16: return EncInt16R(v) case *int32: return EncInt32R(v) case *int64: return EncInt64R(v) case *int: return EncIntR(v) case *uint8: return EncUint8R(v) case *uint16: return EncUint16R(v) case *uint32: return EncUint32R(v) case *uint64: return EncUint64R(v) case *uint: return EncUintR(v) case *big.Int: return EncBigIntR(v) case *string: return EncStringR(v) default: // Custom types (type MyInt int) can be serialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.TypeOf(value) if rv.Kind() != reflect.Ptr { return EncReflect(reflect.ValueOf(v)) } return EncReflectR(reflect.ValueOf(v)) } } ================================================ FILE: serialization/counter/marshal_utils.go ================================================ package counter import ( "fmt" "math/big" "reflect" "strconv" ) const supportedTypes = "~int8, ~int16, ~int32, ~int64, ~int, ~uint8, ~uint16, ~uint32, ~uint64, ~uint, ~string, big.Int" func EncInt8(v int8) ([]byte, error) { if v < 0 { return []byte{255, 255, 255, 255, 255, 255, 255, byte(v)}, nil } return []byte{0, 0, 0, 0, 0, 0, 0, byte(v)}, nil } func EncInt8R(v *int8) ([]byte, error) { if v == nil { return nil, nil } return EncInt8(*v) } func EncInt16(v int16) ([]byte, error) { if v < 0 { return []byte{255, 255, 255, 255, 255, 255, byte(v >> 8), byte(v)}, nil } return []byte{0, 0, 0, 0, 0, 0, byte(v >> 8), byte(v)}, nil } func EncInt16R(v *int16) ([]byte, error) { if v == nil { return nil, nil } return EncInt16(*v) } func EncInt32(v int32) ([]byte, error) { if v < 0 { return []byte{255, 255, 255, 255, byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)}, nil } return []byte{0, 0, 0, 0, byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)}, nil } func EncInt32R(v *int32) ([]byte, error) { if v == nil { return nil, nil } return EncInt32(*v) } func EncInt64(v int64) ([]byte, error) { return encInt64(v), nil } func EncInt64R(v *int64) ([]byte, error) { if v == nil { return nil, nil } return EncInt64(*v) } func EncInt(v int) ([]byte, error) { return []byte{byte(v >> 56), byte(v >> 48), byte(v >> 40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)}, nil } func EncIntR(v *int) ([]byte, error) { if v == nil { return nil, nil } return EncInt(*v) } func EncUint8(v uint8) ([]byte, error) { return []byte{0, 0, 0, 0, 0, 0, 0, v}, nil } func EncUint8R(v *uint8) ([]byte, error) { if v == nil { return nil, nil } return EncUint8(*v) } func EncUint16(v uint16) ([]byte, error) { return []byte{0, 0, 0, 0, 0, 0, byte(v >> 8), byte(v)}, nil } func EncUint16R(v *uint16) ([]byte, error) { if v == nil { return nil, nil } return EncUint16(*v) } func EncUint32(v uint32) ([]byte, error) { return []byte{0, 0, 0, 0, byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)}, nil } func EncUint32R(v *uint32) ([]byte, error) { if v == nil { return nil, nil } return EncUint32(*v) } func EncUint64(v uint64) ([]byte, error) { return []byte{byte(v >> 56), byte(v >> 48), byte(v >> 40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)}, nil } func EncUint64R(v *uint64) ([]byte, error) { if v == nil { return nil, nil } return EncUint64(*v) } func EncUint(v uint) ([]byte, error) { return []byte{byte(v >> 56), byte(v >> 48), byte(v >> 40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)}, nil } func EncUintR(v *uint) ([]byte, error) { if v == nil { return nil, nil } return EncUint(*v) } func EncBigInt(v big.Int) ([]byte, error) { if !v.IsInt64() { return nil, fmt.Errorf("failed to marshal counter: value (%T)(%s) out of range", v, v.String()) } return encInt64(v.Int64()), nil } func EncBigIntR(v *big.Int) ([]byte, error) { if v == nil { return nil, nil } if !v.IsInt64() { return nil, fmt.Errorf("failed to marshal counter: value (%T)(%s) out of range", v, v.String()) } return encInt64(v.Int64()), nil } func EncString(v string) ([]byte, error) { if v == "" { return nil, nil } n, err := strconv.ParseInt(v, 10, 64) if err != nil { return nil, fmt.Errorf("failed to marshal counter: can not marshal %#v %s", v, err) } return encInt64(n), nil } func EncStringR(v *string) ([]byte, error) { if v == nil { return nil, nil } return EncString(*v) } func EncReflect(v reflect.Value) ([]byte, error) { switch v.Kind() { case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8: return EncInt64(v.Int()) case reflect.Uint, reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8: return EncUint64(v.Uint()) case reflect.String: val := v.String() if val == "" { return nil, nil } n, err := strconv.ParseInt(val, 10, 64) if err != nil { return nil, fmt.Errorf("failed to marshal counter: can not marshal (%T)(%[1]v) %s", v.Interface(), err) } return encInt64(n), nil case reflect.Struct: if v.Type().String() == "gocql.unsetColumn" { return nil, nil } return nil, fmt.Errorf("failed to marshal counter: unsupported value type (%T)(%[1]v), supported types: %s, unsetColumn", v.Interface(), supportedTypes) default: return nil, fmt.Errorf("failed to marshal counter: unsupported value type (%T)(%[1]v), supported types: %s, unsetColumn", v.Interface(), supportedTypes) } } func EncReflectR(v reflect.Value) ([]byte, error) { if v.IsNil() { return nil, nil } return EncReflect(v.Elem()) } func encInt64(v int64) []byte { return []byte{byte(v >> 56), byte(v >> 48), byte(v >> 40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} } ================================================ FILE: serialization/counter/unmarshal.go ================================================ package counter import ( "fmt" "math/big" "reflect" ) func Unmarshal(data []byte, value any) error { switch v := value.(type) { case nil: return nil case *int8: return DecInt8(data, v) case *int16: return DecInt16(data, v) case *int32: return DecInt32(data, v) case *int64: return DecInt64(data, v) case *int: return DecInt(data, v) case *uint8: return DecUint8(data, v) case *uint16: return DecUint16(data, v) case *uint32: return DecUint32(data, v) case *uint64: return DecUint64(data, v) case *uint: return DecUint(data, v) case *big.Int: return DecBigInt(data, v) case *string: return DecString(data, v) case **int8: return DecInt8R(data, v) case **int16: return DecInt16R(data, v) case **int32: return DecInt32R(data, v) case **int64: return DecInt64R(data, v) case **int: return DecIntR(data, v) case **uint8: return DecUint8R(data, v) case **uint16: return DecUint16R(data, v) case **uint32: return DecUint32R(data, v) case **uint64: return DecUint64R(data, v) case **uint: return DecUintR(data, v) case **big.Int: return DecBigIntR(data, v) case **string: return DecStringR(data, v) default: // Custom types (type MyInt int) can be deserialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.ValueOf(value) rt := rv.Type() if rt.Kind() != reflect.Ptr { return fmt.Errorf("failed to unmarshal counter: unsupported value type (%T)(%[1]v), supported types: %s", value, supportedTypes) } if rt.Elem().Kind() != reflect.Ptr { return DecReflect(data, rv) } return DecReflectR(data, rv) } } ================================================ FILE: serialization/counter/unmarshal_utils.go ================================================ package counter import ( "fmt" "math" "math/big" "reflect" "strconv" ) var errWrongDataLen = fmt.Errorf("failed to unmarshal counter: the length of the data should be 0 or 8") func errNilReference(v any) error { return fmt.Errorf("failed to unmarshal counter: can not unmarshal into nil reference (%T)(%[1]v))", v) } func DecInt8(p []byte, v *int8) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 8: val := decInt64(p) if val > math.MaxInt8 || val < math.MinInt8 { return fmt.Errorf("failed to unmarshal counter: to unmarshal into int8, the data should be in the int8 range") } *v = int8(val) default: return errWrongDataLen } return nil } func DecInt8R(p []byte, v **int8) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(int8) } case 8: val := decInt64(p) if val > math.MaxInt8 || val < math.MinInt8 { return fmt.Errorf("failed to unmarshal counter: to unmarshal into int8, the data should be in the int8 range") } tmp := int8(val) *v = &tmp default: return errWrongDataLen } return nil } func DecInt16(p []byte, v *int16) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 8: val := decInt64(p) if val > math.MaxInt16 || val < math.MinInt16 { return fmt.Errorf("failed to unmarshal counter: to unmarshal into int16, the data should be in the int16 range") } *v = int16(val) default: return errWrongDataLen } return nil } func DecInt16R(p []byte, v **int16) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(int16) } case 8: val := decInt64(p) if val > math.MaxInt16 || val < math.MinInt16 { return fmt.Errorf("failed to unmarshal counter: to unmarshal into int16, the data should be in the int16 range") } tmp := int16(val) *v = &tmp default: return errWrongDataLen } return nil } func DecInt32(p []byte, v *int32) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 8: val := decInt64(p) if val > math.MaxInt32 || val < math.MinInt32 { return fmt.Errorf("failed to unmarshal counter: to unmarshal into int32, the data should be in the int32 range") } *v = int32(val) default: return errWrongDataLen } return nil } func DecInt32R(p []byte, v **int32) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(int32) } case 8: val := decInt64(p) if val > math.MaxInt32 || val < math.MinInt32 { return fmt.Errorf("failed to unmarshal counter: to unmarshal into int32, the data should be in the int32 range") } tmp := int32(val) *v = &tmp default: return errWrongDataLen } return nil } func DecInt64(p []byte, v *int64) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 8: *v = decInt64(p) default: return errWrongDataLen } return nil } func DecInt64R(p []byte, v **int64) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(int64) } case 8: val := decInt64(p) *v = &val default: return errWrongDataLen } return nil } func DecInt(p []byte, v *int) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 8: *v = int(p[0])<<56 | int(p[1])<<48 | int(p[2])<<40 | int(p[3])<<32 | int(p[4])<<24 | int(p[5])<<16 | int(p[6])<<8 | int(p[7]) default: return errWrongDataLen } return nil } func DecIntR(p []byte, v **int) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(int) } case 8: val := int(p[0])<<56 | int(p[1])<<48 | int(p[2])<<40 | int(p[3])<<32 | int(p[4])<<24 | int(p[5])<<16 | int(p[6])<<8 | int(p[7]) *v = &val default: return errWrongDataLen } return nil } func DecUint8(p []byte, v *uint8) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 8: if p[0] != 0 || p[1] != 0 || p[2] != 0 || p[3] != 0 || p[4] != 0 || p[5] != 0 || p[6] != 0 { return fmt.Errorf("failed to unmarshal counter: to unmarshal into uint8, the data should be in the uint8 range") } *v = p[7] default: return errWrongDataLen } return nil } func DecUint8R(p []byte, v **uint8) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(uint8) } case 8: if p[0] != 0 || p[1] != 0 || p[2] != 0 || p[3] != 0 || p[4] != 0 || p[5] != 0 || p[6] != 0 { return fmt.Errorf("failed to unmarshal counter: to unmarshal into uint8, the data should be in the uint8 range") } val := p[7] *v = &val default: return errWrongDataLen } return nil } func DecUint16(p []byte, v *uint16) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 8: if p[0] != 0 || p[1] != 0 || p[2] != 0 || p[3] != 0 || p[4] != 0 || p[5] != 0 { return fmt.Errorf("failed to unmarshal counter: to unmarshal into uint16, the data should be in the uint16 range") } *v = uint16(p[6])<<8 | uint16(p[7]) default: return errWrongDataLen } return nil } func DecUint16R(p []byte, v **uint16) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(uint16) } case 8: if p[0] != 0 || p[1] != 0 || p[2] != 0 || p[3] != 0 || p[4] != 0 || p[5] != 0 { return fmt.Errorf("failed to unmarshal counter: to unmarshal into uint16, the data should be in the uint16 range") } val := uint16(p[6])<<8 | uint16(p[7]) *v = &val default: return errWrongDataLen } return nil } func DecUint32(p []byte, v *uint32) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 8: if p[0] != 0 || p[1] != 0 || p[2] != 0 || p[3] != 0 { return fmt.Errorf("failed to unmarshal counter: to unmarshal into uint32, the data should be in the uint32 range") } *v = uint32(p[4])<<24 | uint32(p[5])<<16 | uint32(p[6])<<8 | uint32(p[7]) default: return errWrongDataLen } return nil } func DecUint32R(p []byte, v **uint32) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(uint32) } case 8: if p[0] != 0 || p[1] != 0 || p[2] != 0 || p[3] != 0 { return fmt.Errorf("failed to unmarshal counter: to unmarshal into uint32, the data should be in the uint32 range") } val := uint32(p[4])<<24 | uint32(p[5])<<16 | uint32(p[6])<<8 | uint32(p[7]) *v = &val default: return errWrongDataLen } return nil } func DecUint64(p []byte, v *uint64) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 8: *v = decUint64(p) default: return errWrongDataLen } return nil } func DecUint64R(p []byte, v **uint64) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(uint64) } case 8: val := decUint64(p) *v = &val default: return errWrongDataLen } return nil } func DecUint(p []byte, v *uint) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 8: *v = uint(p[0])<<56 | uint(p[1])<<48 | uint(p[2])<<40 | uint(p[3])<<32 | uint(p[4])<<24 | uint(p[5])<<16 | uint(p[6])<<8 | uint(p[7]) default: return errWrongDataLen } return nil } func DecUintR(p []byte, v **uint) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(uint) } case 8: val := uint(p[0])<<56 | uint(p[1])<<48 | uint(p[2])<<40 | uint(p[3])<<32 | uint(p[4])<<24 | uint(p[5])<<16 | uint(p[6])<<8 | uint(p[7]) *v = &val default: return errWrongDataLen } return nil } func DecString(p []byte, v *string) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = "" } else { *v = "0" } case 8: *v = strconv.FormatInt(decInt64(p), 10) default: return errWrongDataLen } return nil } func DecStringR(p []byte, v **string) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { val := "0" *v = &val } case 8: val := strconv.FormatInt(decInt64(p), 10) *v = &val default: return errWrongDataLen } return nil } func DecBigInt(p []byte, v *big.Int) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: v.SetInt64(0) case 8: v.SetInt64(decInt64(p)) default: return errWrongDataLen } return nil } func DecBigIntR(p []byte, v **big.Int) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(big.Int) } case 8: *v = big.NewInt(decInt64(p)) default: return errWrongDataLen } return nil } func DecReflect(p []byte, v reflect.Value) error { if v.IsNil() { return fmt.Errorf("failed to unmarshal counter: can not unmarshal into nil reference (%T)(%[1]v)", v.Interface()) } switch v = v.Elem(); v.Kind() { case reflect.Int8: return decReflectInt8(p, v) case reflect.Int16: return decReflectInt16(p, v) case reflect.Int32: return decReflectInt32(p, v) case reflect.Int64, reflect.Int: return decReflectInts(p, v) case reflect.Uint8: return decReflectUint8(p, v) case reflect.Uint16: return decReflectUint16(p, v) case reflect.Uint32: return decReflectUint32(p, v) case reflect.Uint64, reflect.Uint: return decReflectUints(p, v) case reflect.String: return decReflectString(p, v) default: return fmt.Errorf("failed to unmarshal counter: unsupported value type (%T)(%[1]v), supported types: %s", v.Interface(), supportedTypes) } } func decReflectInt8(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetInt(0) case 8: val := decInt64(p) if val > math.MaxInt8 || val < math.MinInt8 { return fmt.Errorf("failed to unmarshal counter: to unmarshal into %T, the data should be in the int8 range", v.Interface()) } v.SetInt(val) default: return errWrongDataLen } return nil } func decReflectInt16(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetInt(0) case 8: val := decInt64(p) if val > math.MaxInt16 || val < math.MinInt16 { return fmt.Errorf("failed to unmarshal counter: to unmarshal into %T, the data should be in the int16 range", v.Interface()) } v.SetInt(val) default: return errWrongDataLen } return nil } func decReflectInt32(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetInt(0) case 8: val := decInt64(p) if val > math.MaxInt32 || val < math.MinInt32 { return fmt.Errorf("failed to unmarshal counter: to unmarshal into %T, the data should be in the int32 range", v.Interface()) } v.SetInt(val) default: return errWrongDataLen } return nil } func decReflectInts(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetInt(0) case 8: v.SetInt(decInt64(p)) default: return errWrongDataLen } return nil } func decReflectUint8(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetUint(0) case 8: if p[0] != 0 || p[1] != 0 || p[2] != 0 || p[3] != 0 || p[4] != 0 || p[5] != 0 || p[6] != 0 { return fmt.Errorf("failed to unmarshal counter: to unmarshal into %T, the data should be in the uint8 range", v.Interface()) } v.SetUint(uint64(p[7])) default: return errWrongDataLen } return nil } func decReflectUint16(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetUint(0) case 8: if p[0] != 0 || p[1] != 0 || p[2] != 0 || p[3] != 0 || p[4] != 0 || p[5] != 0 { return fmt.Errorf("failed to unmarshal counter: to unmarshal into %T, the data should be in the uint16 range", v.Interface()) } v.SetUint(uint64(p[6])<<8 | uint64(p[7])) default: return errWrongDataLen } return nil } func decReflectUint32(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetUint(0) case 8: if p[0] != 0 || p[1] != 0 || p[2] != 0 || p[3] != 0 { return fmt.Errorf("failed to unmarshal counter: to unmarshal into %T, the data should be in the uint32 range", v.Interface()) } v.SetUint(uint64(p[4])<<24 | uint64(p[5])<<16 | uint64(p[6])<<8 | uint64(p[7])) default: return errWrongDataLen } return nil } func decReflectUints(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetUint(0) case 8: v.SetUint(decUint64(p)) default: return errWrongDataLen } return nil } func decReflectString(p []byte, v reflect.Value) error { switch len(p) { case 0: if p == nil { v.SetString("") } else { v.SetString("0") } case 8: v.SetString(strconv.FormatInt(decInt64(p), 10)) default: return errWrongDataLen } return nil } func DecReflectR(p []byte, v reflect.Value) error { if v.IsNil() { return fmt.Errorf("failed to unmarshal counter: can not unmarshal into nil reference (%T)(%[1]v)", v.Interface()) } switch v.Type().Elem().Elem().Kind() { case reflect.Int8: return decReflectInt8R(p, v) case reflect.Int16: return decReflectInt16R(p, v) case reflect.Int32: return decReflectInt32R(p, v) case reflect.Int64, reflect.Int: return decReflectIntsR(p, v) case reflect.Uint8: return decReflectUint8R(p, v) case reflect.Uint16: return decReflectUint16R(p, v) case reflect.Uint32: return decReflectUint32R(p, v) case reflect.Uint64, reflect.Uint: return decReflectUintsR(p, v) case reflect.String: return decReflectStringR(p, v) default: return fmt.Errorf("failed to unmarshal counter: unsupported value type (%T)(%[1]v), supported types: %s", v.Interface(), supportedTypes) } } func decReflectInt8R(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) case 8: val := decInt64(p) if val > math.MaxInt8 || val < math.MinInt8 { return fmt.Errorf("failed to unmarshal counter: to unmarshal into %T, the data should be in the int8 range", v.Interface()) } newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetInt(val) v.Elem().Set(newVal) default: return errWrongDataLen } return nil } func decReflectInt16R(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) case 8: val := decInt64(p) if val > math.MaxInt16 || val < math.MinInt16 { return fmt.Errorf("failed to unmarshal counter: to unmarshal into %T, the data should be in the int16 range", v.Interface()) } newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetInt(val) v.Elem().Set(newVal) default: return errWrongDataLen } return nil } func decReflectInt32R(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) case 8: val := decInt64(p) if val > math.MaxInt32 || val < math.MinInt32 { return fmt.Errorf("failed to unmarshal counter: to unmarshal into %T, the data should be in the int32 range", v.Interface()) } newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetInt(val) v.Elem().Set(newVal) default: return errWrongDataLen } return nil } func decReflectIntsR(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) case 8: val := reflect.New(v.Type().Elem().Elem()) val.Elem().SetInt(decInt64(p)) v.Elem().Set(val) default: return errWrongDataLen } return nil } func decReflectUint8R(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) case 8: newVal := reflect.New(v.Type().Elem().Elem()) if p[0] != 0 || p[1] != 0 || p[2] != 0 || p[3] != 0 || p[4] != 0 || p[5] != 0 || p[6] != 0 { return fmt.Errorf("failed to unmarshal counter: to unmarshal into %T, the data should be in the uint8 range", v.Interface()) } newVal.Elem().SetUint(uint64(p[7])) v.Elem().Set(newVal) default: return errWrongDataLen } return nil } func decReflectUint16R(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) case 8: newVal := reflect.New(v.Type().Elem().Elem()) if p[0] != 0 || p[1] != 0 || p[2] != 0 || p[3] != 0 || p[4] != 0 || p[5] != 0 { return fmt.Errorf("failed to unmarshal counter: to unmarshal into %T, the data should be in the uint16 range", v.Interface()) } newVal.Elem().SetUint(uint64(p[6])<<8 | uint64(p[7])) v.Elem().Set(newVal) default: return errWrongDataLen } return nil } func decReflectUint32R(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) case 8: newVal := reflect.New(v.Type().Elem().Elem()) if p[0] != 0 || p[1] != 0 || p[2] != 0 || p[3] != 0 { return fmt.Errorf("failed to unmarshal counter: to unmarshal into %T, the data should be in the uint32 range", v.Interface()) } newVal.Elem().SetUint(uint64(p[4])<<24 | uint64(p[5])<<16 | uint64(p[6])<<8 | uint64(p[7])) v.Elem().Set(newVal) default: return errWrongDataLen } return nil } func decReflectUintsR(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) case 8: val := reflect.New(v.Type().Elem().Elem()) val.Elem().SetUint(decUint64(p)) v.Elem().Set(val) default: return errWrongDataLen } return nil } func decReflectStringR(p []byte, v reflect.Value) error { switch len(p) { case 0: var val reflect.Value if p == nil { val = reflect.Zero(v.Type().Elem()) } else { val = reflect.New(v.Type().Elem().Elem()) val.Elem().SetString("0") } v.Elem().Set(val) case 8: val := reflect.New(v.Type().Elem().Elem()) val.Elem().SetString(strconv.FormatInt(decInt64(p), 10)) v.Elem().Set(val) default: return errWrongDataLen } return nil } func decReflectNullableR(p []byte, v reflect.Value) reflect.Value { if p == nil { return reflect.Zero(v.Elem().Type()) } return reflect.New(v.Type().Elem().Elem()) } func decInt64(p []byte) int64 { return int64(p[0])<<56 | int64(p[1])<<48 | int64(p[2])<<40 | int64(p[3])<<32 | int64(p[4])<<24 | int64(p[5])<<16 | int64(p[6])<<8 | int64(p[7]) } func decUint64(p []byte) uint64 { return uint64(p[0])<<56 | uint64(p[1])<<48 | uint64(p[2])<<40 | uint64(p[3])<<32 | uint64(p[4])<<24 | uint64(p[5])<<16 | uint64(p[6])<<8 | uint64(p[7]) } ================================================ FILE: serialization/cqlint/marshal.go ================================================ package cqlint import ( "math/big" "reflect" ) func Marshal(value any) ([]byte, error) { switch v := value.(type) { case nil: return nil, nil case int8: return EncInt8(v) case int32: return EncInt32(v) case int16: return EncInt16(v) case int64: return EncInt64(v) case int: return EncInt(v) case uint8: return EncUint8(v) case uint16: return EncUint16(v) case uint32: return EncUint32(v) case uint64: return EncUint64(v) case uint: return EncUint(v) case big.Int: return EncBigInt(v) case string: return EncString(v) case *int8: return EncInt8R(v) case *int16: return EncInt16R(v) case *int32: return EncInt32R(v) case *int64: return EncInt64R(v) case *int: return EncIntR(v) case *uint8: return EncUint8R(v) case *uint16: return EncUint16R(v) case *uint32: return EncUint32R(v) case *uint64: return EncUint64R(v) case *uint: return EncUintR(v) case *big.Int: return EncBigIntR(v) case *string: return EncStringR(v) default: // Custom types (type MyInt int) can be serialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.TypeOf(value) if rv.Kind() != reflect.Ptr { return EncReflect(reflect.ValueOf(v)) } return EncReflectR(reflect.ValueOf(v)) } } ================================================ FILE: serialization/cqlint/marshal_utils.go ================================================ package cqlint import ( "fmt" "math" "math/big" "reflect" "strconv" ) const supportedTypes = "~int8, ~int16, ~int32, ~int64, ~int, ~uint8, ~uint16, ~uint32, ~uint64, ~uint, ~string, big.Int" var ( maxBigInt = big.NewInt(math.MaxInt32) minBigInt = big.NewInt(math.MinInt32) ) func EncInt8(v int8) ([]byte, error) { if v < 0 { return []byte{255, 255, 255, byte(v)}, nil } return []byte{0, 0, 0, byte(v)}, nil } func EncInt8R(v *int8) ([]byte, error) { if v == nil { return nil, nil } return EncInt8(*v) } func EncInt16(v int16) ([]byte, error) { if v < 0 { return []byte{255, 255, byte(v >> 8), byte(v)}, nil } return []byte{0, 0, byte(v >> 8), byte(v)}, nil } func EncInt16R(v *int16) ([]byte, error) { if v == nil { return nil, nil } return EncInt16(*v) } func EncInt32(v int32) ([]byte, error) { return []byte{byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)}, nil } func EncInt32R(v *int32) ([]byte, error) { if v == nil { return nil, nil } return EncInt32(*v) } func EncInt64(v int64) ([]byte, error) { if v > math.MaxInt32 || v < math.MinInt32 { return nil, fmt.Errorf("failed to marshal int: value %#v out of range", v) } return encInt64(v), nil } func EncInt64R(v *int64) ([]byte, error) { if v == nil { return nil, nil } return EncInt64(*v) } func EncInt(v int) ([]byte, error) { if v > math.MaxInt32 || v < math.MinInt32 { return nil, fmt.Errorf("failed to marshal int: value %#v out of range", v) } return []byte{byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)}, nil } func EncIntR(v *int) ([]byte, error) { if v == nil { return nil, nil } return EncInt(*v) } func EncUint8(v uint8) ([]byte, error) { return []byte{0, 0, 0, v}, nil } func EncUint8R(v *uint8) ([]byte, error) { if v == nil { return nil, nil } return EncUint8(*v) } func EncUint16(v uint16) ([]byte, error) { return []byte{0, 0, byte(v >> 8), byte(v)}, nil } func EncUint16R(v *uint16) ([]byte, error) { if v == nil { return nil, nil } return EncUint16(*v) } func EncUint32(v uint32) ([]byte, error) { return []byte{byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)}, nil } func EncUint32R(v *uint32) ([]byte, error) { if v == nil { return nil, nil } return EncUint32(*v) } func EncUint64(v uint64) ([]byte, error) { if v > math.MaxUint32 { return nil, fmt.Errorf("failed to marshal int: value %#v out of range", v) } return encUint64(v), nil } func EncUint64R(v *uint64) ([]byte, error) { if v == nil { return nil, nil } return EncUint64(*v) } func EncUint(v uint) ([]byte, error) { if v > math.MaxUint32 { return nil, fmt.Errorf("failed to marshal int: value %#v out of range", v) } return []byte{byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)}, nil } func EncUintR(v *uint) ([]byte, error) { if v == nil { return nil, nil } return EncUint(*v) } func EncBigInt(v big.Int) ([]byte, error) { if v.Cmp(maxBigInt) == 1 || v.Cmp(minBigInt) == -1 { return nil, fmt.Errorf("failed to marshal int: value (%T)(%s) out of range", v, v.String()) } return encInt64(v.Int64()), nil } func EncBigIntR(v *big.Int) ([]byte, error) { if v == nil { return nil, nil } if v.Cmp(maxBigInt) == 1 || v.Cmp(minBigInt) == -1 { return nil, fmt.Errorf("failed to marshal int: value (%T)(%s) out of range", v, v.String()) } return encInt64(v.Int64()), nil } func EncString(v string) ([]byte, error) { if v == "" { return nil, nil } n, err := strconv.ParseInt(v, 10, 32) if err != nil { return nil, fmt.Errorf("failed to marshal int: can not marshal (%T)(%[1]v) %s", v, err) } return encInt64(n), nil } func EncStringR(v *string) ([]byte, error) { if v == nil { return nil, nil } return EncString(*v) } func EncReflect(v reflect.Value) ([]byte, error) { switch v.Type().Kind() { case reflect.Int8: val := v.Int() if val < 0 { return []byte{255, 255, 255, byte(val)}, nil } return []byte{0, 0, 0, byte(val)}, nil case reflect.Int16: val := v.Int() if val < 0 { return []byte{255, 255, byte(val >> 8), byte(val)}, nil } return []byte{0, 0, byte(val >> 8), byte(val)}, nil case reflect.Int32: return encInt64(v.Int()), nil case reflect.Int, reflect.Int64: val := v.Int() if val > math.MaxInt32 || val < math.MinInt32 { return nil, fmt.Errorf("failed to marshal int: value (%T)(%[1]v) out of range", v.Interface()) } return encInt64(val), nil case reflect.Uint8: return []byte{0, 0, 0, byte(v.Uint())}, nil case reflect.Uint16: val := v.Uint() return []byte{0, 0, byte(val >> 8), byte(val)}, nil case reflect.Uint32: return encUint64(v.Uint()), nil case reflect.Uint, reflect.Uint64: val := v.Uint() if val > math.MaxUint32 { return nil, fmt.Errorf("failed to marshal int: value (%T)(%[1]v) out of range", v.Interface()) } return encUint64(val), nil case reflect.String: val := v.String() if val == "" { return nil, nil } n, err := strconv.ParseInt(val, 10, 32) if err != nil { return nil, fmt.Errorf("failed to marshal int: can not marshal (%T)(%[1]v) %s", v.Interface(), err) } return encInt64(n), nil case reflect.Struct: if v.Type().String() == "gocql.unsetColumn" { return nil, nil } return nil, fmt.Errorf("failed to marshal int: unsupported value type (%T)(%[1]v), supported types: %s, unsetColumn", v.Interface(), supportedTypes) default: return nil, fmt.Errorf("failed to marshal int: unsupported value type (%T)(%[1]v), supported types: %s, unsetColumn", v.Interface(), supportedTypes) } } func EncReflectR(v reflect.Value) ([]byte, error) { if v.IsNil() { return nil, nil } return EncReflect(v.Elem()) } func encInt64(v int64) []byte { return []byte{byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} } func encUint64(v uint64) []byte { return []byte{byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} } ================================================ FILE: serialization/cqlint/unmarshal.go ================================================ package cqlint import ( "fmt" "math/big" "reflect" ) func Unmarshal(data []byte, value any) error { switch v := value.(type) { case nil: return nil case *int8: return DecInt8(data, v) case *int16: return DecInt16(data, v) case *int32: return DecInt32(data, v) case *int64: return DecInt64(data, v) case *int: return DecInt(data, v) case *uint8: return DecUint8(data, v) case *uint16: return DecUint16(data, v) case *uint32: return DecUint32(data, v) case *uint64: return DecUint64(data, v) case *uint: return DecUint(data, v) case *big.Int: return DecBigInt(data, v) case *string: return DecString(data, v) case **int8: return DecInt8R(data, v) case **int16: return DecInt16R(data, v) case **int32: return DecInt32R(data, v) case **int64: return DecInt64R(data, v) case **int: return DecIntR(data, v) case **uint8: return DecUint8R(data, v) case **uint16: return DecUint16R(data, v) case **uint32: return DecUint32R(data, v) case **uint64: return DecUint64R(data, v) case **uint: return DecUintR(data, v) case **big.Int: return DecBigIntR(data, v) case **string: return DecStringR(data, v) default: // Custom types (type MyInt int) can be deserialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.ValueOf(value) rt := rv.Type() if rt.Kind() != reflect.Ptr { return fmt.Errorf("failed to unmarshal int: unsupported value type (%T)(%[1]v), supported types: %s", value, supportedTypes) } if rt.Elem().Kind() != reflect.Ptr { return DecReflect(data, rv) } return DecReflectR(data, rv) } } ================================================ FILE: serialization/cqlint/unmarshal_utils.go ================================================ package cqlint import ( "fmt" "math" "math/big" "reflect" "strconv" ) const ( negInt64 = int64(-1) << 32 negInt = int(-1) << 32 ) var errWrongDataLen = fmt.Errorf("failed to unmarshal int: the length of the data should be 0 or 4") func errNilReference(v any) error { return fmt.Errorf("failed to unmarshal int: can not unmarshal into nil reference (%T)(%[1]v))", v) } func DecInt8(p []byte, v *int8) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 4: val := decInt32(p) if val > math.MaxInt8 || val < math.MinInt8 { return fmt.Errorf("failed to unmarshal int: to unmarshal into int8, the data should be in the int8 range") } *v = int8(val) default: return errWrongDataLen } return nil } func DecInt8R(p []byte, v **int8) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(int8) } case 4: val := decInt32(p) if val > math.MaxInt8 || val < math.MinInt8 { return fmt.Errorf("failed to unmarshal int: to unmarshal into int8, the data should be in the int8 range") } tmp := int8(val) *v = &tmp default: return errWrongDataLen } return nil } func DecInt16(p []byte, v *int16) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 4: val := decInt32(p) if val > math.MaxInt16 || val < math.MinInt16 { return fmt.Errorf("failed to unmarshal int: to unmarshal into int16, the data should be in the int16 range") } *v = int16(val) default: return errWrongDataLen } return nil } func DecInt16R(p []byte, v **int16) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(int16) } case 4: val := decInt32(p) if val > math.MaxInt16 || val < math.MinInt16 { return fmt.Errorf("failed to unmarshal int: to unmarshal into int16, the data should be in the int16 range") } tmp := int16(val) *v = &tmp default: return errWrongDataLen } return nil } func DecInt32(p []byte, v *int32) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 4: *v = decInt32(p) default: return errWrongDataLen } return nil } func DecInt32R(p []byte, v **int32) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(int32) } case 4: tmp := decInt32(p) *v = &tmp default: return errWrongDataLen } return nil } func DecInt64(p []byte, v *int64) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 4: *v = decInt64(p) default: return errWrongDataLen } return nil } func DecInt64R(p []byte, v **int64) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(int64) } case 4: val := decInt64(p) *v = &val default: return errWrongDataLen } return nil } func DecInt(p []byte, v *int) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 4: *v = decInt(p) default: return errWrongDataLen } return nil } func DecIntR(p []byte, v **int) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(int) } case 4: val := decInt(p) *v = &val default: return errWrongDataLen } return nil } func DecUint8(p []byte, v *uint8) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 4: if p[0] != 0 || p[1] != 0 || p[2] != 0 { return fmt.Errorf("failed to unmarshal int: to unmarshal into uint8, the data should be in the uint8 range") } *v = p[3] default: return errWrongDataLen } return nil } func DecUint8R(p []byte, v **uint8) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(uint8) } case 4: if p[0] != 0 || p[1] != 0 || p[2] != 0 { return fmt.Errorf("failed to unmarshal int: to unmarshal into uint8, the data should be in the uint8 range") } val := p[3] *v = &val default: return errWrongDataLen } return nil } func DecUint16(p []byte, v *uint16) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 4: if p[0] != 0 || p[1] != 0 { return fmt.Errorf("failed to unmarshal int: to unmarshal into uint16, the data should be in the uint16 range") } *v = uint16(p[2])<<8 | uint16(p[3]) default: return errWrongDataLen } return nil } func DecUint16R(p []byte, v **uint16) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(uint16) } case 4: if p[0] != 0 || p[1] != 0 { return fmt.Errorf("failed to unmarshal int: to unmarshal into uint16, the data should be in the uint16 range") } val := uint16(p[2])<<8 | uint16(p[3]) *v = &val default: return errWrongDataLen } return nil } func DecUint32(p []byte, v *uint32) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 4: *v = uint32(p[0])<<24 | uint32(p[1])<<16 | uint32(p[2])<<8 | uint32(p[3]) default: return errWrongDataLen } return nil } func DecUint32R(p []byte, v **uint32) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(uint32) } case 4: val := uint32(p[0])<<24 | uint32(p[1])<<16 | uint32(p[2])<<8 | uint32(p[3]) *v = &val default: return errWrongDataLen } return nil } func DecUint64(p []byte, v *uint64) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 4: *v = decUint64(p) default: return errWrongDataLen } return nil } func DecUint64R(p []byte, v **uint64) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(uint64) } case 4: val := decUint64(p) *v = &val default: return errWrongDataLen } return nil } func DecUint(p []byte, v *uint) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 4: *v = uint(p[0])<<24 | uint(p[1])<<16 | uint(p[2])<<8 | uint(p[3]) default: return errWrongDataLen } return nil } func DecUintR(p []byte, v **uint) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(uint) } case 4: val := uint(p[0])<<24 | uint(p[1])<<16 | uint(p[2])<<8 | uint(p[3]) *v = &val default: return errWrongDataLen } return nil } func DecString(p []byte, v *string) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = "" } else { *v = "0" } case 4: *v = strconv.FormatInt(decInt64(p), 10) default: return errWrongDataLen } return nil } func DecStringR(p []byte, v **string) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { val := "0" *v = &val } case 4: val := strconv.FormatInt(decInt64(p), 10) *v = &val default: return errWrongDataLen } return nil } func DecBigInt(p []byte, v *big.Int) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: v.SetInt64(0) case 4: v.SetInt64(decInt64(p)) default: return errWrongDataLen } return nil } func DecBigIntR(p []byte, v **big.Int) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = big.NewInt(0) } case 4: *v = big.NewInt(decInt64(p)) default: return errWrongDataLen } return nil } func DecReflect(p []byte, v reflect.Value) error { if v.IsNil() { return fmt.Errorf("failed to unmarshal int: can not unmarshal into nil reference (%T)(%[1]v)", v.Interface()) } switch v = v.Elem(); v.Kind() { case reflect.Int8: return decReflectInt8(p, v) case reflect.Int16: return decReflectInt16(p, v) case reflect.Int32, reflect.Int64, reflect.Int: return decReflectInts(p, v) case reflect.Uint8: return decReflectUint8(p, v) case reflect.Uint16: return decReflectUint16(p, v) case reflect.Uint32, reflect.Uint64, reflect.Uint: return decReflectUints(p, v) case reflect.String: return decReflectString(p, v) default: return fmt.Errorf("failed to unmarshal int: unsupported value type (%T)(%[1]v), supported types: %s", v.Interface(), supportedTypes) } } func DecReflectR(p []byte, v reflect.Value) error { if v.IsNil() { return fmt.Errorf("failed to unmarshal int: can not unmarshal into nil reference (%T)(%[1]v)", v.Interface()) } switch v.Type().Elem().Elem().Kind() { case reflect.Int8: return decReflectInt8R(p, v) case reflect.Int16: return decReflectInt16R(p, v) case reflect.Int32, reflect.Int64, reflect.Int: return decReflectIntsR(p, v) case reflect.Uint8: return decReflectUint8R(p, v) case reflect.Uint16: return decReflectUint16R(p, v) case reflect.Uint32, reflect.Uint64, reflect.Uint: return decReflectUintsR(p, v) case reflect.String: return decReflectStringR(p, v) default: return fmt.Errorf("failed to unmarshal int: unsupported value type (%T)(%[1]v), supported types: %s", v.Interface(), supportedTypes) } } func decReflectInt8(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetInt(0) case 4: val := decInt64(p) if val > math.MaxInt8 || val < math.MinInt8 { return fmt.Errorf("failed to unmarshal int: to unmarshal into (%T), the data should be in the int8 range", v.Interface()) } v.SetInt(val) default: return errWrongDataLen } return nil } func decReflectInt16(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetInt(0) case 4: val := decInt64(p) if val > math.MaxInt16 || val < math.MinInt16 { return fmt.Errorf("failed to unmarshal int: to unmarshal into (%T), the data should be in the int16 range", v.Interface()) } v.SetInt(val) default: return errWrongDataLen } return nil } func decReflectInts(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetInt(0) case 4: v.SetInt(decInt64(p)) default: return errWrongDataLen } return nil } func decReflectUint8(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetUint(0) case 4: if p[0] != 0 || p[1] != 0 || p[2] != 0 { return fmt.Errorf("failed to unmarshal int: to unmarshal into (%T), the data should be in the uint8 range", v.Interface()) } v.SetUint(uint64(p[3])) default: return errWrongDataLen } return nil } func decReflectUint16(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetUint(0) case 4: if p[0] != 0 || p[1] != 0 { return fmt.Errorf("failed to unmarshal int: to unmarshal into (%T), the data should be in the uint16 range", v.Interface()) } v.SetUint(uint64(p[2])<<8 | uint64(p[3])) default: return errWrongDataLen } return nil } func decReflectUints(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetUint(0) case 4: v.SetUint(decUint64(p)) default: return errWrongDataLen } return nil } func decReflectString(p []byte, v reflect.Value) error { switch len(p) { case 0: if p == nil { v.SetString("") } else { v.SetString("0") } case 4: v.SetString(strconv.FormatInt(decInt64(p), 10)) default: return errWrongDataLen } return nil } func decReflectNullableR(p []byte, v reflect.Value) reflect.Value { if p == nil { return reflect.Zero(v.Elem().Type()) } return reflect.New(v.Type().Elem().Elem()) } func decReflectInt8R(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) case 4: val := decInt64(p) if val > math.MaxInt8 || val < math.MinInt8 { return fmt.Errorf("failed to unmarshal int: to unmarshal into (%T), the data should be in the int8 range", v.Interface()) } newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetInt(val) v.Elem().Set(newVal) default: return errWrongDataLen } return nil } func decReflectInt16R(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) case 4: val := decInt64(p) if val > math.MaxInt16 || val < math.MinInt16 { return fmt.Errorf("failed to unmarshal int: to unmarshal into (%T), the data should be in the int16 range", v.Interface()) } newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetInt(val) v.Elem().Set(newVal) default: return errWrongDataLen } return nil } func decReflectIntsR(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) case 4: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetInt(decInt64(p)) v.Elem().Set(newVal) default: return errWrongDataLen } return nil } func decReflectUint8R(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) case 4: if p[0] != 0 || p[1] != 0 || p[2] != 0 { return fmt.Errorf("failed to unmarshal int: to unmarshal into (%T), the data should be in the uint8 range", v.Interface()) } newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetUint(uint64(p[3])) v.Elem().Set(newVal) default: return errWrongDataLen } return nil } func decReflectUint16R(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) case 4: if p[0] != 0 || p[1] != 0 { return fmt.Errorf("failed to unmarshal int: to unmarshal into (%T), the data should be in the uint16 range", v.Interface()) } newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetUint(uint64(p[2])<<8 | uint64(p[3])) v.Elem().Set(newVal) default: return errWrongDataLen } return nil } func decReflectUintsR(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) case 4: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetUint(decUint64(p)) v.Elem().Set(newVal) default: return errWrongDataLen } return nil } func decReflectStringR(p []byte, v reflect.Value) error { switch len(p) { case 0: var val reflect.Value if p == nil { val = reflect.Zero(v.Type().Elem()) } else { val = reflect.New(v.Type().Elem().Elem()) val.Elem().SetString("0") } v.Elem().Set(val) case 4: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetString(strconv.FormatInt(decInt64(p), 10)) v.Elem().Set(newVal) default: return errWrongDataLen } return nil } func decInt32(p []byte) int32 { return int32(p[0])<<24 | int32(p[1])<<16 | int32(p[2])<<8 | int32(p[3]) } func decInt64(p []byte) int64 { if p[0] > math.MaxInt8 { return negInt64 | int64(p[0])<<24 | int64(p[1])<<16 | int64(p[2])<<8 | int64(p[3]) } return int64(p[0])<<24 | int64(p[1])<<16 | int64(p[2])<<8 | int64(p[3]) } func decInt(p []byte) int { if p[0] > math.MaxInt8 { return negInt | int(p[0])<<24 | int(p[1])<<16 | int(p[2])<<8 | int(p[3]) } return int(p[0])<<24 | int(p[1])<<16 | int(p[2])<<8 | int(p[3]) } func decUint64(p []byte) uint64 { return uint64(p[0])<<24 | uint64(p[1])<<16 | uint64(p[2])<<8 | uint64(p[3]) } ================================================ FILE: serialization/cqltime/marshal.go ================================================ package cqltime import ( "reflect" "time" ) func Marshal(value any) ([]byte, error) { switch v := value.(type) { case nil: return nil, nil case int64: return EncInt64(v) case *int64: return EncInt64R(v) case time.Duration: return EncDuration(v) case *time.Duration: return EncDurationR(v) default: // Custom types (type MyTime int64) can be serialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.TypeOf(value) if rv.Kind() != reflect.Ptr { return EncReflect(reflect.ValueOf(v)) } return EncReflectR(reflect.ValueOf(v)) } } ================================================ FILE: serialization/cqltime/marshal_utils.go ================================================ package cqltime import ( "fmt" "reflect" "time" ) const ( maxValInt64 int64 = 86399999999999 minValInt64 int64 = 0 maxValDur time.Duration = 86399999999999 minValDur time.Duration = 0 ) var ( errOutRangeInt64 = fmt.Errorf("failed to marshal time: the (int64) should be in the range 0 to 86399999999999") errOutRangeDur = fmt.Errorf("failed to marshal time: the (time.Duration) should be in the range 0 to 86399999999999") ) func EncInt64(v int64) ([]byte, error) { if v > maxValInt64 || v < minValInt64 { return nil, errOutRangeInt64 } return encInt64(v), nil } func EncInt64R(v *int64) ([]byte, error) { if v == nil { return nil, nil } return EncInt64(*v) } func EncDuration(v time.Duration) ([]byte, error) { if v > maxValDur || v < minValDur { return nil, errOutRangeDur } return []byte{byte(v >> 56), byte(v >> 48), byte(v >> 40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)}, nil } func EncDurationR(v *time.Duration) ([]byte, error) { if v == nil { return nil, nil } return EncDuration(*v) } func EncReflect(v reflect.Value) ([]byte, error) { switch v.Kind() { case reflect.Int64: val := v.Int() if val > maxValInt64 || val < minValInt64 { return nil, fmt.Errorf("failed to marshal time: the (%T) should be in the range 0 to 86399999999999", v.Interface()) } return encInt64(val), nil case reflect.Struct: if v.Type().String() == "gocql.unsetColumn" { return nil, nil } return nil, fmt.Errorf("failed to marshal time: unsupported value type (%T)(%[1]v), supported types: ~int64, time.Duration, unsetColumn", v.Interface()) default: return nil, fmt.Errorf("failed to marshal time: unsupported value type (%T)(%[1]v), supported types: ~int64, time.Duration, unsetColumn", v.Interface()) } } func EncReflectR(v reflect.Value) ([]byte, error) { if v.IsNil() { return nil, nil } return EncReflect(v.Elem()) } func encInt64(v int64) []byte { return []byte{byte(v >> 56), byte(v >> 48), byte(v >> 40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} } ================================================ FILE: serialization/cqltime/unmarshal.go ================================================ package cqltime import ( "fmt" "reflect" "time" ) func Unmarshal(data []byte, value any) error { switch v := value.(type) { case nil: return nil case *int64: return DecInt64(data, v) case **int64: return DecInt64R(data, v) case *time.Duration: return DecDuration(data, v) case **time.Duration: return DecDurationR(data, v) default: // Custom types (type MyTime int64) can be deserialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.ValueOf(value) rt := rv.Type() if rt.Kind() != reflect.Ptr { return fmt.Errorf("failed to unmarshal time: unsupported value type (%T)(%[1]v), supported types: ~int64, time.Duration", value) } if rt.Elem().Kind() != reflect.Ptr { return DecReflect(data, rv) } return DecReflectR(data, rv) } } ================================================ FILE: serialization/cqltime/unmarshal_utils.go ================================================ package cqltime import ( "fmt" "reflect" "time" ) var ( errWrongDataLen = fmt.Errorf("failed to unmarshal time: the length of the data should be 0 or 8") errDataOutRangeInt64 = fmt.Errorf("failed to unmarshal time: (int64) the data should be in the range 0 to 86399999999999") errDataOutRangeDur = fmt.Errorf("failed to unmarshal time: (time.Duration) the data should be in the range 0 to 86399999999999") ) func errNilReference(v any) error { return fmt.Errorf("failed to unmarshal time: can not unmarshal into nil reference (%T)(%[1]v))", v) } func DecInt64(p []byte, v *int64) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 8: *v = decInt64(p) if *v > maxValInt64 || *v < minValInt64 { return errDataOutRangeInt64 } default: return errWrongDataLen } return nil } func DecInt64R(p []byte, v **int64) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(int64) } case 8: val := decInt64(p) if val > maxValInt64 || val < minValInt64 { return errDataOutRangeInt64 } *v = &val default: return errWrongDataLen } return nil } func DecDuration(p []byte, v *time.Duration) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 8: *v = decDur(p) if *v > maxValDur || *v < minValDur { return errDataOutRangeDur } default: return errWrongDataLen } return nil } func DecDurationR(p []byte, v **time.Duration) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(time.Duration) } case 8: val := decDur(p) if val > maxValDur || val < minValDur { return errDataOutRangeDur } *v = &val default: return errWrongDataLen } return nil } func DecReflect(p []byte, v reflect.Value) error { if v.IsNil() { return fmt.Errorf("failed to unmarshal time: can not unmarshal into nil reference (%T)(%[1]v))", v.Interface()) } switch v = v.Elem(); v.Kind() { case reflect.Int64, reflect.Int: return decReflectInt64(p, v) default: return fmt.Errorf("failed to unmarshal time: unsupported value type (%T)(%[1]v), supported types: ~int64, time.Duration", v.Interface()) } } func decReflectInt64(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetInt(0) case 8: val := decInt64(p) if val > maxValInt64 || val < minValInt64 { return fmt.Errorf("failed to unmarshal time: (%T) the data should be in the range 0 to 86399999999999", v.Interface()) } v.SetInt(val) default: return errWrongDataLen } return nil } func DecReflectR(p []byte, v reflect.Value) error { if v.IsNil() { return fmt.Errorf("failed to unmarshal time: can not unmarshal into nil reference (%T)(%[1]v)", v.Interface()) } switch v.Type().Elem().Elem().Kind() { case reflect.Int64, reflect.Int: return decReflectIntsR(p, v) default: return fmt.Errorf("failed to unmarshal time: unsupported value type (%T)(%[1]v), supported types: ~int64, time.Duration", v.Interface()) } } func decReflectIntsR(p []byte, v reflect.Value) error { switch len(p) { case 0: if p == nil { v.Elem().Set(reflect.Zero(v.Elem().Type())) } else { v.Elem().Set(reflect.New(v.Type().Elem().Elem())) } case 8: vv := decInt64(p) if vv > maxValInt64 || vv < minValInt64 { return fmt.Errorf("failed to unmarshal time: (%T) the data should be in the range 0 to 86399999999999", v.Interface()) } val := reflect.New(v.Type().Elem().Elem()) val.Elem().SetInt(vv) v.Elem().Set(val) default: return errWrongDataLen } return nil } func decInt64(p []byte) int64 { return int64(p[0])<<56 | int64(p[1])<<48 | int64(p[2])<<40 | int64(p[3])<<32 | int64(p[4])<<24 | int64(p[5])<<16 | int64(p[6])<<8 | int64(p[7]) } func decDur(p []byte) time.Duration { return time.Duration(p[0])<<56 | time.Duration(p[1])<<48 | time.Duration(p[2])<<40 | time.Duration(p[3])<<32 | time.Duration(p[4])<<24 | time.Duration(p[5])<<16 | time.Duration(p[6])<<8 | time.Duration(p[7]) } ================================================ FILE: serialization/date/marshal.go ================================================ package date import ( "reflect" "time" ) func Marshal(value any) ([]byte, error) { switch v := value.(type) { case nil: return nil, nil case int32: return EncInt32(v) case int64: return EncInt64(v) case uint32: return EncUint32(v) case string: return EncString(v) case time.Time: return EncTime(v) case *int32: return EncInt32R(v) case *int64: return EncInt64R(v) case *uint32: return EncUint32R(v) case *string: return EncStringR(v) case *time.Time: return EncTimeR(v) default: // Custom types (type MyDate uint32) can be serialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.TypeOf(value) if rv.Kind() != reflect.Ptr { return EncReflect(reflect.ValueOf(v)) } return EncReflectR(reflect.ValueOf(v)) } } ================================================ FILE: serialization/date/marshal_utils.go ================================================ package date import ( "fmt" "reflect" "strconv" "strings" "time" ) const ( millisecondsInADay int64 = 24 * 60 * 60 * 1000 centerEpoch int64 = 1 << 31 maxYear int = 5881580 minYear int = -5877641 maxMilliseconds int64 = 185542587100800000 minMilliseconds int64 = -185542587187200000 ) var ( maxDate = time.Date(5881580, 07, 11, 0, 0, 0, 0, time.UTC) minDate = time.Date(-5877641, 06, 23, 0, 0, 0, 0, time.UTC) ) func errWrongStringFormat(v any) error { return fmt.Errorf(`failed to marshal date: the (%T)(%[1]v) should have fromat "2006-01-02"`, v) } func EncInt32(v int32) ([]byte, error) { return encInt32(v), nil } func EncInt32R(v *int32) ([]byte, error) { if v == nil { return nil, nil } return encInt32(*v), nil } func EncInt64(v int64) ([]byte, error) { if v > maxMilliseconds || v < minMilliseconds { return nil, fmt.Errorf("failed to marshal date: the (int64)(%v) value out of range", v) } return encInt64(days(v)), nil } func EncInt64R(v *int64) ([]byte, error) { if v == nil { return nil, nil } return EncInt64(*v) } func EncUint32(v uint32) ([]byte, error) { return encUint32(v), nil } func EncUint32R(v *uint32) ([]byte, error) { if v == nil { return nil, nil } return encUint32(*v), nil } func EncTime(v time.Time) ([]byte, error) { if v.After(maxDate) || v.Before(minDate) { return nil, fmt.Errorf("failed to marshal date: the (%T)(%s) value should be in the range from -5877641-06-23 to 5881580-07-11", v, v.Format("2006-01-02")) } return encTime(v), nil } func EncTimeR(v *time.Time) ([]byte, error) { if v == nil { return nil, nil } return EncTime(*v) } func EncString(v string) ([]byte, error) { if v == "" { return nil, nil } var err error var y, m, d int var t time.Time switch ps := strings.Split(v, "-"); len(ps) { case 3: if y, err = strconv.Atoi(ps[0]); err != nil { return nil, errWrongStringFormat(v) } if m, err = strconv.Atoi(ps[1]); err != nil { return nil, errWrongStringFormat(v) } if d, err = strconv.Atoi(ps[2]); err != nil { return nil, errWrongStringFormat(v) } case 4: if y, err = strconv.Atoi(ps[1]); err != nil || ps[0] != "" { return nil, errWrongStringFormat(v) } y = -y if m, err = strconv.Atoi(ps[2]); err != nil { return nil, errWrongStringFormat(v) } if d, err = strconv.Atoi(ps[3]); err != nil { return nil, errWrongStringFormat(v) } default: return nil, errWrongStringFormat(v) } if y > maxYear || y < minYear { return nil, fmt.Errorf("failed to marshal date: the (%T)(%[1]v) value should be in the range from -5877641-06-23 to 5881580-07-11", v) } t = time.Date(y, time.Month(m), d, 0, 0, 0, 0, time.UTC) if t.After(maxDate) || t.Before(minDate) { return nil, fmt.Errorf("failed to marshal date: the (%T)(%[1]v) value should be in the range from -5877641-06-23 to 5881580-07-11", v) } return encTime(t), nil } func EncStringR(v *string) ([]byte, error) { if v == nil { return nil, nil } return EncString(*v) } func EncReflect(v reflect.Value) ([]byte, error) { switch v.Kind() { case reflect.Int32: return encInt64(v.Int()), nil case reflect.Int64: val := v.Int() if val > maxMilliseconds || val < minMilliseconds { return nil, fmt.Errorf("failed to marshal date: the value (%T)(%[1]v) out of range", v.Interface()) } return encInt64(days(val)), nil case reflect.Uint32: val := v.Uint() return []byte{byte(val >> 24), byte(val >> 16), byte(val >> 8), byte(val)}, nil case reflect.String: return encReflectString(v) case reflect.Struct: if v.Type().String() == "gocql.unsetColumn" { return nil, nil } return nil, fmt.Errorf("failed to marshal date: unsupported value type (%T)(%[1]v), supported types: ~int32, ~int64, ~uint32, ~string, time.Time, unsetColumn", v.Interface()) default: return nil, fmt.Errorf("failed to marshal date: unsupported value type (%T)(%[1]v), supported types: ~int32, ~int64, ~uint32, ~string, time.Time, unsetColumn", v.Interface()) } } func EncReflectR(v reflect.Value) ([]byte, error) { if v.IsNil() { return nil, nil } return EncReflect(v.Elem()) } func encReflectString(v reflect.Value) ([]byte, error) { val := v.String() if val == "" { return nil, nil } var err error var y, m, d int var t time.Time ps := strings.Split(val, "-") switch len(ps) { case 3: if y, err = strconv.Atoi(ps[0]); err != nil { return nil, errWrongStringFormat(v.Interface()) } if m, err = strconv.Atoi(ps[1]); err != nil { return nil, errWrongStringFormat(v.Interface()) } if d, err = strconv.Atoi(ps[2]); err != nil { return nil, errWrongStringFormat(v.Interface()) } case 4: if y, err = strconv.Atoi(ps[1]); err != nil { return nil, errWrongStringFormat(v.Interface()) } y = -y if m, err = strconv.Atoi(ps[2]); err != nil { return nil, errWrongStringFormat(v.Interface()) } if d, err = strconv.Atoi(ps[3]); err != nil { return nil, errWrongStringFormat(v.Interface()) } default: return nil, errWrongStringFormat(v.Interface()) } if y > maxYear || y < minYear { return nil, fmt.Errorf("failed to marshal date: the (%T)(%[1]v) value should be in the range from -5877641-06-23 to 5881580-07-11", v.Interface()) } t = time.Date(y, time.Month(m), d, 0, 0, 0, 0, time.UTC) if t.After(maxDate) || t.Before(minDate) { return nil, fmt.Errorf("failed to marshal date: the (%T)(%[1]v) value should be in the range from -5877641-06-23 to 5881580-07-11", v.Interface()) } return encTime(t), nil } func encInt64(v int64) []byte { return []byte{byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} } func encInt32(v int32) []byte { return []byte{byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} } func encUint32(v uint32) []byte { return []byte{byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} } func encTime(v time.Time) []byte { d := days(v.UnixMilli()) return []byte{byte(d >> 24), byte(d >> 16), byte(d >> 8), byte(d)} } func days(v int64) int64 { return v/millisecondsInADay + centerEpoch } ================================================ FILE: serialization/date/unmarshal.go ================================================ package date import ( "fmt" "reflect" "time" ) func Unmarshal(data []byte, value any) error { switch v := value.(type) { case nil: return nil case *int32: return DecInt32(data, v) case *int64: return DecInt64(data, v) case *uint32: return DecUint32(data, v) case *string: return DecString(data, v) case *time.Time: return DecTime(data, v) case **int32: return DecInt32R(data, v) case **int64: return DecInt64R(data, v) case **uint32: return DecUint32R(data, v) case **string: return DecStringR(data, v) case **time.Time: return DecTimeR(data, v) default: // Custom types (type MyDate uint32) can be deserialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.ValueOf(value) rt := rv.Type() if rt.Kind() != reflect.Ptr { return fmt.Errorf("failed to unmarshal date: unsupported value type (%T)(%[1]v), supported types: ~int32, ~int64, ~uint32, ~string, time.Time", value) } if rt.Elem().Kind() != reflect.Ptr { return DecReflect(data, rv) } return DecReflectR(data, rv) } } ================================================ FILE: serialization/date/unmarshal_utils.go ================================================ package date import ( "fmt" "math" "reflect" "time" ) const ( negInt64 = int64(-1) << 32 zeroDate = "-5877641-06-23" zeroMS int64 = -185542587187200000 ) var errWrongDataLen = fmt.Errorf("failed to unmarshal date: the length of the data should be 0 or 4") func errNilReference(v any) error { return fmt.Errorf("failed to unmarshal date: can not unmarshal into nil reference (%T)(%[1]v))", v) } func DecInt32(p []byte, v *int32) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 4: *v = decInt32(p) default: return errWrongDataLen } return nil } func DecInt32R(p []byte, v **int32) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(int32) } case 4: val := decInt32(p) *v = &val default: return errWrongDataLen } return nil } func DecInt64(p []byte, v *int64) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = zeroMS case 4: *v = decMilliseconds(p) default: return errWrongDataLen } return nil } func DecInt64R(p []byte, v **int64) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { val := zeroMS *v = &val } case 4: val := decMilliseconds(p) *v = &val default: return errWrongDataLen } return nil } func DecUint32(p []byte, v *uint32) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 4: *v = decUint32(p) default: return errWrongDataLen } return nil } func DecUint32R(p []byte, v **uint32) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(uint32) } case 4: val := decUint32(p) *v = &val default: return errWrongDataLen } return nil } func DecString(p []byte, v *string) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = "" } else { *v = zeroDate } case 4: *v = decString(p) default: return errWrongDataLen } return nil } func DecStringR(p []byte, v **string) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { val := zeroDate *v = &val } case 4: val := decString(p) *v = &val default: return errWrongDataLen } return nil } func DecTime(p []byte, v *time.Time) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = minDate case 4: *v = decTime(p) default: return errWrongDataLen } return nil } func DecTimeR(p []byte, v **time.Time) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { val := minDate *v = &val } case 4: val := decTime(p) *v = &val default: return errWrongDataLen } return nil } func DecReflect(p []byte, v reflect.Value) error { if v.IsNil() { return fmt.Errorf("failed to unmarshal date: can not unmarshal into nil reference (%T)(%[1]v))", v.Interface()) } switch v = v.Elem(); v.Kind() { case reflect.Int32: return decReflectInt32(p, v) case reflect.Int64: return decReflectInt64(p, v) case reflect.Uint32: return decReflectUint32(p, v) case reflect.String: return decReflectString(p, v) default: return fmt.Errorf("failed to unmarshal date: unsupported value type (%T)(%[1]v), supported types: ~int32, ~int64, ~uint32, ~string, time.Time", v.Interface()) } } func decReflectInt32(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetInt(0) case 4: v.SetInt(decInt64(p)) default: return errWrongDataLen } return nil } func decReflectInt64(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetInt(zeroMS) case 4: v.SetInt(decMilliseconds(p)) default: return errWrongDataLen } return nil } func decReflectUint32(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetUint(0) case 4: v.SetUint(decUint64(p)) default: return errWrongDataLen } return nil } func decReflectString(p []byte, v reflect.Value) error { switch len(p) { case 0: if p == nil { v.SetString("") } else { v.SetString(zeroDate) } case 4: v.SetString(decString(p)) default: return errWrongDataLen } return nil } func DecReflectR(p []byte, v reflect.Value) error { if v.IsNil() { return fmt.Errorf("failed to unmarshal date: can not unmarshal into nil reference (%T)(%[1]v)", v.Interface()) } switch v.Type().Elem().Elem().Kind() { case reflect.Int32: return decReflectInt32R(p, v) case reflect.Int64: return decReflectInt64R(p, v) case reflect.Uint32: return decReflectUint32R(p, v) case reflect.String: return decReflectStringR(p, v) default: return fmt.Errorf("failed to unmarshal date: unsupported value type (%T)(%[1]v), supported types: ~int32, ~int64, ~uint32, ~string, time.Time", v.Interface()) } } func decReflectInt32R(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) case 4: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetInt(decInt64(p)) v.Elem().Set(newVal) default: return errWrongDataLen } return nil } func decReflectInt64R(p []byte, v reflect.Value) error { switch len(p) { case 0: var val reflect.Value if p == nil { val = reflect.Zero(v.Type().Elem()) } else { val = reflect.New(v.Type().Elem().Elem()) val.Elem().SetInt(zeroMS) v.Elem().Set(val) } v.Elem().Set(val) case 4: val := reflect.New(v.Type().Elem().Elem()) val.Elem().SetInt(decMilliseconds(p)) v.Elem().Set(val) default: return errWrongDataLen } return nil } func decReflectUint32R(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) case 4: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetUint(decUint64(p)) v.Elem().Set(newVal) default: return errWrongDataLen } return nil } func decReflectStringR(p []byte, v reflect.Value) error { switch len(p) { case 0: var val reflect.Value if p == nil { val = reflect.Zero(v.Type().Elem()) } else { val = reflect.New(v.Type().Elem().Elem()) val.Elem().SetString(zeroDate) } v.Elem().Set(val) case 4: val := reflect.New(v.Type().Elem().Elem()) val.Elem().SetString(decString(p)) v.Elem().Set(val) default: return errWrongDataLen } return nil } func decReflectNullableR(p []byte, v reflect.Value) reflect.Value { if p == nil { return reflect.Zero(v.Elem().Type()) } return reflect.New(v.Type().Elem().Elem()) } func decInt32(p []byte) int32 { return int32(p[0])<<24 | int32(p[1])<<16 | int32(p[2])<<8 | int32(p[3]) } func decInt64(p []byte) int64 { if p[0] > math.MaxInt8 { return negInt64 | int64(p[0])<<24 | int64(p[1])<<16 | int64(p[2])<<8 | int64(p[3]) } return int64(p[0])<<24 | int64(p[1])<<16 | int64(p[2])<<8 | int64(p[3]) } func decMilliseconds(p []byte) int64 { return (int64(p[0])<<24 | int64(p[1])<<16 | int64(p[2])<<8 | int64(p[3]) - centerEpoch) * millisecondsInADay } func decUint32(p []byte) uint32 { return uint32(p[0])<<24 | uint32(p[1])<<16 | uint32(p[2])<<8 | uint32(p[3]) } func decUint64(p []byte) uint64 { return uint64(p[0])<<24 | uint64(p[1])<<16 | uint64(p[2])<<8 | uint64(p[3]) } func decString(p []byte) string { return decTime(p).Format("2006-01-02") } func decTime(p []byte) time.Time { return time.UnixMilli(decMilliseconds(p)).UTC() } ================================================ FILE: serialization/decimal/marshal.go ================================================ package decimal import ( "reflect" "gopkg.in/inf.v0" ) func Marshal(value any) ([]byte, error) { switch v := value.(type) { case nil: return nil, nil case inf.Dec: return EncInfDec(v) case *inf.Dec: return EncInfDecR(v) case string: return EncString(v) case *string: return EncStringR(v) default: // Custom types (type MyString string) can be serialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.TypeOf(value) if rv.Kind() != reflect.Ptr { return EncReflect(reflect.ValueOf(v)) } return EncReflectR(reflect.ValueOf(v)) } } ================================================ FILE: serialization/decimal/marshal_utils.go ================================================ package decimal import ( "fmt" "math/big" "reflect" "strconv" "strings" "gopkg.in/inf.v0" "github.com/gocql/gocql/serialization/varint" ) func EncInfDec(v inf.Dec) ([]byte, error) { sign := v.Sign() if sign == 0 { return []byte{0, 0, 0, 0, 0}, nil } return append(encScale(v.Scale()), varint.EncBigIntRS(v.UnscaledBig())...), nil } func EncInfDecR(v *inf.Dec) ([]byte, error) { if v == nil { return nil, nil } return encInfDecR(v), nil } // EncString encodes decimal string which should contains `scale` and `unscaled` strings separated by `;`. func EncString(v string) ([]byte, error) { if v == "" { return nil, nil } vs := strings.Split(v, ";") if len(vs) != 2 { return nil, fmt.Errorf("failed to marshal decimal: invalid decimal string %s", v) } scale, err := strconv.ParseInt(vs[0], 10, 32) if err != nil { return nil, fmt.Errorf("failed to marshal decimal: invalid decimal scale string %s", vs[0]) } unscaleData, err := encUnscaledString(vs[1]) if err != nil { return nil, err } return append(encScale64(scale), unscaleData...), nil } func EncStringR(v *string) ([]byte, error) { if v == nil { return nil, nil } return EncString(*v) } func EncReflect(v reflect.Value) ([]byte, error) { switch v.Type().Kind() { case reflect.String: return encReflectString(v) case reflect.Struct: if v.Type().String() == "gocql.unsetColumn" { return nil, nil } return nil, fmt.Errorf("failed to marshal decimal: unsupported value type (%T)(%[1]v), supported types: ~string, inf.Dec, unsetColumn", v.Interface()) default: return nil, fmt.Errorf("failed to marshal decimal: unsupported value type (%T)(%[1]v), supported types: ~string, inf.Dec, unsetColumn", v.Interface()) } } func EncReflectR(v reflect.Value) ([]byte, error) { if v.IsNil() { return nil, nil } return EncReflect(v.Elem()) } func encReflectString(v reflect.Value) ([]byte, error) { val := v.String() if val == "" { return nil, nil } vs := strings.Split(val, ";") if len(vs) != 2 { return nil, fmt.Errorf("failed to marshal decimal: invalid decimal string (%T)(%[1]v)", v.Interface()) } scale, err := strconv.ParseInt(vs[0], 10, 32) if err != nil { return nil, fmt.Errorf("failed to marshal decimal: invalid decimal scale string (%T)(%s)", v.Interface(), vs[0]) } unscaledData, err := encUnscaledString(vs[1]) if err != nil { return nil, err } return append(encScale64(scale), unscaledData...), nil } func encInfDecR(v *inf.Dec) []byte { sign := v.Sign() if sign == 0 { return []byte{0, 0, 0, 0, 0} } return append(encScale(v.Scale()), varint.EncBigIntRS(v.UnscaledBig())...) } func encScale(v inf.Scale) []byte { return []byte{byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} } func encScale64(v int64) []byte { return []byte{byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} } func encUnscaledString(v string) ([]byte, error) { switch { case len(v) == 0: return nil, nil case len(v) <= 18: n, err := strconv.ParseInt(v, 10, 64) if err != nil { return nil, fmt.Errorf("failed to marshal decimal: invalid unscaled string %s, %s", v, err) } return varint.EncInt64Ext(n), nil case len(v) <= 20: n, err := strconv.ParseInt(v, 10, 64) if err == nil { return varint.EncInt64Ext(n), nil } t, ok := new(big.Int).SetString(v, 10) if !ok { return nil, fmt.Errorf("failed to marshal decimal: invalid unscaled string %s", v) } return varint.EncBigIntRS(t), nil default: t, ok := new(big.Int).SetString(v, 10) if !ok { return nil, fmt.Errorf("failed to marshal decimal: invalid unscaled string %s", v) } return varint.EncBigIntRS(t), nil } } ================================================ FILE: serialization/decimal/unmarshal.go ================================================ package decimal import ( "fmt" "reflect" "gopkg.in/inf.v0" ) func Unmarshal(data []byte, value any) error { switch v := value.(type) { case nil: return nil case *inf.Dec: return DecInfDec(data, v) case **inf.Dec: return DecInfDecR(data, v) case *string: return DecString(data, v) case **string: return DecStringR(data, v) default: // Custom types (type MyString string) can be deserialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.ValueOf(value) rt := rv.Type() if rt.Kind() != reflect.Ptr { return fmt.Errorf("failed to unmarshal decimal: unsupported value type (%T)(%#[1]v), supported types: ~string, inf.Dec", value) } if rt.Elem().Kind() != reflect.Ptr { return DecReflect(data, rv) } return DecReflectR(data, rv) } } ================================================ FILE: serialization/decimal/unmarshal_ints.go ================================================ package decimal import ( "gopkg.in/inf.v0" ) const ( neg8 = int64(-1) << 8 neg16 = int64(-1) << 16 neg24 = int64(-1) << 24 neg32 = int64(-1) << 32 neg40 = int64(-1) << 40 neg48 = int64(-1) << 48 neg56 = int64(-1) << 56 neg32Int = int(-1) << 32 ) func decScale(p []byte) inf.Scale { return inf.Scale(p[0])<<24 | inf.Scale(p[1])<<16 | inf.Scale(p[2])<<8 | inf.Scale(p[3]) } func decScaleInt64(p []byte) int64 { if p[0] > 127 { return neg32 | int64(p[0])<<24 | int64(p[1])<<16 | int64(p[2])<<8 | int64(p[3]) } return int64(p[0])<<24 | int64(p[1])<<16 | int64(p[2])<<8 | int64(p[3]) } func dec1toInt64(p []byte) int64 { if p[4] > 127 { return neg8 | int64(p[4]) } return int64(p[4]) } func dec2toInt64(p []byte) int64 { if p[4] > 127 { return neg16 | int64(p[4])<<8 | int64(p[5]) } return int64(p[4])<<8 | int64(p[5]) } func dec3toInt64(p []byte) int64 { if p[4] > 127 { return neg24 | int64(p[4])<<16 | int64(p[5])<<8 | int64(p[6]) } return int64(p[4])<<16 | int64(p[5])<<8 | int64(p[6]) } func dec4toInt64(p []byte) int64 { if p[4] > 127 { return neg32 | int64(p[4])<<24 | int64(p[5])<<16 | int64(p[6])<<8 | int64(p[7]) } return int64(p[4])<<24 | int64(p[5])<<16 | int64(p[6])<<8 | int64(p[7]) } func dec5toInt64(p []byte) int64 { if p[4] > 127 { return neg40 | int64(p[4])<<32 | int64(p[5])<<24 | int64(p[6])<<16 | int64(p[7])<<8 | int64(p[8]) } return int64(p[4])<<32 | int64(p[5])<<24 | int64(p[6])<<16 | int64(p[7])<<8 | int64(p[8]) } func dec6toInt64(p []byte) int64 { if p[4] > 127 { return neg48 | int64(p[4])<<40 | int64(p[5])<<32 | int64(p[6])<<24 | int64(p[7])<<16 | int64(p[8])<<8 | int64(p[9]) } return int64(p[4])<<40 | int64(p[5])<<32 | int64(p[6])<<24 | int64(p[7])<<16 | int64(p[8])<<8 | int64(p[9]) } func dec7toInt64(p []byte) int64 { if p[4] > 127 { return neg56 | int64(p[4])<<48 | int64(p[5])<<40 | int64(p[6])<<32 | int64(p[7])<<24 | int64(p[8])<<16 | int64(p[9])<<8 | int64(p[10]) } return int64(p[4])<<48 | int64(p[5])<<40 | int64(p[6])<<32 | int64(p[7])<<24 | int64(p[8])<<16 | int64(p[9])<<8 | int64(p[10]) } func dec8toInt64(p []byte) int64 { return int64(p[4])<<56 | int64(p[5])<<48 | int64(p[6])<<40 | int64(p[7])<<32 | int64(p[8])<<24 | int64(p[9])<<16 | int64(p[10])<<8 | int64(p[11]) } ================================================ FILE: serialization/decimal/unmarshal_utils.go ================================================ package decimal import ( "fmt" "reflect" "strconv" "gopkg.in/inf.v0" "github.com/gocql/gocql/serialization/varint" ) var errWrongDataLen = fmt.Errorf("failed to unmarshal decimal: the length of the data should be 0 or more than 5") func errBrokenData(p []byte) error { if p[4] == 0 && p[5] <= 127 || p[4] == 255 && p[5] > 127 { return fmt.Errorf("failed to unmarshal decimal: the data is broken") } return nil } func errNilReference(v any) error { return fmt.Errorf("failed to unmarshal decimal: can not unmarshal into nil reference(%T)(%[1]v)", v) } func DecInfDec(p []byte, v *inf.Dec) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: v.SetScale(0).SetUnscaled(0) return nil case 1, 2, 3, 4: return errWrongDataLen case 5: v.SetScale(decScale(p)).SetUnscaled(dec1toInt64(p)) return nil case 6: v.SetScale(decScale(p)).SetUnscaled(dec2toInt64(p)) case 7: v.SetScale(decScale(p)).SetUnscaled(dec3toInt64(p)) case 8: v.SetScale(decScale(p)).SetUnscaled(dec4toInt64(p)) case 9: v.SetScale(decScale(p)).SetUnscaled(dec5toInt64(p)) case 10: v.SetScale(decScale(p)).SetUnscaled(dec6toInt64(p)) case 11: v.SetScale(decScale(p)).SetUnscaled(dec7toInt64(p)) case 12: v.SetScale(decScale(p)).SetUnscaled(dec8toInt64(p)) default: v.SetScale(decScale(p)).SetUnscaledBig(varint.Dec2BigInt(p[4:])) } return errBrokenData(p) } func DecInfDecR(p []byte, v **inf.Dec) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = inf.NewDec(0, 0) } return nil case 1, 2, 3, 4: return errWrongDataLen case 5: *v = inf.NewDec(dec1toInt64(p), decScale(p)) return nil case 6: *v = inf.NewDec(dec2toInt64(p), decScale(p)) case 7: *v = inf.NewDec(dec3toInt64(p), decScale(p)) case 8: *v = inf.NewDec(dec4toInt64(p), decScale(p)) case 9: *v = inf.NewDec(dec5toInt64(p), decScale(p)) case 10: *v = inf.NewDec(dec6toInt64(p), decScale(p)) case 11: *v = inf.NewDec(dec7toInt64(p), decScale(p)) case 12: *v = inf.NewDec(dec8toInt64(p), decScale(p)) default: *v = inf.NewDecBig(varint.Dec2BigInt(p[4:]), decScale(p)) } return errBrokenData(p) } func DecString(p []byte, v *string) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = "" } else { *v = "0;0" } return nil case 1, 2, 3, 4: return errWrongDataLen case 5: *v = decString5(p) return nil case 6: *v = decString6(p) case 7: *v = decString7(p) case 8: *v = decString8(p) case 9: *v = decString9(p) case 10: *v = decString10(p) case 11: *v = decString11(p) case 12: *v = decString12(p) default: *v = decString(p) } return errBrokenData(p) } func DecStringR(p []byte, v **string) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { tmp := "0;0" *v = &tmp } return nil case 1, 2, 3, 4: return errWrongDataLen case 5: tmp := decString5(p) *v = &tmp return nil case 6: tmp := decString6(p) *v = &tmp case 7: tmp := decString7(p) *v = &tmp case 8: tmp := decString8(p) *v = &tmp case 9: tmp := decString9(p) *v = &tmp case 10: tmp := decString10(p) *v = &tmp case 11: tmp := decString11(p) *v = &tmp case 12: tmp := decString12(p) *v = &tmp default: tmp := decString(p) *v = &tmp } return errBrokenData(p) } func DecReflect(p []byte, v reflect.Value) error { if v.IsNil() { return fmt.Errorf("failed to unmarshal decimal: can not unmarshal into nil reference (%T)(%#[1]v)", v.Interface()) } switch v = v.Elem(); v.Kind() { case reflect.String: return decReflectString(p, v) default: return fmt.Errorf("failed to unmarshal decimal: unsupported value type (%T)(%#[1]v), supported types: ~string, inf.Dec", v.Interface()) } } func DecReflectR(p []byte, v reflect.Value) error { if v.IsNil() { return fmt.Errorf("failed to unmarshal decimal: can not unmarshal into nil reference (%T)(%[1]v)", v.Interface()) } switch v.Type().Elem().Elem().Kind() { case reflect.String: return decReflectStringR(p, v) default: return fmt.Errorf("failed to unmarshal decimal: unsupported value type (%T)(%[1]v), supported types: ~string, inf.Dec", v.Interface()) } } func decReflectString(p []byte, v reflect.Value) error { switch len(p) { case 0: if p == nil { v.SetString("") } else { v.SetString("0;0") } return nil case 1, 2, 3, 4: return errWrongDataLen case 5: v.SetString(decString5(p)) return nil case 6: v.SetString(decString6(p)) case 7: v.SetString(decString7(p)) case 8: v.SetString(decString8(p)) case 9: v.SetString(decString9(p)) case 10: v.SetString(decString10(p)) case 11: v.SetString(decString11(p)) case 12: v.SetString(decString12(p)) default: v.SetString(decString(p)) } return errBrokenData(p) } func decReflectStringR(p []byte, v reflect.Value) error { switch len(p) { case 0: var val reflect.Value if p == nil { val = reflect.Zero(v.Type().Elem()) } else { val = reflect.New(v.Type().Elem().Elem()) val.Elem().SetString("0;0") } v.Elem().Set(val) return nil case 1, 2, 3, 4: return errWrongDataLen case 5: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetString(decString5(p)) v.Elem().Set(newVal) return nil case 6: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetString(decString6(p)) v.Elem().Set(newVal) case 7: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetString(decString7(p)) v.Elem().Set(newVal) case 8: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetString(decString8(p)) v.Elem().Set(newVal) case 9: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetString(decString9(p)) v.Elem().Set(newVal) case 10: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetString(decString10(p)) v.Elem().Set(newVal) case 11: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetString(decString11(p)) v.Elem().Set(newVal) case 12: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetString(decString12(p)) v.Elem().Set(newVal) default: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetString(decString(p)) v.Elem().Set(newVal) } return errBrokenData(p) } func decString5(p []byte) string { return strconv.FormatInt(decScaleInt64(p), 10) + ";" + strconv.FormatInt(dec1toInt64(p), 10) } func decString6(p []byte) string { return strconv.FormatInt(decScaleInt64(p), 10) + ";" + strconv.FormatInt(dec2toInt64(p), 10) } func decString7(p []byte) string { return strconv.FormatInt(decScaleInt64(p), 10) + ";" + strconv.FormatInt(dec3toInt64(p), 10) } func decString8(p []byte) string { return strconv.FormatInt(decScaleInt64(p), 10) + ";" + strconv.FormatInt(dec4toInt64(p), 10) } func decString9(p []byte) string { return strconv.FormatInt(decScaleInt64(p), 10) + ";" + strconv.FormatInt(dec5toInt64(p), 10) } func decString10(p []byte) string { return strconv.FormatInt(decScaleInt64(p), 10) + ";" + strconv.FormatInt(dec6toInt64(p), 10) } func decString11(p []byte) string { return strconv.FormatInt(decScaleInt64(p), 10) + ";" + strconv.FormatInt(dec7toInt64(p), 10) } func decString12(p []byte) string { return strconv.FormatInt(decScaleInt64(p), 10) + ";" + strconv.FormatInt(dec8toInt64(p), 10) } func decString(p []byte) string { return strconv.FormatInt(decScaleInt64(p), 10) + ";" + varint.Dec2BigInt(p[4:]).String() } ================================================ FILE: serialization/double/marshal.go ================================================ package double import ( "reflect" ) func Marshal(value any) ([]byte, error) { switch v := value.(type) { case nil: return nil, nil case float64: return EncFloat64(v) case *float64: return EncFloat64R(v) default: // Custom types (type MyFloat float64) can be serialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.TypeOf(value) if rv.Kind() != reflect.Ptr { return EncReflect(reflect.ValueOf(v)) } return EncReflectR(reflect.ValueOf(v)) } } ================================================ FILE: serialization/double/marshal_utils.go ================================================ package double import ( "fmt" "reflect" "unsafe" ) func EncFloat64(v float64) ([]byte, error) { return encFloat64(v), nil } func EncFloat64R(v *float64) ([]byte, error) { if v == nil { return nil, nil } return encFloat64R(v), nil } func EncReflect(v reflect.Value) ([]byte, error) { switch v.Kind() { case reflect.Float64: return encFloat64(v.Float()), nil case reflect.Struct: if v.Type().String() == "gocql.unsetColumn" { return nil, nil } return nil, fmt.Errorf("failed to marshal double: unsupported value type (%T)(%[1]v), supported types: ~float64, unsetColumn", v.Interface()) default: return nil, fmt.Errorf("failed to marshal double: unsupported value type (%T)(%[1]v), supported types: ~float64, unsetColumn", v.Interface()) } } func EncReflectR(v reflect.Value) ([]byte, error) { if v.IsNil() { return nil, nil } return EncReflect(v.Elem()) } func encFloat64(v float64) []byte { return encUint64(floatToUint(v)) } func encFloat64R(v *float64) []byte { return encUint64(floatToUintR(v)) } func encUint64(v uint64) []byte { return []byte{byte(v >> 56), byte(v >> 48), byte(v >> 40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} } func floatToUint(v float64) uint64 { return *(*uint64)(unsafe.Pointer(&v)) } func floatToUintR(v *float64) uint64 { return *(*uint64)(unsafe.Pointer(v)) } ================================================ FILE: serialization/double/unmarshal.go ================================================ package double import ( "fmt" "reflect" ) func Unmarshal(data []byte, value any) error { switch v := value.(type) { case nil: return nil case *float64: return DecFloat64(data, v) case **float64: return DecFloat64R(data, v) default: // Custom types (type MyFloat float64) can be deserialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.ValueOf(value) rt := rv.Type() if rt.Kind() != reflect.Ptr { return fmt.Errorf("failed to unmarshal double: unsupported value type (%T)(%[1]v), supported types: ~float64", v) } if rt.Elem().Kind() != reflect.Ptr { return DecReflect(data, rv) } return DecReflectR(data, rv) } } ================================================ FILE: serialization/double/unmarshal_utils.go ================================================ package double import ( "fmt" "reflect" "unsafe" ) var errWrongDataLen = fmt.Errorf("failed to unmarshal double: the length of the data should be 0 or 8") func errNilReference(v any) error { return fmt.Errorf("failed to unmarshal double: can not unmarshal into nil reference(%T)(%[1]v)", v) } func DecFloat64(p []byte, v *float64) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 8: *v = decFloat64(p) default: return errWrongDataLen } return nil } func DecFloat64R(p []byte, v **float64) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(float64) } case 8: *v = decFloat64R(p) default: return errWrongDataLen } return nil } func DecReflect(p []byte, v reflect.Value) error { if v.IsNil() { return errNilReference(v) } switch v = v.Elem(); v.Kind() { case reflect.Float64: return decReflectFloat64(p, v) default: return fmt.Errorf("failed to unmarshal double: unsupported value type (%T)(%[1]v), supported types: ~float64", v.Interface()) } } func DecReflectR(p []byte, v reflect.Value) error { if v.IsNil() { return errNilReference(v) } switch v.Type().Elem().Elem().Kind() { case reflect.Float64: return decReflectFloat64R(p, v) default: return fmt.Errorf("failed to unmarshal double: unsupported value type (%T)(%[1]v), supported types: ~float64", v.Interface()) } } func decReflectFloat64(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetFloat(0) case 8: v.SetFloat(decFloat64(p)) default: return errWrongDataLen } return nil } func decReflectFloat64R(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) case 8: val := reflect.New(v.Type().Elem().Elem()) val.Elem().SetFloat(decFloat64(p)) v.Elem().Set(val) default: return errWrongDataLen } return nil } func decReflectNullableR(p []byte, v reflect.Value) reflect.Value { if p == nil { return reflect.Zero(v.Elem().Type()) } return reflect.New(v.Type().Elem().Elem()) } func decFloat64(p []byte) float64 { return uint64ToFloat(decUint64(p)) } func decFloat64R(p []byte) *float64 { return uint64ToFloatR(decUint64(p)) } func uint64ToFloat(v uint64) float64 { return *(*float64)(unsafe.Pointer(&v)) } func uint64ToFloatR(v uint64) *float64 { f := *(*float64)(unsafe.Pointer(&v)) return &f } func decUint64(p []byte) uint64 { return uint64(p[0])<<56 | uint64(p[1])<<48 | uint64(p[2])<<40 | uint64(p[3])<<32 | uint64(p[4])<<24 | uint64(p[5])<<16 | uint64(p[6])<<8 | uint64(p[7]) } ================================================ FILE: serialization/duration/duration.go ================================================ package duration type Duration struct { Months int32 Days int32 Nanoseconds int64 } func (d Duration) Valid() bool { return validDuration(d.Months, d.Days, d.Nanoseconds) } func validDuration(m, d int32, n int64) bool { if m >= 0 && d >= 0 && n >= 0 { return true } if m <= 0 && d <= 0 && n <= 0 { return true } return false } ================================================ FILE: serialization/duration/marshal.go ================================================ package duration import ( "reflect" "time" ) func Marshal(value any) ([]byte, error) { switch v := value.(type) { case nil: return nil, nil case int64: return EncInt64(v) case time.Duration: return EncDur(v) case string: return EncString(v) case Duration: return EncDuration(v) case *int64: return EncInt64R(v) case *time.Duration: return EncDurR(v) case *string: return EncStringR(v) case *Duration: return EncDurationR(v) default: // Custom types (type MyDate uint32) can be serialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.TypeOf(value) if rv.Kind() != reflect.Ptr { return EncReflect(reflect.ValueOf(v)) } return EncReflectR(reflect.ValueOf(v)) } } ================================================ FILE: serialization/duration/marshal_str.go ================================================ package duration import ( "errors" "fmt" ) const ( nanosecond = 1 microsecond = 1000 * nanosecond millisecond = 1000 * microsecond second = 1000 * millisecond minute = 60 * second hour = 60 * minute week = 7 year = 12 microsecondFloat = float64(microsecond) millisecondFloat = float64(millisecond) secondFloat = float64(second) minuteFloat = float64(minute) hourFloat = float64(hour) weekFloat = float64(week) yearFloat = float64(year) maxNanosecondsNeg = uint64(9223372036854775808) maxNanosecondsPos = maxNanosecondsNeg - 1 maxMonthsDaysNeg = uint64(2147483648) maxMonthsDaysPos = maxMonthsDaysNeg - 1 maxMicrosecondsNeg = maxNanosecondsNeg / microsecond maxMillisecondsNeg = maxMicrosecondsNeg / 1000 maxSecondsNeg = maxMillisecondsNeg / 1000 maxMinutesNeg = maxSecondsNeg / 60 maxHoursNeg = maxMinutesNeg / 60 maxWeeksNeg = maxNanosecondsNeg / 7 maxYearsNeg = maxNanosecondsNeg / 12 ) type parseReadState byte const ( readInteger parseReadState = iota readFraction readUnit readSkipFraction ) type parseWriteState byte const ( writeNanoseconds parseWriteState = iota writeMilliseconds writeMicroseconds writeSeconds writeMinutes writeHours writeDays writeWeeks writeMonths writeYears ) func errorOutRange(valName string, integer uint64) error { return fmt.Errorf("%s %d out of the range", valName, integer) } func errorUnknownChars(chars string) error { return fmt.Errorf("unknown charesters \"%s\"", chars) } func encString(s string) ([]byte, error) { months, days, nanos, neg, err := encStringToUints(s) if err != nil { return nil, err } if neg { return encVintMonthsDaysNanosNeg(months, days, nanos), nil } return encVintMonthsDaysNanosPos(months, days, nanos), nil } func encStringToUints(s string) (months uint64, days uint64, nanos uint64, neg bool, err error) { // Special case: if all that is left is "0", this is zero. if s == zeroDuration { return 0, 0, 0, false, nil } // get are sing if c := s[0]; c == '-' || c == '+' { neg = c == '-' s = s[1:] } var writeState parseWriteState readState := readInteger scale := float64(1) var integer, fraction uint64 var ok bool for i := 0; i < len(s); i++ { c := s[i] switch readState { case readInteger: // Consume [0-9.]* as integer part of value switch { case c >= '0' && c <= '9': integer = integer*10 + uint64(c) - '0' if integer > maxNanosecondsNeg { return 0, 0, 0, false, errorOutRange("value", integer) } case c == '.': readState = readFraction default: i-- readState = readUnit } case readFraction: // Consume [0-9]* as fraction part of value if c >= '0' && c <= '9' { scale *= 10 fraction = fraction*10 + uint64(c) - '0' if fraction > maxNanosecondsNeg { readState = readSkipFraction fraction /= 10 } } else { i-- readState = readUnit } case readUnit: // Consume unit part of the string, adding the integer and fraction parts of the value to the output values. // Supported units: // "ns" nanosecond, // "us" microsecond, // "µs" microsecond U+00B5 = micro symbol, // "μs" microsecond U+03BC = Greek letter mu // "ms" millisecond, // "s" second, // "m" minute, // "h" hour, // "d" day, // "w" week, // "mo" month, // "y" year, switch c { case 'n': // "ns" nanosecond if i+1 == len(s) || s[i+1] != 's' { return 0, 0, 0, false, errorUnknownChars(s[i:]) } i++ writeState = writeNanoseconds case 'u': // "us" microsecond if i+1 == len(s) || s[i+1] != 's' { return 0, 0, 0, false, errorUnknownChars(s[i:]) } i++ writeState = writeMicroseconds case 194: // "µs" microsecond U+00B5 = micro symbol if i+2 >= len(s) || s[i+1] != 181 || s[i+2] != 's' { return 0, 0, 0, false, errorUnknownChars(s[i:]) } i++ writeState = writeMicroseconds case 206: // "μs" microsecond U+03BC = Greek letter mu if i+2 >= len(s) || s[i+1] != 188 || s[i+2] != 's' { return 0, 0, 0, false, errorUnknownChars(s[i:]) } i++ writeState = writeMicroseconds case 'm': // "ms" millisecond,"mo" month,"m" minute, if i+1 == len(s) { // "m" minute writeState = writeMinutes } else { switch s[i+1] { // "ms" millisecond,"mo" month,"m" minute, case 's': // "ms" millisecond i++ writeState = writeMilliseconds case 'o': // "mo" month i++ writeState = writeMonths case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.': // "m" minute writeState = writeMinutes default: return 0, 0, 0, false, errorUnknownChars(s[i:]) } } case 's': // "s" second writeState = writeSeconds case 'h': // "h" hour writeState = writeHours case 'd': // "d" day writeState = writeDays case 'w': // "w" week writeState = writeWeeks case 'y': // "y" year writeState = writeYears default: // unsupported characters return 0, 0, 0, false, errorUnknownChars(s[i:]) } switch writeState { case writeNanoseconds: if nanos, ok = addNanoseconds(nanos, integer, fraction, scale); !ok { return 0, 0, 0, false, errorOutRange("nanoseconds", nanos) } case writeMicroseconds: if nanos, ok = addMicroseconds(nanos, integer, fraction, scale); !ok { return 0, 0, 0, false, errorOutRange("nanoseconds", nanos) } case writeMilliseconds: if nanos, ok = addMilliseconds(nanos, integer, fraction, scale); !ok { return 0, 0, 0, false, errorOutRange("nanoseconds", nanos) } case writeSeconds: if nanos, ok = addSeconds(nanos, integer, fraction, scale); !ok { return 0, 0, 0, false, errorOutRange("nanoseconds", nanos) } case writeMinutes: if nanos, ok = addMinutes(nanos, integer, fraction, scale); !ok { return 0, 0, 0, false, errorOutRange("nanoseconds", nanos) } case writeHours: if nanos, ok = addHours(nanos, integer, fraction, scale); !ok { return 0, 0, 0, false, errorOutRange("nanoseconds", nanos) } case writeDays: if days, ok = addDaysMonths(days, integer, fraction, scale); !ok { return 0, 0, 0, false, errorOutRange("days", days) } case writeWeeks: if days, ok = addWeeks(days, integer, fraction, scale); !ok { return 0, 0, 0, false, errorOutRange("days", days) } case writeMonths: if months, ok = addDaysMonths(months, integer, fraction, scale); !ok { return 0, 0, 0, false, errorOutRange("months", months) } default: // writeYears if months, ok = addYears(months, integer, fraction, scale); !ok { return 0, 0, 0, false, errorOutRange("years", months) } } // reset the temporary values, after write readState = readInteger integer, fraction, scale = 0, 0, 1 default: // Consume [0-9]* in case with overflow of the fraction part of value. Just skip digits. if c < '0' || c > '9' { i-- readState = readUnit } } } if integer != 0 || fraction != 0 || scale != 1 { // if the temporary values are not reset, it means that the reading is not fully completed. return 0, 0, 0, false, errors.New("unsupported format") } if !neg { if months > maxMonthsDaysPos { return 0, 0, 0, false, errorOutRange("months", months) } if days > maxMonthsDaysPos { return 0, 0, 0, false, errorOutRange("days", days) } if nanos > maxNanosecondsPos { return 0, 0, 0, false, errorOutRange("nanoseconds", days) } } return } func addNanoseconds(in, add, fraction uint64, scale float64) (uint64, bool) { if fraction > 0 { add += uint64(float64(fraction) * (float64(1) / scale)) if add > maxNanosecondsNeg { return add, false } } in += add if in > maxNanosecondsNeg { return in, false } return in, true } func addMicroseconds(in, add, fraction uint64, scale float64) (uint64, bool) { if add > maxMicrosecondsNeg { return add, false } add *= microsecond if fraction > 0 { add += uint64(float64(fraction) * (microsecondFloat / scale)) if add > maxNanosecondsNeg { return add, false } } in += add if in > maxNanosecondsNeg { return in, false } return in, true } func addMilliseconds(in, add, fraction uint64, scale float64) (uint64, bool) { if add > maxMillisecondsNeg { return add, false } add *= millisecond if fraction > 0 { add += uint64(float64(fraction) * (millisecondFloat / scale)) if add > maxNanosecondsNeg { return add, false } } in += add if in > maxNanosecondsNeg { return in, false } return in, true } func addSeconds(in, add, fraction uint64, scale float64) (uint64, bool) { if add > maxSecondsNeg { return add, false } add *= second if fraction > 0 { add += uint64(float64(fraction) * (secondFloat / scale)) if add > maxNanosecondsNeg { return add, false } } in += add if in > maxNanosecondsNeg { return in, false } return in, true } func addMinutes(in, add, fraction uint64, scale float64) (uint64, bool) { if add > maxMinutesNeg { return add, false } add *= minute if fraction > 0 { add += uint64(float64(fraction) * (minuteFloat / scale)) if add > maxNanosecondsNeg { return add, false } } in += add if in > maxNanosecondsNeg { return in, false } return in, true } func addHours(in, add, fraction uint64, scale float64) (uint64, bool) { if add > maxHoursNeg { return add, false } add *= hour if fraction > 0 { add += uint64(float64(fraction) * (hourFloat / scale)) if add > maxNanosecondsNeg { return add, false } } in += add if in > maxNanosecondsNeg { return in, false } return in, true } func addDaysMonths(in, add, fraction uint64, scale float64) (uint64, bool) { if fraction > 0 { add += uint64(float64(fraction) * (float64(1) / scale)) if add > maxMonthsDaysNeg { return add, false } } in += add if in > maxMonthsDaysNeg { return in, false } return in, true } func addWeeks(in, add, fraction uint64, scale float64) (uint64, bool) { if add > maxWeeksNeg { return add, false } add *= week if fraction > 0 { add += uint64(float64(fraction) * (weekFloat / scale)) if add > maxMonthsDaysNeg { return add, false } } in += add if in > maxMonthsDaysNeg { return in, false } return in, true } func addYears(in, add, fraction uint64, scale float64) (uint64, bool) { if add > maxYearsNeg { return add, false } add *= year if fraction > 0 { add += uint64(float64(fraction) * (yearFloat / scale)) if add > maxMonthsDaysNeg { return add, false } } in += add if in > maxMonthsDaysNeg { return in, false } return in, true } ================================================ FILE: serialization/duration/marshal_str_test.go ================================================ package duration import ( "math" "testing" ) func TestEncStr(t *testing.T) { for n := int64(math.MaxInt64); n != 1; n = n / 2 { m, d := int32(n), int32(n) if n > math.MaxInt32 { m, d = math.MaxInt32, math.MaxInt32 } testEncString(t, m, d, n) testEncString(t, 0, d, n) testEncString(t, m, 0, n) testEncString(t, m, d, 0) } for n := int64(math.MinInt64); n != -1; n = n / 2 { m, d := int32(n), int32(n) if n < math.MinInt32 { m, d = math.MinInt32, math.MinInt32 } testEncString(t, m, d, n) testEncString(t, 0, d, n) testEncString(t, m, 0, n) testEncString(t, m, d, 0) } } func testEncString(t *testing.T, m, d int32, n int64) { t.Helper() testStr := getTestString(m, d, n) mu, du, nu, neg, err := encStringToUints(testStr) if err != nil { t.Fatalf("failed on encoding testcase value:m:%d,d:%d,n:%d\ntest string:%s\nerror:%s", m, d, n, testStr, err) } me, de, ne := int32(mu), int32(du), int64(nu) if neg { me, de, ne = -me, -de, -ne } if me != m { t.Fatalf("testcase:%s\nexpected and recieved months not equal expected:%d received:%d", testStr, m, me) } if de != d { t.Fatalf("testcase:%s\nexpected and recieved days not equal expected:%d received:%d", testStr, d, de) } if ne != n { t.Fatalf("testcase:%s\nexpected and recieved nonoseconds not equal expected:%d received:%d", testStr, n, ne) } } ================================================ FILE: serialization/duration/marshal_utils.go ================================================ package duration import ( "fmt" "reflect" "time" ) const ( vintPrefix1 byte = 128 vintPrefix2 byte = 192 vintPrefix3 byte = 224 vintPrefix4 byte = 240 vintPrefix5 byte = 248 vintPrefix6 byte = 252 vintPrefix7 byte = 254 vintPrefix8 byte = 255 nanoDayPos = 24 * 60 * 60 * 1000 * 1000 * 1000 nanoDayNeg = -nanoDayPos ) func EncInt64(v int64) ([]byte, error) { return encInt64(v), nil } func EncInt64R(v *int64) ([]byte, error) { if v == nil { return nil, nil } return encInt64(*v), nil } func EncDur(v time.Duration) ([]byte, error) { return encDur(v), nil } func EncDurR(v *time.Duration) ([]byte, error) { if v == nil { return nil, nil } return encDur(*v), nil } func EncString(v string) ([]byte, error) { if v == "" { return nil, nil } data, err := encString(v) if err != nil { return nil, fmt.Errorf("failed to marshal duration: the parse error of the (string)(%s): %v", v, err) } return data, nil } func EncStringR(v *string) ([]byte, error) { if v == nil { return nil, nil } return EncString(*v) } func EncDuration(v Duration) ([]byte, error) { if !v.Valid() { return nil, fmt.Errorf("failed to marshal duration: the (Duration) values of months (%d), days (%d) and nanoseconds (%d) should have the same sign", v.Months, v.Days, v.Nanoseconds) } return encVintMonthsDaysNanos(v.Months, v.Days, v.Nanoseconds), nil } func EncDurationR(v *Duration) ([]byte, error) { if v == nil { return nil, nil } if !v.Valid() { return nil, fmt.Errorf("failed to marshal duration: the (*Duration) values of the months (%d), days (%d) and nanoseconds (%d) should have same sign", v.Months, v.Days, v.Nanoseconds) } return encVintMonthsDaysNanos(v.Months, v.Days, v.Nanoseconds), nil } func EncReflect(v reflect.Value) ([]byte, error) { switch v.Kind() { case reflect.Int64: return encInt64(v.Int()), nil case reflect.String: val := v.String() if val == "" { return nil, nil } data, err := encString(val) if err != nil { return nil, fmt.Errorf("failed to marshal duration: the (%T)(%[1]v) have invalid format, %v", v, err) } return data, nil case reflect.Struct: if v.Type().String() == "gocql.unsetColumn" { return nil, nil } return nil, fmt.Errorf("failed to marshal duration: unsupported value type (%T)(%[1]v), supported types: ~int64, ~string, time.Duration, gocql.Duration, unsetColumn", v.Interface()) default: return nil, fmt.Errorf("failed to marshal duration: unsupported value type (%T)(%[1]v), supported types: ~int64, ~string, time.Duration, gocql.Duration, unsetColumn", v.Interface()) } } func EncReflectR(v reflect.Value) ([]byte, error) { if v.IsNil() { return nil, nil } return EncReflect(v.Elem()) } func encDur(v time.Duration) []byte { if v < nanoDayPos && v > nanoDayNeg { return encNanos(encIntZigZagDur(v)) } n := v % nanoDayPos return encDaysNanos(encIntZigZag32(int32((v-n)/nanoDayPos)), encIntZigZagDur(n)) } func encInt64(v int64) []byte { if v < nanoDayPos && v > nanoDayNeg { return encNanos(encIntZigZag64(v)) } n := v % nanoDayPos return encDaysNanos(encIntZigZag32(int32((v-n)/nanoDayPos)), encIntZigZag64(n)) } func encZigZagUint64Pos(v uint64) uint64 { return v << 1 } func encIntZigZag32(v int32) uint32 { return uint32((v >> 31) ^ (v << 1)) } func encIntZigZag64(v int64) uint64 { return uint64((v >> 63) ^ (v << 1)) } func encIntZigZagDur(v time.Duration) uint64 { return uint64((v >> 63) ^ (v << 1)) } func encVint32(v uint32) []byte { switch { case byte(v>>28) != 0: return []byte{vintPrefix4, byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>21) != 0: return []byte{vintPrefix3 | byte(v>>24), byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>14) != 0: return []byte{vintPrefix2 | byte(v>>16), byte(v >> 8), byte(v)} case byte(v>>7) != 0: return []byte{vintPrefix1 | byte(v>>8), byte(v)} default: return []byte{byte(v)} } } func encVint64as32(v uint64) []byte { switch { case byte(v>>28) != 0: return []byte{vintPrefix4, byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>21) != 0: return []byte{vintPrefix3 | byte(v>>24), byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>14) != 0: return []byte{vintPrefix2 | byte(v>>16), byte(v >> 8), byte(v)} case byte(v>>7) != 0: return []byte{vintPrefix1 | byte(v>>8), byte(v)} default: return []byte{byte(v)} } } func encVint64(v uint64) []byte { switch { case byte(v>>56) != 0: return []byte{vintPrefix8, byte(v >> 56), byte(v >> 48), byte(v >> 40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>49) != 0: return []byte{vintPrefix7 | byte(v>>56), byte(v >> 48), byte(v >> 40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>42) != 0: return []byte{vintPrefix6 | byte(v>>48), byte(v >> 40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>35) != 0: return []byte{vintPrefix5 | byte(v>>40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>28) != 0: return []byte{vintPrefix4 | byte(v>>32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>21) != 0: return []byte{vintPrefix3 | byte(v>>24), byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>14) != 0: return []byte{vintPrefix2 | byte(v>>16), byte(v >> 8), byte(v)} case byte(v>>7) != 0: return []byte{vintPrefix1 | byte(v>>8), byte(v)} default: return []byte{byte(v)} } } func encVintMonthsDaysNanos(m, d int32, n int64) []byte { if m == 0 { if d == 0 { return encNanos(encIntZigZag64(n)) } return append(encDays(encIntZigZag32(d)), encVint64(encIntZigZag64(n))...) } return append(append(encVint32(encIntZigZag32(m)), encVint32(encIntZigZag32(d))...), encVint64(encIntZigZag64(n))...) } func encVintMonthsDaysNanosPos(m, d, n uint64) []byte { if m == 0 { if d == 0 { return encNanos(encZigZagUint64Pos(n)) } return append(encDays64(encZigZagUint64Pos(d)), encVint64(encZigZagUint64Pos(n))...) } return append(append(encVint64as32(encZigZagUint64Pos(m)), encVint64as32(encZigZagUint64Pos(d))...), encVint64(encZigZagUint64Pos(n))...) } func encVintMonthsDaysNanosNeg(m, d, n uint64) []byte { if m == 0 { if d == 0 { return encNanos(encIntZigZag64(int64(-n))) } return append(encDays(encIntZigZag32(int32(-d))), encVint64(encIntZigZag64(int64(-n)))...) } return append(append(encVint32(encIntZigZag32(int32(-m))), encVint32(encIntZigZag32(int32(-d)))...), encVint64(encIntZigZag64(int64(-n)))...) } func encDaysNanos(d uint32, n uint64) []byte { return append(encDays(d), encVint64(n)...) } func encDays(v uint32) []byte { switch { case byte(v>>28) != 0: return []byte{0, vintPrefix4, byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>21) != 0: return []byte{0, vintPrefix3 | byte(v>>24), byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>14) != 0: return []byte{0, vintPrefix2 | byte(v>>16), byte(v >> 8), byte(v)} case byte(v>>7) != 0: return []byte{0, vintPrefix1 | byte(v>>8), byte(v)} default: return []byte{0, byte(v)} } } func encDays64(v uint64) []byte { switch { case byte(v>>28) != 0: return []byte{0, vintPrefix4, byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>21) != 0: return []byte{0, vintPrefix3 | byte(v>>24), byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>14) != 0: return []byte{0, vintPrefix2 | byte(v>>16), byte(v >> 8), byte(v)} case byte(v>>7) != 0: return []byte{0, vintPrefix1 | byte(v>>8), byte(v)} default: return []byte{0, byte(v)} } } func encNanos(v uint64) []byte { switch { case byte(v>>56) != 0: return []byte{0, 0, vintPrefix8, byte(v >> 56), byte(v >> 48), byte(v >> 40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>49) != 0: return []byte{0, 0, vintPrefix7 | byte(v>>56), byte(v >> 48), byte(v >> 40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>42) != 0: return []byte{0, 0, vintPrefix6 | byte(v>>48), byte(v >> 40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>35) != 0: return []byte{0, 0, vintPrefix5 | byte(v>>40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>28) != 0: return []byte{0, 0, vintPrefix4 | byte(v>>32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>21) != 0: return []byte{0, 0, vintPrefix3 | byte(v>>24), byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>14) != 0: return []byte{0, 0, vintPrefix2 | byte(v>>16), byte(v >> 8), byte(v)} case byte(v>>7) != 0: return []byte{0, 0, vintPrefix1 | byte(v>>8), byte(v)} default: return []byte{0, 0, byte(v)} } } ================================================ FILE: serialization/duration/marshal_vint_test.go ================================================ package duration import ( "bytes" "math" "math/bits" "testing" ) func TestEncVint32(t *testing.T) { for i := int32(math.MaxInt32); i != 1; i = i / 2 { testEnc32(t, i) testEnc32(t, -i-1) } } func TestEncVint64(t *testing.T) { for i := int64(math.MaxInt64); i != 1; i = i / 2 { testEnc64(t, i) testEnc64(t, -i-1) } } func testEnc32(t *testing.T, v int32) { t.Helper() expected := genVintData(int64(v)) received := encVint32(encIntZigZag32(v)) if !bytes.Equal(expected, received) { t.Fatalf("expected and recieved data not equal\nvalue:%d\ndata expected:%b\ndata received:%b", v, expected, received) } } func testEnc64(t *testing.T, v int64) { t.Helper() expected := genVintData(v) received := encVint64(encIntZigZag64(v)) if !bytes.Equal(expected, received) { t.Fatalf("expected and recieved data not equal\nvalue:%d\ndata expected:%b\ndata received:%b", v, expected, received) } } func genVintData(v int64) []byte { vEnc := encIntZigZag64(v) lead0 := bits.LeadingZeros64(vEnc) numBytes := (639 - lead0*9) >> 6 // It can be 1 or 0 is v ==0 if numBytes <= 1 { return []byte{byte(vEnc)} } extraBytes := numBytes - 1 var buf = make([]byte, numBytes) for i := extraBytes; i >= 0; i-- { buf[i] = byte(vEnc) vEnc >>= 8 } buf[0] |= byte(^(0xff >> uint(extraBytes))) return buf } ================================================ FILE: serialization/duration/unmarshal.go ================================================ package duration import ( "fmt" "reflect" "time" ) func Unmarshal(data []byte, value any) error { switch v := value.(type) { case nil: return nil case *int64: return DecInt64(data, v) case *string: return DecString(data, v) case *time.Duration: return DecDur(data, v) case *Duration: return DecDuration(data, v) case **int64: return DecInt64R(data, v) case **string: return DecStringR(data, v) case **time.Duration: return DecDurR(data, v) case **Duration: return DecDurationR(data, v) default: // Custom types (type MyDate uint32) can be deserialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.ValueOf(value) rt := rv.Type() if rt.Kind() != reflect.Ptr { return fmt.Errorf("failed to unmarshal duration: unsupported value type (%T)(%[1]v), supported types: ~int64, ~string, time.Duration, gocql.Duration", value) } if rt.Elem().Kind() != reflect.Ptr { return DecReflect(data, rv) } return DecReflectR(data, rv) } } ================================================ FILE: serialization/duration/unmarshal_str.go ================================================ package duration func decString(m, d int32, n int64) string { var neg bool var tmp uint64 out := new([50]byte) // max string "-178956970y8mo306783378w2d2562047h47m16.854775808s" pos := 49 if m < 0 || d < 0 || n < 0 { neg = true } if n != 0 { tmp = uint64(n) if neg { tmp = -tmp } pos = printNanoseconds(out, tmp, pos) } if d != 0 { tmp = uint64(d) if neg { tmp = -tmp } pos = printDays(out, tmp, pos) } if m != 0 { tmp = uint64(m) if neg { tmp = -tmp } pos = printMonths(out, tmp, pos) } if pos == 49 { return zeroDuration } if neg { out[pos] = '-' pos-- } return string(out[pos+1:]) } func printMonths(out *[50]byte, m uint64, pos int) int { months := m % 12 if months == 0 { m-- months = 12 } out[pos] = 'o' pos-- out[pos] = 'm' pos-- pos = printInt(out, months, pos) if m > 12 { // print years out[pos] = 'y' pos-- pos = printInt(out, m/12, pos) } return pos } func printDays(out *[50]byte, d uint64, pos int) int { days := d % 7 if days == 0 { d-- days = 7 } out[pos] = 'd' // print days pos-- out[pos] = byte(days) + '0' pos-- if d > 7 { // print weeks out[pos] = 'w' pos-- pos = printInt(out, d/7, pos) } return pos } func printNanoseconds(out *[50]byte, n uint64, pos int) int { if n < second { // Special case: if nanoseconds is smaller than a second, // use smaller units, like 1.2ms dotPos := 0 out[pos] = 's' pos-- switch { case n < microsecond: // case for nanoseconds out[pos] = 'n' pos-- case n < millisecond: // case for microseconds copy(out[pos-1:], "µ") // U+00B5 'µ' micro sign == 0xC2 0xB5 pos -= 2 if n%microsecond == 0 { n /= microsecond } else { dotPos = 3 } default: // case for milliseconds out[pos] = 'm' pos-- if n%millisecond == 0 { n /= millisecond } else { dotPos = 6 } } if dotPos == 0 { return printInt(out, n, pos) } return printIntFrac(out, n, dotPos, pos) } if s := n % 60000000000; s != 0 { // case for seconds out[pos] = 's' pos-- pos = printIntFrac(out, s, 9, pos) } if n >= 60000000000 { n /= 60000000000 // n is now integer minutes if mn := n % 60; mn != 0 { // case for minutes out[pos] = 'm' pos-- pos = printInt(out, mn, pos) } if n >= 60 { out[pos] = 'h' pos-- pos = printInt(out, n/60, pos) // n is now integer hours } } return pos } func printIntFrac(out *[50]byte, n uint64, dotPos, pos int) int { start := false for i := 0; n > 0; i++ { digit := n % 10 if start && i == dotPos { out[pos] = '.' pos-- } start = start || digit != 0 || i == dotPos if start { out[pos] = byte(digit) + '0' pos-- } n /= 10 } return pos } func printInt(out *[50]byte, n uint64, pos int) int { switch { case n >= 100: for n > 0 { out[pos] = byte(n%10) + '0' n /= 10 pos-- } case n >= 10: out[pos] = byte(n%10) + '0' pos-- out[pos] = byte(n/10) + '0' pos-- default: out[pos] = byte(n) + '0' pos-- } return pos } ================================================ FILE: serialization/duration/unmarshal_str_test.go ================================================ package duration import ( "math" "strconv" "testing" "time" ) func TestDecStr(t *testing.T) { for n := int64(math.MaxInt64); n != 1; n = n / 2 { m, d := int32(n), int32(n) if n > math.MaxInt32 { m, d = math.MaxInt32, math.MaxInt32 } testDecString(t, m, d, n) testDecString(t, 0, d, n) testDecString(t, m, 0, n) testDecString(t, m, d, 0) } for n := int64(math.MinInt64); n != -1; n = n / 2 { m, d := int32(n), int32(n) if n < math.MinInt32 { m, d = math.MinInt32, math.MinInt32 } testDecString(t, m, d, n) testDecString(t, 0, d, n) testDecString(t, m, 0, n) testDecString(t, m, d, 0) } } func testDecString(t *testing.T, m, d int32, n int64) { t.Helper() expected := getTestString(m, d, n) received := decString(m, d, n) if expected != received { t.Fatalf("expected and recieved strings not equal\nvalue:m:%d,d:%d,n:%d\nexpected:%s\nreceived:%s", m, d, n, expected, received) } } func getTestString(m, d int32, n int64) string { out := "" if m < 0 || d < 0 || n < 0 { out += "-" } if m != 0 { out += getStringMonths(m) } if d != 0 { out += getStringDays(d) } if n != 0 { out += getStringNanos(n) } if out == "" { return zeroDuration } return out } func getStringMonths(m int32) string { out := "" mu := uint64(m) if m < 0 { mu = -mu } y := mu / 12 if mu = mu % 12; mu == 0 { y-- mu = 12 } if y != 0 { out += strconv.FormatUint(y, 10) + "y" } out += strconv.FormatUint(mu, 10) + "mo" return out } func getStringDays(d int32) string { out := "" du := uint64(d) if d < 0 { du = -du } w := du / 7 if du = du % 7; du == 0 { w-- du = 7 } if w != 0 { out += strconv.FormatUint(w, 10) + "w" } out += strconv.FormatUint(du, 10) + "d" return out } func getStringNanos(d int64) string { out := time.Duration(d).String() if d < 0 { out = out[1:] } return out } ================================================ FILE: serialization/duration/unmarshal_utils.go ================================================ package duration import ( "fmt" "math" "reflect" "time" ) const ( maxDays = (math.MaxInt64 - math.MaxInt64%nanoDayPos) / nanoDayPos minDays = -maxDays maxDaysNanos = maxDays * nanoDayPos minDaysNanos = minDays * nanoDayPos zeroDuration = "0s" ) var ( errWrongDataLen = fmt.Errorf("failed to unmarshal duration: the length of the data should be 0 or 3-19") errBrokenData = fmt.Errorf("failed to unmarshal duration: the data is broken") errInvalidSign = fmt.Errorf("failed to unmarshal duration: the data values of months, days and nanoseconds should have the same sign") ) func errNilReference(v any) error { return fmt.Errorf("failed to unmarshal duration: can not unmarshal into nil reference (%T)(%[1]v))", v) } func DecInt64(p []byte, v *int64) error { if v == nil { return errNilReference(v) } switch l := len(p); { case l == 0: *v = 0 case l < 3: return errWrongDataLen default: if p[0] != 0 { return fmt.Errorf("failed to unmarshal duration: to unmarshal into (int64) the months value should be 0") } if p[1] == 0 { var ok bool if *v, ok = decNanos64(p); !ok { return errBrokenData } } else { d, n, ok := decDaysNanos64(p) if !ok { return errBrokenData } if !validSignDateNanos(d, n) { return errInvalidSign } if *v, ok = daysToNanos(d, n); !ok { return fmt.Errorf("failed to unmarshal duration: to unmarshal into (int64) the data value should be in int64 range") } } } return nil } func DecInt64R(p []byte, v **int64) error { if v == nil { return errNilReference(v) } switch l := len(p); { case l == 0: if p == nil { *v = nil } else { *v = new(int64) } case l < 3: return errWrongDataLen default: if p[0] != 0 { return fmt.Errorf("failed to unmarshal duration: to unmarshal into (*int64) the months value should be 0") } if p[1] == 0 { n, ok := decNanos64(p) if !ok { return errBrokenData } *v = &n } else { d, n, ok := decDaysNanos64(p) if !ok { return errBrokenData } if !validSignDateNanos(d, n) { return errInvalidSign } if n, ok = daysToNanos(d, n); !ok { return fmt.Errorf("failed to unmarshal duration: to unmarshal into (*int64) the data value should be in int64 range") } *v = &n } } return nil } func DecString(p []byte, v *string) error { if v == nil { return errNilReference(v) } switch l := len(p); { case l == 0: if p == nil { *v = "" } else { *v = zeroDuration } case l < 3: return errWrongDataLen default: m, d, n, ok := decDuration(p) if !ok { return errBrokenData } if !validDuration(m, d, n) { return errInvalidSign } *v = decString(m, d, n) } return nil } func DecStringR(p []byte, v **string) error { if v == nil { return errNilReference(v) } switch l := len(p); { case l == 0: if p == nil { *v = nil } else { val := zeroDuration *v = &val } case l < 3: return errWrongDataLen default: var val string m, d, n, ok := decDuration(p) if !ok { return errBrokenData } if !validDuration(m, d, n) { return errInvalidSign } val = decString(m, d, n) *v = &val } return nil } func DecDur(p []byte, v *time.Duration) error { if v == nil { return errNilReference(v) } switch l := len(p); { case l == 0: *v = 0 case l < 3: return errWrongDataLen default: if p[0] != 0 { return fmt.Errorf("failed to unmarshal duration: to unmarshal into (time.Duration) the months value should be 0") } if p[1] == 0 { var ok bool if *v, ok = decNanosDur(p); !ok { return errBrokenData } } else { d, n, ok := decDaysNanosDur(p) if !ok { return errBrokenData } if !validDateNanosDur(d, n) { return errInvalidSign } if n, ok = daysToNanosDur(d, n); !ok { return fmt.Errorf("failed to unmarshal duration: to unmarshal into (time.Duration) the data value should be in int64 range") } *v = n } } return nil } func DecDurR(p []byte, v **time.Duration) error { if v == nil { return errNilReference(v) } switch l := len(p); { case l == 0: if p == nil { *v = nil } else { *v = new(time.Duration) } case l < 3: return errWrongDataLen default: if p[0] != 0 { return fmt.Errorf("failed to unmarshal duration: to unmarshal into (*time.Duration) the months value should be 0") } if p[1] == 0 { n, ok := decNanosDur(p) if !ok { return errBrokenData } *v = &n } else { d, n, ok := decDaysNanosDur(p) if !ok { return errBrokenData } if !validDateNanosDur(d, n) { return errInvalidSign } if n, ok = daysToNanosDur(d, n); !ok { return fmt.Errorf("failed to unmarshal duration: to unmarshal into (*time.Duration) the data value should be in int64 range") } *v = &n } } return nil } func DecDuration(p []byte, v *Duration) error { if v == nil { return errNilReference(v) } switch l := len(p); { case l == 0: *v = Duration{} case l < 3: return errWrongDataLen default: var ok bool v.Months, v.Days, v.Nanoseconds, ok = decVints(p) if !ok { return errBrokenData } if !v.Valid() { return errInvalidSign } } return nil } func DecDurationR(p []byte, v **Duration) error { if v == nil { return errNilReference(v) } switch l := len(p); { case l == 0: if p == nil { *v = nil } else { *v = new(Duration) } case l < 3: return errWrongDataLen default: var ok bool var val Duration val.Months, val.Days, val.Nanoseconds, ok = decVints(p) if !ok { return errBrokenData } if !val.Valid() { return errInvalidSign } *v = &val } return nil } func DecReflect(p []byte, v reflect.Value) error { if v.IsNil() { return fmt.Errorf("failed to unmarshal duration: can not unmarshal into nil reference (%T)(%[1]v))", v.Interface()) } switch v = v.Elem(); v.Kind() { case reflect.Int64: return decReflectInt64(p, v) case reflect.String: return decReflectString(p, v) default: return fmt.Errorf("failed to unmarshal duration: unsupported value type (%T)(%[1]v), supported types: ~int64, ~string, time.Duration, gocql.Duration", v.Interface()) } } func decReflectInt64(p []byte, v reflect.Value) error { switch l := len(p); { case l == 0: v.SetInt(0) case l < 3: return errWrongDataLen default: if p[0] != 0 { return fmt.Errorf("failed to unmarshal duration: to unmarshal into (%T) the months value should be 0", v.Interface()) } if p[1] == 0 { n, ok := decNanos64(p) if !ok { return errBrokenData } v.SetInt(n) } else { d, n, ok := decDaysNanos64(p) if !ok { return errBrokenData } if !validSignDateNanos(d, n) { return errInvalidSign } if n, ok = daysToNanos(d, n); !ok { return fmt.Errorf("failed to unmarshal duration: to unmarshal into (%T) the data value should be in int64 range", v.Interface()) } v.SetInt(n) } } return nil } func decReflectString(p []byte, v reflect.Value) error { switch l := len(p); { case l == 0: if p == nil { v.SetString("") } else { v.SetString(zeroDuration) } case l < 3: return errWrongDataLen default: m, d, n, ok := decDuration(p) if !ok { return errBrokenData } if !validDuration(m, d, n) { return errInvalidSign } v.SetString(decString(m, d, n)) } return nil } func DecReflectR(p []byte, v reflect.Value) error { if v.IsNil() { return fmt.Errorf("failed to unmarshal duration: can not unmarshal into nil reference (%T)(%[1]v)", v.Interface()) } switch v.Type().Elem().Elem().Kind() { case reflect.Int64: return decReflectInt64R(p, v) case reflect.String: return decReflectStringR(p, v) default: return fmt.Errorf("failed to unmarshal duration: unsupported value type (%T)(%[1]v), supported types: ~int64, ~string, time.Duration, gocql.Duration", v.Interface()) } } func decReflectInt64R(p []byte, v reflect.Value) error { switch l := len(p); { case l == 0: var val reflect.Value if p == nil { val = reflect.Zero(v.Type().Elem()) } else { val = reflect.New(v.Type().Elem().Elem()) } v.Elem().Set(val) case l < 3: return errWrongDataLen default: if p[0] != 0 { return fmt.Errorf("failed to unmarshal duration: to unmarshal into (%T) the months value should be 0", v.Interface()) } val := reflect.New(v.Type().Elem().Elem()) if p[1] == 0 { n, ok := decNanos64(p) if !ok { return errBrokenData } val.Elem().SetInt(n) } else { d, n, ok := decDaysNanos64(p) if !ok { return errBrokenData } if !validSignDateNanos(d, n) { return errInvalidSign } if n, ok = daysToNanos(d, n); !ok { return fmt.Errorf("failed to unmarshal duration: to unmarshal into (%T) the data value should be in int64 range", v.Interface()) } val.Elem().SetInt(n) } v.Elem().Set(val) } return nil } func decReflectStringR(p []byte, v reflect.Value) error { switch l := len(p); { case l == 0: var val reflect.Value if p == nil { val = reflect.Zero(v.Type().Elem()) } else { val = reflect.New(v.Type().Elem().Elem()) val.Elem().SetString(zeroDuration) } v.Elem().Set(val) case l < 3: return errWrongDataLen default: m, d, n, ok := decDuration(p) if !ok { return errBrokenData } if !validDuration(m, d, n) { return errInvalidSign } val := reflect.New(v.Type().Elem().Elem()) val.Elem().SetString(decString(m, d, n)) v.Elem().Set(val) } return nil } func validSignDateNanos(d int64, n int64) bool { if d >= 0 && n >= 0 { return true } if d <= 0 && n <= 0 { return true } return false } func daysToNanos(d int64, n int64) (int64, bool) { if d > maxDays || d < minDays { return 0, false } d *= nanoDayPos if (d > 0 && math.MaxInt64-d < n) || (d < 0 && math.MinInt64-d > n) { return 0, false } return n + d, true } func daysToNanosDur(d time.Duration, n time.Duration) (time.Duration, bool) { if d > maxDays || d < minDays { return 0, false } d *= nanoDayPos if (d > 0 && math.MaxInt64-d < n) || (d < 0 && math.MinInt64-d > n) { return 0, false } return n + d, true } func validDateNanosDur(d time.Duration, n time.Duration) bool { if d >= 0 && n >= 0 { return true } if d <= 0 && n <= 0 { return true } return false } func decDuration(p []byte) (m int32, d int32, n int64, ok bool) { if p[0] != 0 { m, d, n, ok = decVints(p) } else if p[1] != 0 { d, n, ok = decDaysNanos(p) } else { n, ok = decNanos64(p) } return } func decVints(p []byte) (int32, int32, int64, bool) { m, read := decVint32(p, 0) if read == 0 { return 0, 0, 0, false } d, read := decVint32(p, read) if read == 0 { return 0, 0, 0, false } n, read := decVint64(p, read) if read == 0 { return 0, 0, 0, false } return decZigZag32(m), decZigZag32(d), decZigZag64(n), true } func decDaysNanos(p []byte) (int32, int64, bool) { d, read := decVint32(p, 1) if read == 0 { return 0, 0, false } n, read := decVint64(p, read) if read == 0 { return 0, 0, false } return decZigZag32(d), decZigZag64(n), true } func decDaysNanos64(p []byte) (int64, int64, bool) { d, read := decVint3264(p, 1) if read == 0 { return 0, 0, false } n, read := decVint64(p, read) if read == 0 { return 0, 0, false } return decZigZag64(d), decZigZag64(n), true } func decNanos64(p []byte) (int64, bool) { n, read := decVint64(p, 2) if read == 0 { return 0, false } return decZigZag64(n), true } func decNanosDur(p []byte) (time.Duration, bool) { n, read := decVint64(p, 2) if read == 0 { return 0, false } return decZigZagDur(n), true } func decDaysNanosDur(p []byte) (time.Duration, time.Duration, bool) { d, read := decVint3264(p, 1) if read == 0 { return 0, 0, false } n, read := decVint64(p, read) if read == 0 { return 0, 0, false } return decZigZagDur(d), decZigZagDur(n), true } func decVint64(p []byte, s int) (uint64, int) { vintLen := decVintLen(p[s:]) if vintLen+s != len(p) { return 0, 0 } switch vintLen { case 9: return dec9Vint64(p[s:]), s + 9 case 8: return dec8Vint64(p[s:]), s + 8 case 7: return dec7Vint64(p[s:]), s + 7 case 6: return dec6Vint64(p[s:]), s + 6 case 5: return dec5Vint64(p[s:]), s + 5 case 4: return dec4Vint64(p[s:]), s + 4 case 3: return dec3Vint64(p[s:]), s + 3 case 2: return dec2Vint64(p[s:]), s + 2 case 1: return dec1Vint64(p[s:]), s + 1 case 0: return 0, s + 1 default: return 0, 0 } } func decVint32(p []byte, s int) (uint32, int) { vintLen := decVintLen(p[s:]) if vintLen+s >= len(p) { return 0, 0 } switch vintLen { case 5: if p[s] != vintPrefix4 { return 0, 0 } return dec5Vint32(p[s:]), s + 5 case 4: return dec4Vint32(p[s:]), s + 4 case 3: return dec3Vint32(p[s:]), s + 3 case 2: return dec2Vint32(p[s:]), s + 2 case 1: return dec1Vint32(p[s:]), s + 1 case 0: return 0, s + 1 default: return 0, 0 } } func decVint3264(p []byte, s int) (uint64, int) { vintLen := decVintLen(p[s:]) if vintLen+s >= len(p) { return 0, 0 } switch vintLen { case 5: if p[s] != vintPrefix4 { return 0, 0 } return dec5Vint64(p[s:]), s + 5 case 4: return dec4Vint64(p[s:]), s + 4 case 3: return dec3Vint64(p[s:]), s + 3 case 2: return dec2Vint64(p[s:]), s + 2 case 1: return dec1Vint64(p[s:]), s + 1 case 0: return 0, s + 1 default: return 0, 0 } } func decVintLen(p []byte) int { switch { case p[0] == 255: return 9 case p[0]>>1 == 127: return 8 case p[0]>>2 == 63: return 7 case p[0]>>3 == 31: return 6 case p[0]>>4 == 15: return 5 case p[0]>>5 == 7: return 4 case p[0]>>6 == 3: return 3 case p[0]>>7 == 1: return 2 default: return 1 } } func decZigZag32(n uint32) int32 { return int32((n >> 1) ^ -(n & 1)) } func decZigZag64(n uint64) int64 { return int64((n >> 1) ^ -(n & 1)) } func decZigZagDur(n uint64) time.Duration { return time.Duration((n >> 1) ^ -(n & 1)) } func dec5Vint32(p []byte) uint32 { return uint32(p[1])<<24 | uint32(p[2])<<16 | uint32(p[3])<<8 | uint32(p[4]) } func dec4Vint32(p []byte) uint32 { return uint32(p[0]&^vintPrefix3)<<24 | uint32(p[1])<<16 | uint32(p[2])<<8 | uint32(p[3]) } func dec3Vint32(p []byte) uint32 { return uint32(p[0]&^vintPrefix2)<<16 | uint32(p[1])<<8 | uint32(p[2]) } func dec2Vint32(p []byte) uint32 { return uint32(p[0]&^vintPrefix1)<<8 | uint32(p[1]) } func dec1Vint32(p []byte) uint32 { return uint32(p[0]) } func dec9Vint64(p []byte) uint64 { return uint64(p[1])<<56 | uint64(p[2])<<48 | uint64(p[3])<<40 | uint64(p[4])<<32 | uint64(p[5])<<24 | uint64(p[6])<<16 | uint64(p[7])<<8 | uint64(p[8]) } func dec8Vint64(p []byte) uint64 { return uint64(p[0]&^vintPrefix7)<<56 | uint64(p[1])<<48 | uint64(p[2])<<40 | uint64(p[3])<<32 | uint64(p[4])<<24 | uint64(p[5])<<16 | uint64(p[6])<<8 | uint64(p[7]) } func dec7Vint64(p []byte) uint64 { return uint64(p[0]&^vintPrefix6)<<48 | uint64(p[1])<<40 | uint64(p[2])<<32 | uint64(p[3])<<24 | uint64(p[4])<<16 | uint64(p[5])<<8 | uint64(p[6]) } func dec6Vint64(p []byte) uint64 { return uint64(p[0]&^vintPrefix5)<<40 | uint64(p[1])<<32 | uint64(p[2])<<24 | uint64(p[3])<<16 | uint64(p[4])<<8 | uint64(p[5]) } func dec5Vint64(p []byte) uint64 { return uint64(p[0]&^vintPrefix4)<<32 | uint64(p[1])<<24 | uint64(p[2])<<16 | uint64(p[3])<<8 | uint64(p[4]) } func dec4Vint64(p []byte) uint64 { return uint64(p[0]&^vintPrefix3)<<24 | uint64(p[1])<<16 | uint64(p[2])<<8 | uint64(p[3]) } func dec3Vint64(p []byte) uint64 { return uint64(p[0]&^vintPrefix2)<<16 | uint64(p[1])<<8 | uint64(p[2]) } func dec2Vint64(p []byte) uint64 { return uint64(p[0]&^vintPrefix1)<<8 | uint64(p[1]) } func dec1Vint64(p []byte) uint64 { return uint64(p[0]) } ================================================ FILE: serialization/duration/unmarshal_vint_test.go ================================================ package duration import ( "math" "testing" ) func TestDecVint32(t *testing.T) { for i := int32(math.MaxInt32); i != 1; i = i / 2 { testDec32(t, i) testDec32(t, -i-1) } } func TestDecVint64(t *testing.T) { for i := int64(math.MaxInt64); i != 1; i = i / 2 { testDec64(t, i) testDec64(t, -i-1) } } func testDec32(t *testing.T, expected int32) { t.Helper() // appending one byte is necessary because the `decVint32` function looks at the length of the data for the next vint len read. data := append(genVintData(int64(expected)), 0) vint, read := decVint32(data, 0) if read == 0 { t.Fatalf("decVint32 function can`t read vint data: value %d, data %b", expected, data) } received := decZigZag32(vint) if expected != received { t.Fatalf("\nexpected:%d\nreceived:%d\ndata:%b", expected, received, data) } } func testDec64(t *testing.T, expected int64) { t.Helper() data := genVintData(int64(expected)) vint, read := decVint64(data, 0) if read == 0 { t.Fatalf("decVint64 function can`t read vint data: value %d, data %b", expected, data) } received := decZigZag64(vint) if expected != received { t.Fatalf("\nexpected:%d\nreceived:%d\ndata:%b", expected, received, data) } } ================================================ FILE: serialization/float/marshal.go ================================================ package float import ( "reflect" ) func Marshal(value any) ([]byte, error) { switch v := value.(type) { case nil: return nil, nil case float32: return EncFloat32(v) case *float32: return EncFloat32R(v) default: // Custom types (type MyFloat float32) can be serialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.TypeOf(value) if rv.Kind() != reflect.Ptr { return EncReflect(reflect.ValueOf(v)) } return EncReflectR(reflect.ValueOf(v)) } } ================================================ FILE: serialization/float/marshal_utils.go ================================================ package float import ( "fmt" "reflect" "unsafe" ) func EncFloat32(v float32) ([]byte, error) { return encFloat32(v), nil } func EncFloat32R(v *float32) ([]byte, error) { if v == nil { return nil, nil } return encFloat32R(v), nil } func EncReflect(v reflect.Value) ([]byte, error) { switch v.Kind() { case reflect.Float32: return encFloat32(float32(v.Float())), nil case reflect.Struct: if v.Type().String() == "gocql.unsetColumn" { return nil, nil } return nil, fmt.Errorf("failed to marshal float: unsupported value type (%T)(%[1]v), supported types: ~float32, unsetColumn", v.Interface()) default: return nil, fmt.Errorf("failed to marshal float: unsupported value type (%T)(%[1]v), supported types: ~float32, unsetColumn", v.Interface()) } } func EncReflectR(v reflect.Value) ([]byte, error) { if v.IsNil() { return nil, nil } return EncReflect(v.Elem()) } func encFloat32(v float32) []byte { return encUint32(floatToUint(v)) } func encFloat32R(v *float32) []byte { return encUint32(floatToUintR(v)) } func encUint32(v uint32) []byte { return []byte{byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} } func floatToUint(v float32) uint32 { return *(*uint32)(unsafe.Pointer(&v)) } func floatToUintR(v *float32) uint32 { return *(*uint32)(unsafe.Pointer(v)) } ================================================ FILE: serialization/float/unmarshal.go ================================================ package float import ( "fmt" "reflect" ) func Unmarshal(data []byte, value any) error { switch v := value.(type) { case nil: return nil case *float32: return DecFloat32(data, v) case **float32: return DecFloat32R(data, v) default: // Custom types (type MyFloat float32) can be deserialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.ValueOf(value) rt := rv.Type() if rt.Kind() != reflect.Ptr { return fmt.Errorf("failed to unmarshal float: unsupported value type (%T)(%[1]v), supported types: ~float32", v) } if rt.Elem().Kind() != reflect.Ptr { return DecReflect(data, rv) } return DecReflectR(data, rv) } } ================================================ FILE: serialization/float/unmarshal_utils.go ================================================ package float import ( "fmt" "reflect" "unsafe" ) var errWrongDataLen = fmt.Errorf("failed to unmarshal float: the length of the data should be 0 or 4") func errNilReference(v any) error { return fmt.Errorf("failed to unmarshal float: can not unmarshal into nil reference(%T)(%[1]v)", v) } func DecFloat32(p []byte, v *float32) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 4: *v = decFloat32(p) default: return errWrongDataLen } return nil } func DecFloat32R(p []byte, v **float32) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(float32) } case 4: *v = decFloat32R(p) default: return errWrongDataLen } return nil } func DecReflect(p []byte, v reflect.Value) error { if v.IsNil() { return errNilReference(v) } switch v = v.Elem(); v.Kind() { case reflect.Float32: return decReflectFloat32(p, v) default: return fmt.Errorf("failed to unmarshal float: unsupported value type (%T)(%[1]v), supported types: ~float32", v.Interface()) } } func DecReflectR(p []byte, v reflect.Value) error { if v.IsNil() { return errNilReference(v) } switch v.Type().Elem().Elem().Kind() { case reflect.Float32: return decReflectFloat32R(p, v) default: return fmt.Errorf("failed to unmarshal float: unsupported value type (%T)(%[1]v), supported types: ~float32", v.Interface()) } } func decReflectFloat32(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetFloat(0) case 4: v.SetFloat(float64(decFloat32(p))) default: return errWrongDataLen } return nil } func decReflectFloat32R(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) case 4: val := reflect.New(v.Type().Elem().Elem()) val.Elem().SetFloat(float64(decFloat32(p))) v.Elem().Set(val) default: return errWrongDataLen } return nil } func decReflectNullableR(p []byte, v reflect.Value) reflect.Value { if p == nil { return reflect.Zero(v.Elem().Type()) } return reflect.New(v.Type().Elem().Elem()) } func decFloat32(p []byte) float32 { return uint32ToFloat(decUint32(p)) } func decFloat32R(p []byte) *float32 { return uint32ToFloatR(decUint32(p)) } func uint32ToFloat(v uint32) float32 { return *(*float32)(unsafe.Pointer(&v)) } func uint32ToFloatR(v uint32) *float32 { f := *(*float32)(unsafe.Pointer(&v)) return &f } func decUint32(p []byte) uint32 { return uint32(p[0])<<24 | uint32(p[1])<<16 | uint32(p[2])<<8 | uint32(p[3]) } ================================================ FILE: serialization/inet/marshal.go ================================================ package inet import ( "net" "reflect" ) func Marshal(value any) ([]byte, error) { switch v := value.(type) { case nil: return nil, nil case []byte: return EncBytes(v) case *[]byte: return EncBytesR(v) case net.IP: return EncNetIP(v) case *net.IP: return EncNetIPr(v) case [4]byte: return EncArray4(v) case *[4]byte: return EncArray4R(v) case [16]byte: return EncArray16(v) case *[16]byte: return EncArray16R(v) case string: return EncString(v) case *string: return EncStringR(v) default: // Custom types (type MyIP []byte) can be serialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.TypeOf(value) if rv.Kind() != reflect.Ptr { return EncReflect(reflect.ValueOf(v)) } return EncReflectR(reflect.ValueOf(v)) } } ================================================ FILE: serialization/inet/marshal_utils.go ================================================ package inet import ( "fmt" "net" "reflect" ) func EncBytes(v []byte) ([]byte, error) { switch len(v) { case 0: if v == nil { return nil, nil } return make([]byte, 0), nil case 4: tmp := make([]byte, 4) copy(tmp, v) return tmp, nil case 16: tmp := make([]byte, 16) copy(tmp, v) return tmp, nil default: return nil, fmt.Errorf("failed to marshal inet: the ([]byte) length can be 0,4,16") } } func EncBytesR(v *[]byte) ([]byte, error) { if v == nil { return nil, nil } return EncBytes(*v) } func EncNetIP(v net.IP) ([]byte, error) { switch len(v) { case 0: if v == nil { return nil, nil } return make([]byte, 0), nil case 4, 16: t := v.To4() if t == nil { return v.To16(), nil } return t, nil default: return nil, fmt.Errorf("failed to marshal inet: the (net.IP) length can be 0,4,16") } } func EncNetIPr(v *net.IP) ([]byte, error) { if v == nil { return nil, nil } return EncNetIP(*v) } func EncArray16(v [16]byte) ([]byte, error) { tmp := make([]byte, 16) copy(tmp, v[:]) return tmp, nil } func EncArray16R(v *[16]byte) ([]byte, error) { if v == nil { return nil, nil } return EncArray16(*v) } func EncArray4(v [4]byte) ([]byte, error) { tmp := make([]byte, 4) copy(tmp, v[:]) return tmp, nil } func EncArray4R(v *[4]byte) ([]byte, error) { if v == nil { return nil, nil } return EncArray4(*v) } func EncString(v string) ([]byte, error) { if len(v) == 0 { return nil, nil } b := net.ParseIP(v) if b != nil { t := b.To4() if t == nil { return b.To16(), nil } return t, nil } return nil, fmt.Errorf("failed to marshal inet: invalid IP string %s", v) } func EncStringR(v *string) ([]byte, error) { if v == nil { return nil, nil } return EncString(*v) } func EncReflect(v reflect.Value) ([]byte, error) { switch v.Kind() { case reflect.Array: if l := v.Len(); v.Type().Elem().Kind() != reflect.Uint8 || (l != 16 && l != 4) { return nil, fmt.Errorf("failed to marshal inet: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[4]byte, ~[16]byte, ~string, net.IP, unsetColumn", v.Interface()) } nv := reflect.New(v.Type()) nv.Elem().Set(v) return nv.Elem().Bytes(), nil case reflect.Slice: if v.Type().Elem().Kind() != reflect.Uint8 { return nil, fmt.Errorf("failed to marshal inet: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[4]byte, ~[16]byte, ~string, net.IP, unsetColumn", v.Interface()) } return encReflectBytes(v) case reflect.String: return encReflectString(v) case reflect.Struct: if v.Type().String() == "gocql.unsetColumn" { return nil, nil } return nil, fmt.Errorf("failed to marshal inet: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[4]byte, ~[16]byte, ~string, net.IP, unsetColumn", v.Interface()) default: return nil, fmt.Errorf("failed to marshal inet: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[4]byte, ~[16]byte, ~string, net.IP, unsetColumn", v.Interface()) } } func EncReflectR(v reflect.Value) ([]byte, error) { if v.IsNil() { return nil, nil } switch ev := v.Elem(); ev.Kind() { case reflect.Array: if l := v.Len(); ev.Type().Elem().Kind() != reflect.Uint8 || (l != 16 && l != 4) { return nil, fmt.Errorf("failed to marshal inet: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[4]byte, ~[16]byte, ~string, net.IP, unsetColumn", v.Interface()) } return v.Elem().Bytes(), nil case reflect.Slice: if ev.Type().Elem().Kind() != reflect.Uint8 { return nil, fmt.Errorf("failed to marshal inet: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[4]byte, ~[16]byte, ~string, net.IP, unsetColumn", v.Interface()) } return encReflectBytes(ev) case reflect.String: return encReflectString(ev) default: return nil, fmt.Errorf("failed to marshal inet: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[4]byte, ~[16]byte, ~string, net.IP, unsetColumn", v.Interface()) } } func encReflectString(v reflect.Value) ([]byte, error) { val := v.String() if len(val) == 0 { return nil, nil } b := net.ParseIP(val) if b != nil { t := b.To4() if t == nil { return b.To16(), nil } return t, nil } return nil, fmt.Errorf("failed to marshal inet: invalid IP string (%T)(%[1]v)", v.Interface()) } func encReflectBytes(v reflect.Value) ([]byte, error) { val := v.Bytes() switch len(val) { case 0: if val == nil { return nil, nil } return make([]byte, 0), nil case 4: tmp := make([]byte, 4) copy(tmp, val) return tmp, nil case 16: tmp := make([]byte, 16) copy(tmp, val) return tmp, nil default: return nil, fmt.Errorf("failed to marshal inet: the (%T) length can be 0,4,16", v.Interface()) } } ================================================ FILE: serialization/inet/unmarshal.go ================================================ package inet import ( "fmt" "net" "reflect" ) func Unmarshal(data []byte, value any) error { switch v := value.(type) { case nil: return nil case *[]byte: return DecBytes(data, v) case **[]byte: return DecBytesR(data, v) case *net.IP: return DecNetIP(data, v) case **net.IP: return DecNetIPr(data, v) case *[4]byte: return DecArray4(data, v) case **[4]byte: return DecArray4R(data, v) case *[16]byte: return DecArray16(data, v) case **[16]byte: return DecArray16R(data, v) case *string: return DecString(data, v) case **string: return DecStringR(data, v) default: // Custom types (type MyIP []byte) can be deserialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.ValueOf(value) rt := rv.Type() if rt.Kind() != reflect.Ptr { return fmt.Errorf("failed to unmarshal inet: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[4]byte, ~[16]byte, ~string, net.IP", v) } if rt.Elem().Kind() != reflect.Ptr { return DecReflect(data, rv) } return DecReflectR(data, rv) } } ================================================ FILE: serialization/inet/unmarshal_utils.go ================================================ package inet import ( "fmt" "net" "net/netip" "reflect" "unsafe" ) var ( errWrongDataLen = fmt.Errorf("failed to unmarshal inet: the length of the data can be 0,4,16") digits = getDigits() ) func errNilReference(v any) error { return fmt.Errorf("failed to unmarshal inet: can not unmarshal into nil reference(%T)(%[1]v)", v) } func DecBytes(p []byte, v *[]byte) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = make([]byte, 0) } case 4: *v = make([]byte, 4) copy(*v, p) case 16: *v = make([]byte, 16) copy(*v, p) default: return errWrongDataLen } return nil } func DecBytesR(p []byte, v **[]byte) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { tmp := make([]byte, 0) *v = &tmp } case 4: *v = &[]byte{0, 0, 0, 0} copy(**v, p) case 16: *v = &[]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} copy(**v, p) default: return errWrongDataLen } return nil } func DecNetIP(p []byte, v *net.IP) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = make(net.IP, 0) } case 4: *v = make(net.IP, 4) copy(*v, p) case 16: *v = make(net.IP, 16) copy(*v, p) if v4 := v.To4(); v4 != nil { *v = v4 } default: return errWrongDataLen } return nil } func DecNetIPr(p []byte, v **net.IP) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { tmp := make(net.IP, 0) *v = &tmp } case 4: *v = &net.IP{0, 0, 0, 0} copy(**v, p) case 16: *v = &net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} copy(**v, p) if v4 := (*v).To4(); v4 != nil { **v = v4 } default: return errWrongDataLen } return nil } func DecArray4(p []byte, v *[4]byte) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = [4]byte{} case 4: *v = [4]byte{} copy(v[:], p) case 16: if !isFist10Zeros(p) { return fmt.Errorf("failed to unmarshal inet: can not unmarshal ipV6 into [4]byte") } *v = [4]byte{} copy(v[:], p[12:16]) default: return errWrongDataLen } return nil } func DecArray4R(p []byte, v **[4]byte) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = &[4]byte{} } case 4: *v = &[4]byte{} copy((*v)[:], p) case 16: if !isFist10Zeros(p) { return fmt.Errorf("failed to unmarshal inet: can not unmarshal ipV6 into [4]byte") } *v = &[4]byte{} copy((*v)[:], p[12:16]) default: return errWrongDataLen } return nil } func DecArray16(p []byte, v *[16]byte) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = [16]byte{} case 4, 16: *v = [16]byte{} copy(v[:], p) default: return errWrongDataLen } return nil } func DecArray16R(p []byte, v **[16]byte) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = &[16]byte{} } case 4, 16: *v = &[16]byte{} copy((*v)[:], p) default: return errWrongDataLen } return nil } func DecString(p []byte, v *string) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = "" } else { *v = "0.0.0.0" } case 4: *v = decString4(p) case 16: *v = decString16(p) default: return errWrongDataLen } return nil } func DecStringR(p []byte, v **string) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { tmp := "0.0.0.0" *v = &tmp } case 4: tmp := decString4(p) *v = &tmp case 16: tmp := decString16(p) *v = &tmp default: return errWrongDataLen } return nil } func DecReflect(p []byte, v reflect.Value) error { if v.IsNil() { return errNilReference(v) } switch v = v.Elem(); v.Kind() { case reflect.Array: if v.Type().Elem().Kind() != reflect.Uint8 { return fmt.Errorf("failed to unmarshal inet: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[4]byte, ~[16]byte, ~string, net.IP", v.Interface()) } switch v.Len() { case 4: return decReflectArray4(p, v) case 16: return decReflectArray16(p, v) default: return fmt.Errorf("failed to unmarshal inet: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[4]byte, ~[16]byte, ~string, net.IP", v.Interface()) } case reflect.Slice: if v.Type().Elem().Kind() != reflect.Uint8 { return fmt.Errorf("failed to unmarshal inet: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[4]byte, ~[16]byte, ~string, net.IP", v.Interface()) } return decReflectBytes(p, v) case reflect.String: return decReflectString(p, v) default: return fmt.Errorf("failed to unmarshal inet: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[4]byte, ~[16]byte, ~string, net.IP", v.Interface()) } } func DecReflectR(p []byte, v reflect.Value) error { if v.IsNil() { return errNilReference(v) } ev := v.Elem() switch evt := ev.Type().Elem(); evt.Kind() { case reflect.Array: if evt.Elem().Kind() != reflect.Uint8 { return fmt.Errorf("failed to marshal inet: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[4]byte, ~[16]byte, ~string, net.IP", v.Interface()) } switch ev.Len() { case 4: return decReflectArray4R(p, ev) case 16: return decReflectArray16R(p, ev) default: return fmt.Errorf("failed to unmarshal inet: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[4]byte, ~[16]byte, ~string, net.IP", v.Interface()) } case reflect.Slice: if evt.Elem().Kind() != reflect.Uint8 { return fmt.Errorf("failed to marshal inet: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[4]byte, ~[16]byte, ~string, net.IP", v.Interface()) } return decReflectBytesR(p, ev) case reflect.String: return decReflectStringR(p, ev) default: return fmt.Errorf("failed to unmarshal inet: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[4]byte, ~[16]byte, ~string, net.IP", v.Interface()) } } func decReflectArray4(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetZero() case 4: val := reflect.New(v.Type()) copy((*[4]byte)(val.UnsafePointer())[:], p) v.Set(val.Elem()) case 16: if !isFist10Zeros(p) { return fmt.Errorf("failed to unmarshal inet: can not unmarshal ipV6 into (%T)", v.Interface()) } val := reflect.New(v.Type()) copy((*[4]byte)(val.UnsafePointer())[:], p[12:16]) v.Set(val.Elem()) default: return fmt.Errorf("failed to unmarshal inet: to unmarshal into (%T) the length of the data can be 0,4,16", v.Interface()) } return nil } func decReflectArray16(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetZero() case 4, 16: val := reflect.New(v.Type()) copy((*[16]byte)(val.UnsafePointer())[:], p) v.Set(val.Elem()) default: return fmt.Errorf("failed to unmarshal inet: to unmarshal into (%T) the length of the data can be 0,4,16", v.Interface()) } return nil } func decReflectBytes(p []byte, v reflect.Value) error { switch len(p) { case 0: if p == nil { v.SetBytes(nil) } else { v.SetBytes(make([]byte, 0)) } case 4: tmp := make([]byte, 4) copy(tmp, p) v.SetBytes(tmp) case 16: tmp := make([]byte, 16) copy(tmp, p) v.SetBytes(tmp) default: return errWrongDataLen } return nil } func decReflectString(p []byte, v reflect.Value) error { switch len(p) { case 0: if p == nil { v.SetString("") } else { v.SetString("0.0.0.0") } case 4: v.SetString(decString4(p)) case 16: v.SetString(decString16(p)) default: return errWrongDataLen } return nil } func decReflectArray4R(p []byte, v reflect.Value) error { switch len(p) { case 0: if p == nil { v.Set(reflect.Zero(v.Type())) } else { val := reflect.New(v.Type().Elem()) v.Set(val) } case 4: val := reflect.New(v.Type().Elem()) copy((*[4]byte)(val.UnsafePointer())[:], p) v.Set(val) case 16: if !isFist10Zeros(p) { return fmt.Errorf("failed to unmarshal inet: can not unmarshal ipV6 into (%T)", v.Interface()) } val := reflect.New(v.Type().Elem()) copy((*[4]byte)(val.UnsafePointer())[:], p[12:16]) v.Set(val) default: return fmt.Errorf("failed to unmarshal inet: to unmarshal into (%T) the length of the data can be 0,4,16", v.Interface()) } return nil } func decReflectArray16R(p []byte, v reflect.Value) error { switch len(p) { case 0: if p == nil { v.Set(reflect.Zero(v.Type())) } else { val := reflect.New(v.Type().Elem()) v.Set(val) } case 4, 16: val := reflect.New(v.Type().Elem()) copy((*[16]byte)(val.UnsafePointer())[:], p) v.Set(val) default: return fmt.Errorf("failed to unmarshal inet: to unmarshal into (%T) the length of the data can be 0,4,16", v.Interface()) } return nil } func decReflectBytesR(p []byte, v reflect.Value) error { switch len(p) { case 0: if p == nil { v.Set(reflect.Zero(v.Type())) } else { val := reflect.New(v.Type().Elem()) val.Elem().SetBytes(make([]byte, 0)) v.Set(val) } case 4: tmp := make([]byte, 4) copy(tmp, p) val := reflect.New(v.Type().Elem()) val.Elem().SetBytes(tmp) v.Set(val) case 16: tmp := make([]byte, 16) copy(tmp, p) val := reflect.New(v.Type().Elem()) val.Elem().SetBytes(tmp) v.Set(val) default: return errWrongDataLen } return nil } func decReflectStringR(p []byte, v reflect.Value) error { switch len(p) { case 0: if p == nil { v.Set(reflect.Zero(v.Type())) } else { val := reflect.New(v.Type().Elem()) val.Elem().SetString("0.0.0.0") v.Set(val) } case 4: val := reflect.New(v.Type().Elem()) val.Elem().SetString(decString4(p)) v.Set(val) case 16: val := reflect.New(v.Type().Elem()) val.Elem().SetString(decString16(p)) v.Set(val) default: return errWrongDataLen } return nil } func decString4(p []byte) string { out := make([]byte, 0, 15) for _, x := range p { out = append(out, digits[x]...) } return string(out[:len(out)-1]) } func decString16(p []byte) string { if isV4MappedToV6(p) { return decString4(p[12:16]) } return netip.AddrFrom16(*(*[16]byte)(unsafe.Pointer(&p[0]))).String() } func getDigits() []string { out := make([]string, 256) for i := range out { out[i] = fmt.Sprintf("%d.", i) } return out } func isV4MappedToV6(p []byte) bool { return p[0] == 0 && p[1] == 0 && p[2] == 0 && p[3] == 0 && p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] == 0 && p[8] == 0 && p[9] == 0 && p[10] == 255 && p[11] == 255 } func isFist10Zeros(p []byte) bool { return p[0] == 0 && p[1] == 0 && p[2] == 0 && p[3] == 0 && p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] == 0 && p[8] == 0 && p[9] == 0 } ================================================ FILE: serialization/smallint/marshal.go ================================================ package smallint import ( "math/big" "reflect" ) func Marshal(value any) ([]byte, error) { switch v := value.(type) { case nil: return nil, nil case int8: return EncInt8(v) case int32: return EncInt32(v) case int16: return EncInt16(v) case int64: return EncInt64(v) case int: return EncInt(v) case uint8: return EncUint8(v) case uint16: return EncUint16(v) case uint32: return EncUint32(v) case uint64: return EncUint64(v) case uint: return EncUint(v) case big.Int: return EncBigInt(v) case string: return EncString(v) case *int8: return EncInt8R(v) case *int16: return EncInt16R(v) case *int32: return EncInt32R(v) case *int64: return EncInt64R(v) case *int: return EncIntR(v) case *uint8: return EncUint8R(v) case *uint16: return EncUint16R(v) case *uint32: return EncUint32R(v) case *uint64: return EncUint64R(v) case *uint: return EncUintR(v) case *big.Int: return EncBigIntR(v) case *string: return EncStringR(v) default: // Custom types (type MyInt int) can be serialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.TypeOf(value) if rv.Kind() != reflect.Ptr { return EncReflect(reflect.ValueOf(v)) } return EncReflectR(reflect.ValueOf(v)) } } ================================================ FILE: serialization/smallint/marshal_utils.go ================================================ package smallint import ( "fmt" "math" "math/big" "reflect" "strconv" ) const supportedTypes = "~int8, ~int16, ~int32, ~int64, ~int, ~uint8, ~uint16, ~uint32, ~uint64, ~uint, ~string, big.Int" var ( maxBigInt = big.NewInt(math.MaxInt16) minBigInt = big.NewInt(math.MinInt16) ) func EncInt8(v int8) ([]byte, error) { if v < 0 { return []byte{255, byte(v)}, nil } return []byte{0, byte(v)}, nil } func EncInt8R(v *int8) ([]byte, error) { if v == nil { return nil, nil } return EncInt8(*v) } func EncInt16(v int16) ([]byte, error) { return encInt16(v), nil } func EncInt16R(v *int16) ([]byte, error) { if v == nil { return nil, nil } return EncInt16(*v) } func EncInt32(v int32) ([]byte, error) { if v > math.MaxInt16 || v < math.MinInt16 { return nil, fmt.Errorf("failed to marshal smallint: value %#v out of range", v) } return []byte{byte(v >> 8), byte(v)}, nil } func EncInt32R(v *int32) ([]byte, error) { if v == nil { return nil, nil } return EncInt32(*v) } func EncInt64(v int64) ([]byte, error) { if v > math.MaxInt16 || v < math.MinInt16 { return nil, fmt.Errorf("failed to marshal smallint: value %#v out of range", v) } return encInt64(v), nil } func EncInt64R(v *int64) ([]byte, error) { if v == nil { return nil, nil } return EncInt64(*v) } func EncInt(v int) ([]byte, error) { if v > math.MaxInt16 || v < math.MinInt16 { return nil, fmt.Errorf("failed to marshal smallint: value %#v out of range", v) } return []byte{byte(v >> 8), byte(v)}, nil } func EncIntR(v *int) ([]byte, error) { if v == nil { return nil, nil } return EncInt(*v) } func EncUint8(v uint8) ([]byte, error) { return []byte{0, v}, nil } func EncUint8R(v *uint8) ([]byte, error) { if v == nil { return nil, nil } return EncUint8(*v) } func EncUint16(v uint16) ([]byte, error) { return []byte{byte(v >> 8), byte(v)}, nil } func EncUint16R(v *uint16) ([]byte, error) { if v == nil { return nil, nil } return EncUint16(*v) } func EncUint32(v uint32) ([]byte, error) { if v > math.MaxUint16 { return nil, fmt.Errorf("failed to marshal smallint: value %#v out of range", v) } return []byte{byte(v >> 8), byte(v)}, nil } func EncUint32R(v *uint32) ([]byte, error) { if v == nil { return nil, nil } return EncUint32(*v) } func EncUint64(v uint64) ([]byte, error) { if v > math.MaxUint16 { return nil, fmt.Errorf("failed to marshal smallint: value %#v out of range", v) } return encUint64(v), nil } func EncUint64R(v *uint64) ([]byte, error) { if v == nil { return nil, nil } return EncUint64(*v) } func EncUint(v uint) ([]byte, error) { if v > math.MaxUint16 { return nil, fmt.Errorf("failed to marshal smallint: value %#v out of range", v) } return []byte{byte(v >> 8), byte(v)}, nil } func EncUintR(v *uint) ([]byte, error) { if v == nil { return nil, nil } return EncUint(*v) } func EncBigInt(v big.Int) ([]byte, error) { if v.Cmp(maxBigInt) == 1 || v.Cmp(minBigInt) == -1 { return nil, fmt.Errorf("failed to marshal smallint: value (%T)(%s) out of range", v, v.String()) } return encInt64(v.Int64()), nil } func EncBigIntR(v *big.Int) ([]byte, error) { if v == nil { return nil, nil } if v.Cmp(maxBigInt) == 1 || v.Cmp(minBigInt) == -1 { return nil, fmt.Errorf("failed to marshal smallint: value (%T)(%s) out of range", v, v.String()) } return encInt64(v.Int64()), nil } func EncString(v string) ([]byte, error) { if v == "" { return nil, nil } n, err := strconv.ParseInt(v, 10, 16) if err != nil { return nil, fmt.Errorf("failed to marshal smallint: can not marshal (%T)(%[1]v) %s", v, err) } return encInt64(n), nil } func EncStringR(v *string) ([]byte, error) { if v == nil { return nil, nil } return EncString(*v) } func EncReflect(v reflect.Value) ([]byte, error) { switch v.Type().Kind() { case reflect.Int8: val := v.Int() if val < 0 { return []byte{255, byte(val)}, nil } return []byte{0, byte(val)}, nil case reflect.Int16: return encInt64(v.Int()), nil case reflect.Int, reflect.Int64, reflect.Int32: val := v.Int() if val > math.MaxInt16 || val < math.MinInt16 { return nil, fmt.Errorf("failed to marshal smallint: custom type value (%T)(%[1]v) out of range", v.Interface()) } return encInt64(val), nil case reflect.Uint8: return []byte{0, byte(v.Uint())}, nil case reflect.Uint16: return encUint64(v.Uint()), nil case reflect.Uint, reflect.Uint64, reflect.Uint32: val := v.Uint() if val > math.MaxUint16 { return nil, fmt.Errorf("failed to marshal smallint: custom type value (%T)(%[1]v) out of range", v.Interface()) } return encUint64(val), nil case reflect.String: val := v.String() if val == "" { return nil, nil } n, err := strconv.ParseInt(val, 10, 16) if err != nil { return nil, fmt.Errorf("failed to marshal smallint: can not marshal (%T)(%[1]v), %s", v.Interface(), err) } return encInt64(n), nil case reflect.Struct: if v.Type().String() == "gocql.unsetColumn" { return nil, nil } return nil, fmt.Errorf("failed to marshal smallint: unsupported value type (%T)(%[1]v), supported types: %s, unsetColumn", v.Interface(), supportedTypes) default: return nil, fmt.Errorf("failed to marshal smallint: unsupported value type (%T)(%[1]v), supported types: %s, unsetColumn", v.Interface(), supportedTypes) } } func EncReflectR(v reflect.Value) ([]byte, error) { if v.IsNil() { return nil, nil } return EncReflect(v.Elem()) } func encInt16(v int16) []byte { return []byte{byte(v >> 8), byte(v)} } func encInt64(v int64) []byte { return []byte{byte(v >> 8), byte(v)} } func encUint64(v uint64) []byte { return []byte{byte(v >> 8), byte(v)} } ================================================ FILE: serialization/smallint/unmarshal.go ================================================ package smallint import ( "fmt" "math/big" "reflect" ) func Unmarshal(data []byte, value any) error { switch v := value.(type) { case nil: return nil case *int8: return DecInt8(data, v) case *int16: return DecInt16(data, v) case *int32: return DecInt32(data, v) case *int64: return DecInt64(data, v) case *int: return DecInt(data, v) case *uint8: return DecUint8(data, v) case *uint16: return DecUint16(data, v) case *uint32: return DecUint32(data, v) case *uint64: return DecUint64(data, v) case *uint: return DecUint(data, v) case *big.Int: return DecBigInt(data, v) case *string: return DecString(data, v) case **int8: return DecInt8R(data, v) case **int16: return DecInt16R(data, v) case **int32: return DecInt32R(data, v) case **int64: return DecInt64R(data, v) case **int: return DecIntR(data, v) case **uint8: return DecUint8R(data, v) case **uint16: return DecUint16R(data, v) case **uint32: return DecUint32R(data, v) case **uint64: return DecUint64R(data, v) case **uint: return DecUintR(data, v) case **big.Int: return DecBigIntR(data, v) case **string: return DecStringR(data, v) default: // Custom types (type MyInt int) can be deserialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.ValueOf(value) rt := rv.Type() if rt.Kind() != reflect.Ptr { return fmt.Errorf("failed to unmarshal smallint: unsupported value type (%T)(%[1]v)", value) } if rt.Elem().Kind() != reflect.Ptr { return DecReflect(data, rv) } return DecReflectR(data, rv) } } ================================================ FILE: serialization/smallint/unmarshal_utils.go ================================================ package smallint import ( "fmt" "math" "math/big" "reflect" "strconv" ) const ( negInt32 = int32(-1) << 16 negInt64 = int64(-1) << 16 negInt = int(-1) << 16 ) var errWrongDataLen = fmt.Errorf("failed to unmarshal smallint: the length of the data should be 0 or 2") func errNilReference(v any) error { return fmt.Errorf("failed to unmarshal smallint: can not unmarshal into nil reference (%T)(%[1]v))", v) } func DecInt8(p []byte, v *int8) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 2: val := decInt16(p) if val > math.MaxInt8 || val < math.MinInt8 { return fmt.Errorf("failed to unmarshal smallint: to unmarshal into int8, the data should be in the int8 range") } *v = int8(val) default: return errWrongDataLen } return nil } func DecInt8R(p []byte, v **int8) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(int8) } case 2: val := decInt16(p) if val > math.MaxInt8 || val < math.MinInt8 { return fmt.Errorf("failed to unmarshal smallint: to unmarshal into int8, the data should be in the int8 range") } tmp := int8(val) *v = &tmp default: return errWrongDataLen } return nil } func DecInt16(p []byte, v *int16) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 2: *v = decInt16(p) default: return errWrongDataLen } return nil } func DecInt16R(p []byte, v **int16) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(int16) } case 2: val := decInt16(p) *v = &val default: return errWrongDataLen } return nil } func DecInt32(p []byte, v *int32) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 2: *v = decInt32(p) default: return errWrongDataLen } return nil } func DecInt32R(p []byte, v **int32) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(int32) } case 2: val := decInt32(p) *v = &val default: return errWrongDataLen } return nil } func DecInt64(p []byte, v *int64) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 2: *v = decInt64(p) default: return errWrongDataLen } return nil } func DecInt64R(p []byte, v **int64) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(int64) } case 2: val := decInt64(p) *v = &val default: return errWrongDataLen } return nil } func DecInt(p []byte, v *int) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 2: *v = decInt(p) default: return errWrongDataLen } return nil } func DecIntR(p []byte, v **int) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(int) } case 2: val := decInt(p) *v = &val default: return errWrongDataLen } return nil } func DecUint8(p []byte, v *uint8) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 2: if p[0] != 0 { return fmt.Errorf("failed to unmarshal smallint: to unmarshal into uint8, the data should be in the uint8 range") } *v = p[1] default: return errWrongDataLen } return nil } func DecUint8R(p []byte, v **uint8) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(uint8) } case 2: if p[0] != 0 { return fmt.Errorf("failed to unmarshal smallint: to unmarshal into uint8, the data should be in the uint8 range") } val := p[1] *v = &val default: return errWrongDataLen } return nil } func DecUint16(p []byte, v *uint16) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 2: *v = decUint16(p) default: return errWrongDataLen } return nil } func DecUint16R(p []byte, v **uint16) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(uint16) } case 2: val := decUint16(p) *v = &val default: return errWrongDataLen } return nil } func DecUint32(p []byte, v *uint32) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 2: *v = decUint32(p) default: return errWrongDataLen } return nil } func DecUint32R(p []byte, v **uint32) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(uint32) } case 2: val := decUint32(p) *v = &val default: return errWrongDataLen } return nil } func DecUint64(p []byte, v *uint64) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 2: *v = decUint64(p) default: return errWrongDataLen } return nil } func DecUint64R(p []byte, v **uint64) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(uint64) } case 2: val := decUint64(p) *v = &val default: return errWrongDataLen } return nil } func DecUint(p []byte, v *uint) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 2: *v = decUint(p) default: return errWrongDataLen } return nil } func DecUintR(p []byte, v **uint) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(uint) } case 2: val := decUint(p) *v = &val default: return errWrongDataLen } return nil } func DecString(p []byte, v *string) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = "" } else { *v = "0" } case 2: *v = strconv.FormatInt(decInt64(p), 10) default: return errWrongDataLen } return nil } func DecStringR(p []byte, v **string) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { val := "0" *v = &val } case 2: val := strconv.FormatInt(decInt64(p), 10) *v = &val default: return errWrongDataLen } return nil } func DecBigInt(p []byte, v *big.Int) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: v.SetInt64(0) case 2: v.SetInt64(decInt64(p)) default: return errWrongDataLen } return nil } func DecBigIntR(p []byte, v **big.Int) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = big.NewInt(0) } case 2: *v = big.NewInt(decInt64(p)) default: return errWrongDataLen } return nil } func DecReflect(p []byte, v reflect.Value) error { if v.IsNil() { return fmt.Errorf("failed to unmarshal smallint: can not unmarshal into nil reference (%T)(%[1]v)", v.Interface()) } switch v = v.Elem(); v.Kind() { case reflect.Int8: return decReflectInt8(p, v) case reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: return decReflectInts(p, v) case reflect.Uint8: return decReflectUint8(p, v) case reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: return decReflectUints(p, v) case reflect.String: return decReflectString(p, v) default: return fmt.Errorf("failed to unmarshal smallint: unsupported value type (%T)(%[1]v), supported types: %s", v.Interface(), supportedTypes) } } func DecReflectR(p []byte, v reflect.Value) error { if v.IsNil() { return fmt.Errorf("failed to unmarshal smallint: can not unmarshal into nil reference (%T)(%[1]v)", v.Interface()) } switch v.Type().Elem().Elem().Kind() { case reflect.Int8: return decReflectInt8R(p, v) case reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: return decReflectIntsR(p, v) case reflect.Uint8: return decReflectUint8R(p, v) case reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: return decReflectUintsR(p, v) case reflect.String: return decReflectStringR(p, v) default: return fmt.Errorf("failed to unmarshal smallint: unsupported value type (%T)(%[1]v), supported types: %s", v.Interface(), supportedTypes) } } func decReflectInt8(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetInt(0) case 2: val := decInt64(p) if val > math.MaxInt8 || val < math.MinInt8 { return fmt.Errorf("failed to unmarshal smallint: to unmarshal into (%T), the data should be in the int8 range", v.Interface()) } v.SetInt(val) default: return errWrongDataLen } return nil } func decReflectInts(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetInt(0) case 2: v.SetInt(decInt64(p)) default: return errWrongDataLen } return nil } func decReflectUint8(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetUint(0) case 2: if p[0] != 0 { return fmt.Errorf("failed to unmarshal smallint: to unmarshal into (%T), the data should be in the uint8 range", v.Interface()) } v.SetUint(uint64(p[1])) default: return errWrongDataLen } return nil } func decReflectUints(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetUint(0) case 2: v.SetUint(decUint64(p)) default: return errWrongDataLen } return nil } func decReflectString(p []byte, v reflect.Value) error { switch len(p) { case 0: if p != nil { v.SetString("0") } else { v.SetString("") } case 2: v.SetString(strconv.FormatInt(decInt64(p), 10)) default: return errWrongDataLen } return nil } func decReflectNullableR(p []byte, v reflect.Value) reflect.Value { if p == nil { return reflect.Zero(v.Elem().Type()) } return reflect.New(v.Type().Elem().Elem()) } func decReflectInt8R(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) case 2: val := decInt64(p) if val > math.MaxInt8 || val < math.MinInt8 { return fmt.Errorf("failed to unmarshal smallint: to unmarshal into (%T), the data should be in the int8 range", v.Interface()) } newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetInt(val) v.Elem().Set(newVal) default: return errWrongDataLen } return nil } func decReflectIntsR(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) case 2: val := reflect.New(v.Type().Elem().Elem()) val.Elem().SetInt(decInt64(p)) v.Elem().Set(val) default: return errWrongDataLen } return nil } func decReflectUint8R(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) case 2: if p[0] != 0 { return fmt.Errorf("failed to unmarshal smallint: to unmarshal into (%T), the data should be in the uint8 range", v.Interface()) } newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetUint(uint64(p[1])) v.Elem().Set(newVal) default: return errWrongDataLen } return nil } func decReflectUintsR(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) case 2: val := reflect.New(v.Type().Elem().Elem()) val.Elem().SetUint(decUint64(p)) v.Elem().Set(val) default: return errWrongDataLen } return nil } func decReflectStringR(p []byte, v reflect.Value) error { switch len(p) { case 0: var val reflect.Value if p == nil { val = reflect.Zero(v.Type().Elem()) } else { val = reflect.New(v.Type().Elem().Elem()) val.Elem().SetString("0") } v.Elem().Set(val) case 2: val := reflect.New(v.Type().Elem().Elem()) val.Elem().SetString(strconv.FormatInt(decInt64(p), 10)) v.Elem().Set(val) default: return errWrongDataLen } return nil } func decInt16(p []byte) int16 { return int16(p[0])<<8 | int16(p[1]) } func decInt32(p []byte) int32 { if p[0] > math.MaxInt8 { return negInt32 | int32(p[0])<<8 | int32(p[1]) } return int32(p[0])<<8 | int32(p[1]) } func decInt64(p []byte) int64 { if p[0] > math.MaxInt8 { return negInt64 | int64(p[0])<<8 | int64(p[1]) } return int64(p[0])<<8 | int64(p[1]) } func decInt(p []byte) int { if p[0] > math.MaxInt8 { return negInt | int(p[0])<<8 | int(p[1]) } return int(p[0])<<8 | int(p[1]) } func decUint16(p []byte) uint16 { return uint16(p[0])<<8 | uint16(p[1]) } func decUint32(p []byte) uint32 { return uint32(p[0])<<8 | uint32(p[1]) } func decUint64(p []byte) uint64 { return uint64(p[0])<<8 | uint64(p[1]) } func decUint(p []byte) uint { return uint(p[0])<<8 | uint(p[1]) } ================================================ FILE: serialization/text/marshal.go ================================================ package text import ( "reflect" ) func Marshal(value any) ([]byte, error) { switch v := value.(type) { case nil: return nil, nil case string: return EncString(v) case *string: return EncStringR(v) case []byte: return EncBytes(v) case *[]byte: return EncBytesR(v) default: // Custom types (type MyString string) can be serialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.ValueOf(value) if rv.Kind() != reflect.Ptr { return EncReflect(rv) } return EncReflectR(rv) } } ================================================ FILE: serialization/text/marshal_utils.go ================================================ package text import ( "fmt" "reflect" ) func EncString(v string) ([]byte, error) { return encString(v), nil } func EncStringR(v *string) ([]byte, error) { if v == nil { return nil, nil } return encString(*v), nil } func EncBytes(v []byte) ([]byte, error) { return v, nil } func EncBytesR(v *[]byte) ([]byte, error) { if v == nil { return nil, nil } return *v, nil } func EncReflect(v reflect.Value) ([]byte, error) { switch v.Kind() { case reflect.String: return encString(v.String()), nil case reflect.Slice: if v.Type().Elem().Kind() != reflect.Uint8 { return nil, fmt.Errorf("failed to marshal text: unsupported value type (%T)(%[1]v), supported types: ~string, ~[]byte, unsetColumn", v.Interface()) } return EncBytes(v.Bytes()) case reflect.Struct: if v.Type().String() == "gocql.unsetColumn" { return nil, nil } return nil, fmt.Errorf("failed to marshal text: unsupported value type (%T)(%[1]v), supported types: ~string, ~[]byte, unsetColumn", v.Interface()) default: return nil, fmt.Errorf("failed to marshal text: unsupported value type (%T)(%[1]v), supported types: ~string, ~[]byte, unsetColumn", v.Interface()) } } func EncReflectR(v reflect.Value) ([]byte, error) { if v.IsNil() { return nil, nil } return EncReflect(v.Elem()) } func encString(v string) []byte { if v == "" { return make([]byte, 0) } return []byte(v) } ================================================ FILE: serialization/text/unmarshal.go ================================================ package text import ( "fmt" "reflect" ) func Unmarshal(data []byte, value any) error { switch v := value.(type) { case nil: return nil case *string: return DecString(data, v) case **string: return DecStringR(data, v) case *[]byte: return DecBytes(data, v) case **[]byte: return DecBytesR(data, v) case *any: return DecInterface(data, v) default: // Custom types (type MyString string) can be deserialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.ValueOf(value) rt := rv.Type() if rt.Kind() != reflect.Ptr { return fmt.Errorf("failed to unmarshal text: unsupported value type (%T)(%[1]v), supported types: ~string, ~[]byte", v) } if rt.Elem().Kind() != reflect.Ptr { return DecReflect(data, rv) } return DecReflectR(data, rv) } } ================================================ FILE: serialization/text/unmarshal_utils.go ================================================ package text import ( "fmt" "reflect" ) func errNilReference(v any) error { return fmt.Errorf("failed to unmarshal text: can not unmarshal into nil reference(%T)(%[1]v)", v) } func DecString(p []byte, v *string) error { if v == nil { return errNilReference(v) } *v = decString(p) return nil } func DecStringR(p []byte, v **string) error { if v == nil { return errNilReference(v) } *v = decStringR(p) return nil } func DecBytes(p []byte, v *[]byte) error { if v == nil { return errNilReference(v) } if p == nil { *v = nil return nil } if len(p) == 0 { *v = make([]byte, 0) return nil } *v = append((*v)[:0], p...) return nil } func DecBytesR(p []byte, v **[]byte) error { if v == nil { return errNilReference(v) } *v = decBytesR(p) return nil } func DecInterface(p []byte, v *any) error { if v == nil { return errNilReference(v) } *v = decBytes(p) return nil } func DecReflect(p []byte, v reflect.Value) error { if v.IsNil() { return errNilReference(v) } switch v = v.Elem(); v.Kind() { case reflect.String: v.SetString(decString(p)) case reflect.Slice: if v.Type().Elem().Kind() != reflect.Uint8 { return fmt.Errorf("failed to marshal text: unsupported value type (%T)(%[1]v), supported types: ~string, ~[]byte", v.Interface()) } v.SetBytes(decBytes(p)) case reflect.Interface: v.Set(reflect.ValueOf(decBytes(p))) default: return fmt.Errorf("failed to unmarshal text: unsupported value type (%T)(%[1]v), supported types: ~string, ~[]byte", v.Interface()) } return nil } func DecReflectR(p []byte, v reflect.Value) error { if v.IsNil() { return errNilReference(v) } switch ev := v.Type().Elem().Elem(); ev.Kind() { case reflect.String: return decReflectStringR(p, v) case reflect.Slice: if ev.Elem().Kind() != reflect.Uint8 { return fmt.Errorf("failed to marshal text: unsupported value type (%T)(%[1]v), supported types: ~string, ~[]byte", v.Interface()) } return decReflectBytesR(p, v) default: return fmt.Errorf("failed to unmarshal text: unsupported value type (%T)(%[1]v), supported types: ~string, ~[]byte", v.Interface()) } } func decReflectStringR(p []byte, v reflect.Value) error { if len(p) == 0 { if p == nil { v.Elem().Set(reflect.Zero(v.Type().Elem())) } else { v.Elem().Set(reflect.New(v.Type().Elem().Elem())) } return nil } val := reflect.New(v.Type().Elem().Elem()) val.Elem().SetString(string(p)) v.Elem().Set(val) return nil } func decReflectBytesR(p []byte, v reflect.Value) error { if len(p) == 0 { if p == nil { v.Elem().Set(reflect.Zero(v.Elem().Type())) } else { val := reflect.New(v.Type().Elem().Elem()) val.Elem().SetBytes(make([]byte, 0)) v.Elem().Set(val) } return nil } tmp := make([]byte, len(p)) copy(tmp, p) val := reflect.New(v.Type().Elem().Elem()) val.Elem().SetBytes(tmp) v.Elem().Set(val) return nil } func decString(p []byte) string { if len(p) == 0 { return "" } return string(p) } func decStringR(p []byte) *string { if len(p) == 0 { if p == nil { return nil } return new(string) } tmp := string(p) return &tmp } func decBytes(p []byte) []byte { if len(p) == 0 { if p == nil { return nil } return make([]byte, 0) } tmp := make([]byte, len(p)) copy(tmp, p) return tmp } func decBytesR(p []byte) *[]byte { if len(p) == 0 { if p == nil { return nil } tmp := make([]byte, 0) return &tmp } tmp := make([]byte, len(p)) copy(tmp, p) return &tmp } ================================================ FILE: serialization/timestamp/marshal.go ================================================ package timestamp import ( "reflect" "time" ) func Marshal(value any) ([]byte, error) { switch v := value.(type) { case nil: return nil, nil case int64: return EncInt64(v) case *int64: return EncInt64R(v) case time.Time: return EncTime(v) case *time.Time: return EncTimeR(v) default: // Custom types (type MyTime int64) can be serialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.TypeOf(value) if rv.Kind() != reflect.Ptr { return EncReflect(reflect.ValueOf(v)) } return EncReflectR(reflect.ValueOf(v)) } } ================================================ FILE: serialization/timestamp/marshal_utils.go ================================================ package timestamp import ( "fmt" "reflect" "time" ) var ( maxTimestamp = time.Date(292278994, 8, 17, 7, 12, 55, 807*1000000, time.UTC) minTimestamp = time.Date(-292275055, 5, 16, 16, 47, 4, 192*1000000, time.UTC) ) func EncInt64(v int64) ([]byte, error) { return encInt64(v), nil } func EncInt64R(v *int64) ([]byte, error) { if v == nil { return nil, nil } return EncInt64(*v) } func EncTime(v time.Time) ([]byte, error) { if v.After(maxTimestamp) || v.Before(minTimestamp) { return nil, fmt.Errorf("failed to marshal timestamp: the (%T)(%s) value should be in the range from -292275055-05-16T16:47:04.192Z to 292278994-08-17T07:12:55.807", v, v.Format(time.RFC3339Nano)) } // It supposed to be v.UTC().UnixMilli(), for backward compatibility map `time.Time{}` to nil value if v.IsZero() { return make([]byte, 0), nil } ms := v.UTC().UnixMilli() return []byte{byte(ms >> 56), byte(ms >> 48), byte(ms >> 40), byte(ms >> 32), byte(ms >> 24), byte(ms >> 16), byte(ms >> 8), byte(ms)}, nil } func EncTimeR(v *time.Time) ([]byte, error) { if v == nil { return nil, nil } return EncTime(*v) } func EncReflect(v reflect.Value) ([]byte, error) { switch v.Kind() { case reflect.Int64: return encInt64(v.Int()), nil case reflect.Struct: if v.Type().String() == "gocql.unsetColumn" { return nil, nil } return nil, fmt.Errorf("failed to marshal timestamp: unsupported value type (%T)(%[1]v), supported types: ~int64, time.Time, unsetColumn", v.Interface()) default: return nil, fmt.Errorf("failed to marshal timestamp: unsupported value type (%T)(%[1]v), supported types: ~int64, time.Time, unsetColumn", v.Interface()) } } func EncReflectR(v reflect.Value) ([]byte, error) { if v.IsNil() { return nil, nil } return EncReflect(v.Elem()) } func encInt64(v int64) []byte { return []byte{byte(v >> 56), byte(v >> 48), byte(v >> 40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} } ================================================ FILE: serialization/timestamp/unmarshal.go ================================================ package timestamp import ( "fmt" "reflect" "time" ) func Unmarshal(data []byte, value any) error { switch v := value.(type) { case nil: return nil case *int64: return DecInt64(data, v) case **int64: return DecInt64R(data, v) case *time.Time: return DecTime(data, v) case **time.Time: return DecTimeR(data, v) default: // Custom types (type MyTime int64) can be deserialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.ValueOf(value) rt := rv.Type() if rt.Kind() != reflect.Ptr { return fmt.Errorf("failed to unmarshal timestamp: unsupported value type (%T)(%[1]v), supported types: ~int64, time.Time", value) } if rt.Elem().Kind() != reflect.Ptr { return DecReflect(data, rv) } return DecReflectR(data, rv) } } ================================================ FILE: serialization/timestamp/unmarshal_utils.go ================================================ package timestamp import ( "fmt" "reflect" "time" ) var ( errWrongDataLen = fmt.Errorf("failed to unmarshal timestamp: the length of the data should be 0 or 8") ) func errNilReference(v any) error { return fmt.Errorf("failed to unmarshal timestamp: can not unmarshal into nil reference (%T)(%[1]v))", v) } func DecInt64(p []byte, v *int64) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 8: *v = decInt64(p) default: return errWrongDataLen } return nil } func DecInt64R(p []byte, v **int64) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(int64) } case 8: val := decInt64(p) *v = &val default: return errWrongDataLen } return nil } func DecTime(p []byte, v *time.Time) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: // supposed to be zero timestamp `time.UnixMilli(0).UTC()`, but for backward compatibility mapped to zero time *v = time.Time{} case 8: *v = decTime(p) default: return errWrongDataLen } return nil } func DecTimeR(p []byte, v **time.Time) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { // supposed to be zero timestamp `time.UnixMilli(0).UTC()`, but for backward compatibility mapped to zero time val := time.Time{} *v = &val } case 8: val := decTime(p) *v = &val default: return errWrongDataLen } return nil } func DecReflect(p []byte, v reflect.Value) error { if v.IsNil() { return fmt.Errorf("failed to unmarshal timestamp: can not unmarshal into nil reference (%T)(%[1]v))", v.Interface()) } switch v = v.Elem(); v.Kind() { case reflect.Int64: return decReflectInt64(p, v) default: return fmt.Errorf("failed to unmarshal timestamp: unsupported value type (%T)(%[1]v), supported types: ~int64, time.Time", v.Interface()) } } func decReflectInt64(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetInt(0) case 8: v.SetInt(decInt64(p)) default: return errWrongDataLen } return nil } func DecReflectR(p []byte, v reflect.Value) error { if v.IsNil() { return fmt.Errorf("failed to unmarshal timestamp: can not unmarshal into nil reference (%T)(%[1]v)", v.Interface()) } switch v.Type().Elem().Elem().Kind() { case reflect.Int64: return decReflectIntsR(p, v) default: return fmt.Errorf("failed to unmarshal timestamp: unsupported value type (%T)(%[1]v), supported types: ~int64, time.Time", v.Interface()) } } func decReflectIntsR(p []byte, v reflect.Value) error { switch len(p) { case 0: if p == nil { v.Elem().Set(reflect.Zero(v.Elem().Type())) } else { v.Elem().Set(reflect.New(v.Type().Elem().Elem())) } case 8: val := reflect.New(v.Type().Elem().Elem()) val.Elem().SetInt(decInt64(p)) v.Elem().Set(val) default: return errWrongDataLen } return nil } func decInt64(p []byte) int64 { return int64(p[0])<<56 | int64(p[1])<<48 | int64(p[2])<<40 | int64(p[3])<<32 | int64(p[4])<<24 | int64(p[5])<<16 | int64(p[6])<<8 | int64(p[7]) } func decTime(p []byte) time.Time { msec := decInt64(p) return time.Unix(msec/1e3, (msec%1e3)*1e6).UTC() } ================================================ FILE: serialization/timeuuid/marshal.go ================================================ package timeuuid import ( "reflect" ) func Marshal(value any) ([]byte, error) { switch v := value.(type) { case nil: return nil, nil case [16]byte: return EncArray(v) case *[16]byte: return EncArrayR(v) case []byte: return EncSlice(v) case *[]byte: return EncSliceR(v) case string: return EncString(v) case *string: return EncStringR(v) default: // Custom types (type MyUUID [16]byte) can be serialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.ValueOf(value) if rv.Kind() != reflect.Ptr { return EncReflect(rv) } return EncReflectR(rv) } } ================================================ FILE: serialization/timeuuid/marshal_utils.go ================================================ package timeuuid import ( "fmt" "reflect" "strings" ) func EncArray(v [16]byte) ([]byte, error) { return v[:], nil } func EncArrayR(v *[16]byte) ([]byte, error) { if v == nil { return nil, nil } return v[:], nil } func EncSlice(v []byte) ([]byte, error) { switch len(v) { case 0: if v == nil { return nil, nil } return make([]byte, 0), nil case 16: return v, nil default: return nil, fmt.Errorf("failed to marshal timeuuid: the ([]byte) length should be 0 or 16") } } func EncSliceR(v *[]byte) ([]byte, error) { if v == nil { return nil, nil } return EncSlice(*v) } func EncString(v string) ([]byte, error) { return encString(v) } func EncStringR(v *string) ([]byte, error) { if v == nil { return nil, nil } return encString(*v) } func EncReflect(v reflect.Value) ([]byte, error) { switch v.Kind() { case reflect.Array: if v.Type().Elem().Kind() != reflect.Uint8 || v.Len() != 16 { return nil, fmt.Errorf("failed to marshal timeuuid: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[16]byte, ~string, unsetColumn", v.Interface()) } nv := reflect.New(v.Type()) nv.Elem().Set(v) return nv.Elem().Bytes(), nil case reflect.Slice: if v.Type().Elem().Kind() != reflect.Uint8 { return nil, fmt.Errorf("failed to marshal timeuuid: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[16]byte, ~string, unsetColumn", v.Interface()) } return encReflectBytes(v) case reflect.String: return encReflectString(v) case reflect.Struct: if v.Type().String() == "gocql.unsetColumn" { return nil, nil } return nil, fmt.Errorf("failed to marshal timeuuid: timeuuid value type (%T)(%[1]v), supported types: ~[]byte, ~[16]byte, ~string, unsetColumn", v.Interface()) default: return nil, fmt.Errorf("failed to marshal timeuuid: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[16]byte, ~string, unsetColumn", v.Interface()) } } func EncReflectR(v reflect.Value) ([]byte, error) { if v.IsNil() { return nil, nil } switch ev := v.Elem(); ev.Kind() { case reflect.Array: if ev.Type().Elem().Kind() != reflect.Uint8 || ev.Len() != 16 { return nil, fmt.Errorf("failed to marshal timeuuid: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[16]byte, ~string, unsetColumn", v.Interface()) } return v.Elem().Bytes(), nil case reflect.Slice: if ev.Type().Elem().Kind() != reflect.Uint8 { return nil, fmt.Errorf("failed to marshal timeuuid: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[16]byte, ~string, unsetColumn", v.Interface()) } return encReflectBytes(ev) case reflect.String: return encReflectString(ev) default: return nil, fmt.Errorf("failed to marshal timeuuid: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[16]byte, ~string, unsetColumn", v.Interface()) } } func encReflectBytes(rv reflect.Value) ([]byte, error) { switch rv.Len() { case 0: if rv.IsNil() { return nil, nil } return make([]byte, 0), nil case 16: return rv.Bytes(), nil default: return nil, fmt.Errorf("failed to marshal timeuuid: the (%T) length should be 0 or 16", rv.Interface()) } } // encReflectString encodes uuid strings via reflect package. // The following code was taken from the `Parse` function of the "github.com/google/uuid" package. func encReflectString(v reflect.Value) ([]byte, error) { s := v.String() if s == zeroUUID { return make([]byte, 0), nil } switch len(s) { case 45: // urn:timeuuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx if !strings.EqualFold(s[:9], "urn:timeuuid:") { return nil, fmt.Errorf("failed to marshal timeuuid: the (%T) have invalid urn prefix: %q", v.Interface(), s[:9]) } s = s[9:] case 38: // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} s = s[1:] case 36: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx case 32: // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx var ok bool data := make([]byte, 16) for i := range data { data[i], ok = xtob(s[i*2], s[i*2+1]) if !ok { return nil, fmt.Errorf("failed to marshal timeuuid: the (%T) have invalid UUID format: %q", v.Interface(), s) } } return data, nil case 0: return nil, nil default: return nil, fmt.Errorf("failed to marshal timeuuid: the (%T) length can be 0,32,36,38,45", v.Interface()) } // s is now at least 36 bytes long // it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { return nil, fmt.Errorf("failed to marshal timeuuid: the (%T) have invalid UUID format: %q", v.Interface(), s) } data := make([]byte, 16) for i, x := range [16]int{ 0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34, } { b, ok := xtob(s[x], s[x+1]) if !ok { return nil, fmt.Errorf("failed to marshal timeuuid: the (%T) have invalid UUID format: %q", v.Interface(), b) } data[i] = b } return data, nil } // encString encodes uuid strings. // The following code was taken from the `Parse` function of the "github.com/google/uuid" package. func encString(s string) ([]byte, error) { if s == zeroUUID { return make([]byte, 0), nil } switch len(s) { case 45: // urn:timeuuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx if !strings.EqualFold(s[:9], "urn:timeuuid:") { return nil, fmt.Errorf("failed to marshal timeuuid: (string) have invalid urn prefix: %q", s[:9]) } s = s[9:] case 38: // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} s = s[1:] case 36: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx case 32: // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx var ok bool data := make([]byte, 16) for i := range data { data[i], ok = xtob(s[i*2], s[i*2+1]) if !ok { return nil, fmt.Errorf("failed to marshal timeuuid: the (string) have invalid UUID format: %q", s) } } return data, nil case 0: return nil, nil default: return nil, fmt.Errorf("failed to marshal timeuuid: the (string) length can be 0,32,36,38,45") } // s is now at least 36 bytes long // it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { return nil, fmt.Errorf("failed to marshal timeuuid: the (string) have invalid UUID format: %q", s) } data := make([]byte, 16) for i, x := range [16]int{ 0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34, } { b, ok := xtob(s[x], s[x+1]) if !ok { return nil, fmt.Errorf("failed to marshal timeuuid: the (string) have invalid UUID format: %q", b) } data[i] = b } return data, nil } // xtob converts hex characters x1 and x2 into a byte. // The following code was taken from the "github.com/google/uuid" package. func xtob(x1, x2 byte) (byte, bool) { b1 := xvalues[x1] b2 := xvalues[x2] return (b1 << 4) | b2, b1 != 255 && b2 != 255 } // xvalues returns the value of a byte as a hexadecimal digit or 255. // The following code was taken from the "github.com/google/uuid" package. var xvalues = [256]byte{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, } ================================================ FILE: serialization/timeuuid/unmarshal.go ================================================ package timeuuid import ( "fmt" "reflect" "time" ) func Unmarshal(data []byte, value any) error { switch v := value.(type) { case nil: return nil case *[16]byte: return DecArray(data, v) case **[16]byte: return DecArrayR(data, v) case *[]byte: return DecSlice(data, v) case **[]byte: return DecSliceR(data, v) case *string: return DecString(data, v) case **string: return DecStringR(data, v) case *time.Time: return DecTime(data, v) case **time.Time: return DecTimeR(data, v) default: // Custom types (type MyFloat float32) can be deserialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.ValueOf(value) if rv.Kind() != reflect.Ptr { return fmt.Errorf("failed to unmarshal timeuuid: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[16]byte, ~string", v) } if rv.Type().Elem().Kind() != reflect.Ptr { return DecReflect(data, rv) } return DecReflectR(data, rv) } } ================================================ FILE: serialization/timeuuid/unmarshal_utils.go ================================================ package timeuuid import ( "fmt" "reflect" "time" ) const ( hexString = "0123456789abcdef" zeroUUID = "00000000-0000-0000-0000-000000000000" ) var ( offsets = [...]int{0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34} timeBase = time.Date(1582, time.October, 15, 0, 0, 0, 0, time.UTC).Unix() errWrongDataLen = fmt.Errorf("failed to unmarshal timeuuid: the length of the data should be 0 or 16") ) func errNilReference(v any) error { return fmt.Errorf("failed to unmarshal timeuuid: can not unmarshal into nil reference(%T)(%[1]v)", v) } func DecArray(p []byte, v *[16]byte) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = [16]byte{} case 16: copy(v[:], p) default: return errWrongDataLen } return nil } func DecArrayR(p []byte, v **[16]byte) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new([16]byte) } case 16: *v = &[16]byte{} copy((*v)[:], p) default: return errWrongDataLen } return nil } func DecSlice(p []byte, v *[]byte) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = make([]byte, 0) } case 16: *v = make([]byte, 16) copy(*v, p) default: return errWrongDataLen } return nil } func DecSliceR(p []byte, v **[]byte) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { tmp := make([]byte, 0) *v = &tmp } case 16: *v = &[]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} copy(**v, p) default: return errWrongDataLen } return nil } func DecString(p []byte, v *string) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = "" } else { *v = zeroUUID } case 16: *v = decString(p) default: return errWrongDataLen } return nil } func DecStringR(p []byte, v **string) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { tmp := zeroUUID *v = &tmp } case 16: tmp := decString(p) *v = &tmp default: return errWrongDataLen } return nil } func DecTime(p []byte, v *time.Time) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = time.Time{} case 16: *v = decTime(p) default: return errWrongDataLen } return nil } func DecTimeR(p []byte, v **time.Time) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(time.Time) } case 16: val := decTime(p) *v = &val default: return errWrongDataLen } return nil } func DecReflect(p []byte, v reflect.Value) error { if v.IsNil() { return errNilReference(v) } switch v = v.Elem(); v.Kind() { case reflect.Array: if v.Type().Elem().Kind() != reflect.Uint8 || v.Len() != 16 { return fmt.Errorf("failed to unmarshal timeuuid: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[16]byte, ~string", v.Interface()) } return decReflectArray(p, v) case reflect.Slice: if v.Type().Elem().Kind() != reflect.Uint8 { return fmt.Errorf("failed to unmarshal timeuuid: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[16]byte, ~string", v.Interface()) } return decReflectBytes(p, v) case reflect.String: return decReflectString(p, v) default: return fmt.Errorf("failed to unmarshal timeuuid: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[16]byte, ~string", v.Interface()) } } func DecReflectR(p []byte, v reflect.Value) error { if v.IsNil() { return errNilReference(v) } ev := v.Elem() switch evt := ev.Type().Elem(); evt.Kind() { case reflect.Array: if evt.Elem().Kind() != reflect.Uint8 || ev.Len() != 16 { return fmt.Errorf("failed to marshal timeuuid: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[16]byte, ~string", v.Interface()) } return decReflectArrayR(p, ev) case reflect.Slice: if evt.Elem().Kind() != reflect.Uint8 { return fmt.Errorf("failed to marshal timeuuid: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[16]byte, ~string", v.Interface()) } return decReflectBytesR(p, ev) case reflect.String: return decReflectStringR(p, ev) default: return fmt.Errorf("failed to unmarshal timeuuid: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[16]byte, ~string", v.Interface()) } } func decReflectArray(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetZero() case 16: val := reflect.New(v.Type()) copy((*[16]byte)(val.UnsafePointer())[:], p) v.Set(val.Elem()) default: return errWrongDataLen } return nil } func decReflectBytes(p []byte, v reflect.Value) error { switch len(p) { case 0: if p == nil { v.SetBytes(nil) } else { v.SetBytes(make([]byte, 0)) } case 16: tmp := make([]byte, 16) copy(tmp, p) v.SetBytes(tmp) default: return errWrongDataLen } return nil } func decReflectString(p []byte, v reflect.Value) error { switch len(p) { case 0: if p == nil { v.SetString("") } else { v.SetString(zeroUUID) } case 16: v.SetString(decString(p)) default: return errWrongDataLen } return nil } func decReflectArrayR(p []byte, v reflect.Value) error { switch len(p) { case 0: if p == nil { v.Set(reflect.Zero(v.Type())) } else { val := reflect.New(v.Type().Elem()) v.Set(val) } case 16: val := reflect.New(v.Type().Elem()) copy((*[16]byte)(val.UnsafePointer())[:], p) v.Set(val) default: return errWrongDataLen } return nil } func decReflectBytesR(p []byte, v reflect.Value) error { switch len(p) { case 0: if p == nil { v.Set(reflect.Zero(v.Type())) } else { val := reflect.New(v.Type().Elem()) val.Elem().SetBytes(make([]byte, 0)) v.Set(val) } case 16: tmp := make([]byte, 16) copy(tmp, p) val := reflect.New(v.Type().Elem()) val.Elem().SetBytes(tmp) v.Set(val) default: return errWrongDataLen } return nil } func decReflectStringR(p []byte, v reflect.Value) error { switch len(p) { case 0: if p == nil { v.Set(reflect.Zero(v.Type())) } else { val := reflect.New(v.Type().Elem()) val.Elem().SetString(zeroUUID) v.Set(val) } case 16: val := reflect.New(v.Type().Elem()) val.Elem().SetString(decString(p)) v.Set(val) default: return errWrongDataLen } return nil } func decString(p []byte) string { r := make([]byte, 36) for i, b := range p { r[offsets[i]] = hexString[b>>4] r[offsets[i]+1] = hexString[b&0xF] } r[8] = '-' r[13] = '-' r[18] = '-' r[23] = '-' return string(r) } func decTime(u []byte) time.Time { ts := decTimestamp(u) sec := ts / 1e7 nsec := (ts % 1e7) * 100 return time.Unix(sec+timeBase, nsec).UTC() } func decTimestamp(u []byte) int64 { return int64(uint64(u[0])<<24|uint64(u[1])<<16| uint64(u[2])<<8|uint64(u[3])) + int64(uint64(u[4])<<40|uint64(u[5])<<32) + int64(uint64(u[6]&0x0F)<<56|uint64(u[7])<<48) } ================================================ FILE: serialization/tinyint/marshal.go ================================================ package tinyint import ( "math/big" "reflect" ) func Marshal(value any) ([]byte, error) { switch v := value.(type) { case nil: return nil, nil case int8: return EncInt8(v) case int32: return EncInt32(v) case int16: return EncInt16(v) case int64: return EncInt64(v) case int: return EncInt(v) case uint8: return EncUint8(v) case uint16: return EncUint16(v) case uint32: return EncUint32(v) case uint64: return EncUint64(v) case uint: return EncUint(v) case big.Int: return EncBigInt(v) case string: return EncString(v) case *int8: return EncInt8R(v) case *int16: return EncInt16R(v) case *int32: return EncInt32R(v) case *int64: return EncInt64R(v) case *int: return EncIntR(v) case *uint8: return EncUint8R(v) case *uint16: return EncUint16R(v) case *uint32: return EncUint32R(v) case *uint64: return EncUint64R(v) case *uint: return EncUintR(v) case *big.Int: return EncBigIntR(v) case *string: return EncStringR(v) default: // Custom types (type MyInt int) can be serialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.TypeOf(value) if rv.Kind() != reflect.Ptr { return EncReflect(reflect.ValueOf(v)) } return EncReflectR(reflect.ValueOf(v)) } } ================================================ FILE: serialization/tinyint/marshal_utils.go ================================================ package tinyint import ( "fmt" "math" "math/big" "reflect" "strconv" ) const supportedTypes = "~int8, ~int16, ~int32, ~int64, ~int, ~uint8, ~uint16, ~uint32, ~uint64, ~uint, ~string, big.Int" var ( maxBigInt = big.NewInt(math.MaxInt8) minBigInt = big.NewInt(math.MinInt8) ) func EncInt8(v int8) ([]byte, error) { return []byte{byte(v)}, nil } func EncInt8R(v *int8) ([]byte, error) { if v == nil { return nil, nil } return EncInt8(*v) } func EncInt16(v int16) ([]byte, error) { if v > math.MaxInt8 || v < math.MinInt8 { return nil, fmt.Errorf("failed to marshal tinyint: value %#v out of range", v) } return []byte{byte(v)}, nil } func EncInt16R(v *int16) ([]byte, error) { if v == nil { return nil, nil } return EncInt16(*v) } func EncInt32(v int32) ([]byte, error) { if v > math.MaxInt8 || v < math.MinInt8 { return nil, fmt.Errorf("failed to marshal tinyint: value %#v out of range", v) } return []byte{byte(v)}, nil } func EncInt32R(v *int32) ([]byte, error) { if v == nil { return nil, nil } return EncInt32(*v) } func EncInt64(v int64) ([]byte, error) { if v > math.MaxInt8 || v < math.MinInt8 { return nil, fmt.Errorf("failed to marshal tinyint: value %#v out of range", v) } return []byte{byte(v)}, nil } func EncInt64R(v *int64) ([]byte, error) { if v == nil { return nil, nil } return EncInt64(*v) } func EncInt(v int) ([]byte, error) { if v > math.MaxInt8 || v < math.MinInt8 { return nil, fmt.Errorf("failed to marshal tinyint: value %#v out of range", v) } return []byte{byte(v)}, nil } func EncIntR(v *int) ([]byte, error) { if v == nil { return nil, nil } return EncInt(*v) } func EncUint8(v uint8) ([]byte, error) { return []byte{v}, nil } func EncUint8R(v *uint8) ([]byte, error) { if v == nil { return nil, nil } return EncUint8(*v) } func EncUint16(v uint16) ([]byte, error) { if v > math.MaxUint8 { return nil, fmt.Errorf("failed to marshal tinyint: value %#v out of range", v) } return []byte{byte(v)}, nil } func EncUint16R(v *uint16) ([]byte, error) { if v == nil { return nil, nil } return EncUint16(*v) } func EncUint32(v uint32) ([]byte, error) { if v > math.MaxUint8 { return nil, fmt.Errorf("failed to marshal tinyint: value %#v out of range", v) } return []byte{byte(v)}, nil } func EncUint32R(v *uint32) ([]byte, error) { if v == nil { return nil, nil } return EncUint32(*v) } func EncUint64(v uint64) ([]byte, error) { if v > math.MaxUint8 { return nil, fmt.Errorf("failed to marshal tinyint: value %#v out of range", v) } return []byte{byte(v)}, nil } func EncUint64R(v *uint64) ([]byte, error) { if v == nil { return nil, nil } return EncUint64(*v) } func EncUint(v uint) ([]byte, error) { if v > math.MaxUint8 { return nil, fmt.Errorf("failed to marshal tinyint: value %#v out of range", v) } return []byte{byte(v)}, nil } func EncUintR(v *uint) ([]byte, error) { if v == nil { return nil, nil } return EncUint(*v) } func EncBigInt(v big.Int) ([]byte, error) { if v.Cmp(maxBigInt) == 1 || v.Cmp(minBigInt) == -1 { return nil, fmt.Errorf("failed to marshal tinyint: value (%T)(%s) out of range", v, v.String()) } return []byte{byte(v.Int64())}, nil } func EncBigIntR(v *big.Int) ([]byte, error) { if v == nil { return nil, nil } return EncBigInt(*v) } func EncString(v string) ([]byte, error) { if v == "" { return nil, nil } n, err := strconv.ParseInt(v, 10, 8) if err != nil { return nil, fmt.Errorf("failed to marshal tinyint: can not marshal (%T)(%[1]v) %s", v, err) } return []byte{byte(n)}, nil } func EncStringR(v *string) ([]byte, error) { if v == nil { return nil, nil } return EncString(*v) } func EncReflect(v reflect.Value) ([]byte, error) { switch v.Kind() { case reflect.Int8: return []byte{byte(v.Int())}, nil case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16: val := v.Int() if val > math.MaxInt8 || val < math.MinInt8 { return nil, fmt.Errorf("failed to marshal tinyint: value (%T)(%[1]v) out of range", v.Interface()) } return []byte{byte(val)}, nil case reflect.Uint8: return []byte{byte(v.Uint())}, nil case reflect.Uint, reflect.Uint64, reflect.Uint32, reflect.Uint16: val := v.Uint() if val > math.MaxUint8 { return nil, fmt.Errorf("failed to marshal tinyint: value (%T)(%[1]v) out of range", v.Interface()) } return []byte{byte(val)}, nil case reflect.String: val := v.String() if val == "" { return nil, nil } n, err := strconv.ParseInt(val, 10, 8) if err != nil { return nil, fmt.Errorf("failed to marshal tinyint: can not marshal (%T)(%[1]v) %s", v.Interface(), err) } return []byte{byte(n)}, nil case reflect.Struct: if v.Type().String() == "gocql.unsetColumn" { return nil, nil } return nil, fmt.Errorf("failed to marshal tinyint: unsupported value type (%T)(%[1]v), supported types: %s, unsetColumn", v.Interface(), supportedTypes) default: return nil, fmt.Errorf("failed to marshal tinyint: unsupported value type (%T)(%[1]v), supported types: %s, unsetColumn", v.Interface(), supportedTypes) } } func EncReflectR(v reflect.Value) ([]byte, error) { if v.IsNil() { return nil, nil } return EncReflect(v.Elem()) } ================================================ FILE: serialization/tinyint/unmarshal.go ================================================ package tinyint import ( "fmt" "math/big" "reflect" ) func Unmarshal(data []byte, value any) error { switch v := value.(type) { case nil: return nil case *int8: return DecInt8(data, v) case *int16: return DecInt16(data, v) case *int32: return DecInt32(data, v) case *int64: return DecInt64(data, v) case *int: return DecInt(data, v) case *uint8: return DecUint8(data, v) case *uint16: return DecUint16(data, v) case *uint32: return DecUint32(data, v) case *uint64: return DecUint64(data, v) case *uint: return DecUint(data, v) case *big.Int: return DecBigInt(data, v) case *string: return DecString(data, v) case **int8: return DecInt8R(data, v) case **int16: return DecInt16R(data, v) case **int32: return DecInt32R(data, v) case **int64: return DecInt64R(data, v) case **int: return DecIntR(data, v) case **uint8: return DecUint8R(data, v) case **uint16: return DecUint16R(data, v) case **uint32: return DecUint32R(data, v) case **uint64: return DecUint64R(data, v) case **uint: return DecUintR(data, v) case **big.Int: return DecBigIntR(data, v) case **string: return DecStringR(data, v) default: // Custom types (type MyInt int) can be deserialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.ValueOf(value) rt := rv.Type() if rt.Kind() != reflect.Ptr { return fmt.Errorf("failed to unmarshal tinyint: unsupported value type (%T)(%[1]v), supported types: %s", v, supportedTypes) } if rt.Elem().Kind() != reflect.Ptr { return DecReflect(data, rv) } return DecReflectR(data, rv) } } ================================================ FILE: serialization/tinyint/unmarshal_utils.go ================================================ package tinyint import ( "fmt" "math" "math/big" "reflect" "strconv" ) const ( negInt16 = int16(-1) << 8 negInt32 = int32(-1) << 8 negInt64 = int64(-1) << 8 negInt = int(-1) << 8 ) var errWrongDataLen = fmt.Errorf("failed to unmarshal tinyint: the length of the data should less or equal then 1") func errNilReference(v any) error { return fmt.Errorf("failed to unmarshal tinyint: can not unmarshal into nil reference(%T)(%[1]v)", v) } func DecInt8(p []byte, v *int8) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 1: *v = int8(p[0]) default: return errWrongDataLen } return nil } func DecInt8R(p []byte, v **int8) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(int8) } case 1: val := int8(p[0]) *v = &val default: return errWrongDataLen } return nil } func DecInt16(p []byte, v *int16) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 1: *v = decInt16(p) default: return errWrongDataLen } return nil } func DecInt16R(p []byte, v **int16) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(int16) } case 1: val := decInt16(p) *v = &val default: return errWrongDataLen } return nil } func DecInt32(p []byte, v *int32) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 1: *v = decInt32(p) default: return errWrongDataLen } return nil } func DecInt32R(p []byte, v **int32) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(int32) } case 1: val := decInt32(p) *v = &val default: return errWrongDataLen } return nil } func DecInt64(p []byte, v *int64) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 1: *v = decInt64(p) default: return errWrongDataLen } return nil } func DecInt64R(p []byte, v **int64) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(int64) } case 1: val := decInt64(p) *v = &val default: return errWrongDataLen } return nil } func DecInt(p []byte, v *int) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 1: *v = decInt(p) default: return errWrongDataLen } return nil } func DecIntR(p []byte, v **int) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(int) } case 1: val := decInt(p) *v = &val default: return errWrongDataLen } return nil } func DecUint8(p []byte, v *uint8) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 1: *v = p[0] default: return errWrongDataLen } return nil } func DecUint8R(p []byte, v **uint8) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(uint8) } case 1: val := p[0] *v = &val default: return errWrongDataLen } return nil } func DecUint16(p []byte, v *uint16) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 1: *v = uint16(p[0]) default: return errWrongDataLen } return nil } func DecUint16R(p []byte, v **uint16) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(uint16) } case 1: val := uint16(p[0]) *v = &val default: return errWrongDataLen } return nil } func DecUint32(p []byte, v *uint32) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 1: *v = uint32(p[0]) default: return errWrongDataLen } return nil } func DecUint32R(p []byte, v **uint32) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(uint32) } case 1: val := uint32(p[0]) *v = &val default: return errWrongDataLen } return nil } func DecUint64(p []byte, v *uint64) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 1: *v = uint64(p[0]) default: return errWrongDataLen } return nil } func DecUint64R(p []byte, v **uint64) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(uint64) } case 1: val := uint64(p[0]) *v = &val default: return errWrongDataLen } return nil } func DecUint(p []byte, v *uint) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 1: *v = uint(p[0]) default: return errWrongDataLen } return nil } func DecUintR(p []byte, v **uint) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(uint) } case 1: val := uint(p[0]) *v = &val default: return errWrongDataLen } return nil } func DecString(p []byte, v *string) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = "" } else { *v = "0" } case 1: *v = strconv.FormatInt(decInt64(p), 10) default: return errWrongDataLen } return nil } func DecStringR(p []byte, v **string) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { val := "0" *v = &val } case 1: *v = new(string) **v = strconv.FormatInt(decInt64(p), 10) default: return errWrongDataLen } return nil } func DecBigInt(p []byte, v *big.Int) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: v.SetInt64(0) case 1: v.SetInt64(decInt64(p)) default: return errWrongDataLen } return nil } func DecBigIntR(p []byte, v **big.Int) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = big.NewInt(0) } case 1: *v = big.NewInt(decInt64(p)) default: return errWrongDataLen } return nil } func DecReflect(p []byte, v reflect.Value) error { if v.IsNil() { return errNilReference(v) } switch v = v.Elem(); v.Kind() { case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: return decReflectInts(p, v) case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: return decReflectUints(p, v) case reflect.String: return decReflectString(p, v) default: return fmt.Errorf("failed to unmarshal tinyint: unsupported value type (%T)(%[1]v), supported types: %s", v.Interface(), supportedTypes) } } func DecReflectR(p []byte, v reflect.Value) error { if v.IsNil() { return errNilReference(v) } switch v.Type().Elem().Elem().Kind() { case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: return decReflectIntsR(p, v) case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: return decReflectUintsR(p, v) case reflect.String: return decReflectStringR(p, v) default: return fmt.Errorf("failed to unmarshal tinyint: unsupported value type (%T)(%[1]v), supported types: %s", v.Interface(), supportedTypes) } } func decReflectInts(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetInt(0) case 1: v.SetInt(decInt64(p)) default: return errWrongDataLen } return nil } func decReflectUints(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetUint(0) case 1: v.SetUint(uint64(p[0])) default: return errWrongDataLen } return nil } func decReflectString(p []byte, v reflect.Value) error { switch len(p) { case 0: if p == nil { v.SetString("") } else { v.SetString("0") } case 1: v.SetString(strconv.FormatInt(decInt64(p), 10)) default: return errWrongDataLen } return nil } func decReflectIntsR(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) case 1: val := reflect.New(v.Type().Elem().Elem()) val.Elem().SetInt(decInt64(p)) v.Elem().Set(val) default: return errWrongDataLen } return nil } func decReflectUintsR(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) case 1: val := reflect.New(v.Type().Elem().Elem()) val.Elem().SetUint(uint64(p[0])) v.Elem().Set(val) default: return errWrongDataLen } return nil } func decReflectStringR(p []byte, v reflect.Value) error { switch len(p) { case 0: var val reflect.Value if p == nil { val = reflect.Zero(v.Type().Elem()) } else { val = reflect.New(v.Type().Elem().Elem()) val.Elem().SetString("0") } v.Elem().Set(val) case 1: val := reflect.New(v.Type().Elem().Elem()) val.Elem().SetString(strconv.FormatInt(decInt64(p), 10)) v.Elem().Set(val) default: return errWrongDataLen } return nil } func decReflectNullableR(p []byte, v reflect.Value) reflect.Value { if p == nil { return reflect.Zero(v.Elem().Type()) } return reflect.New(v.Type().Elem().Elem()) } func decInt16(p []byte) int16 { if p[0] > math.MaxInt8 { return negInt16 | int16(p[0]) } return int16(p[0]) } func decInt32(p []byte) int32 { if p[0] > math.MaxInt8 { return negInt32 | int32(p[0]) } return int32(p[0]) } func decInt64(p []byte) int64 { if p[0] > math.MaxInt8 { return negInt64 | int64(p[0]) } return int64(p[0]) } func decInt(p []byte) int { if p[0] > math.MaxInt8 { return negInt | int(p[0]) } return int(p[0]) } ================================================ FILE: serialization/uuid/marshal.go ================================================ package uuid import ( "reflect" ) func Marshal(value any) ([]byte, error) { switch v := value.(type) { case nil: return nil, nil case [16]byte: return EncArray(v) case *[16]byte: return EncArrayR(v) case []byte: return EncSlice(v) case *[]byte: return EncSliceR(v) case string: return EncString(v) case *string: return EncStringR(v) default: // Custom types (type MyUUID [16]byte) can be serialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.ValueOf(value) if rv.Kind() != reflect.Ptr { return EncReflect(rv) } return EncReflectR(rv) } } ================================================ FILE: serialization/uuid/marshal_utils.go ================================================ package uuid import ( "fmt" "reflect" "strings" ) func EncArray(v [16]byte) ([]byte, error) { return v[:], nil } func EncArrayR(v *[16]byte) ([]byte, error) { if v == nil { return nil, nil } return v[:], nil } func EncSlice(v []byte) ([]byte, error) { switch len(v) { case 0: if v == nil { return nil, nil } return make([]byte, 0), nil case 16: return v, nil default: return nil, fmt.Errorf("failed to marshal uuid: the ([]byte) length should be 0 or 16") } } func EncSliceR(v *[]byte) ([]byte, error) { if v == nil { return nil, nil } return EncSlice(*v) } func EncString(v string) ([]byte, error) { return encString(v) } func EncStringR(v *string) ([]byte, error) { if v == nil { return nil, nil } return encString(*v) } func EncReflect(v reflect.Value) ([]byte, error) { switch v.Kind() { case reflect.Array: if v.Type().Elem().Kind() != reflect.Uint8 || v.Len() != 16 { return nil, fmt.Errorf("failed to marshal uuid: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[16]byte, ~string, unsetColumn", v.Interface()) } nv := reflect.New(v.Type()) nv.Elem().Set(v) return nv.Elem().Bytes(), nil case reflect.Slice: if v.Type().Elem().Kind() != reflect.Uint8 { return nil, fmt.Errorf("failed to marshal uuid: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[16]byte, ~string, unsetColumn", v.Interface()) } return encReflectBytes(v) case reflect.String: return encReflectString(v) case reflect.Struct: if v.Type().String() == "gocql.unsetColumn" { return nil, nil } return nil, fmt.Errorf("failed to marshal uuid: timeuuid value type (%T)(%[1]v), supported types: ~[]byte, ~[16]byte, ~string, unsetColumn", v.Interface()) default: return nil, fmt.Errorf("failed to marshal uuid: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[16]byte, ~string, unsetColumn", v.Interface()) } } func EncReflectR(v reflect.Value) ([]byte, error) { if v.IsNil() { return nil, nil } switch ev := v.Elem(); ev.Kind() { case reflect.Array: if ev.Type().Elem().Kind() != reflect.Uint8 || ev.Len() != 16 { return nil, fmt.Errorf("failed to marshal uuid: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[16]byte, ~string, unsetColumn", v.Interface()) } return v.Elem().Bytes(), nil case reflect.Slice: if ev.Type().Elem().Kind() != reflect.Uint8 { return nil, fmt.Errorf("failed to marshal uuid: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[16]byte, ~string, unsetColumn", v.Interface()) } return encReflectBytes(ev) case reflect.String: return encReflectString(ev) default: return nil, fmt.Errorf("failed to marshal uuid: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[16]byte, ~string, unsetColumn", v.Interface()) } } func encReflectBytes(rv reflect.Value) ([]byte, error) { switch rv.Len() { case 0: if rv.IsNil() { return nil, nil } return make([]byte, 0), nil case 16: return rv.Bytes(), nil default: return nil, fmt.Errorf("failed to marshal uuid: the (%T) length should be 0 or 16", rv.Interface()) } } // encReflectString encodes uuid strings via reflect package. // The following code was taken from the `Parse` function of the "github.com/google/uuid" package. func encReflectString(v reflect.Value) ([]byte, error) { s := v.String() switch len(s) { case 45: // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx if !strings.EqualFold(s[:9], "urn:uuid:") { return nil, fmt.Errorf("failed to marshal uuid: the (%T) have invalid urn prefix: %q", v.Interface(), s[:9]) } s = s[9:] case 38: // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} s = s[1:] case 36: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx case 32: // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx var ok bool data := make([]byte, 16) for i := range data { data[i], ok = xtob(s[i*2], s[i*2+1]) if !ok { return nil, fmt.Errorf("failed to marshal uuid: the (%T) have invalid UUID format: %q", v.Interface(), s) } } return data, nil case 0: return nil, nil default: return nil, fmt.Errorf("failed to marshal uuid: the (%T) length can be 0,32,36,38,45", v.Interface()) } // s is now at least 36 bytes long // it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { return nil, fmt.Errorf("failed to marshal uuid: the (%T) have invalid UUID format: %q", v.Interface(), s) } data := make([]byte, 16) for i, x := range [16]int{ 0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34, } { b, ok := xtob(s[x], s[x+1]) if !ok { return nil, fmt.Errorf("failed to marshal uuid: the (%T) have invalid UUID format: %q", v.Interface(), b) } data[i] = b } return data, nil } // encString encodes uuid strings. // The following code was taken from the `Parse` function of the "github.com/google/uuid" package. func encString(s string) ([]byte, error) { switch len(s) { case 45: // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx if !strings.EqualFold(s[:9], "urn:uuid:") { return nil, fmt.Errorf("failed to marshal uuid: (string) have invalid urn prefix: %q", s[:9]) } s = s[9:] case 38: // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} s = s[1:] case 36: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx case 32: // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx var ok bool data := make([]byte, 16) for i := range data { data[i], ok = xtob(s[i*2], s[i*2+1]) if !ok { return nil, fmt.Errorf("failed to marshal uuid: the (string) have invalid UUID format: %q", s) } } return data, nil case 0: return nil, nil default: return nil, fmt.Errorf("failed to marshal uuid: the (string) length can be 0,32,36,38,45") } // s is now at least 36 bytes long // it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { return nil, fmt.Errorf("failed to marshal uuid: the (string) have invalid UUID format: %q", s) } data := make([]byte, 16) for i, x := range [16]int{ 0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34, } { b, ok := xtob(s[x], s[x+1]) if !ok { return nil, fmt.Errorf("failed to marshal uuid: the (string) have invalid UUID format: %q", b) } data[i] = b } return data, nil } // xtob converts hex characters x1 and x2 into a byte. // The following code was taken from the "github.com/google/uuid" package. func xtob(x1, x2 byte) (byte, bool) { b1 := xvalues[x1] b2 := xvalues[x2] return (b1 << 4) | b2, b1 != 255 && b2 != 255 } // xvalues returns the value of a byte as a hexadecimal digit or 255. // The following code was taken from the "github.com/google/uuid" package. var xvalues = [256]byte{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, } ================================================ FILE: serialization/uuid/unmarshal.go ================================================ package uuid import ( "fmt" "reflect" ) func Unmarshal(data []byte, value any) error { switch v := value.(type) { case nil: return nil case *[16]byte: return DecArray(data, v) case **[16]byte: return DecArrayR(data, v) case *[]byte: return DecSlice(data, v) case **[]byte: return DecSliceR(data, v) case *string: return DecString(data, v) case **string: return DecStringR(data, v) default: // Custom types (type MyFloat float32) can be deserialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.ValueOf(value) if rv.Kind() != reflect.Ptr { return fmt.Errorf("failed to unmarshal uuid: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[16]byte, ~string", v) } if rv.Type().Elem().Kind() != reflect.Ptr { return DecReflect(data, rv) } return DecReflectR(data, rv) } } ================================================ FILE: serialization/uuid/unmarshal_utils.go ================================================ package uuid import ( "fmt" "reflect" ) const hexString = "0123456789abcdef" var ( offsets = [...]int{0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34} errWrongDataLen = fmt.Errorf("failed to unmarshal uuid: the length of the data should be 0 or 16") ) func errNilReference(v any) error { return fmt.Errorf("failed to unmarshal uuid: can not unmarshal into nil reference(%T)(%[1]v)", v) } func DecArray(p []byte, v *[16]byte) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = [16]byte{} case 16: copy(v[:], p) default: return errWrongDataLen } return nil } func DecArrayR(p []byte, v **[16]byte) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new([16]byte) } case 16: *v = &[16]byte{} copy((*v)[:], p) default: return errWrongDataLen } return nil } func DecSlice(p []byte, v *[]byte) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = make([]byte, 0) } case 16: *v = make([]byte, 16) copy(*v, p) default: return errWrongDataLen } return nil } func DecSliceR(p []byte, v **[]byte) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { tmp := make([]byte, 0) *v = &tmp } case 16: *v = &[]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} copy(**v, p) default: return errWrongDataLen } return nil } func DecString(p []byte, v *string) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = "" } else { *v = "00000000-0000-0000-0000-000000000000" } case 16: *v = decString(p) default: return errWrongDataLen } return nil } func DecStringR(p []byte, v **string) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { tmp := "00000000-0000-0000-0000-000000000000" *v = &tmp } case 16: tmp := decString(p) *v = &tmp default: return errWrongDataLen } return nil } func DecReflect(p []byte, v reflect.Value) error { if v.IsNil() { return errNilReference(v) } switch v = v.Elem(); v.Kind() { case reflect.Array: if v.Type().Elem().Kind() != reflect.Uint8 || v.Len() != 16 { return fmt.Errorf("failed to unmarshal uuid: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[16]byte, ~string", v.Interface()) } return decReflectArray(p, v) case reflect.Slice: if v.Type().Elem().Kind() != reflect.Uint8 { return fmt.Errorf("failed to unmarshal uuid: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[16]byte, ~string", v.Interface()) } return decReflectBytes(p, v) case reflect.String: return decReflectString(p, v) default: return fmt.Errorf("failed to unmarshal uuid: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[16]byte, ~string", v.Interface()) } } func DecReflectR(p []byte, v reflect.Value) error { if v.IsNil() { return errNilReference(v) } ev := v.Elem() switch evt := ev.Type().Elem(); evt.Kind() { case reflect.Array: if evt.Elem().Kind() != reflect.Uint8 || ev.Len() != 16 { return fmt.Errorf("failed to marshal timeuuid: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[16]byte, ~string", v.Interface()) } return decReflectArrayR(p, ev) case reflect.Slice: if evt.Elem().Kind() != reflect.Uint8 { return fmt.Errorf("failed to marshal timeuuid: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[16]byte, ~string", v.Interface()) } return decReflectBytesR(p, ev) case reflect.String: return decReflectStringR(p, ev) default: return fmt.Errorf("failed to unmarshal timeuuid: unsupported value type (%T)(%[1]v), supported types: ~[]byte, ~[16]byte, ~string", v.Interface()) } } func decReflectArray(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetZero() case 16: val := reflect.New(v.Type()) copy((*[16]byte)(val.UnsafePointer())[:], p) v.Set(val.Elem()) default: return errWrongDataLen } return nil } func decReflectBytes(p []byte, v reflect.Value) error { switch len(p) { case 0: if p == nil { v.SetBytes(nil) } else { v.SetBytes(make([]byte, 0)) } case 16: tmp := make([]byte, 16) copy(tmp, p) v.SetBytes(tmp) default: return errWrongDataLen } return nil } func decReflectString(p []byte, v reflect.Value) error { switch len(p) { case 0: if p == nil { v.SetString("") } else { v.SetString("00000000-0000-0000-0000-000000000000") } case 16: v.SetString(decString(p)) default: return errWrongDataLen } return nil } func decReflectArrayR(p []byte, v reflect.Value) error { switch len(p) { case 0: if p == nil { v.Set(reflect.Zero(v.Type())) } else { val := reflect.New(v.Type().Elem()) v.Set(val) } case 16: val := reflect.New(v.Type().Elem()) copy((*[16]byte)(val.UnsafePointer())[:], p) v.Set(val) default: return errWrongDataLen } return nil } func decReflectBytesR(p []byte, v reflect.Value) error { switch len(p) { case 0: if p == nil { v.Set(reflect.Zero(v.Type())) } else { val := reflect.New(v.Type().Elem()) val.Elem().SetBytes(make([]byte, 0)) v.Set(val) } case 16: tmp := make([]byte, 16) copy(tmp, p) val := reflect.New(v.Type().Elem()) val.Elem().SetBytes(tmp) v.Set(val) default: return errWrongDataLen } return nil } func decReflectStringR(p []byte, v reflect.Value) error { switch len(p) { case 0: if p == nil { v.Set(reflect.Zero(v.Type())) } else { val := reflect.New(v.Type().Elem()) val.Elem().SetString("00000000-0000-0000-0000-000000000000") v.Set(val) } case 16: val := reflect.New(v.Type().Elem()) val.Elem().SetString(decString(p)) v.Set(val) default: return errWrongDataLen } return nil } func decString(p []byte) string { r := make([]byte, 36) for i, b := range p { r[offsets[i]] = hexString[b>>4] r[offsets[i]+1] = hexString[b&0xF] } r[8] = '-' r[13] = '-' r[18] = '-' r[23] = '-' return string(r) } ================================================ FILE: serialization/varchar/marshal.go ================================================ package varchar import ( "reflect" ) func Marshal(value any) ([]byte, error) { switch v := value.(type) { case nil: return nil, nil case string: return EncString(v) case *string: return EncStringR(v) case []byte: return EncBytes(v) case *[]byte: return EncBytesR(v) default: // Custom types (type MyString string) can be serialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.ValueOf(value) if rv.Kind() != reflect.Ptr { return EncReflect(rv) } return EncReflectR(rv) } } ================================================ FILE: serialization/varchar/marshal_utils.go ================================================ package varchar import ( "fmt" "reflect" ) func EncString(v string) ([]byte, error) { return encString(v), nil } func EncStringR(v *string) ([]byte, error) { if v == nil { return nil, nil } return encString(*v), nil } func EncBytes(v []byte) ([]byte, error) { return v, nil } func EncBytesR(v *[]byte) ([]byte, error) { if v == nil { return nil, nil } return *v, nil } func EncReflect(v reflect.Value) ([]byte, error) { switch v.Kind() { case reflect.String: return encString(v.String()), nil case reflect.Slice: if v.Type().Elem().Kind() != reflect.Uint8 { return nil, fmt.Errorf("failed to marshal varchar: unsupported value type (%T)(%[1]v), supported types: ~string, ~[]byte, unsetColumn", v.Interface()) } return EncBytes(v.Bytes()) case reflect.Struct: if v.Type().String() == "gocql.unsetColumn" { return nil, nil } return nil, fmt.Errorf("failed to marshal varchar: unsupported value type (%T)(%[1]v), supported types: ~string, ~[]byte, unsetColumn", v.Interface()) default: return nil, fmt.Errorf("failed to marshal varchar: unsupported value type (%T)(%[1]v), supported types: ~string, ~[]byte, unsetColumn", v.Interface()) } } func EncReflectR(v reflect.Value) ([]byte, error) { if v.IsNil() { return nil, nil } return EncReflect(v.Elem()) } func encString(v string) []byte { if v == "" { return make([]byte, 0) } return []byte(v) } ================================================ FILE: serialization/varchar/unmarshal.go ================================================ package varchar import ( "fmt" "reflect" ) func Unmarshal(data []byte, value any) error { switch v := value.(type) { case nil: return nil case *string: return DecString(data, v) case **string: return DecStringR(data, v) case *[]byte: return DecBytes(data, v) case **[]byte: return DecBytesR(data, v) case *any: return DecInterface(data, v) default: // Custom types (type MyString string) can be deserialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.ValueOf(value) rt := rv.Type() if rt.Kind() != reflect.Ptr { return fmt.Errorf("failed to unmarshal varchar: unsupported value type (%T)(%[1]v), supported types: ~string, ~[]byte", v) } if rt.Elem().Kind() != reflect.Ptr { return DecReflect(data, rv) } return DecReflectR(data, rv) } } ================================================ FILE: serialization/varchar/unmarshal_utils.go ================================================ package varchar import ( "fmt" "reflect" ) func errNilReference(v any) error { return fmt.Errorf("failed to unmarshal varchar: can not unmarshal into nil reference(%T)(%[1]v)", v) } func DecString(p []byte, v *string) error { if v == nil { return errNilReference(v) } *v = decString(p) return nil } func DecStringR(p []byte, v **string) error { if v == nil { return errNilReference(v) } *v = decStringR(p) return nil } func DecBytes(p []byte, v *[]byte) error { if v == nil { return errNilReference(v) } if p == nil { *v = nil return nil } if len(p) == 0 { *v = make([]byte, 0) return nil } *v = append((*v)[:0], p...) return nil } func DecBytesR(p []byte, v **[]byte) error { if v == nil { return errNilReference(v) } *v = decBytesR(p) return nil } func DecInterface(p []byte, v *any) error { if v == nil { return errNilReference(v) } *v = decBytes(p) return nil } func DecReflect(p []byte, v reflect.Value) error { if v.IsNil() { return errNilReference(v) } switch v = v.Elem(); v.Kind() { case reflect.String: v.SetString(decString(p)) case reflect.Slice: if v.Type().Elem().Kind() != reflect.Uint8 { return fmt.Errorf("failed to marshal varchar: unsupported value type (%T)(%[1]v), supported types: ~string, ~[]byte", v.Interface()) } v.SetBytes(decBytes(p)) case reflect.Interface: v.Set(reflect.ValueOf(decBytes(p))) default: return fmt.Errorf("failed to unmarshal varchar: unsupported value type (%T)(%[1]v), supported types: ~string, ~[]byte", v.Interface()) } return nil } func DecReflectR(p []byte, v reflect.Value) error { if v.IsNil() { return errNilReference(v) } switch ev := v.Type().Elem().Elem(); ev.Kind() { case reflect.String: return decReflectStringR(p, v) case reflect.Slice: if ev.Elem().Kind() != reflect.Uint8 { return fmt.Errorf("failed to marshal varchar: unsupported value type (%T)(%[1]v), supported types: ~string, ~[]byte", v.Interface()) } return decReflectBytesR(p, v) default: return fmt.Errorf("failed to unmarshal varchar: unsupported value type (%T)(%[1]v), supported types: ~string, ~[]byte", v.Interface()) } } func decReflectStringR(p []byte, v reflect.Value) error { if len(p) == 0 { if p == nil { v.Elem().Set(reflect.Zero(v.Type().Elem())) } else { v.Elem().Set(reflect.New(v.Type().Elem().Elem())) } return nil } val := reflect.New(v.Type().Elem().Elem()) val.Elem().SetString(string(p)) v.Elem().Set(val) return nil } func decReflectBytesR(p []byte, v reflect.Value) error { if len(p) == 0 { if p == nil { v.Elem().Set(reflect.Zero(v.Elem().Type())) } else { val := reflect.New(v.Type().Elem().Elem()) val.Elem().SetBytes(make([]byte, 0)) v.Elem().Set(val) } return nil } tmp := make([]byte, len(p)) copy(tmp, p) val := reflect.New(v.Type().Elem().Elem()) val.Elem().SetBytes(tmp) v.Elem().Set(val) return nil } func decString(p []byte) string { if len(p) == 0 { return "" } return string(p) } func decStringR(p []byte) *string { if len(p) == 0 { if p == nil { return nil } return new(string) } tmp := string(p) return &tmp } func decBytes(p []byte) []byte { if len(p) == 0 { if p == nil { return nil } return make([]byte, 0) } tmp := make([]byte, len(p)) copy(tmp, p) return tmp } func decBytesR(p []byte) *[]byte { if len(p) == 0 { if p == nil { return nil } tmp := make([]byte, 0) return &tmp } tmp := make([]byte, len(p)) copy(tmp, p) return &tmp } ================================================ FILE: serialization/varint/marshal.go ================================================ package varint import ( "math/big" "reflect" ) func Marshal(value any) ([]byte, error) { switch v := value.(type) { case nil: return nil, nil case int8: return EncInt8(v) case int32: return EncInt32(v) case int16: return EncInt16(v) case int64: return EncInt64(v) case int: return EncInt(v) case uint8: return EncUint8(v) case uint16: return EncUint16(v) case uint32: return EncUint32(v) case uint64: return EncUint64(v) case uint: return EncUint(v) case big.Int: return EncBigInt(v) case string: return EncString(v) case *int8: return EncInt8R(v) case *int16: return EncInt16R(v) case *int32: return EncInt32R(v) case *int64: return EncInt64R(v) case *int: return EncIntR(v) case *uint8: return EncUint8R(v) case *uint16: return EncUint16R(v) case *uint32: return EncUint32R(v) case *uint64: return EncUint64R(v) case *uint: return EncUintR(v) case *big.Int: return EncBigIntR(v) case *string: return EncStringR(v) default: // Custom types (type MyInt int) can be serialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.TypeOf(value) if rv.Kind() != reflect.Ptr { return EncReflect(reflect.ValueOf(v)) } return EncReflectR(reflect.ValueOf(v)) } } ================================================ FILE: serialization/varint/marshal_bigint_test.go ================================================ package varint import ( "bytes" "math" "math/big" "math/rand" "testing" ) func TestEnc2BigInt(t *testing.T) { t.Parallel() genData := func(v int64) []byte { data := []byte{byte(v >> 56), byte(v >> 48), byte(v >> 40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} out := make([]byte, 0) add := false for i, b := range data { if !add { if v < 0 { if b != 255 || b == 255 && data[i+1] < 128 { add = true } else { continue } } else { if b != 0 || b == 0 && data[i+1] > 127 { add = true } else { continue } } } out = append(out, b) } return out } t.Run("positive", func(t *testing.T) { rnd := rand.New(rand.NewSource(rand.Int63())) for i := int64(math.MaxInt16); i < 1<<24; i = i + int64(rnd.Int31n(300)) { expected := genData(i) received := EncBigIntRS(big.NewInt(i)) if !bytes.Equal(expected, received) { t.Fatalf("%d\nexpected:%x\nreceived:%x", i, expected, received) } received = EncInt64Ext(i) if !bytes.Equal(expected, received) { t.Fatalf("%d\nexpected:%x\nreceived:%x", i, expected, received) } } }) t.Run("negative", func(t *testing.T) { rnd := rand.New(rand.NewSource(rand.Int63())) for i := int64(math.MinInt16); i > -1<<24; i = i - int64(rnd.Int31n(300)) { expected := genData(i) received := EncBigIntRS(big.NewInt(i)) if !bytes.Equal(expected, received) { t.Fatalf("%d\nexpected:%x\nreceived:%x", i, expected, received) } received = EncInt64Ext(i) if !bytes.Equal(expected, received) { t.Fatalf("%d\nexpected:%x\nreceived:%x", i, expected, received) } } }) } ================================================ FILE: serialization/varint/marshal_custom.go ================================================ package varint import ( "fmt" "math/big" "reflect" "strconv" ) const supportedTypes = "~int8, ~int16, ~int32, ~int64, ~int, ~uint8, ~uint16, ~uint32, ~uint64, ~uint, ~string, big.Int" func EncReflect(v reflect.Value) ([]byte, error) { switch v.Type().Kind() { case reflect.Int8: return EncInt8(int8(v.Int())) case reflect.Int16: return EncInt16(int16(v.Int())) case reflect.Int32: return EncInt32(int32(v.Int())) case reflect.Int, reflect.Int64: return EncInt64(v.Int()) case reflect.Uint8: return EncUint8(uint8(v.Uint())) case reflect.Uint16: return EncUint16(uint16(v.Uint())) case reflect.Uint32: return EncUint32(uint32(v.Uint())) case reflect.Uint, reflect.Uint64: return EncUint64(v.Uint()) case reflect.String: return encReflectString(v) case reflect.Struct: if v.Type().String() == "gocql.unsetColumn" { return nil, nil } return nil, fmt.Errorf("failed to marshal varint: unsupported value type (%T)(%[1]v), supported types: %s, unsetColumn", v.Interface(), supportedTypes) default: return nil, fmt.Errorf("failed to marshal varint: unsupported value type (%T)(%[1]v), supported types: %s, unsetColumn", v.Interface(), supportedTypes) } } func EncReflectR(v reflect.Value) ([]byte, error) { if v.IsNil() { return nil, nil } return EncReflect(v.Elem()) } func encReflectString(v reflect.Value) ([]byte, error) { val := v.String() switch { case len(val) == 0: return nil, nil case len(val) <= 18: n, err := strconv.ParseInt(val, 10, 64) if err != nil { return nil, fmt.Errorf("failed to marshal varint: can not marshal (%T)(%[1]v), %s", v.Interface(), err) } return EncInt64Ext(n), nil case len(val) <= 20: n, err := strconv.ParseInt(val, 10, 64) if err == nil { return EncInt64Ext(n), nil } t, ok := new(big.Int).SetString(val, 10) if !ok { return nil, fmt.Errorf("failed to marshal varint: can not marshal (%T)(%[1]v)", v.Interface()) } return EncBigIntRS(t), nil default: t, ok := new(big.Int).SetString(val, 10) if !ok { return nil, fmt.Errorf("failed to marshal varint: can not marshal (%T)(%[1]v)", v.Interface()) } return EncBigIntRS(t), nil } } ================================================ FILE: serialization/varint/marshal_ints.go ================================================ package varint func EncInt8(v int8) ([]byte, error) { return encInt8(v), nil } func EncInt8R(v *int8) ([]byte, error) { if v == nil { return nil, nil } return encInt8(*v), nil } func EncInt16(v int16) ([]byte, error) { return encInt16(v), nil } func EncInt16R(v *int16) ([]byte, error) { if v == nil { return nil, nil } return encInt16(*v), nil } func EncInt32(v int32) ([]byte, error) { return encInt32(v), nil } func EncInt32R(v *int32) ([]byte, error) { if v == nil { return nil, nil } return encInt32(*v), nil } func EncInt64(v int64) ([]byte, error) { return EncInt64Ext(v), nil } func EncInt64R(v *int64) ([]byte, error) { if v == nil { return nil, nil } return EncInt64Ext(*v), nil } func EncInt(v int) ([]byte, error) { return encInt(v), nil } func EncIntR(v *int) ([]byte, error) { if v == nil { return nil, nil } return encInt(*v), nil } func encInt8(v int8) []byte { return []byte{byte(v)} } func encInt16(v int16) []byte { if v <= maxInt8 && v >= minInt8 { return []byte{byte(v)} } return []byte{byte(v >> 8), byte(v)} } func encInt32(v int32) []byte { if v <= maxInt8 && v >= minInt8 { return []byte{byte(v)} } if v <= maxInt16 && v >= minInt16 { return []byte{byte(v >> 8), byte(v)} } if v <= maxInt24 && v >= minInt24 { return []byte{byte(v >> 16), byte(v >> 8), byte(v)} } return []byte{byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} } func EncInt64Ext(v int64) []byte { if v <= maxInt8 && v >= minInt8 { return []byte{byte(v)} } if v <= maxInt16 && v >= minInt16 { return []byte{byte(v >> 8), byte(v)} } if v <= maxInt24 && v >= minInt24 { return []byte{byte(v >> 16), byte(v >> 8), byte(v)} } if v <= maxInt32 && v >= minInt32 { return []byte{byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} } if v <= maxInt40 && v >= minInt40 { return []byte{byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} } if v <= maxInt48 && v >= minInt48 { return []byte{byte(v >> 40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} } if v <= maxInt56 && v >= minInt56 { return []byte{byte(v >> 48), byte(v >> 40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} } return []byte{byte(v >> 56), byte(v >> 48), byte(v >> 40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} } func encInt(v int) []byte { if v <= maxInt8 && v >= minInt8 { return []byte{byte(v)} } if v <= maxInt16 && v >= minInt16 { return []byte{byte(v >> 8), byte(v)} } if v <= maxInt24 && v >= minInt24 { return []byte{byte(v >> 16), byte(v >> 8), byte(v)} } if v <= maxInt32 && v >= minInt32 { return []byte{byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} } if v <= maxInt40 && v >= minInt40 { return []byte{byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} } if v <= maxInt48 && v >= minInt48 { return []byte{byte(v >> 40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} } if v <= maxInt56 && v >= minInt56 { return []byte{byte(v >> 48), byte(v >> 40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} } return []byte{byte(v >> 56), byte(v >> 48), byte(v >> 40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} } ================================================ FILE: serialization/varint/marshal_uints.go ================================================ package varint func EncUint8(v uint8) ([]byte, error) { return encUint8(v), nil } func EncUint8R(v *uint8) ([]byte, error) { if v == nil { return nil, nil } return encUint8(*v), nil } func EncUint16(v uint16) ([]byte, error) { return encUint16(v), nil } func EncUint16R(v *uint16) ([]byte, error) { if v == nil { return nil, nil } return encUint16(*v), nil } func EncUint32(v uint32) ([]byte, error) { return encUint32(v), nil } func EncUint32R(v *uint32) ([]byte, error) { if v == nil { return nil, nil } return encUint32(*v), nil } func EncUint64(v uint64) ([]byte, error) { return encUint64(v), nil } func EncUint64R(v *uint64) ([]byte, error) { if v == nil { return nil, nil } return encUint64(*v), nil } func EncUint(v uint) ([]byte, error) { return encUint(v), nil } func EncUintR(v *uint) ([]byte, error) { if v == nil { return nil, nil } return encUint(*v), nil } func encUint8(v uint8) []byte { if v > maxInt8 { return []byte{0, v} } return []byte{v} } func encUint16(v uint16) []byte { switch { case byte(v>>15) != 0: return []byte{0, byte(v >> 8), byte(v)} case byte(v>>7) != 0: return []byte{byte(v >> 8), byte(v)} default: return []byte{byte(v)} } } func encUint32(v uint32) []byte { switch { case byte(v>>31) != 0: return []byte{0, byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>23) != 0: return []byte{byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>15) != 0: return []byte{byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>7) != 0: return []byte{byte(v >> 8), byte(v)} default: return []byte{byte(v)} } } func encUint64(v uint64) []byte { switch { case byte(v>>63) != 0: return []byte{0, byte(v >> 56), byte(v >> 48), byte(v >> 40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>55) != 0: return []byte{byte(v >> 56), byte(v >> 48), byte(v >> 40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>47) != 0: return []byte{byte(v >> 48), byte(v >> 40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>39) != 0: return []byte{byte(v >> 40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>31) != 0: return []byte{byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>23) != 0: return []byte{byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>15) != 0: return []byte{byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>7) != 0: return []byte{byte(v >> 8), byte(v)} default: return []byte{byte(v)} } } func encUint(v uint) []byte { switch { case byte(v>>63) != 0: return []byte{0, byte(v >> 56), byte(v >> 48), byte(v >> 40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>55) != 0: return []byte{byte(v >> 56), byte(v >> 48), byte(v >> 40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>47) != 0: return []byte{byte(v >> 48), byte(v >> 40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>39) != 0: return []byte{byte(v >> 40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>31) != 0: return []byte{byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>23) != 0: return []byte{byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>15) != 0: return []byte{byte(v >> 16), byte(v >> 8), byte(v)} case byte(v>>7) != 0: return []byte{byte(v >> 8), byte(v)} default: return []byte{byte(v)} } } ================================================ FILE: serialization/varint/marshal_utils.go ================================================ package varint import ( "fmt" "math" "math/big" "strconv" ) const ( maxInt8 = 1<<7 - 1 maxInt16 = 1<<15 - 1 maxInt24 = 1<<23 - 1 maxInt32 = 1<<31 - 1 maxInt40 = 1<<39 - 1 maxInt48 = 1<<47 - 1 maxInt56 = 1<<55 - 1 maxInt64 = 1<<63 - 1 minInt8 = -1 << 7 minInt16 = -1 << 15 minInt24 = -1 << 23 minInt32 = -1 << 31 minInt40 = -1 << 39 minInt48 = -1 << 47 minInt56 = -1 << 55 ) func EncBigInt(v big.Int) ([]byte, error) { return encBigInt(v), nil } func EncBigIntR(v *big.Int) ([]byte, error) { if v == nil { return nil, nil } return EncBigIntRS(v), nil } func EncString(v string) ([]byte, error) { switch { case len(v) == 0: return nil, nil case len(v) <= 18: n, err := strconv.ParseInt(v, 10, 64) if err != nil { return nil, fmt.Errorf("failed to marshal varint: can not marshal %#v, %s", v, err) } return EncInt64Ext(n), nil case len(v) <= 20: n, err := strconv.ParseInt(v, 10, 64) if err == nil { return EncInt64Ext(n), nil } t, ok := new(big.Int).SetString(v, 10) if !ok { return nil, fmt.Errorf("failed to marshal varint: can not marshal %#v", v) } return EncBigIntRS(t), nil default: t, ok := new(big.Int).SetString(v, 10) if !ok { return nil, fmt.Errorf("failed to marshal varint: can not marshal %#v", v) } return EncBigIntRS(t), nil } } func EncStringR(v *string) ([]byte, error) { if v == nil { return nil, nil } return EncString(*v) } func encBigInt(v big.Int) []byte { switch v.Sign() { case 1: data := v.Bytes() if data[0] > math.MaxInt8 { data = append([]byte{0}, data...) } return data case -1: data := v.Bytes() add := true for i := len(data) - 1; i >= 0; i-- { if !add { data[i] = 255 - data[i] } else { data[i] = 255 - data[i] + 1 if data[i] != 0 { add = false } } } if data[0] < 128 { data = append([]byte{255}, data...) } return data default: return []byte{0} } } // EncBigIntRS encode big.Int to []byte. // This function shared to use in marshal `decimal`. func EncBigIntRS(v *big.Int) []byte { switch v.Sign() { case 1: data := v.Bytes() if data[0] > math.MaxInt8 { data = append([]byte{0}, data...) } return data case -1: data := v.Bytes() add := true for i := len(data) - 1; i >= 0; i-- { if !add { data[i] = 255 - data[i] } else { data[i] = 255 - data[i] + 1 if data[i] != 0 { add = false } } } if data[0] < 128 { data = append([]byte{255}, data...) } return data default: return []byte{0} } } ================================================ FILE: serialization/varint/unmarshal.go ================================================ package varint import ( "fmt" "math/big" "reflect" ) func Unmarshal(data []byte, value any) error { switch v := value.(type) { case nil: return nil case *int8: return DecInt8(data, v) case *int16: return DecInt16(data, v) case *int32: return DecInt32(data, v) case *int64: return DecInt64(data, v) case *int: return DecInt(data, v) case *uint8: return DecUint8(data, v) case *uint16: return DecUint16(data, v) case *uint32: return DecUint32(data, v) case *uint64: return DecUint64(data, v) case *uint: return DecUint(data, v) case *big.Int: return DecBigInt(data, v) case *string: return DecString(data, v) case **int8: return DecInt8R(data, v) case **int16: return DecInt16R(data, v) case **int32: return DecInt32R(data, v) case **int64: return DecInt64R(data, v) case **int: return DecIntR(data, v) case **uint8: return DecUint8R(data, v) case **uint16: return DecUint16R(data, v) case **uint32: return DecUint32R(data, v) case **uint64: return DecUint64R(data, v) case **uint: return DecUintR(data, v) case **big.Int: return DecBigIntR(data, v) case **string: return DecStringR(data, v) default: // Custom types (type MyInt int) can be deserialized only via `reflect` package. // Later, when generic-based serialization is introduced we can do that via generics. rv := reflect.ValueOf(value) rt := rv.Type() if rt.Kind() != reflect.Ptr { return fmt.Errorf("failed to unmarshal varint: unsupported value type (%T)(%#[1]v), supported types: %s", value, supportedTypes) } if rt.Elem().Kind() != reflect.Ptr { return DecReflect(data, rv) } return DecReflectR(data, rv) } } ================================================ FILE: serialization/varint/unmarshal_bigint_test.go ================================================ package varint import ( "math" "math/big" "math/rand" "testing" ) func TestDec2BigInt(t *testing.T) { t.Parallel() genData := func(v int64) []byte { data := []byte{byte(v >> 56), byte(v >> 48), byte(v >> 40), byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)} out := make([]byte, 0) add := false for i, b := range data { if !add { if v < 0 { if b != 255 || b == 255 && data[i+1] < 128 { add = true } else { continue } } else { if b != 0 || b == 0 && data[i+1] > 127 { add = true } else { continue } } } out = append(out, b) } return out } t.Run("positive", func(t *testing.T) { rnd := rand.New(rand.NewSource(rand.Int63())) for i := int64(math.MaxInt16); i < 1<<23; i = i + int64(rnd.Int31n(300)) { data := genData(i) expected := big.NewInt(i) received := Dec2BigInt(data) if expected.Cmp(received) != 0 { t.Fatalf("%d\nexpected:%s\nreceived:%s", i, expected, received) } _ = DecBigInt(data, received) if expected.Cmp(received) != 0 { t.Fatalf("%d\nexpected:%s\nreceived:%s", i, expected, received) } } }) t.Run("negative", func(t *testing.T) { rnd := rand.New(rand.NewSource(rand.Int63())) for i := int64(math.MinInt16); i > -1<<23; i = i - int64(rnd.Int31n(300)) { data := genData(i) expected := big.NewInt(i) received := Dec2BigInt(data) if expected.Cmp(received) != 0 { t.Fatalf("%d\nexpected:%s\nreceived:%s", i, expected, received) } _ = DecBigInt(data, received) if expected.Cmp(received) != 0 { t.Fatalf("%d\nexpected:%s\nreceived:%s", i, expected, received) } } }) } ================================================ FILE: serialization/varint/unmarshal_custom.go ================================================ package varint import ( "fmt" "reflect" "strconv" ) func DecReflect(p []byte, v reflect.Value) error { if v.IsNil() { return fmt.Errorf("failed to unmarshal varint: can not unmarshal into nil reference (%T)(%#[1]v)", v.Interface()) } switch v = v.Elem(); v.Kind() { case reflect.Int8: return decReflectInt8(p, v) case reflect.Int16: return decReflectInt16(p, v) case reflect.Int32: return decReflectInt32(p, v) case reflect.Int64, reflect.Int: return decReflectInts(p, v) case reflect.Uint8: return decReflectUint8(p, v) case reflect.Uint16: return decReflectUint16(p, v) case reflect.Uint32: return decReflectUint32(p, v) case reflect.Uint64, reflect.Uint: return decReflectUints(p, v) case reflect.String: return decReflectString(p, v) default: return fmt.Errorf("failed to unmarshal varint: unsupported value type (%T)(%#[1]v), supported types: %s", v.Interface(), supportedTypes) } } func decReflectInt8(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetInt(0) case 1: v.SetInt(dec1toInt64(p)) default: return fmt.Errorf("failed to unmarshal varint: to unmarshal into %T, the data value should be in the int8 range", v.Interface()) } return nil } func decReflectInt16(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetInt(0) return nil case 1: v.SetInt(dec1toInt64(p)) return nil case 2: v.SetInt(dec2toInt64(p)) default: return fmt.Errorf("failed to unmarshal varint: to unmarshal into %T, the data value should be in the int16 range", v.Interface()) } return errBrokenData(p) } func decReflectInt32(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetInt(0) return nil case 1: v.SetInt(dec1toInt64(p)) return nil case 2: v.SetInt(dec2toInt64(p)) case 3: v.SetInt(dec3toInt64(p)) case 4: v.SetInt(dec4toInt64(p)) default: return fmt.Errorf("failed to unmarshal varint: to unmarshal into %T, the data value should be in the uint32 range", v.Interface()) } return errBrokenData(p) } func decReflectInts(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetInt(0) return nil case 1: v.SetInt(dec1toInt64(p)) return nil case 2: v.SetInt(dec2toInt64(p)) case 3: v.SetInt(dec3toInt64(p)) case 4: v.SetInt(dec4toInt64(p)) case 5: v.SetInt(dec5toInt64(p)) case 6: v.SetInt(dec6toInt64(p)) case 7: v.SetInt(dec7toInt64(p)) case 8: v.SetInt(dec8toInt64(p)) default: return fmt.Errorf("failed to unmarshal varint: to unmarshal into %T, the data value should be in the int64 range", v.Interface()) } return errBrokenData(p) } func decReflectUint8(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetUint(0) return nil case 1: v.SetUint(dec1toUint64(p)) return nil case 2: if p[0] == 0 { v.SetUint(dec2toUint64(p)) } else { return fmt.Errorf("failed to unmarshal varint: to unmarshal into %T, the data value should be in the uint8 range", v.Interface()) } default: return fmt.Errorf("failed to unmarshal varint: to unmarshal into %T, the data value should be in the uint8 range", v.Interface()) } return errBrokenData(p) } func decReflectUint16(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetUint(0) return nil case 1: v.SetUint(dec1toUint64(p)) return nil case 2: v.SetUint(dec2toUint64(p)) case 3: if p[0] == 0 { v.SetUint(dec3toUint64(p)) } else { return fmt.Errorf("failed to unmarshal varint: to unmarshal into %T, the data value should be in the uint16 range", v.Interface()) } default: return fmt.Errorf("failed to unmarshal varint: to unmarshal into %T, the data value should be in the uint16 range", v.Interface()) } return errBrokenData(p) } func decReflectUint32(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetUint(0) return nil case 1: v.SetUint(dec1toUint64(p)) return nil case 2: v.SetUint(dec2toUint64(p)) case 3: v.SetUint(dec3toUint64(p)) case 4: v.SetUint(dec4toUint64(p)) case 5: if p[0] == 0 { v.SetUint(dec5toUint64(p)) } else { return fmt.Errorf("failed to unmarshal varint: to unmarshal into %T, the data value should be in the uint32 range", v.Interface()) } default: return fmt.Errorf("failed to unmarshal varint: to unmarshal into %T, the data value should be in the uint32 range", v.Interface()) } return errBrokenData(p) } func decReflectUints(p []byte, v reflect.Value) error { switch len(p) { case 0: v.SetUint(0) return nil case 1: v.SetUint(dec1toUint64(p)) return nil case 2: v.SetUint(dec2toUint64(p)) case 3: v.SetUint(dec3toUint64(p)) case 4: v.SetUint(dec4toUint64(p)) case 5: v.SetUint(dec5toUint64(p)) case 6: v.SetUint(dec6toUint64(p)) case 7: v.SetUint(dec7toUint64(p)) case 8: v.SetUint(dec8toUint64(p)) case 9: if p[0] == 0 { v.SetUint(dec9toUint64(p)) } else { return fmt.Errorf("failed to unmarshal varint: to unmarshal into %T, the data value should be in the uint64 range", v.Interface()) } default: return fmt.Errorf("failed to unmarshal varint: to unmarshal into %T, the data value should be in the uint64 range", v.Interface()) } return errBrokenData(p) } func decReflectString(p []byte, v reflect.Value) error { switch len(p) { case 0: if p == nil { v.SetString("") } else { v.SetString("0") } return nil case 1: v.SetString(strconv.FormatInt(dec1toInt64(p), 10)) return nil case 2: v.SetString(strconv.FormatInt(dec2toInt64(p), 10)) case 3: v.SetString(strconv.FormatInt(dec3toInt64(p), 10)) case 4: v.SetString(strconv.FormatInt(dec4toInt64(p), 10)) case 5: v.SetString(strconv.FormatInt(dec5toInt64(p), 10)) case 6: v.SetString(strconv.FormatInt(dec6toInt64(p), 10)) case 7: v.SetString(strconv.FormatInt(dec7toInt64(p), 10)) case 8: v.SetString(strconv.FormatInt(dec8toInt64(p), 10)) default: v.SetString(Dec2BigInt(p).String()) } return errBrokenData(p) } func DecReflectR(p []byte, v reflect.Value) error { if v.IsNil() { return fmt.Errorf("failed to unmarshal bigint: can not unmarshal into nil reference (%T)(%[1]v)", v.Interface()) } switch v.Type().Elem().Elem().Kind() { case reflect.Int8: return decReflectInt8R(p, v) case reflect.Int16: return decReflectInt16R(p, v) case reflect.Int32: return decReflectInt32R(p, v) case reflect.Int64, reflect.Int: return decReflectIntsR(p, v) case reflect.Uint8: return decReflectUint8R(p, v) case reflect.Uint16: return decReflectUint16R(p, v) case reflect.Uint32: return decReflectUint32R(p, v) case reflect.Uint64, reflect.Uint: return decReflectUintsR(p, v) case reflect.String: return decReflectStringR(p, v) default: return fmt.Errorf("failed to unmarshal bigint: unsupported value type (%T)(%[1]v), supported types: %s", v.Interface(), supportedTypes) } } func decReflectNullableR(p []byte, v reflect.Value) reflect.Value { if p == nil { return reflect.Zero(v.Elem().Type()) } return reflect.New(v.Type().Elem().Elem()) } func decReflectInt8R(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) case 1: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetInt(dec1toInt64(p)) v.Elem().Set(newVal) default: return fmt.Errorf("failed to unmarshal varint: to unmarshal into %T, the data value should be in the int8 range", v.Interface()) } return nil } func decReflectInt16R(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) return nil case 1: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetInt(dec1toInt64(p)) v.Elem().Set(newVal) return nil case 2: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetInt(dec2toInt64(p)) v.Elem().Set(newVal) default: return fmt.Errorf("failed to unmarshal varint: to unmarshal into %T, the data value should be in the int16 range", v.Interface()) } return errBrokenData(p) } func decReflectInt32R(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) return nil case 1: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetInt(dec1toInt64(p)) v.Elem().Set(newVal) return nil case 2: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetInt(dec2toInt64(p)) v.Elem().Set(newVal) case 3: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetInt(dec3toInt64(p)) v.Elem().Set(newVal) case 4: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetInt(dec4toInt64(p)) v.Elem().Set(newVal) default: return fmt.Errorf("failed to unmarshal varint: to unmarshal into %T, the data value should be in the int32 range", v.Interface()) } return errBrokenData(p) } func decReflectIntsR(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) return nil case 1: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetInt(dec1toInt64(p)) v.Elem().Set(newVal) return nil case 2: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetInt(dec2toInt64(p)) v.Elem().Set(newVal) case 3: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetInt(dec3toInt64(p)) v.Elem().Set(newVal) case 4: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetInt(dec4toInt64(p)) v.Elem().Set(newVal) case 5: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetInt(dec5toInt64(p)) v.Elem().Set(newVal) case 6: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetInt(dec6toInt64(p)) v.Elem().Set(newVal) case 7: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetInt(dec7toInt64(p)) v.Elem().Set(newVal) case 8: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetInt(dec8toInt64(p)) v.Elem().Set(newVal) default: return fmt.Errorf("failed to unmarshal varint: to unmarshal into %T, the data value should be in the int64 range", v.Interface()) } return errBrokenData(p) } func decReflectUint8R(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) return nil case 1: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetUint(dec1toUint64(p)) v.Elem().Set(newVal) return nil case 2: if p[0] == 0 { newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetUint(dec2toUint64(p)) v.Elem().Set(newVal) } else { return fmt.Errorf("failed to unmarshal varint: to unmarshal into %T, the data value should be in the uint8 range", v.Interface()) } default: return fmt.Errorf("failed to unmarshal varint: to unmarshal into %T, the data value should be in the uint8 range", v.Interface()) } return errBrokenData(p) } func decReflectUint16R(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) return nil case 1: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetUint(dec1toUint64(p)) v.Elem().Set(newVal) return nil case 2: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetUint(dec2toUint64(p)) v.Elem().Set(newVal) case 3: if p[0] == 0 { newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetUint(dec3toUint64(p)) v.Elem().Set(newVal) } else { return fmt.Errorf("failed to unmarshal varint: to unmarshal into %T, the data value should be in the uint16 range", v.Interface()) } default: return fmt.Errorf("failed to unmarshal varint: to unmarshal into %T, the data value should be in the uint16 range", v.Interface()) } return errBrokenData(p) } func decReflectUint32R(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) return nil case 1: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetUint(dec1toUint64(p)) v.Elem().Set(newVal) return nil case 2: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetUint(dec2toUint64(p)) v.Elem().Set(newVal) case 3: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetUint(dec3toUint64(p)) v.Elem().Set(newVal) case 4: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetUint(dec4toUint64(p)) v.Elem().Set(newVal) case 5: if p[0] == 0 { newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetUint(dec5toUint64(p)) v.Elem().Set(newVal) } else { return fmt.Errorf("failed to unmarshal varint: to unmarshal into %T, the data value should be in the uint32 range", v.Interface()) } default: return fmt.Errorf("failed to unmarshal varint: to unmarshal into %T, the data value should be in the uint32 range", v.Interface()) } return errBrokenData(p) } func decReflectUintsR(p []byte, v reflect.Value) error { switch len(p) { case 0: v.Elem().Set(decReflectNullableR(p, v)) return nil case 1: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetUint(dec1toUint64(p)) v.Elem().Set(newVal) return nil case 2: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetUint(dec2toUint64(p)) v.Elem().Set(newVal) case 3: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetUint(dec3toUint64(p)) v.Elem().Set(newVal) case 4: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetUint(dec4toUint64(p)) v.Elem().Set(newVal) case 5: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetUint(dec5toUint64(p)) v.Elem().Set(newVal) case 6: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetUint(dec6toUint64(p)) v.Elem().Set(newVal) case 7: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetUint(dec7toUint64(p)) v.Elem().Set(newVal) case 8: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetUint(dec8toUint64(p)) v.Elem().Set(newVal) case 9: if p[0] == 0 { newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetUint(dec9toUint64(p)) v.Elem().Set(newVal) } else { return fmt.Errorf("failed to unmarshal varint: to unmarshal into %T, the data value should be in the uint64 range", v.Interface()) } default: return fmt.Errorf("failed to unmarshal varint: to unmarshal into %T, the data value should be in the uint64 range", v.Interface()) } return errBrokenData(p) } func decReflectStringR(p []byte, v reflect.Value) error { switch len(p) { case 0: var val reflect.Value if p == nil { val = reflect.Zero(v.Type().Elem()) } else { val = reflect.New(v.Type().Elem().Elem()) val.Elem().SetString("0") } v.Elem().Set(val) return nil case 1: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetString(strconv.FormatInt(dec1toInt64(p), 10)) v.Elem().Set(newVal) return nil case 2: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetString(strconv.FormatInt(dec2toInt64(p), 10)) v.Elem().Set(newVal) case 3: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetString(strconv.FormatInt(dec3toInt64(p), 10)) v.Elem().Set(newVal) case 4: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetString(strconv.FormatInt(dec4toInt64(p), 10)) v.Elem().Set(newVal) case 5: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetString(strconv.FormatInt(dec5toInt64(p), 10)) v.Elem().Set(newVal) case 6: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetString(strconv.FormatInt(dec6toInt64(p), 10)) v.Elem().Set(newVal) case 7: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetString(strconv.FormatInt(dec7toInt64(p), 10)) v.Elem().Set(newVal) case 8: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetString(strconv.FormatInt(dec8toInt64(p), 10)) v.Elem().Set(newVal) default: newVal := reflect.New(v.Type().Elem().Elem()) newVal.Elem().SetString(Dec2BigInt(p).String()) v.Elem().Set(newVal) } return errBrokenData(p) } ================================================ FILE: serialization/varint/unmarshal_ints.go ================================================ package varint import ( "fmt" ) const ( negInt16s8 = int16(-1) << 8 negInt32s8 = int32(-1) << 8 negInt32s16 = int32(-1) << 16 negInt32s24 = int32(-1) << 24 negInt64s8 = int64(-1) << 8 negInt64s16 = int64(-1) << 16 negInt64s24 = int64(-1) << 24 negInt64s32 = int64(-1) << 32 negInt64s40 = int64(-1) << 40 negInt64s48 = int64(-1) << 48 negInt64s56 = int64(-1) << 56 negIntS8 = int(-1) << 8 negIntS16 = int(-1) << 16 negIntS24 = int(-1) << 24 negIntS32 = int(-1) << 32 negIntS40 = int(-1) << 40 negIntS48 = int(-1) << 48 negIntS56 = int(-1) << 56 ) func DecInt8(p []byte, v *int8) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 case 1: *v = dec1toInt8(p) default: return fmt.Errorf("failed to unmarshal varint: to unmarshal into int8, the data value should be in the int8 range") } return nil } func DecInt8R(p []byte, v **int8) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(int8) } case 1: val := dec1toInt8(p) *v = &val default: return fmt.Errorf("failed to unmarshal varint: to unmarshal into int8, the data value should be in the int8 range") } return nil } func DecInt16(p []byte, v *int16) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 return nil case 1: *v = dec1toInt16(p) return nil case 2: *v = dec2toInt16(p) default: return fmt.Errorf("failed to unmarshal varint: to unmarshal into int16, the data value should be in the int16 range") } return errBrokenData(p) } func DecInt16R(p []byte, v **int16) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(int16) } return nil case 1: val := dec1toInt16(p) *v = &val return nil case 2: val := dec2toInt16(p) *v = &val default: return fmt.Errorf("failed to unmarshal varint: to unmarshal into int16, the data value should be in the int16 range") } return errBrokenData(p) } func DecInt32(p []byte, v *int32) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 return nil case 1: *v = dec1toInt32(p) return nil case 2: *v = dec2toInt32(p) case 3: *v = dec3toInt32(p) case 4: *v = dec4toInt32(p) default: return fmt.Errorf("failed to unmarshal varint: to unmarshal into int32, the data value should be in the int32 range") } return errBrokenData(p) } func DecInt32R(p []byte, v **int32) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(int32) } return nil case 1: val := dec1toInt32(p) *v = &val return nil case 2: val := dec2toInt32(p) *v = &val case 3: val := dec3toInt32(p) *v = &val case 4: val := dec4toInt32(p) *v = &val default: return fmt.Errorf("failed to unmarshal varint: to unmarshal into int32, the data value should be in the int32 range") } return errBrokenData(p) } func DecInt64(p []byte, v *int64) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 return nil case 1: *v = dec1toInt64(p) return nil case 2: *v = dec2toInt64(p) case 3: *v = dec3toInt64(p) case 4: *v = dec4toInt64(p) case 5: *v = dec5toInt64(p) case 6: *v = dec6toInt64(p) case 7: *v = dec7toInt64(p) case 8: *v = dec8toInt64(p) default: return fmt.Errorf("failed to unmarshal varint: to unmarshal into int64, the data value should be in the int64 range") } return errBrokenData(p) } func DecInt64R(p []byte, v **int64) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(int64) } return nil case 1: val := dec1toInt64(p) *v = &val return nil case 2: val := dec2toInt64(p) *v = &val case 3: val := dec3toInt64(p) *v = &val case 4: val := dec4toInt64(p) *v = &val case 5: val := dec5toInt64(p) *v = &val case 6: val := dec6toInt64(p) *v = &val case 7: val := dec7toInt64(p) *v = &val case 8: val := dec8toInt64(p) *v = &val default: return fmt.Errorf("failed to unmarshal varint: to unmarshal into int64, the data value should be in the int64 range") } return errBrokenData(p) } func DecInt(p []byte, v *int) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 return nil case 1: *v = dec1toInt(p) return nil case 2: *v = dec2toInt(p) case 3: *v = dec3toInt(p) case 4: *v = dec4toInt(p) case 5: *v = dec5toInt(p) case 6: *v = dec6toInt(p) case 7: *v = dec7toInt(p) case 8: *v = dec8toInt(p) default: return fmt.Errorf("failed to unmarshal varint: to unmarshal into int, the data value should be in the int range") } return errBrokenData(p) } func DecIntR(p []byte, v **int) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(int) } return nil case 1: val := dec1toInt(p) *v = &val return nil case 2: val := dec2toInt(p) *v = &val case 3: val := dec3toInt(p) *v = &val case 4: val := dec4toInt(p) *v = &val case 5: val := dec5toInt(p) *v = &val case 6: val := dec6toInt(p) *v = &val case 7: val := dec7toInt(p) *v = &val case 8: val := dec8toInt(p) *v = &val default: return fmt.Errorf("failed to unmarshal varint: to unmarshal into int, the data value should be in the int range") } return errBrokenData(p) } func dec1toInt8(p []byte) int8 { return int8(p[0]) } func dec1toInt16(p []byte) int16 { if p[0] > 127 { return negInt16s8 | int16(p[0]) } return int16(p[0]) } func dec1toInt32(p []byte) int32 { if p[0] > 127 { return negInt32s8 | int32(p[0]) } return int32(p[0]) } func dec1toInt64(p []byte) int64 { if p[0] > 127 { return negInt64s8 | int64(p[0]) } return int64(p[0]) } func dec1toInt(p []byte) int { if p[0] > 127 { return negIntS8 | int(p[0]) } return int(p[0]) } func dec2toInt16(p []byte) int16 { return int16(p[0])<<8 | int16(p[1]) } func dec2toInt32(p []byte) int32 { if p[0] > 127 { return negInt32s16 | int32(p[0])<<8 | int32(p[1]) } return int32(p[0])<<8 | int32(p[1]) } func dec2toInt64(p []byte) int64 { if p[0] > 127 { return negInt64s16 | int64(p[0])<<8 | int64(p[1]) } return int64(p[0])<<8 | int64(p[1]) } func dec2toInt(p []byte) int { if p[0] > 127 { return negIntS16 | int(p[0])<<8 | int(p[1]) } return int(p[0])<<8 | int(p[1]) } func dec3toInt32(p []byte) int32 { if p[0] > 127 { return negInt32s24 | int32(p[0])<<16 | int32(p[1])<<8 | int32(p[2]) } return int32(p[0])<<16 | int32(p[1])<<8 | int32(p[2]) } func dec3toInt64(p []byte) int64 { if p[0] > 127 { return negInt64s24 | int64(p[0])<<16 | int64(p[1])<<8 | int64(p[2]) } return int64(p[0])<<16 | int64(p[1])<<8 | int64(p[2]) } func dec3toInt(p []byte) int { if p[0] > 127 { return negIntS24 | int(p[0])<<16 | int(p[1])<<8 | int(p[2]) } return int(p[0])<<16 | int(p[1])<<8 | int(p[2]) } func dec4toInt32(p []byte) int32 { return int32(p[0])<<24 | int32(p[1])<<16 | int32(p[2])<<8 | int32(p[3]) } func dec4toInt64(p []byte) int64 { if p[0] > 127 { return negInt64s32 | int64(p[0])<<24 | int64(p[1])<<16 | int64(p[2])<<8 | int64(p[3]) } return int64(p[0])<<24 | int64(p[1])<<16 | int64(p[2])<<8 | int64(p[3]) } func dec4toInt(p []byte) int { if p[0] > 127 { return negIntS32 | int(p[0])<<24 | int(p[1])<<16 | int(p[2])<<8 | int(p[3]) } return int(p[0])<<24 | int(p[1])<<16 | int(p[2])<<8 | int(p[3]) } func dec5toInt64(p []byte) int64 { if p[0] > 127 { return negInt64s40 | int64(p[0])<<32 | int64(p[1])<<24 | int64(p[2])<<16 | int64(p[3])<<8 | int64(p[4]) } return int64(p[0])<<32 | int64(p[1])<<24 | int64(p[2])<<16 | int64(p[3])<<8 | int64(p[4]) } func dec5toInt(p []byte) int { if p[0] > 127 { return negIntS40 | int(p[0])<<32 | int(p[1])<<24 | int(p[2])<<16 | int(p[3])<<8 | int(p[4]) } return int(p[0])<<32 | int(p[1])<<24 | int(p[2])<<16 | int(p[3])<<8 | int(p[4]) } func dec6toInt64(p []byte) int64 { if p[0] > 127 { return negInt64s48 | int64(p[0])<<40 | int64(p[1])<<32 | int64(p[2])<<24 | int64(p[3])<<16 | int64(p[4])<<8 | int64(p[5]) } return int64(p[0])<<40 | int64(p[1])<<32 | int64(p[2])<<24 | int64(p[3])<<16 | int64(p[4])<<8 | int64(p[5]) } func dec6toInt(p []byte) int { if p[0] > 127 { return negIntS48 | int(p[0])<<40 | int(p[1])<<32 | int(p[2])<<24 | int(p[3])<<16 | int(p[4])<<8 | int(p[5]) } return int(p[0])<<40 | int(p[1])<<32 | int(p[2])<<24 | int(p[3])<<16 | int(p[4])<<8 | int(p[5]) } func dec7toInt64(p []byte) int64 { if p[0] > 127 { return negInt64s56 | int64(p[0])<<48 | int64(p[1])<<40 | int64(p[2])<<32 | int64(p[3])<<24 | int64(p[4])<<16 | int64(p[5])<<8 | int64(p[6]) } return int64(p[0])<<48 | int64(p[1])<<40 | int64(p[2])<<32 | int64(p[3])<<24 | int64(p[4])<<16 | int64(p[5])<<8 | int64(p[6]) } func dec7toInt(p []byte) int { if p[0] > 127 { return negIntS56 | int(p[0])<<48 | int(p[1])<<40 | int(p[2])<<32 | int(p[3])<<24 | int(p[4])<<16 | int(p[5])<<8 | int(p[6]) } return int(p[0])<<48 | int(p[1])<<40 | int(p[2])<<32 | int(p[3])<<24 | int(p[4])<<16 | int(p[5])<<8 | int(p[6]) } func dec8toInt64(p []byte) int64 { return int64(p[0])<<56 | int64(p[1])<<48 | int64(p[2])<<40 | int64(p[3])<<32 | int64(p[4])<<24 | int64(p[5])<<16 | int64(p[6])<<8 | int64(p[7]) } func dec8toInt(p []byte) int { return int(p[0])<<56 | int(p[1])<<48 | int(p[2])<<40 | int(p[3])<<32 | int(p[4])<<24 | int(p[5])<<16 | int(p[6])<<8 | int(p[7]) } ================================================ FILE: serialization/varint/unmarshal_uints.go ================================================ package varint import ( "fmt" ) func DecUint8(p []byte, v *uint8) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 return nil case 1: *v = dec1toUint8(p) return nil case 2: if p[0] != 0 { return fmt.Errorf("failed to unmarshal varint: to unmarshal into uint8, the data value should be in the uint8 range") } *v = dec2toUint8(p) default: return fmt.Errorf("failed to unmarshal varint: to unmarshal into uint8, the data value should be in the uint8 range") } return errBrokenData(p) } func DecUint8R(p []byte, v **uint8) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(uint8) } return nil case 1: val := dec1toUint8(p) *v = &val return nil case 2: if p[0] != 0 { return fmt.Errorf("failed to unmarshal varint: to unmarshal into uint8, the data value should be in the uint8 range") } val := dec2toUint8(p) *v = &val default: return fmt.Errorf("failed to unmarshal varint: to unmarshal into uint8, the data value should be in the uint8 range") } return errBrokenData(p) } func DecUint16(p []byte, v *uint16) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 return nil case 1: *v = dec1toUint16(p) return nil case 2: *v = dec2toUint16(p) case 3: if p[0] != 0 { return fmt.Errorf("failed to unmarshal varint: to unmarshal into uint16, the data value should be in the uint16 range") } *v = dec3toUint16(p) default: return fmt.Errorf("failed to unmarshal varint: to unmarshal into uint16, the data value should be in the uint16 range") } return errBrokenData(p) } func DecUint16R(p []byte, v **uint16) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(uint16) } return nil case 1: val := dec1toUint16(p) *v = &val return nil case 2: val := dec2toUint16(p) *v = &val case 3: if p[0] != 0 { return fmt.Errorf("failed to unmarshal varint: to unmarshal into uint16, the data value should be in the uint16 range") } val := dec3toUint16(p) *v = &val default: return fmt.Errorf("failed to unmarshal varint: to unmarshal into uint16, the data value should be in the uint16 range") } return errBrokenData(p) } func DecUint32(p []byte, v *uint32) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 return nil case 1: *v = dec1toUint32(p) return nil case 2: *v = dec2toUint32(p) case 3: *v = dec3toUint32(p) case 4: *v = dec4toUint32(p) case 5: if p[0] != 0 { return fmt.Errorf("failed to unmarshal varint: to unmarshal into uint32, the data value should be in the uint32 range") } *v = dec5toUint32(p) default: return fmt.Errorf("failed to unmarshal varint: to unmarshal into uint32, the data value should be in the uint32 range") } return errBrokenData(p) } func DecUint32R(p []byte, v **uint32) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(uint32) } return nil case 1: val := dec1toUint32(p) *v = &val return nil case 2: val := dec2toUint32(p) *v = &val case 3: val := dec3toUint32(p) *v = &val case 4: val := dec4toUint32(p) *v = &val case 5: if p[0] != 0 { return fmt.Errorf("failed to unmarshal varint: to unmarshal into uint32, the data value should be in the uint32 range") } val := dec5toUint32(p) *v = &val default: return fmt.Errorf("failed to unmarshal varint: to unmarshal into uint32, the data value should be in the uint32 range") } return errBrokenData(p) } func DecUint64(p []byte, v *uint64) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 return nil case 1: *v = dec1toUint64(p) return nil case 2: *v = dec2toUint64(p) case 3: *v = dec3toUint64(p) case 4: *v = dec4toUint64(p) case 5: *v = dec5toUint64(p) case 6: *v = dec6toUint64(p) case 7: *v = dec7toUint64(p) case 8: *v = dec8toUint64(p) case 9: if p[0] != 0 { return fmt.Errorf("failed to unmarshal varint: to unmarshal into uint64, the data value should be in the uint64 range") } *v = dec9toUint64(p) default: return fmt.Errorf("failed to unmarshal varint: to unmarshal into uint64, the data value should be in the uint64 range") } return errBrokenData(p) } func DecUint64R(p []byte, v **uint64) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(uint64) } return nil case 1: val := dec1toUint64(p) *v = &val return nil case 2: val := dec2toUint64(p) *v = &val case 3: val := dec3toUint64(p) *v = &val case 4: val := dec4toUint64(p) *v = &val case 5: val := dec5toUint64(p) *v = &val case 6: val := dec6toUint64(p) *v = &val case 7: val := dec7toUint64(p) *v = &val case 8: val := dec8toUint64(p) *v = &val case 9: if p[0] != 0 { return fmt.Errorf("failed to unmarshal varint: to unmarshal into uint64, the data value should be in the uint64 range") } val := dec9toUint64(p) *v = &val default: return fmt.Errorf("failed to unmarshal varint: to unmarshal into uint64, the data value should be in the uint64 range") } return errBrokenData(p) } func DecUint(p []byte, v *uint) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: *v = 0 return nil case 1: *v = dec1toUint(p) return nil case 2: *v = dec2toUint(p) case 3: *v = dec3toUint(p) case 4: *v = dec4toUint(p) case 5: *v = dec5toUint(p) case 6: *v = dec6toUint(p) case 7: *v = dec7toUint(p) case 8: *v = dec8toUint(p) case 9: if p[0] != 0 { return fmt.Errorf("failed to unmarshal varint: to unmarshal into uint, the data value should be in the uint range") } *v = dec9toUint(p) default: return fmt.Errorf("failed to unmarshal varint: to unmarshal into uint, the data value should be in the uint range") } return errBrokenData(p) } func DecUintR(p []byte, v **uint) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { *v = new(uint) } return nil case 1: val := dec1toUint(p) *v = &val return nil case 2: val := dec2toUint(p) *v = &val case 3: val := dec3toUint(p) *v = &val case 4: val := dec4toUint(p) *v = &val case 5: val := dec5toUint(p) *v = &val case 6: val := dec6toUint(p) *v = &val case 7: val := dec7toUint(p) *v = &val case 8: val := dec8toUint(p) *v = &val case 9: if p[0] != 0 { return fmt.Errorf("failed to unmarshal varint: to unmarshal into uint, the data value should be in the uint range") } val := dec9toUint(p) *v = &val default: return fmt.Errorf("failed to unmarshal varint: to unmarshal into uint, the data value should be in the uint range") } return errBrokenData(p) } func dec1toUint8(p []byte) uint8 { return p[0] } func dec1toUint16(p []byte) uint16 { return uint16(p[0]) } func dec1toUint32(p []byte) uint32 { return uint32(p[0]) } func dec1toUint64(p []byte) uint64 { return uint64(p[0]) } func dec1toUint(p []byte) uint { return uint(p[0]) } func dec2toUint8(p []byte) uint8 { return p[1] } func dec2toUint16(p []byte) uint16 { return uint16(p[0])<<8 | uint16(p[1]) } func dec2toUint32(p []byte) uint32 { return uint32(p[0])<<8 | uint32(p[1]) } func dec2toUint64(p []byte) uint64 { return uint64(p[0])<<8 | uint64(p[1]) } func dec2toUint(p []byte) uint { return uint(p[0])<<8 | uint(p[1]) } func dec3toUint16(p []byte) uint16 { return uint16(p[1])<<8 | uint16(p[2]) } func dec3toUint32(p []byte) uint32 { return uint32(p[0])<<16 | uint32(p[1])<<8 | uint32(p[2]) } func dec3toUint64(p []byte) uint64 { return uint64(p[0])<<16 | uint64(p[1])<<8 | uint64(p[2]) } func dec3toUint(p []byte) uint { return uint(p[0])<<16 | uint(p[1])<<8 | uint(p[2]) } func dec4toUint32(p []byte) uint32 { return uint32(p[0])<<24 | uint32(p[1])<<16 | uint32(p[2])<<8 | uint32(p[3]) } func dec4toUint64(p []byte) uint64 { return uint64(p[0])<<24 | uint64(p[1])<<16 | uint64(p[2])<<8 | uint64(p[3]) } func dec4toUint(p []byte) uint { return uint(p[0])<<24 | uint(p[1])<<16 | uint(p[2])<<8 | uint(p[3]) } func dec5toUint32(p []byte) uint32 { return uint32(p[1])<<24 | uint32(p[2])<<16 | uint32(p[3])<<8 | uint32(p[4]) } func dec5toUint64(p []byte) uint64 { return uint64(p[0])<<32 | uint64(p[1])<<24 | uint64(p[2])<<16 | uint64(p[3])<<8 | uint64(p[4]) } func dec5toUint(p []byte) uint { return uint(p[0])<<32 | uint(p[1])<<24 | uint(p[2])<<16 | uint(p[3])<<8 | uint(p[4]) } func dec6toUint64(p []byte) uint64 { return uint64(p[0])<<40 | uint64(p[1])<<32 | uint64(p[2])<<24 | uint64(p[3])<<16 | uint64(p[4])<<8 | uint64(p[5]) } func dec6toUint(p []byte) uint { return uint(p[0])<<40 | uint(p[1])<<32 | uint(p[2])<<24 | uint(p[3])<<16 | uint(p[4])<<8 | uint(p[5]) } func dec7toUint64(p []byte) uint64 { return uint64(p[0])<<48 | uint64(p[1])<<40 | uint64(p[2])<<32 | uint64(p[3])<<24 | uint64(p[4])<<16 | uint64(p[5])<<8 | uint64(p[6]) } func dec7toUint(p []byte) uint { return uint(p[0])<<48 | uint(p[1])<<40 | uint(p[2])<<32 | uint(p[3])<<24 | uint(p[4])<<16 | uint(p[5])<<8 | uint(p[6]) } func dec8toUint64(p []byte) uint64 { return uint64(p[0])<<56 | uint64(p[1])<<48 | uint64(p[2])<<40 | uint64(p[3])<<32 | uint64(p[4])<<24 | uint64(p[5])<<16 | uint64(p[6])<<8 | uint64(p[7]) } func dec8toUint(p []byte) uint { return uint(p[0])<<56 | uint(p[1])<<48 | uint(p[2])<<40 | uint(p[3])<<32 | uint(p[4])<<24 | uint(p[5])<<16 | uint(p[6])<<8 | uint(p[7]) } func dec9toUint64(p []byte) uint64 { return uint64(p[1])<<56 | uint64(p[2])<<48 | uint64(p[3])<<40 | uint64(p[4])<<32 | uint64(p[5])<<24 | uint64(p[6])<<16 | uint64(p[7])<<8 | uint64(p[8]) } func dec9toUint(p []byte) uint { return uint(p[1])<<56 | uint(p[2])<<48 | uint(p[3])<<40 | uint(p[4])<<32 | uint(p[5])<<24 | uint(p[6])<<16 | uint(p[7])<<8 | uint(p[8]) } ================================================ FILE: serialization/varint/unmarshal_utils.go ================================================ package varint import ( "fmt" "math/big" "strconv" ) func errBrokenData(p []byte) error { if p[0] == 0 && p[1] <= 127 || p[0] == 255 && p[1] > 127 { return fmt.Errorf("failed to unmarshal varint: the data is broken") } return nil } func errNilReference(v any) error { return fmt.Errorf("failed to unmarshal varint: can not unmarshal into nil reference %#v)", v) } func DecString(p []byte, v *string) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = "" } else { *v = "0" } return nil case 1: *v = strconv.FormatInt(dec1toInt64(p), 10) return nil case 2: *v = strconv.FormatInt(dec2toInt64(p), 10) case 3: *v = strconv.FormatInt(dec3toInt64(p), 10) case 4: *v = strconv.FormatInt(dec4toInt64(p), 10) case 5: *v = strconv.FormatInt(dec5toInt64(p), 10) case 6: *v = strconv.FormatInt(dec6toInt64(p), 10) case 7: *v = strconv.FormatInt(dec7toInt64(p), 10) case 8: *v = strconv.FormatInt(dec8toInt64(p), 10) default: *v = Dec2BigInt(p).String() } return errBrokenData(p) } func DecStringR(p []byte, v **string) error { if v == nil { return errNilReference(v) } switch len(p) { case 0: if p == nil { *v = nil } else { val := "0" *v = &val } return nil case 1: val := strconv.FormatInt(dec1toInt64(p), 10) *v = &val return nil case 2: val := strconv.FormatInt(dec2toInt64(p), 10) *v = &val case 3: val := strconv.FormatInt(dec3toInt64(p), 10) *v = &val case 4: val := strconv.FormatInt(dec4toInt64(p), 10) *v = &val case 5: val := strconv.FormatInt(dec5toInt64(p), 10) *v = &val case 6: val := strconv.FormatInt(dec6toInt64(p), 10) *v = &val case 7: val := strconv.FormatInt(dec7toInt64(p), 10) *v = &val case 8: val := strconv.FormatInt(dec8toInt64(p), 10) *v = &val default: val := Dec2BigInt(p).String() *v = &val } return errBrokenData(p) } func DecBigInt(p []byte, v *big.Int) error { switch len(p) { case 0: v.SetInt64(0) return nil case 1: v.SetInt64(dec1toInt64(p)) return nil case 2: v.SetInt64(dec2toInt64(p)) case 3: v.SetInt64(dec3toInt64(p)) case 4: v.SetInt64(dec4toInt64(p)) case 5: v.SetInt64(dec5toInt64(p)) case 6: v.SetInt64(dec6toInt64(p)) case 7: v.SetInt64(dec7toInt64(p)) case 8: v.SetInt64(dec8toInt64(p)) default: dec2ToBigInt(p, v) } return errBrokenData(p) } func DecBigIntR(p []byte, v **big.Int) error { if p != nil { *v = big.NewInt(0) return DecBigInt(p, *v) } *v = nil return nil } // Dec2BigInt decode p to big.Int. Use for cases with len(p)>=2. // This function shared to use in unmarshal `decimal`. func Dec2BigInt(p []byte) *big.Int { // Positive range processing if p[0] <= 127 { return new(big.Int).SetBytes(p) } // negative range processing data := make([]byte, len(p)) copy(data, p) add := true for i := len(data) - 1; i >= 0; i-- { if !add { data[i] = 255 - data[i] } else { data[i] = 255 - data[i] + 1 if data[i] != 0 { add = false } } } return new(big.Int).Neg(new(big.Int).SetBytes(data)) } func dec2ToBigInt(p []byte, v *big.Int) { if p[0] <= 127 { // Positive range processing v.SetBytes(p) } else { // negative range processing data := make([]byte, len(p)) copy(data, p) add := true for i := len(data) - 1; i >= 0; i-- { if !add { data[i] = 255 - data[i] } else { data[i] = 255 - data[i] + 1 if data[i] != 0 { add = false } } } v.Set(new(big.Int).Neg(new(big.Int).SetBytes(data))) } } ================================================ FILE: session.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2012, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "bytes" "context" "errors" "fmt" "maps" "net" "slices" "strings" "sync" "sync/atomic" "time" "unicode" "unicode/utf8" "github.com/gocql/gocql/debounce" "github.com/gocql/gocql/events" "github.com/gocql/gocql/internal/debug" "github.com/gocql/gocql/internal/eventbus" "github.com/gocql/gocql/internal/lru" "github.com/gocql/gocql/tablets" ) // Session is the interface used by users to interact with the database. // // It's safe for concurrent use by multiple goroutines and a typical usage // scenario is to have one global session object to interact with the // whole Cassandra cluster. // // This type extends the Node interface by adding a convenient query builder // and automatically sets a default consistency level on all operations // that do not have a consistency level set. type Session struct { warningHandler WarningHandler queryObserver QueryObserver control controlConnection ctx context.Context logger StdLogger trace Tracer policy HostSelectionPolicy batchObserver BatchObserver connectObserver ConnectObserver frameObserver FrameHeaderObserver streamObserver StreamObserver initErr error nodeEvents *eventDebouncer stmtsLRU *preparedLRU hostSource *ringDescriber pool *policyConnPool ringRefresher *debounce.RefreshDebouncer readyCh chan struct{} executor *queryExecutor cancel context.CancelFunc schemaEvents *eventDebouncer metadataDescriber *metadataDescriber eventBus *eventbus.EventBus[events.Event] connCfg *ConnConfig clientRoutesHandler *ClientRoutesHandler routingKeyInfoCache routingKeyInfoLRU addressTranslator AddressTranslator cfg ClusterConfig prefetch float64 pageSize int mu sync.RWMutex sessionStateMu sync.RWMutex cons Consistency isClosing bool hasAggregatesAndFunctions bool useSystemSchema bool tabletsRoutingV1 bool isInitialized bool isClosed bool } var queryPool = &sync.Pool{ New: func() any { return &Query{ routingInfo: &queryRoutingInfo{}, metrics: &queryMetrics{m: make(map[UUID]*hostMetrics)}, refCount: 1, } }, } func resolveInitialEndpoints(resolver DNSResolver, addrs []string, defaultPort int, logger StdLogger) ([]*HostInfo, error) { var hosts []*HostInfo var errs []error for _, hostaddr := range addrs { resolvedHosts, err := resolveInitialEndpoint(resolver, hostaddr, defaultPort) if err != nil { err = fmt.Errorf("failed to resolve endpoint %q: %w", hostaddr, err) errs = append(errs, err) logger.Println(err.Error()) continue } hosts = append(hosts, resolvedHosts...) } if len(hosts) == 0 { return nil, fmt.Errorf("failed to resolve any of the provided hostnames: %w", errors.Join(errs...)) } return hosts, nil } func newSessionCommon(cfg ClusterConfig) (*Session, error) { if err := cfg.Validate(); err != nil { return nil, fmt.Errorf("gocql: unable to create session: cluster config validation failed: %v", err) } // TODO: we should take a context in here at some point ctx, cancel := context.WithCancel(context.TODO()) s := &Session{ cons: cfg.Consistency, prefetch: 0.25, cfg: cfg, pageSize: cfg.PageSize, stmtsLRU: &preparedLRU{lru: lru.New[stmtCacheKey](cfg.MaxPreparedStmts)}, connectObserver: cfg.ConnectObserver, ctx: ctx, cancel: cancel, logger: cfg.logger(), addressTranslator: cfg.AddressTranslator, readyCh: make(chan struct{}, 1), } if cfg.ClientRoutesConfig != nil { s.clientRoutesHandler = NewClientRoutesAddressTranslator(*cfg.ClientRoutesConfig, s.cfg.DNSResolver, s.cfg.SslOpts != nil, s.logger) s.addressTranslator = s.clientRoutesHandler } // Close created resources on error otherwise they'll leak var err error defer func() { if err != nil { s.Close() } }() s.metadataDescriber = newMetadataDescriber(s) s.eventBus = eventbus.New[events.Event](cfg.EventBusConfig, cfg.Logger) if err = s.eventBus.Start(); err != nil { return nil, fmt.Errorf("gocql: unable to create session: %v", err) } s.nodeEvents = newEventDebouncer("NodeEvents", s.handleNodeEvent, s.logger) s.schemaEvents = newEventDebouncer("SchemaEvents", s.handleSchemaEvent, s.logger) s.routingKeyInfoCache.lru = lru.New[string](cfg.MaxRoutingKeyInfo) s.hostSource = &ringDescriber{cfg: &s.cfg, logger: s.logger} s.ringRefresher = debounce.NewRefreshDebouncer(debounce.RingRefreshDebounceTime, func() error { return s.refreshRing() }) if cfg.PoolConfig.HostSelectionPolicy == nil { cfg.PoolConfig.HostSelectionPolicy = TokenAwareHostPolicy(RoundRobinHostPolicy()) } s.pool = cfg.PoolConfig.buildPool(s) s.policy = cfg.PoolConfig.HostSelectionPolicy s.policy.Init(s) s.executor = &queryExecutor{ pool: s.pool, policy: cfg.PoolConfig.HostSelectionPolicy, } s.queryObserver = cfg.QueryObserver s.batchObserver = cfg.BatchObserver s.connectObserver = cfg.ConnectObserver s.frameObserver = cfg.FrameHeaderObserver s.streamObserver = cfg.StreamObserver //Check the TLS Config before trying to connect to anything external connCfg, err := connConfig(&s.cfg) if err != nil { //TODO: Return a typed error return nil, fmt.Errorf("gocql: unable to create session: %v", err) } s.connCfg = connCfg if cfg.WarningsHandlerBuilder != nil { s.warningHandler = cfg.WarningsHandlerBuilder(s) } return s, nil } // NewSession wraps an existing Node. func NewSession(cfg ClusterConfig) (*Session, error) { s, err := newSessionCommon(cfg) if err != nil { return nil, err } if err = s.init(); err != nil { if err == ErrNoConnectionsStarted { //This error used to be generated inside NewSession & returned directly //Forward it on up to be backwards compatible return nil, ErrNoConnectionsStarted } else { // TODO(zariel): dont wrap this error in fmt.Errorf, return a typed error return nil, fmt.Errorf("gocql: unable to create session: %v", err) } } s.readyCh <- struct{}{} close(s.readyCh) return s, nil } func NewSessionNonBlocking(cfg ClusterConfig) (*Session, error) { s, err := newSessionCommon(cfg) if err != nil { return nil, err } go func() { if initErr := s.init(); initErr != nil { s.sessionStateMu.Lock() s.initErr = fmt.Errorf("gocql: unable to create session: %v", initErr) s.sessionStateMu.Unlock() } s.readyCh <- struct{}{} close(s.readyCh) }() return s, nil } // SubscribeToEvents adds a new subscriber to the event bus. // name: subscriber name // queueSize: buffer size for the subscriber events, when buffer is overflowed events are dropped // filter: optional filter function (can be nil to receive all events) // // Returns a Subscriber instance that provides access to events and a Stop method. func (s *Session) SubscribeToEvents(name string, queueSize int, filter eventbus.FilterFunc[events.Event]) *eventbus.Subscriber[events.Event] { return s.eventBus.Subscribe(name, queueSize, filter) } func (s *Session) init() error { if s.cfg.disableInit { return nil } hosts, err := resolveInitialEndpoints(s.cfg.DNSResolver, s.cfg.Hosts, s.cfg.Port, s.logger) if err != nil { return err } var partitioner string if !s.cfg.disableControlConn { s.control = createControlConn(s) reconnectionPolicy := s.cfg.InitialReconnectionPolicy for i := 0; i < reconnectionPolicy.GetMaxRetries(); i++ { if i != 0 { time.Sleep(reconnectionPolicy.GetInterval(i)) } if s.cfg.ProtoVersion == 0 { var proto int proto, err = s.control.discoverProtocol(hosts) if err != nil { err = fmt.Errorf("unable to discover protocol version: %w\n", err) if debug.Enabled { s.logger.Println(err.Error()) } continue } else if proto == 0 { return errors.New("unable to discovery protocol version") } // TODO(zariel): we really only need this in 1 place s.cfg.ProtoVersion = proto s.connCfg.ProtoVersion = proto } if err = s.control.connect(hosts); err != nil { err = fmt.Errorf("unable to create control connection: %w\n", err) if debug.Enabled { s.logger.Println(err.Error()) } continue } break } if err != nil { return fmt.Errorf("unable to connect to the cluster, last error: %w", err) } conn := s.control.getConn().conn.(*Conn) conn.mu.Lock() s.tabletsRoutingV1 = conn.isTabletSupported() conn.mu.Unlock() s.hostSource.setControlConn(s.control) if s.clientRoutesHandler != nil { if err = s.clientRoutesHandler.Initialize(s); err != nil { return fmt.Errorf("unable to initialize client routes handler: %w", err) } } if !s.cfg.DisableInitialHostLookup { var newHosts []*HostInfo newHosts, partitioner, err = s.hostSource.GetHostsFromSystem() if err != nil { return err } filteredHosts := make([]*HostInfo, 0, len(newHosts)) for _, host := range newHosts { if !s.cfg.filterHost(host) { filteredHosts = append(filteredHosts, host) } } hosts = filteredHosts } newer, _ := checkSystemSchema(s.control) s.useSystemSchema = newer defer conn.finalizeConnection() } else { // For testing purposes we populate host ids for _, host := range hosts { if host.hostId.IsEmpty() { host.hostId = MustRandomUUID() } } } if partitioner != "" { s.policy.SetPartitioner(partitioner) } hostMap := make(map[string]*HostInfo, len(hosts)) for _, host := range hosts { hostMap[host.HostID()] = host } hosts = hosts[:0] // each host will increment left and decrement it after connecting and once // there's none left, we'll close hostCh var left int64 // we will receive up to len(hostMap) of messages so create a buffer so we // don't end up stuck in a goroutine if we stopped listening connectedCh := make(chan struct{}, len(hostMap)) // we add one here because we don't want to end up closing hostCh until we're // done looping and the decerement code might be reached before we've looped // again atomic.AddInt64(&left, 1) for _, host := range hostMap { host := s.hostSource.addOrUpdate(host) if s.cfg.filterHost(host) { continue } atomic.AddInt64(&left, 1) go func() { s.pool.addHost(host) connectedCh <- struct{}{} // if there are no hosts left, then close the hostCh to unblock the loop // below if its still waiting if atomic.AddInt64(&left, -1) == 0 { close(connectedCh) } }() hosts = append(hosts, host) } // once we're done looping we subtract the one we initially added and check // to see if we should close if atomic.AddInt64(&left, -1) == 0 { close(connectedCh) } if s.cfg.disableControlConn { version := s.hostSource.getHostsList()[0].Version() s.useSystemSchema = version.AtLeast(3, 0, 0) s.hasAggregatesAndFunctions = version.AtLeast(2, 2, 0) } // before waiting for them to connect, add them all to the policy so we can // utilize efficiencies by calling AddHosts if the policy supports it type bulkAddHosts interface { AddHosts([]*HostInfo) } if v, ok := s.policy.(bulkAddHosts); ok { v.AddHosts(hosts) } else { for _, host := range hosts { s.policy.AddHost(host) } } readyPolicy, _ := s.policy.(ReadyPolicy) // now loop over connectedCh until it's closed (meaning we've connected to all) // or until the policy says we're ready for range connectedCh { if readyPolicy != nil && readyPolicy.Ready() { break } } // TODO(zariel): we probably dont need this any more as we verify that we // can connect to one of the endpoints supplied by using the control conn. // See if there are any connections in the pool if s.cfg.ReconnectInterval > 0 { go s.reconnectDownedHosts(s.cfg.ReconnectInterval) } if s.pool.Size() == 0 { return ErrNoConnectionsStarted } // Invoke KeyspaceChanged to let the policy cache the session keyspace // parameters. This is used by tokenAwareHostPolicy to discover replicas. if !s.cfg.disableControlConn && s.cfg.Keyspace != "" { s.policy.KeyspaceChanged(KeyspaceUpdateEvent{Keyspace: s.cfg.Keyspace}) } if err = s.policy.IsOperational(s); err != nil { return fmt.Errorf("gocql: unable to create session: %v", err) } s.sessionStateMu.Lock() s.isInitialized = true s.sessionStateMu.Unlock() return nil } // AwaitSchemaAgreement will wait until schema versions across all nodes in the // cluster are the same (as seen from the point of view of the control connection). // The maximum amount of time this takes is governed // by the MaxWaitSchemaAgreement setting in the configuration (default: 60s). // AwaitSchemaAgreement returns an error in case schema versions are not the same // after the timeout specified in MaxWaitSchemaAgreement elapses. func (s *Session) AwaitSchemaAgreement(ctx context.Context) error { if s.cfg.disableControlConn { return errNoControl } if err := s.Ready(); err != nil { return err } ch := s.control.getConn() return (&Iter{err: ch.conn.awaitSchemaAgreement(ctx)}).err } func (s *Session) reconnectDownedHosts(intv time.Duration) { reconnectTicker := time.NewTicker(intv) defer reconnectTicker.Stop() for { select { case <-reconnectTicker.C: hosts := s.hostSource.getHostsList() // Print session.hostSource for debug. if debug.Enabled { buf := bytes.NewBufferString("Session.hostSource:") for _, h := range hosts { buf.WriteString("[" + h.ConnectAddress().String() + ":" + h.State().String() + "]") } s.logger.Println(buf.String()) } for _, h := range hosts { if h.IsUp() { continue } // we let the pool call handleNodeConnected to change the host state s.pool.addHost(h) } case <-s.ctx.Done(): return } } } // SetConsistency sets the default consistency level for this session. This // setting can also be changed on a per-query basis and the default value // is Quorum. func (s *Session) SetConsistency(cons Consistency) { s.mu.Lock() s.cons = cons s.mu.Unlock() } // SetPageSize sets the default page size for this session. A value <= 0 will // disable paging. This setting can also be changed on a per-query basis. func (s *Session) SetPageSize(n int) { s.mu.Lock() s.pageSize = n s.mu.Unlock() } // SetPrefetch sets the default threshold for pre-fetching new pages. If // there are only p*pageSize rows remaining, the next page will be requested // automatically. This value can also be changed on a per-query basis and // the default value is 0.25. func (s *Session) SetPrefetch(p float64) { s.mu.Lock() s.prefetch = p s.mu.Unlock() } // SetTrace sets the default tracer for this session. This setting can also // be changed on a per-query basis. func (s *Session) SetTrace(trace Tracer) { s.mu.Lock() s.trace = trace s.mu.Unlock() } // QueryWithContext same as Query, but adds context to it. func (s *Session) QueryWithContext(ctx context.Context, stmt string, values ...any) *Query { q := s.Query(stmt, values...) q.context = ctx return q } // Query generates a new query object for interacting with the database. // Further details of the query may be tweaked using the resulting query // value before the query is executed. Query is automatically prepared // if it has not previously been executed. func (s *Session) Query(stmt string, values ...any) *Query { qry := queryPool.Get().(*Query) qry.session = s qry.stmt = stmt qry.values = values qry.defaultsFromSession() qry.SetRequestTimeout(s.cfg.Timeout) return qry } type QueryInfo struct { Id []byte Args []ColumnInfo Rval []ColumnInfo PKeyColumns []int } // Bind generates a new query object based on the query statement passed in. // The query is automatically prepared if it has not previously been executed. // The binding callback allows the application to define which query argument // values will be marshalled as part of the query execution. // During execution, the meta data of the prepared query will be routed to the // binding callback, which is responsible for producing the query argument values. func (s *Session) Bind(stmt string, b func(q *QueryInfo) ([]any, error)) *Query { qry := queryPool.Get().(*Query) qry.session = s qry.stmt = stmt qry.binding = b qry.defaultsFromSession() qry.SetRequestTimeout(s.cfg.Timeout) return qry } // Close closes all connections. The session is unusable after this // operation. func (s *Session) Close() { s.sessionStateMu.Lock() if s.isClosing { s.sessionStateMu.Unlock() return } s.isClosing = true s.sessionStateMu.Unlock() if s.pool != nil { s.pool.Close() } if s.control != nil { s.control.close() } if s.nodeEvents != nil { s.nodeEvents.stop() } if s.schemaEvents != nil { s.schemaEvents.stop() } if s.eventBus != nil { _ = s.eventBus.Stop() } if s.ringRefresher != nil { s.ringRefresher.Stop() } if s.cancel != nil { s.cancel() } if s.clientRoutesHandler != nil { s.clientRoutesHandler.Stop() } s.sessionStateMu.Lock() s.isClosed = true s.sessionStateMu.Unlock() if s.metadataDescriber != nil && s.metadataDescriber.metadata != nil { s.metadataDescriber.metadata.tabletsMetadata.Close() } } func (s *Session) Closed() bool { s.sessionStateMu.RLock() closed := s.isClosed s.sessionStateMu.RUnlock() return closed } func (s *Session) initialized() bool { s.sessionStateMu.RLock() initialized := s.isInitialized s.sessionStateMu.RUnlock() return initialized } func (s *Session) Ready() error { s.sessionStateMu.RLock() err := ErrSessionNotReady if s.isInitialized || s.initErr != nil { err = s.initErr } s.sessionStateMu.RUnlock() return err } func (s *Session) WaitUntilReady() error { <-s.readyCh return s.initErr } func (s *Session) executeQuery(qry *Query) (it *Iter) { if s.Closed() { return &Iter{err: ErrSessionClosed} } if err := s.Ready(); err != nil { return &Iter{err: err} } iter, err := s.executor.executeQuery(qry) if err != nil { return &Iter{err: err} } if iter == nil { panic("nil iter") } return iter } func (s *Session) removeHost(h *HostInfo) { s.policy.RemoveHost(h) hostID := h.HostID() s.pool.removeHost(hostID) s.hostSource.removeHost(hostID) } // KeyspaceMetadata returns the schema metadata for the keyspace specified. Returns an error if the keyspace does not exist. func (s *Session) KeyspaceMetadata(keyspace string) (*KeyspaceMetadata, error) { if s.Closed() { return nil, ErrSessionClosed } else if err := s.Ready(); err != nil { return nil, err } else if keyspace == "" { return nil, ErrNoKeyspace } return s.metadataDescriber.GetKeyspace(keyspace) } // TableMetadata returns the schema metadata for the specified table. Returns an error if the keyspace or table does not exist. func (s *Session) TableMetadata(keyspace, table string) (*TableMetadata, error) { if s.Closed() { return nil, ErrSessionClosed } else if err := s.Ready(); err != nil { return nil, err } else if keyspace == "" { return nil, ErrNoKeyspace } else if table == "" { return nil, ErrNoTable } return s.metadataDescriber.GetTable(keyspace, table) } // TabletsMetadata returns the metadata about all tablets across all keyspaces and tables. // // Deprecated: Use [Session.TableTabletsMetadata] for per-table lookups or // [Session.ForEachTablet] to iterate without aggregating into a flat list. func (s *Session) TabletsMetadata() (tablets.TabletInfoList, error) { if s.Closed() { return nil, ErrSessionClosed } else if err := s.Ready(); err != nil { return nil, err } else if !s.tabletsRoutingV1 { return nil, ErrTabletsNotUsed } return s.metadataDescriber.getTablets(), nil } // TableTabletsMetadata returns the tablet metadata for the specified keyspace and table. // Returns (nil, nil) when no tablets exist for the given keyspace/table. func (s *Session) TableTabletsMetadata(keyspace, table string) (tablets.TabletEntryList, error) { // fail fast if s.Closed() { return nil, ErrSessionClosed } else if err := s.Ready(); err != nil { return nil, err } else if !s.tabletsRoutingV1 { return nil, ErrTabletsNotUsed } else if keyspace == "" { return nil, ErrNoKeyspace } else if table == "" { return nil, ErrNoTable } return s.metadataDescriber.getTableTablets(keyspace, table), nil } // ForEachTablet iterates over all keyspace/table pairs and their tablet entries, // calling fn for each one. If fn returns false, iteration stops early. // The entries slice is a shallow copy; do not mutate individual entries. func (s *Session) ForEachTablet(fn func(keyspace, table string, entries tablets.TabletEntryList) bool) error { // fail fast if s.Closed() { return ErrSessionClosed } else if err := s.Ready(); err != nil { return err } else if !s.tabletsRoutingV1 { return ErrTabletsNotUsed } if fn == nil { return nil } s.metadataDescriber.forEachTablet(fn) return nil } func (s *Session) getConn() *Conn { hosts := s.hostSource.getHostsList() for _, host := range hosts { if !host.IsUp() { continue } pool, ok := s.pool.getPool(host) if !ok { continue } else if conn := pool.Pick(nil, nil); conn != nil { return conn } } return nil } // findTabletReplicasUnsafeForToken returns the raw replica slice for the tablet // owning the given token. The returned slice must not be modified by callers. func (s *Session) findTabletReplicasUnsafeForToken(keyspace, table string, token int64) []tablets.ReplicaInfo { if s.Closed() { return nil } md := s.metadataDescriber if md == nil || md.metadata == nil { return nil } return md.metadata.tabletsMetadata.FindReplicasUnsafeForToken(keyspace, table, token) } // returns routing key indexes and type info func (s *Session) routingKeyInfo(ctx context.Context, stmt string, requestTimeout time.Duration) (*routingKeyInfo, error) { s.routingKeyInfoCache.mu.Lock() entry, cached := s.routingKeyInfoCache.lru.Get(stmt) if cached { // done accessing the cache s.routingKeyInfoCache.mu.Unlock() // the entry is an inflight struct similar to that used by // Conn to prepare statements inflight := entry.(*inflightCachedEntry) // wait for any inflight work inflight.wg.Wait() if inflight.err != nil { return nil, inflight.err } key, _ := inflight.value.(*routingKeyInfo) return key, nil } // create a new inflight entry while the data is created inflight := new(inflightCachedEntry) inflight.wg.Add(1) defer inflight.wg.Done() s.routingKeyInfoCache.lru.Add(stmt, inflight) s.routingKeyInfoCache.mu.Unlock() var ( info *preparedStatment partitionKey []*ColumnMetadata ) conn := s.getConn() if conn == nil { // TODO: better error? inflight.err = errors.New("gocql: unable to fetch prepared info: no connection available") return nil, inflight.err } // get the query info for the statement info, inflight.err = conn.prepareStatement(ctx, stmt, nil, requestTimeout) if inflight.err != nil { // don't cache this error s.routingKeyInfoCache.Remove(stmt) return nil, inflight.err } // TODO: it would be nice to mark hosts here but as we are not using the policies // to fetch hosts we cant if info.request.colCount == 0 { // no arguments, no routing key, and no error return nil, nil } table := info.request.table keyspace := info.request.keyspace // Fall back to per-column metadata when FlagGlobalTableSpec is not set. if keyspace == "" && len(info.request.columns) > 0 { keyspace = info.request.columns[0].Keyspace } if table == "" && len(info.request.columns) > 0 { table = info.request.columns[0].Table } partitioner, err := scyllaGetTablePartitioner(s, keyspace, table) if err != nil { // don't cache this error, but make sure all waiters see the same failure. inflight.err = err s.routingKeyInfoCache.Remove(stmt) return nil, err } if len(info.request.pkeyColumns) > 0 { // proto v4 dont need to calculate primary key columns types := make([]TypeInfo, len(info.request.pkeyColumns)) for i, col := range info.request.pkeyColumns { types[i] = info.request.columns[col].TypeInfo } routingKeyInfo := &routingKeyInfo{ indexes: info.request.pkeyColumns, types: types, lwt: info.request.lwt, partitioner: partitioner, keyspace: keyspace, table: table, } inflight.value = routingKeyInfo return routingKeyInfo, nil } // get the table metadata (uses TableMetadata to handle cache invalidation) var tableMetadata *TableMetadata tableMetadata, inflight.err = s.TableMetadata(keyspace, table) if inflight.err != nil { // don't cache this error s.routingKeyInfoCache.Remove(stmt) return nil, inflight.err } partitionKey = tableMetadata.PartitionKey // Build a name→index map so that partition key lookup is O(1) per // column instead of a nested O(partitionKey × columns) scan. // The map records the first occurrence of each column name. colIndex := make(map[string]int, len(info.request.columns)) for i, c := range info.request.columns { if _, exists := colIndex[c.Name]; !exists { colIndex[c.Name] = i } } size := len(partitionKey) routingKeyInfo := &routingKeyInfo{ indexes: make([]int, size), types: make([]TypeInfo, size), lwt: info.request.lwt, partitioner: partitioner, keyspace: keyspace, table: table, } for keyIndex, keyColumn := range partitionKey { argIndex, found := colIndex[keyColumn.Name] if !found { // missing a routing key column mapping // no routing key, and no error return nil, nil } routingKeyInfo.indexes[keyIndex] = argIndex routingKeyInfo.types[keyIndex] = info.request.columns[argIndex].TypeInfo } // cache this result inflight.value = routingKeyInfo return routingKeyInfo, nil } func (b *Batch) execute(ctx context.Context, conn *Conn) *Iter { return conn.executeBatch(ctx, b) } // Exec executes a batch operation and returns nil if successful // otherwise an error is returned describing the failure. func (b *Batch) Exec() error { iter := b.session.executeBatch(b) return iter.Close() } func (s *Session) executeBatch(batch *Batch) *Iter { // fail fast if s.Closed() { return &Iter{err: ErrSessionClosed} } if err := s.Ready(); err != nil { return &Iter{err: err} } // Drop metrics from prior query executions batch.metrics.reset() // Prevent the execution of the batch if greater than the limit // Currently batches have a limit of 65536 queries. // https://datastax-oss.atlassian.net/browse/JAVA-229 if batch.Size() > BatchSizeMaximum { return &Iter{err: ErrTooManyStmts} } iter, err := s.executor.executeQuery(batch) if err != nil { return &Iter{err: err} } return iter } // ExecuteBatch executes a batch operation and returns nil if successful // otherwise an error is returned describing the failure. func (s *Session) ExecuteBatch(batch *Batch) error { iter := s.executeBatch(batch) return iter.Close() } // ExecuteBatchCAS executes a batch operation and returns true if successful and // an iterator (to scan additional rows if more than one conditional statement) // was sent. // Further scans on the interator must also remember to include // the applied boolean as the first argument to *Iter.Scan func (s *Session) ExecuteBatchCAS(batch *Batch, dest ...any) (applied bool, iter *Iter, err error) { iter = s.executeBatch(batch) if err := iter.checkErrAndNotFound(); err != nil { iter.Close() return false, nil, err } if len(iter.Columns()) > 1 { dest = append([]any{&applied}, dest...) iter.Scan(dest...) } else { iter.Scan(&applied) } return applied, iter, iter.err } // MapExecuteBatchCAS executes a batch operation much like ExecuteBatchCAS, // however it accepts a map rather than a list of arguments for the initial // scan. func (s *Session) MapExecuteBatchCAS(batch *Batch, dest map[string]any) (applied bool, iter *Iter, err error) { iter = s.executeBatch(batch) if err := iter.checkErrAndNotFound(); err != nil { iter.Close() return false, nil, err } iter.MapScan(dest) if iter.err != nil { return false, iter, iter.err } // check if [applied] was returned, otherwise it might not be CAS if appliedRaw, ok := dest["[applied]"]; ok { applied, ok = appliedRaw.(bool) if !ok { s.logger.Println("encountered non-bool \"[applied]\" key") } delete(dest, "[applied]") } // we usually close here, but instead of closing, just returin an error // if MapScan failed. Although Close just returns err, using Close // here might be confusing as we are not actually closing the iter return applied, iter, iter.err } // translateAddressPort is a helper method that will use the given AddressTranslator // if defined, to translate the given address and port into a possibly new address // and port, If no AddressTranslator or if an error occurs, the given address and // port will be returned. func translateAddressPort(addressTranslator AddressTranslator, host *HostInfo, addr AddressPort, logger StdLogger) (AddressPort, error) { if addressTranslator == nil || !addr.IsValid() { return addr, nil } translatorV2, ok := addressTranslator.(AddressTranslatorV2) if !ok { newAddr, newPort := addressTranslator.Translate(addr.Address, int(addr.Port)) if debug.Enabled { logger.Printf("gocql: translated address %q to '%v:%d'", addr, newAddr, newPort) } return AddressPort{ Address: newAddr, Port: uint16(newPort), }, nil } newAddr, err := translatorV2.TranslateHost(host, addr) if err != nil { if debug.Enabled { logger.Printf("gocql: failed to translate address %q: %s", addr, err.Error()) } return addr, err } if debug.Enabled { logger.Printf("gocql: translated address %q to %q", addr, newAddr) } return newAddr, nil } type hostMetrics struct { // Attempts is count of how many times this query has been attempted for this host. // An attempt is either a retry or fetching next page of results. Attempts int // TotalLatency is the sum of attempt latencies for this host in nanoseconds. TotalLatency int64 } type queryMetrics struct { m map[UUID]*hostMetrics // totalAttempts is total number of attempts. // Equal to sum of all hostMetrics' Attempts totalAttempts int l sync.RWMutex } // preFilledQueryMetrics initializes new queryMetrics based on per-host supplied data. func preFilledQueryMetrics(m map[UUID]*hostMetrics) *queryMetrics { qm := &queryMetrics{m: m} for _, hm := range qm.m { qm.totalAttempts += hm.Attempts } return qm } // hostMetrics returns a snapshot of metrics for given host. // If the metrics for host don't exist, they are created. func (qm *queryMetrics) hostMetrics(host *HostInfo) *hostMetrics { qm.l.Lock() metrics := qm.hostMetricsLocked(host) copied := new(hostMetrics) *copied = *metrics qm.l.Unlock() return copied } // hostMetricsLocked gets or creates host metrics for given host. // It must be called only while holding qm.l lock. func (qm *queryMetrics) hostMetricsLocked(host *HostInfo) *hostMetrics { id := host.hostUUID() metrics, exists := qm.m[id] if !exists { // if the host is not in the map, it means it's been accessed for the first time metrics = &hostMetrics{} qm.m[id] = metrics } return metrics } // attempts returns the number of times the query was executed. func (qm *queryMetrics) attempts() int { qm.l.Lock() attempts := qm.totalAttempts qm.l.Unlock() return attempts } func (qm *queryMetrics) latency() int64 { qm.l.Lock() var ( attempts int latency int64 ) for _, metric := range qm.m { attempts += metric.Attempts latency += metric.TotalLatency } qm.l.Unlock() if attempts > 0 { return latency / int64(attempts) } return 0 } // reset resets metrics, to forget about prior query executions. // Uses clear() instead of make() to preserve the map's backing array, // avoiding a heap allocation on each re-execution. func (qm *queryMetrics) reset() { qm.l.Lock() clear(qm.m) qm.totalAttempts = 0 qm.l.Unlock() } // attempt adds given number of attempts and latency for given host. // It returns previous total attempts. // If needsHostMetrics is true, a copy of updated hostMetrics is returned. func (qm *queryMetrics) attempt(addAttempts int, addLatency time.Duration, host *HostInfo, needsHostMetrics bool) (int, *hostMetrics) { qm.l.Lock() totalAttempts := qm.totalAttempts qm.totalAttempts += addAttempts updateHostMetrics := qm.hostMetricsLocked(host) updateHostMetrics.Attempts += addAttempts updateHostMetrics.TotalLatency += addLatency.Nanoseconds() var hostMetricsCopy *hostMetrics if needsHostMetrics { hostMetricsCopy = new(hostMetrics) *hostMetricsCopy = *updateHostMetrics } qm.l.Unlock() return totalAttempts, hostMetricsCopy } // Query represents a CQL statement that can be executed. type Query struct { trace Tracer context context.Context // pageContextParent keeps paging fetch contexts anchored to the original // query context instead of chaining each page under the previous page fetch. pageContextParent context.Context spec SpeculativeExecutionPolicy rt RetryPolicy conn ConnInterface observer QueryObserver metrics *queryMetrics session *Session // Timeout on waiting for response from server customPayload map[string][]byte // getKeyspace is field so that it can be overriden in tests getKeyspace func() string // routingInfo is a pointer because Query can be copied and copyable struct can't hold a mutex. routingInfo *queryRoutingInfo binding func(q *QueryInfo) ([]any, error) // hostID specifies the host on which the query should be executed. // If it is empty, then the host is picked by HostSelectionPolicy hostID string stmt string routingKey []byte values []any pageState []byte // requestTimeout is a timeout on waiting for response from server requestTimeout time.Duration defaultTimestampValue int64 prefetch float64 pageSize int refCount uint32 cons Consistency serialCons Consistency disableAutoPage bool deferReleasedErrorFinalize bool idempotent bool skipPrepare bool disableSkipMetadata bool defaultTimestamp bool // prepareCache caches whether shouldPrepare has been computed. // Since q.stmt is immutable after construction, the result never // changes. Accessed atomically because speculative execution may // call shouldPrepare from multiple goroutines concurrently. // Values: 0 = unknown, 1 = should prepare (DML), 2 = should not prepare. prepareCache uint32 } type queryRoutingInfo struct { // partitioner is a reference to a Partitioner instance // If nil default partitioner will be used. partitioner Partitioner keyspace string table string // mu protects contents of queryRoutingInfo. mu sync.RWMutex // "lwt" denotes the query being an LWT operation // In effect if the query is of the form "INSERT/UPDATE/DELETE ... IF ..." // For more details see https://docs.scylladb.com/using-scylla/lwt/ lwt bool } func (qri *queryRoutingInfo) isLWT() bool { qri.mu.RLock() defer qri.mu.RUnlock() return qri.lwt } func (qri *queryRoutingInfo) getPartitioner() Partitioner { qri.mu.RLock() defer qri.mu.RUnlock() return qri.partitioner } func (q *Query) defaultsFromSession() { s := q.session s.mu.RLock() q.cons = s.cons q.pageSize = s.pageSize q.trace = s.trace q.observer = s.queryObserver q.prefetch = s.prefetch q.rt = s.cfg.RetryPolicy q.serialCons = s.cfg.SerialConsistency q.defaultTimestamp = s.cfg.DefaultTimestamp q.idempotent = s.cfg.DefaultIdempotence if q.metrics == nil { q.metrics = &queryMetrics{m: make(map[UUID]*hostMetrics)} } q.spec = defaultNonSpecExec s.mu.RUnlock() } // Statement returns the statement that was used to generate this query. func (q Query) Statement() string { return q.stmt } // Values returns the values passed in via Bind. // This can be used by a wrapper type that needs to access the bound values. func (q Query) Values() []any { return q.values } // String implements the stringer interface. func (q Query) String() string { return fmt.Sprintf("[query statement=%q values=%+v consistency=%s]", q.stmt, q.values, q.cons) } // Attempts returns the number of times the query was executed. func (q *Query) Attempts() int { return q.metrics.attempts() } func (q *Query) AddAttempts(i int, host *HostInfo) { q.metrics.attempt(i, 0, host, false) } // Latency returns the average amount of nanoseconds per attempt of the query. func (q *Query) Latency() int64 { return q.metrics.latency() } func (q *Query) AddLatency(l int64, host *HostInfo) { q.metrics.attempt(0, time.Duration(l)*time.Nanosecond, host, false) } // Consistency sets the consistency level for this query. If no consistency // level have been set, the default consistency level of the cluster // is used. func (q *Query) Consistency(c Consistency) *Query { q.cons = c return q } // GetConsistency returns the currently configured consistency level for // the query. func (q *Query) GetConsistency() Consistency { return q.cons } // Same as Consistency but without a return value func (q *Query) SetConsistency(c Consistency) { q.cons = c } // CustomPayload sets the custom payload level for this query. func (q *Query) CustomPayload(customPayload map[string][]byte) *Query { q.customPayload = customPayload return q } func (q *Query) Context() context.Context { if q.context == nil { return context.Background() } return q.context } // Trace enables tracing of this query. Look at the documentation of the // Tracer interface to learn more about tracing. func (q *Query) Trace(trace Tracer) *Query { q.trace = trace return q } // Observer enables query-level observer on this query. // The provided observer will be called every time this query is executed. func (q *Query) Observer(observer QueryObserver) *Query { q.observer = observer return q } // PageSize will tell the iterator to fetch the result in pages of size n. // This is useful for iterating over large result sets, but setting the // page size too low might decrease the performance. This feature is only // available in Cassandra 2 and onwards. func (q *Query) PageSize(n int) *Query { q.pageSize = n return q } // DefaultTimestamp will enable the with default timestamp flag on the query. // If enable, this will replace the server side assigned // timestamp as default timestamp. Note that a timestamp in the query itself // will still override this timestamp. This is entirely optional. // // Only available on protocol >= 3 func (q *Query) DefaultTimestamp(enable bool) *Query { q.defaultTimestamp = enable return q } // WithTimestamp will enable the with default timestamp flag on the query // like DefaultTimestamp does. But also allows to define value for timestamp. // It works the same way as USING TIMESTAMP in the query itself, but // should not break prepared query optimization. // // Only available on protocol >= 3 func (q *Query) WithTimestamp(timestamp int64) *Query { q.DefaultTimestamp(true) q.defaultTimestampValue = timestamp return q } // RoutingKey sets the routing key to use when a token aware connection // pool is used to optimize the routing of this query. func (q *Query) RoutingKey(routingKey []byte) *Query { q.routingKey = routingKey return q } func (q *Query) withContext(ctx context.Context) ExecutableQuery { // I really wish go had covariant types return q.WithContext(ctx) } // WithContext returns a shallow copy of q with its context // set to ctx. // // The provided context controls the entire lifetime of executing a // query, queries will be canceled and return once the context is // canceled. func (q *Query) WithContext(ctx context.Context) *Query { q2 := *q q2.context = ctx return &q2 } // Deprecate: does nothing, cancel the context passed to WithContext func (q *Query) Cancel() { // TODO: delete } func (q *Query) execute(ctx context.Context, conn *Conn) *Iter { return conn.executeQuery(ctx, q) } func (q *Query) attempt(keyspace string, end, start time.Time, iter *Iter, host *HostInfo) { latency := end.Sub(start) attempt, metricsForHost := q.metrics.attempt(1, latency, host, q.observer != nil) if q.observer != nil { q.observer.ObserveQuery(q.Context(), ObservedQuery{ Keyspace: keyspace, Statement: q.stmt, Values: q.values, Start: start, End: end, Rows: iter.numRows, Host: host, Metrics: metricsForHost, Err: iter.err, Attempt: attempt, }) } } func (q *Query) retryPolicy() RetryPolicy { return q.rt } // Keyspace returns the keyspace the query will be executed against. func (q *Query) Keyspace() string { if q.getKeyspace != nil { return q.getKeyspace() } if q.routingInfo.keyspace != "" { return q.routingInfo.keyspace } if q.session == nil { return "" } // TODO(chbannis): this should be parsed from the query or we should let // this be set by users. return q.session.cfg.Keyspace } // Table returns name of the table the query will be executed against. func (q *Query) Table() string { return q.routingInfo.table } func (q *Query) GetSession() *Session { return q.session } // GetRoutingKey gets the routing key to use for routing this query. If // a routing key has not been explicitly set, then the routing key will // be constructed if possible using the keyspace's schema and the query // info for this query statement. If the routing key cannot be determined // then nil will be returned with no error. On any error condition, // an error description will be returned. func (q *Query) GetRoutingKey() ([]byte, error) { if q.routingKey != nil { return q.routingKey, nil } else if q.binding != nil && len(q.values) == 0 { // If this query was created using session.Bind we wont have the query // values yet, so we have to pass down to the next policy. // TODO: Remove this and handle this case return nil, nil } // Non-DML statements (DDL, USE, GRANT, etc.) do not need preparation // and have no routing key. Skip the routingKeyInfo call which would // otherwise send a wasteful PREPARE frame to the server. if !q.shouldPrepare() { return nil, nil } // try to determine the routing key routingKeyInfo, err := q.session.routingKeyInfo(q.Context(), q.stmt, q.requestTimeout) if err != nil { return nil, err } if routingKeyInfo != nil { q.routingInfo.mu.Lock() q.routingInfo.lwt = routingKeyInfo.lwt q.routingInfo.partitioner = routingKeyInfo.partitioner q.routingInfo.keyspace = routingKeyInfo.keyspace q.routingInfo.table = routingKeyInfo.table q.routingInfo.mu.Unlock() } return createRoutingKey(routingKeyInfo, q.values) } func (q *Query) shouldPrepare() bool { if v := atomic.LoadUint32(&q.prepareCache); v != 0 { return v == 1 } result := stmtIsDML(q.stmt) if result { atomic.StoreUint32(&q.prepareCache, 1) } else { atomic.StoreUint32(&q.prepareCache, 2) } return result } // stmtKeyword returns the first whitespace-delimited keyword of a CQL // statement, skipping leading whitespace. For "BEGIN …" statements it // returns the last word instead (e.g. "BATCH"). The returned substring // shares the backing array of stmt (zero allocations). The result is // not lowercased; callers should use strings.EqualFold for comparison. func stmtKeyword(stmt string) string { // Skip leading whitespace using unicode-aware scanning (no allocation). i := 0 for i < len(stmt) { r, size := utf8.DecodeRuneInString(stmt[i:]) if !unicode.IsSpace(r) { break } i += size } // Find the end of the first word. j := i for j < len(stmt) { r, size := utf8.DecodeRuneInString(stmt[j:]) if unicode.IsSpace(r) { break } j += size } word := stmt[i:j] if strings.EqualFold(word, "begin") { // Handle "BEGIN BATCH ... APPLY BATCH" — extract the last word. end := len(stmt) for end > j { r, size := utf8.DecodeLastRuneInString(stmt[:end]) if !unicode.IsSpace(r) && r != ';' { break } end -= size } start := end for start > j { r, size := utf8.DecodeLastRuneInString(stmt[:start]) if unicode.IsSpace(r) { break } start -= size } word = stmt[start:end] } return word } // stmtIsDML reports whether stmt is a DML statement that should be prepared. func stmtIsDML(stmt string) bool { kw := stmtKeyword(stmt) switch len(kw) { case 5: // "batch" return strings.EqualFold(kw, "batch") case 6: // "select", "insert", "update", "delete" return strings.EqualFold(kw, "select") || strings.EqualFold(kw, "insert") || strings.EqualFold(kw, "update") || strings.EqualFold(kw, "delete") } return false } // SetPrefetch sets the default threshold for pre-fetching new pages. If // there are only p*pageSize rows remaining, the next page will be requested // automatically. func (q *Query) Prefetch(p float64) *Query { q.prefetch = p return q } // RetryPolicy sets the policy to use when retrying the query. func (q *Query) RetryPolicy(r RetryPolicy) *Query { q.rt = r return q } // SetSpeculativeExecutionPolicy sets the execution policy func (q *Query) SetSpeculativeExecutionPolicy(sp SpeculativeExecutionPolicy) *Query { q.spec = sp return q } // speculativeExecutionPolicy fetches the policy func (q *Query) speculativeExecutionPolicy() SpeculativeExecutionPolicy { return q.spec } // IsIdempotent returns whether the query is marked as idempotent. // Non-idempotent query won't be retried. // See "Retries and speculative execution" in package docs for more details. func (q *Query) IsIdempotent() bool { return q.idempotent } func (q *Query) IsLWT() bool { return q.routingInfo.isLWT() } func (q *Query) GetCustomPartitioner() Partitioner { return q.routingInfo.getPartitioner() } // Idempotent marks the query as being idempotent or not depending on // the value. // Non-idempotent query won't be retried. // See "Retries and speculative execution" in package docs for more details. func (q *Query) Idempotent(value bool) *Query { q.idempotent = value return q } // Bind sets query arguments of query. This can also be used to rebind new query arguments // to an existing query instance. func (q *Query) Bind(v ...any) *Query { q.values = v q.pageState = nil return q } // SerialConsistency sets the consistency level for the // serial phase of conditional updates. That consistency can only be // either SERIAL or LOCAL_SERIAL and if not present, it defaults to // SERIAL. This option will be ignored for anything else that a // conditional update/insert. func (q *Query) SerialConsistency(cons Consistency) *Query { if !cons.IsSerial() { panic("Serial consistency can only be SERIAL or LOCAL_SERIAL got " + cons.String()) } q.serialCons = cons return q } // PageState sets the paging state for the query to resume paging from a specific // point in time. Setting this will disable to query paging for this query, and // must be used for all subsequent pages. func (q *Query) PageState(state []byte) *Query { q.pageState = state q.disableAutoPage = true return q } // NoSkipMetadata will override the internal result metadata cache so that the driver does not // send skip_metadata for queries, this means that the result will always contain // the metadata to parse the rows and will not reuse the metadata from the prepared // statement. This should only be used to work around cassandra bugs, such as when using // CAS operations which do not end in Cas. // // See https://issues.apache.org/jira/browse/CASSANDRA-11099 // https://github.com/apache/cassandra-gocql-driver/issues/612 func (q *Query) NoSkipMetadata() *Query { q.disableSkipMetadata = true return q } // Exec executes the query without returning any rows. func (q *Query) Exec() error { return q.Iter().Close() } // GetRequestTimeout returns time driver waits for single server response // This timeout is applied to preparing statement request and for query execution requests func (b *Query) GetRequestTimeout() time.Duration { return b.requestTimeout } // SetRequestTimeout sets time driver waits for server to respond // This timeout is applied to preparing statement request and for query execution requests func (b *Query) SetRequestTimeout(timeout time.Duration) *Query { b.requestTimeout = timeout return b } func isUseStatement(stmt string) bool { if len(stmt) < 3 { return false } return strings.EqualFold(stmt[0:3], "use") } // Iter executes the query and returns an iterator capable of iterating // over all results. func (q *Query) Iter() *Iter { if isUseStatement(q.stmt) { return &Iter{err: ErrUseStmt} } if !q.disableAutoPage { return q.executeQuery() } // Retry on empty page if pagination is manual iter := q.executeQueryForIterPostProcessing() var hiddenWarnings []string for iter.err == nil && iter.numRows == 0 && !iter.LastPage() { if warnings := iter.Warnings(); len(warnings) > 0 { hiddenWarnings = append(hiddenWarnings, warnings...) } ps := iter.PageState() iter.discard() q.PageState(ps) iter = q.executeQueryForIterPostProcessing() } if len(hiddenWarnings) > 0 { iter.allWarnings = append(hiddenWarnings, iter.allWarnings...) } if iter.err != nil && iter.framer == nil && iter.next == nil { iter.finalize(true) } return iter } func (q *Query) executeQueryForIterPostProcessing() (iter *Iter) { q.deferReleasedErrorFinalize = true defer func() { q.deferReleasedErrorFinalize = false }() return q.executeQuery() } func (q *Query) executeQuery() *Iter { // Drop metrics from prior query executions q.metrics.reset() if q.conn != nil { // if the query was specifically run on a connection then re-use that // connection when fetching the next results return q.conn.executeQuery(q.Context(), q) } return q.session.executeQuery(q) } // MapScan executes the query, copies the columns of the first selected // row into the map pointed at by m and discards the rest. If no rows // were selected, ErrNotFound is returned. func (q *Query) MapScan(m map[string]any) error { iter := q.Iter() if err := iter.checkErrAndNotFound(); err != nil { iter.Close() return err } iter.MapScan(m) return iter.Close() } // Scan executes the query, copies the columns of the first selected // row into the values pointed at by dest and discards the rest. If no rows // were selected, ErrNotFound is returned. func (q *Query) Scan(dest ...any) error { iter := q.Iter() if err := iter.checkErrAndNotFound(); err != nil { iter.Close() return err } iter.Scan(dest...) return iter.Close() } // ScanCAS executes a lightweight transaction (i.e. an UPDATE or INSERT // statement containing an IF clause). If the transaction fails because // the existing values did not match, the previous values will be stored // in dest. // // As for INSERT .. IF NOT EXISTS, previous values will be returned as if // SELECT * FROM. So using ScanCAS with INSERT is inherently prone to // column mismatching. Use MapScanCAS to capture them safely. func (q *Query) ScanCAS(dest ...any) (applied bool, err error) { q.disableSkipMetadata = true iter := q.Iter() if err := iter.checkErrAndNotFound(); err != nil { iter.Close() return false, err } if len(iter.Columns()) > 1 { dest = append([]any{&applied}, dest...) iter.Scan(dest...) } else { iter.Scan(&applied) } return applied, iter.Close() } // MapScanCAS executes a lightweight transaction (i.e. an UPDATE or INSERT // statement containing an IF clause). If the transaction fails because // the existing values did not match, the previous values will be stored // in dest map. // // As for INSERT .. IF NOT EXISTS, previous values will be returned as if // SELECT * FROM. So using ScanCAS with INSERT is inherently prone to // column mismatching. MapScanCAS is added to capture them safely. func (q *Query) MapScanCAS(dest map[string]any) (applied bool, err error) { q.disableSkipMetadata = true iter := q.Iter() if err := iter.checkErrAndNotFound(); err != nil { iter.Close() return false, err } iter.MapScan(dest) if iter.err != nil { return false, iter.Close() } // check if [applied] was returned, otherwise it might not be CAS if appliedRaw, ok := dest["[applied]"]; ok { applied, ok = appliedRaw.(bool) if !ok { q.session.logger.Println("encountered non-bool \"[applied]\" key") } delete(dest, "[applied]") } return applied, iter.Close() } // Release releases a query back into a pool of queries. Released Queries // cannot be reused. // // Example: // // qry := session.Query("SELECT * FROM my_table") // qry.Exec() // qry.Release() func (q *Query) Release() { q.decRefCount() } // reset zeroes out all fields of a query so that it can be safely pooled. // It preserves the metrics allocation for reuse. routingInfo is always freshly // allocated because paging copies share the pointer (see conn.go executeQuery). func (q *Query) reset() { m := q.metrics if m != nil { clear(m.m) m.totalAttempts = 0 } *q = Query{routingInfo: &queryRoutingInfo{}, metrics: m, refCount: 1} } func (q *Query) incRefCount() { atomic.AddUint32(&q.refCount, 1) } func (q *Query) decRefCount() { if res := atomic.AddUint32(&q.refCount, ^uint32(0)); res == 0 { // do release q.reset() queryPool.Put(q) } } func (q *Query) borrowForExecution() { q.incRefCount() } func (q *Query) releaseAfterExecution() { q.decRefCount() } // SetHostID allows to define the host the query should be executed against. If the // host was filtered or otherwise unavailable, then the query will error. If an empty // string is sent, the default behavior, using the configured HostSelectionPolicy will // be used. A hostID can be obtained from HostInfo.HostID() after calling GetHosts(). func (q *Query) SetHostID(hostID string) *Query { q.hostID = hostID return q } // GetHostID returns id of the host on which query should be executed. func (q *Query) GetHostID() string { return q.hostID } // Iter represents an iterator that can be used to iterate over all rows that // were returned by a query. The iterator might send additional queries to the // database during the iteration if paging was enabled. // // IMPORTANT: Close should still be called whenever iteration may stop early. // Iterators that run to exhaustion through Scan/Scanner.Next auto-finalize when // they become terminal, but Close remains the safest pattern and is still needed // to surface errors after manual early termination. Use defer immediately after // obtaining an Iter when in doubt: // // iter := session.Query("...").Iter() // defer iter.Close() // // Failure to call Close() after early termination may leak resources and prevent // buffer reuse. // // CONCURRENCY: Iter is NOT safe for concurrent use. An Iter instance should only // be used from a single goroutine at a time. While Close() is safe to call multiple // times (idempotent), calling Scan(), Next(), or other methods concurrently with // Close() or each other will result in undefined behavior. type Iter struct { warningQuery ExecutableQuery framer framerInterface err error warningHandler WarningHandler releasedCustomPayload map[string][]byte next *nextIter host *HostInfo // allWarnings accumulates warnings across page boundaries. // When a page's framer is released during fetchNextPage(), its warnings // are appended here so they are not lost. allWarnings []string // scanColumns caches the column names computed by RowData() so that // MapScan does not recompute them on every row. Populated lazily on // the first call to getScanColumns(). scanColumns []string meta resultMetadata pos int numRows int closed int32 warningsHandled int32 warningQueryOwned bool } // Host returns the host which the query was sent to. func (iter *Iter) Host() *HostInfo { return iter.host } // Columns returns the name and type of the selected columns. func (iter *Iter) Columns() []ColumnInfo { return iter.meta.columns } // copyPageData copies page-related fields from src to iter, excluding the closed flag. // This is used when fetching the next page to avoid races with concurrent Close() calls. // // After this call, src must not be used because its framer ownership has been // transferred to iter (src.framer is set to nil to prevent double-release). func (iter *Iter) copyPageData(src *Iter) { iter.err = src.err iter.framer = src.framer iter.next = src.next iter.host = src.host iter.meta = src.meta iter.allWarnings = append(iter.allWarnings, src.allWarnings...) iter.releasedCustomPayload = src.releasedCustomPayload iter.pos = src.pos iter.numRows = src.numRows if iter.warningQuery == nil { iter.warningHandler = src.warningHandler iter.warningQuery = src.warningQuery iter.warningQueryOwned = src.warningQueryOwned } else { src.releaseWarningQuery() } // Clear source framer to prevent double-release: ownership is now with iter. src.framer = nil src.allWarnings = nil src.releasedCustomPayload = nil src.next = nil src.warningHandler = nil src.warningQuery = nil src.warningQueryOwned = false // Intentionally don't copy iter.closed - it's managed with atomic operations } func (iter *Iter) bindWarningHandler(qry ExecutableQuery, handler WarningHandler) *Iter { if iter == nil || handler == nil { return iter } iter.warningQuery = qry iter.warningHandler = handler if pooledQuery, ok := qry.(*Query); ok { pooledQuery.incRefCount() iter.warningQueryOwned = true if iter.err != nil && iter.framer == nil && iter.next == nil && !pooledQuery.deferReleasedErrorFinalize { iter.finalize(true) } return iter } if iter.err != nil && iter.framer == nil && iter.next == nil { iter.finalize(true) } return iter } func (iter *Iter) releaseWarningQuery() { qry := iter.warningQuery owned := iter.warningQueryOwned iter.warningQueryOwned = false iter.warningQuery = nil if !owned { return } pooledQuery, ok := qry.(*Query) if !ok { return } pooledQuery.decRefCount() } func (iter *Iter) collectReleasedFramerMetadata(f framerInterface) { if f == nil { return } if warnings := f.GetHeaderWarnings(); len(warnings) > 0 { iter.allWarnings = append(iter.allWarnings, warnings...) } if payload := f.GetCustomPayload(); len(payload) > 0 { iter.releasedCustomPayload = maps.Clone(payload) } } func (iter *Iter) handleWarningsOnce() { if iter.warningHandler == nil { return } if !atomic.CompareAndSwapInt32(&iter.warningsHandled, 0, 1) { return } if warnings := iter.Warnings(); len(warnings) > 0 { iter.warningHandler.HandleWarnings(iter.warningQuery, iter.host, warnings) } } func (iter *Iter) finalize(dispatchWarnings bool) { if !atomic.CompareAndSwapInt32(&iter.closed, 0, 1) { return } if iter.framer != nil { iter.collectReleasedFramerMetadata(iter.framer) iter.framer.Release() iter.framer = nil } if iter.next != nil { iter.next.close() iter.next = nil } if dispatchWarnings { iter.handleWarningsOnce() } iter.releaseWarningQuery() iter.warningHandler = nil } func (iter *Iter) discard() { iter.finalize(false) } func newErrorIterWithReleasedFramer(err error, framer framerInterface) *Iter { iter := &Iter{err: err} if framer != nil { iter.collectReleasedFramerMetadata(framer) framer.Release() } return iter } // fetchNextPage releases the current page's framer and loads the next page // into iter. Returns true if a new page was successfully loaded, // false if no more pages or if the fetch produced an error. func (iter *Iter) fetchNextPage() bool { if iter.pos < iter.numRows || iter.next == nil { return false } currentNext := iter.next if iter.framer != nil { // Accumulate warnings from the current page before releasing its framer, // so they are not lost across page boundaries. if w := iter.framer.GetHeaderWarnings(); len(w) > 0 { iter.allWarnings = append(iter.allWarnings, w...) } iter.framer.Release() iter.framer = nil // prevent accidental use of released framer } next := currentNext.fetch() currentNext.consume() iter.copyPageData(next) return iter.err == nil } type Scanner interface { // Next advances the row pointer to point at the next row, the row is valid until // the next call of Next. It returns true if there is a row which is available to be // scanned into with Scan. // Next must be called before every call to Scan. Next() bool // Scan copies the current row's columns into dest. If the length of dest does not equal // the number of columns returned in the row an error is returned. If an error is encountered // when unmarshalling a column into the value in dest an error is returned and the row is invalidated // until the next call to Next. // Next must be called before calling Scan, if it is not an error is returned. Scan(...any) error // Err returns the if there was one during iteration that resulted in iteration being unable to complete. // Err will also release resources held by the iterator, the Scanner should not used after being called. Err() error } type iterScanner struct { iter *Iter cols [][]byte valid bool } func (is *iterScanner) Next() bool { iter := is.iter if iter.err != nil { iter.finalize(true) return false } for iter.pos >= iter.numRows { if !iter.fetchNextPage() { iter.finalize(true) return false } } for i := 0; i < len(is.cols); i++ { col, err := iter.readColumn() if err != nil { iter.err = err iter.finalize(true) return false } is.cols[i] = col } iter.pos++ is.valid = true return true } func scanColumn(p []byte, col ColumnInfo, dest []any) (int, error) { if dest[0] == nil { return 1, nil } if col.TypeInfo.Type() == TypeTuple { // this will panic, actually a bug, please report tuple := col.TypeInfo.(TupleTypeInfo) count := len(tuple.Elems) // here we pass in a slice of the struct which has the number number of // values as elements in the tuple if err := Unmarshal(col.TypeInfo, p, dest[:count]); err != nil { return 0, err } return count, nil } else { if err := Unmarshal(col.TypeInfo, p, dest[0]); err != nil { return 0, err } return 1, nil } } func (is *iterScanner) Scan(dest ...any) error { if !is.valid { return errors.New("gocql: Scan called without calling Next") } iter := is.iter // currently only support scanning into an expand tuple, such that its the same // as scanning in more values from a single column if len(dest) != iter.meta.actualColCount { return fmt.Errorf("gocql: not enough columns to scan into: have %d want %d", len(dest), iter.meta.actualColCount) } // i is the current position in dest, could posible replace it and just use // slices of dest i := 0 var err error for _, col := range iter.meta.columns { var n int n, err = scanColumn(is.cols[i], col, dest[i:]) if err != nil { break } i += n } is.valid = false return err } func (is *iterScanner) Err() error { iter := is.iter is.iter = nil is.cols = nil is.valid = false return iter.Close() } // Scanner returns a row Scanner which provides an interface to scan rows in a manner which is // similar to database/sql. The iter should NOT be used again after calling this method. func (iter *Iter) Scanner() Scanner { if iter == nil { return nil } return &iterScanner{iter: iter, cols: make([][]byte, len(iter.meta.columns))} } func (iter *Iter) readColumn() ([]byte, error) { if atomic.LoadInt32(&iter.closed) != 0 { return nil, errors.New("iterator closed") } if iter.framer == nil { return nil, errors.New("no framer available") } return iter.framer.ReadBytesInternal() } // Scan consumes the next row of the iterator and copies the columns of the // current row into the values pointed at by dest. Use nil as a dest value // to skip the corresponding column. Scan might send additional queries // to the database to retrieve the next set of rows if paging was enabled. // // Scan returns true if the row was successfully unmarshaled or false if the // end of the result set was reached or if an error occurred. Close should // be called afterwards to retrieve any potential errors. func (iter *Iter) Scan(dest ...any) bool { if iter.err != nil { iter.finalize(true) return false } for iter.pos >= iter.numRows { if !iter.fetchNextPage() { iter.finalize(true) return false } } if iter.next != nil && iter.pos >= iter.next.pos { iter.next.fetchAsync() } // currently only support scanning into an expand tuple, such that its the same // as scanning in more values from a single column if len(dest) != iter.meta.actualColCount { iter.err = fmt.Errorf("gocql: not enough columns to scan into: have %d want %d", len(dest), iter.meta.actualColCount) iter.finalize(true) return false } // i is the current position in dest, could posible replace it and just use // slices of dest i := 0 for _, col := range iter.meta.columns { colBytes, err := iter.readColumn() if err != nil { iter.err = err iter.finalize(true) return false } n, err := scanColumn(colBytes, col, dest[i:]) if err != nil { iter.err = err iter.finalize(true) return false } i += n } iter.pos++ return true } // GetCustomPayload returns any parsed custom payload results if given in the // response from Cassandra. The returned map is a shallow copy and is safe to // retain after the Iter advances or is closed. // // When paging is enabled, this returns the custom payload from the most recently // loaded page only. Custom payloads from previously consumed pages are not retained. // If you need the payload, retrieve it before advancing to the next page. // // This additional feature of CQL Protocol v4 // allows additional results and query information to be returned by // custom QueryHandlers running in your C* cluster. // See https://datastax.github.io/java-driver/manual/custom_payloads/ func (iter *Iter) GetCustomPayload() map[string][]byte { if iter.framer != nil { return maps.Clone(iter.framer.GetCustomPayload()) } return maps.Clone(iter.releasedCustomPayload) } // Warnings returns any warnings generated if given in the response from Cassandra. // When paging is enabled, warnings are accumulated across all pages that have been // consumed so far plus the warnings from the current (not yet released) page. // // This is only available starting with CQL Protocol v4. func (iter *Iter) Warnings() []string { var current []string if iter.framer != nil { current = iter.framer.GetHeaderWarnings() } if len(iter.allWarnings) == 0 { return slices.Clone(current) // always return a caller-owned slice } if len(current) == 0 { return slices.Clone(iter.allWarnings) } return slices.Concat(iter.allWarnings, current) } // Close closes the iterator and returns any errors that happened during // the query or the iteration. func (iter *Iter) Close() error { iter.finalize(true) return iter.err } // WillSwitchPage detects if iterator reached end of current page // and the next page is available. func (iter *Iter) WillSwitchPage() bool { return iter.pos >= iter.numRows && iter.next != nil } // checkErrAndNotFound handle error and NotFound in one method. func (iter *Iter) checkErrAndNotFound() error { if iter.err != nil { return iter.err } else if iter.numRows == 0 { return ErrNotFound } return nil } // PageState return the current paging state for a query which can be used for // subsequent queries to resume paging this point. func (iter *Iter) PageState() []byte { return iter.meta.pagingState } // LastPage returns true if there are no more pages to fetch. func (iter *Iter) LastPage() bool { return len(iter.meta.pagingState) == 0 } // NumRows returns the number of rows in this pagination, it will update when new // pages are fetched, it is not the value of the total number of rows this iter // will return unless there is only a single page returned. func (iter *Iter) NumRows() int { return iter.numRows } // nextIter holds state for fetching a single page in an iterator. // single page might be attempted multiple times due to retries. type nextIter struct { qry *Query next *Iter cancel context.CancelFunc oncea sync.Once once sync.Once mu sync.Mutex pos int closed bool } func newNextIter(qry *Query, pos int) *nextIter { parentCtx := qry.pageContextParent if parentCtx == nil { parentCtx = qry.Context() } ctx, cancel := context.WithCancel(parentCtx) nextQry := qry.WithContext(ctx) nextQry.pageContextParent = parentCtx return &nextIter{ qry: nextQry, pos: pos, cancel: cancel, } } func (n *nextIter) fetchAsync() { n.oncea.Do(func() { go n.fetch() }) } func (n *nextIter) storeFetched(next *Iter) { if next == nil { return } n.mu.Lock() if n.closed { n.mu.Unlock() next.discard() return } n.next = next n.mu.Unlock() } func (n *nextIter) close() { if n.cancel != nil { n.cancel() } n.mu.Lock() n.closed = true next := n.next n.next = nil n.mu.Unlock() if next != nil { next.discard() } } // consume retires the next-page fetch context after the fetched page has been // handed off to the caller. Unlike close(), it keeps the fetched Iter alive so // its page data can become the current iterator state. func (n *nextIter) consume() { if n.cancel != nil { n.cancel() } n.mu.Lock() n.closed = true n.next = nil n.mu.Unlock() } func (n *nextIter) fetch() *Iter { n.once.Do(func() { // if the query was specifically run on a connection then re-use that // connection when fetching the next results var next *Iter if n.qry.conn != nil { next = n.qry.conn.executeQuery(n.qry.Context(), n.qry) } else { next = n.qry.session.executeQuery(n.qry) } n.storeFetched(next) }) n.mu.Lock() next := n.next n.mu.Unlock() return next } type Batch struct { context context.Context rt RetryPolicy spec SpeculativeExecutionPolicy trace Tracer observer BatchObserver // routingInfo is a pointer because Query can be copied and copyable struct can't hold a mutex. routingInfo *queryRoutingInfo metrics *queryMetrics cancelBatch func() CustomPayload map[string][]byte session *Session keyspace string // hostID specifies the host on which the query should be executed. // If it is empty, then the host is picked by HostSelectionPolicy hostID string routingKey []byte Entries []BatchEntry defaultTimestampValue int64 // requestTimeout is a timeout on waiting for response from serve requestTimeout time.Duration serialCons Consistency Cons Consistency defaultTimestamp bool Type BatchType } // NewBatch creates a new batch operation using defaults defined in the cluster // // Deprecated: use session.Batch instead func (s *Session) NewBatch(typ BatchType) *Batch { return s.Batch(typ) } // BatchWithContext creates a new batch operation using defaults defined in the cluster, with context func (s *Session) BatchWithContext(ctx context.Context, typ BatchType) *Batch { b := s.Batch(typ) b.context = ctx return b } // Batch creates a new batch operation using defaults defined in the cluster func (s *Session) Batch(typ BatchType) *Batch { s.mu.RLock() batch := &Batch{ Type: typ, rt: s.cfg.RetryPolicy, serialCons: s.cfg.SerialConsistency, trace: s.trace, observer: s.batchObserver, session: s, Cons: s.cons, defaultTimestamp: s.cfg.DefaultTimestamp, keyspace: s.cfg.Keyspace, metrics: &queryMetrics{m: make(map[UUID]*hostMetrics)}, spec: defaultNonSpecExec, routingInfo: &queryRoutingInfo{}, requestTimeout: s.cfg.Timeout, } s.mu.RUnlock() return batch } // Trace enables tracing of this batch. Look at the documentation of the // Tracer interface to learn more about tracing. func (b *Batch) Trace(trace Tracer) *Batch { b.trace = trace return b } // Observer enables batch-level observer on this batch. // The provided observer will be called every time this batched query is executed. func (b *Batch) Observer(observer BatchObserver) *Batch { b.observer = observer return b } func (b *Batch) Keyspace() string { return b.keyspace } // Batch has no reasonable eqivalent of Query.Table(). func (b *Batch) Table() string { return b.routingInfo.table } func (b *Batch) GetSession() *Session { return b.session } // Attempts returns the number of attempts made to execute the batch. func (b *Batch) Attempts() int { return b.metrics.attempts() } func (b *Batch) AddAttempts(i int, host *HostInfo) { b.metrics.attempt(i, 0, host, false) } // Latency returns the average number of nanoseconds to execute a single attempt of the batch. func (b *Batch) Latency() int64 { return b.metrics.latency() } func (b *Batch) AddLatency(l int64, host *HostInfo) { b.metrics.attempt(0, time.Duration(l)*time.Nanosecond, host, false) } // GetConsistency returns the currently configured consistency level for the batch // operation. func (b *Batch) GetConsistency() Consistency { return b.Cons } // SetConsistency sets the currently configured consistency level for the batch // operation. func (b *Batch) SetConsistency(c Consistency) { b.Cons = c } func (b *Batch) Context() context.Context { if b.context == nil { return context.Background() } return b.context } func (b *Batch) IsIdempotent() bool { for _, entry := range b.Entries { if !entry.Idempotent { return false } } return true } func (b *Batch) IsLWT() bool { return b.routingInfo.isLWT() } func (b *Batch) GetCustomPartitioner() Partitioner { return b.routingInfo.getPartitioner() } func (b *Batch) speculativeExecutionPolicy() SpeculativeExecutionPolicy { return b.spec } func (b *Batch) SpeculativeExecutionPolicy(sp SpeculativeExecutionPolicy) *Batch { b.spec = sp return b } // Query adds the query to the batch operation func (b *Batch) Query(stmt string, args ...any) *Batch { b.Entries = append(b.Entries, BatchEntry{Stmt: stmt, Args: args}) return b } // Bind adds the query to the batch operation and correlates it with a binding callback // that will be invoked when the batch is executed. The binding callback allows the application // to define which query argument values will be marshalled as part of the batch execution. func (b *Batch) Bind(stmt string, bind func(q *QueryInfo) ([]any, error)) { b.Entries = append(b.Entries, BatchEntry{Stmt: stmt, binding: bind}) } func (b *Batch) retryPolicy() RetryPolicy { return b.rt } // RetryPolicy sets the retry policy to use when executing the batch operation func (b *Batch) RetryPolicy(r RetryPolicy) *Batch { b.rt = r return b } func (b *Batch) withContext(ctx context.Context) ExecutableQuery { return b.WithContext(ctx) } // WithContext returns a shallow copy of b with its context // set to ctx. // // The provided context controls the entire lifetime of executing a // query, queries will be canceled and return once the context is // canceled. func (b *Batch) WithContext(ctx context.Context) *Batch { b2 := *b b2.context = ctx return &b2 } // Deprecate: does nothing, cancel the context passed to WithContext func (*Batch) Cancel() { // TODO: delete } // Size returns the number of batch statements to be executed by the batch operation. func (b *Batch) Size() int { return len(b.Entries) } // SerialConsistency sets the consistency level for the // serial phase of conditional updates. That consistency can only be // either SERIAL or LOCAL_SERIAL and if not present, it defaults to // SERIAL. This option will be ignored for anything else that a // conditional update/insert. // // Only available for protocol 3 and above func (b *Batch) SerialConsistency(cons Consistency) *Batch { if !cons.IsSerial() { panic("Serial consistency can only be SERIAL or LOCAL_SERIAL got " + cons.String()) } b.serialCons = cons return b } // DefaultTimestamp will enable the with default timestamp flag on the query. // If enable, this will replace the server side assigned // timestamp as default timestamp. Note that a timestamp in the query itself // will still override this timestamp. This is entirely optional. // // Only available on protocol >= 3 func (b *Batch) DefaultTimestamp(enable bool) *Batch { b.defaultTimestamp = enable return b } // WithTimestamp will enable the with default timestamp flag on the query // like DefaultTimestamp does. But also allows to define value for timestamp. // It works the same way as USING TIMESTAMP in the query itself, but // should not break prepared query optimization. // // Only available on protocol >= 3 func (b *Batch) WithTimestamp(timestamp int64) *Batch { b.DefaultTimestamp(true) b.defaultTimestampValue = timestamp return b } func (b *Batch) attempt(keyspace string, end, start time.Time, iter *Iter, host *HostInfo) { latency := end.Sub(start) attempt, metricsForHost := b.metrics.attempt(1, latency, host, b.observer != nil) if b.observer == nil { return } statements := make([]string, len(b.Entries)) values := make([][]any, len(b.Entries)) for i, entry := range b.Entries { statements[i] = entry.Stmt values[i] = entry.Args } b.observer.ObserveBatch(b.Context(), ObservedBatch{ Keyspace: keyspace, Statements: statements, Values: values, Start: start, End: end, // Rows not used in batch observations // TODO - might be able to support it when using BatchCAS Host: host, Metrics: metricsForHost, Err: iter.err, Attempt: attempt, }) } func (b *Batch) GetRoutingKey() ([]byte, error) { if b.routingKey != nil { return b.routingKey, nil } if len(b.Entries) == 0 { return nil, nil } entry := b.Entries[0] if entry.binding != nil { // bindings do not have the values let's skip it like Query does. return nil, nil } // try to determine the routing key routingKeyInfo, err := b.session.routingKeyInfo(b.Context(), entry.Stmt, b.GetRequestTimeout()) if err != nil { return nil, err } if routingKeyInfo != nil { b.routingInfo.mu.Lock() b.routingInfo.lwt = routingKeyInfo.lwt b.routingInfo.partitioner = routingKeyInfo.partitioner b.routingInfo.mu.Unlock() } return createRoutingKey(routingKeyInfo, entry.Args) } // GetRequestTimeout returns time driver waits for single server response // This timeout is applied to preparing statement request and for query execution requests func (b *Batch) GetRequestTimeout() time.Duration { return b.requestTimeout } // SetRequestTimeout sets time driver waits for single server response // This timeout is applied to preparing statement request and for query execution requests func (b *Batch) SetRequestTimeout(timeout time.Duration) *Batch { b.requestTimeout = timeout return b } func createRoutingKey(routingKeyInfo *routingKeyInfo, values []any) ([]byte, error) { if routingKeyInfo == nil { return nil, nil } if len(routingKeyInfo.indexes) == 1 { // single column routing key routingKey, err := Marshal( routingKeyInfo.types[0], values[routingKeyInfo.indexes[0]], ) if err != nil { return nil, err } return routingKey, nil } // composite routing key // Use a stack-allocated backing array to avoid heap allocation for the // common case where the composite key fits in 256 bytes. Each component // is encoded as: [2-byte big-endian length][marshaled value][0x00 terminator]. var backing [256]byte buf := backing[:0] for i := range routingKeyInfo.indexes { encoded, err := Marshal( routingKeyInfo.types[i], values[routingKeyInfo.indexes[i]], ) if err != nil { return nil, err } n := len(encoded) buf = append(buf, byte(n>>8), byte(n)) buf = append(buf, encoded...) buf = append(buf, 0x00) } // Return a copy so the backing array doesn't escape when the result // is stored beyond this stack frame. routingKey := make([]byte, len(buf)) copy(routingKey, buf) return routingKey, nil } func (b *Batch) borrowForExecution() { // empty, because Batch has no equivalent of Query.Release() // that would race with speculative executions. } func (b *Batch) releaseAfterExecution() { // empty, because Batch has no equivalent of Query.Release() // that would race with speculative executions. } // SetHostID allows to define the host the query should be executed against. If the // host was filtered or otherwise unavailable, then the query will error. If an empty // string is sent, the default behavior, using the configured HostSelectionPolicy will // be used. A hostID can be obtained from HostInfo.HostID() after calling GetHosts(). func (b *Batch) SetHostID(hostID string) *Batch { b.hostID = hostID return b } // GetHostID satisfies ExecutableQuery interface but does noop. func (b *Batch) GetHostID() string { return b.hostID } type BatchType byte const ( LoggedBatch BatchType = 0 UnloggedBatch BatchType = 1 CounterBatch BatchType = 2 ) type BatchEntry struct { binding func(q *QueryInfo) ([]any, error) Stmt string Args []any Idempotent bool } type ColumnInfo struct { TypeInfo TypeInfo Keyspace string Table string Name string } func (c ColumnInfo) String() string { return fmt.Sprintf("[column keyspace=%s table=%s name=%s type=%v]", c.Keyspace, c.Table, c.Name, c.TypeInfo) } // routing key indexes LRU cache type routingKeyInfoLRU struct { lru *lru.Cache[string] mu sync.Mutex } type routingKeyInfo struct { partitioner Partitioner keyspace string table string indexes []int types []TypeInfo lwt bool } func (r *routingKeyInfo) String() string { return fmt.Sprintf("routing key index=%v types=%v", r.indexes, r.types) } func (r *routingKeyInfoLRU) Remove(key string) { r.mu.Lock() r.lru.Remove(key) r.mu.Unlock() } // Max adjusts the maximum size of the cache and cleans up the oldest records if // the new max is lower than the previous value. Not concurrency safe. func (r *routingKeyInfoLRU) Max(max int) { r.mu.Lock() for r.lru.Len() > max { r.lru.RemoveOldest() } r.lru.MaxEntries = max r.mu.Unlock() } type inflightCachedEntry struct { err error value any wg sync.WaitGroup } // GetHosts return a list of hosts in the ring the driver knows of. func (s *Session) GetHosts() []*HostInfo { return s.hostSource.getHostsList() } type HostInformation interface { Peer() net.IP ConnectAddress() net.IP UntranslatedConnectAddress() net.IP BroadcastAddress() net.IP ListenAddress() net.IP RPCAddress() net.IP PreferredIP() net.IP DataCenter() string Rack() string HostID() string WorkLoad() string Partitioner() string ClusterName() string Tokens() []string Port() int IsUp() bool ScyllaShardAwarePort() uint16 ScyllaShardAwarePortTLS() uint16 ScyllaShardCount() int } type HostPoolInfo interface { GetConnectionCount() int GetExcessConnectionCount() int GetShardCount() int String() string InFlight() int Host() HostInformation IsClosed() bool } func (s *Session) GetHostPoolByID(hostID string) HostPoolInfo { hostPool, _ := s.pool.getPoolByHostID(hostID) return hostPool } func (s *Session) IterateHostPools(iter func(info HostPoolInfo) bool) { s.pool.iteratePool(iter) } type ObservedQuery struct { // Start is a time when the query was attempted Start time.Time // End is a time when the query attempt was completed End time.Time // Err is the error in the query. // It only tracks network errors or errors of bad cassandra syntax, in particular selects with no match return nil error // Do not modify the values here, they are shared with multiple goroutines. Err error // Host is a reference to the host where the query was executed. Host *HostInfo // Metrics is the metrics for this attempt Metrics *hostMetrics Keyspace string Statement string // Values holds a slice of bound values for the query. // Do not modify the values here, they are shared with multiple goroutines. Values []any // Rows is the number of rows in the current iter. // In paginated queries, rows from previous scans are not counted. // Rows is not used in batch queries and remains at the default value Rows int // Attempt is the index of attempt at executing this query. // The first attempt is number zero and any retries have non-zero attempt number. Attempt int } // QueryObserver is the interface implemented by query observers / stat collectors. type QueryObserver interface { // ObserveQuery gets called on every query to cassandra, including all queries in an iterator when paging is enabled. // It doesn't get called if there is no query because the session is closed or there are no connections available. // The error reported only shows query errors, i.e. if a SELECT is valid but finds no matches it will be nil. ObserveQuery(context.Context, ObservedQuery) } type ObservedBatch struct { // Start is a time when the batch was attempted Start time.Time // End is a time when the batch attempt was completed End time.Time // Err is the error in the batch query. // It only tracks network errors or errors of bad cassandra syntax, in particular selects with no match return nil error Err error // Host is a reference to the host where the batch was executed. Host *HostInfo // Metrics is the metrics for this attempt Metrics *hostMetrics Keyspace string Statements []string // Values holds a slice of bound values for each statement. // Values[i] are bound values passed to Statements[i]. // Do not modify the values here, they are shared with multiple goroutines. Values [][]any // Attempt is the index of attempt at executing this query. // The first attempt is number zero and any retries have non-zero attempt number. Attempt int } // BatchObserver is the interface implemented by batch observers / stat collectors. type BatchObserver interface { // ObserveBatch gets called on every batch query to cassandra. // It also gets called once for each query in a batch. // It doesn't get called if there is no query because the session is closed or there are no connections available. // The error reported only shows query errors, i.e. if a SELECT is valid but finds no matches it will be nil. // Unlike QueryObserver.ObserveQuery it does no reporting on rows read. ObserveBatch(context.Context, ObservedBatch) } type ObservedConnect struct { // Host is the information about the host about to connect Host *HostInfo Start time.Time // time immediately before the dial is called End time.Time // time immediately after the dial returned // Err is the connection error (if any) Err error } // ConnectObserver is the interface implemented by connect observers / stat collectors. type ConnectObserver interface { // ObserveConnect gets called when a new connection to cassandra is made. ObserveConnect(ObservedConnect) } type Error struct { Message string Code int } func (e Error) Error() string { return e.Message } var ( ErrNotFound = errors.New("not found") ErrUnavailable = errors.New("unavailable") ErrUnsupported = errors.New("feature not supported") ErrTooManyStmts = errors.New("too many statements") ErrUseStmt = errors.New("use statements aren't supported. Please see https://github.com/apache/cassandra-gocql-driver for explanation.") ErrSessionClosed = errors.New("session has been closed") ErrNoConnections = errors.New("gocql: no hosts available in the pool") ErrNoKeyspace = errors.New("no keyspace provided") ErrNoTable = errors.New("no table name provided") ErrKeyspaceDoesNotExist = errors.New("keyspace does not exist") ErrNoMetadata = errors.New("no metadata available") ErrTabletsNotUsed = errors.New("tablets not used") ErrSessionNotReady = errors.New("session is not ready yet") ) type ErrProtocol struct{ error } func NewErrProtocol(format string, args ...any) error { return ErrProtocol{error: fmt.Errorf(format, args...)} } // BatchSizeMaximum is the maximum number of statements a batch operation can have. // This limit is set by cassandra and could change in the future. const BatchSizeMaximum = 65535 ================================================ FILE: session_connect_test.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "net" "strconv" "sync" ) type OneConnTestServer struct { Err error listener net.Listener acceptChan chan struct{} Addr net.IP Port int mu sync.Mutex closed bool } func NewOneConnTestServer() (*OneConnTestServer, error) { lstn, err := net.Listen("tcp4", "localhost:0") if err != nil { return nil, err } addr, port := parseAddressPort(lstn.Addr().String()) return &OneConnTestServer{ listener: lstn, acceptChan: make(chan struct{}), Addr: addr, Port: port, }, nil } func (c *OneConnTestServer) Accepted() chan struct{} { return c.acceptChan } func (c *OneConnTestServer) Close() { c.lockedClose() } func (c *OneConnTestServer) Serve() { conn, err := c.listener.Accept() c.Err = err if conn != nil { conn.Close() } c.lockedClose() } func (c *OneConnTestServer) lockedClose() { c.mu.Lock() defer c.mu.Unlock() if !c.closed { close(c.acceptChan) c.listener.Close() c.closed = true } } func parseAddressPort(hostPort string) (net.IP, int) { host, portStr, err := net.SplitHostPort(hostPort) if err != nil { return net.ParseIP(""), 0 } port, _ := strconv.Atoi(portStr) return net.ParseIP(host), port } ================================================ FILE: session_event_bus_integration_test.go ================================================ //go:build integration // +build integration package gocql import ( "fmt" "testing" "time" "github.com/gocql/gocql/events" ) // WARNING: This test must NOT use t.Parallel(). It listens for schema events // and concurrent DDL from parallel tests could cause spurious matches. // //nolint:paralleltest // listens for schema events from the global control connection func TestSessionEventBusReceivesSchemaChangeEvent(t *testing.T) { cluster := createCluster() cluster.Events.DisableSchemaEvents = false sess, err := cluster.CreateSession() if err != nil { t.Fatalf("unable to create session: %v", err) } defer sess.Close() keyspace := fmt.Sprintf("eventbus_schema_%d", time.Now().UnixNano()) // Filter events to the specific keyspace this test creates, so that // concurrent DDL from parallel tests does not cause spurious matches. sub := sess.SubscribeToEvents("schema-event", 10, func(ev events.Event) bool { if ks, ok := ev.(*events.SchemaChangeKeyspaceEvent); ok { return ks.Keyspace == keyspace } return false }) defer sub.Stop() createStmt := fmt.Sprintf(`CREATE KEYSPACE %s WITH replication = {'class': 'NetworkTopologyStrategy', 'replication_factor': 1}`, keyspace) if err := sess.Query(createStmt).Exec(); err != nil { t.Fatalf("create keyspace: %v", err) } defer sess.Query("DROP KEYSPACE " + keyspace).Exec() select { case ev := <-sub.Events(): if _, ok := ev.(*events.SchemaChangeKeyspaceEvent); !ok { t.Fatalf("unexpected event type: %T", ev) } case <-time.After(30 * time.Second): t.Fatal("timeout waiting for schema change event") } } func TestSessionEventBusReceivesControlReconnectEvent(t *testing.T) { t.Parallel() cluster := createCluster() cluster.Events.DisableTopologyEvents = true cluster.Events.DisableNodeStatusEvents = true sess, err := cluster.CreateSession() if err != nil { t.Fatalf("unable to create session: %v", err) } defer sess.Close() sub := sess.SubscribeToEvents("control-reconnect", 10, func(ev events.Event) bool { return ev.Type() == events.SessionEventTypeControlConnectionRecreated }) defer sub.Stop() if err := sess.control.reconnect(); err != nil { t.Fatalf("control reconnect: %v", err) } select { case ev := <-sub.Events(): if _, ok := ev.(*events.ControlConnectionRecreatedEvent); !ok { t.Fatalf("unexpected event type: %T", ev) } case <-time.After(30 * time.Second): t.Fatal("timeout waiting for control reconnect event") } } ================================================ FILE: session_event_bus_test.go ================================================ //go:build unit // +build unit package gocql import ( "net" "testing" "time" "github.com/gocql/gocql/events" "github.com/gocql/gocql/internal/eventbus" ) func TestSessionEventBusPublishesEvents(t *testing.T) { s := &Session{ eventBus: eventbus.New[events.Event](eventbus.EventBusConfig{ InputEventsQueueSize: 1, }, nil), logger: &nopLogger{}, } if err := s.eventBus.Start(); err != nil { t.Fatalf("starting event bus: %v", err) } defer s.eventBus.Stop() sub := s.SubscribeToEvents("test", 1, nil) defer sub.Stop() ev := &events.StatusChangeEvent{ Change: "UP", Host: net.ParseIP("127.0.0.1"), Port: 9042, } s.publishEvent(ev) select { case received := <-sub.Events(): if received != ev { t.Fatalf("unexpected event pointer: got %p want %p", received, ev) } case <-time.After(2 * time.Second): t.Fatal("timeout waiting for event") } } ================================================ FILE: session_test.go ================================================ //go:build integration // +build integration /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "context" "crypto/tls" "fmt" "net" "sync" "testing" "time" ) func TestSessionAPI(t *testing.T) { t.Parallel() cfg := &ClusterConfig{} s := &Session{ cfg: *cfg, cons: Quorum, policy: RoundRobinHostPolicy(), logger: cfg.logger(), } s.pool = cfg.PoolConfig.buildPool(s) s.executor = &queryExecutor{ pool: s.pool, policy: s.policy, } defer s.Close() s.SetConsistency(All) if s.cons != All { t.Fatalf("expected consistency 'All', got '%v'", s.cons) } s.SetPageSize(100) if s.pageSize != 100 { t.Fatalf("expected pageSize 100, got %v", s.pageSize) } s.SetPrefetch(0.75) if s.prefetch != 0.75 { t.Fatalf("expceted prefetch 0.75, got %v", s.prefetch) } trace := NewTracer(nil) s.SetTrace(trace) if s.trace != trace { t.Fatalf("expected tracer '%v',got '%v'", trace, s.trace) } qry := s.Query("test", 1) if v, ok := qry.values[0].(int); !ok { t.Fatalf("expected qry.values[0] to be an int, got %v", qry.values[0]) } else if v != 1 { t.Fatalf("expceted qry.values[0] to be 1, got %v", v) } else if qry.stmt != "test" { t.Fatalf("expected qry.stmt to be 'test', got '%v'", qry.stmt) } boundQry := s.Bind("test", func(q *QueryInfo) ([]any, error) { return nil, nil }) if boundQry.binding == nil { t.Fatal("expected qry.binding to be defined, got nil") } else if boundQry.stmt != "test" { t.Fatalf("expected qry.stmt to be 'test', got '%v'", boundQry.stmt) } itr := s.executeQuery(qry) if itr.err != ErrSessionNotReady { t.Fatalf("expected itr.err to be '%v', got '%v'", ErrSessionNotReady, itr.err) } testBatch := s.Batch(LoggedBatch) testBatch.Query("test") err := s.ExecuteBatch(testBatch) if err != ErrSessionNotReady { t.Fatalf("expected session.ExecuteBatch to return '%v', got '%v'", ErrSessionNotReady, err) } s.Close() if !s.Closed() { t.Fatal("expected s.Closed() to be true, got false") } //Should just return cleanly s.Close() err = s.ExecuteBatch(testBatch) if err != ErrSessionClosed { t.Fatalf("expected session.ExecuteBatch to return '%v', got '%v'", ErrSessionClosed, err) } } type funcQueryObserver func(context.Context, ObservedQuery) func (f funcQueryObserver) ObserveQuery(ctx context.Context, o ObservedQuery) { f(ctx, o) } func TestQueryBasicAPI(t *testing.T) { t.Parallel() qry := &Query{routingInfo: &queryRoutingInfo{}} // Initiate host ip := "127.0.0.1" hostID := TimeUUID() qry.metrics = preFilledQueryMetrics(map[UUID]*hostMetrics{hostID: {Attempts: 0, TotalLatency: 0}}) if qry.Latency() != 0 { t.Fatalf("expected Query.Latency() to return 0, got %v", qry.Latency()) } qry.metrics = preFilledQueryMetrics(map[UUID]*hostMetrics{hostID: {Attempts: 2, TotalLatency: 4}}) if qry.Attempts() != 2 { t.Fatalf("expected Query.Attempts() to return 2, got %v", qry.Attempts()) } if qry.Latency() != 2 { t.Fatalf("expected Query.Latency() to return 2, got %v", qry.Latency()) } qry.AddAttempts(2, &HostInfo{hostname: ip, connectAddress: net.ParseIP(ip), port: 9042}) if qry.Attempts() != 4 { t.Fatalf("expected Query.Attempts() to return 4, got %v", qry.Attempts()) } qry.Consistency(All) if qry.GetConsistency() != All { t.Fatalf("expected Query.GetConsistency to return 'All', got '%s'", qry.GetConsistency()) } qry.Consistency(LocalSerial) if qry.GetConsistency() != LocalSerial { t.Fatalf("expected Query.GetConsistency to return 'LocalSerial', got '%s'", qry.GetConsistency()) } qry.SerialConsistency(LocalSerial) if qry.GetConsistency() != LocalSerial { t.Fatalf("expected Query.GetConsistency to return 'LocalSerial', got '%s'", qry.GetConsistency()) } trace := NewTracer(nil) qry.Trace(trace) if qry.trace != trace { t.Fatalf("expected Query.Trace to be '%v', got '%v'", trace, qry.trace) } observer := funcQueryObserver(func(context.Context, ObservedQuery) {}) qry.Observer(observer) if qry.observer == nil { // can't compare func to func, checking not nil instead t.Fatal("expected Query.QueryObserver to be set, got nil") } qry.PageSize(10) if qry.pageSize != 10 { t.Fatalf("expected Query.PageSize to be 10, got %v", qry.pageSize) } qry.Prefetch(0.75) if qry.prefetch != 0.75 { t.Fatalf("expected Query.Prefetch to be 0.75, got %v", qry.prefetch) } rt := &SimpleRetryPolicy{NumRetries: 3} if qry.RetryPolicy(rt); qry.rt != rt { t.Fatalf("expected Query.RetryPolicy to be '%v', got '%v'", rt, qry.rt) } qry.Bind(qry) if qry.values[0] != qry { t.Fatalf("expected Query.Values[0] to be '%v', got '%v'", qry, qry.values[0]) } } func TestQueryShouldPrepare(t *testing.T) { t.Parallel() toPrepare := []string{"select * ", "INSERT INTO", "update table", "delete from", "begin batch"} cantPrepare := []string{"create table", "USE table", "LIST keyspaces", "alter table", "drop table", "grant user", "revoke user"} for i := 0; i < len(toPrepare); i++ { q := &Query{stmt: toPrepare[i], routingInfo: &queryRoutingInfo{}} if !q.shouldPrepare() { t.Fatalf("expected Query.shouldPrepare to return true, got false for statement '%v'", toPrepare[i]) } } for i := 0; i < len(cantPrepare); i++ { q := &Query{stmt: cantPrepare[i], routingInfo: &queryRoutingInfo{}} if q.shouldPrepare() { t.Fatalf("expected Query.shouldPrepare to return false, got true for statement '%v'", cantPrepare[i]) } } } func TestBatchBasicAPI(t *testing.T) { t.Parallel() cfg := &ClusterConfig{RetryPolicy: &SimpleRetryPolicy{NumRetries: 2}} s := &Session{ cfg: *cfg, cons: Quorum, logger: cfg.logger(), } defer s.Close() s.pool = cfg.PoolConfig.buildPool(s) // Test UnloggedBatch b := s.Batch(UnloggedBatch) if b.Type != UnloggedBatch { t.Fatalf("expceted batch.Type to be '%v', got '%v'", UnloggedBatch, b.Type) } else if b.rt != cfg.RetryPolicy { t.Fatalf("expceted batch.RetryPolicy to be '%v', got '%v'", cfg.RetryPolicy, b.rt) } // Test LoggedBatch b = s.Batch(LoggedBatch) if b.Type != LoggedBatch { t.Fatalf("expected batch.Type to be '%v', got '%v'", LoggedBatch, b.Type) } ip := "127.0.0.1" hostID := TimeUUID() // Test attempts b.metrics = preFilledQueryMetrics(map[UUID]*hostMetrics{hostID: {Attempts: 1}}) if b.Attempts() != 1 { t.Fatalf("expected batch.Attempts() to return %v, got %v", 1, b.Attempts()) } b.AddAttempts(2, &HostInfo{hostname: ip, connectAddress: net.ParseIP(ip), port: 9042}) if b.Attempts() != 3 { t.Fatalf("expected batch.Attempts() to return %v, got %v", 3, b.Attempts()) } // Test latency if b.Latency() != 0 { t.Fatalf("expected batch.Latency() to be 0, got %v", b.Latency()) } b.metrics = preFilledQueryMetrics(map[UUID]*hostMetrics{hostID: {Attempts: 1, TotalLatency: 4}}) if b.Latency() != 4 { t.Fatalf("expected batch.Latency() to return %v, got %v", 4, b.Latency()) } // Test Consistency b.Cons = One if b.GetConsistency() != One { t.Fatalf("expected batch.GetConsistency() to return 'One', got '%s'", b.GetConsistency()) } trace := NewTracer(nil) b.Trace(trace) if b.trace != trace { t.Fatalf("expected batch.Trace to be '%v', got '%v'", trace, b.trace) } // Test batch.Query() b.Query("test", 1) if b.Entries[0].Stmt != "test" { t.Fatalf("expected batch.Entries[0].Statement to be 'test', got '%v'", b.Entries[0].Stmt) } else if b.Entries[0].Args[0].(int) != 1 { t.Fatalf("expected batch.Entries[0].Args[0] to be 1, got %v", b.Entries[0].Args[0]) } b.Bind("test2", func(q *QueryInfo) ([]any, error) { return nil, nil }) if b.Entries[1].Stmt != "test2" { t.Fatalf("expected batch.Entries[1].Statement to be 'test2', got '%v'", b.Entries[1].Stmt) } else if b.Entries[1].binding == nil { t.Fatal("expected batch.Entries[1].binding to be defined, got nil") } // Test RetryPolicy r := &SimpleRetryPolicy{NumRetries: 4} b.RetryPolicy(r) if b.rt != r { t.Fatalf("expected batch.RetryPolicy to be '%v', got '%v'", r, b.rt) } if b.Size() != 2 { t.Fatalf("expected batch.Size() to return 2, got %v", b.Size()) } } func TestConsistencyNames(t *testing.T) { t.Parallel() names := map[fmt.Stringer]string{ Any: "ANY", One: "ONE", Two: "TWO", Three: "THREE", Quorum: "QUORUM", All: "ALL", LocalQuorum: "LOCAL_QUORUM", EachQuorum: "EACH_QUORUM", Serial: "SERIAL", LocalSerial: "LOCAL_SERIAL", LocalOne: "LOCAL_ONE", } for k, v := range names { if k.String() != v { t.Fatalf("expected '%v', got '%v'", v, k.String()) } } } func TestIsUseStatement(t *testing.T) { t.Parallel() testCases := []struct { input string exp bool }{ {"USE foo", true}, {"USe foo", true}, {"UsE foo", true}, {"Use foo", true}, {"uSE foo", true}, {"uSe foo", true}, {"usE foo", true}, {"use foo", true}, {"SELECT ", false}, {"UPDATE ", false}, {"INSERT ", false}, {"", false}, } for _, tc := range testCases { v := isUseStatement(tc.input) if v != tc.exp { t.Fatalf("expected %v but got %v for statement %q", tc.exp, v, tc.input) } } } type simpleTestRetryPolycy struct { RetryType RetryType NumRetries int } func (p *simpleTestRetryPolycy) Attempt(q RetryableQuery) bool { return q.Attempts() <= p.NumRetries } func (p *simpleTestRetryPolycy) GetRetryType(error) RetryType { return p.RetryType } // TestRetryType_IgnoreRethrow verify that with Ignore/Rethrow retry types: // - retries stopped // - return error is not nil on Rethrow, Ignore // - observed error is not nil func TestRetryType_IgnoreRethrow(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() var observedErr error var observedAttempts int resetObserved := func() { observedErr = nil observedAttempts = 0 } observer := funcQueryObserver(func(ctx context.Context, o ObservedQuery) { observedErr = o.Err observedAttempts++ }) for i, caseParams := range []struct { retries int retryType RetryType }{ {0, Ignore}, // check that stops retries {1, Ignore}, // check that stops retries {0, Rethrow}, // check that stops retries {1, Rethrow}, // check that stops retries } { retryPolicy := &simpleTestRetryPolycy{RetryType: caseParams.retryType, NumRetries: caseParams.retries} err := session.Query("INSERT INTO gocql_test.invalid_table(value) VALUES(1)").Idempotent(true).RetryPolicy(retryPolicy).Observer(observer).Exec() if err == nil { t.Fatalf("case %d [%v] Expected unconfigured table error, got: nil", i, caseParams.retryType) } if observedErr == nil { t.Fatalf("case %d expected unconfigured table error in Obserer, got: nil", i) } expectedAttempts := caseParams.retries if expectedAttempts == 0 { expectedAttempts = 1 } if observedAttempts != expectedAttempts { t.Fatalf("case %d expected %d attempts, got: %d", i, expectedAttempts, observedAttempts) } resetObserved() } } type sessionCache struct { orig tls.ClientSessionCache values map[string][][]byte caches map[string][]int64 valuesLock sync.Mutex } func (c *sessionCache) Get(sessionKey string) (session *tls.ClientSessionState, ok bool) { return c.orig.Get(sessionKey) } func (c *sessionCache) Put(sessionKey string, cs *tls.ClientSessionState) { ticket, _, err := cs.ResumptionState() if err != nil { panic(err) } if len(ticket) == 0 { panic("ticket should not be empty") } c.valuesLock.Lock() c.values[sessionKey] = append(c.values[sessionKey], ticket) c.valuesLock.Unlock() c.orig.Put(sessionKey, cs) } func (c *sessionCache) NumberOfTickets() int { c.valuesLock.Lock() defer c.valuesLock.Unlock() total := 0 for _, tickets := range c.values { total += len(tickets) } return total } func newSessionCache() *sessionCache { return &sessionCache{ orig: tls.NewLRUClientSessionCache(1024), values: make(map[string][][]byte), caches: make(map[string][]int64), valuesLock: sync.Mutex{}, } } func withSessionCache(cache tls.ClientSessionCache) func(config *ClusterConfig) { return func(config *ClusterConfig) { config.SslOpts = &SslOptions{ EnableHostVerification: false, Config: &tls.Config{ ClientSessionCache: cache, InsecureSkipVerify: true, }, } } } func TestTLSTicketResumption(t *testing.T) { t.Parallel() t.Skip("TLS ticket resumption is only supported by 2025.2 and later") c := newSessionCache() session := createSession(t, withSessionCache(c)) defer session.Close() waitAllConnectionsOpened := func() error { println("wait all connections opened") defer println("end of wait all connections closed") endtime := time.Now().UTC().Add(time.Second * 10) for { if time.Now().UTC().After(endtime) { return fmt.Errorf("timed out waiting for all connections opened") } missing, err := session.MissingConnections() if err != nil { return fmt.Errorf("failed to get missing connections count: %w", err) } if missing == 0 { return nil } time.Sleep(time.Millisecond * 100) } } if err := waitAllConnectionsOpened(); err != nil { t.Fatal(err) } tickets := c.NumberOfTickets() if tickets == 0 { t.Fatal("no tickets learned, which means that server does not support TLS tickets") } session.CloseAllConnections() if err := waitAllConnectionsOpened(); err != nil { t.Fatal(err) } newTickets1 := c.NumberOfTickets() session.CloseAllConnections() if err := waitAllConnectionsOpened(); err != nil { t.Fatal(err) } newTickets2 := c.NumberOfTickets() if newTickets1 != tickets { t.Fatalf("new tickets learned, it looks like tls tickets where not reused: new %d, was %d", newTickets1, tickets) } if newTickets2 != tickets { t.Fatalf("new tickets learned, it looks like tls tickets where not reused: new %d, was %d", newTickets2, tickets) } } ================================================ FILE: session_unit_test.go ================================================ //go:build unit // +build unit /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "context" "errors" "reflect" "slices" "strings" "sync/atomic" "testing" "time" "github.com/gocql/gocql/tablets" ) func TestShouldPrepareNonDML(t *testing.T) { t.Parallel() nonDMLStatements := []string{ "CREATE TABLE ks.tbl (id int PRIMARY KEY)", "ALTER TABLE ks.tbl ADD col text", "DROP TABLE ks.tbl", "TRUNCATE ks.tbl", "CREATE KEYSPACE ks WITH replication = {'class': 'SimpleStrategy'}", "DROP KEYSPACE ks", "GRANT SELECT ON ks.tbl TO user1", "USE ks", } for _, stmt := range nonDMLStatements { t.Run(stmt, func(t *testing.T) { q := &Query{stmt: stmt, routingInfo: &queryRoutingInfo{}} if q.shouldPrepare() { t.Errorf("shouldPrepare(%q) = true, want false", stmt) } }) } } func TestShouldPrepareDML(t *testing.T) { t.Parallel() dmlStatements := []string{ "SELECT * FROM ks.tbl", "INSERT INTO ks.tbl (id) VALUES (?)", "UPDATE ks.tbl SET col = ? WHERE id = ?", "DELETE FROM ks.tbl WHERE id = ?", "BEGIN BATCH INSERT INTO ks.tbl (id) VALUES (1) APPLY BATCH", "BEGIN BATCH INSERT INTO ks.tbl (id) VALUES (1) APPLY BATCH;", "BEGIN UNLOGGED BATCH INSERT INTO ks.tbl (id) VALUES (1) APPLY BATCH", " SELECT * FROM ks.tbl", "\t INSERT INTO ks.tbl (id) VALUES (?)", "\u00a0SELECT * FROM ks.tbl", } for _, stmt := range dmlStatements { t.Run(stmt, func(t *testing.T) { q := &Query{stmt: stmt, routingInfo: &queryRoutingInfo{}} if !q.shouldPrepare() { t.Errorf("shouldPrepare(%q) = false, want true", stmt) } }) } } func TestAsyncSessionInit(t *testing.T) { t.Parallel() // Build a 3 node cluster to test host metric mapping var addresses = []string{ "127.0.0.1", "127.0.0.2", "127.0.0.3", } // only build 1 of the servers so that we can test not connecting to the last // one srv := NewTestServerWithAddress(addresses[0]+":0", t, defaultProto, context.Background()) defer srv.Stop() // just choose any port cluster := testCluster(defaultProto, srv.Address, addresses[1]+":9999", addresses[2]+":9999") cluster.PoolConfig.HostSelectionPolicy = SingleHostReadyPolicy(RoundRobinHostPolicy()) db, err := cluster.CreateSession() if err != nil { t.Fatalf("NewCluster: %v", err) } defer db.Close() // make sure the session works if err := db.Query("void").Exec(); err != nil { t.Fatalf("unexpected error from void") } } func TestExtractKeyspaceTableFromDDL(t *testing.T) { t.Parallel() tests := []struct { name string ddl string wantKS string wantTable string }{ { name: "simple_create_table", ddl: "CREATE TABLE gocql_test.my_table (id int PRIMARY KEY)", wantKS: "gocql_test", wantTable: "my_table", }, { name: "create_table_if_not_exists", ddl: "CREATE TABLE IF NOT EXISTS gocql_test.my_table (id int PRIMARY KEY)", wantKS: "gocql_test", wantTable: "my_table", }, { name: "lowercase_create_table", ddl: "create table gocql_test.my_table (id int primary key)", wantKS: "gocql_test", wantTable: "my_table", }, { name: "mixed_case_if_not_exists", ddl: "Create Table If Not Exists gocql_test.my_table (id int PRIMARY KEY)", wantKS: "gocql_test", wantTable: "my_table", }, { name: "no_keyspace_prefix", ddl: "CREATE TABLE my_table (id int PRIMARY KEY)", wantKS: "", wantTable: "", }, { name: "empty_string", ddl: "", wantKS: "", wantTable: "", }, { name: "create_keyspace_ignored", ddl: "CREATE KEYSPACE my_ks WITH replication = {}", wantKS: "", wantTable: "", }, { name: "materialized_view_ignored", ddl: "CREATE MATERIALIZED VIEW my_ks.my_view AS SELECT * FROM my_ks.my_table WHERE id IS NOT NULL PRIMARY KEY (id)", wantKS: "", wantTable: "", }, { name: "multiline_ddl", ddl: "CREATE TABLE gocql_test.test_single_routing_key (\n\tfirst_id int,\n\tsecond_id int,\n\tPRIMARY KEY (first_id, second_id)\n)", wantKS: "gocql_test", wantTable: "test_single_routing_key", }, { name: "tablets_disabled_keyspace", ddl: "CREATE TABLE gocql_test_tablets_disabled.my_table (id int PRIMARY KEY)", wantKS: "gocql_test_tablets_disabled", wantTable: "my_table", }, { name: "drop_table_if_exists", ddl: "DROP TABLE IF EXISTS gocql_test.my_table", wantKS: "gocql_test", wantTable: "my_table", }, { name: "drop_table_if_exists_lowercase", ddl: "drop table if exists gocql_test.my_table", wantKS: "gocql_test", wantTable: "my_table", }, { name: "drop_table_no_keyspace", ddl: "DROP TABLE IF EXISTS my_table", wantKS: "", wantTable: "", }, { name: "table_with_space_before_paren", ddl: "CREATE TABLE gocql_test.t1 (id int PRIMARY KEY)", wantKS: "gocql_test", wantTable: "t1", }, { name: "drop_keyspace_returns_empty", ddl: "DROP KEYSPACE IF EXISTS gocql_test", wantKS: "", wantTable: "", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { gotKS, gotTable := extractKeyspaceTableFromDDL(tt.ddl) if gotKS != tt.wantKS { t.Errorf("extractKeyspaceTableFromDDL(%q) keyspace = %q, want %q", tt.ddl, gotKS, tt.wantKS) } if gotTable != tt.wantTable { t.Errorf("extractKeyspaceTableFromDDL(%q) table = %q, want %q", tt.ddl, gotTable, tt.wantTable) } }) } } func TestTableMetadataAfterInvalidation(t *testing.T) { t.Parallel() ctrl := &schemaDataMock{ knownKeyspaces: map[string][]tableInfo{ "test_ks": { {name: "tbl_a", columns: []columnInfo{{name: "id", kind: "partition_key", position: 0}}}, }, }, } s := newSchemaEventTestSessionWithMock(ctrl) defer s.Close() s.isInitialized = true populateKeyspace(s, "test_ks", "tbl_a") tbl, err := s.TableMetadata("test_ks", "tbl_a") if err != nil { t.Fatalf("initial TableMetadata failed: %v", err) } if tbl.Name != "tbl_a" { t.Fatalf("expected table name tbl_a, got %s", tbl.Name) } s.metadataDescriber.invalidateTableSchema("test_ks", "tbl_a") ctrl.resetQueries() tbl, err = s.TableMetadata("test_ks", "tbl_a") if err != nil { t.Fatalf("TableMetadata after invalidation failed: %v", err) } if tbl.Name != "tbl_a" { t.Fatalf("expected table name tbl_a, got %s", tbl.Name) } if ctrl.getQueryCount() == 0 { t.Fatal("expected queries to refresh tbl_a after invalidation") } } func TestTableMetadataAfterKeyspaceInvalidation(t *testing.T) { t.Parallel() ctrl := &schemaDataMock{ knownKeyspaces: map[string][]tableInfo{ "test_ks": { {name: "tbl_a", columns: []columnInfo{{name: "id", kind: "partition_key", position: 0}}}, }, }, } s := newSchemaEventTestSessionWithMock(ctrl) defer s.Close() s.isInitialized = true populateKeyspace(s, "test_ks", "tbl_a") _, err := s.TableMetadata("test_ks", "tbl_a") if err != nil { t.Fatalf("initial TableMetadata failed: %v", err) } s.metadataDescriber.invalidateKeyspaceSchema("test_ks") ctrl.resetQueries() tbl, err := s.TableMetadata("test_ks", "tbl_a") if err != nil { t.Fatalf("TableMetadata after keyspace invalidation failed: %v", err) } if tbl.Name != "tbl_a" { t.Fatalf("expected table name tbl_a, got %s", tbl.Name) } if ctrl.getQueryCount() == 0 { t.Fatal("expected queries to reload keyspace after invalidation") } } func newTestSessionForTableMetadata(ctrl *schemaDataMock) *Session { s := newSchemaEventTestSessionWithMock(ctrl) s.isInitialized = true return s } func TestScyllaIsCdcTableAfterInvalidation(t *testing.T) { t.Parallel() ctrl := &schemaDataMock{ knownKeyspaces: map[string][]tableInfo{ "test_ks": { {name: "tbl_scylla_cdc_log", columns: []columnInfo{{name: "id", kind: "partition_key", position: 0}}}, }, }, } s := newTestSessionForTableMetadata(ctrl) defer s.Close() populateKeyspace(s, "test_ks", "tbl_scylla_cdc_log") _, err := scyllaIsCdcTable(s, "test_ks", "tbl_scylla_cdc_log") if err != nil { t.Fatalf("initial scyllaIsCdcTable failed: %v", err) } s.metadataDescriber.invalidateTableSchema("test_ks", "tbl_scylla_cdc_log") ctrl.resetQueries() _, err = scyllaIsCdcTable(s, "test_ks", "tbl_scylla_cdc_log") if err != nil { t.Fatalf("scyllaIsCdcTable after invalidation failed: %v", err) } if ctrl.getQueryCount() == 0 { t.Fatal("expected queries to refresh tbl_scylla_cdc_log after invalidation") } } func TestScyllaIsCdcTableNotCdcSuffix(t *testing.T) { t.Parallel() ctrl := &schemaDataMock{ knownKeyspaces: map[string][]tableInfo{ "test_ks": { {name: "regular_table", columns: []columnInfo{{name: "id", kind: "partition_key", position: 0}}}, }, }, } s := newTestSessionForTableMetadata(ctrl) defer s.Close() populateKeyspace(s, "test_ks", "regular_table") isCdc, err := scyllaIsCdcTable(s, "test_ks", "regular_table") if err != nil { t.Fatalf("scyllaIsCdcTable failed: %v", err) } if isCdc { t.Fatal("expected regular_table to not be a CDC table") } } func TestTestTableName(t *testing.T) { t.Parallel() tests := []struct { name string parts []string want string }{ { name: "basic", want: "testtesttablename_basic", }, { name: "with_parts", parts: []string{"single"}, want: "testtesttablename_with_parts_single", }, { name: "multiple_parts", parts: []string{"foo", "bar"}, want: "testtesttablename_multiple_parts_foo_bar", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := testTableName(t, tt.parts...) if got != tt.want { t.Errorf("testTableName() = %q, want %q", got, tt.want) } }) } } func TestTestTableNameSanitizesSpecialChars(t *testing.T) { t.Parallel() t.Run("sub/with/slashes", func(t *testing.T) { got := testTableName(t) if strings.Contains(got, "/") { t.Errorf("expected no slashes, got %q", got) } if strings.Contains(got, "__") { t.Errorf("expected no consecutive underscores, got %q", got) } }) } func TestTestTableNameTruncation(t *testing.T) { t.Parallel() long := "abcdefghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz" t.Run(long, func(t *testing.T) { got := testTableName(t, "extra") if len(got) > maxCQLIdentifierLen { t.Errorf("len = %d, want <= %d; value = %q", len(got), maxCQLIdentifierLen, got) } // Should preserve chars from both the start and end around the hash. if got[:5] != "testt" { t.Errorf("expected prefix from test name, got %q", got) } if !strings.HasSuffix(got, "_extra") { t.Errorf("expected suffix from test name and parts, got %q", got) } if len(got) != maxCQLIdentifierLen { t.Errorf("expected truncated name to use full identifier budget, got len=%d value=%q", len(got), got) } if got[15] != '_' || got[32] != '_' { t.Errorf("expected __ structure, got %q", got) } for _, ch := range got[16:32] { if (ch < '0' || ch > '9') && (ch < 'a' || ch > 'f') { t.Errorf("expected hex hash in the middle, got %q", got) break } } }) } func TestTestTableNameUniqueness(t *testing.T) { t.Parallel() a := testTableName(t, "alpha") b := testTableName(t, "beta") if a == b { t.Errorf("expected different names, both got %q", a) } } // testWarningFramer is a mock framerInterface that returns configurable warnings. type testWarningFramer struct { warnings []string customPayload map[string][]byte released bool } func (f *testWarningFramer) ReadBytesInternal() ([]byte, error) { return nil, nil } func (f *testWarningFramer) GetCustomPayload() map[string][]byte { return f.customPayload } func (f *testWarningFramer) GetHeaderWarnings() []string { return f.warnings } func (f *testWarningFramer) Release() { f.released = true } type recordingWarningHandler struct { calls int lastHost *HostInfo lastQry ExecutableQuery queryStmt string warnings []string } func (h *recordingWarningHandler) HandleWarnings(qry ExecutableQuery, host *HostInfo, warnings []string) { h.calls++ h.lastQry = qry h.lastHost = host if query, ok := qry.(*Query); ok { h.queryStmt = query.stmt } h.warnings = slices.Clone(warnings) } type staticConnPicker struct { conn *Conn } func (p staticConnPicker) Pick(Token, ExecutableQuery) *Conn { return p.conn } func (p staticConnPicker) Put(*Conn) error { return nil } func (p staticConnPicker) Remove(*Conn) {} func (p staticConnPicker) InFlight() int { return 0 } func (p staticConnPicker) Size() (int, int) { return 1, 0 } func (p staticConnPicker) Close() {} func (p staticConnPicker) NextShard() (shardID, nrShards int) { return 0, 0 } func (p staticConnPicker) GetConnectionCount() int { return 1 } func (p staticConnPicker) GetExcessConnectionCount() int { return 0 } func (p staticConnPicker) GetShardCount() int { return 0 } type staticSelectedHost struct { host *HostInfo } func (h staticSelectedHost) Info() *HostInfo { return h.host } func (h staticSelectedHost) Token() Token { return nil } func (h staticSelectedHost) Mark(error) {} type pagingTestConn struct { executeQueryFunc func(ctx context.Context, qry *Query) *Iter } func (*pagingTestConn) Close() {} func (*pagingTestConn) exec(context.Context, frameBuilder, Tracer, time.Duration) (*framer, error) { return nil, nil } func (*pagingTestConn) awaitSchemaAgreement(context.Context) error { return nil } func (c *pagingTestConn) executeQuery(ctx context.Context, qry *Query) *Iter { return c.executeQueryFunc(ctx, qry) } func (*pagingTestConn) querySystem(context.Context, string, ...any) *Iter { return nil } func (*pagingTestConn) getIsSchemaV2() bool { return false } func (*pagingTestConn) setSchemaV2(bool) {} func (*pagingTestConn) getScyllaSupported() ScyllaConnectionFeatures { return ScyllaConnectionFeatures{} } type fixedRetryPolicy struct { maxRetries int retryType RetryType } func (p *fixedRetryPolicy) Attempt(q RetryableQuery) bool { return q.Attempts() <= p.maxRetries } func (p *fixedRetryPolicy) GetRetryType(error) RetryType { return p.retryType } type executorTestQuery struct { ctx context.Context rt RetryPolicy spec SpeculativeExecutionPolicy idempotent bool consistency Consistency attempts int borrowed int released int executeFunc func(context.Context, *Conn) *Iter } func (q *executorTestQuery) borrowForExecution() { q.borrowed++ } func (q *executorTestQuery) releaseAfterExecution() { q.released++ } func (q *executorTestQuery) execute(ctx context.Context, conn *Conn) *Iter { return q.executeFunc(ctx, conn) } func (q *executorTestQuery) attempt(string, time.Time, time.Time, *Iter, *HostInfo) { q.attempts++ } func (q *executorTestQuery) retryPolicy() RetryPolicy { return q.rt } func (q *executorTestQuery) speculativeExecutionPolicy() SpeculativeExecutionPolicy { if q.spec == nil { return NonSpeculativeExecution{} } return q.spec } func (q *executorTestQuery) GetRoutingKey() ([]byte, error) { return nil, nil } func (q *executorTestQuery) Keyspace() string { return "" } func (q *executorTestQuery) Table() string { return "" } func (q *executorTestQuery) IsIdempotent() bool { return q.idempotent } func (q *executorTestQuery) IsLWT() bool { return false } func (q *executorTestQuery) GetCustomPartitioner() Partitioner { return nil } func (q *executorTestQuery) GetHostID() string { return "" } func (q *executorTestQuery) withContext(ctx context.Context) ExecutableQuery { q2 := *q q2.ctx = ctx return &q2 } func (q *executorTestQuery) Attempts() int { return q.attempts } func (q *executorTestQuery) SetConsistency(c Consistency) { q.consistency = c } func (q *executorTestQuery) GetConsistency() Consistency { return q.consistency } func (q *executorTestQuery) Context() context.Context { if q.ctx == nil { return context.Background() } return q.ctx } func (q *executorTestQuery) GetSession() *Session { return nil } func newTestQueryExecutor(host *HostInfo) *queryExecutor { return &queryExecutor{ pool: &policyConnPool{ hostConnPools: map[string]*hostConnPool{ host.HostID(): &hostConnPool{ host: host, connPicker: staticConnPicker{conn: &Conn{}}, }, }, }, } } func newWarningTestQuery() *Query { return &Query{ context: context.Background(), routingInfo: &queryRoutingInfo{}, metrics: &queryMetrics{m: make(map[UUID]*hostMetrics)}, rt: &SimpleRetryPolicy{NumRetries: 0}, spec: NonSpeculativeExecution{}, } } func TestIterWarnings(t *testing.T) { t.Parallel() t.Run("NoFramer", func(t *testing.T) { iter := &Iter{} warnings := iter.Warnings() if len(warnings) != 0 { t.Errorf("expected no warnings, got %v", warnings) } }) t.Run("SinglePage", func(t *testing.T) { framer := &testWarningFramer{warnings: []string{"warn1", "warn2"}} iter := &Iter{framer: framer} warnings := iter.Warnings() want := []string{"warn1", "warn2"} if !slices.Equal(warnings, want) { t.Errorf("Warnings() = %v, want %v", warnings, want) } }) t.Run("ReturnsCopy", func(t *testing.T) { framer := &testWarningFramer{warnings: []string{"warn1"}} iter := &Iter{framer: framer} w1 := iter.Warnings() w2 := iter.Warnings() // Mutating w1 should not affect w2 w1[0] = "mutated" if w2[0] == "mutated" { t.Error("Warnings() returned a shared slice, expected independent copies") } }) t.Run("AccumulatedAcrossPages", func(t *testing.T) { page1Framer := &testWarningFramer{warnings: []string{"page1-warn1", "page1-warn2"}} iter := &Iter{ framer: page1Framer, numRows: 1, pos: 1, next: nil, } if w := iter.framer.GetHeaderWarnings(); len(w) > 0 { iter.allWarnings = append(iter.allWarnings, w...) } iter.framer.Release() page2Framer := &testWarningFramer{warnings: []string{"page2-warn1"}} iter.framer = page2Framer warnings := iter.Warnings() want := []string{"page1-warn1", "page1-warn2", "page2-warn1"} if !slices.Equal(warnings, want) { t.Errorf("Warnings() = %v, want %v", warnings, want) } if !page1Framer.released { t.Error("page 1 framer was not released") } }) t.Run("AfterClose", func(t *testing.T) { framer := &testWarningFramer{warnings: []string{"last-page-warn"}} iter := &Iter{ framer: framer, allWarnings: []string{"prev-page-warn"}, } iter.Close() if !framer.released { t.Error("framer was not released on Close()") } if iter.framer != nil { t.Error("framer was not nilled on Close()") } warnings := iter.Warnings() want := []string{"prev-page-warn", "last-page-warn"} if !slices.Equal(warnings, want) { t.Errorf("Warnings() after Close() = %v, want %v", warnings, want) } }) t.Run("EmptyPages", func(t *testing.T) { iter := &Iter{ allWarnings: []string{"page1-warn"}, } page2Framer := &testWarningFramer{warnings: nil} iter.framer = page2Framer warnings := iter.Warnings() want := []string{"page1-warn"} if !slices.Equal(warnings, want) { t.Errorf("Warnings() = %v, want %v", warnings, want) } }) t.Run("CloseIdempotent", func(t *testing.T) { framer := &testWarningFramer{warnings: []string{"warn"}} iter := &Iter{framer: framer} iter.Close() iter.Close() warnings := iter.Warnings() want := []string{"warn"} if !slices.Equal(warnings, want) { t.Errorf("Warnings() after double Close() = %v, want %v", warnings, want) } }) } func TestNewErrorIterWithReleasedFramer(t *testing.T) { t.Parallel() t.Run("PreservesMetadata", func(t *testing.T) { payload := map[string][]byte{"tablet": {1, 2, 3}} framer := &testWarningFramer{ warnings: []string{"warn1"}, customPayload: payload, } iter := newErrorIterWithReleasedFramer(errors.New("boom"), framer) if !framer.released { t.Fatal("expected framer to be released") } if !slices.Equal(iter.Warnings(), []string{"warn1"}) { t.Fatalf("Warnings() = %v, want %v", iter.Warnings(), []string{"warn1"}) } if !reflect.DeepEqual(iter.GetCustomPayload(), payload) { t.Fatalf("GetCustomPayload() = %v, want %v", iter.GetCustomPayload(), payload) } }) } func TestIterWarningHandler(t *testing.T) { t.Parallel() t.Run("CloseDispatchesAccumulatedWarnings", func(t *testing.T) { handler := &recordingWarningHandler{} host := &HostInfo{hostId: UUID{1}} qry := &Query{ routingInfo: &queryRoutingInfo{}, metrics: &queryMetrics{m: make(map[UUID]*hostMetrics)}, } iter := (&Iter{ framer: &testWarningFramer{warnings: []string{"page2"}}, allWarnings: []string{"page1"}, host: host, }).bindWarningHandler(qry, handler) if err := iter.Close(); err != nil { t.Fatalf("Close() returned unexpected error: %v", err) } want := []string{"page1", "page2"} if !slices.Equal(handler.warnings, want) { t.Fatalf("handler warnings = %v, want %v", handler.warnings, want) } if handler.calls != 1 { t.Fatalf("handler call count = %d, want 1", handler.calls) } if handler.lastHost != host { t.Fatal("handler host mismatch") } if handler.lastQry != qry { t.Fatal("handler query mismatch") } }) t.Run("CloseIsIdempotent", func(t *testing.T) { handler := &recordingWarningHandler{} iter := (&Iter{ framer: &testWarningFramer{warnings: []string{"warn"}}, }).bindWarningHandler(&Query{ routingInfo: &queryRoutingInfo{}, metrics: &queryMetrics{m: make(map[UUID]*hostMetrics)}, }, handler) iter.Close() iter.Close() if handler.calls != 1 { t.Fatalf("handler call count = %d, want 1", handler.calls) } }) t.Run("CopyPageDataTransfersReleasedMetadata", func(t *testing.T) { src := newErrorIterWithReleasedFramer(errors.New("boom"), &testWarningFramer{ warnings: []string{"warn"}, customPayload: map[string][]byte{"k": {9}}, }) dst := &Iter{ allWarnings: []string{"first-page"}, } dst.copyPageData(src) wantWarnings := []string{"first-page", "warn"} if !slices.Equal(dst.Warnings(), wantWarnings) { t.Fatalf("Warnings() = %v, want %v", dst.Warnings(), wantWarnings) } if !reflect.DeepEqual(dst.GetCustomPayload(), map[string][]byte{"k": {9}}) { t.Fatalf("GetCustomPayload() = %v, want %v", dst.GetCustomPayload(), map[string][]byte{"k": {9}}) } }) t.Run("BindIgnoresNilHandler", func(t *testing.T) { iter := (&Iter{}).bindWarningHandler(&Query{ routingInfo: &queryRoutingInfo{}, metrics: &queryMetrics{m: make(map[UUID]*hostMetrics)}, }, nil) if iter.warningHandler != nil { t.Fatal("expected warning handler to remain nil") } }) t.Run("HostPreservedAcrossClose", func(t *testing.T) { handler := &recordingWarningHandler{} host := &HostInfo{port: 9042, hostId: UUID{2}} iter := (&Iter{ framer: &testWarningFramer{warnings: []string{"warn"}}, host: host, }).bindWarningHandler(&Batch{ context: context.Background(), routingInfo: &queryRoutingInfo{}, metrics: &queryMetrics{m: make(map[UUID]*hostMetrics)}, rt: &SimpleRetryPolicy{NumRetries: 0}, spec: NonSpeculativeExecution{}, }, handler) iter.Close() if handler.lastHost != host { t.Fatal("expected handler to receive the iterator host") } }) t.Run("CloseClearsBatchWarningQueryReference", func(t *testing.T) { handler := &recordingWarningHandler{} batch := &Batch{ context: context.Background(), routingInfo: &queryRoutingInfo{}, metrics: &queryMetrics{m: make(map[UUID]*hostMetrics)}, rt: &SimpleRetryPolicy{NumRetries: 0}, spec: NonSpeculativeExecution{}, } iter := (&Iter{ framer: &testWarningFramer{warnings: []string{"warn"}}, }).bindWarningHandler(batch, handler) if err := iter.Close(); err != nil { t.Fatalf("Close() returned unexpected error: %v", err) } if handler.lastQry != batch { t.Fatal("handler batch mismatch") } if iter.warningQuery != nil { t.Fatal("expected warning query to be cleared after Close") } if iter.warningQueryOwned { t.Fatal("expected warningQueryOwned to be false after Close") } }) t.Run("CloseWithoutWarningsDoesNotInvokeHandler", func(t *testing.T) { handler := &recordingWarningHandler{} iter := (&Iter{ framer: &testWarningFramer{}, }).bindWarningHandler(&Query{ context: context.Background(), routingInfo: &queryRoutingInfo{}, metrics: &queryMetrics{m: make(map[UUID]*hostMetrics)}, rt: &SimpleRetryPolicy{NumRetries: 0}, spec: NonSpeculativeExecution{}, }, handler) iter.Close() if handler.calls != 0 { t.Fatalf("handler call count = %d, want 0", handler.calls) } }) t.Run("HandleWarningsOnceAfterManualAccumulation", func(t *testing.T) { handler := &recordingWarningHandler{} iter := (&Iter{ allWarnings: []string{"warn1"}, host: &HostInfo{hostId: UUID{3}}, }).bindWarningHandler(&Query{ routingInfo: &queryRoutingInfo{}, metrics: &queryMetrics{m: make(map[UUID]*hostMetrics)}, }, handler) iter.handleWarningsOnce() iter.handleWarningsOnce() if handler.calls != 1 { t.Fatalf("handler call count = %d, want 1", handler.calls) } }) t.Run("QueryReleaseBeforeCloseKeepsWarningQueryAlive", func(t *testing.T) { handler := &recordingWarningHandler{} qry := newWarningTestQuery() qry.refCount = 1 qry.stmt = "SELECT now() FROM system.local" iter := (&Iter{ framer: &testWarningFramer{warnings: []string{"warn"}}, }).bindWarningHandler(qry, handler) qry.Release() if qry.stmt != "SELECT now() FROM system.local" { t.Fatalf("query statement reset before iterator close: %q", qry.stmt) } if err := iter.Close(); err != nil { t.Fatalf("Close() returned unexpected error: %v", err) } if handler.calls != 1 { t.Fatalf("handler call count = %d, want 1", handler.calls) } capturedQry, ok := handler.lastQry.(*Query) if !ok { t.Fatalf("handler query type = %T, want *Query", handler.lastQry) } if capturedQry != qry { t.Fatal("handler query mismatch") } if handler.queryStmt != "SELECT now() FROM system.local" { t.Fatalf("handler saw query statement %q, want %q", handler.queryStmt, "SELECT now() FROM system.local") } }) t.Run("ReleasedErrorIterAutoFinalizesOnBind", func(t *testing.T) { handler := &recordingWarningHandler{} qry := newWarningTestQuery() qry.refCount = 1 qry.stmt = "SELECT fail()" iter := newErrorIterWithReleasedFramer(errors.New("boom"), &testWarningFramer{ warnings: []string{"warn"}, }).bindWarningHandler(qry, handler) if got := atomic.LoadUint32(&qry.refCount); got != 1 { t.Fatalf("query refCount = %d, want 1", got) } if iter.warningQuery != nil { t.Fatal("expected warning query to be released") } if handler.calls != 1 { t.Fatalf("handler call count = %d, want 1", handler.calls) } if !slices.Equal(handler.warnings, []string{"warn"}) { t.Fatalf("handler warnings = %v, want %v", handler.warnings, []string{"warn"}) } if err := iter.Close(); err == nil || err.Error() != "boom" { t.Fatalf("Close() = %v, want boom", err) } }) } func TestIterAutoFinalizeOnTerminalConsumption(t *testing.T) { t.Parallel() t.Run("ScanEOFReleasesResources", func(t *testing.T) { handler := &recordingWarningHandler{} qry := newWarningTestQuery() qry.refCount = 1 framer := &testWarningFramer{warnings: []string{"scan-eof"}} iter := (&Iter{ framer: framer, numRows: 1, meta: resultMetadata{ actualColCount: 0, }, }).bindWarningHandler(qry, handler) if !iter.Scan() { t.Fatal("expected first Scan() to succeed") } if iter.Scan() { t.Fatal("expected second Scan() to report EOF") } if !framer.released { t.Fatal("expected EOF to release the framer") } if iter.framer != nil { t.Fatal("expected framer to be cleared after EOF") } if got := atomic.LoadUint32(&qry.refCount); got != 1 { t.Fatalf("query refCount = %d, want 1", got) } if handler.calls != 1 { t.Fatalf("handler call count = %d, want 1", handler.calls) } if !slices.Equal(handler.warnings, []string{"scan-eof"}) { t.Fatalf("handler warnings = %v, want %v", handler.warnings, []string{"scan-eof"}) } }) t.Run("ScannerNextEOFReleasesResources", func(t *testing.T) { handler := &recordingWarningHandler{} qry := newWarningTestQuery() qry.refCount = 1 framer := &testWarningFramer{warnings: []string{"scanner-eof"}} iter := (&Iter{ framer: framer, numRows: 1, meta: resultMetadata{ actualColCount: 0, }, }).bindWarningHandler(qry, handler) scanner := iter.Scanner() if !scanner.Next() { t.Fatal("expected first Next() to succeed") } if err := scanner.Scan(); err != nil { t.Fatalf("Scan() returned unexpected error: %v", err) } if scanner.Next() { t.Fatal("expected second Next() to report EOF") } if !framer.released { t.Fatal("expected EOF to release the framer") } if iter.framer != nil { t.Fatal("expected framer to be cleared after EOF") } if got := atomic.LoadUint32(&qry.refCount); got != 1 { t.Fatalf("query refCount = %d, want 1", got) } if handler.calls != 1 { t.Fatalf("handler call count = %d, want 1", handler.calls) } if !slices.Equal(handler.warnings, []string{"scanner-eof"}) { t.Fatalf("handler warnings = %v, want %v", handler.warnings, []string{"scanner-eof"}) } }) } func TestQueryExecutorRetryAndDiscardWarningHandling(t *testing.T) { t.Parallel() t.Run("SpeculativeLoserIsDiscardedWithoutWarnings", func(t *testing.T) { host := (&HostInfo{hostId: UUID{4}}).setState(NodeUp) handler := &recordingWarningHandler{} framer := &testWarningFramer{warnings: []string{"loser"}} qry := &executorTestQuery{ rt: &fixedRetryPolicy{maxRetries: 0, retryType: Rethrow}, spec: NonSpeculativeExecution{}, idempotent: true, } qry.executeFunc = func(context.Context, *Conn) *Iter { return (&Iter{framer: framer}).bindWarningHandler(qry, handler) } ctx, cancel := context.WithCancel(context.Background()) cancel() executor := newTestQueryExecutor(host) executor.run(ctx, qry, func() SelectedHost { return staticSelectedHost{host: host} }, make(chan *Iter)) if handler.calls != 0 { t.Fatalf("handler call count = %d, want 0", handler.calls) } if !framer.released { t.Fatal("speculative loser framer was not released") } if qry.released != 1 { t.Fatalf("releaseAfterExecution calls = %d, want 1", qry.released) } }) t.Run("RetriedAttemptStillWarnsOnce", func(t *testing.T) { host := (&HostInfo{hostId: UUID{5}}).setState(NodeUp) handler := &recordingWarningHandler{} firstFramer := &testWarningFramer{warnings: []string{"retry-warn"}} finalFramer := &testWarningFramer{} qry := &executorTestQuery{ ctx: context.Background(), rt: &fixedRetryPolicy{maxRetries: 1, retryType: Retry}, spec: NonSpeculativeExecution{}, idempotent: true, } attempt := 0 qry.executeFunc = func(context.Context, *Conn) *Iter { attempt++ if attempt == 1 { return (&Iter{err: errors.New("boom"), framer: firstFramer}).bindWarningHandler(qry, handler) } return (&Iter{framer: finalFramer}).bindWarningHandler(qry, handler) } executor := newTestQueryExecutor(host) iter := executor.do(context.Background(), qry, func() SelectedHost { return staticSelectedHost{host: host} }) defer iter.Close() if iter.err != nil { t.Fatalf("unexpected final error: %v", iter.err) } if !firstFramer.released { t.Fatal("retried attempt framer was not released") } if handler.calls != 1 { t.Fatalf("handler call count = %d, want 1", handler.calls) } if !slices.Equal(handler.warnings, []string{"retry-warn"}) { t.Fatalf("handler warnings = %v, want %v", handler.warnings, []string{"retry-warn"}) } }) } func TestIterCloseCleansPrefetchedNextPage(t *testing.T) { t.Parallel() t.Run("MaterializedNextPageIsReleasedWithoutDispatchingItsWarnings", func(t *testing.T) { handler := &recordingWarningHandler{} qry := newWarningTestQuery() currentFramer := &testWarningFramer{warnings: []string{"current"}} nextFramer := &testWarningFramer{warnings: []string{"prefetched"}} iter := (&Iter{ framer: currentFramer, next: &nextIter{ next: (&Iter{framer: nextFramer}).bindWarningHandler(qry, handler), }, }).bindWarningHandler(qry, handler) iter.Close() if !currentFramer.released { t.Fatal("current framer was not released") } if !nextFramer.released { t.Fatal("prefetched next framer was not released") } if handler.calls != 1 { t.Fatalf("handler call count = %d, want 1", handler.calls) } if !slices.Equal(handler.warnings, []string{"current"}) { t.Fatalf("handler warnings = %v, want %v", handler.warnings, []string{"current"}) } if iter.next != nil { t.Fatal("expected prefetched next iterator to be cleared on Close") } }) t.Run("LatePrefetchResultIsClosedAfterCancellation", func(t *testing.T) { handler := &recordingWarningHandler{} next := newNextIter(newWarningTestQuery(), 1) next.close() select { case <-next.qry.Context().Done(): default: t.Fatal("expected next-page context to be canceled") } lateFramer := &testWarningFramer{warnings: []string{"late"}} next.storeFetched((&Iter{framer: lateFramer}).bindWarningHandler(next.qry, handler)) if !lateFramer.released { t.Fatal("late prefetched framer was not released") } if handler.calls != 0 { t.Fatalf("handler call count = %d, want 0", handler.calls) } }) } func TestSliceMapClosesIterator(t *testing.T) { t.Parallel() handler := &recordingWarningHandler{} qry := newWarningTestQuery() framer := &testWarningFramer{warnings: []string{"slice-map"}} iter := (&Iter{ framer: framer, meta: resultMetadata{ actualColCount: 0, }, }).bindWarningHandler(qry, handler) rows, err := iter.SliceMap() if err != nil { t.Fatalf("unexpected SliceMap error: %v", err) } if len(rows) != 0 { t.Fatalf("expected no rows, got %d", len(rows)) } if !framer.released { t.Fatal("expected SliceMap to release the iterator framer") } if handler.calls != 1 { t.Fatalf("handler call count = %d, want 1", handler.calls) } if !slices.Equal(handler.warnings, []string{"slice-map"}) { t.Fatalf("handler warnings = %v, want %v", handler.warnings, []string{"slice-map"}) } } func TestIterFetchNextPageRetiresConsumedFetchContextOnly(t *testing.T) { t.Parallel() rootCtx, cancel := context.WithCancel(context.Background()) defer cancel() var fetchedQry *Query nextPageFramer := &testWarningFramer{warnings: []string{"next"}} conn := &pagingTestConn{ executeQueryFunc: func(_ context.Context, qry *Query) *Iter { fetchedQry = qry return &Iter{ framer: nextPageFramer, numRows: 1, next: newNextIter(qry, 1), } }, } baseQry := newWarningTestQuery().WithContext(rootCtx) baseQry.conn = conn currentFramer := &testWarningFramer{warnings: []string{"current"}} iter := &Iter{ framer: currentFramer, numRows: 1, pos: 1, next: newNextIter(baseQry, 1), } defer iter.Close() if !iter.fetchNextPage() { t.Fatal("expected next page fetch to succeed") } if fetchedQry == nil { t.Fatal("expected next-page query to execute") } select { case <-fetchedQry.Context().Done(): default: t.Fatal("expected consumed next-page context to be canceled") } select { case <-iter.next.qry.Context().Done(): t.Fatal("expected following page context to remain active") default: } if !currentFramer.released { t.Fatal("expected current page framer to be released") } if iter.framer != nextPageFramer { t.Fatal("expected fetched page framer to become current") } } func TestQueryIterManualPagingDefersHiddenEmptyPageWarnings(t *testing.T) { t.Parallel() handler := &recordingWarningHandler{} firstFramer := &testWarningFramer{warnings: []string{"empty-page"}} finalFramer := &testWarningFramer{warnings: []string{"final-page"}} baseQry := newWarningTestQuery() baseQry.refCount = 1 baseQry.PageState([]byte("initial")) call := 0 baseQry.conn = &pagingTestConn{ executeQueryFunc: func(_ context.Context, qry *Query) *Iter { call++ switch call { case 1: if !slices.Equal(qry.pageState, []byte("initial")) { t.Fatalf("first page state = %q, want %q", qry.pageState, []byte("initial")) } return (&Iter{ framer: firstFramer, numRows: 0, meta: resultMetadata{ pagingState: []byte("next"), }, }).bindWarningHandler(qry, handler) case 2: if !slices.Equal(qry.pageState, []byte("next")) { t.Fatalf("second page state = %q, want %q", qry.pageState, []byte("next")) } return (&Iter{ framer: finalFramer, numRows: 1, }).bindWarningHandler(qry, handler) default: t.Fatalf("unexpected executeQuery call %d", call) return nil } }, } iter := baseQry.Iter() if call != 2 { t.Fatalf("executeQuery call count = %d, want 2", call) } if handler.calls != 0 { t.Fatalf("handler call count before Close = %d, want 0", handler.calls) } if !firstFramer.released { t.Fatal("hidden empty-page framer was not released") } if warnings := iter.Warnings(); !slices.Equal(warnings, []string{"empty-page", "final-page"}) { t.Fatalf("Warnings() = %v, want %v", warnings, []string{"empty-page", "final-page"}) } if err := iter.Close(); err != nil { t.Fatalf("Close() returned unexpected error: %v", err) } if handler.calls != 1 { t.Fatalf("handler call count after Close = %d, want 1", handler.calls) } if !slices.Equal(handler.warnings, []string{"empty-page", "final-page"}) { t.Fatalf("handler warnings = %v, want %v", handler.warnings, []string{"empty-page", "final-page"}) } } func TestQueryIterManualPagingPreservesHiddenWarningsOnTerminalError(t *testing.T) { t.Parallel() handler := &recordingWarningHandler{} firstFramer := &testWarningFramer{warnings: []string{"empty-page"}} baseQry := newWarningTestQuery() baseQry.refCount = 1 baseQry.PageState([]byte("initial")) call := 0 baseQry.conn = &pagingTestConn{ executeQueryFunc: func(_ context.Context, qry *Query) *Iter { call++ switch call { case 1: if !slices.Equal(qry.pageState, []byte("initial")) { t.Fatalf("first page state = %q, want %q", qry.pageState, []byte("initial")) } return (&Iter{ framer: firstFramer, numRows: 0, meta: resultMetadata{ pagingState: []byte("next"), }, }).bindWarningHandler(qry, handler) case 2: if !slices.Equal(qry.pageState, []byte("next")) { t.Fatalf("second page state = %q, want %q", qry.pageState, []byte("next")) } return newErrorIterWithReleasedFramer(errors.New("boom"), &testWarningFramer{ warnings: []string{"final-error"}, }).bindWarningHandler(qry, handler) default: t.Fatalf("unexpected executeQuery call %d", call) return nil } }, } iter := baseQry.Iter() if call != 2 { t.Fatalf("executeQuery call count = %d, want 2", call) } if !firstFramer.released { t.Fatal("hidden empty-page framer was not released") } if handler.calls != 1 { t.Fatalf("handler call count after Iter = %d, want 1", handler.calls) } if !slices.Equal(handler.warnings, []string{"empty-page", "final-error"}) { t.Fatalf("handler warnings = %v, want %v", handler.warnings, []string{"empty-page", "final-error"}) } if warnings := iter.Warnings(); !slices.Equal(warnings, []string{"empty-page", "final-error"}) { t.Fatalf("Warnings() = %v, want %v", warnings, []string{"empty-page", "final-error"}) } if err := iter.Close(); err == nil || err.Error() != "boom" { t.Fatalf("Close() = %v, want boom", err) } } func TestTableTabletsMetadata(t *testing.T) { t.Parallel() t.Run("HappyPath", func(t *testing.T) { t.Parallel() ctrl := &schemaDataMock{knownKeyspaces: map[string][]tableInfo{}} s := newSchemaEventTestSessionWithMock(ctrl) defer s.Close() s.isInitialized = true s.tabletsRoutingV1 = true addTestTablets(t, s, "test_ks", "tbl_a") entries, err := s.TableTabletsMetadata("test_ks", "tbl_a") if err != nil { t.Fatalf("unexpected error: %v", err) } if len(entries) != 2 { t.Fatalf("expected 2 tablet entries, got %d", len(entries)) } }) t.Run("ClosedSession", func(t *testing.T) { t.Parallel() ctrl := &schemaDataMock{knownKeyspaces: map[string][]tableInfo{}} s := newSchemaEventTestSessionWithMock(ctrl) defer s.Close() s.isInitialized = true s.tabletsRoutingV1 = true s.isClosed = true _, err := s.TableTabletsMetadata("ks", "tb") if !errors.Is(err, ErrSessionClosed) { t.Fatalf("expected ErrSessionClosed, got %v", err) } }) t.Run("NotReady", func(t *testing.T) { t.Parallel() ctrl := &schemaDataMock{knownKeyspaces: map[string][]tableInfo{}} s := newSchemaEventTestSessionWithMock(ctrl) defer s.Close() s.tabletsRoutingV1 = true _, err := s.TableTabletsMetadata("ks", "tb") if !errors.Is(err, ErrSessionNotReady) { t.Fatalf("expected ErrSessionNotReady, got %v", err) } }) t.Run("TabletsNotEnabled", func(t *testing.T) { t.Parallel() ctrl := &schemaDataMock{knownKeyspaces: map[string][]tableInfo{}} s := newSchemaEventTestSessionWithMock(ctrl) defer s.Close() s.isInitialized = true _, err := s.TableTabletsMetadata("ks", "tb") if !errors.Is(err, ErrTabletsNotUsed) { t.Fatalf("expected ErrTabletsNotUsed, got %v", err) } }) t.Run("EmptyKeyspace", func(t *testing.T) { t.Parallel() ctrl := &schemaDataMock{knownKeyspaces: map[string][]tableInfo{}} s := newSchemaEventTestSessionWithMock(ctrl) defer s.Close() s.isInitialized = true s.tabletsRoutingV1 = true _, err := s.TableTabletsMetadata("", "tb") if !errors.Is(err, ErrNoKeyspace) { t.Fatalf("expected ErrNoKeyspace, got %v", err) } }) t.Run("EmptyTable", func(t *testing.T) { t.Parallel() ctrl := &schemaDataMock{knownKeyspaces: map[string][]tableInfo{}} s := newSchemaEventTestSessionWithMock(ctrl) defer s.Close() s.isInitialized = true s.tabletsRoutingV1 = true _, err := s.TableTabletsMetadata("ks", "") if !errors.Is(err, ErrNoTable) { t.Fatalf("expected ErrNoTable, got %v", err) } }) t.Run("NoData", func(t *testing.T) { t.Parallel() ctrl := &schemaDataMock{knownKeyspaces: map[string][]tableInfo{}} s := newSchemaEventTestSessionWithMock(ctrl) defer s.Close() s.isInitialized = true s.tabletsRoutingV1 = true entries, err := s.TableTabletsMetadata("ks", "nonexistent") if err != nil { t.Fatalf("unexpected error: %v", err) } if entries != nil { t.Fatalf("expected nil for nonexistent table, got %d entries", len(entries)) } }) } func TestForEachTablet(t *testing.T) { t.Parallel() t.Run("HappyPath", func(t *testing.T) { t.Parallel() ctrl := &schemaDataMock{knownKeyspaces: map[string][]tableInfo{}} s := newSchemaEventTestSessionWithMock(ctrl) defer s.Close() s.isInitialized = true s.tabletsRoutingV1 = true addTestTablets(t, s, "ks1", "tbl_a") addTestTablets(t, s, "ks2", "tbl_b") visited := make(map[string]int) err := s.ForEachTablet(func(keyspace, table string, entries tablets.TabletEntryList) bool { visited[keyspace+"."+table] = len(entries) return true }) if err != nil { t.Fatalf("unexpected error: %v", err) } if len(visited) != 2 { t.Fatalf("expected 2 tables visited, got %d", len(visited)) } if visited["ks1.tbl_a"] != 2 { t.Fatalf("expected 2 entries for ks1.tbl_a, got %d", visited["ks1.tbl_a"]) } if visited["ks2.tbl_b"] != 2 { t.Fatalf("expected 2 entries for ks2.tbl_b, got %d", visited["ks2.tbl_b"]) } }) t.Run("EarlyStop", func(t *testing.T) { t.Parallel() ctrl := &schemaDataMock{knownKeyspaces: map[string][]tableInfo{}} s := newSchemaEventTestSessionWithMock(ctrl) defer s.Close() s.isInitialized = true s.tabletsRoutingV1 = true addTestTablets(t, s, "ks1", "tbl_a") addTestTablets(t, s, "ks2", "tbl_b") count := 0 err := s.ForEachTablet(func(keyspace, table string, entries tablets.TabletEntryList) bool { count++ return false }) if err != nil { t.Fatalf("unexpected error: %v", err) } if count != 1 { t.Fatalf("expected 1 callback invocation, got %d", count) } }) t.Run("ClosedSession", func(t *testing.T) { t.Parallel() ctrl := &schemaDataMock{knownKeyspaces: map[string][]tableInfo{}} s := newSchemaEventTestSessionWithMock(ctrl) defer s.Close() s.isInitialized = true s.tabletsRoutingV1 = true s.isClosed = true err := s.ForEachTablet(func(keyspace, table string, entries tablets.TabletEntryList) bool { t.Fatal("callback should not be called on closed session") return true }) if !errors.Is(err, ErrSessionClosed) { t.Fatalf("expected ErrSessionClosed, got %v", err) } }) t.Run("TabletsNotEnabled", func(t *testing.T) { t.Parallel() ctrl := &schemaDataMock{knownKeyspaces: map[string][]tableInfo{}} s := newSchemaEventTestSessionWithMock(ctrl) defer s.Close() s.isInitialized = true err := s.ForEachTablet(func(keyspace, table string, entries tablets.TabletEntryList) bool { t.Fatal("callback should not be called when tablets not enabled") return true }) if !errors.Is(err, ErrTabletsNotUsed) { t.Fatalf("expected ErrTabletsNotUsed, got %v", err) } }) t.Run("NilCallback", func(t *testing.T) { t.Parallel() ctrl := &schemaDataMock{knownKeyspaces: map[string][]tableInfo{}} s := newSchemaEventTestSessionWithMock(ctrl) defer s.Close() s.isInitialized = true s.tabletsRoutingV1 = true addTestTablets(t, s, "ks", "tb") err := s.ForEachTablet(nil) if err != nil { t.Fatalf("expected nil error for nil callback, got %v", err) } }) } func TestFindTabletReplicasUnsafeForToken(t *testing.T) { t.Parallel() t.Run("NilMetadataDescriber", func(t *testing.T) { t.Parallel() s := &Session{} s.metadataDescriber = nil result := s.findTabletReplicasUnsafeForToken("ks", "tb", 42) if result != nil { t.Fatalf("expected nil replicas for nil metadataDescriber, got %v", result) } }) t.Run("NilMetadata", func(t *testing.T) { t.Parallel() s := &Session{} s.metadataDescriber = &metadataDescriber{ session: s, metadata: nil, } result := s.findTabletReplicasUnsafeForToken("ks", "tb", 42) if result != nil { t.Fatalf("expected nil replicas for nil metadata, got %v", result) } }) t.Run("ClosedSession", func(t *testing.T) { t.Parallel() ctrl := &schemaDataMock{knownKeyspaces: map[string][]tableInfo{}} s := newSchemaEventTestSessionWithMock(ctrl) defer s.Close() s.isInitialized = true s.isClosed = true result := s.findTabletReplicasUnsafeForToken("ks", "tb", 42) if result != nil { t.Fatalf("expected nil replicas for closed session, got %v", result) } }) } func TestTableMetadataValidation(t *testing.T) { t.Parallel() t.Run("EmptyTableReturnsErrNoTable", func(t *testing.T) { t.Parallel() ctrl := &schemaDataMock{knownKeyspaces: map[string][]tableInfo{}} s := newSchemaEventTestSessionWithMock(ctrl) defer s.Close() s.isInitialized = true _, err := s.TableMetadata("ks", "") if !errors.Is(err, ErrNoTable) { t.Fatalf("TableMetadata: expected ErrNoTable, got %v", err) } }) } ================================================ FILE: stress_test.go ================================================ //go:build integration // +build integration /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "fmt" "sync/atomic" "testing" ) func BenchmarkConnStress(b *testing.B) { const workers = 16 cluster := createCluster() cluster.NumConns = 1 session := createSessionFromCluster(cluster, b) defer session.Close() table := testTableName(b) if err := createTable(session, fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s (id int primary key)", table)); err != nil { b.Fatal(err) } var seed uint64 writer := func(pb *testing.PB) { seed := atomic.AddUint64(&seed, 1) var i uint64 = 0 for pb.Next() { if err := session.Query(fmt.Sprintf("insert into %s (id) values (?)", table), i*seed).Exec(); err != nil { b.Error(err) return } i++ } } b.SetParallelism(workers) b.RunParallel(writer) } func BenchmarkConnRoutingKey(b *testing.B) { const workers = 16 cluster := createCluster() cluster.NumConns = 1 cluster.PoolConfig.HostSelectionPolicy = TokenAwareHostPolicy(RoundRobinHostPolicy()) session := createSessionFromCluster(cluster, b) defer session.Close() table := testTableName(b) if err := createTable(session, fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s (id int primary key)", table)); err != nil { b.Fatal(err) } var seed uint64 writer := func(pb *testing.PB) { seed := atomic.AddUint64(&seed, 1) var i uint64 = 0 query := session.Query(fmt.Sprintf("insert into %s (id) values (?)", table)) for pb.Next() { if _, err := query.Bind(i * seed).GetRoutingKey(); err != nil { b.Error(err) return } i++ } } b.SetParallelism(workers) b.RunParallel(writer) } ================================================ FILE: tablet_integration_test.go ================================================ //go:build integration // +build integration package gocql import ( "context" "fmt" "testing" "time" ) // Check if TokenAwareHostPolicy works correctly when using tablets func TestTablets(t *testing.T) { t.Parallel() if !isTabletsSupported() { t.Skip("Tablets are not supported by this server") } cluster := createCluster() fallback := RoundRobinHostPolicy() cluster.PoolConfig.HostSelectionPolicy = TokenAwareHostPolicy(fallback) session := createSessionFromCluster(cluster, t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf(`CREATE TABLE %s (pk int, ck int, v int, PRIMARY KEY (pk, ck)); `, table)); err != nil { t.Fatalf("unable to create table: %v", err) } hosts := session.hostSource.getHostsList() hostAddresses := []string{} for _, host := range hosts { hostAddresses = append(hostAddresses, host.connectAddress.String()) } ctx := context.Background() for i := 0; i < 5; i++ { err := session.Query(fmt.Sprintf(`INSERT INTO %s (pk, ck, v) VALUES (?, ?, ?);`, table), i, i%5, i%2).WithContext(ctx).Exec() if err != nil { t.Fatal(err) } } for i := range 5 { startTime := time.Now() timeout := 2 * time.Second backoffDelay := 100 * time.Millisecond success := false for attempt := 1; time.Since(startTime) < timeout; attempt++ { iter := session.Query(fmt.Sprintf(`SELECT pk, ck, v FROM %s WHERE pk = ?;`, table), i).WithContext(ctx).Consistency(One).Iter() payload := iter.GetCustomPayload() if err := iter.Close(); err != nil { t.Fatal(err) } if payload == nil || payload["tablets-routing-v1"] == nil { // Routing is working correctly success = true break } // Hint received, tablet migration may be in progress hint := payload["tablets-routing-v1"] tablet, err := unmarshalTabletHint(hint, 4, "", "") if err != nil { t.Fatalf("failed to extract tablet information: %s", err.Error()) } t.Logf("Attempt %d: received tablet hint (replicas: %s) - tablet migration may be in progress, backing off %v", attempt, tablet.Replicas(), backoffDelay) // Backoff to allow tablet migration to complete, but do not exceed the overall timeout. remaining := timeout - time.Since(startTime) if remaining <= 0 { // Overall timeout reached; exit the retry loop and fail after the loop. break } sleepFor := backoffDelay if sleepFor > remaining { sleepFor = remaining } time.Sleep(sleepFor) backoffDelay *= 2 // Exponential backoff } if !success { elapsed := time.Since(startTime) t.Fatalf("Timed out after %v (elapsed %v) waiting for tablets to stabilize (migrations still in progress)", timeout, elapsed) } } } ================================================ FILE: tablets/cow_tablet_list_test.go ================================================ //go:build unit // +build unit package tablets import ( "fmt" "math" "sort" "sync" "testing" "time" "github.com/gocql/gocql/internal/tests" ) func testHostUUID(s string) HostUUID { var u HostUUID copy(u[:], s) return u } func compareEntryRanges(entries TabletEntryList, ranges [][]int64) bool { if len(entries) != len(ranges) { return false } for i, e := range entries { if e.firstToken != ranges[i][0] || e.lastToken != ranges[i][1] { return false } } return true } func TestAddTabletToPerTableList(t *testing.T) { t.Parallel() t.Run("Empty", func(t *testing.T) { tl := TabletEntryList{} tl = tl.addEntry(TabletEntry{ firstToken: -100, lastToken: 100, }) tests.AssertEqual(t, "length", 1, len(tl)) tests.AssertEqual(t, "firstToken", int64(-100), tl[0].firstToken) tests.AssertEqual(t, "lastToken", int64(100), tl[0].lastToken) }) t.Run("Beginning", func(t *testing.T) { tl := TabletEntryList{{ firstToken: 100, lastToken: 200, }} tl = tl.addEntry(TabletEntry{ firstToken: -200, lastToken: -100, }) tests.AssertEqual(t, "length", 2, len(tl)) tests.AssertTrue(t, "sorted", compareEntryRanges(tl, [][]int64{{-200, -100}, {100, 200}})) }) t.Run("End", func(t *testing.T) { tl := TabletEntryList{{ firstToken: -200, lastToken: -100, }} tl = tl.addEntry(TabletEntry{ firstToken: 100, lastToken: 200, }) tests.AssertEqual(t, "length", 2, len(tl)) tests.AssertTrue(t, "sorted", compareEntryRanges(tl, [][]int64{{-200, -100}, {100, 200}})) }) t.Run("Overlap", func(t *testing.T) { tl := TabletEntryList{ {firstToken: -300, lastToken: -200}, {firstToken: -200, lastToken: -100}, {firstToken: -100, lastToken: 0}, {firstToken: 0, lastToken: 100}, } tl = tl.addEntry(TabletEntry{ firstToken: -150, lastToken: 50, }) tests.AssertTrue(t, "overlap resolved", compareEntryRanges(tl, [][]int64{{-300, -200}, {-150, 50}})) }) t.Run("NewTabletContainedWithinExisting", func(t *testing.T) { entries := TabletEntryList{ {firstToken: -300, lastToken: 300}, } result := entries.addEntry(TabletEntry{firstToken: -100, lastToken: 100}) if len(result) != 1 { t.Errorf("expected 1 tablet after replacement, got %d", len(result)) } if len(result) == 1 { tests.AssertEqual(t, "tablet firstToken", int64(-100), result[0].firstToken) tests.AssertEqual(t, "tablet lastToken", int64(100), result[0].lastToken) } }) t.Run("NewTabletContainsMultiple", func(t *testing.T) { entries := TabletEntryList{ {firstToken: -200, lastToken: -100}, {firstToken: -100, lastToken: 0}, {firstToken: 0, lastToken: 100}, } result := entries.addEntry(TabletEntry{firstToken: -300, lastToken: 200}) if len(result) != 1 { t.Errorf("expected consolidation to 1 tablet, got %d", len(result)) } if len(result) == 1 { tests.AssertEqual(t, "tablet firstToken", int64(-300), result[0].firstToken) tests.AssertEqual(t, "tablet lastToken", int64(200), result[0].lastToken) } }) t.Run("MultiplePartialOverlaps", func(t *testing.T) { entries := TabletEntryList{ {firstToken: -300, lastToken: -200}, {firstToken: -100, lastToken: 0}, {firstToken: 100, lastToken: 200}, } result := entries.addEntry(TabletEntry{firstToken: -150, lastToken: 150}) if len(result) != 2 { t.Errorf("expected 2 tablets after partial overlap, got %d", len(result)) } if len(result) == 2 { tests.AssertEqual(t, "first tablet firstToken", int64(-300), result[0].firstToken) tests.AssertEqual(t, "first tablet lastToken", int64(-200), result[0].lastToken) tests.AssertEqual(t, "second tablet firstToken", int64(-150), result[1].firstToken) tests.AssertEqual(t, "second tablet lastToken", int64(150), result[1].lastToken) } }) } func TestBulkAddToPerTableList(t *testing.T) { t.Parallel() t.Run("Empty", func(t *testing.T) { tl := TabletEntryList{} batch := TabletEntryList{ {firstToken: -200, lastToken: -100}, {firstToken: -100, lastToken: 0}, {firstToken: 0, lastToken: 100}, } tl = tl.bulkAddEntries(batch) tests.AssertEqual(t, "length", 3, len(tl)) tests.AssertTrue(t, "ranges", compareEntryRanges(tl, [][]int64{{-200, -100}, {-100, 0}, {0, 100}})) }) t.Run("Overlap", func(t *testing.T) { tl := TabletEntryList{ {firstToken: -400, lastToken: -300}, {firstToken: -300, lastToken: -200}, {firstToken: -200, lastToken: -100}, {firstToken: 100, lastToken: 200}, } batch := TabletEntryList{ {firstToken: -350, lastToken: -250}, {firstToken: -250, lastToken: -150}, } tl = tl.bulkAddEntries(batch) tests.AssertTrue(t, "overlap resolved", compareEntryRanges(tl, [][]int64{{-350, -250}, {-250, -150}, {100, 200}})) }) t.Run("IntraBatchOverlappingPair", func(t *testing.T) { tl := TabletEntryList{} batch := TabletEntryList{ {firstToken: 0, lastToken: 100, replicas: []ReplicaInfo{{testHostUUID("h1"), 0}}}, {firstToken: 50, lastToken: 150, replicas: []ReplicaInfo{{testHostUUID("h2"), 0}}}, } tl = tl.bulkAddEntries(batch) tests.AssertEqual(t, "length", 1, len(tl)) tests.AssertEqual(t, "firstToken", int64(50), tl[0].firstToken) tests.AssertEqual(t, "lastToken", int64(150), tl[0].lastToken) tests.AssertEqual(t, "host", testHostUUID("h2"), tl[0].replicas[0].hostId) }) t.Run("IntraBatchOverlappingTriple", func(t *testing.T) { tl := TabletEntryList{} batch := TabletEntryList{ {firstToken: 0, lastToken: 100, replicas: []ReplicaInfo{{testHostUUID("h1"), 0}}}, {firstToken: 50, lastToken: 150, replicas: []ReplicaInfo{{testHostUUID("h2"), 0}}}, {firstToken: 100, lastToken: 200, replicas: []ReplicaInfo{{testHostUUID("h3"), 0}}}, } tl = tl.bulkAddEntries(batch) tests.AssertEqual(t, "length", 1, len(tl)) tests.AssertEqual(t, "firstToken", int64(100), tl[0].firstToken) tests.AssertEqual(t, "lastToken", int64(200), tl[0].lastToken) }) t.Run("IntraBatchOverlappingWithExistingList", func(t *testing.T) { tl := TabletEntryList{ {firstToken: -500, lastToken: -400}, {firstToken: 500, lastToken: 600}, } batch := TabletEntryList{ {firstToken: 0, lastToken: 100, replicas: []ReplicaInfo{{testHostUUID("h1"), 0}}}, {firstToken: 50, lastToken: 150, replicas: []ReplicaInfo{{testHostUUID("h2"), 0}}}, } tl = tl.bulkAddEntries(batch) tests.AssertEqual(t, "length", 3, len(tl)) tests.AssertEqual(t, "existing-pre firstToken", int64(-500), tl[0].firstToken) tests.AssertEqual(t, "resolved firstToken", int64(50), tl[1].firstToken) tests.AssertEqual(t, "resolved lastToken", int64(150), tl[1].lastToken) tests.AssertEqual(t, "existing-post firstToken", int64(500), tl[2].firstToken) }) t.Run("NonOverlappingBatchStillWorks", func(t *testing.T) { tl := TabletEntryList{} batch := TabletEntryList{ {firstToken: 0, lastToken: 100}, {firstToken: 100, lastToken: 200}, {firstToken: 200, lastToken: 300}, } tl = tl.bulkAddEntries(batch) tests.AssertEqual(t, "length", 3, len(tl)) tests.AssertTrue(t, "ranges", compareEntryRanges(tl, [][]int64{{0, 100}, {100, 200}, {200, 300}})) }) t.Run("BatchWithGapsPreservesExisting", func(t *testing.T) { tl := TabletEntryList{ {firstToken: 200, lastToken: 300, replicas: []ReplicaInfo{{testHostUUID("existing"), 0}}}, } batch := TabletEntryList{ {firstToken: 0, lastToken: 100, replicas: []ReplicaInfo{{testHostUUID("h1"), 0}}}, {firstToken: 500, lastToken: 600, replicas: []ReplicaInfo{{testHostUUID("h2"), 0}}}, } tl = tl.bulkAddEntries(batch) tests.AssertEqual(t, "length", 3, len(tl)) tests.AssertTrue(t, "ranges", compareEntryRanges(tl, [][]int64{{0, 100}, {200, 300}, {500, 600}})) tests.AssertEqual(t, "existing host preserved", testHostUUID("existing"), tl[1].replicas[0].hostId) }) } func TestCowTabletListAddAndFind(t *testing.T) { t.Parallel() t.Run("BasicAddAndFind", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() host1 := GenerateHostUUIDs(1)[0] cl.AddTablet(TabletInfo{ keyspaceName: "ks1", tableName: "tb1", firstToken: -100, lastToken: 0, replicas: []ReplicaInfo{{host1, 0}}, }) cl.AddTablet(TabletInfo{ keyspaceName: "ks1", tableName: "tb1", firstToken: 0, lastToken: 100, replicas: []ReplicaInfo{{host1, 1}}, }) cl.Flush() ti, ok := cl.FindTabletForToken("ks1", "tb1", -50) if !ok { t.Fatal("expected tablet for token -50") } tests.AssertEqual(t, "lastToken", int64(0), ti.LastToken()) ti, ok = cl.FindTabletForToken("ks1", "tb1", 50) if !ok { t.Fatal("expected tablet for token 50") } tests.AssertEqual(t, "lastToken", int64(100), ti.LastToken()) _, ok = cl.FindTabletForToken("ks1", "unknown", 0) if ok { t.Fatal("expected nil for unknown table") } _, ok = cl.FindTabletForToken("unknown", "tb1", 0) if ok { t.Fatal("expected nil for unknown keyspace") } }) t.Run("FindReplicasUnsafeForToken", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() hosts := GenerateHostUUIDs(2) host1 := hosts[0] host2 := hosts[1] cl.AddTablet(TabletInfo{ keyspaceName: "ks1", tableName: "tb1", firstToken: -100, lastToken: 100, replicas: []ReplicaInfo{{host1, 0}, {host2, 1}}, }) cl.Flush() replicas := cl.FindReplicasUnsafeForToken("ks1", "tb1", 0) tests.AssertEqual(t, "replica count", 2, len(replicas)) tests.AssertEqual(t, "replica0 host", host1.String(), replicas[0].HostID()) tests.AssertEqual(t, "replica1 host", host2.String(), replicas[1].HostID()) replicas = cl.FindReplicasUnsafeForToken("ks1", "missing", 0) if replicas != nil { t.Fatal("expected nil replicas for missing table") } }) t.Run("MultiTable", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() host1 := GenerateHostUUIDs(1)[0] cl.AddTablet(TabletInfo{ keyspaceName: "ks", tableName: "tb1", firstToken: -100, lastToken: 100, replicas: []ReplicaInfo{{host1, 0}}, }) cl.AddTablet(TabletInfo{ keyspaceName: "ks", tableName: "tb2", firstToken: -100, lastToken: 100, replicas: []ReplicaInfo{{host1, 5}}, }) cl.Flush() r1 := cl.FindReplicasUnsafeForToken("ks", "tb1", 0) tests.AssertEqual(t, "tb1 shard", 0, r1[0].ShardID()) r2 := cl.FindReplicasUnsafeForToken("ks", "tb2", 0) tests.AssertEqual(t, "tb2 shard", 5, r2[0].ShardID()) }) t.Run("MultiKeyspace", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() host1 := GenerateHostUUIDs(1)[0] cl.AddTablet(TabletInfo{ keyspaceName: "ks1", tableName: "tb", firstToken: -100, lastToken: 100, replicas: []ReplicaInfo{{host1, 1}}, }) cl.AddTablet(TabletInfo{ keyspaceName: "ks2", tableName: "tb", firstToken: -100, lastToken: 100, replicas: []ReplicaInfo{{host1, 2}}, }) cl.Flush() r1 := cl.FindReplicasUnsafeForToken("ks1", "tb", 0) tests.AssertEqual(t, "ks1 shard", 1, r1[0].ShardID()) r2 := cl.FindReplicasUnsafeForToken("ks2", "tb", 0) tests.AssertEqual(t, "ks2 shard", 2, r2[0].ShardID()) }) t.Run("OverwritesExisting", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() hosts := GenerateHostUUIDs(2) host1 := hosts[0] host2 := hosts[1] cl.AddTablet(TabletInfo{ keyspaceName: "ks", tableName: "tb", firstToken: -100, lastToken: 100, replicas: []ReplicaInfo{{host1, 0}}, }) cl.AddTablet(TabletInfo{ keyspaceName: "ks", tableName: "tb", firstToken: -100, lastToken: 100, replicas: []ReplicaInfo{{host2, 5}}, }) cl.Flush() ti, ok := cl.FindTabletForToken("ks", "tb", 0) if !ok { t.Fatal("expected tablet") } tests.AssertEqual(t, "updated host", host2.String(), ti.Replicas()[0].HostID()) tests.AssertEqual(t, "updated shard", 5, ti.Replicas()[0].ShardID()) }) t.Run("SameFirstTokenDifferentLastToken", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() hosts := GenerateHostUUIDs(2) host1 := hosts[0] host2 := hosts[1] cl.AddTablet(TabletInfo{ keyspaceName: "ks", tableName: "tb", firstToken: 0, lastToken: 100, replicas: []ReplicaInfo{{host1, 0}}, }) cl.AddTablet(TabletInfo{ keyspaceName: "ks", tableName: "tb", firstToken: 0, lastToken: 200, replicas: []ReplicaInfo{{host2, 1}}, }) cl.Flush() ti, ok := cl.FindTabletForToken("ks", "tb", 50) if !ok { t.Fatal("expected tablet for token 50") } tests.AssertEqual(t, "replaced host", host2.String(), ti.Replicas()[0].HostID()) tests.AssertEqual(t, "replaced lastToken", int64(200), ti.LastToken()) ti, ok = cl.FindTabletForToken("ks", "tb", 150) if !ok { t.Fatal("expected tablet for token 150") } tests.AssertEqual(t, "host at 150", host2.String(), ti.Replicas()[0].HostID()) }) } func TestCowTabletListBulkAdd(t *testing.T) { t.Parallel() t.Run("Basic", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() host1 := GenerateHostUUIDs(1)[0] batch := TabletInfoList{ {keyspaceName: "ks", tableName: "tb", firstToken: -300, lastToken: -200, replicas: []ReplicaInfo{{host1, 0}}}, {keyspaceName: "ks", tableName: "tb", firstToken: -200, lastToken: -100, replicas: []ReplicaInfo{{host1, 1}}}, {keyspaceName: "ks", tableName: "tb", firstToken: -100, lastToken: 0, replicas: []ReplicaInfo{{host1, 2}}}, } cl.BulkAddTablets(batch) cl.Flush() ti, ok := cl.FindTabletForToken("ks", "tb", -250) if !ok { t.Fatal("expected tablet") } tests.AssertEqual(t, "shard", 0, ti.Replicas()[0].ShardID()) ti, ok = cl.FindTabletForToken("ks", "tb", -150) if !ok { t.Fatal("expected tablet") } tests.AssertEqual(t, "shard", 1, ti.Replicas()[0].ShardID()) }) t.Run("MultiTable", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() host1 := GenerateHostUUIDs(1)[0] batch := TabletInfoList{ {keyspaceName: "ks", tableName: "tb1", firstToken: -100, lastToken: 0, replicas: []ReplicaInfo{{host1, 0}}}, {keyspaceName: "ks", tableName: "tb1", firstToken: 0, lastToken: 100, replicas: []ReplicaInfo{{host1, 1}}}, {keyspaceName: "ks", tableName: "tb2", firstToken: -50, lastToken: 50, replicas: []ReplicaInfo{{host1, 2}}}, } cl.BulkAddTablets(batch) cl.Flush() ti, ok := cl.FindTabletForToken("ks", "tb1", -50) if !ok { t.Fatal("expected tablet for tb1 token -50") } tests.AssertEqual(t, "tb1 shard", 0, ti.Replicas()[0].ShardID()) ti, ok = cl.FindTabletForToken("ks", "tb2", 0) if !ok { t.Fatal("expected tablet for tb2 token 0") } tests.AssertEqual(t, "tb2 shard", 2, ti.Replicas()[0].ShardID()) }) t.Run("SortsPerTableGroups", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() host1 := GenerateHostUUIDs(1)[0] batch := TabletInfoList{ {keyspaceName: "ks", tableName: "tb1", firstToken: 100, lastToken: 200, replicas: []ReplicaInfo{{host1, 2}}}, {keyspaceName: "ks", tableName: "tb2", firstToken: -100, lastToken: 100, replicas: []ReplicaInfo{{host1, 7}}}, {keyspaceName: "ks", tableName: "tb1", firstToken: -100, lastToken: 0, replicas: []ReplicaInfo{{host1, 0}}}, {keyspaceName: "ks", tableName: "tb1", firstToken: 0, lastToken: 100, replicas: []ReplicaInfo{{host1, 1}}}, } cl.BulkAddTablets(batch) cl.Flush() ti, ok := cl.FindTabletForToken("ks", "tb1", -50) if !ok { t.Fatal("expected tablet for tb1 token -50") } tests.AssertEqual(t, "tb1 shard for -50", 0, ti.Replicas()[0].ShardID()) ti, ok = cl.FindTabletForToken("ks", "tb1", 50) if !ok { t.Fatal("expected tablet for tb1 token 50") } tests.AssertEqual(t, "tb1 shard for 50", 1, ti.Replicas()[0].ShardID()) ti, ok = cl.FindTabletForToken("ks", "tb1", 150) if !ok { t.Fatal("expected tablet for tb1 token 150") } tests.AssertEqual(t, "tb1 shard for 150", 2, ti.Replicas()[0].ShardID()) ti, ok = cl.FindTabletForToken("ks", "tb2", 0) if !ok { t.Fatal("expected tablet for tb2 token 0") } tests.AssertEqual(t, "tb2 shard", 7, ti.Replicas()[0].ShardID()) }) t.Run("ZeroValueEntries", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() host1 := GenerateHostUUIDs(1)[0] cl.BulkAddTablets(TabletInfoList{ {}, {keyspaceName: "ks", tableName: "tb", firstToken: 0, lastToken: 100, replicas: []ReplicaInfo{{host1, 0}}}, {}, }) cl.Flush() result := cl.GetTableTablets("ks", "tb") tests.AssertEqual(t, "tablet count", 1, len(result)) tests.AssertEqual(t, "firstToken", int64(0), result[0].FirstToken()) tests.AssertEqual(t, "lastToken", int64(100), result[0].LastToken()) }) t.Run("EmptyIdentifiers", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() host1 := GenerateHostUUIDs(1)[0] cl.BulkAddTablets(TabletInfoList{ {keyspaceName: "", tableName: "tb", firstToken: 0, lastToken: 100, replicas: []ReplicaInfo{{host1, 0}}}, {keyspaceName: "ks", tableName: "", firstToken: 0, lastToken: 100, replicas: []ReplicaInfo{{host1, 1}}}, {keyspaceName: "", tableName: "", firstToken: 0, lastToken: 100, replicas: []ReplicaInfo{{host1, 2}}}, {keyspaceName: "ks", tableName: "tb", firstToken: 0, lastToken: 100, replicas: []ReplicaInfo{{host1, 3}}}, }) cl.Flush() result := cl.GetTableTablets("ks", "tb") tests.AssertEqual(t, "valid tablet count", 1, len(result)) tests.AssertEqual(t, "shard", 3, result[0].Replicas()[0].ShardID()) tests.AssertEqual(t, "empty-ks phantom", 0, len(cl.GetTableTablets("", "tb"))) tests.AssertEqual(t, "empty-table phantom", 0, len(cl.GetTableTablets("ks", ""))) tests.AssertEqual(t, "both-empty phantom", 0, len(cl.GetTableTablets("", ""))) }) t.Run("IntraBatchOverlap", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() batch := TabletInfoList{ {keyspaceName: "ks", tableName: "tb", firstToken: 0, lastToken: 100, replicas: []ReplicaInfo{{testHostUUID("h1"), 0}}}, {keyspaceName: "ks", tableName: "tb", firstToken: 50, lastToken: 150, replicas: []ReplicaInfo{{testHostUUID("h2"), 1}}}, } cl.BulkAddTablets(batch) cl.Flush() result := cl.GetTableTablets("ks", "tb") tests.AssertEqual(t, "tablet count", 1, len(result)) tests.AssertEqual(t, "firstToken", int64(50), result[0].FirstToken()) tests.AssertEqual(t, "lastToken", int64(150), result[0].LastToken()) tests.AssertEqual(t, "shard", 1, result[0].Replicas()[0].ShardID()) }) } func TestCowTabletListGet(t *testing.T) { t.Parallel() t.Run("AllTablets", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() host1 := GenerateHostUUIDs(1)[0] cl.AddTablet(TabletInfo{ keyspaceName: "ks1", tableName: "tb1", firstToken: -100, lastToken: 0, replicas: []ReplicaInfo{{host1, 0}}, }) cl.AddTablet(TabletInfo{ keyspaceName: "ks1", tableName: "tb2", firstToken: 0, lastToken: 100, replicas: []ReplicaInfo{{host1, 1}}, }) cl.AddTablet(TabletInfo{ keyspaceName: "ks2", tableName: "tb1", firstToken: 100, lastToken: 200, replicas: []ReplicaInfo{{host1, 2}}, }) cl.Flush() flat := cl.Get() tests.AssertEqual(t, "total tablets", 3, len(flat)) sort.Slice(flat, func(i, j int) bool { return flat[i].FirstToken() < flat[j].FirstToken() }) tests.AssertEqual(t, "first", int64(-100), flat[0].FirstToken()) tests.AssertEqual(t, "second", int64(0), flat[1].FirstToken()) tests.AssertEqual(t, "third", int64(100), flat[2].FirstToken()) }) t.Run("Empty", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() flat := cl.Get() tests.AssertEqual(t, "empty list length", 0, len(flat)) }) } func TestCowTabletListGetTableTablets(t *testing.T) { t.Parallel() t.Run("MultipleTablesAndKeyspaces", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() host1 := GenerateHostUUIDs(1)[0] cl.AddTablet(TabletInfo{ keyspaceName: "ks1", tableName: "tb1", firstToken: -100, lastToken: 0, replicas: []ReplicaInfo{{host1, 0}}, }) cl.AddTablet(TabletInfo{ keyspaceName: "ks1", tableName: "tb1", firstToken: 0, lastToken: 100, replicas: []ReplicaInfo{{host1, 1}}, }) cl.AddTablet(TabletInfo{ keyspaceName: "ks1", tableName: "tb2", firstToken: 100, lastToken: 200, replicas: []ReplicaInfo{{host1, 2}}, }) cl.AddTablet(TabletInfo{ keyspaceName: "ks2", tableName: "tb1", firstToken: 200, lastToken: 300, replicas: []ReplicaInfo{{host1, 3}}, }) cl.Flush() result := cl.GetTableTablets("ks1", "tb1") tests.AssertEqual(t, "ks1.tb1 count", 2, len(result)) tests.AssertEqual(t, "ks1.tb1 first token", int64(-100), result[0].FirstToken()) tests.AssertEqual(t, "ks1.tb1 second token", int64(0), result[1].FirstToken()) result = cl.GetTableTablets("ks1", "tb2") tests.AssertEqual(t, "ks1.tb2 count", 1, len(result)) tests.AssertEqual(t, "ks1.tb2 first token", int64(100), result[0].FirstToken()) result = cl.GetTableTablets("ks2", "tb1") tests.AssertEqual(t, "ks2.tb1 count", 1, len(result)) tests.AssertEqual(t, "ks2.tb1 first token", int64(200), result[0].FirstToken()) }) t.Run("NonExistent", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() host1 := GenerateHostUUIDs(1)[0] cl.AddTablet(TabletInfo{ keyspaceName: "ks1", tableName: "tb1", firstToken: -100, lastToken: 0, replicas: []ReplicaInfo{{host1, 0}}, }) cl.Flush() result := cl.GetTableTablets("no_such_ks", "tb1") if result != nil { t.Fatalf("expected nil for non-existent keyspace, got %d tablets", len(result)) } result = cl.GetTableTablets("ks1", "no_such_tb") if result != nil { t.Fatalf("expected nil for non-existent table, got %d tablets", len(result)) } }) t.Run("ReturnsCopy", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() host1 := GenerateHostUUIDs(1)[0] cl.AddTablet(TabletInfo{ keyspaceName: "ks", tableName: "tb", firstToken: -100, lastToken: 0, replicas: []ReplicaInfo{{host1, 0}}, }) cl.AddTablet(TabletInfo{ keyspaceName: "ks", tableName: "tb", firstToken: 0, lastToken: 100, replicas: []ReplicaInfo{{host1, 1}}, }) cl.Flush() result1 := cl.GetTableTablets("ks", "tb") result2 := cl.GetTableTablets("ks", "tb") result1[0] = TabletEntry{} if result2[0].FirstToken() != -100 { t.Fatal("GetTableTablets should return independent copies") } }) t.Run("Empty", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() result := cl.GetTableTablets("ks", "tb") if result != nil { t.Fatalf("expected nil for empty list, got %d tablets", len(result)) } }) } func TestCowTabletListRemove(t *testing.T) { t.Parallel() t.Run("WithHost", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() hosts := GenerateHostUUIDs(2) removedHost := hosts[0] keptHost := hosts[1] cl.AddTablet(TabletInfo{ keyspaceName: "ks", tableName: "tb1", firstToken: -100, lastToken: 0, replicas: []ReplicaInfo{{removedHost, 0}}, }) cl.AddTablet(TabletInfo{ keyspaceName: "ks", tableName: "tb1", firstToken: 0, lastToken: 100, replicas: []ReplicaInfo{{keptHost, 1}}, }) cl.AddTablet(TabletInfo{ keyspaceName: "ks", tableName: "tb2", firstToken: -100, lastToken: 100, replicas: []ReplicaInfo{{removedHost, 2}}, }) cl.RemoveTabletsWithHost(removedHost) cl.Flush() ti, ok := cl.FindTabletForToken("ks", "tb1", 50) if !ok { t.Fatal("expected kept tablet in tb1") } tests.AssertEqual(t, "kept shard", 1, ti.Replicas()[0].ShardID()) flat := cl.Get() for _, tab := range flat { for _, r := range tab.Replicas() { if r.HostUUIDValue() == removedHost { t.Fatalf("found removed host in tablet %v", tab) } } } }) t.Run("WithKeyspace", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() host1 := GenerateHostUUIDs(1)[0] cl.AddTablet(TabletInfo{ keyspaceName: "removed_ks", tableName: "tb1", firstToken: -100, lastToken: 0, replicas: []ReplicaInfo{{host1, 0}}, }) cl.AddTablet(TabletInfo{ keyspaceName: "removed_ks", tableName: "tb2", firstToken: 0, lastToken: 100, replicas: []ReplicaInfo{{host1, 1}}, }) cl.AddTablet(TabletInfo{ keyspaceName: "kept_ks", tableName: "tb1", firstToken: 100, lastToken: 200, replicas: []ReplicaInfo{{host1, 2}}, }) cl.RemoveTabletsWithKeyspace("removed_ks") cl.Flush() _, ok := cl.FindTabletForToken("removed_ks", "tb1", -50) if ok { t.Fatal("expected nil for removed keyspace table tb1") } _, ok = cl.FindTabletForToken("removed_ks", "tb2", 50) if ok { t.Fatal("expected nil for removed keyspace table tb2") } ti, ok := cl.FindTabletForToken("kept_ks", "tb1", 150) if !ok { t.Fatal("expected tablet for kept keyspace") } tests.AssertEqual(t, "kept shard", 2, ti.Replicas()[0].ShardID()) tests.AssertEqual(t, "total tablets", 1, len(cl.Get())) }) t.Run("WithTable", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() host1 := GenerateHostUUIDs(1)[0] cl.AddTablet(TabletInfo{ keyspaceName: "ks", tableName: "removed_tb", firstToken: -100, lastToken: 0, replicas: []ReplicaInfo{{host1, 0}}, }) cl.AddTablet(TabletInfo{ keyspaceName: "ks", tableName: "kept_tb", firstToken: 0, lastToken: 100, replicas: []ReplicaInfo{{host1, 1}}, }) cl.RemoveTabletsWithTable("ks", "removed_tb") cl.Flush() _, ok := cl.FindTabletForToken("ks", "removed_tb", -50) if ok { t.Fatal("expected nil for removed table") } ti, ok := cl.FindTabletForToken("ks", "kept_tb", 50) if !ok { t.Fatal("expected tablet for kept table") } tests.AssertEqual(t, "kept shard", 1, ti.Replicas()[0].ShardID()) }) t.Run("Nonexistent", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() host1 := GenerateHostUUIDs(1)[0] cl.AddTablet(TabletInfo{ keyspaceName: "ks", tableName: "tb", firstToken: -100, lastToken: 100, replicas: []ReplicaInfo{{host1, 0}}, }) cl.RemoveTabletsWithKeyspace("nonexistent") cl.RemoveTabletsWithTable("ks", "nonexistent") cl.RemoveTabletsWithHost(testHostUUID("nonexistent-host")) cl.Flush() tests.AssertEqual(t, "still has tablet", 1, len(cl.Get())) }) } func TestCowTabletListForEach(t *testing.T) { t.Parallel() t.Run("VisitsAllTables", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() host1 := GenerateHostUUIDs(1)[0] cl.AddTablet(TabletInfo{ keyspaceName: "ks1", tableName: "tb1", firstToken: -100, lastToken: 0, replicas: []ReplicaInfo{{host1, 0}}, }) cl.AddTablet(TabletInfo{ keyspaceName: "ks1", tableName: "tb2", firstToken: 0, lastToken: 100, replicas: []ReplicaInfo{{host1, 1}}, }) cl.AddTablet(TabletInfo{ keyspaceName: "ks2", tableName: "tb1", firstToken: 100, lastToken: 200, replicas: []ReplicaInfo{{host1, 2}}, }) cl.Flush() visited := make(map[string]int) // "keyspace.table" -> entry count cl.ForEach(func(keyspace, table string, entries TabletEntryList) bool { visited[keyspace+"."+table] = len(entries) return true }) tests.AssertEqual(t, "visited count", 3, len(visited)) tests.AssertEqual(t, "ks1.tb1 entries", 1, visited["ks1.tb1"]) tests.AssertEqual(t, "ks1.tb2 entries", 1, visited["ks1.tb2"]) tests.AssertEqual(t, "ks2.tb1 entries", 1, visited["ks2.tb1"]) }) t.Run("StopsEarly", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() host1 := GenerateHostUUIDs(1)[0] for i := 0; i < 10; i++ { cl.AddTablet(TabletInfo{ keyspaceName: "ks", tableName: fmt.Sprintf("tb%d", i), firstToken: int64(i * 100), lastToken: int64(i*100 + 99), replicas: []ReplicaInfo{{host1, i}}, }) } cl.Flush() count := 0 cl.ForEach(func(keyspace, table string, entries TabletEntryList) bool { count++ return count < 3 // stop after visiting 3 tables }) tests.AssertEqual(t, "stopped after 3", 3, count) }) t.Run("Empty", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() count := 0 cl.ForEach(func(keyspace, table string, entries TabletEntryList) bool { count++ return true }) tests.AssertEqual(t, "empty iteration", 0, count) }) t.Run("NilReceiver", func(t *testing.T) { var cl *CowTabletList cl.ForEach(func(keyspace, table string, entries TabletEntryList) bool { t.Fatal("callback should not be called on nil receiver") return true }) }) t.Run("MutationDoesNotCorruptState", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() host1 := GenerateHostUUIDs(1)[0] cl.AddTablet(TabletInfo{ keyspaceName: "ks", tableName: "tb", firstToken: -100, lastToken: 0, replicas: []ReplicaInfo{{host1, 0}}, }) cl.AddTablet(TabletInfo{ keyspaceName: "ks", tableName: "tb", firstToken: 0, lastToken: 100, replicas: []ReplicaInfo{{host1, 1}}, }) cl.Flush() cl.ForEach(func(keyspace, table string, entries TabletEntryList) bool { for i, j := 0, len(entries)-1; i < j; i, j = i+1, j-1 { entries[i], entries[j] = entries[j], entries[i] } entries[0] = TabletEntry{} return true }) entry, ok := cl.FindTabletForToken("ks", "tb", 50) if !ok { t.Fatal("expected to find tablet for token 50 after ForEach mutation") } tests.AssertEqual(t, "firstToken", int64(0), entry.FirstToken()) tests.AssertEqual(t, "lastToken", int64(100), entry.LastToken()) entry, ok = cl.FindTabletForToken("ks", "tb", -50) if !ok { t.Fatal("expected to find tablet for token -50 after ForEach mutation") } tests.AssertEqual(t, "firstToken", int64(-100), entry.FirstToken()) tests.AssertEqual(t, "lastToken", int64(0), entry.LastToken()) }) t.Run("EntriesAreReadable", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() host1 := GenerateHostUUIDs(1)[0] cl.AddTablet(TabletInfo{ keyspaceName: "ks", tableName: "tb", firstToken: -100, lastToken: 0, replicas: []ReplicaInfo{{host1, 0}}, }) cl.AddTablet(TabletInfo{ keyspaceName: "ks", tableName: "tb", firstToken: 0, lastToken: 100, replicas: []ReplicaInfo{{host1, 1}}, }) cl.Flush() cl.ForEach(func(keyspace, table string, entries TabletEntryList) bool { tests.AssertEqual(t, "keyspace", "ks", keyspace) tests.AssertEqual(t, "table", "tb", table) tests.AssertEqual(t, "entry count", 2, len(entries)) tests.AssertEqual(t, "first entry firstToken", int64(-100), entries[0].FirstToken()) tests.AssertEqual(t, "first entry lastToken", int64(0), entries[0].LastToken()) tests.AssertEqual(t, "second entry firstToken", int64(0), entries[1].FirstToken()) tests.AssertEqual(t, "second entry lastToken", int64(100), entries[1].LastToken()) return true }) }) t.Run("NilCallback", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() cl.AddTablet(TabletInfo{ keyspaceName: "ks", tableName: "tb", firstToken: -100, lastToken: 100, replicas: []ReplicaInfo{{testHostUUID("host1"), 0}}, }) cl.Flush() cl.ForEach(nil) }) } func TestCowTabletListLifecycle(t *testing.T) { t.Parallel() t.Run("CloseIdempotent", func(t *testing.T) { cl := NewCowTabletList() cl.Close() done := make(chan struct{}) go func() { defer close(done) cl.Close() }() select { case <-done: case <-time.After(time.Second): t.Fatal("second Close call did not return") } }) t.Run("FlushAfterClose", func(t *testing.T) { cl := NewCowTabletList() cl.Close() done := make(chan struct{}) go func() { defer close(done) cl.Flush() }() select { case <-done: case <-time.After(time.Second): t.Fatal("Flush after Close did not return") } }) t.Run("AddAfterCloseNoop", func(t *testing.T) { cl := NewCowTabletList() cl.Close() host1 := GenerateHostUUIDs(1)[0] done := make(chan struct{}) go func() { defer close(done) cl.AddTablet(TabletInfo{ keyspaceName: "ks", tableName: "tb", firstToken: -100, lastToken: 100, replicas: []ReplicaInfo{{host1, 0}}, }) }() select { case <-done: case <-time.After(time.Second): t.Fatal("AddTablet after Close did not return") } tests.AssertEqual(t, "tablet count", 0, len(cl.Get())) }) t.Run("NilReceiver", func(t *testing.T) { var cl *CowTabletList cl.Close() cl.Flush() cl.AddTablet(TabletInfo{ keyspaceName: "ks", tableName: "tb", firstToken: -100, lastToken: 100, replicas: []ReplicaInfo{{testHostUUID("host"), 0}}, }) cl.BulkAddTablets(TabletInfoList{{ keyspaceName: "ks", tableName: "tb", firstToken: -100, lastToken: 100, replicas: []ReplicaInfo{{testHostUUID("host"), 0}}, }}) cl.RemoveTabletsWithHost(testHostUUID("host")) cl.RemoveTabletsWithKeyspace("ks") cl.RemoveTabletsWithTable("ks", "tb") }) } func TestOpQueueRun(t *testing.T) { t.Parallel() newTablet := func(first, last int64) TabletInfo { return TabletInfo{ keyspaceName: "ks", tableName: "tb", firstToken: first, lastToken: last, } } runQueue := func(send func(q *opQueue), wantTypes []string, validate func(t *testing.T, processed []tabletOp)) { t.Helper() q := newOpQueue() var mu sync.Mutex processed := make([]tabletOp, 0, len(wantTypes)) go q.run(func(op tabletOp) { mu.Lock() processed = append(processed, op) mu.Unlock() if flush, ok := op.(opFlush); ok { close(flush.done) } }) send(q) q.close() mu.Lock() defer mu.Unlock() if len(processed) != len(wantTypes) { t.Fatalf("processed %d ops, want %d", len(processed), len(wantTypes)) } for i, op := range processed { switch wantTypes[i] { case "bulk": if _, ok := op.(opBulkAddTablets); !ok { t.Fatalf("processed[%d] = %T, want opBulkAddTablets", i, op) } case "flush": if _, ok := op.(opFlush); !ok { t.Fatalf("processed[%d] = %T, want opFlush", i, op) } case "removeHost": if _, ok := op.(opRemoveHost); !ok { t.Fatalf("processed[%d] = %T, want opRemoveHost", i, op) } default: t.Fatalf("unsupported expected type %q", wantTypes[i]) } } if validate != nil { validate(t, processed) } } t.Run("CoalescesBufferedAddOps", func(t *testing.T) { runQueue(func(q *opQueue) { q.send(opAddTablet{tablet: newTablet(0, 99)}) q.send(opAddTablet{tablet: newTablet(100, 199)}) q.send(opAddTablet{tablet: newTablet(200, 299)}) q.flush() }, []string{"bulk", "flush"}, func(t *testing.T, processed []tabletOp) { bulk := processed[0].(opBulkAddTablets) if len(bulk.tablets) != 3 { t.Fatalf("coalesced %d tablets, want 3", len(bulk.tablets)) } }) }) t.Run("DoesNotCrossFlushOrNonAddBoundaries", func(t *testing.T) { runQueue(func(q *opQueue) { flushDone := make(chan struct{}) q.send(opAddTablet{tablet: newTablet(0, 99)}) q.send(opFlush{done: flushDone}) q.send(opRemoveHost{hostID: testHostUUID("host-1")}) q.send(opAddTablet{tablet: newTablet(100, 199)}) <-flushDone }, []string{"bulk", "flush", "removeHost", "bulk"}, func(t *testing.T, processed []tabletOp) { first := processed[0].(opBulkAddTablets) second := processed[3].(opBulkAddTablets) if len(first.tablets) != 1 || len(second.tablets) != 1 { t.Fatalf("unexpected coalesced batch sizes: %d and %d", len(first.tablets), len(second.tablets)) } }) }) } func TestCowTabletListConcurrency(t *testing.T) { t.Parallel() t.Run("ConcurrentReads", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() hosts := GenerateHostUUIDs(3) cl.BulkAddTablets(createTablets("ks", "tb", hosts, 2, 100, 100)) cl.Flush() var wg sync.WaitGroup for i := 0; i < 20; i++ { wg.Add(1) go func() { defer wg.Done() rnd := getThreadSafeRnd() for j := 0; j < 1000; j++ { token := rnd.Int63() _, _ = cl.FindTabletForToken("ks", "tb", token) cl.FindReplicasUnsafeForToken("ks", "tb", token) } }() } wg.Wait() }) t.Run("ConcurrentReadWrite", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() hosts := GenerateHostUUIDs(3) cl.BulkAddTablets(createTablets("ks", "tb", hosts, 2, 100, 100)) cl.Flush() var wg sync.WaitGroup for i := 0; i < 10; i++ { wg.Add(1) go func() { defer wg.Done() rnd := getThreadSafeRnd() for j := 0; j < 1000; j++ { _, _ = cl.FindTabletForToken("ks", "tb", rnd.Int63()) cl.Get() } }() } repGen := NewReplicaSetGenerator(hosts, 2) for i := 0; i < 5; i++ { wg.Add(1) go func() { defer wg.Done() rnd := getThreadSafeRnd() for j := 0; j < 100; j++ { token := rnd.Int63() cl.AddTablet(TabletInfo{ keyspaceName: "ks", tableName: "tb", firstToken: token - 100, lastToken: token, replicas: repGen.Next(), }) } }() } wg.Wait() }) t.Run("ConcurrentMultiTableReadWrite", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() hosts := GenerateHostUUIDs(6) tables := []string{"tb1", "tb2", "tb3", "tb4", "tb5"} for _, tb := range tables { cl.BulkAddTablets(createTablets("ks", tb, hosts, 3, 50, 50)) } cl.Flush() var wg sync.WaitGroup for i := 0; i < 10; i++ { wg.Add(1) go func(idx int) { defer wg.Done() rnd := getThreadSafeRnd() tb := tables[idx%len(tables)] for j := 0; j < 500; j++ { _, _ = cl.FindTabletForToken("ks", tb, rnd.Int63()) } }(i) } repGen := NewReplicaSetGenerator(hosts, 3) for i := 0; i < 5; i++ { wg.Add(1) go func(idx int) { defer wg.Done() rnd := getThreadSafeRnd() tb := tables[idx%len(tables)] for j := 0; j < 50; j++ { token := rnd.Int63() cl.AddTablet(TabletInfo{ keyspaceName: "ks", tableName: tb, firstToken: token - 100, lastToken: token, replicas: repGen.Next(), }) } }(i) } wg.Add(1) go func() { defer wg.Done() for i := 0; i < 5; i++ { cl.RemoveTabletsWithHost(hosts[i]) } }() wg.Wait() }) t.Run("ConcurrentRemoveKeyspace", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() hosts := GenerateHostUUIDs(3) cl.BulkAddTablets(createTablets("ks1", "tb", hosts, 2, 50, 50)) cl.BulkAddTablets(createTablets("ks2", "tb", hosts, 2, 50, 50)) cl.Flush() var wg sync.WaitGroup for i := 0; i < 10; i++ { wg.Add(1) go func() { defer wg.Done() rnd := getThreadSafeRnd() for j := 0; j < 500; j++ { _, _ = cl.FindTabletForToken("ks1", "tb", rnd.Int63()) _, _ = cl.FindTabletForToken("ks2", "tb", rnd.Int63()) } }() } wg.Add(1) go func() { defer wg.Done() cl.RemoveTabletsWithKeyspace("ks2") }() wg.Wait() cl.Flush() _, ok := cl.FindTabletForToken("ks2", "tb", 0) if ok { t.Fatal("expected nil for removed keyspace") } _, ok = cl.FindTabletForToken("ks1", "tb", 0) if !ok { t.Fatal("expected tablet for ks1") } }) t.Run("ConcurrentRemovalOperations", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() hostUUIDs := GenerateHostUUIDs(9) // 3 keyspaces * 3 hosts hostIdx := 0 for ks := 0; ks < 3; ks++ { for host := 0; host < 3; host++ { hostID := hostUUIDs[hostIdx] hostIdx++ for i := 0; i < 10; i++ { cl.AddTablet(TabletInfo{ keyspaceName: fmt.Sprintf("ks%d", ks), tableName: "tb", firstToken: int64(ks*10000 + host*1000 + i*100), lastToken: int64(ks*10000 + host*1000 + i*100 + 99), replicas: []ReplicaInfo{{hostID, 0}}, }) } } } cl.Flush() allTablets := cl.Get() var host0, host1 HostUUID var host0Set, host1Set bool hostCount := make(map[HostUUID]int) for _, tablet := range allTablets { for _, replica := range tablet.Replicas() { hostID := replica.HostUUIDValue() hostCount[hostID]++ if !host0Set { host0 = hostID host0Set = true } else if !host1Set && hostID != host0 { host1 = hostID host1Set = true } } } _ = host1 // host1 is used below implicitly via hostCount var wg sync.WaitGroup wg.Add(3) go func() { defer wg.Done() cl.RemoveTabletsWithHost(host0) }() go func() { defer wg.Done() cl.RemoveTabletsWithKeyspace("ks1") }() go func() { defer wg.Done() cl.RemoveTabletsWithTable("ks2", "tb") }() wg.Wait() cl.Flush() remaining := cl.Get() t.Logf("Remaining tablets after concurrent removals: %d", len(remaining)) for _, tablet := range remaining { for _, replica := range tablet.Replicas() { if replica.HostUUIDValue() == host0 { t.Errorf("found tablet with removed host %s", host0.String()) } } } for _, tablet := range remaining { if tablet.KeyspaceName() == "ks1" || tablet.KeyspaceName() == "ks2" { t.Errorf("found tablet in removed keyspace: %s", tablet.KeyspaceName()) } } }) t.Run("CloseRace", func(t *testing.T) { list := NewCowTabletList() tablet := TabletInfo{ keyspaceName: "ks", tableName: "tbl", firstToken: -100, lastToken: 100, replicas: []ReplicaInfo{{testHostUUID("host1"), 0}}, } list.BulkAddTablets(TabletInfoList{tablet}) list.Flush() ready := make(chan struct{}) done := make(chan bool) for i := 0; i < 10; i++ { go func() { defer func() { done <- true }() ready <- struct{}{} for j := 0; j < 1000; j++ { _, _ = list.FindTabletForToken("ks", "tbl", 50) } }() } for i := 0; i < 10; i++ { <-ready } list.Close() for i := 0; i < 10; i++ { <-done } }) t.Run("FlushCloseRace", func(t *testing.T) { cl := NewCowTabletList() uuids := GenerateHostUUIDs(100) for i := 0; i < 100; i++ { cl.AddTablet(TabletInfo{ keyspaceName: "ks", tableName: "tb", firstToken: int64(i * 100), lastToken: int64(i*100 + 99), replicas: []ReplicaInfo{{uuids[i], 0}}, }) } var wg sync.WaitGroup const flushers = 10 for i := 0; i < flushers; i++ { wg.Add(1) go func() { defer wg.Done() cl.Flush() }() } cl.Close() done := make(chan struct{}) go func() { wg.Wait() close(done) }() select { case <-done: case <-time.After(5 * time.Second): t.Fatal("concurrent Flush + Close caused deadlock") } }) } func TestCowTabletListEdgeCases(t *testing.T) { t.Parallel() t.Run("ExtremeTokenValues", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() host1 := GenerateHostUUIDs(1)[0] cl.AddTablet(TabletInfo{ keyspaceName: "ks", tableName: "tb", firstToken: math.MinInt64, lastToken: 0, replicas: []ReplicaInfo{{host1, 0}}, }) cl.AddTablet(TabletInfo{ keyspaceName: "ks", tableName: "tb", firstToken: 0, lastToken: math.MaxInt64, replicas: []ReplicaInfo{{host1, 1}}, }) cl.Flush() ti, ok := cl.FindTabletForToken("ks", "tb", math.MinInt64) if !ok { t.Fatal("expected tablet for math.MinInt64") } tests.AssertEqual(t, "MinInt64 shard", 0, ti.Replicas()[0].ShardID()) ti, ok = cl.FindTabletForToken("ks", "tb", math.MaxInt64) if !ok { t.Fatal("expected tablet for math.MaxInt64") } tests.AssertEqual(t, "MaxInt64 shard", 1, ti.Replicas()[0].ShardID()) _, ok = cl.FindTabletForToken("ks", "tb", 0) if !ok { t.Fatal("expected tablet for token 0") } }) t.Run("EmptyReplicaSet", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() cl.AddTablet(TabletInfo{ keyspaceName: "ks", tableName: "tb", firstToken: -100, lastToken: 100, replicas: []ReplicaInfo{}, }) cl.Flush() replicas := cl.FindReplicasForToken("ks", "tb", 0) if replicas == nil { t.Log("FindReplicasForToken returned nil for tablet with no replicas") } else if len(replicas) != 0 { t.Errorf("expected empty replica set, got %d replicas", len(replicas)) } tablet, ok := cl.FindTabletForToken("ks", "tb", 0) if !ok { t.Fatal("expected tablet to exist") } if len(tablet.ReplicasUnsafe()) != 0 { t.Errorf("expected empty replica list, got %d replicas", len(tablet.ReplicasUnsafe())) } }) t.Run("TabletInfoBuilderInvalidRange", func(t *testing.T) { hostID := GenerateHostUUIDs(1)[0] builder := TabletInfoBuilder{ KeyspaceName: "ks", TableName: "tb", FirstToken: 100, LastToken: -100, Replicas: [][]any{{hostID.String(), 0}}, } _, err := builder.Build() if err == nil { t.Fatal("expected error for inverted token range") } t.Logf("Got expected error: %v", err) }) t.Run("QueueSaturation", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() const operations = 10000 done := make(chan bool) uuids := GenerateHostUUIDs(operations) go func() { for i := 0; i < operations; i++ { tablet := TabletInfo{ keyspaceName: "ks", tableName: "tb", firstToken: int64(i * 100), lastToken: int64(i*100 + 99), replicas: []ReplicaInfo{{uuids[i], 0}}, } cl.AddTablet(tablet) } done <- true }() select { case <-done: cl.Flush() tablets := cl.GetTableTablets("ks", "tb") tests.AssertEqual(t, "tablet count", operations, len(tablets)) case <-time.After(10 * time.Second): t.Fatal("queue saturation caused deadlock") } }) t.Run("QueueSaturationReadsConsistent", func(t *testing.T) { cl := NewCowTabletList() defer cl.Close() cl.AddTablet(TabletInfo{ keyspaceName: "ks", tableName: "tb", firstToken: -1000, lastToken: -900, replicas: []ReplicaInfo{{GenerateHostUUIDs(1)[0], 0}}, }) cl.Flush() const writers = 10000 var wg sync.WaitGroup writerUUIDs := GenerateHostUUIDs(writers) wg.Add(1) go func() { defer wg.Done() for i := 0; i < writers; i++ { cl.AddTablet(TabletInfo{ keyspaceName: "ks", tableName: "tb", firstToken: int64(i * 100), lastToken: int64(i*100 + 99), replicas: []ReplicaInfo{{writerUUIDs[i], 0}}, }) } }() const readers = 5 for r := 0; r < readers; r++ { wg.Add(1) go func() { defer wg.Done() for i := 0; i < 1000; i++ { entry, ok := cl.FindTabletForToken("ks", "tb", -950) if ok { if entry.FirstToken() > entry.LastToken() { t.Errorf("invalid token range: first=%d > last=%d", entry.FirstToken(), entry.LastToken()) } replicas := entry.Replicas() if len(replicas) == 0 { t.Error("expected at least one replica") } } entries := cl.GetTableTablets("ks", "tb") for _, e := range entries { if e.FirstToken() > e.LastToken() { t.Errorf("invalid token range in list: first=%d > last=%d", e.FirstToken(), e.LastToken()) } } } }() } wg.Wait() }) } ================================================ FILE: tablets/tabets_utils_test.go ================================================ //go:build unit // +build unit package tablets import ( "math" "testing" ) func TestCreateTablets(t *testing.T) { t.Run("BasicDistribution", func(t *testing.T) { hosts := GenerateHostUUIDs(3) tl := createTablets("ks", "tbl", hosts, 2, 6, 6) if len(tl) != 6 { t.Errorf("expected 6 tablets, got %d", len(tl)) } for _, tablet := range tl { if len(tablet.replicas) != 2 { t.Errorf("each tablet should have 2 replicas, got %d", len(tablet.replicas)) } replicaSet := map[HostUUID]bool{} for _, r := range tablet.replicas { if replicaSet[r.hostId] { t.Errorf("duplicate replica %s in tablet", r.hostId) } replicaSet[r.hostId] = true } } }) t.Run("SingleTabletFullRange", func(t *testing.T) { hosts := GenerateHostUUIDs(3) tl := createTablets("ks", "tbl", hosts, 3, 1, 1) t0 := tl[0] if t0.firstToken != math.MinInt64 { t.Errorf("unexpected firstToken: %d", t0.firstToken) } if t0.lastToken != math.MaxInt64 { t.Errorf("unexpected lastToken: %d", t0.lastToken) } }) } func TestReplicaGenerator(t *testing.T) { hosts := GenerateHostUUIDs(4) rf := 2 g := NewReplicaSetGenerator(hosts, rf) var seen [][]HostUUID for i := 0; i < 6; i++ { gen := g.Next() if len(gen) != rf { t.Fatalf("expected %d replicas, got %d", rf, len(gen)) } var ids []HostUUID for _, r := range gen { ids = append(ids, r.HostUUIDValue()) } seen = append(seen, ids) } for i := 0; i < len(seen); i++ { outer: for j := i + 1; j < len(seen); j++ { for k := 0; k < len(seen[i]); k++ { if seen[i][k] != seen[j][k] { continue outer } } t.Errorf("expected different output for different seeds, but found same seeds for %d and %d: %v == %v", i, j, seen[i], seen[j]) } } } ================================================ FILE: tablets/tablet_utils.go ================================================ package tablets import ( "encoding/binary" "math" "math/rand" "sync/atomic" "github.com/gocql/gocql/internal/tests" ) const randSeed = 100 // ReplicaSetGenerator generates all possible k-combinations (replica sets) of a given list of hosts, // where each combination contains `rf` elements. The generator cycles through all possible combinations // infinitely in a thread-safe manner using an atomic counter. type ReplicaSetGenerator struct { hosts []HostUUID // List of available hosts rf int // Replication factor (number of hosts per combination) len int // Total number of hosts counter uint64 // Current position in the sequence of combinations total uint64 // Total number of possible combinations (n choose rf) } // NewReplicaSetGenerator creates and returns a new ReplicaSetGenerator for the given set of hosts // and replication factor `rf`. It panics if `rf` is non-positive or greater than the number of hosts. // The generator produces all k-combinations of the input set and loops over them indefinitely. func NewReplicaSetGenerator(hosts []HostUUID, rf int) *ReplicaSetGenerator { n := len(hosts) if rf <= 0 { panic("replication factor must be positive") } if rf > len(hosts) { panic("replication factor cannot exceed number of hosts") } return &ReplicaSetGenerator{ hosts: hosts, rf: rf, len: n, total: uint64(binomial(n, rf)), } } // Next returns the next replica set as a slice of ReplicaInfo. The combinations are returned in a // deterministic order and wrap around after exhausting all possible combinations. // This method is safe for concurrent use. func (it *ReplicaSetGenerator) Next() []ReplicaInfo { // Advance and wrap around counter := atomic.AddUint64(&it.counter, 1) % it.total // Map current counter to combination return unrankCombination(it.len, it.rf, int(counter), it.hosts) } // binomial calculates the number of unique combinations (n choose k) // for selecting `rf` elements from a set of `hosts` elements. // // It returns the binomial coefficient C(hosts, rf), which represents // the number of ways to choose `rf` items from a total of `hosts` without // regard to order. // // If rf < 0 or rf > hosts, the function returns 0. // If rf == 0 or rf == hosts, the function returns 1. func binomial(hosts, rf int) int { if rf < 0 || rf > hosts { return 0 } if rf == 0 || rf == hosts { return 1 } num := 1 den := 1 for i := 1; i <= rf; i++ { num *= hosts - (i - 1) den *= i } return num / den } // unrankCombination returns the k-combination of elements from the input slice // corresponding to the given rank (counter) in lexicographic order. func unrankCombination(n, k, counter int, input []HostUUID) []ReplicaInfo { comb := make([]ReplicaInfo, 0, k) x := 0 for i := 0; i < k; i++ { for { b := binomial(n-x-1, k-i-1) if counter < b { comb = append(comb, ReplicaInfo{ hostId: input[x], shardId: 0, }) x++ break } else { counter -= b x++ } } } return comb } func getThreadSafeRnd() *tests.ThreadSafeRand { return tests.NewThreadSafeRand(randSeed) } func getRnd() *rand.Rand { return rand.New(rand.NewSource(randSeed)) } // GenerateHostUUIDs generates a slice of deterministic HostUUIDs for testing. // Byte 0 is set to 0xFE so that even index 0 is never the zero UUID. func GenerateHostUUIDs(count int) []HostUUID { hosts := make([]HostUUID, count) for i := range hosts { hosts[i][0] = 0xFE binary.BigEndian.PutUint64(hosts[i][8:], uint64(i)) } return hosts } // createTablets generates a list of TabletInfo entries for a given keyspace and table. // Each tablet is assigned a token range and a set of replica hosts. func createTablets(ks, table string, hosts []HostUUID, rf, count int, tokenRangeCount int64) TabletInfoList { out := make(TabletInfoList, count) step := math.MaxUint64 / uint64(tokenRangeCount) repGen := NewReplicaSetGenerator(hosts, rf) firstToken := int64(math.MinInt64) for i := 0; i < count; i++ { out[i] = TabletInfo{ keyspaceName: ks, tableName: table, firstToken: firstToken, lastToken: firstToken + int64(step), replicas: repGen.Next(), } firstToken = firstToken + int64(step) } return out } ================================================ FILE: tablets/tablets.go ================================================ package tablets import ( "encoding/hex" "fmt" "slices" "sort" "sync" "sync/atomic" ) // HostUUID is a 16-byte binary UUID used to identify hosts without heap allocation. type HostUUID [16]byte // String returns the canonical UUID string representation (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx). func (u HostUUID) String() string { var buf [36]byte hex.Encode(buf[0:8], u[0:4]) buf[8] = '-' hex.Encode(buf[9:13], u[4:6]) buf[13] = '-' hex.Encode(buf[14:18], u[6:8]) buf[18] = '-' hex.Encode(buf[19:23], u[8:10]) buf[23] = '-' hex.Encode(buf[24:36], u[10:16]) return string(buf[:]) } // IsEmpty reports whether the UUID is all zeros. func (u HostUUID) IsEmpty() bool { return u == HostUUID{} } // ParseHostUUID parses a UUID string (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) into a HostUUID. func ParseHostUUID(s string) (HostUUID, error) { if len(s) != 36 { return HostUUID{}, fmt.Errorf("invalid UUID length: %d", len(s)) } if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { return HostUUID{}, fmt.Errorf("invalid UUID format: %s", s) } var u HostUUID var buf [32]byte copy(buf[0:8], s[0:8]) copy(buf[8:12], s[9:13]) copy(buf[12:16], s[14:18]) copy(buf[16:20], s[19:23]) copy(buf[20:32], s[24:36]) _, err := hex.Decode(u[:], buf[:]) if err != nil { return HostUUID{}, fmt.Errorf("invalid UUID hex: %s: %w", s, err) } return u, nil } // MustParseHostUUID is like ParseHostUUID but panics on error. func MustParseHostUUID(s string) HostUUID { u, err := ParseHostUUID(s) if err != nil { panic(err) } return u } type ReplicaInfo struct { // hostId stored as binary UUID to avoid per-replica heap allocation. hostId HostUUID shardId int } func (r ReplicaInfo) HostID() string { return r.hostId.String() } // HostUUIDValue returns the raw binary host UUID for zero-allocation comparison. func (r ReplicaInfo) HostUUIDValue() HostUUID { return r.hostId } func (r ReplicaInfo) ShardID() int { return r.shardId } func (r ReplicaInfo) String() string { return fmt.Sprintf("ReplicaInfo{hostId:%s, shardId:%d}", r.hostId.String(), r.shardId) } type TabletInfoBuilder struct { KeyspaceName string TableName string Replicas [][]any FirstToken int64 LastToken int64 } func NewTabletInfoBuilder() TabletInfoBuilder { return TabletInfoBuilder{} } type toString interface { String() string } // uuidProvider is satisfied by types that can provide raw UUID bytes (e.g., gocql.UUID). type uuidProvider interface { Bytes() []byte } func (b TabletInfoBuilder) Build() (TabletInfo, error) { if b.FirstToken > b.LastToken { return TabletInfo{}, fmt.Errorf("invalid token range: firstToken (%d) > lastToken (%d)", b.FirstToken, b.LastToken) } tabletReplicas := make([]ReplicaInfo, 0, len(b.Replicas)) for _, replica := range b.Replicas { if len(replica) != 2 { return TabletInfo{}, fmt.Errorf("replica info should have exactly two elements, but it has %d: %v", len(replica), replica) } shardId, ok := replica[1].(int) if !ok { return TabletInfo{}, fmt.Errorf("second element (shard) of replica is not int: %v", replica) } var hostUUID HostUUID switch v := replica[0].(type) { case uuidProvider: raw := v.Bytes() if len(raw) != 16 { return TabletInfo{}, fmt.Errorf("UUID bytes has wrong length %d, expected 16", len(raw)) } copy(hostUUID[:], raw) case string: parsed, err := ParseHostUUID(v) if err != nil { return TabletInfo{}, fmt.Errorf("first element (hostID) cannot be parsed as UUID: %v: %w", replica, err) } hostUUID = parsed case toString: parsed, err := ParseHostUUID(v.String()) if err != nil { return TabletInfo{}, fmt.Errorf("first element (hostID) cannot be parsed as UUID: %v: %w", replica, err) } hostUUID = parsed default: return TabletInfo{}, fmt.Errorf("first element (hostID) of replica is not UUID: %v", replica) } tabletReplicas = append(tabletReplicas, ReplicaInfo{hostUUID, shardId}) } return TabletInfo{ keyspaceName: b.KeyspaceName, tableName: b.TableName, firstToken: b.FirstToken, lastToken: b.LastToken, replicas: tabletReplicas, }, nil } // TabletInfo represents a tablet with its token range and replica set. type TabletInfo struct { keyspaceName string tableName string replicas []ReplicaInfo firstToken int64 lastToken int64 } func (t TabletInfo) KeyspaceName() string { return t.keyspaceName } func (t TabletInfo) FirstToken() int64 { return t.firstToken } func (t TabletInfo) LastToken() int64 { return t.lastToken } func (t TabletInfo) TableName() string { return t.tableName } func (t TabletInfo) Replicas() []ReplicaInfo { result := make([]ReplicaInfo, len(t.replicas)) copy(result, t.replicas) return result } // ReplicasUnsafe returns the raw replica slice without copying. func (t TabletInfo) ReplicasUnsafe() []ReplicaInfo { return t.replicas } type TabletInfoList []TabletInfo // TabletEntry is the per-table representation of a tablet, without keyspace/table names. type TabletEntry struct { replicas []ReplicaInfo firstToken int64 lastToken int64 } type TabletEntryList []TabletEntry // Replicas returns a copy of the replica list for this entry. func (e TabletEntry) Replicas() []ReplicaInfo { result := make([]ReplicaInfo, len(e.replicas)) copy(result, e.replicas) return result } // ReplicasUnsafe returns the raw replica slice without copying. func (e TabletEntry) ReplicasUnsafe() []ReplicaInfo { return e.replicas } func (e TabletEntry) FirstToken() int64 { return e.firstToken } func (e TabletEntry) LastToken() int64 { return e.lastToken } // findOverlapRange returns the start and tailStart indices for entries // overlapping with the token range [firstToken, lastToken]. // start is the first overlapping entry; tailStart is the first entry // after the overlap region. func (t TabletEntryList) findOverlapRange(firstToken, lastToken int64) (start, tailStart int) { if len(t) == 0 { return 0, 0 } l := 0 r := len(t) l1, r1 := l, r l2, r2 := l1, r1 for l1 < r1 { mid := l1 + (r1-l1)/2 if t[mid].firstToken < firstToken { l1 = mid + 1 } else { r1 = mid } } start = l1 // Adjust start backward if the previous entry overlaps. if start > l && t[start-1].lastToken > firstToken { start = start - 1 } for l2 < r2 { mid := l2 + (r2-l2)/2 if t[mid].lastToken < lastToken { l2 = mid + 1 } else { r2 = mid } } end := l2 if end < len(t) && t[end].firstToken > lastToken { end = end - 1 } if end >= len(t) { end = len(t) - 1 } if start <= end && end >= 0 { tailStart = end + 1 } else { tailStart = start } return start, tailStart } // addEntry inserts a single entry into the sorted list, replacing any overlapping ranges. // Returns a new slice without mutating the original. func (t TabletEntryList) addEntry(e TabletEntry) TabletEntryList { start, tailStart := t.findOverlapRange(e.firstToken, e.lastToken) result := make(TabletEntryList, 0, start+1+(len(t)-tailStart)) result = append(result, t[:start]...) result = append(result, e) result = append(result, t[tailStart:]...) return result } // bulkAddEntries inserts a sorted batch of entries, replacing any overlapping ranges. // Returns a new slice without mutating the original. // The entries must be sorted by firstToken, then lastToken. Entries may have // gaps between them or overlap each other within the batch; existing entries // that fall in gaps between batch entries are preserved. Intra-batch overlaps // are resolved by letting later entries replace earlier ones. func (t TabletEntryList) bulkAddEntries(entries TabletEntryList) TabletEntryList { if len(entries) == 0 { return t } // Resolve intra-batch overlaps: later entries replace earlier ones. deduped := make(TabletEntryList, 0, len(entries)) for _, e := range entries { // Drop any previously added entries that the current one overlaps. for len(deduped) > 0 && deduped[len(deduped)-1].firstToken >= e.firstToken { deduped = deduped[:len(deduped)-1] } // If the last kept entry partially overlaps, drop it too. if len(deduped) > 0 && deduped[len(deduped)-1].lastToken > e.firstToken { deduped = deduped[:len(deduped)-1] } deduped = append(deduped, e) } entries = deduped // Merge the existing list (t) with the batch (entries). // Both are sorted by firstToken. For each batch entry we remove any // overlapping existing entries; existing entries that sit in gaps // between batch entries are preserved. result := make(TabletEntryList, 0, len(t)+len(entries)) ti := 0 // index into t (existing list) for _, e := range entries { // Copy existing entries that come entirely before this batch entry. for ti < len(t) && t[ti].lastToken <= e.firstToken && t[ti].firstToken < e.firstToken { result = append(result, t[ti]) ti++ } // Skip existing entries that overlap with this batch entry. for ti < len(t) && t[ti].firstToken < e.lastToken && t[ti].lastToken > e.firstToken { ti++ } result = append(result, e) } // Append remaining existing entries that come after all batch entries. result = append(result, t[ti:]...) return result } // findEntryForToken performs a binary search within [l, r) to find the entry // covering the given token. Returns false if no such entry exists. func (t TabletEntryList) findEntryForToken(token int64, l int, r int) (TabletEntry, bool) { if l < 0 || r > len(t) || l > r { return TabletEntry{}, false } if l == r { return TabletEntry{}, false } for l < r { m := l + (r-l)/2 if t[m].lastToken < token { l = m + 1 } else { r = m } } if l >= len(t) { return TabletEntry{}, false } if t[l].firstToken > token { return TabletEntry{}, false } return t[l], true } // removeEntriesWithHost returns a new list excluding entries with a replica on the given host. func (t TabletEntryList) removeEntriesWithHost(hostID HostUUID) TabletEntryList { hasMatch := false for _, e := range t { for _, r := range e.replicas { if r.hostId == hostID { hasMatch = true break } } if hasMatch { break } } if !hasMatch { return t } result := make(TabletEntryList, 0, len(t)) for _, e := range t { exclude := false for _, r := range e.replicas { if r.hostId == hostID { exclude = true break } } if !exclude { result = append(result, e) } } return result } // toEntry converts a TabletInfo to a TabletEntry. func (t TabletInfo) toEntry() TabletEntry { return TabletEntry{ replicas: slices.Clone(t.replicas), firstToken: t.firstToken, lastToken: t.lastToken, } } // toTabletInfo converts a TabletEntry back to a TabletInfo. func (e TabletEntry) toTabletInfo(keyspace, table string) TabletInfo { return TabletInfo{ keyspaceName: keyspace, tableName: table, replicas: slices.Clone(e.replicas), firstToken: e.firstToken, lastToken: e.lastToken, } } // tableKey identifies a specific table within a keyspace. type tableKey struct { keyspace string table string } // tableTablets holds a per-table sorted tablet list with copy-on-write semantics. type tableTablets struct { list atomic.Pointer[TabletEntryList] // stores TabletEntryList for this table } func newTableTablets() *tableTablets { tt := &tableTablets{} empty := make(TabletEntryList, 0) tt.list.Store(&empty) return tt } func (tt *tableTablets) store(list TabletEntryList) { tt.list.Store(&list) } // tabletOp is an operation processed by the writer goroutine. type tabletOp interface { execute(c *CowTabletList) } type opAddTablet struct { tablet TabletInfo } func (op opAddTablet) execute(c *CowTabletList) { c.doAddTablet(op.tablet) } type opBulkAddTablets struct { tablets TabletInfoList } func (op opBulkAddTablets) execute(c *CowTabletList) { c.doBulkAddTablets(op.tablets) } type opRemoveHost struct { hostID HostUUID } func (op opRemoveHost) execute(c *CowTabletList) { c.doRemoveTabletsWithHost(op.hostID) } type opRemoveKeyspace struct { keyspace string } func (op opRemoveKeyspace) execute(c *CowTabletList) { c.doRemoveTabletsWithKeyspace(op.keyspace) } type opRemoveTable struct { keyspace string table string } func (op opRemoveTable) execute(c *CowTabletList) { c.doRemoveTabletsWithTable(op.keyspace, op.table) } type opFlush struct { done chan struct{} } func (op opFlush) execute(_ *CowTabletList) { close(op.done) } // opQueueBufferSize is the capacity of the writer goroutine's operation queue. const opQueueBufferSize = 4096 // opQueue manages a single writer goroutine with safe send/close/flush coordination. type opQueue struct { cachedItem tabletOp ops chan tabletOp quit chan struct{} stopped chan struct{} waiters *sync.Cond senders int closeOnce sync.Once lifecycle sync.Mutex closed bool } func newOpQueue() *opQueue { q := &opQueue{ ops: make(chan tabletOp, opQueueBufferSize), quit: make(chan struct{}), stopped: make(chan struct{}), } q.waiters = sync.NewCond(&q.lifecycle) return q } func (q *opQueue) next() tabletOp { if q.cachedItem != nil { item := q.cachedItem q.cachedItem = nil return item } var op tabletOp select { case <-q.quit: { return nil } case op = <-q.ops: opAdd, ok := op.(opAddTablet) if !ok { return op } bulkOp := opBulkAddTablets{ tablets: make(TabletInfoList, 0, 1), } bulkOp.tablets = append(bulkOp.tablets, opAdd.tablet) for { select { case op = <-q.ops: opAdd, ok = op.(opAddTablet) if !ok { q.cachedItem = op return bulkOp } bulkOp.tablets = append(bulkOp.tablets, opAdd.tablet) default: return bulkOp } } } } // run is the writer goroutine loop. func (q *opQueue) run(process func(tabletOp)) { defer close(q.stopped) for { op := q.next() if op == nil { return } process(op) } } // close stops the writer goroutine after draining in-flight senders. func (q *opQueue) close() { q.closeOnce.Do(func() { q.lifecycle.Lock() q.closed = true for q.senders > 0 { q.waiters.Wait() } close(q.quit) q.lifecycle.Unlock() }) <-q.stopped } // flush blocks until all previously submitted operations have been processed. func (q *opQueue) flush() { done := make(chan struct{}) if !q.beginSend() { return } defer q.endSend() sent := false select { case q.ops <- opFlush{done: done}: sent = true case <-q.quit: } if !sent { return } select { case <-done: case <-q.stopped: } } // send enqueues an operation. func (q *opQueue) send(op tabletOp) { if !q.beginSend() { return } defer q.endSend() select { case q.ops <- op: case <-q.quit: } } func (q *opQueue) beginSend() bool { q.lifecycle.Lock() defer q.lifecycle.Unlock() if q.closed { return false } q.senders++ return true } func (q *opQueue) endSend() { q.lifecycle.Lock() q.senders-- if q.senders == 0 { q.waiters.Broadcast() } q.lifecycle.Unlock() } // tableMap is the type stored inside the atomic pointer. type tableMap = map[tableKey]*tableTablets // CowTabletList stores tablets partitioned by keyspace/table. // All writes are serialized through a single writer goroutine; reads are lock-free. // Write operations are asynchronous; use Flush() for read-your-writes consistency. type CowTabletList struct { tables atomic.Pointer[tableMap] queue *opQueue } // NewCowTabletList creates a new CowTabletList and starts its writer goroutine. // The caller must call Close() when done to stop the writer goroutine. func NewCowTabletList() *CowTabletList { c := &CowTabletList{ queue: newOpQueue(), } empty := make(tableMap) c.tables.Store(&empty) go c.queue.run(func(op tabletOp) { op.execute(c) }) return c } // Close stops the writer goroutine after draining all pending operations. func (c *CowTabletList) Close() { if c == nil { return } c.queue.close() } // Flush blocks until all previously submitted write operations have been processed. func (c *CowTabletList) Flush() { if c == nil { return } c.queue.flush() } // cloneTableMap returns a shallow copy of the current table map. func (c *CowTabletList) cloneTableMap() tableMap { old := *c.tables.Load() m := make(tableMap, len(old)+1) for k, v := range old { m[k] = v } return m } // getOrCreateTable returns the tableTablets for the given key, creating it if needed. func (c *CowTabletList) getOrCreateTable(key tableKey) *tableTablets { current := *c.tables.Load() tt := current[key] if tt != nil { return tt } tt = newTableTablets() m := c.cloneTableMap() m[key] = tt c.tables.Store(&m) return tt } func (c *CowTabletList) doAddTablet(tablet TabletInfo) { if tablet.keyspaceName == "" || tablet.tableName == "" { return } key := tableKey{tablet.keyspaceName, tablet.tableName} tt := c.getOrCreateTable(key) tt.store((*tt.list.Load()).addEntry(tablet.toEntry())) } func (c *CowTabletList) doBulkAddTablets(tablets TabletInfoList) { if len(tablets) == 0 { return } groups := make(map[tableKey]TabletInfoList) for _, t := range tablets { if t.keyspaceName == "" || t.tableName == "" { continue } key := tableKey{t.keyspaceName, t.tableName} groups[key] = append(groups[key], t) } for key, group := range groups { sort.Slice(group, func(i, j int) bool { if group[i].FirstToken() != group[j].FirstToken() { return group[i].FirstToken() < group[j].FirstToken() } return group[i].LastToken() < group[j].LastToken() }) entries := make(TabletEntryList, len(group)) for i, t := range group { entries[i] = t.toEntry() } tt := c.getOrCreateTable(key) tt.store((*tt.list.Load()).bulkAddEntries(entries)) } } func (c *CowTabletList) doRemoveTabletsWithHost(hostID HostUUID) { current := *c.tables.Load() needsMapUpdate := false for _, tt := range current { old := tt.list.Load() newList := (*old).removeEntriesWithHost(hostID) if len(newList) != len(*old) { tt.store(newList) if len(newList) == 0 { needsMapUpdate = true } } } if needsMapUpdate { newMap := make(tableMap, len(current)) for k, v := range current { if len(*v.list.Load()) > 0 { newMap[k] = v } } c.tables.Store(&newMap) } } func (c *CowTabletList) doRemoveTabletsWithKeyspace(keyspace string) { current := *c.tables.Load() hasKey := false for k := range current { if k.keyspace == keyspace { hasKey = true break } } if !hasKey { return } newMap := make(tableMap, len(current)) for k, v := range current { if k.keyspace != keyspace { newMap[k] = v } } c.tables.Store(&newMap) } func (c *CowTabletList) doRemoveTabletsWithTable(keyspace, table string) { current := *c.tables.Load() key := tableKey{keyspace, table} if _, exists := current[key]; !exists { return } newMap := make(tableMap, len(current)) for k, v := range current { if k != key { newMap[k] = v } } c.tables.Store(&newMap) } // --- Public read methods --- // getTable returns the tableTablets for the given key, or nil if not found. func (c *CowTabletList) getTable(key tableKey) *tableTablets { current := *c.tables.Load() return current[key] } // Get returns a flat TabletInfoList containing all tablets across all tables. // // Deprecated: Use [CowTabletList.GetTableTablets] for per-table lookups or // [CowTabletList.ForEach] to iterate without aggregating into a flat list. func (c *CowTabletList) Get() TabletInfoList { if c == nil { return nil } current := *c.tables.Load() type snap struct { key tableKey list TabletEntryList } snaps := make([]snap, 0, len(current)) total := 0 for key, tt := range current { l := *tt.list.Load() snaps = append(snaps, snap{key, l}) total += len(l) } result := make(TabletInfoList, 0, total) for _, s := range snaps { for _, e := range s.list { result = append(result, e.toTabletInfo(s.key.keyspace, s.key.table)) } } return result } // GetTableTablets returns a copy of the tablet list for the specified keyspace and table. // Returns nil if no tablets exist for the given combination. func (c *CowTabletList) GetTableTablets(keyspace, table string) TabletEntryList { if c == nil { return nil } tt := c.getTable(tableKey{keyspace, table}) if tt == nil { return nil } snapshot := *tt.list.Load() if len(snapshot) == 0 { return nil } result := make(TabletEntryList, len(snapshot)) copy(result, snapshot) return result } // ForEach iterates over all keyspace/table pairs and their tablet entry lists, // calling fn for each one. Iteration stops early if fn returns false. // The returned TabletEntryList is a shallow copy; do not mutate entries or their replica slices. func (c *CowTabletList) ForEach(fn func(keyspace, table string, entries TabletEntryList) bool) { if c == nil || fn == nil { return } current := *c.tables.Load() for key, tt := range current { snapshot := *tt.list.Load() if len(snapshot) == 0 { continue } entries := make(TabletEntryList, len(snapshot)) copy(entries, snapshot) if !fn(key.keyspace, key.table, entries) { return } } } // FindReplicasForToken returns a copy of the replica set for the given token. func (c *CowTabletList) FindReplicasForToken(keyspace, table string, token int64) []ReplicaInfo { tl, ok := c.FindTabletForToken(keyspace, table, token) if !ok { return nil } return tl.Replicas() } // FindReplicasUnsafeForToken returns the replica set for the given token without copying. func (c *CowTabletList) FindReplicasUnsafeForToken(keyspace, table string, token int64) []ReplicaInfo { tl, ok := c.FindTabletForToken(keyspace, table, token) if !ok { return nil } return tl.ReplicasUnsafe() } // FindTabletForToken locates the tablet covering the given token. Returns false if not found. func (c *CowTabletList) FindTabletForToken(keyspace, table string, token int64) (TabletEntry, bool) { if c == nil { return TabletEntry{}, false } tt := c.getTable(tableKey{keyspace, table}) if tt == nil { return TabletEntry{}, false } entries := *tt.list.Load() if len(entries) == 0 { return TabletEntry{}, false } return entries.findEntryForToken(token, 0, len(entries)) } // --- Public write methods --- // sendOp sends an operation to the writer goroutine. func (c *CowTabletList) sendOp(op tabletOp) { if c == nil { return } c.queue.send(op) } // AddTablet queues a single tablet addition. func (c *CowTabletList) AddTablet(tablet TabletInfo) { c.sendOp(opAddTablet{tablet: tablet}) } // BulkAddTablets queues a batch tablet addition. func (c *CowTabletList) BulkAddTablets(tablets TabletInfoList) { c.sendOp(opBulkAddTablets{tablets: tablets}) } // RemoveTabletsWithHost queues removal of all tablets with replicas on the specified host. func (c *CowTabletList) RemoveTabletsWithHost(hostID HostUUID) { c.sendOp(opRemoveHost{hostID: hostID}) } // RemoveTabletsWithKeyspace queues removal of all tablets for the given keyspace. func (c *CowTabletList) RemoveTabletsWithKeyspace(keyspace string) { c.sendOp(opRemoveKeyspace{keyspace: keyspace}) } // RemoveTabletsWithTable queues removal of all tablets for the specified table. func (c *CowTabletList) RemoveTabletsWithTable(keyspace string, table string) { c.sendOp(opRemoveTable{keyspace: keyspace, table: table}) } ================================================ FILE: tablets/tablets_bench_test.go ================================================ //go:build unit // +build unit package tablets import ( "fmt" "runtime" "sync/atomic" "testing" ) const tabletsCountMedium = 1500 // BenchmarkFindReplicasUnsafeForToken measures the pure lookup+replica-return // path for a prepopulated CowTabletList. func BenchmarkFindReplicasUnsafeForToken(b *testing.B) { for _, numTablets := range []int{1500, 10000} { b.Run(fmt.Sprintf("Tablets%d", numTablets), func(b *testing.B) { const rf = 3 const hostsCount = 6 hosts := GenerateHostUUIDs(hostsCount) tl := NewCowTabletList() defer tl.Close() tl.BulkAddTablets(createTablets("ks", "tbl", hosts, rf, numTablets, int64(numTablets))) tl.Flush() runtime.GC() b.ResetTimer() b.ReportAllocs() rnd := getThreadSafeRnd() b.RunParallel(func(pb *testing.PB) { for pb.Next() { token := rnd.Int63() replicas := tl.FindReplicasUnsafeForToken("ks", "tbl", token) if len(replicas) != rf { // Token may fall in a gap; that's fine for benchmarking. _ = replicas } } }) }) } } type opConfig struct { opRemoveKeyspace int64 opRemoveTable int64 opRemoveHost int64 } func BenchmarkCowTabletList(b *testing.B) { const ( rf = 3 ) b.Run("Parallel-10", func(b *testing.B) { runCowTabletListTestSuit(b, "ManyTables", 6, 10, rf, 1500, 5) runCowTabletListTestSuit(b, "SingleTable", 6, 10, rf, 1500, 0) }) b.Run("SingleThread", func(b *testing.B) { runCowTabletListTestSuit(b, "ManyTables", 6, 1, rf, 1500, 5) runCowTabletListTestSuit(b, "SingleTable", 6, 1, rf, 1500, 0) }) } func runCowTabletListTestSuit(b *testing.B, name string, hostsCount, parallelism, rf, totalTablets, extraTables int) { b.Run(name, func(b *testing.B) { b.Run("New", func(b *testing.B) { runSingleCowTabletListTest(b, hostsCount, parallelism, rf, totalTablets, extraTables, false, opConfig{ opRemoveKeyspace: -1, opRemoveHost: -1, opRemoveTable: -1, }) }) b.Run("Prepopulated", func(b *testing.B) { runSingleCowTabletListTest(b, hostsCount, parallelism, rf, totalTablets, extraTables, true, opConfig{ opRemoveKeyspace: -1, opRemoveHost: -1, opRemoveTable: -1, }) }) b.Run("RemoveHost", func(b *testing.B) { runSingleCowTabletListTest(b, hostsCount, parallelism, rf, totalTablets, extraTables, true, opConfig{ opRemoveKeyspace: -1, opRemoveTable: -1, opRemoveHost: 1000, // Every 1000 query is remove host, to measure congestion }) }) b.Run("RemoveTable", func(b *testing.B) { runSingleCowTabletListTest(b, hostsCount, parallelism, rf, totalTablets, extraTables, true, opConfig{ opRemoveKeyspace: -1, opRemoveHost: -1, opRemoveTable: 1000, // Every 1000 query is remove table, to measure congestion }) }) b.Run("RemoveKeyspace", func(b *testing.B) { runSingleCowTabletListTest(b, hostsCount, parallelism, rf, totalTablets, extraTables, true, opConfig{ opRemoveHost: -1, opRemoveTable: -1, opRemoveKeyspace: 1000, // Every 1000 query is remove keyspace, to measure congestion }) }) }) } func runSingleCowTabletListTest(b *testing.B, hostsCount, parallelism, rf, totalTablets, extraTables int, prepopulate bool, ratios opConfig) { tokenRangeCount64 := int64(totalTablets) hosts := GenerateHostUUIDs(hostsCount) targetKS := "kstarget" targetTable := "ttarget" removeKs := "ksremove" removeTable := "tremove" repGen := NewReplicaSetGenerator(hosts, rf) readyTablets := createTablets(removeKs, removeTable, hosts, rf, totalTablets, tokenRangeCount64) b.SetParallelism(parallelism) tl := NewCowTabletList() defer tl.Close() rnd := getThreadSafeRnd() opID := atomic.Int64{} if prepopulate { tl.BulkAddTablets(createTablets(targetKS, targetTable, hosts, rf, totalTablets, tokenRangeCount64)) } for i := 0; i < extraTables; i++ { tl.BulkAddTablets(createTablets(targetKS, fmt.Sprintf("table-%d", i), hosts, rf, totalTablets, tokenRangeCount64)) } tl.Flush() runtime.GC() b.ResetTimer() b.RunParallel(func(pb *testing.PB) { for pb.Next() { id := opID.Add(1) token := rnd.Int63() tablet, found := tl.FindTabletForToken(targetKS, targetTable, token) if found || tablet.lastToken < token || tablet.firstToken > token { // If there is no tablet for token, emulate update, same way it is usually happening firstToken := (token / tokenRangeCount64) * tokenRangeCount64 lastToken := firstToken + tokenRangeCount64 tl.AddTablet(TabletInfo{ keyspaceName: targetKS, tableName: targetTable, firstToken: firstToken, lastToken: lastToken, replicas: repGen.Next(), }) } if ratios.opRemoveTable == 0 || ((ratios.opRemoveTable != -1) && id%ratios.opRemoveTable == 0) { tl.BulkAddTablets(readyTablets) tl.RemoveTabletsWithTable(targetKS, removeTable) } if ratios.opRemoveKeyspace == 0 || ((ratios.opRemoveKeyspace != -1) && id%ratios.opRemoveKeyspace == 0) { tl.BulkAddTablets(readyTablets) tl.RemoveTabletsWithKeyspace(removeKs) } if ratios.opRemoveHost == 0 || ((ratios.opRemoveHost != -1) && id%ratios.opRemoveHost == 0) { tl.RemoveTabletsWithHost(hosts[rnd.Intn(len(hosts))]) } } }) } ================================================ FILE: tablets/tablets_test.go ================================================ //go:build unit // +build unit package tablets import ( "math" "testing" ) func TestFindEntryForToken(t *testing.T) { t.Parallel() t.Run("ExactLastToken", func(t *testing.T) { entries := TabletEntryList{ {firstToken: -100, lastToken: 0}, {firstToken: 0, lastToken: 100}, } entry, ok := entries.findEntryForToken(0, 0, len(entries)) if !ok { t.Fatal("expected entry for token at exact lastToken boundary") } if entry.lastToken != 0 { t.Fatalf("expected lastToken=0, got %d", entry.lastToken) } }) t.Run("ExactFirstToken", func(t *testing.T) { entries := TabletEntryList{ {firstToken: -100, lastToken: 0}, {firstToken: 0, lastToken: 100}, } entry, ok := entries.findEntryForToken(-100, 0, len(entries)) if !ok { t.Fatal("expected entry for token at exact firstToken boundary") } if entry.firstToken != -100 { t.Fatalf("expected firstToken=-100, got %d", entry.firstToken) } }) t.Run("BeyondAll", func(t *testing.T) { entries := TabletEntryList{ {firstToken: -100, lastToken: 0}, {firstToken: 0, lastToken: 100}, } _, ok := entries.findEntryForToken(200, 0, len(entries)) if ok { t.Fatal("expected nil for token beyond all tablets") } }) t.Run("BeforeAll", func(t *testing.T) { entries := TabletEntryList{ {firstToken: -100, lastToken: 0}, {firstToken: 0, lastToken: 100}, } _, ok := entries.findEntryForToken(-200, 0, len(entries)) if ok { t.Fatal("expected nil for token before all tablets") } }) t.Run("InGap", func(t *testing.T) { entries := TabletEntryList{ {firstToken: -200, lastToken: -100}, {firstToken: 100, lastToken: 200}, } _, ok := entries.findEntryForToken(0, 0, len(entries)) if ok { t.Fatal("expected nil for token in gap between non-contiguous tablets") } }) t.Run("EmptyList", func(t *testing.T) { entries := TabletEntryList{} _, ok := entries.findEntryForToken(0, 0, 0) if ok { t.Fatal("expected nil for empty entry list") } }) t.Run("SingleEntry", func(t *testing.T) { entries := TabletEntryList{ {firstToken: -50, lastToken: 50}, } _, ok := entries.findEntryForToken(0, 0, len(entries)) if !ok { t.Fatal("expected entry for token inside single entry") } _, ok = entries.findEntryForToken(-50, 0, len(entries)) if !ok { t.Fatal("expected entry for token at firstToken of single entry") } _, ok = entries.findEntryForToken(50, 0, len(entries)) if !ok { t.Fatal("expected entry for token at lastToken of single entry") } _, ok = entries.findEntryForToken(-51, 0, len(entries)) if ok { t.Fatal("expected nil for token before single entry") } _, ok = entries.findEntryForToken(51, 0, len(entries)) if ok { t.Fatal("expected nil for token after single entry") } }) t.Run("InvalidBounds", func(t *testing.T) { entries := TabletEntryList{ {firstToken: 0, lastToken: 100, replicas: []ReplicaInfo{{testHostUUID("host1"), 0}}}, } testCases := []struct { name string l, r int }{ {"negative l", -1, 1}, {"r beyond length", 0, 10}, {"l > r", 1, 0}, {"both invalid", -1, 10}, {"l == r (empty range)", 0, 0}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { result, ok := entries.findEntryForToken(50, tc.l, tc.r) if ok { t.Errorf("expected nil for invalid bounds l=%d r=%d, got %+v", tc.l, tc.r, result) } }) } }) t.Run("SingleTokenTablet", func(t *testing.T) { entries := TabletEntryList{ {firstToken: -100, lastToken: -50}, {firstToken: 42, lastToken: 42}, {firstToken: 100, lastToken: 200}, } entry, ok := entries.findEntryForToken(42, 0, len(entries)) if !ok { t.Fatal("expected entry for single-token tablet") } if entry.firstToken != 42 || entry.lastToken != 42 { t.Fatalf("expected [42,42], got [%d,%d]", entry.firstToken, entry.lastToken) } _, ok = entries.findEntryForToken(41, 0, len(entries)) if ok { t.Fatal("expected nil for token just before single-token tablet") } _, ok = entries.findEntryForToken(43, 0, len(entries)) if ok { t.Fatal("expected nil for token just after single-token tablet") } }) } func TestFindOverlapRange(t *testing.T) { t.Parallel() t.Run("ContiguousBoundary", func(t *testing.T) { entries := TabletEntryList{ {firstToken: 0, lastToken: 100, replicas: []ReplicaInfo{{testHostUUID("host1"), 0}}}, {firstToken: 200, lastToken: 300, replicas: []ReplicaInfo{{testHostUUID("host2"), 1}}}, } start, tailStart := entries.findOverlapRange(100, 200) if start != 1 { t.Errorf("expected start=1 for contiguous boundary, got %d", start) } if tailStart != 2 { t.Errorf("expected tailStart=2 for contiguous boundary, got %d", tailStart) } }) t.Run("ExtremeValues", func(t *testing.T) { entries := TabletEntryList{ {firstToken: math.MinInt64, lastToken: 0, replicas: []ReplicaInfo{{testHostUUID("host1"), 0}}}, {firstToken: 0, lastToken: math.MaxInt64, replicas: []ReplicaInfo{{testHostUUID("host2"), 1}}}, } start, tailStart := entries.findOverlapRange(math.MinInt64, math.MaxInt64) if start != 0 { t.Errorf("expected start=0 for full range overlap, got %d", start) } if tailStart != 2 { t.Errorf("expected tailStart=2 for full range overlap, got %d", tailStart) } start, tailStart = entries.findOverlapRange(math.MinInt64, -100) if start != 0 { t.Errorf("expected start=0 for MinInt64 range, got %d", start) } if tailStart != 1 { t.Errorf("expected tailStart=1 for MinInt64 range, got %d", tailStart) } start, tailStart = entries.findOverlapRange(100, math.MaxInt64) if start != 1 { t.Errorf("expected start=1 for MaxInt64 range, got %d", start) } if tailStart != 2 { t.Errorf("expected tailStart=2 for MaxInt64 range, got %d", tailStart) } }) t.Run("SingleEntry", func(t *testing.T) { entries := TabletEntryList{ {firstToken: -100, lastToken: 100, replicas: []ReplicaInfo{{testHostUUID("host1"), 0}}}, } start, tailStart := entries.findOverlapRange(-50, 50) if start != 0 || tailStart != 1 { t.Errorf("expected start=0 tailStart=1 for overlapping range, got start=%d tailStart=%d", start, tailStart) } start, tailStart = entries.findOverlapRange(-200, 200) if start != 0 || tailStart != 1 { t.Errorf("expected start=0 tailStart=1 for extended range, got start=%d tailStart=%d", start, tailStart) } start, tailStart = entries.findOverlapRange(-200, -150) if start != 0 || tailStart != 0 { t.Errorf("expected start=0 tailStart=0 for range before, got start=%d tailStart=%d", start, tailStart) } start, tailStart = entries.findOverlapRange(150, 200) if start != 1 || tailStart != 1 { t.Errorf("expected start=1 tailStart=1 for range after, got start=%d tailStart=%d", start, tailStart) } start, tailStart = entries.findOverlapRange(-100, -50) if start != 0 || tailStart != 1 { t.Errorf("expected start=0 tailStart=1 for range sharing firstToken, got start=%d tailStart=%d", start, tailStart) } start, tailStart = entries.findOverlapRange(100, 200) if start != 1 || tailStart != 1 { t.Errorf("expected start=1 tailStart=1 for contiguous range at lastToken, got start=%d tailStart=%d", start, tailStart) } }) t.Run("SingleTokenTablet", func(t *testing.T) { entries := TabletEntryList{} start, tailStart := entries.findOverlapRange(42, 42) if start != 0 || tailStart != 0 { t.Errorf("empty list: expected start=0 tailStart=0, got start=%d tailStart=%d", start, tailStart) } entries = TabletEntryList{ {firstToken: 40, lastToken: 50}, } start, tailStart = entries.findOverlapRange(42, 42) if start != 0 || tailStart != 1 { t.Errorf("contained: expected start=0 tailStart=1, got start=%d tailStart=%d", start, tailStart) } entries = TabletEntryList{ {firstToken: 0, lastToken: 42}, } start, tailStart = entries.findOverlapRange(42, 42) if start != 1 || tailStart != 1 { t.Errorf("adjacent: expected start=1 tailStart=1, got start=%d tailStart=%d", start, tailStart) } }) } func TestAddEntry(t *testing.T) { t.Parallel() t.Run("SingleTokenTablet", func(t *testing.T) { tl := TabletEntryList{} tl = tl.addEntry(TabletEntry{firstToken: 42, lastToken: 42}) if len(tl) != 1 || tl[0].firstToken != 42 || tl[0].lastToken != 42 { t.Fatalf("expected single [42,42] entry, got %v", tl) } tl = TabletEntryList{ {firstToken: -100, lastToken: -50}, {firstToken: 100, lastToken: 200}, } tl = tl.addEntry(TabletEntry{firstToken: 42, lastToken: 42}) if len(tl) != 3 { t.Fatalf("expected 3 entries, got %d", len(tl)) } if tl[1].firstToken != 42 || tl[1].lastToken != 42 { t.Fatalf("expected middle entry [42,42], got [%d,%d]", tl[1].firstToken, tl[1].lastToken) } }) } ================================================ FILE: testdata/pki/ca.cnf ================================================ [req] default_bits = 2048 prompt = no default_md = sha256 distinguished_name = dn [req_ext] basicConstraints = CA:TRUE keyUsage = digitalSignature, keyCertSign [dn] CN = ca ================================================ FILE: testdata/pki/cassandra.cnf ================================================ [req] default_bits = 2048 prompt = no default_md = sha256 distinguished_name = dn [dn] CN = cassandra [req_ext] basicConstraints = CA:FALSE keyUsage = digitalSignature, keyEncipherment subjectAltName = @alt_names [alt_names] URI = spiffe://test.cassandra.apache.org/cassandra-gocql-driver/integrationTest/cassandra ================================================ FILE: testdata/pki/generate_certs.sh ================================================ #! /bin/bash # # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This script generates the various certificates used for integration # tests. All certificates are created with a validity of 3650 days, # or 10 years. Therefore, this only needs to be used sparingly, # although could eventually be repurposed to regenerate certificates # as part of setting up the integration test harness. set -eux # How long certificates should be considered valid, 100 years VALIDITY=36500 # Generate 4096-bit unencrypted RSA private key using aes256 function generatePrivateKey() { base=$1 rm -fv ${base}.key || true echo "Generating private key ${base}.key" # Generate Private Key openssl genrsa -aes256 -out ${base}.key -passout pass:cassandra 4096 echo "Decrypting ${base}.key" # Decrypt Private Key openssl rsa -in ${base}.key -out ${base}.key -passin pass:cassandra } # Generate a X509 Certificate signed by the generated CA function generateCASignedCert() { base=$1 rm -fv ${base}.csr ${base}.crt || true # Generate Certificate Signing Request echo "Generating certificate signing request ${base}.csr" openssl req -new -key ${base}.key -out ${base}.csr -config ${base}.cnf # Generate Certificate using CA echo "Generating certificate ${base}.crt" openssl x509 -req -in ${base}.csr -CA ca.crt -CAkey ca.key \ -CAcreateserial -out ${base}.crt -days $VALIDITY \ -extensions req_ext -extfile ${base}.cnf -text rm -fv ${base}.csr || true } # CA # Generate CA that signs both gocql and cassandra certs generatePrivateKey ca # Generate CA Certificate echo "Generating CA certificate ca.crt" rm -fv ca.crt || true openssl req -x509 -new -nodes -key ca.key -days $VALIDITY \ -out ca.crt -config ca.cnf -text # Import CA certificate into JKS truststore so it can be used by Cassandra. echo "Generating truststore .truststore for Cassandra" rm -fv .truststore || true keytool -import -keystore .truststore -trustcacerts \ -file ca.crt -alias ca -storetype JKS \ -storepass cassandra -noprompt # GoCQL # Generate CA-signed certificate for GoCQL client for integration tests generatePrivateKey gocql generateCASignedCert gocql # Cassandra # Generate CA-signed certificate for Cassandra generatePrivateKey cassandra generateCASignedCert cassandra # Import cassandra private key and certificate into a PKCS12 keystore # and to a JKS keystore so it can be used by cassandra. echo "Generating cassandra.p12 and .keystore for Cassandra" rm -fv cassandra.p12 || true openssl pkcs12 -export -in cassandra.crt -inkey cassandra.key \ -out cassandra.p12 -name cassandra \ -CAfile ca.crt -caname ca \ -password pass:cassandra \ -noiter -nomaciter rm -fv .keystore || true keytool -importkeystore -srckeystore cassandra.p12 -srcstoretype PKCS12 \ -srcstorepass cassandra -srcalias cassandra \ -destkeystore .keystore -deststoretype JKS \ -deststorepass cassandra -destalias cassandra ================================================ FILE: testdata/pki/gocql.cnf ================================================ [req] default_bits = 2048 prompt = no default_md = sha256 distinguished_name = dn [dn] CN = gocql [req_ext] basicConstraints = CA:FALSE keyUsage = digitalSignature, keyEncipherment subjectAltName = @alt_names [alt_names] URI = spiffe://test.cassandra.apache.org/cassandra-gocql-driver/integrationTest/gocql ================================================ FILE: testdata/recreate/aggregates.cql ================================================ CREATE KEYSPACE gocqlx_aggregates WITH replication = { 'class': 'NetworkTopologyStrategy', 'replication_factor': '2' }; CREATE FUNCTION gocqlx_aggregates.avgstate( state tuple, val double) CALLED ON NULL INPUT RETURNS frozen> LANGUAGE lua AS $$ return { state[1]+1, state[2]+val } $$; CREATE FUNCTION gocqlx_aggregates.avgfinal( state tuple) CALLED ON NULL INPUT RETURNS double LANGUAGE lua as $$ r=0 r=state[2] r=r/state[1] return r $$; CREATE AGGREGATE gocqlx_aggregates.average(double) SFUNC avgstate STYPE tuple FINALFUNC avgfinal INITCOND (0,0.0); ================================================ FILE: testdata/recreate/aggregates_golden.cql ================================================ CREATE KEYSPACE gocqlx_aggregates WITH replication = {'class': 'org.apache.cassandra.locator.NetworkTopologyStrategy', 'datacenter1': '2'} AND durable_writes = true; CREATE FUNCTION gocqlx_aggregates.avgfinal(state frozen>) CALLED ON NULL INPUT RETURNS double LANGUAGE lua AS $$ r=0 r=state[2] r=r/state[1] return r $$; CREATE FUNCTION gocqlx_aggregates.avgstate(state frozen>, val double) CALLED ON NULL INPUT RETURNS frozen> LANGUAGE lua AS $$ return { state[1]+1, state[2]+val } $$; CREATE AGGREGATE gocqlx_aggregates.average(double) SFUNC avgstate STYPE frozen> FINALFUNC avgfinal INITCOND (0, 0); ================================================ FILE: testdata/recreate/index.cql ================================================ CREATE KEYSPACE gocqlx_idx WITH replication = { 'class': 'NetworkTopologyStrategy', 'replication_factor': '2' }; CREATE TABLE gocqlx_idx.menus ( location text, name text, price float, dish_type text, PRIMARY KEY(location, name) ); CREATE INDEX ON gocqlx_idx.menus(name); ================================================ FILE: testdata/recreate/index_golden.cql ================================================ CREATE KEYSPACE gocqlx_idx WITH replication = {'class': 'org.apache.cassandra.locator.NetworkTopologyStrategy', 'datacenter1': '2'} AND durable_writes = true; CREATE TABLE gocqlx_idx.menus ( location text, name text, dish_type text, price float, PRIMARY KEY (location, name) ) WITH CLUSTERING ORDER BY (name ASC) AND bloom_filter_fp_chance = 0.01 AND caching = {'keys': 'ALL', 'rows_per_partition': 'ALL'} AND comment = '' AND compaction = {'class': 'SizeTieredCompactionStrategy'} AND compression = {'sstable_compression': 'org.apache.cassandra.io.compress.LZ4Compressor'} AND crc_check_chance = 1 AND default_time_to_live = 0 AND gc_grace_seconds = 864000 AND max_index_interval = 2048 AND memtable_flush_period_in_ms = 0 AND min_index_interval = 128 AND speculative_retry = '99.0PERCENTILE' AND paxos_grace_seconds = 864000 AND tombstone_gc = {'mode': 'timeout', 'propagation_delay_in_seconds': '3600'}; CREATE INDEX menus_name_idx ON gocqlx_idx.menus(name); ================================================ FILE: testdata/recreate/keyspace.cql ================================================ CREATE KEYSPACE gocqlx_keyspace WITH replication = { 'class': 'NetworkTopologyStrategy', 'replication_factor': '2' }; ================================================ FILE: testdata/recreate/keyspace_golden.cql ================================================ CREATE KEYSPACE gocqlx_keyspace WITH replication = {'class': 'org.apache.cassandra.locator.NetworkTopologyStrategy', 'datacenter1': '2'} AND durable_writes = true; ================================================ FILE: testdata/recreate/materialized_views.cql ================================================ CREATE KEYSPACE gocqlx_mv WITH replication = { 'class': 'NetworkTopologyStrategy', 'replication_factor': '2' }; CREATE TABLE gocqlx_mv.mv_buildings ( name text, city text, built int, meters int, PRIMARY KEY (name) ); CREATE MATERIALIZED VIEW gocqlx_mv.mv_building_by_city AS SELECT * FROM mv_buildings WHERE city IS NOT NULL PRIMARY KEY(city, name); CREATE MATERIALIZED VIEW gocqlx_mv.mv_building_by_city2 AS SELECT meters FROM mv_buildings WHERE city IS NOT NULL PRIMARY KEY(city, name); ================================================ FILE: testdata/recreate/materialized_views_golden.cql ================================================ CREATE KEYSPACE gocqlx_mv WITH replication = {'class': 'org.apache.cassandra.locator.NetworkTopologyStrategy', 'datacenter1': '2'} AND durable_writes = true; CREATE TABLE gocqlx_mv.mv_buildings ( name text, built int, city text, meters int, PRIMARY KEY (name) ) WITH bloom_filter_fp_chance = 0.01 AND caching = {'keys': 'ALL', 'rows_per_partition': 'ALL'} AND comment = '' AND compaction = {'class': 'SizeTieredCompactionStrategy'} AND compression = {'sstable_compression': 'org.apache.cassandra.io.compress.LZ4Compressor'} AND crc_check_chance = 1 AND default_time_to_live = 0 AND gc_grace_seconds = 864000 AND max_index_interval = 2048 AND memtable_flush_period_in_ms = 0 AND min_index_interval = 128 AND speculative_retry = '99.0PERCENTILE' AND paxos_grace_seconds = 864000 AND tombstone_gc = {'mode': 'timeout', 'propagation_delay_in_seconds': '3600'}; CREATE MATERIALIZED VIEW gocqlx_mv.mv_building_by_city AS SELECT city, name, built, meters FROM gocqlx_mv.mv_buildings WHERE city IS NOT null PRIMARY KEY (city, name) WITH CLUSTERING ORDER BY (name ASC) AND bloom_filter_fp_chance = 0.01 AND caching = {'keys': 'ALL', 'rows_per_partition': 'ALL'} AND comment = '' AND compaction = {'class': 'SizeTieredCompactionStrategy'} AND compression = {'sstable_compression': 'org.apache.cassandra.io.compress.LZ4Compressor'} AND crc_check_chance = 1 AND default_time_to_live = 0 AND gc_grace_seconds = 864000 AND max_index_interval = 2048 AND memtable_flush_period_in_ms = 0 AND min_index_interval = 128 AND speculative_retry = '99.0PERCENTILE' AND paxos_grace_seconds = 864000 AND tombstone_gc = {'mode': 'timeout', 'propagation_delay_in_seconds': '3600'}; CREATE MATERIALIZED VIEW gocqlx_mv.mv_building_by_city2 AS SELECT city, name, meters FROM gocqlx_mv.mv_buildings WHERE city IS NOT null PRIMARY KEY (city, name) WITH CLUSTERING ORDER BY (name ASC) AND bloom_filter_fp_chance = 0.01 AND caching = {'keys': 'ALL', 'rows_per_partition': 'ALL'} AND comment = '' AND compaction = {'class': 'SizeTieredCompactionStrategy'} AND compression = {'sstable_compression': 'org.apache.cassandra.io.compress.LZ4Compressor'} AND crc_check_chance = 1 AND default_time_to_live = 0 AND gc_grace_seconds = 864000 AND max_index_interval = 2048 AND memtable_flush_period_in_ms = 0 AND min_index_interval = 128 AND speculative_retry = '99.0PERCENTILE' AND paxos_grace_seconds = 864000 AND tombstone_gc = {'mode': 'timeout', 'propagation_delay_in_seconds': '3600'}; ================================================ FILE: testdata/recreate/scylla_encryption_options_golden.json ================================================ {"cipher_algorithm":"AES/ECB/PKCS5Padding","secret_key_strength":128,"key_provider":"LocalFileSystemKeyProviderFactory","secret_key_file":"/etc/scylla/encryption_keys/data_encryption_keys"} ================================================ FILE: testdata/recreate/secondary_index.cql ================================================ CREATE KEYSPACE gocqlx_sec_idx WITH replication = { 'class': 'NetworkTopologyStrategy', 'replication_factor': '2' }; CREATE TABLE gocqlx_sec_idx.menus ( location text, name text, price float, dish_type text, PRIMARY KEY(location, name) ); CREATE INDEX ON gocqlx_sec_idx.menus((location), name); ================================================ FILE: testdata/recreate/secondary_index_golden.cql ================================================ CREATE KEYSPACE gocqlx_sec_idx WITH replication = {'class': 'org.apache.cassandra.locator.NetworkTopologyStrategy', 'datacenter1': '2'} AND durable_writes = true; CREATE TABLE gocqlx_sec_idx.menus ( location text, name text, dish_type text, price float, PRIMARY KEY (location, name) ) WITH CLUSTERING ORDER BY (name ASC) AND bloom_filter_fp_chance = 0.01 AND caching = {'keys': 'ALL', 'rows_per_partition': 'ALL'} AND comment = '' AND compaction = {'class': 'SizeTieredCompactionStrategy'} AND compression = {'sstable_compression': 'org.apache.cassandra.io.compress.LZ4Compressor'} AND crc_check_chance = 1 AND default_time_to_live = 0 AND gc_grace_seconds = 864000 AND max_index_interval = 2048 AND memtable_flush_period_in_ms = 0 AND min_index_interval = 128 AND speculative_retry = '99.0PERCENTILE' AND paxos_grace_seconds = 864000 AND tombstone_gc = {'mode': 'timeout', 'propagation_delay_in_seconds': '3600'}; CREATE INDEX menus_name_idx ON gocqlx_sec_idx.menus((location), name); ================================================ FILE: testdata/recreate/table.cql ================================================ CREATE KEYSPACE gocqlx_table WITH replication = { 'class': 'NetworkTopologyStrategy', 'replication_factor': '2' }; CREATE TABLE gocqlx_table.monkeySpecies ( species text PRIMARY KEY, common_name text, population varint, average_size int ) WITH comment='Important biological records'; CREATE TABLE gocqlx_table.timeline ( userid uuid, posted_month int, posted_time uuid, body text, posted_by text, PRIMARY KEY (userid, posted_month, posted_time) ) WITH compaction = { 'class' : 'LeveledCompactionStrategy' }; CREATE TABLE gocqlx_table.loads ( machine inet, cpu int, mtime timeuuid, load float, PRIMARY KEY ((machine, cpu), mtime) ) WITH CLUSTERING ORDER BY (mtime DESC) AND caching = {'keys':'ALL', 'rows_per_partition':'NONE'} AND compaction = {'compaction_window_size': '14', 'compaction_window_unit': 'DAYS', 'class': 'org.apache.cassandra.db.compaction.TimeWindowCompactionStrategy'}; CREATE TABLE gocqlx_table.users_picture ( userid uuid, pictureid uuid, body text static, posted_by text, PRIMARY KEY (userid, pictureid, posted_by) ) WITH compression = {'sstable_compression': 'LZ4Compressor'}; ================================================ FILE: testdata/recreate/table_golden.cql ================================================ CREATE KEYSPACE gocqlx_table WITH replication = {'class': 'org.apache.cassandra.locator.NetworkTopologyStrategy', 'datacenter1': '2'} AND durable_writes = true; CREATE TABLE gocqlx_table.loads ( machine inet, cpu int, mtime timeuuid, load float, PRIMARY KEY ((machine, cpu), mtime) ) WITH CLUSTERING ORDER BY (mtime DESC) AND bloom_filter_fp_chance = 0.01 AND caching = {'keys': 'ALL', 'rows_per_partition': 'NONE'} AND comment = '' AND compaction = {'class': 'TimeWindowCompactionStrategy', 'compaction_window_size': '14', 'compaction_window_unit': 'DAYS'} AND compression = {'sstable_compression': 'org.apache.cassandra.io.compress.LZ4Compressor'} AND crc_check_chance = 1 AND default_time_to_live = 0 AND gc_grace_seconds = 864000 AND max_index_interval = 2048 AND memtable_flush_period_in_ms = 0 AND min_index_interval = 128 AND speculative_retry = '99.0PERCENTILE' AND paxos_grace_seconds = 864000 AND tombstone_gc = {'mode': 'timeout', 'propagation_delay_in_seconds': '3600'}; CREATE TABLE gocqlx_table.monkeyspecies ( species text, average_size int, common_name text, population varint, PRIMARY KEY (species) ) WITH bloom_filter_fp_chance = 0.01 AND caching = {'keys': 'ALL', 'rows_per_partition': 'ALL'} AND comment = 'Important biological records' AND compaction = {'class': 'SizeTieredCompactionStrategy'} AND compression = {'sstable_compression': 'org.apache.cassandra.io.compress.LZ4Compressor'} AND crc_check_chance = 1 AND default_time_to_live = 0 AND gc_grace_seconds = 864000 AND max_index_interval = 2048 AND memtable_flush_period_in_ms = 0 AND min_index_interval = 128 AND speculative_retry = '99.0PERCENTILE' AND paxos_grace_seconds = 864000 AND tombstone_gc = {'mode': 'timeout', 'propagation_delay_in_seconds': '3600'}; CREATE TABLE gocqlx_table.timeline ( userid uuid, posted_month int, posted_time uuid, body text, posted_by text, PRIMARY KEY (userid, posted_month, posted_time) ) WITH CLUSTERING ORDER BY (posted_month ASC, posted_time ASC) AND bloom_filter_fp_chance = 0.01 AND caching = {'keys': 'ALL', 'rows_per_partition': 'ALL'} AND comment = '' AND compaction = {'class': 'LeveledCompactionStrategy'} AND compression = {'sstable_compression': 'org.apache.cassandra.io.compress.LZ4Compressor'} AND crc_check_chance = 1 AND default_time_to_live = 0 AND gc_grace_seconds = 864000 AND max_index_interval = 2048 AND memtable_flush_period_in_ms = 0 AND min_index_interval = 128 AND speculative_retry = '99.0PERCENTILE' AND paxos_grace_seconds = 864000 AND tombstone_gc = {'mode': 'timeout', 'propagation_delay_in_seconds': '3600'}; CREATE TABLE gocqlx_table.users_picture ( userid uuid, pictureid uuid, posted_by text, body text STATIC, PRIMARY KEY (userid, pictureid, posted_by) ) WITH CLUSTERING ORDER BY (pictureid ASC, posted_by ASC) AND bloom_filter_fp_chance = 0.01 AND caching = {'keys': 'ALL', 'rows_per_partition': 'ALL'} AND comment = '' AND compaction = {'class': 'SizeTieredCompactionStrategy'} AND compression = {'sstable_compression': 'org.apache.cassandra.io.compress.LZ4Compressor'} AND crc_check_chance = 1 AND default_time_to_live = 0 AND gc_grace_seconds = 864000 AND max_index_interval = 2048 AND memtable_flush_period_in_ms = 0 AND min_index_interval = 128 AND speculative_retry = '99.0PERCENTILE' AND paxos_grace_seconds = 864000 AND tombstone_gc = {'mode': 'timeout', 'propagation_delay_in_seconds': '3600'}; ================================================ FILE: testdata/recreate/udt.cql ================================================ CREATE KEYSPACE gocqlx_udt WITH replication = { 'class': 'NetworkTopologyStrategy', 'replication_factor': '2' }; CREATE TYPE gocqlx_udt.phone ( country_code int, number text ); CREATE TYPE gocqlx_udt.address ( street text, city text, zip text, phones map> ); ================================================ FILE: testdata/recreate/udt_golden.cql ================================================ CREATE KEYSPACE gocqlx_udt WITH replication = {'class': 'org.apache.cassandra.locator.NetworkTopologyStrategy', 'datacenter1': '2'} AND durable_writes = true; CREATE TYPE gocqlx_udt.phone ( country_code int, number text ); CREATE TYPE gocqlx_udt.address ( street text, city text, zip text, phones map> ); ================================================ FILE: tests/bench/bench_marshal_test.go ================================================ package bench_test import ( "encoding/json" "io" "math/rand" "testing" "time" "github.com/brianvoe/gofakeit/v6" "github.com/gocql/gocql" ) func generateRandomBinaryData(size int) []byte { rnd := rand.New(rand.NewSource(100)) randomBuffer := make([]byte, size) io.ReadAtLeast(rnd, randomBuffer, size) return randomBuffer } type RandomData struct { ID string `json:"id"` FirstName string `json:"first_name"` LastName string `json:"last_name"` Email string `json:"email"` City string `json:"city"` State string `json:"state"` Zip string `json:"zip"` Phone string `json:"phone"` } func generateRandomJSON(size int) string { gofakeit.Seed(100) var jsonData []byte var randomData []RandomData currentLength := 0 for currentLength < size { data := RandomData{ ID: gofakeit.UUID(), FirstName: gofakeit.FirstName(), LastName: gofakeit.LastName(), Email: gofakeit.Email(), City: gofakeit.City(), State: gofakeit.State(), Zip: gofakeit.Zip(), Phone: gofakeit.Phone(), } randomData = append(randomData, data) tempData, _ := json.Marshal(randomData) currentLength = len(tempData) jsonData = tempData } return string(jsonData) } func BenchmarkSerialization(b *testing.B) { b.Run("SimpleTypes", func(b *testing.B) { b.Run("Int", func(b *testing.B) { tType := gocql.NewNativeType(4, gocql.TypeInt) var val int = 42 b.Run("Marshal", func(b *testing.B) { for i := 0; i < b.N; i++ { _, err := gocql.Marshal(tType, val) if err != nil { b.Fatal(err) } } }) marshaled, err := gocql.Marshal(tType, val) if err != nil { b.Fatal(err) } b.Run("Unmarshal", func(b *testing.B) { for i := 0; i < b.N; i++ { var unmarshaled int err = gocql.Unmarshal(tType, marshaled, &unmarshaled) if err != nil { b.Fatal(err) } } }) }) cases := []struct { name string size int }{ {"Small-100b", 100}, {"Medium-1kb", 1024}, {"Big-1M", 1024 * 1024}, } for _, c := range cases { b.Run("Blob"+c.name, func(b *testing.B) { tType := gocql.NewNativeType(4, gocql.TypeBlob) val := generateRandomBinaryData(c.size) b.Run("Marshal", func(b *testing.B) { for i := 0; i < b.N; i++ { _, err := gocql.Marshal(tType, val) if err != nil { b.Fatal(err) } } }) marshaled, err := gocql.Marshal(tType, val) if err != nil { b.Fatal(err) } b.Run("Unmarshal", func(b *testing.B) { for i := 0; i < b.N; i++ { var unmarshaled []byte err = gocql.Unmarshal(tType, marshaled, &unmarshaled) if err != nil { b.Fatal(err) } } }) }) } for _, c := range cases { b.Run("Text"+c.name, func(b *testing.B) { tType := gocql.NewNativeType(4, gocql.TypeText) val := generateRandomJSON(c.size) b.Run("Marshal", func(b *testing.B) { for i := 0; i < b.N; i++ { _, err := gocql.Marshal(tType, val) if err != nil { b.Fatal(err) } } }) marshaled, err := gocql.Marshal(tType, val) if err != nil { b.Fatal(err) } b.Run("Unmarshal", func(b *testing.B) { for i := 0; i < b.N; i++ { var unmarshaled string err = gocql.Unmarshal(tType, marshaled, &unmarshaled) if err != nil { b.Fatal(err) } } }) }) } b.Run("UUID", func(b *testing.B) { tType := gocql.NewNativeType(4, gocql.TypeUUID) val := gocql.UUID{} b.Run("Marshal", func(b *testing.B) { for i := 0; i < b.N; i++ { _, err := gocql.Marshal(tType, val) if err != nil { b.Fatal(err) } } }) marshaled, err := gocql.Marshal(tType, val) if err != nil { b.Fatal(err) } b.Run("Unmarshal", func(b *testing.B) { for i := 0; i < b.N; i++ { var unmarshaled gocql.UUID err = gocql.Unmarshal(tType, marshaled, &unmarshaled) if err != nil { b.Fatal(err) } } }) }) b.Run("Duration", func(b *testing.B) { tType := gocql.NewNativeType(4, gocql.TypeDuration) val := gocql.Duration{Nanoseconds: 300000000000} b.Run("Marshal", func(b *testing.B) { for i := 0; i < b.N; i++ { _, err := gocql.Marshal(tType, val) if err != nil { b.Fatal(err) } } }) marshaled, err := gocql.Marshal(tType, val) if err != nil { b.Fatal(err) } b.Run("Unmarshal", func(b *testing.B) { for i := 0; i < b.N; i++ { var unmarshaled gocql.Duration err = gocql.Unmarshal(tType, marshaled, &unmarshaled) if err != nil { b.Fatal(err) } } }) }) b.Run("Timestamp", func(b *testing.B) { tType := gocql.NewNativeType(4, gocql.TypeTimestamp) val := time.Now() b.Run("Marshal", func(b *testing.B) { for i := 0; i < b.N; i++ { _, err := gocql.Marshal(tType, val) if err != nil { b.Fatal(err) } } }) marshaled, err := gocql.Marshal(tType, val) if err != nil { b.Fatal(err) } b.Run("Unmarshal", func(b *testing.B) { for i := 0; i < b.N; i++ { var unmarshaled time.Time err = gocql.Unmarshal(tType, marshaled, &unmarshaled) if err != nil { b.Fatal(err) } } }) }) }) b.Run("ComplexTypes", func(b *testing.B) { b.Run("List", func(b *testing.B) { tType := gocql.CollectionType{ NativeType: gocql.NewNativeType(4, gocql.TypeList), Elem: gocql.NewNativeType(4, gocql.TypeText), } val := []string{"foo", "bar", "baz"} b.Run("Marshal", func(b *testing.B) { for i := 0; i < b.N; i++ { _, err := gocql.Marshal(tType, val) if err != nil { b.Fatal(err) } } }) marshaled, err := gocql.Marshal(tType, val) if err != nil { b.Fatal(err) } b.Run("Unmarshal", func(b *testing.B) { for i := 0; i < b.N; i++ { var unmarshaled []string err = gocql.Unmarshal(tType, marshaled, &unmarshaled) if err != nil { b.Fatal(err) } } }) }) b.Run("Map", func(b *testing.B) { tType := gocql.CollectionType{ NativeType: gocql.NewNativeType(4, gocql.TypeMap), Key: gocql.NewNativeType(4, gocql.TypeVarchar), Elem: gocql.NewNativeType(4, gocql.TypeInt), } val := map[string]int{"a": 1, "b": 2} b.Run("Marshal", func(b *testing.B) { for i := 0; i < b.N; i++ { _, err := gocql.Marshal(tType, val) if err != nil { b.Fatal(err) } } }) marshaled, err := gocql.Marshal(tType, val) if err != nil { b.Fatal(err) } b.Run("Unmarshal", func(b *testing.B) { for i := 0; i < b.N; i++ { var unmarshaled map[string]int err = gocql.Unmarshal(tType, marshaled, &unmarshaled) if err != nil { b.Fatal(err) } } }) }) b.Run("Set", func(b *testing.B) { tType := gocql.CollectionType{ NativeType: gocql.NewNativeType(4, gocql.TypeSet), Elem: gocql.NewNativeType(4, gocql.TypeInt), } val := map[int]struct{}{1: {}, 2: {}} b.Run("Marshal", func(b *testing.B) { for i := 0; i < b.N; i++ { _, err := gocql.Marshal(tType, val) if err != nil { b.Fatal(err) } } }) marshaled, err := gocql.Marshal(tType, val) if err != nil { b.Fatal(err) } b.Run("Unmarshal", func(b *testing.B) { for i := 0; i < b.N; i++ { var unmarshaled []int err = gocql.Unmarshal(tType, marshaled, &unmarshaled) if err != nil { b.Fatal(err) } } }) }) b.Run("UDT", func(b *testing.B) { type MyUDT struct { ID gocql.UUID Name string Value int } val := MyUDT{ ID: gocql.UUID{}, Name: "test udt", Value: 123, } tType := gocql.UDTTypeInfo{ NativeType: gocql.NewNativeType(4, gocql.TypeUDT), Name: "myudt", KeySpace: "myks", Elements: []gocql.UDTField{ { Name: "id", Type: gocql.NewNativeType(4, gocql.TypeUUID), }, { Name: "name", Type: gocql.NewNativeType(4, gocql.TypeText), }, { Name: "value", Type: gocql.NewNativeType(4, gocql.TypeInt), }, }, } b.Run("Marshal", func(b *testing.B) { for i := 0; i < b.N; i++ { _, err := gocql.Marshal(tType, val) if err != nil { b.Fatal(err) } } }) marshaled, err := gocql.Marshal(tType, val) if err != nil { b.Fatal(err) } b.Run("Unmarshal", func(b *testing.B) { for i := 0; i < b.N; i++ { var unmarshaled MyUDT err = gocql.Unmarshal(tType, marshaled, &unmarshaled) if err != nil { b.Fatal(err) } } }) }) b.Run("Tuple", func(b *testing.B) { val := struct { Field1 int Field2 string }{ Field1: 1, Field2: "test tuple", } tType := gocql.TupleTypeInfo{ NativeType: gocql.NewNativeType(4, gocql.TypeTuple), Elems: []gocql.TypeInfo{ gocql.NewNativeType(4, gocql.TypeInt), gocql.NewNativeType(4, gocql.TypeText), }, } b.Run("Marshal", func(b *testing.B) { for i := 0; i < b.N; i++ { _, err := gocql.Marshal(tType, val) if err != nil { b.Fatal(err) } } }) marshaled, err := gocql.Marshal(tType, val) if err != nil { b.Fatal(err) } b.Run("Unmarshal", func(b *testing.B) { for i := 0; i < b.N; i++ { var unmarshaled struct { Field1 int Field2 string } err = gocql.Unmarshal(tType, marshaled, &unmarshaled) if err != nil { b.Fatal(err) } } }) }) }) b.Run("NestedTypes", func(b *testing.B) { b.Run("3-lvl", func(b *testing.B) { type MyUDT struct { ID gocql.UUID Name string Value int } val := []map[string]MyUDT{ { "key1": {ID: gocql.UUID{}, Name: "name1", Value: 123}, "key2": {ID: gocql.UUID{}, Name: "name2", Value: 456}, }, { "key3": {ID: gocql.UUID{}, Name: "name3", Value: 789}, }, } tType := gocql.CollectionType{ NativeType: gocql.NewNativeType(4, gocql.TypeList), Elem: gocql.CollectionType{ NativeType: gocql.NewNativeType(4, gocql.TypeMap), Key: gocql.NewNativeType(4, gocql.TypeText), Elem: gocql.UDTTypeInfo{ NativeType: gocql.NewNativeType(4, gocql.TypeUDT), Name: "myudt", KeySpace: "myks", Elements: []gocql.UDTField{ { Name: "id", Type: gocql.NewNativeType(4, gocql.TypeUUID), }, { Name: "name", Type: gocql.NewNativeType(4, gocql.TypeText), }, { Name: "value", Type: gocql.NewNativeType(4, gocql.TypeInt), }, }, }, }, } b.Run("Marshal", func(b *testing.B) { for i := 0; i < b.N; i++ { _, err := gocql.Marshal(tType, val) if err != nil { b.Fatal(err) } } }) marshaled, err := gocql.Marshal(tType, val) if err != nil { b.Fatal(err) } b.Run("Unmarshal", func(b *testing.B) { for i := 0; i < b.N; i++ { var unmarshaled []map[string]MyUDT err = gocql.Unmarshal(tType, marshaled, &unmarshaled) if err != nil { b.Fatal(err) } } }) }) }) } ================================================ FILE: tests/bench/bench_single_conn_test.go ================================================ package bench_test import ( "flag" "fmt" "os" "strconv" "testing" "github.com/gocql/gocql" "github.com/gocql/gocql/dialer/recorder" "github.com/gocql/gocql/dialer/replayer" ) func InitializeCluster() error { cluster := gocql.NewCluster("192.168.100.11") cluster.Consistency = gocql.Quorum fallback := gocql.RoundRobinHostPolicy() cluster.PoolConfig.HostSelectionPolicy = gocql.TokenAwareHostPolicy(fallback) executor, err := gocql.NewSingleHostQueryExecutor(cluster) if err != nil { return fmt.Errorf("failed to create executor: %v", err) } defer executor.Close() keyspace := "single_conn_bench" err = executor.Exec(`DROP KEYSPACE IF EXISTS ` + keyspace) if err != nil { return fmt.Errorf("unable to drop keyspace: %v", err) } err = executor.Exec(fmt.Sprintf(`CREATE KEYSPACE %s WITH replication = {'class' : 'NetworkTopologyStrategy','replication_factor' : 1}`, keyspace)) if err != nil { return fmt.Errorf("unable to create keyspace: %v", err) } if err = executor.Exec(fmt.Sprintf(`CREATE TABLE %s.%s (pk int, ck int, v text, PRIMARY KEY (pk)); `, keyspace, "table1")); err != nil { return fmt.Errorf("unable to create table: %v", err) } return nil } func RecordSelectTraffic(size int, dir string) error { cluster := gocql.NewCluster("192.168.100.11") cluster.Consistency = gocql.Quorum cluster.Dialer = recorder.NewRecordDialer(dir) fallback := gocql.RoundRobinHostPolicy() cluster.PoolConfig.HostSelectionPolicy = gocql.TokenAwareHostPolicy(fallback) executor, err := gocql.NewSingleHostQueryExecutor(cluster) if err != nil { return fmt.Errorf("failed to create executor: %v", err) } defer executor.Close() for i := 0; i < size; i++ { iter := executor.Iter(`SELECT v FROM single_conn_bench.table1 WHERE pk = ?;`, i) var name string for iter.Scan(&name) { if name[:4] != "Name" { return fmt.Errorf("got wrong value for name: %s", name) } } if err := iter.Close(); err != nil { return fmt.Errorf("failed to close iterator: %v", err) } } return nil } func RecordInsertTraffic(size int, dir string) error { cluster := gocql.NewCluster("192.168.100.11") cluster.Consistency = gocql.Quorum cluster.Dialer = recorder.NewRecordDialer(dir) fallback := gocql.RoundRobinHostPolicy() cluster.PoolConfig.HostSelectionPolicy = gocql.TokenAwareHostPolicy(fallback) executor, err := gocql.NewSingleHostQueryExecutor(cluster) if err != nil { return fmt.Errorf("failed to create executor: %v", err) } defer executor.Close() for i := 0; i < size; i++ { err = executor.Exec(`INSERT INTO single_conn_bench.table1 (pk, ck, v) VALUES (?, ?, ?);`, i, i%5, fmt.Sprintf("Name_%d", i)) if err != nil { return fmt.Errorf("failed to insert: %v", err) } } return nil } func BenchmarkSingleConnectionSelect(b *testing.B) { cluster := gocql.NewCluster("192.168.100.11") cluster.Consistency = gocql.Quorum cluster.Dialer = replayer.NewReplayDialer("rec_select") fallback := gocql.RoundRobinHostPolicy() cluster.PoolConfig.HostSelectionPolicy = gocql.TokenAwareHostPolicy(fallback) executor, err := gocql.NewSingleHostQueryExecutor(cluster) if err != nil { b.Fatalf("failed to create executor: %v", err) } defer executor.Close() b.Run("Select", func(b *testing.B) { for i := 0; i < 10; i++ { b.Run("Case"+strconv.Itoa(i), func(b *testing.B) { for j := 0; j < b.N; j++ { _ = executor.Iter(`SELECT v FROM single_conn_bench.table1 WHERE pk = ?;`, i) } }) } }) } func BenchmarkSingleConnectionInsert(b *testing.B) { cluster := gocql.NewCluster("192.168.100.11") cluster.Consistency = gocql.Quorum cluster.Dialer = replayer.NewReplayDialer("rec_insert") fallback := gocql.RoundRobinHostPolicy() cluster.PoolConfig.HostSelectionPolicy = gocql.TokenAwareHostPolicy(fallback) executor, err := gocql.NewSingleHostQueryExecutor(cluster) if err != nil { b.Fatalf("failed to create executor: %v", err) } defer executor.Close() b.Run("Insert", func(b *testing.B) { for i := 0; i < 10; i++ { b.Run("Case"+strconv.Itoa(i), func(b *testing.B) { for j := 0; j < b.N; j++ { err = executor.Exec(`INSERT INTO single_conn_bench.table1 (pk, ck, v) VALUES (?, ?, ?);`, i, i%5, fmt.Sprintf("Name_%d", i)) if err != nil { b.Fatalf("failed to insert: %v", err) } } }) } }) } func TestMain(m *testing.M) { update := flag.Bool("update-golden", false, "Update golden files") flag.Parse() if *update { err := InitializeCluster() if err != nil { fmt.Printf("failed to initialize cluster: %v\n", err) os.Exit(1) } err = RecordInsertTraffic(10, "rec_insert") if err != nil { fmt.Printf("failed to record insert traffic: %v\n", err) os.Exit(1) } err = RecordSelectTraffic(10, "rec_select") if err != nil { fmt.Printf("failed to record select traffic: %v\n", err) os.Exit(1) } } os.Exit(m.Run()) } ================================================ FILE: tests/bench/bench_vector_public_test.go ================================================ package bench_test import ( "encoding/binary" "math" "strconv" "testing" "github.com/gocql/gocql" ) const vectorProto = 4 const apacheCassandraTypePrefix = "org.apache.cassandra.db.marshal." const vectorTypePrefix = apacheCassandraTypePrefix + "VectorType(" + apacheCassandraTypePrefix + "FloatType, " const vectorTypeSuffix = ")" func makeFloatVectorType(dim int) gocql.VectorType { dimStr := strconv.Itoa(dim) return gocql.VectorType{ NativeType: gocql.NewCustomType( vectorProto, gocql.TypeCustom, vectorTypePrefix+dimStr+vectorTypeSuffix, ), SubType: gocql.NewNativeType(vectorProto, gocql.TypeFloat), Dimensions: dim, } } func BenchmarkVectorMarshalFloat32Public(b *testing.B) { dims := []int{128, 384, 768, 1536} for _, dim := range dims { dimStr := strconv.Itoa(dim) b.Run("dim_"+dimStr, func(b *testing.B) { b.ReportAllocs() vec := make([]float32, dim) for i := range vec { vec[i] = float32(i) * 0.1 } info := makeFloatVectorType(dim) b.SetBytes(int64(dim * 4)) b.ResetTimer() for i := 0; i < b.N; i++ { if _, err := gocql.Marshal(info, vec); err != nil { b.Fatal(err) } } }) } } func BenchmarkVectorUnmarshalFloat32Public(b *testing.B) { dims := []int{128, 384, 768, 1536} for _, dim := range dims { dimStr := strconv.Itoa(dim) b.Run("dim_"+dimStr, func(b *testing.B) { b.ReportAllocs() data := make([]byte, dim*4) for i := 0; i < dim; i++ { binary.BigEndian.PutUint32(data[i*4:], math.Float32bits(float32(i)*0.1)) } info := makeFloatVectorType(dim) var result []float32 b.SetBytes(int64(dim * 4)) b.ResetTimer() for i := 0; i < b.N; i++ { if err := gocql.Unmarshal(info, data, &result); err != nil { b.Fatal(err) } } }) } } func BenchmarkVectorRoundTripPublic(b *testing.B) { dims := []int{128, 384, 768, 1536} for _, dim := range dims { dimStr := strconv.Itoa(dim) b.Run("dim_"+dimStr, func(b *testing.B) { b.ReportAllocs() srcVec := make([]float32, dim) for i := range srcVec { srcVec[i] = float32(i) * 0.1 } info := makeFloatVectorType(dim) var dstVec []float32 b.SetBytes(int64(dim * 4 * 2)) b.ResetTimer() for i := 0; i < b.N; i++ { data, err := gocql.Marshal(info, srcVec) if err != nil { b.Fatal(err) } if err := gocql.Unmarshal(info, data, &dstVec); err != nil { b.Fatal(err) } } }) } } ================================================ FILE: tests/bench/go.mod ================================================ module github.com/gocql/gocql/bench_test go 1.25.0 require ( github.com/brianvoe/gofakeit/v6 v6.28.0 github.com/gocql/gocql v1.7.0 ) require ( github.com/google/uuid v1.6.0 // indirect github.com/klauspost/compress v1.18.5 // indirect golang.org/x/sync v0.20.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect ) replace github.com/gocql/gocql => ../.. ================================================ FILE: tests/bench/go.sum ================================================ github.com/brianvoe/gofakeit/v6 v6.28.0 h1:Xib46XXuQfmlLS2EXRuJpqcw8St6qSZz75OUo0tgAW4= github.com/brianvoe/gofakeit/v6 v6.28.0/go.mod h1:Xj58BMSnFqcn/fAQeSK+/PLtC5kSb7FJIq4JyGa8vEs= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE= github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA= golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= ================================================ FILE: tests/bench/rec_insert/192.168.100.11:9042-0Reads ================================================ {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABCR5FsunQ0R7+r+IQBJDGRzAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABYAAAABAAQHnu5NL7K3LWoBiT3dKFi9QAAAAEAAAADAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAJjawAJAAF2AA0AAAAEAAAAAA=="} {"stream_id":320,"data":"hAABQAgAAAAEAAAAAQ=="} {"stream_id":384,"data":"hAABgAgAAAAEAAAAAQ=="} {"stream_id":448,"data":"hAABwAgAAAAEAAAAAQ=="} {"stream_id":512,"data":"hAACAAgAAAAEAAAAAQ=="} {"stream_id":576,"data":"hAACQAgAAAAEAAAAAQ=="} {"stream_id":640,"data":"hAACgAgAAAAEAAAAAQ=="} {"stream_id":704,"data":"hAACwAgAAAAEAAAAAQ=="} {"stream_id":768,"data":"hAADAAgAAAAEAAAAAQ=="} {"stream_id":832,"data":"hAADQAgAAAAEAAAAAQ=="} {"stream_id":896,"data":"hAADgAgAAAAEAAAAAQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABDu2cDanQ0R7wuYHo4KKzsUAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABYAAAABAAQHnu5NL7K3LWoBiT3dKFi9QAAAAEAAAADAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAJjawAJAAF2AA0AAAAEAAAAAA=="} {"stream_id":320,"data":"hAABQAgAAAAEAAAAAQ=="} {"stream_id":384,"data":"hAABgAgAAAAEAAAAAQ=="} {"stream_id":448,"data":"hAABwAgAAAAEAAAAAQ=="} {"stream_id":512,"data":"hAACAAgAAAAEAAAAAQ=="} {"stream_id":576,"data":"hAACQAgAAAAEAAAAAQ=="} {"stream_id":640,"data":"hAACgAgAAAAEAAAAAQ=="} {"stream_id":704,"data":"hAACwAgAAAAEAAAAAQ=="} {"stream_id":768,"data":"hAADAAgAAAAEAAAAAQ=="} {"stream_id":832,"data":"hAADQAgAAAAEAAAAAQ=="} {"stream_id":896,"data":"hAADgAgAAAAEAAAAAQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABAInmaEnRIR7y3XxXaiIG8xAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABYAAAABAAQHnu5NL7K3LWoBiT3dKFi9QAAAAEAAAADAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAJjawAJAAF2AA0AAAAEAAAAAA=="} {"stream_id":320,"data":"hAABQAgAAAAEAAAAAQ=="} {"stream_id":384,"data":"hAABgAgAAAAEAAAAAQ=="} {"stream_id":448,"data":"hAABwAgAAAAEAAAAAQ=="} {"stream_id":512,"data":"hAACAAgAAAAEAAAAAQ=="} {"stream_id":576,"data":"hAACQAgAAAAEAAAAAQ=="} {"stream_id":640,"data":"hAACgAgAAAAEAAAAAQ=="} {"stream_id":704,"data":"hAACwAgAAAAEAAAAAQ=="} {"stream_id":768,"data":"hAADAAgAAAAEAAAAAQ=="} {"stream_id":832,"data":"hAADQAgAAAAEAAAAAQ=="} {"stream_id":896,"data":"hAADgAgAAAAEAAAAAQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABAEFnxKnRMR73PNIzDsRApMAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABYAAAABAAQHnu5NL7K3LWoBiT3dKFi9QAAAAEAAAADAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAJjawAJAAF2AA0AAAAEAAAAAA=="} {"stream_id":320,"data":"hAABQAgAAAAEAAAAAQ=="} {"stream_id":384,"data":"hAABgAgAAAAEAAAAAQ=="} {"stream_id":448,"data":"hAABwAgAAAAEAAAAAQ=="} {"stream_id":512,"data":"hAACAAgAAAAEAAAAAQ=="} {"stream_id":576,"data":"hAACQAgAAAAEAAAAAQ=="} {"stream_id":640,"data":"hAACgAgAAAAEAAAAAQ=="} {"stream_id":704,"data":"hAACwAgAAAAEAAAAAQ=="} {"stream_id":768,"data":"hAADAAgAAAAEAAAAAQ=="} {"stream_id":832,"data":"hAADQAgAAAAEAAAAAQ=="} {"stream_id":896,"data":"hAADgAgAAAAEAAAAAQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABAb5TUUnRMR717g5pvokC/kAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABYAAAABAAQHnu5NL7K3LWoBiT3dKFi9QAAAAEAAAADAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAJjawAJAAF2AA0AAAAEAAAAAA=="} {"stream_id":320,"data":"hAABQAgAAAAEAAAAAQ=="} {"stream_id":384,"data":"hAABgAgAAAAEAAAAAQ=="} {"stream_id":448,"data":"hAABwAgAAAAEAAAAAQ=="} {"stream_id":512,"data":"hAACAAgAAAAEAAAAAQ=="} {"stream_id":576,"data":"hAACQAgAAAAEAAAAAQ=="} {"stream_id":640,"data":"hAACgAgAAAAEAAAAAQ=="} {"stream_id":704,"data":"hAACwAgAAAAEAAAAAQ=="} {"stream_id":768,"data":"hAADAAgAAAAEAAAAAQ=="} {"stream_id":832,"data":"hAADQAgAAAAEAAAAAQ=="} {"stream_id":896,"data":"hAADgAgAAAAEAAAAAQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABAxAe3ynRMR766bODzKESb3AAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABYAAAABAAQHnu5NL7K3LWoBiT3dKFi9QAAAAEAAAADAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAJjawAJAAF2AA0AAAAEAAAAAA=="} {"stream_id":320,"data":"hAABQAgAAAAEAAAAAQ=="} {"stream_id":384,"data":"hAABgAgAAAAEAAAAAQ=="} {"stream_id":448,"data":"hAABwAgAAAAEAAAAAQ=="} {"stream_id":512,"data":"hAACAAgAAAAEAAAAAQ=="} {"stream_id":576,"data":"hAACQAgAAAAEAAAAAQ=="} {"stream_id":640,"data":"hAACgAgAAAAEAAAAAQ=="} {"stream_id":704,"data":"hAACwAgAAAAEAAAAAQ=="} {"stream_id":768,"data":"hAADAAgAAAAEAAAAAQ=="} {"stream_id":832,"data":"hAADQAgAAAAEAAAAAQ=="} {"stream_id":896,"data":"hAADgAgAAAAEAAAAAQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABCND7fSnRMR7xa0C3LDZrpzAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABYAAAABAAQHnu5NL7K3LWoBiT3dKFi9QAAAAEAAAADAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAJjawAJAAF2AA0AAAAEAAAAAA=="} {"stream_id":320,"data":"hAABQAgAAAAEAAAAAQ=="} {"stream_id":384,"data":"hAABgAgAAAAEAAAAAQ=="} {"stream_id":448,"data":"hAABwAgAAAAEAAAAAQ=="} {"stream_id":512,"data":"hAACAAgAAAAEAAAAAQ=="} {"stream_id":576,"data":"hAACQAgAAAAEAAAAAQ=="} {"stream_id":640,"data":"hAACgAgAAAAEAAAAAQ=="} {"stream_id":704,"data":"hAACwAgAAAAEAAAAAQ=="} {"stream_id":768,"data":"hAADAAgAAAAEAAAAAQ=="} {"stream_id":832,"data":"hAADQAgAAAAEAAAAAQ=="} {"stream_id":896,"data":"hAADgAgAAAAEAAAAAQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABCxO0fonRMR73j1wwJQHdWrAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABYAAAABAAQHnu5NL7K3LWoBiT3dKFi9QAAAAEAAAADAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAJjawAJAAF2AA0AAAAEAAAAAA=="} {"stream_id":320,"data":"hAABQAgAAAAEAAAAAQ=="} {"stream_id":384,"data":"hAABgAgAAAAEAAAAAQ=="} {"stream_id":448,"data":"hAABwAgAAAAEAAAAAQ=="} {"stream_id":512,"data":"hAACAAgAAAAEAAAAAQ=="} {"stream_id":576,"data":"hAACQAgAAAAEAAAAAQ=="} {"stream_id":640,"data":"hAACgAgAAAAEAAAAAQ=="} {"stream_id":704,"data":"hAACwAgAAAAEAAAAAQ=="} {"stream_id":768,"data":"hAADAAgAAAAEAAAAAQ=="} {"stream_id":832,"data":"hAADQAgAAAAEAAAAAQ=="} {"stream_id":896,"data":"hAADgAgAAAAEAAAAAQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABDvMx4YnRMR76NfXEL1hL0aAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABYAAAABAAQHnu5NL7K3LWoBiT3dKFi9QAAAAEAAAADAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAJjawAJAAF2AA0AAAAEAAAAAA=="} {"stream_id":320,"data":"hAABQAgAAAAEAAAAAQ=="} {"stream_id":384,"data":"hAABgAgAAAAEAAAAAQ=="} {"stream_id":448,"data":"hAABwAgAAAAEAAAAAQ=="} {"stream_id":512,"data":"hAACAAgAAAAEAAAAAQ=="} {"stream_id":576,"data":"hAACQAgAAAAEAAAAAQ=="} {"stream_id":640,"data":"hAACgAgAAAAEAAAAAQ=="} {"stream_id":704,"data":"hAACwAgAAAAEAAAAAQ=="} {"stream_id":768,"data":"hAADAAgAAAAEAAAAAQ=="} {"stream_id":832,"data":"hAADQAgAAAAEAAAAAQ=="} {"stream_id":896,"data":"hAADgAgAAAAEAAAAAQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABBD0FVsnRQR7zNx10SKYLAmAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABYAAAABAAQHnu5NL7K3LWoBiT3dKFi9QAAAAEAAAADAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAJjawAJAAF2AA0AAAAEAAAAAA=="} {"stream_id":320,"data":"hAABQAgAAAAEAAAAAQ=="} {"stream_id":384,"data":"hAABgAgAAAAEAAAAAQ=="} {"stream_id":448,"data":"hAABwAgAAAAEAAAAAQ=="} {"stream_id":512,"data":"hAACAAgAAAAEAAAAAQ=="} {"stream_id":576,"data":"hAACQAgAAAAEAAAAAQ=="} {"stream_id":640,"data":"hAACgAgAAAAEAAAAAQ=="} {"stream_id":704,"data":"hAACwAgAAAAEAAAAAQ=="} {"stream_id":768,"data":"hAADAAgAAAAEAAAAAQ=="} {"stream_id":832,"data":"hAADQAgAAAAEAAAAAQ=="} {"stream_id":896,"data":"hAADgAgAAAAEAAAAAQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABBjRpfanRQR73UZ2qUmi5vUAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABYAAAABAAQHnu5NL7K3LWoBiT3dKFi9QAAAAEAAAADAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAJjawAJAAF2AA0AAAAEAAAAAA=="} {"stream_id":320,"data":"hAABQAgAAAAEAAAAAQ=="} {"stream_id":384,"data":"hAABgAgAAAAEAAAAAQ=="} {"stream_id":448,"data":"hAABwAgAAAAEAAAAAQ=="} {"stream_id":512,"data":"hAACAAgAAAAEAAAAAQ=="} {"stream_id":576,"data":"hAACQAgAAAAEAAAAAQ=="} {"stream_id":640,"data":"hAACgAgAAAAEAAAAAQ=="} {"stream_id":704,"data":"hAACwAgAAAAEAAAAAQ=="} {"stream_id":768,"data":"hAADAAgAAAAEAAAAAQ=="} {"stream_id":832,"data":"hAADQAgAAAAEAAAAAQ=="} {"stream_id":896,"data":"hAADgAgAAAAEAAAAAQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABC1lYC0nRQR74XiOWDrg7z6AAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABYAAAABAAQHnu5NL7K3LWoBiT3dKFi9QAAAAEAAAADAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAJjawAJAAF2AA0AAAAEAAAAAA=="} {"stream_id":320,"data":"hAABQAgAAAAEAAAAAQ=="} {"stream_id":384,"data":"hAABgAgAAAAEAAAAAQ=="} {"stream_id":448,"data":"hAABwAgAAAAEAAAAAQ=="} {"stream_id":512,"data":"hAACAAgAAAAEAAAAAQ=="} {"stream_id":576,"data":"hAACQAgAAAAEAAAAAQ=="} {"stream_id":640,"data":"hAACgAgAAAAEAAAAAQ=="} {"stream_id":704,"data":"hAACwAgAAAAEAAAAAQ=="} {"stream_id":768,"data":"hAADAAgAAAAEAAAAAQ=="} {"stream_id":832,"data":"hAADQAgAAAAEAAAAAQ=="} {"stream_id":896,"data":"hAADgAgAAAAEAAAAAQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATEAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABADC04KnRUR74eTL5lMntPqAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABYAAAABAAQHnu5NL7K3LWoBiT3dKFi9QAAAAEAAAADAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAJjawAJAAF2AA0AAAAEAAAAAA=="} {"stream_id":320,"data":"hAABQAgAAAAEAAAAAQ=="} {"stream_id":384,"data":"hAABgAgAAAAEAAAAAQ=="} {"stream_id":448,"data":"hAABwAgAAAAEAAAAAQ=="} {"stream_id":512,"data":"hAACAAgAAAAEAAAAAQ=="} {"stream_id":576,"data":"hAACQAgAAAAEAAAAAQ=="} {"stream_id":640,"data":"hAACgAgAAAAEAAAAAQ=="} {"stream_id":704,"data":"hAACwAgAAAAEAAAAAQ=="} {"stream_id":768,"data":"hAADAAgAAAAEAAAAAQ=="} {"stream_id":832,"data":"hAADQAgAAAAEAAAAAQ=="} {"stream_id":896,"data":"hAADgAgAAAAEAAAAAQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABBIX9jMnR8R77IoLTLjoWSOAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABYAAAABAAQHnu5NL7K3LWoBiT3dKFi9QAAAAEAAAADAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAJjawAJAAF2AA0AAAAEAAAAAA=="} {"stream_id":320,"data":"hAABQAgAAAAEAAAAAQ=="} {"stream_id":384,"data":"hAABgAgAAAAEAAAAAQ=="} {"stream_id":448,"data":"hAABwAgAAAAEAAAAAQ=="} {"stream_id":512,"data":"hAACAAgAAAAEAAAAAQ=="} {"stream_id":576,"data":"hAACQAgAAAAEAAAAAQ=="} {"stream_id":640,"data":"hAACgAgAAAAEAAAAAQ=="} {"stream_id":704,"data":"hAACwAgAAAAEAAAAAQ=="} {"stream_id":768,"data":"hAADAAgAAAAEAAAAAQ=="} {"stream_id":832,"data":"hAADQAgAAAAEAAAAAQ=="} {"stream_id":896,"data":"hAADgAgAAAAEAAAAAQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABCrS9PmnR8R73xgJaYWggcvAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABYAAAABAAQHnu5NL7K3LWoBiT3dKFi9QAAAAEAAAADAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAJjawAJAAF2AA0AAAAEAAAAAA=="} {"stream_id":320,"data":"hAABQAgAAAAEAAAAAQ=="} {"stream_id":384,"data":"hAABgAgAAAAEAAAAAQ=="} {"stream_id":448,"data":"hAABwAgAAAAEAAAAAQ=="} {"stream_id":512,"data":"hAACAAgAAAAEAAAAAQ=="} {"stream_id":576,"data":"hAACQAgAAAAEAAAAAQ=="} {"stream_id":640,"data":"hAACgAgAAAAEAAAAAQ=="} {"stream_id":704,"data":"hAACwAgAAAAEAAAAAQ=="} {"stream_id":768,"data":"hAADAAgAAAAEAAAAAQ=="} {"stream_id":832,"data":"hAADQAgAAAAEAAAAAQ=="} {"stream_id":896,"data":"hAADgAgAAAAEAAAAAQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABDOO9JmnR8R7+ZrEBJHQVPvAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABYAAAABAAQHnu5NL7K3LWoBiT3dKFi9QAAAAEAAAADAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAJjawAJAAF2AA0AAAAEAAAAAA=="} {"stream_id":320,"data":"hAABQAgAAAAEAAAAAQ=="} {"stream_id":384,"data":"hAABgAgAAAAEAAAAAQ=="} {"stream_id":448,"data":"hAABwAgAAAAEAAAAAQ=="} {"stream_id":512,"data":"hAACAAgAAAAEAAAAAQ=="} {"stream_id":576,"data":"hAACQAgAAAAEAAAAAQ=="} {"stream_id":640,"data":"hAACgAgAAAAEAAAAAQ=="} {"stream_id":704,"data":"hAACwAgAAAAEAAAAAQ=="} {"stream_id":768,"data":"hAADAAgAAAAEAAAAAQ=="} {"stream_id":832,"data":"hAADQAgAAAAEAAAAAQ=="} {"stream_id":896,"data":"hAADgAgAAAAEAAAAAQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABADHwB6nSAR76qRwPvy1BuoAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABYAAAABAAQHnu5NL7K3LWoBiT3dKFi9QAAAAEAAAADAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAJjawAJAAF2AA0AAAAEAAAAAA=="} {"stream_id":320,"data":"hAABQAgAAAAEAAAAAQ=="} {"stream_id":384,"data":"hAABgAgAAAAEAAAAAQ=="} {"stream_id":448,"data":"hAABwAgAAAAEAAAAAQ=="} {"stream_id":512,"data":"hAACAAgAAAAEAAAAAQ=="} {"stream_id":576,"data":"hAACQAgAAAAEAAAAAQ=="} {"stream_id":640,"data":"hAACgAgAAAAEAAAAAQ=="} {"stream_id":704,"data":"hAACwAgAAAAEAAAAAQ=="} {"stream_id":768,"data":"hAADAAgAAAAEAAAAAQ=="} {"stream_id":832,"data":"hAADQAgAAAAEAAAAAQ=="} {"stream_id":896,"data":"hAADgAgAAAAEAAAAAQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABBbUOgSnSAR7xG1Imw8wjVzAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABYAAAABAAQHnu5NL7K3LWoBiT3dKFi9QAAAAEAAAADAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAJjawAJAAF2AA0AAAAEAAAAAA=="} {"stream_id":320,"data":"hAABQAgAAAAEAAAAAQ=="} {"stream_id":384,"data":"hAABgAgAAAAEAAAAAQ=="} {"stream_id":448,"data":"hAABwAgAAAAEAAAAAQ=="} {"stream_id":512,"data":"hAACAAgAAAAEAAAAAQ=="} {"stream_id":576,"data":"hAACQAgAAAAEAAAAAQ=="} {"stream_id":640,"data":"hAACgAgAAAAEAAAAAQ=="} {"stream_id":704,"data":"hAACwAgAAAAEAAAAAQ=="} {"stream_id":768,"data":"hAADAAgAAAAEAAAAAQ=="} {"stream_id":832,"data":"hAADQAgAAAAEAAAAAQ=="} {"stream_id":896,"data":"hAADgAgAAAAEAAAAAQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABDFBq2MnSAR7xkFw6f0PNUWAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABYAAAABAAQHnu5NL7K3LWoBiT3dKFi9QAAAAEAAAADAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAJjawAJAAF2AA0AAAAEAAAAAA=="} {"stream_id":320,"data":"hAABQAgAAAAEAAAAAQ=="} {"stream_id":384,"data":"hAABgAgAAAAEAAAAAQ=="} {"stream_id":448,"data":"hAABwAgAAAAEAAAAAQ=="} {"stream_id":512,"data":"hAACAAgAAAAEAAAAAQ=="} {"stream_id":576,"data":"hAACQAgAAAAEAAAAAQ=="} {"stream_id":640,"data":"hAACgAgAAAAEAAAAAQ=="} {"stream_id":704,"data":"hAACwAgAAAAEAAAAAQ=="} {"stream_id":768,"data":"hAADAAgAAAAEAAAAAQ=="} {"stream_id":832,"data":"hAADQAgAAAAEAAAAAQ=="} {"stream_id":896,"data":"hAADgAgAAAAEAAAAAQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABAADcsinSER7wlwGUhVdvaXAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABYAAAABAAQHnu5NL7K3LWoBiT3dKFi9QAAAAEAAAADAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAJjawAJAAF2AA0AAAAEAAAAAA=="} {"stream_id":320,"data":"hAABQAgAAAAEAAAAAQ=="} {"stream_id":384,"data":"hAABgAgAAAAEAAAAAQ=="} {"stream_id":448,"data":"hAABwAgAAAAEAAAAAQ=="} {"stream_id":512,"data":"hAACAAgAAAAEAAAAAQ=="} {"stream_id":576,"data":"hAACQAgAAAAEAAAAAQ=="} {"stream_id":640,"data":"hAACgAgAAAAEAAAAAQ=="} {"stream_id":704,"data":"hAACwAgAAAAEAAAAAQ=="} {"stream_id":768,"data":"hAADAAgAAAAEAAAAAQ=="} {"stream_id":832,"data":"hAADQAgAAAAEAAAAAQ=="} {"stream_id":896,"data":"hAADgAgAAAAEAAAAAQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABAqC91qnSER70M1po5LqfR8AAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABYAAAABAAQHnu5NL7K3LWoBiT3dKFi9QAAAAEAAAADAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAJjawAJAAF2AA0AAAAEAAAAAA=="} {"stream_id":320,"data":"hAABQAgAAAAEAAAAAQ=="} {"stream_id":384,"data":"hAABgAgAAAAEAAAAAQ=="} {"stream_id":448,"data":"hAABwAgAAAAEAAAAAQ=="} {"stream_id":512,"data":"hAACAAgAAAAEAAAAAQ=="} {"stream_id":576,"data":"hAACQAgAAAAEAAAAAQ=="} {"stream_id":640,"data":"hAACgAgAAAAEAAAAAQ=="} {"stream_id":704,"data":"hAACwAgAAAAEAAAAAQ=="} {"stream_id":768,"data":"hAADAAgAAAAEAAAAAQ=="} {"stream_id":832,"data":"hAADQAgAAAAEAAAAAQ=="} {"stream_id":896,"data":"hAADgAgAAAAEAAAAAQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABB4RbfknSER73p3mZ3s7fVcAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABYAAAABAAQHnu5NL7K3LWoBiT3dKFi9QAAAAEAAAADAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAJjawAJAAF2AA0AAAAEAAAAAA=="} {"stream_id":320,"data":"hAABQAgAAAAEAAAAAQ=="} {"stream_id":384,"data":"hAABgAgAAAAEAAAAAQ=="} {"stream_id":448,"data":"hAABwAgAAAAEAAAAAQ=="} {"stream_id":512,"data":"hAACAAgAAAAEAAAAAQ=="} {"stream_id":576,"data":"hAACQAgAAAAEAAAAAQ=="} {"stream_id":640,"data":"hAACgAgAAAAEAAAAAQ=="} {"stream_id":704,"data":"hAACwAgAAAAEAAAAAQ=="} {"stream_id":768,"data":"hAADAAgAAAAEAAAAAQ=="} {"stream_id":832,"data":"hAADQAgAAAAEAAAAAQ=="} {"stream_id":896,"data":"hAADgAgAAAAEAAAAAQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABCSpEDinSER7zv6d1XvBXKLAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABYAAAABAAQHnu5NL7K3LWoBiT3dKFi9QAAAAEAAAADAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAJjawAJAAF2AA0AAAAEAAAAAA=="} {"stream_id":320,"data":"hAABQAgAAAAEAAAAAQ=="} {"stream_id":384,"data":"hAABgAgAAAAEAAAAAQ=="} {"stream_id":448,"data":"hAABwAgAAAAEAAAAAQ=="} {"stream_id":512,"data":"hAACAAgAAAAEAAAAAQ=="} {"stream_id":576,"data":"hAACQAgAAAAEAAAAAQ=="} {"stream_id":640,"data":"hAACgAgAAAAEAAAAAQ=="} {"stream_id":704,"data":"hAACwAgAAAAEAAAAAQ=="} {"stream_id":768,"data":"hAADAAgAAAAEAAAAAQ=="} {"stream_id":832,"data":"hAADQAgAAAAEAAAAAQ=="} {"stream_id":896,"data":"hAADgAgAAAAEAAAAAQ=="} ================================================ FILE: tests/bench/rec_insert/192.168.100.11:9042-0Writes ================================================ {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAC0NRTF9WRVJTSU9OAAUzLjAuMAALRFJJVkVSX05BTUUAFVNjeWxsYURCIEdvQ1FMIERyaXZlcgAORFJJVkVSX1ZFUlNJT04AAAAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwApTFdUX09QVElNSVpBVElPTl9NRVRBX0JJVF9NQVNLPTIxNDc0ODM2NDgAF1NDWUxMQV9SQVRFX0xJTUlUX0VSUk9SAAAAElRBQkxFVFNfUk9VVElOR19WMQAA"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlK/1uU/"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAABGAAAAQklOU0VSVCBJTlRPIHNpbmdsZV9jb25uX2JlbmNoLnRhYmxlMSAocGssIGNrLCB2KSBWQUxVRVMgKD8sID8sID8pOw=="} {"stream_id":320,"data":"BAABQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAAAAAAQAAAAAAAAABk5hbWVfMAAAE4gABiZSv9bsbw=="} {"stream_id":384,"data":"BAABgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAQAAAAQAAAABAAAABk5hbWVfMQAAE4gABiZSv9bukQ=="} {"stream_id":448,"data":"BAABwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAgAAAAQAAAACAAAABk5hbWVfMgAAE4gABiZSv9bv/A=="} {"stream_id":512,"data":"BAACAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAwAAAAQAAAADAAAABk5hbWVfMwAAE4gABiZSv9byNA=="} {"stream_id":576,"data":"BAACQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABAAAAAQAAAAEAAAABk5hbWVfNAAAE4gABiZSv9bzwQ=="} {"stream_id":640,"data":"BAACgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABQAAAAQAAAAAAAAABk5hbWVfNQAAE4gABiZSv9b1IA=="} {"stream_id":704,"data":"BAACwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABgAAAAQAAAABAAAABk5hbWVfNgAAE4gABiZSv9b2bg=="} {"stream_id":768,"data":"BAADAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABwAAAAQAAAACAAAABk5hbWVfNwAAE4gABiZSv9b3sQ=="} {"stream_id":832,"data":"BAADQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACAAAAAQAAAADAAAABk5hbWVfOAAAE4gABiZSv9b5UQ=="} {"stream_id":896,"data":"BAADgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACQAAAAQAAAAEAAAABk5hbWVfOQAAE4gABiZSv9b6fg=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAC0RSSVZFUl9OQU1FABVTY3lsbGFEQiBHb0NRTCBEcml2ZXIADkRSSVZFUl9WRVJTSU9OAAAAHFNDWUxMQV9MV1RfQUREX01FVEFEQVRBX01BUksAKUxXVF9PUFRJTUlaQVRJT05fTUVUQV9CSVRfTUFTSz0yMTQ3NDgzNjQ4ABdTQ1lMTEFfUkFURV9MSU1JVF9FUlJPUgAAABJUQUJMRVRTX1JPVVRJTkdfVjEAAAALQ1FMX1ZFUlNJT04ABTMuMC4w"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlLJIrmb"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAABGAAAAQklOU0VSVCBJTlRPIHNpbmdsZV9jb25uX2JlbmNoLnRhYmxlMSAocGssIGNrLCB2KSBWQUxVRVMgKD8sID8sID8pOw=="} {"stream_id":320,"data":"BAABQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAAAAAAQAAAAAAAAABk5hbWVfMAAAE4gABiZSySLE3Q=="} {"stream_id":384,"data":"BAABgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAQAAAAQAAAABAAAABk5hbWVfMQAAE4gABiZSySLINA=="} {"stream_id":448,"data":"BAABwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAgAAAAQAAAACAAAABk5hbWVfMgAAE4gABiZSySLKbQ=="} {"stream_id":512,"data":"BAACAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAwAAAAQAAAADAAAABk5hbWVfMwAAE4gABiZSySLNVA=="} {"stream_id":576,"data":"BAACQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABAAAAAQAAAAEAAAABk5hbWVfNAAAE4gABiZSySLQFg=="} {"stream_id":640,"data":"BAACgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABQAAAAQAAAAAAAAABk5hbWVfNQAAE4gABiZSySLSvA=="} {"stream_id":704,"data":"BAACwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABgAAAAQAAAABAAAABk5hbWVfNgAAE4gABiZSySLVzw=="} {"stream_id":768,"data":"BAADAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABwAAAAQAAAACAAAABk5hbWVfNwAAE4gABiZSySLYTw=="} {"stream_id":832,"data":"BAADQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACAAAAAQAAAADAAAABk5hbWVfOAAAE4gABiZSySLbwQ=="} {"stream_id":896,"data":"BAADgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACQAAAAQAAAAEAAAABk5hbWVfOQAAE4gABiZSySLd9A=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAElRBQkxFVFNfUk9VVElOR19WMQAAAAtDUUxfVkVSU0lPTgAFMy4wLjAAC0RSSVZFUl9OQU1FABVTY3lsbGFEQiBHb0NRTCBEcml2ZXIADkRSSVZFUl9WRVJTSU9OAAAAHFNDWUxMQV9MV1RfQUREX01FVEFEQVRBX01BUksAKUxXVF9PUFRJTUlaQVRJT05fTUVUQV9CSVRfTUFTSz0yMTQ3NDgzNjQ4ABdTQ1lMTEFfUkFURV9MSU1JVF9FUlJPUgAA"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlMyHKee"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAABGAAAAQklOU0VSVCBJTlRPIHNpbmdsZV9jb25uX2JlbmNoLnRhYmxlMSAocGssIGNrLCB2KSBWQUxVRVMgKD8sID8sID8pOw=="} {"stream_id":320,"data":"BAABQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAAAAAAQAAAAAAAAABk5hbWVfMAAAE4gABiZTMhyv7A=="} {"stream_id":384,"data":"BAABgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAQAAAAQAAAABAAAABk5hbWVfMQAAE4gABiZTMhyzSA=="} {"stream_id":448,"data":"BAABwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAgAAAAQAAAACAAAABk5hbWVfMgAAE4gABiZTMhy1jQ=="} {"stream_id":512,"data":"BAACAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAwAAAAQAAAADAAAABk5hbWVfMwAAE4gABiZTMhy4/g=="} {"stream_id":576,"data":"BAACQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABAAAAAQAAAAEAAAABk5hbWVfNAAAE4gABiZTMhy7gA=="} {"stream_id":640,"data":"BAACgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABQAAAAQAAAAAAAAABk5hbWVfNQAAE4gABiZTMhy9lA=="} {"stream_id":704,"data":"BAACwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABgAAAAQAAAABAAAABk5hbWVfNgAAE4gABiZTMhzCyQ=="} {"stream_id":768,"data":"BAADAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABwAAAAQAAAACAAAABk5hbWVfNwAAE4gABiZTMhzElw=="} {"stream_id":832,"data":"BAADQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACAAAAAQAAAADAAAABk5hbWVfOAAAE4gABiZTMhzGzw=="} {"stream_id":896,"data":"BAADgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACQAAAAQAAAAEAAAABk5hbWVfOQAAE4gABiZTMhzIcw=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAHFNDWUxMQV9MV1RfQUREX01FVEFEQVRBX01BUksAKUxXVF9PUFRJTUlaQVRJT05fTUVUQV9CSVRfTUFTSz0yMTQ3NDgzNjQ4ABdTQ1lMTEFfUkFURV9MSU1JVF9FUlJPUgAAABJUQUJMRVRTX1JPVVRJTkdfVjEAAAALQ1FMX1ZFUlNJT04ABTMuMC4wAAtEUklWRVJfTkFNRQAVU2N5bGxhREIgR29DUUwgRHJpdmVyAA5EUklWRVJfVkVSU0lPTgAA"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlNLQnk9"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAABGAAAAQklOU0VSVCBJTlRPIHNpbmdsZV9jb25uX2JlbmNoLnRhYmxlMSAocGssIGNrLCB2KSBWQUxVRVMgKD8sID8sID8pOw=="} {"stream_id":320,"data":"BAABQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAAAAAAQAAAAAAAAABk5hbWVfMAAAE4gABiZTS0KLYg=="} {"stream_id":384,"data":"BAABgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAQAAAAQAAAABAAAABk5hbWVfMQAAE4gABiZTS0KQ3g=="} {"stream_id":448,"data":"BAABwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAgAAAAQAAAACAAAABk5hbWVfMgAAE4gABiZTS0KVaA=="} {"stream_id":512,"data":"BAACAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAwAAAAQAAAADAAAABk5hbWVfMwAAE4gABiZTS0KYdA=="} {"stream_id":576,"data":"BAACQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABAAAAAQAAAAEAAAABk5hbWVfNAAAE4gABiZTS0KbOg=="} {"stream_id":640,"data":"BAACgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABQAAAAQAAAAAAAAABk5hbWVfNQAAE4gABiZTS0KfVw=="} {"stream_id":704,"data":"BAACwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABgAAAAQAAAABAAAABk5hbWVfNgAAE4gABiZTS0KjSA=="} {"stream_id":768,"data":"BAADAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABwAAAAQAAAACAAAABk5hbWVfNwAAE4gABiZTS0Kmnw=="} {"stream_id":832,"data":"BAADQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACAAAAAQAAAADAAAABk5hbWVfOAAAE4gABiZTS0Kqsg=="} {"stream_id":896,"data":"BAADgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACQAAAAQAAAAEAAAABk5hbWVfOQAAE4gABiZTS0KtNw=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAElRBQkxFVFNfUk9VVElOR19WMQAAAAtDUUxfVkVSU0lPTgAFMy4wLjAAC0RSSVZFUl9OQU1FABVTY3lsbGFEQiBHb0NRTCBEcml2ZXIADkRSSVZFUl9WRVJTSU9OAAAAHFNDWUxMQV9MV1RfQUREX01FVEFEQVRBX01BUksAKUxXVF9PUFRJTUlaQVRJT05fTUVUQV9CSVRfTUFTSz0yMTQ3NDgzNjQ4ABdTQ1lMTEFfUkFURV9MSU1JVF9FUlJPUgAA"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlNNo7wJ"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAABGAAAAQklOU0VSVCBJTlRPIHNpbmdsZV9jb25uX2JlbmNoLnRhYmxlMSAocGssIGNrLCB2KSBWQUxVRVMgKD8sID8sID8pOw=="} {"stream_id":320,"data":"BAABQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAAAAAAQAAAAAAAAABk5hbWVfMAAAE4gABiZTTaPCAw=="} {"stream_id":384,"data":"BAABgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAQAAAAQAAAABAAAABk5hbWVfMQAAE4gABiZTTaPD2A=="} {"stream_id":448,"data":"BAABwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAgAAAAQAAAACAAAABk5hbWVfMgAAE4gABiZTTaPFMw=="} {"stream_id":512,"data":"BAACAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAwAAAAQAAAADAAAABk5hbWVfMwAAE4gABiZTTaPG0w=="} {"stream_id":576,"data":"BAACQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABAAAAAQAAAAEAAAABk5hbWVfNAAAE4gABiZTTaPIaQ=="} {"stream_id":640,"data":"BAACgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABQAAAAQAAAAAAAAABk5hbWVfNQAAE4gABiZTTaPJ0g=="} {"stream_id":704,"data":"BAACwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABgAAAAQAAAABAAAABk5hbWVfNgAAE4gABiZTTaPLVA=="} {"stream_id":768,"data":"BAADAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABwAAAAQAAAACAAAABk5hbWVfNwAAE4gABiZTTaPMww=="} {"stream_id":832,"data":"BAADQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACAAAAAQAAAADAAAABk5hbWVfOAAAE4gABiZTTaPOVg=="} {"stream_id":896,"data":"BAADgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACQAAAAQAAAAEAAAABk5hbWVfOQAAE4gABiZTTaPP9Q=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAC0RSSVZFUl9OQU1FABVTY3lsbGFEQiBHb0NRTCBEcml2ZXIADkRSSVZFUl9WRVJTSU9OAAAAHFNDWUxMQV9MV1RfQUREX01FVEFEQVRBX01BUksAKUxXVF9PUFRJTUlaQVRJT05fTUVUQV9CSVRfTUFTSz0yMTQ3NDgzNjQ4ABdTQ1lMTEFfUkFURV9MSU1JVF9FUlJPUgAAABJUQUJMRVRTX1JPVVRJTkdfVjEAAAALQ1FMX1ZFUlNJT04ABTMuMC4w"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlNPwG3Q"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAABGAAAAQklOU0VSVCBJTlRPIHNpbmdsZV9jb25uX2JlbmNoLnRhYmxlMSAocGssIGNrLCB2KSBWQUxVRVMgKD8sID8sID8pOw=="} {"stream_id":320,"data":"BAABQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAAAAAAQAAAAAAAAABk5hbWVfMAAAE4gABiZTT8B6Kg=="} {"stream_id":384,"data":"BAABgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAQAAAAQAAAABAAAABk5hbWVfMQAAE4gABiZTT8CALQ=="} {"stream_id":448,"data":"BAABwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAgAAAAQAAAACAAAABk5hbWVfMgAAE4gABiZTT8CDAg=="} {"stream_id":512,"data":"BAACAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAwAAAAQAAAADAAAABk5hbWVfMwAAE4gABiZTT8CGiA=="} {"stream_id":576,"data":"BAACQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABAAAAAQAAAAEAAAABk5hbWVfNAAAE4gABiZTT8CJgA=="} {"stream_id":640,"data":"BAACgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABQAAAAQAAAAAAAAABk5hbWVfNQAAE4gABiZTT8CM/g=="} {"stream_id":704,"data":"BAACwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABgAAAAQAAAABAAAABk5hbWVfNgAAE4gABiZTT8CRKQ=="} {"stream_id":768,"data":"BAADAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABwAAAAQAAAACAAAABk5hbWVfNwAAE4gABiZTT8CT5g=="} {"stream_id":832,"data":"BAADQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACAAAAAQAAAADAAAABk5hbWVfOAAAE4gABiZTT8CXTQ=="} {"stream_id":896,"data":"BAADgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACQAAAAQAAAAEAAAABk5hbWVfOQAAE4gABiZTT8Cawg=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAC0NRTF9WRVJTSU9OAAUzLjAuMAALRFJJVkVSX05BTUUAFVNjeWxsYURCIEdvQ1FMIERyaXZlcgAORFJJVkVSX1ZFUlNJT04AAAAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwApTFdUX09QVElNSVpBVElPTl9NRVRBX0JJVF9NQVNLPTIxNDc0ODM2NDgAF1NDWUxMQV9SQVRFX0xJTUlUX0VSUk9SAAAAElRBQkxFVFNfUk9VVElOR19WMQAA"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlNY9MP5"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAABGAAAAQklOU0VSVCBJTlRPIHNpbmdsZV9jb25uX2JlbmNoLnRhYmxlMSAocGssIGNrLCB2KSBWQUxVRVMgKD8sID8sID8pOw=="} {"stream_id":320,"data":"BAABQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAAAAAAQAAAAAAAAABk5hbWVfMAAAE4gABiZTWPTKKA=="} {"stream_id":384,"data":"BAABgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAQAAAAQAAAABAAAABk5hbWVfMQAAE4gABiZTWPTNJQ=="} {"stream_id":448,"data":"BAABwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAgAAAAQAAAACAAAABk5hbWVfMgAAE4gABiZTWPTOjA=="} {"stream_id":512,"data":"BAACAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAwAAAAQAAAADAAAABk5hbWVfMwAAE4gABiZTWPTSTQ=="} {"stream_id":576,"data":"BAACQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABAAAAAQAAAAEAAAABk5hbWVfNAAAE4gABiZTWPTUAA=="} {"stream_id":640,"data":"BAACgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABQAAAAQAAAAAAAAABk5hbWVfNQAAE4gABiZTWPTVkw=="} {"stream_id":704,"data":"BAACwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABgAAAAQAAAABAAAABk5hbWVfNgAAE4gABiZTWPTaFA=="} {"stream_id":768,"data":"BAADAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABwAAAAQAAAACAAAABk5hbWVfNwAAE4gABiZTWPTbcg=="} {"stream_id":832,"data":"BAADQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACAAAAAQAAAADAAAABk5hbWVfOAAAE4gABiZTWPTc8w=="} {"stream_id":896,"data":"BAADgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACQAAAAQAAAAEAAAABk5hbWVfOQAAE4gABiZTWPTePw=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAF1NDWUxMQV9SQVRFX0xJTUlUX0VSUk9SAAAAElRBQkxFVFNfUk9VVElOR19WMQAAAAtDUUxfVkVSU0lPTgAFMy4wLjAAC0RSSVZFUl9OQU1FABVTY3lsbGFEQiBHb0NRTCBEcml2ZXIADkRSSVZFUl9WRVJTSU9OAAAAHFNDWUxMQV9MV1RfQUREX01FVEFEQVRBX01BUksAKUxXVF9PUFRJTUlaQVRJT05fTUVUQV9CSVRfTUFTSz0yMTQ3NDgzNjQ4"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlNcksmc"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAABGAAAAQklOU0VSVCBJTlRPIHNpbmdsZV9jb25uX2JlbmNoLnRhYmxlMSAocGssIGNrLCB2KSBWQUxVRVMgKD8sID8sID8pOw=="} {"stream_id":320,"data":"BAABQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAAAAAAQAAAAAAAAABk5hbWVfMAAAE4gABiZTXJLb0A=="} {"stream_id":384,"data":"BAABgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAQAAAAQAAAABAAAABk5hbWVfMQAAE4gABiZTXJLfTQ=="} {"stream_id":448,"data":"BAABwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAgAAAAQAAAACAAAABk5hbWVfMgAAE4gABiZTXJLkCg=="} {"stream_id":512,"data":"BAACAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAwAAAAQAAAADAAAABk5hbWVfMwAAE4gABiZTXJLpWQ=="} {"stream_id":576,"data":"BAACQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABAAAAAQAAAAEAAAABk5hbWVfNAAAE4gABiZTXJLuoA=="} {"stream_id":640,"data":"BAACgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABQAAAAQAAAAAAAAABk5hbWVfNQAAE4gABiZTXJLzqA=="} {"stream_id":704,"data":"BAACwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABgAAAAQAAAABAAAABk5hbWVfNgAAE4gABiZTXJL6EQ=="} {"stream_id":768,"data":"BAADAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABwAAAAQAAAACAAAABk5hbWVfNwAAE4gABiZTXJL+Ww=="} {"stream_id":832,"data":"BAADQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACAAAAAQAAAADAAAABk5hbWVfOAAAE4gABiZTXJMEHw=="} {"stream_id":896,"data":"BAADgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACQAAAAQAAAAEAAAABk5hbWVfOQAAE4gABiZTXJMHeQ=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAC0NRTF9WRVJTSU9OAAUzLjAuMAALRFJJVkVSX05BTUUAFVNjeWxsYURCIEdvQ1FMIERyaXZlcgAORFJJVkVSX1ZFUlNJT04AAAAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwApTFdUX09QVElNSVpBVElPTl9NRVRBX0JJVF9NQVNLPTIxNDc0ODM2NDgAF1NDWUxMQV9SQVRFX0xJTUlUX0VSUk9SAAAAElRBQkxFVFNfUk9VVElOR19WMQAA"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlNixTSQ"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAABGAAAAQklOU0VSVCBJTlRPIHNpbmdsZV9jb25uX2JlbmNoLnRhYmxlMSAocGssIGNrLCB2KSBWQUxVRVMgKD8sID8sID8pOw=="} {"stream_id":320,"data":"BAABQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAAAAAAQAAAAAAAAABk5hbWVfMAAAE4gABiZTYsVAzA=="} {"stream_id":384,"data":"BAABgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAQAAAAQAAAABAAAABk5hbWVfMQAAE4gABiZTYsVFpg=="} {"stream_id":448,"data":"BAABwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAgAAAAQAAAACAAAABk5hbWVfMgAAE4gABiZTYsVIoA=="} {"stream_id":512,"data":"BAACAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAwAAAAQAAAADAAAABk5hbWVfMwAAE4gABiZTYsVMcQ=="} {"stream_id":576,"data":"BAACQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABAAAAAQAAAAEAAAABk5hbWVfNAAAE4gABiZTYsVQhQ=="} {"stream_id":640,"data":"BAACgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABQAAAAQAAAAAAAAABk5hbWVfNQAAE4gABiZTYsVVQw=="} {"stream_id":704,"data":"BAACwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABgAAAAQAAAABAAAABk5hbWVfNgAAE4gABiZTYsVY/g=="} {"stream_id":768,"data":"BAADAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABwAAAAQAAAACAAAABk5hbWVfNwAAE4gABiZTYsVfBQ=="} {"stream_id":832,"data":"BAADQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACAAAAAQAAAADAAAABk5hbWVfOAAAE4gABiZTYsViPQ=="} {"stream_id":896,"data":"BAADgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACQAAAAQAAAAEAAAABk5hbWVfOQAAE4gABiZTYsVkoQ=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAF1NDWUxMQV9SQVRFX0xJTUlUX0VSUk9SAAAAElRBQkxFVFNfUk9VVElOR19WMQAAAAtDUUxfVkVSU0lPTgAFMy4wLjAAC0RSSVZFUl9OQU1FABVTY3lsbGFEQiBHb0NRTCBEcml2ZXIADkRSSVZFUl9WRVJTSU9OAAAAHFNDWUxMQV9MV1RfQUREX01FVEFEQVRBX01BUksAKUxXVF9PUFRJTUlaQVRJT05fTUVUQV9CSVRfTUFTSz0yMTQ3NDgzNjQ4"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlNrO079"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAABGAAAAQklOU0VSVCBJTlRPIHNpbmdsZV9jb25uX2JlbmNoLnRhYmxlMSAocGssIGNrLCB2KSBWQUxVRVMgKD8sID8sID8pOw=="} {"stream_id":320,"data":"BAABQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAAAAAAQAAAAAAAAABk5hbWVfMAAAE4gABiZTaztZSQ=="} {"stream_id":384,"data":"BAABgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAQAAAAQAAAABAAAABk5hbWVfMQAAE4gABiZTaztdBQ=="} {"stream_id":448,"data":"BAABwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAgAAAAQAAAACAAAABk5hbWVfMgAAE4gABiZTaztf8A=="} {"stream_id":512,"data":"BAACAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAwAAAAQAAAADAAAABk5hbWVfMwAAE4gABiZTazti5g=="} {"stream_id":576,"data":"BAACQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABAAAAAQAAAAEAAAABk5hbWVfNAAAE4gABiZTaztmBA=="} {"stream_id":640,"data":"BAACgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABQAAAAQAAAAAAAAABk5hbWVfNQAAE4gABiZTaztoSw=="} {"stream_id":704,"data":"BAACwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABgAAAAQAAAABAAAABk5hbWVfNgAAE4gABiZTaztqcw=="} {"stream_id":768,"data":"BAADAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABwAAAAQAAAACAAAABk5hbWVfNwAAE4gABiZTaztsRQ=="} {"stream_id":832,"data":"BAADQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACAAAAAQAAAADAAAABk5hbWVfOAAAE4gABiZTaztuew=="} {"stream_id":896,"data":"BAADgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACQAAAAQAAAAEAAAABk5hbWVfOQAAE4gABiZTaztwLg=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAC0NRTF9WRVJTSU9OAAUzLjAuMAALRFJJVkVSX05BTUUAFVNjeWxsYURCIEdvQ1FMIERyaXZlcgAORFJJVkVSX1ZFUlNJT04AAAAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwApTFdUX09QVElNSVpBVElPTl9NRVRBX0JJVF9NQVNLPTIxNDc0ODM2NDgAF1NDWUxMQV9SQVRFX0xJTUlUX0VSUk9SAAAAElRBQkxFVFNfUk9VVElOR19WMQAA"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlNuYLG5"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAABGAAAAQklOU0VSVCBJTlRPIHNpbmdsZV9jb25uX2JlbmNoLnRhYmxlMSAocGssIGNrLCB2KSBWQUxVRVMgKD8sID8sID8pOw=="} {"stream_id":320,"data":"BAABQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAAAAAAQAAAAAAAAABk5hbWVfMAAAE4gABiZTbmC4rg=="} {"stream_id":384,"data":"BAABgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAQAAAAQAAAABAAAABk5hbWVfMQAAE4gABiZTbmC66A=="} {"stream_id":448,"data":"BAABwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAgAAAAQAAAACAAAABk5hbWVfMgAAE4gABiZTbmC8vA=="} {"stream_id":512,"data":"BAACAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAwAAAAQAAAADAAAABk5hbWVfMwAAE4gABiZTbmC/ZA=="} {"stream_id":576,"data":"BAACQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABAAAAAQAAAAEAAAABk5hbWVfNAAAE4gABiZTbmDB5Q=="} {"stream_id":640,"data":"BAACgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABQAAAAQAAAAAAAAABk5hbWVfNQAAE4gABiZTbmDD+g=="} {"stream_id":704,"data":"BAACwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABgAAAAQAAAABAAAABk5hbWVfNgAAE4gABiZTbmDGLA=="} {"stream_id":768,"data":"BAADAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABwAAAAQAAAACAAAABk5hbWVfNwAAE4gABiZTbmDIiw=="} {"stream_id":832,"data":"BAADQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACAAAAAQAAAADAAAABk5hbWVfOAAAE4gABiZTbmDLFw=="} {"stream_id":896,"data":"BAADgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACQAAAAQAAAAEAAAABk5hbWVfOQAAE4gABiZTbmDNFw=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAElRBQkxFVFNfUk9VVElOR19WMQAAAAtDUUxfVkVSU0lPTgAFMy4wLjAAC0RSSVZFUl9OQU1FABVTY3lsbGFEQiBHb0NRTCBEcml2ZXIADkRSSVZFUl9WRVJTSU9OAAAAHFNDWUxMQV9MV1RfQUREX01FVEFEQVRBX01BUksAKUxXVF9PUFRJTUlaQVRJT05fTUVUQV9CSVRfTUFTSz0yMTQ3NDgzNjQ4ABdTQ1lMTEFfUkFURV9MSU1JVF9FUlJPUgAA"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlN2m+QB"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAABGAAAAQklOU0VSVCBJTlRPIHNpbmdsZV9jb25uX2JlbmNoLnRhYmxlMSAocGssIGNrLCB2KSBWQUxVRVMgKD8sID8sID8pOw=="} {"stream_id":320,"data":"BAABQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAAAAAAQAAAAAAAAABk5hbWVfMAAAE4gABiZTdpvzYw=="} {"stream_id":384,"data":"BAABgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAQAAAAQAAAABAAAABk5hbWVfMQAAE4gABiZTdpv3Ag=="} {"stream_id":448,"data":"BAABwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAgAAAAQAAAACAAAABk5hbWVfMgAAE4gABiZTdpv5hg=="} {"stream_id":512,"data":"BAACAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAwAAAAQAAAADAAAABk5hbWVfMwAAE4gABiZTdpv9WQ=="} {"stream_id":576,"data":"BAACQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABAAAAAQAAAAEAAAABk5hbWVfNAAAE4gABiZTdpwB2A=="} {"stream_id":640,"data":"BAACgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABQAAAAQAAAAAAAAABk5hbWVfNQAAE4gABiZTdpwFlQ=="} {"stream_id":704,"data":"BAACwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABgAAAAQAAAABAAAABk5hbWVfNgAAE4gABiZTdpwITg=="} {"stream_id":768,"data":"BAADAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABwAAAAQAAAACAAAABk5hbWVfNwAAE4gABiZTdpwMUA=="} {"stream_id":832,"data":"BAADQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACAAAAAQAAAADAAAABk5hbWVfOAAAE4gABiZTdpwQ/Q=="} {"stream_id":896,"data":"BAADgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACQAAAAQAAAAEAAAABk5hbWVfOQAAE4gABiZTdpwVHA=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAC0NRTF9WRVJTSU9OAAUzLjAuMAALRFJJVkVSX05BTUUAFVNjeWxsYURCIEdvQ1FMIERyaXZlcgAORFJJVkVSX1ZFUlNJT04AAAAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwApTFdUX09QVElNSVpBVElPTl9NRVRBX0JJVF9NQVNLPTIxNDc0ODM2NDgAF1NDWUxMQV9SQVRFX0xJTUlUX0VSUk9SAAAAElRBQkxFVFNfUk9VVElOR19WMQAA"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlN+WsOm"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAABGAAAAQklOU0VSVCBJTlRPIHNpbmdsZV9jb25uX2JlbmNoLnRhYmxlMSAocGssIGNrLCB2KSBWQUxVRVMgKD8sID8sID8pOw=="} {"stream_id":320,"data":"BAABQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAAAAAAQAAAAAAAAABk5hbWVfMAAAE4gABiZTflrNKg=="} {"stream_id":384,"data":"BAABgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAQAAAAQAAAABAAAABk5hbWVfMQAAE4gABiZTflrQRw=="} {"stream_id":448,"data":"BAABwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAgAAAAQAAAACAAAABk5hbWVfMgAAE4gABiZTflrTKw=="} {"stream_id":512,"data":"BAACAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAwAAAAQAAAADAAAABk5hbWVfMwAAE4gABiZTflrVnQ=="} {"stream_id":576,"data":"BAACQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABAAAAAQAAAAEAAAABk5hbWVfNAAAE4gABiZTflrXjg=="} {"stream_id":640,"data":"BAACgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABQAAAAQAAAAAAAAABk5hbWVfNQAAE4gABiZTflrZsQ=="} {"stream_id":704,"data":"BAACwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABgAAAAQAAAABAAAABk5hbWVfNgAAE4gABiZTflrb1A=="} {"stream_id":768,"data":"BAADAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABwAAAAQAAAACAAAABk5hbWVfNwAAE4gABiZTflrd6Q=="} {"stream_id":832,"data":"BAADQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACAAAAAQAAAADAAAABk5hbWVfOAAAE4gABiZTflrgWg=="} {"stream_id":896,"data":"BAADgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACQAAAAQAAAAEAAAABk5hbWVfOQAAE4gABiZTflrkxA=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAC0NRTF9WRVJTSU9OAAUzLjAuMAALRFJJVkVSX05BTUUAFVNjeWxsYURCIEdvQ1FMIERyaXZlcgAORFJJVkVSX1ZFUlNJT04AAAAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwApTFdUX09QVElNSVpBVElPTl9NRVRBX0JJVF9NQVNLPTIxNDc0ODM2NDgAF1NDWUxMQV9SQVRFX0xJTUlUX0VSUk9SAAAAElRBQkxFVFNfUk9VVElOR19WMQAA"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlSFSYWe"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAABGAAAAQklOU0VSVCBJTlRPIHNpbmdsZV9jb25uX2JlbmNoLnRhYmxlMSAocGssIGNrLCB2KSBWQUxVRVMgKD8sID8sID8pOw=="} {"stream_id":320,"data":"BAABQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAAAAAAQAAAAAAAAABk5hbWVfMAAAE4gABiZUhUmJIw=="} {"stream_id":384,"data":"BAABgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAQAAAAQAAAABAAAABk5hbWVfMQAAE4gABiZUhUmKjg=="} {"stream_id":448,"data":"BAABwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAgAAAAQAAAACAAAABk5hbWVfMgAAE4gABiZUhUmLMg=="} {"stream_id":512,"data":"BAACAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAwAAAAQAAAADAAAABk5hbWVfMwAAE4gABiZUhUmMAg=="} {"stream_id":576,"data":"BAACQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABAAAAAQAAAAEAAAABk5hbWVfNAAAE4gABiZUhUmM0g=="} {"stream_id":640,"data":"BAACgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABQAAAAQAAAAAAAAABk5hbWVfNQAAE4gABiZUhUmNgQ=="} {"stream_id":704,"data":"BAACwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABgAAAAQAAAABAAAABk5hbWVfNgAAE4gABiZUhUmOQA=="} {"stream_id":768,"data":"BAADAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABwAAAAQAAAACAAAABk5hbWVfNwAAE4gABiZUhUmO7g=="} {"stream_id":832,"data":"BAADQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACAAAAAQAAAADAAAABk5hbWVfOAAAE4gABiZUhUmP3g=="} {"stream_id":896,"data":"BAADgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACQAAAAQAAAAEAAAABk5hbWVfOQAAE4gABiZUhUmQkQ=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAC0NRTF9WRVJTSU9OAAUzLjAuMAALRFJJVkVSX05BTUUAFVNjeWxsYURCIEdvQ1FMIERyaXZlcgAORFJJVkVSX1ZFUlNJT04AAAAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwApTFdUX09QVElNSVpBVElPTl9NRVRBX0JJVF9NQVNLPTIxNDc0ODM2NDgAF1NDWUxMQV9SQVRFX0xJTUlUX0VSUk9SAAAAElRBQkxFVFNfUk9VVElOR19WMQAA"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlSPLfhY"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAABGAAAAQklOU0VSVCBJTlRPIHNpbmdsZV9jb25uX2JlbmNoLnRhYmxlMSAocGssIGNrLCB2KSBWQUxVRVMgKD8sID8sID8pOw=="} {"stream_id":320,"data":"BAABQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAAAAAAQAAAAAAAAABk5hbWVfMAAAE4gABiZUjy383g=="} {"stream_id":384,"data":"BAABgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAQAAAAQAAAABAAAABk5hbWVfMQAAE4gABiZUjy3+Gg=="} {"stream_id":448,"data":"BAABwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAgAAAAQAAAACAAAABk5hbWVfMgAAE4gABiZUjy3/PQ=="} {"stream_id":512,"data":"BAACAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAwAAAAQAAAADAAAABk5hbWVfMwAAE4gABiZUjy4AJQ=="} {"stream_id":576,"data":"BAACQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABAAAAAQAAAAEAAAABk5hbWVfNAAAE4gABiZUjy4BQw=="} {"stream_id":640,"data":"BAACgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABQAAAAQAAAAAAAAABk5hbWVfNQAAE4gABiZUjy4Cqg=="} {"stream_id":704,"data":"BAACwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABgAAAAQAAAABAAAABk5hbWVfNgAAE4gABiZUjy4D5g=="} {"stream_id":768,"data":"BAADAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABwAAAAQAAAACAAAABk5hbWVfNwAAE4gABiZUjy4FJQ=="} {"stream_id":832,"data":"BAADQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACAAAAAQAAAADAAAABk5hbWVfOAAAE4gABiZUjy4Hxw=="} {"stream_id":896,"data":"BAADgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACQAAAAQAAAAEAAAABk5hbWVfOQAAE4gABiZUjy4I9Q=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAC0NRTF9WRVJTSU9OAAUzLjAuMAALRFJJVkVSX05BTUUAFVNjeWxsYURCIEdvQ1FMIERyaXZlcgAORFJJVkVSX1ZFUlNJT04AAAAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwApTFdUX09QVElNSVpBVElPTl9NRVRBX0JJVF9NQVNLPTIxNDc0ODM2NDgAF1NDWUxMQV9SQVRFX0xJTUlUX0VSUk9SAAAAElRBQkxFVFNfUk9VVElOR19WMQAA"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlSSrE+n"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAABGAAAAQklOU0VSVCBJTlRPIHNpbmdsZV9jb25uX2JlbmNoLnRhYmxlMSAocGssIGNrLCB2KSBWQUxVRVMgKD8sID8sID8pOw=="} {"stream_id":320,"data":"BAABQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAAAAAAQAAAAAAAAABk5hbWVfMAAAE4gABiZUkqxSYQ=="} {"stream_id":384,"data":"BAABgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAQAAAAQAAAABAAAABk5hbWVfMQAAE4gABiZUkqxTKQ=="} {"stream_id":448,"data":"BAABwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAgAAAAQAAAACAAAABk5hbWVfMgAAE4gABiZUkqxTyg=="} {"stream_id":512,"data":"BAACAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAwAAAAQAAAADAAAABk5hbWVfMwAAE4gABiZUkqxUaA=="} {"stream_id":576,"data":"BAACQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABAAAAAQAAAAEAAAABk5hbWVfNAAAE4gABiZUkqxVAQ=="} {"stream_id":640,"data":"BAACgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABQAAAAQAAAAAAAAABk5hbWVfNQAAE4gABiZUkqxVlA=="} {"stream_id":704,"data":"BAACwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABgAAAAQAAAABAAAABk5hbWVfNgAAE4gABiZUkqxWKg=="} {"stream_id":768,"data":"BAADAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABwAAAAQAAAACAAAABk5hbWVfNwAAE4gABiZUkqxWvA=="} {"stream_id":832,"data":"BAADQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACAAAAAQAAAADAAAABk5hbWVfOAAAE4gABiZUkqxXTQ=="} {"stream_id":896,"data":"BAADgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACQAAAAQAAAAEAAAABk5hbWVfOQAAE4gABiZUkqxX7A=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAC0NRTF9WRVJTSU9OAAUzLjAuMAALRFJJVkVSX05BTUUAFVNjeWxsYURCIEdvQ1FMIERyaXZlcgAORFJJVkVSX1ZFUlNJT04AAAAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwApTFdUX09QVElNSVpBVElPTl9NRVRBX0JJVF9NQVNLPTIxNDc0ODM2NDgAF1NDWUxMQV9SQVRFX0xJTUlUX0VSUk9SAAAAElRBQkxFVFNfUk9VVElOR19WMQAA"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlSX9kQB"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAABGAAAAQklOU0VSVCBJTlRPIHNpbmdsZV9jb25uX2JlbmNoLnRhYmxlMSAocGssIGNrLCB2KSBWQUxVRVMgKD8sID8sID8pOw=="} {"stream_id":320,"data":"BAABQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAAAAAAQAAAAAAAAABk5hbWVfMAAAE4gABiZUl/ZIsQ=="} {"stream_id":384,"data":"BAABgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAQAAAAQAAAABAAAABk5hbWVfMQAAE4gABiZUl/ZKKA=="} {"stream_id":448,"data":"BAABwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAgAAAAQAAAACAAAABk5hbWVfMgAAE4gABiZUl/ZLIA=="} {"stream_id":512,"data":"BAACAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAwAAAAQAAAADAAAABk5hbWVfMwAAE4gABiZUl/ZNeA=="} {"stream_id":576,"data":"BAACQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABAAAAAQAAAAEAAAABk5hbWVfNAAAE4gABiZUl/ZOyg=="} {"stream_id":640,"data":"BAACgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABQAAAAQAAAAAAAAABk5hbWVfNQAAE4gABiZUl/ZP3Q=="} {"stream_id":704,"data":"BAACwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABgAAAAQAAAABAAAABk5hbWVfNgAAE4gABiZUl/ZRhw=="} {"stream_id":768,"data":"BAADAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABwAAAAQAAAACAAAABk5hbWVfNwAAE4gABiZUl/ZSiA=="} {"stream_id":832,"data":"BAADQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACAAAAAQAAAADAAAABk5hbWVfOAAAE4gABiZUl/ZTTQ=="} {"stream_id":896,"data":"BAADgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACQAAAAQAAAAEAAAABk5hbWVfOQAAE4gABiZUl/ZUNw=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAC0RSSVZFUl9OQU1FABVTY3lsbGFEQiBHb0NRTCBEcml2ZXIADkRSSVZFUl9WRVJTSU9OAAAAHFNDWUxMQV9MV1RfQUREX01FVEFEQVRBX01BUksAKUxXVF9PUFRJTUlaQVRJT05fTUVUQV9CSVRfTUFTSz0yMTQ3NDgzNjQ4ABdTQ1lMTEFfUkFURV9MSU1JVF9FUlJPUgAAABJUQUJMRVRTX1JPVVRJTkdfVjEAAAALQ1FMX1ZFUlNJT04ABTMuMC4w"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlSgyAkQ"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAABGAAAAQklOU0VSVCBJTlRPIHNpbmdsZV9jb25uX2JlbmNoLnRhYmxlMSAocGssIGNrLCB2KSBWQUxVRVMgKD8sID8sID8pOw=="} {"stream_id":320,"data":"BAABQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAAAAAAQAAAAAAAAABk5hbWVfMAAAE4gABiZUoMgMEA=="} {"stream_id":384,"data":"BAABgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAQAAAAQAAAABAAAABk5hbWVfMQAAE4gABiZUoMgNwQ=="} {"stream_id":448,"data":"BAABwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAgAAAAQAAAACAAAABk5hbWVfMgAAE4gABiZUoMgOeg=="} {"stream_id":512,"data":"BAACAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAwAAAAQAAAADAAAABk5hbWVfMwAAE4gABiZUoMgPng=="} {"stream_id":576,"data":"BAACQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABAAAAAQAAAAEAAAABk5hbWVfNAAAE4gABiZUoMgQWQ=="} {"stream_id":640,"data":"BAACgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABQAAAAQAAAAAAAAABk5hbWVfNQAAE4gABiZUoMgRDw=="} {"stream_id":704,"data":"BAACwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABgAAAAQAAAABAAAABk5hbWVfNgAAE4gABiZUoMgR1Q=="} {"stream_id":768,"data":"BAADAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABwAAAAQAAAACAAAABk5hbWVfNwAAE4gABiZUoMgSpw=="} {"stream_id":832,"data":"BAADQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACAAAAAQAAAADAAAABk5hbWVfOAAAE4gABiZUoMgTog=="} {"stream_id":896,"data":"BAADgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACQAAAAQAAAAEAAAABk5hbWVfOQAAE4gABiZUoMgU+g=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAF1NDWUxMQV9SQVRFX0xJTUlUX0VSUk9SAAAAElRBQkxFVFNfUk9VVElOR19WMQAAAAtDUUxfVkVSU0lPTgAFMy4wLjAAC0RSSVZFUl9OQU1FABVTY3lsbGFEQiBHb0NRTCBEcml2ZXIADkRSSVZFUl9WRVJTSU9OAAAAHFNDWUxMQV9MV1RfQUREX01FVEFEQVRBX01BUksAKUxXVF9PUFRJTUlaQVRJT05fTUVUQV9CSVRfTUFTSz0yMTQ3NDgzNjQ4"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlSrWjpD"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAABGAAAAQklOU0VSVCBJTlRPIHNpbmdsZV9jb25uX2JlbmNoLnRhYmxlMSAocGssIGNrLCB2KSBWQUxVRVMgKD8sID8sID8pOw=="} {"stream_id":320,"data":"BAABQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAAAAAAQAAAAAAAAABk5hbWVfMAAAE4gABiZUq1o+Bw=="} {"stream_id":384,"data":"BAABgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAQAAAAQAAAABAAAABk5hbWVfMQAAE4gABiZUq1o/Qg=="} {"stream_id":448,"data":"BAABwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAgAAAAQAAAACAAAABk5hbWVfMgAAE4gABiZUq1pADA=="} {"stream_id":512,"data":"BAACAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAwAAAAQAAAADAAAABk5hbWVfMwAAE4gABiZUq1pA3w=="} {"stream_id":576,"data":"BAACQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABAAAAAQAAAAEAAAABk5hbWVfNAAAE4gABiZUq1pBig=="} {"stream_id":640,"data":"BAACgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABQAAAAQAAAAAAAAABk5hbWVfNQAAE4gABiZUq1pCOw=="} {"stream_id":704,"data":"BAACwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABgAAAAQAAAABAAAABk5hbWVfNgAAE4gABiZUq1pC+g=="} {"stream_id":768,"data":"BAADAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABwAAAAQAAAACAAAABk5hbWVfNwAAE4gABiZUq1pDkg=="} {"stream_id":832,"data":"BAADQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACAAAAAQAAAADAAAABk5hbWVfOAAAE4gABiZUq1pEGg=="} {"stream_id":896,"data":"BAADgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACQAAAAQAAAAEAAAABk5hbWVfOQAAE4gABiZUq1pEmg=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYADkRSSVZFUl9WRVJTSU9OAAAAHFNDWUxMQV9MV1RfQUREX01FVEFEQVRBX01BUksAKUxXVF9PUFRJTUlaQVRJT05fTUVUQV9CSVRfTUFTSz0yMTQ3NDgzNjQ4ABdTQ1lMTEFfUkFURV9MSU1JVF9FUlJPUgAAABJUQUJMRVRTX1JPVVRJTkdfVjEAAAALQ1FMX1ZFUlNJT04ABTMuMC4wAAtEUklWRVJfTkFNRQAVU2N5bGxhREIgR29DUUwgRHJpdmVy"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlSxQVBh"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAABGAAAAQklOU0VSVCBJTlRPIHNpbmdsZV9jb25uX2JlbmNoLnRhYmxlMSAocGssIGNrLCB2KSBWQUxVRVMgKD8sID8sID8pOw=="} {"stream_id":320,"data":"BAABQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAAAAAAQAAAAAAAAABk5hbWVfMAAAE4gABiZUsUFTig=="} {"stream_id":384,"data":"BAABgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAQAAAAQAAAABAAAABk5hbWVfMQAAE4gABiZUsUFUcg=="} {"stream_id":448,"data":"BAABwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAgAAAAQAAAACAAAABk5hbWVfMgAAE4gABiZUsUFVMg=="} {"stream_id":512,"data":"BAACAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAwAAAAQAAAADAAAABk5hbWVfMwAAE4gABiZUsUFV+Q=="} {"stream_id":576,"data":"BAACQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABAAAAAQAAAAEAAAABk5hbWVfNAAAE4gABiZUsUFWsQ=="} {"stream_id":640,"data":"BAACgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABQAAAAQAAAAAAAAABk5hbWVfNQAAE4gABiZUsUFXXw=="} {"stream_id":704,"data":"BAACwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABgAAAAQAAAABAAAABk5hbWVfNgAAE4gABiZUsUFYDA=="} {"stream_id":768,"data":"BAADAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABwAAAAQAAAACAAAABk5hbWVfNwAAE4gABiZUsUFYwA=="} {"stream_id":832,"data":"BAADQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACAAAAAQAAAADAAAABk5hbWVfOAAAE4gABiZUsUFZaA=="} {"stream_id":896,"data":"BAADgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACQAAAAQAAAAEAAAABk5hbWVfOQAAE4gABiZUsUFaDA=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAF1NDWUxMQV9SQVRFX0xJTUlUX0VSUk9SAAAAElRBQkxFVFNfUk9VVElOR19WMQAAAAtDUUxfVkVSU0lPTgAFMy4wLjAAC0RSSVZFUl9OQU1FABVTY3lsbGFEQiBHb0NRTCBEcml2ZXIADkRSSVZFUl9WRVJTSU9OAAAAHFNDWUxMQV9MV1RfQUREX01FVEFEQVRBX01BUksAKUxXVF9PUFRJTUlaQVRJT05fTUVUQV9CSVRfTUFTSz0yMTQ3NDgzNjQ4"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlS1dFJz"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAABGAAAAQklOU0VSVCBJTlRPIHNpbmdsZV9jb25uX2JlbmNoLnRhYmxlMSAocGssIGNrLCB2KSBWQUxVRVMgKD8sID8sID8pOw=="} {"stream_id":320,"data":"BAABQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAAAAAAQAAAAAAAAABk5hbWVfMAAAE4gABiZUtXRVRw=="} {"stream_id":384,"data":"BAABgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAQAAAAQAAAABAAAABk5hbWVfMQAAE4gABiZUtXRWKA=="} {"stream_id":448,"data":"BAABwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAgAAAAQAAAACAAAABk5hbWVfMgAAE4gABiZUtXRW9Q=="} {"stream_id":512,"data":"BAACAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAwAAAAQAAAADAAAABk5hbWVfMwAAE4gABiZUtXRY/g=="} {"stream_id":576,"data":"BAACQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABAAAAAQAAAAEAAAABk5hbWVfNAAAE4gABiZUtXRZ8g=="} {"stream_id":640,"data":"BAACgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABQAAAAQAAAAAAAAABk5hbWVfNQAAE4gABiZUtXRc7g=="} {"stream_id":704,"data":"BAACwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABgAAAAQAAAABAAAABk5hbWVfNgAAE4gABiZUtXRd0A=="} {"stream_id":768,"data":"BAADAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABwAAAAQAAAACAAAABk5hbWVfNwAAE4gABiZUtXRemw=="} {"stream_id":832,"data":"BAADQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACAAAAAQAAAADAAAABk5hbWVfOAAAE4gABiZUtXRfQQ=="} {"stream_id":896,"data":"BAADgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACQAAAAQAAAAEAAAABk5hbWVfOQAAE4gABiZUtXRhPg=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYADkRSSVZFUl9WRVJTSU9OAAAAHFNDWUxMQV9MV1RfQUREX01FVEFEQVRBX01BUksAKUxXVF9PUFRJTUlaQVRJT05fTUVUQV9CSVRfTUFTSz0yMTQ3NDgzNjQ4ABdTQ1lMTEFfUkFURV9MSU1JVF9FUlJPUgAAABJUQUJMRVRTX1JPVVRJTkdfVjEAAAALQ1FMX1ZFUlNJT04ABTMuMC4wAAtEUklWRVJfTkFNRQAVU2N5bGxhREIgR29DUUwgRHJpdmVy"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlS9RuYh"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAABGAAAAQklOU0VSVCBJTlRPIHNpbmdsZV9jb25uX2JlbmNoLnRhYmxlMSAocGssIGNrLCB2KSBWQUxVRVMgKD8sID8sID8pOw=="} {"stream_id":320,"data":"BAABQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAAAAAAQAAAAAAAAABk5hbWVfMAAAE4gABiZUvUbotg=="} {"stream_id":384,"data":"BAABgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAQAAAAQAAAABAAAABk5hbWVfMQAAE4gABiZUvUbpew=="} {"stream_id":448,"data":"BAABwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAgAAAAQAAAACAAAABk5hbWVfMgAAE4gABiZUvUbqOw=="} {"stream_id":512,"data":"BAACAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAwAAAAQAAAADAAAABk5hbWVfMwAAE4gABiZUvUbq6A=="} {"stream_id":576,"data":"BAACQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABAAAAAQAAAAEAAAABk5hbWVfNAAAE4gABiZUvUbrfg=="} {"stream_id":640,"data":"BAACgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABQAAAAQAAAAAAAAABk5hbWVfNQAAE4gABiZUvUbsEQ=="} {"stream_id":704,"data":"BAACwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABgAAAAQAAAABAAAABk5hbWVfNgAAE4gABiZUvUbssQ=="} {"stream_id":768,"data":"BAADAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABwAAAAQAAAACAAAABk5hbWVfNwAAE4gABiZUvUbtRg=="} {"stream_id":832,"data":"BAADQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACAAAAAQAAAADAAAABk5hbWVfOAAAE4gABiZUvUbt3w=="} {"stream_id":896,"data":"BAADgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACQAAAAQAAAAEAAAABk5hbWVfOQAAE4gABiZUvUbucQ=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAF1NDWUxMQV9SQVRFX0xJTUlUX0VSUk9SAAAAElRBQkxFVFNfUk9VVElOR19WMQAAAAtDUUxfVkVSU0lPTgAFMy4wLjAAC0RSSVZFUl9OQU1FABVTY3lsbGFEQiBHb0NRTCBEcml2ZXIADkRSSVZFUl9WRVJTSU9OAAAAHFNDWUxMQV9MV1RfQUREX01FVEFEQVRBX01BUksAKUxXVF9PUFRJTUlaQVRJT05fTUVUQV9CSVRfTUFTSz0yMTQ3NDgzNjQ4"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlS/6guU"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAABGAAAAQklOU0VSVCBJTlRPIHNpbmdsZV9jb25uX2JlbmNoLnRhYmxlMSAocGssIGNrLCB2KSBWQUxVRVMgKD8sID8sID8pOw=="} {"stream_id":320,"data":"BAABQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAAAAAAQAAAAAAAAABk5hbWVfMAAAE4gABiZUv+oZhg=="} {"stream_id":384,"data":"BAABgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAQAAAAQAAAABAAAABk5hbWVfMQAAE4gABiZUv+ofUQ=="} {"stream_id":448,"data":"BAABwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAgAAAAQAAAACAAAABk5hbWVfMgAAE4gABiZUv+okCg=="} {"stream_id":512,"data":"BAACAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAAAwAAAAQAAAADAAAABk5hbWVfMwAAE4gABiZUv+orXA=="} {"stream_id":576,"data":"BAACQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABAAAAAQAAAAEAAAABk5hbWVfNAAAE4gABiZUv+ozZg=="} {"stream_id":640,"data":"BAACgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABQAAAAQAAAAAAAAABk5hbWVfNQAAE4gABiZUv+o7MA=="} {"stream_id":704,"data":"BAACwAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABgAAAAQAAAABAAAABk5hbWVfNgAAE4gABiZUv+o9OA=="} {"stream_id":768,"data":"BAADAAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAABwAAAAQAAAACAAAABk5hbWVfNwAAE4gABiZUv+o/7g=="} {"stream_id":832,"data":"BAADQAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACAAAAAQAAAADAAAABk5hbWVfOAAAE4gABiZUv+pCqw=="} {"stream_id":896,"data":"BAADgAoAAAA9ABAee7k0vsrctagGJPd0oWL1AAElAAMAAAAEAAAACQAAAAQAAAAEAAAABk5hbWVfOQAAE4gABiZUv+pE+g=="} ================================================ FILE: tests/bench/rec_select/192.168.100.11:9042-0Reads ================================================ {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABCR5FsunQ0R7+r+IQBJDGRzAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABtAAAABAAQqkp70wQOeh4OLIlDiVT63wAAAAEAAAABAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAAAAAAAAAEAEXNpbmdsZV9jb25uX2JlbmNoAAZ0YWJsZTEAAXYADQ=="} {"stream_id":320,"data":"hAABQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMA=="} {"stream_id":384,"data":"hAABgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMQ=="} {"stream_id":448,"data":"hAABwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMg=="} {"stream_id":512,"data":"hAACAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMw=="} {"stream_id":576,"data":"hAACQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNA=="} {"stream_id":640,"data":"hAACgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNQ=="} {"stream_id":704,"data":"hAACwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNg=="} {"stream_id":768,"data":"hAADAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNw=="} {"stream_id":832,"data":"hAADQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOA=="} {"stream_id":896,"data":"hAADgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABDu2cDanQ0R7wuYHo4KKzsUAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABtAAAABAAQqkp70wQOeh4OLIlDiVT63wAAAAEAAAABAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAAAAAAAAAEAEXNpbmdsZV9jb25uX2JlbmNoAAZ0YWJsZTEAAXYADQ=="} {"stream_id":320,"data":"hAABQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMA=="} {"stream_id":384,"data":"hAABgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMQ=="} {"stream_id":448,"data":"hAABwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMg=="} {"stream_id":512,"data":"hAACAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMw=="} {"stream_id":576,"data":"hAACQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNA=="} {"stream_id":640,"data":"hAACgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNQ=="} {"stream_id":704,"data":"hAACwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNg=="} {"stream_id":768,"data":"hAADAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNw=="} {"stream_id":832,"data":"hAADQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOA=="} {"stream_id":896,"data":"hAADgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABAInmaEnRIR7y3XxXaiIG8xAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABtAAAABAAQqkp70wQOeh4OLIlDiVT63wAAAAEAAAABAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAAAAAAAAAEAEXNpbmdsZV9jb25uX2JlbmNoAAZ0YWJsZTEAAXYADQ=="} {"stream_id":320,"data":"hAABQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMA=="} {"stream_id":384,"data":"hAABgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMQ=="} {"stream_id":448,"data":"hAABwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMg=="} {"stream_id":512,"data":"hAACAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMw=="} {"stream_id":576,"data":"hAACQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNA=="} {"stream_id":640,"data":"hAACgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNQ=="} {"stream_id":704,"data":"hAACwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNg=="} {"stream_id":768,"data":"hAADAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNw=="} {"stream_id":832,"data":"hAADQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOA=="} {"stream_id":896,"data":"hAADgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABAEFnxKnRMR73PNIzDsRApMAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABtAAAABAAQqkp70wQOeh4OLIlDiVT63wAAAAEAAAABAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAAAAAAAAAEAEXNpbmdsZV9jb25uX2JlbmNoAAZ0YWJsZTEAAXYADQ=="} {"stream_id":320,"data":"hAABQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMA=="} {"stream_id":384,"data":"hAABgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMQ=="} {"stream_id":448,"data":"hAABwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMg=="} {"stream_id":512,"data":"hAACAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMw=="} {"stream_id":576,"data":"hAACQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNA=="} {"stream_id":640,"data":"hAACgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNQ=="} {"stream_id":704,"data":"hAACwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNg=="} {"stream_id":768,"data":"hAADAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNw=="} {"stream_id":832,"data":"hAADQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOA=="} {"stream_id":896,"data":"hAADgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABAb5TUUnRMR717g5pvokC/kAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABtAAAABAAQqkp70wQOeh4OLIlDiVT63wAAAAEAAAABAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAAAAAAAAAEAEXNpbmdsZV9jb25uX2JlbmNoAAZ0YWJsZTEAAXYADQ=="} {"stream_id":320,"data":"hAABQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMA=="} {"stream_id":384,"data":"hAABgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMQ=="} {"stream_id":448,"data":"hAABwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMg=="} {"stream_id":512,"data":"hAACAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMw=="} {"stream_id":576,"data":"hAACQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNA=="} {"stream_id":640,"data":"hAACgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNQ=="} {"stream_id":704,"data":"hAACwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNg=="} {"stream_id":768,"data":"hAADAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNw=="} {"stream_id":832,"data":"hAADQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOA=="} {"stream_id":896,"data":"hAADgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABAxAe3ynRMR766bODzKESb3AAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABtAAAABAAQqkp70wQOeh4OLIlDiVT63wAAAAEAAAABAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAAAAAAAAAEAEXNpbmdsZV9jb25uX2JlbmNoAAZ0YWJsZTEAAXYADQ=="} {"stream_id":320,"data":"hAABQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMA=="} {"stream_id":384,"data":"hAABgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMQ=="} {"stream_id":448,"data":"hAABwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMg=="} {"stream_id":512,"data":"hAACAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMw=="} {"stream_id":576,"data":"hAACQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNA=="} {"stream_id":640,"data":"hAACgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNQ=="} {"stream_id":704,"data":"hAACwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNg=="} {"stream_id":768,"data":"hAADAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNw=="} {"stream_id":832,"data":"hAADQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOA=="} {"stream_id":896,"data":"hAADgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABCND7fSnRMR7xa0C3LDZrpzAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABtAAAABAAQqkp70wQOeh4OLIlDiVT63wAAAAEAAAABAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAAAAAAAAAEAEXNpbmdsZV9jb25uX2JlbmNoAAZ0YWJsZTEAAXYADQ=="} {"stream_id":320,"data":"hAABQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMA=="} {"stream_id":384,"data":"hAABgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMQ=="} {"stream_id":448,"data":"hAABwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMg=="} {"stream_id":512,"data":"hAACAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMw=="} {"stream_id":576,"data":"hAACQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNA=="} {"stream_id":640,"data":"hAACgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNQ=="} {"stream_id":704,"data":"hAACwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNg=="} {"stream_id":768,"data":"hAADAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNw=="} {"stream_id":832,"data":"hAADQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOA=="} {"stream_id":896,"data":"hAADgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABCxO0fonRMR73j1wwJQHdWrAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABtAAAABAAQqkp70wQOeh4OLIlDiVT63wAAAAEAAAABAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAAAAAAAAAEAEXNpbmdsZV9jb25uX2JlbmNoAAZ0YWJsZTEAAXYADQ=="} {"stream_id":320,"data":"hAABQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMA=="} {"stream_id":384,"data":"hAABgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMQ=="} {"stream_id":448,"data":"hAABwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMg=="} {"stream_id":512,"data":"hAACAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMw=="} {"stream_id":576,"data":"hAACQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNA=="} {"stream_id":640,"data":"hAACgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNQ=="} {"stream_id":704,"data":"hAACwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNg=="} {"stream_id":768,"data":"hAADAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNw=="} {"stream_id":832,"data":"hAADQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOA=="} {"stream_id":896,"data":"hAADgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABDvMx4YnRMR76NfXEL1hL0aAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABtAAAABAAQqkp70wQOeh4OLIlDiVT63wAAAAEAAAABAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAAAAAAAAAEAEXNpbmdsZV9jb25uX2JlbmNoAAZ0YWJsZTEAAXYADQ=="} {"stream_id":320,"data":"hAABQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMA=="} {"stream_id":384,"data":"hAABgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMQ=="} {"stream_id":448,"data":"hAABwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMg=="} {"stream_id":512,"data":"hAACAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMw=="} {"stream_id":576,"data":"hAACQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNA=="} {"stream_id":640,"data":"hAACgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNQ=="} {"stream_id":704,"data":"hAACwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNg=="} {"stream_id":768,"data":"hAADAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNw=="} {"stream_id":832,"data":"hAADQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOA=="} {"stream_id":896,"data":"hAADgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABBD0FVsnRQR7zNx10SKYLAmAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABtAAAABAAQqkp70wQOeh4OLIlDiVT63wAAAAEAAAABAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAAAAAAAAAEAEXNpbmdsZV9jb25uX2JlbmNoAAZ0YWJsZTEAAXYADQ=="} {"stream_id":320,"data":"hAABQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMA=="} {"stream_id":384,"data":"hAABgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMQ=="} {"stream_id":448,"data":"hAABwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMg=="} {"stream_id":512,"data":"hAACAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMw=="} {"stream_id":576,"data":"hAACQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNA=="} {"stream_id":640,"data":"hAACgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNQ=="} {"stream_id":704,"data":"hAACwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNg=="} {"stream_id":768,"data":"hAADAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNw=="} {"stream_id":832,"data":"hAADQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOA=="} {"stream_id":896,"data":"hAADgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABBjRpfanRQR73UZ2qUmi5vUAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABtAAAABAAQqkp70wQOeh4OLIlDiVT63wAAAAEAAAABAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAAAAAAAAAEAEXNpbmdsZV9jb25uX2JlbmNoAAZ0YWJsZTEAAXYADQ=="} {"stream_id":320,"data":"hAABQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMA=="} {"stream_id":384,"data":"hAABgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMQ=="} {"stream_id":448,"data":"hAABwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMg=="} {"stream_id":512,"data":"hAACAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMw=="} {"stream_id":576,"data":"hAACQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNA=="} {"stream_id":640,"data":"hAACgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNQ=="} {"stream_id":704,"data":"hAACwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNg=="} {"stream_id":768,"data":"hAADAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNw=="} {"stream_id":832,"data":"hAADQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOA=="} {"stream_id":896,"data":"hAADgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABC1lYC0nRQR74XiOWDrg7z6AAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABtAAAABAAQqkp70wQOeh4OLIlDiVT63wAAAAEAAAABAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAAAAAAAAAEAEXNpbmdsZV9jb25uX2JlbmNoAAZ0YWJsZTEAAXYADQ=="} {"stream_id":320,"data":"hAABQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMA=="} {"stream_id":384,"data":"hAABgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMQ=="} {"stream_id":448,"data":"hAABwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMg=="} {"stream_id":512,"data":"hAACAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMw=="} {"stream_id":576,"data":"hAACQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNA=="} {"stream_id":640,"data":"hAACgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNQ=="} {"stream_id":704,"data":"hAACwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNg=="} {"stream_id":768,"data":"hAADAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNw=="} {"stream_id":832,"data":"hAADQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOA=="} {"stream_id":896,"data":"hAADgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABADC04KnRUR74eTL5lMntPqAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABtAAAABAAQqkp70wQOeh4OLIlDiVT63wAAAAEAAAABAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAAAAAAAAAEAEXNpbmdsZV9jb25uX2JlbmNoAAZ0YWJsZTEAAXYADQ=="} {"stream_id":320,"data":"hAABQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMA=="} {"stream_id":384,"data":"hAABgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMQ=="} {"stream_id":448,"data":"hAABwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMg=="} {"stream_id":512,"data":"hAACAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMw=="} {"stream_id":576,"data":"hAACQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNA=="} {"stream_id":640,"data":"hAACgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNQ=="} {"stream_id":704,"data":"hAACwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNg=="} {"stream_id":768,"data":"hAADAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNw=="} {"stream_id":832,"data":"hAADQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOA=="} {"stream_id":896,"data":"hAADgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABBIX9jMnR8R77IoLTLjoWSOAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABtAAAABAAQqkp70wQOeh4OLIlDiVT63wAAAAEAAAABAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAAAAAAAAAEAEXNpbmdsZV9jb25uX2JlbmNoAAZ0YWJsZTEAAXYADQ=="} {"stream_id":320,"data":"hAABQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMA=="} {"stream_id":384,"data":"hAABgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMQ=="} {"stream_id":448,"data":"hAABwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMg=="} {"stream_id":512,"data":"hAACAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMw=="} {"stream_id":576,"data":"hAACQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNA=="} {"stream_id":640,"data":"hAACgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNQ=="} {"stream_id":704,"data":"hAACwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNg=="} {"stream_id":768,"data":"hAADAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNw=="} {"stream_id":832,"data":"hAADQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOA=="} {"stream_id":896,"data":"hAADgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABCrS9PmnR8R73xgJaYWggcvAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABtAAAABAAQqkp70wQOeh4OLIlDiVT63wAAAAEAAAABAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAAAAAAAAAEAEXNpbmdsZV9jb25uX2JlbmNoAAZ0YWJsZTEAAXYADQ=="} {"stream_id":320,"data":"hAABQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMA=="} {"stream_id":384,"data":"hAABgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMQ=="} {"stream_id":448,"data":"hAABwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMg=="} {"stream_id":512,"data":"hAACAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMw=="} {"stream_id":576,"data":"hAACQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNA=="} {"stream_id":640,"data":"hAACgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNQ=="} {"stream_id":704,"data":"hAACwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNg=="} {"stream_id":768,"data":"hAADAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNw=="} {"stream_id":832,"data":"hAADQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOA=="} {"stream_id":896,"data":"hAADgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABDOO9JmnR8R7+ZrEBJHQVPvAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABtAAAABAAQqkp70wQOeh4OLIlDiVT63wAAAAEAAAABAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAAAAAAAAAEAEXNpbmdsZV9jb25uX2JlbmNoAAZ0YWJsZTEAAXYADQ=="} {"stream_id":320,"data":"hAABQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMA=="} {"stream_id":384,"data":"hAABgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMQ=="} {"stream_id":448,"data":"hAABwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMg=="} {"stream_id":512,"data":"hAACAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMw=="} {"stream_id":576,"data":"hAACQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNA=="} {"stream_id":640,"data":"hAACgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNQ=="} {"stream_id":704,"data":"hAACwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNg=="} {"stream_id":768,"data":"hAADAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNw=="} {"stream_id":832,"data":"hAADQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOA=="} {"stream_id":896,"data":"hAADgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABADHwB6nSAR76qRwPvy1BuoAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABtAAAABAAQqkp70wQOeh4OLIlDiVT63wAAAAEAAAABAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAAAAAAAAAEAEXNpbmdsZV9jb25uX2JlbmNoAAZ0YWJsZTEAAXYADQ=="} {"stream_id":320,"data":"hAABQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMA=="} {"stream_id":384,"data":"hAABgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMQ=="} {"stream_id":448,"data":"hAABwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMg=="} {"stream_id":512,"data":"hAACAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMw=="} {"stream_id":576,"data":"hAACQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNA=="} {"stream_id":640,"data":"hAACgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNQ=="} {"stream_id":704,"data":"hAACwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNg=="} {"stream_id":768,"data":"hAADAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNw=="} {"stream_id":832,"data":"hAADQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOA=="} {"stream_id":896,"data":"hAADgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABBbUOgSnSAR7xG1Imw8wjVzAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABtAAAABAAQqkp70wQOeh4OLIlDiVT63wAAAAEAAAABAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAAAAAAAAAEAEXNpbmdsZV9jb25uX2JlbmNoAAZ0YWJsZTEAAXYADQ=="} {"stream_id":320,"data":"hAABQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMA=="} {"stream_id":384,"data":"hAABgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMQ=="} {"stream_id":448,"data":"hAABwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMg=="} {"stream_id":512,"data":"hAACAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMw=="} {"stream_id":576,"data":"hAACQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNA=="} {"stream_id":640,"data":"hAACgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNQ=="} {"stream_id":704,"data":"hAACwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNg=="} {"stream_id":768,"data":"hAADAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNw=="} {"stream_id":832,"data":"hAADQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOA=="} {"stream_id":896,"data":"hAADgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABDFBq2MnSAR7xkFw6f0PNUWAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABtAAAABAAQqkp70wQOeh4OLIlDiVT63wAAAAEAAAABAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAAAAAAAAAEAEXNpbmdsZV9jb25uX2JlbmNoAAZ0YWJsZTEAAXYADQ=="} {"stream_id":320,"data":"hAABQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMA=="} {"stream_id":384,"data":"hAABgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMQ=="} {"stream_id":448,"data":"hAABwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMg=="} {"stream_id":512,"data":"hAACAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMw=="} {"stream_id":576,"data":"hAACQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNA=="} {"stream_id":640,"data":"hAACgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNQ=="} {"stream_id":704,"data":"hAACwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNg=="} {"stream_id":768,"data":"hAADAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNw=="} {"stream_id":832,"data":"hAADQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOA=="} {"stream_id":896,"data":"hAADgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABAADcsinSER7wlwGUhVdvaXAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABtAAAABAAQqkp70wQOeh4OLIlDiVT63wAAAAEAAAABAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAAAAAAAAAEAEXNpbmdsZV9jb25uX2JlbmNoAAZ0YWJsZTEAAXYADQ=="} {"stream_id":320,"data":"hAABQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMA=="} {"stream_id":384,"data":"hAABgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMQ=="} {"stream_id":448,"data":"hAABwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMg=="} {"stream_id":512,"data":"hAACAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMw=="} {"stream_id":576,"data":"hAACQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNA=="} {"stream_id":640,"data":"hAACgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNQ=="} {"stream_id":704,"data":"hAACwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNg=="} {"stream_id":768,"data":"hAADAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNw=="} {"stream_id":832,"data":"hAADQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOA=="} {"stream_id":896,"data":"hAADgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABAqC91qnSER70M1po5LqfR8AAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABtAAAABAAQqkp70wQOeh4OLIlDiVT63wAAAAEAAAABAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAAAAAAAAAEAEXNpbmdsZV9jb25uX2JlbmNoAAZ0YWJsZTEAAXYADQ=="} {"stream_id":320,"data":"hAABQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMA=="} {"stream_id":384,"data":"hAABgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMQ=="} {"stream_id":448,"data":"hAABwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMg=="} {"stream_id":512,"data":"hAACAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMw=="} {"stream_id":576,"data":"hAACQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNA=="} {"stream_id":640,"data":"hAACgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNQ=="} {"stream_id":704,"data":"hAACwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNg=="} {"stream_id":768,"data":"hAADAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNw=="} {"stream_id":832,"data":"hAADQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOA=="} {"stream_id":896,"data":"hAADgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABB4RbfknSER73p3mZ3s7fVcAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABtAAAABAAQqkp70wQOeh4OLIlDiVT63wAAAAEAAAABAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAAAAAAAAAEAEXNpbmdsZV9jb25uX2JlbmNoAAZ0YWJsZTEAAXYADQ=="} {"stream_id":320,"data":"hAABQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMA=="} {"stream_id":384,"data":"hAABgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMQ=="} {"stream_id":448,"data":"hAABwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMg=="} {"stream_id":512,"data":"hAACAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMw=="} {"stream_id":576,"data":"hAACQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNA=="} {"stream_id":640,"data":"hAACgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNQ=="} {"stream_id":704,"data":"hAACwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNg=="} {"stream_id":768,"data":"hAADAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNw=="} {"stream_id":832,"data":"hAADQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOA=="} {"stream_id":896,"data":"hAADgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOQ=="} {"stream_id":1,"data":"hAAAAQYAAAHSAAwAC0NPTVBSRVNTSU9OAAIAA2x6NAAGc25hcHB5AAtDUUxfVkVSU0lPTgABAAUzLjMuMQAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwABAClMV1RfT1BUSU1JWkFUSU9OX01FVEFfQklUX01BU0s9MjE0NzQ4MzY0OAAQU0NZTExBX05SX1NIQVJEUwABAAEyABJTQ1lMTEFfUEFSVElUSU9ORVIAAQArb3JnLmFwYWNoZS5jYXNzYW5kcmEuZGh0Lk11cm11cjNQYXJ0aXRpb25lcgAXU0NZTExBX1JBVEVfTElNSVRfRVJST1IAAQAQRVJST1JfQ09ERT02MTQ0MAAMU0NZTExBX1NIQVJEAAEAATAAGVNDWUxMQV9TSEFSRElOR19BTEdPUklUSE0AAQAYYmlhc2VkLXRva2VuLXJvdW5kLXJvYmluABpTQ1lMTEFfU0hBUkRJTkdfSUdOT1JFX01TQgABAAIxMgAXU0NZTExBX1NIQVJEX0FXQVJFX1BPUlQAAQAFMTkwNDIAG1NDWUxMQV9TSEFSRF9BV0FSRV9QT1JUX1NTTAABAAUxOTE0MgASVEFCTEVUU19ST1VUSU5HX1YxAAEAAA=="} {"stream_id":64,"data":"hAAAQAIAAAAA"} {"stream_id":128,"data":"hAAAgAgAAAf3AAAAAgAAAAAAAAASAAZzeXN0ZW0ABWxvY2FsAANrZXkADQAGc3lzdGVtAAVsb2NhbAAMYm9vdHN0cmFwcGVkAA0ABnN5c3RlbQAFbG9jYWwAEWJyb2FkY2FzdF9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADGNsdXN0ZXJfbmFtZQANAAZzeXN0ZW0ABWxvY2FsAAtjcWxfdmVyc2lvbgANAAZzeXN0ZW0ABWxvY2FsAAtkYXRhX2NlbnRlcgANAAZzeXN0ZW0ABWxvY2FsABFnb3NzaXBfZ2VuZXJhdGlvbgAJAAZzeXN0ZW0ABWxvY2FsAAdob3N0X2lkAAwABnN5c3RlbQAFbG9jYWwADmxpc3Rlbl9hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwAF25hdGl2ZV9wcm90b2NvbF92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3BhcnRpdGlvbmVyAA0ABnN5c3RlbQAFbG9jYWwABHJhY2sADQAGc3lzdGVtAAVsb2NhbAAPcmVsZWFzZV92ZXJzaW9uAA0ABnN5c3RlbQAFbG9jYWwAC3JwY19hZGRyZXNzABAABnN5c3RlbQAFbG9jYWwADnNjaGVtYV92ZXJzaW9uAAwABnN5c3RlbQAFbG9jYWwAEnN1cHBvcnRlZF9mZWF0dXJlcwANAAZzeXN0ZW0ABWxvY2FsAAZ0b2tlbnMAIgANAAZzeXN0ZW0ABWxvY2FsAAx0cnVuY2F0ZWRfYXQAIQAMAAMAAAABAAAABWxvY2FsAAAACUNPTVBMRVRFRAAAAATAqGQLAAAAAAAAAAUzLjMuMQAAAAtkYXRhY2VudGVyMQAAAARnLL6fAAAAEA9mbU1OFUflmhq/DEnf3pkAAAAEwKhkCwAAAAE0AAAAK29yZy5hcGFjaGUuY2Fzc2FuZHJhLmRodC5NdXJtdXIzUGFydGl0aW9uZXIAAAAFcmFjazEAAAAFMy4wLjgAAAAEwKhkCwAAABCSpEDinSER7zv6d1XvBXKLAAAEyUFHR1JFR0FURV9TVE9SQUdFX09QVElPTlMsQUxURVJOQVRPUl9UVEwsQ0RDLENEQ19HRU5FUkFUSU9OU19WMixDT0xMRUNUSU9OX0lOREVYSU5HLENPTVBVVEVEX0NPTFVNTlMsQ09SUkVDVF9DT1VOVEVSX09SREVSLENPUlJFQ1RfSURYX1RPS0VOX0lOX1NFQ09OREFSWV9JTkRFWCxDT1JSRUNUX05PTl9DT01QT1VORF9SQU5HRV9UT01CU1RPTkVTLENPUlJFQ1RfU1RBVElDX0NPTVBBQ1RfSU5fTUMsQ09VTlRFUlMsRElHRVNUX0ZPUl9OVUxMX1ZBTFVFUyxESUdFU1RfSU5TRU5TSVRJVkVfVE9fRVhQSVJZLERJR0VTVF9NVUxUSVBBUlRJVElPTl9SRUFELEVNUFRZX1JFUExJQ0FfTVVUQVRJT05fUEFHRVMsRU1QVFlfUkVQTElDQV9QQUdFUyxHUk9VUDBfU0NIRU1BX1ZFUlNJT05JTkcsSElOVEVEX0hBTkRPRkZfU0VQQVJBVEVfQ09OTkVDVElPTixIT1NUX0lEX0JBU0VEX0hJTlRFRF9IQU5ET0ZGLElOREVYRVMsTEFSR0VfQ09MTEVDVElPTl9ERVRFQ1RJT04sTEFSR0VfUEFSVElUSU9OUyxMQV9TU1RBQkxFX0ZPUk1BVCxMV1QsTUFURVJJQUxJWkVEX1ZJRVdTLE1DX1NTVEFCTEVfRk9STUFULE1EX1NTVEFCTEVfRk9STUFULE1FX1NTVEFCTEVfRk9STUFULE5PTkZST1pFTl9VRFRTLFBBUkFMTEVMSVpFRF9BR0dSRUdBVElPTixQRVJfVEFCTEVfQ0FDSElORyxQRVJfVEFCTEVfUEFSVElUSU9ORVJTLFJBTkdFX1NDQU5fREFUQV9WQVJJQU5ULFJBTkdFX1RPTUJTVE9ORVMsUkFOR0VfVE9NQlNUT05FX0FORF9ERUFEX1JPV1NfREVURUNUSU9OLFJPTEVTLFJPV19MRVZFTF9SRVBBSVIsU0NIRU1BX0NPTU1JVExPRyxTQ0hFTUFfVEFCTEVTX1YzLFNFQ09OREFSWV9JTkRFWEVTX09OX1NUQVRJQ19DT0xVTU5TLFNFUEFSQVRFX1BBR0VfU0laRV9BTkRfU0FGRVRZX0xJTUlULFNUUkVBTV9XSVRIX1JQQ19TVFJFQU0sU1VQUE9SVFNfQ09OU0lTVEVOVF9UT1BPTE9HWV9DSEFOR0VTLFNVUFBPUlRTX1JBRlRfQ0xVU1RFUl9NQU5BR0VNRU5ULFRBQkxFX0RJR0VTVF9JTlNFTlNJVElWRV9UT19FWFBJUlksVE9NQlNUT05FX0dDX09QVElPTlMsVFJVTkNBVElPTl9UQUJMRSxUWVBFRF9FUlJPUlNfSU5fUkVBRF9SUEMsVURBLFVEQV9OQVRJVkVfUEFSQUxMRUxJWkVEX0FHR1JFR0FUSU9OLFVERixVTkJPVU5ERURfUkFOR0VfVE9NQlNUT05FUyxVVUlEX1NTVEFCTEVfSURFTlRJRklFUlMsVklFV19WSVJUVUFMX0NPTFVNTlMsV1JJVEVfRkFJTFVSRV9SRVBMWSxYWEhBU0gAAAAcAAAAAQAAABQtNDI0MjU1ODEzNTU4OTc4OTE0M/////8="} {"stream_id":192,"data":"hAAAwAIAAAAA"} {"stream_id":256,"data":"hAABAAgAAABtAAAABAAQqkp70wQOeh4OLIlDiVT63wAAAAEAAAABAAAAAQAAABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAJwawAJAAAAAAAAAAEAEXNpbmdsZV9jb25uX2JlbmNoAAZ0YWJsZTEAAXYADQ=="} {"stream_id":320,"data":"hAABQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMA=="} {"stream_id":384,"data":"hAABgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMQ=="} {"stream_id":448,"data":"hAABwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMg=="} {"stream_id":512,"data":"hAACAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfMw=="} {"stream_id":576,"data":"hAACQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNA=="} {"stream_id":640,"data":"hAACgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNQ=="} {"stream_id":704,"data":"hAACwAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNg=="} {"stream_id":768,"data":"hAADAAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfNw=="} {"stream_id":832,"data":"hAADQAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOA=="} {"stream_id":896,"data":"hAADgAgAAAA6AAAAAgAAAAAAAAABABFzaW5nbGVfY29ubl9iZW5jaAAGdGFibGUxAAF2AA0AAAABAAAABk5hbWVfOQ=="} ================================================ FILE: tests/bench/rec_select/192.168.100.11:9042-0Writes ================================================ {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYADkRSSVZFUl9WRVJTSU9OAAAAHFNDWUxMQV9MV1RfQUREX01FVEFEQVRBX01BUksAKUxXVF9PUFRJTUlaQVRJT05fTUVUQV9CSVRfTUFTSz0yMTQ3NDgzNjQ4ABdTQ1lMTEFfUkFURV9MSU1JVF9FUlJPUgAAABJUQUJMRVRTX1JPVVRJTkdfVjEAAAALQ1FMX1ZFUlNJT04ABTMuMC4wAAtEUklWRVJfTkFNRQAVU2N5bGxhREIgR29DUUwgRHJpdmVy"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlK/1wHd"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAAA4AAAANFNFTEVDVCB2IEZST00gc2luZ2xlX2Nvbm5fYmVuY2gudGFibGUxIFdIRVJFIHBrID0gPzs="} {"stream_id":320,"data":"BAABQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAAAAE4gABiZSv9cJhg=="} {"stream_id":384,"data":"BAABgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAQAAE4gABiZSv9cLoA=="} {"stream_id":448,"data":"BAABwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAgAAE4gABiZSv9cNCQ=="} {"stream_id":512,"data":"BAACAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAwAAE4gABiZSv9cPsg=="} {"stream_id":576,"data":"BAACQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABAAAE4gABiZSv9cScQ=="} {"stream_id":640,"data":"BAACgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABQAAE4gABiZSv9cUrg=="} {"stream_id":704,"data":"BAACwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABgAAE4gABiZSv9cWfA=="} {"stream_id":768,"data":"BAADAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABwAAE4gABiZSv9cY2A=="} {"stream_id":832,"data":"BAADQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACAAAE4gABiZSv9cbLA=="} {"stream_id":896,"data":"BAADgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACQAAE4gABiZSv9cdFA=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAHFNDWUxMQV9MV1RfQUREX01FVEFEQVRBX01BUksAKUxXVF9PUFRJTUlaQVRJT05fTUVUQV9CSVRfTUFTSz0yMTQ3NDgzNjQ4ABdTQ1lMTEFfUkFURV9MSU1JVF9FUlJPUgAAABJUQUJMRVRTX1JPVVRJTkdfVjEAAAALQ1FMX1ZFUlNJT04ABTMuMC4wAAtEUklWRVJfTkFNRQAVU2N5bGxhREIgR29DUUwgRHJpdmVyAA5EUklWRVJfVkVSU0lPTgAA"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlLJIujg"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAAA4AAAANFNFTEVDVCB2IEZST00gc2luZ2xlX2Nvbm5fYmVuY2gudGFibGUxIFdIRVJFIHBrID0gPzs="} {"stream_id":320,"data":"BAABQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAAAAE4gABiZSySLzLA=="} {"stream_id":384,"data":"BAABgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAQAAE4gABiZSySL2SQ=="} {"stream_id":448,"data":"BAABwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAgAAE4gABiZSySL5dQ=="} {"stream_id":512,"data":"BAACAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAwAAE4gABiZSySL8Vg=="} {"stream_id":576,"data":"BAACQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABAAAE4gABiZSySL/vg=="} {"stream_id":640,"data":"BAACgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABQAAE4gABiZSySMCsw=="} {"stream_id":704,"data":"BAACwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABgAAE4gABiZSySMGKA=="} {"stream_id":768,"data":"BAADAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABwAAE4gABiZSySMJtQ=="} {"stream_id":832,"data":"BAADQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACAAAE4gABiZSySMNcQ=="} {"stream_id":896,"data":"BAADgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACQAAE4gABiZSySMQGw=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAC0NRTF9WRVJTSU9OAAUzLjAuMAALRFJJVkVSX05BTUUAFVNjeWxsYURCIEdvQ1FMIERyaXZlcgAORFJJVkVSX1ZFUlNJT04AAAAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwApTFdUX09QVElNSVpBVElPTl9NRVRBX0JJVF9NQVNLPTIxNDc0ODM2NDgAF1NDWUxMQV9SQVRFX0xJTUlUX0VSUk9SAAAAElRBQkxFVFNfUk9VVElOR19WMQAA"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlMyHNa+"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAAA4AAAANFNFTEVDVCB2IEZST00gc2luZ2xlX2Nvbm5fYmVuY2gudGFibGUxIFdIRVJFIHBrID0gPzs="} {"stream_id":320,"data":"BAABQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAAAAE4gABiZTMhzh5Q=="} {"stream_id":384,"data":"BAABgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAQAAE4gABiZTMhzkoA=="} {"stream_id":448,"data":"BAABwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAgAAE4gABiZTMhznXA=="} {"stream_id":512,"data":"BAACAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAwAAE4gABiZTMhzqyw=="} {"stream_id":576,"data":"BAACQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABAAAE4gABiZTMhzuDw=="} {"stream_id":640,"data":"BAACgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABQAAE4gABiZTMhzxSg=="} {"stream_id":704,"data":"BAACwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABgAAE4gABiZTMhz23Q=="} {"stream_id":768,"data":"BAADAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABwAAE4gABiZTMhz6aw=="} {"stream_id":832,"data":"BAADQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACAAAE4gABiZTMhz+RA=="} {"stream_id":896,"data":"BAADgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACQAAE4gABiZTMh0BLg=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAElRBQkxFVFNfUk9VVElOR19WMQAAAAtDUUxfVkVSU0lPTgAFMy4wLjAAC0RSSVZFUl9OQU1FABVTY3lsbGFEQiBHb0NRTCBEcml2ZXIADkRSSVZFUl9WRVJTSU9OAAAAHFNDWUxMQV9MV1RfQUREX01FVEFEQVRBX01BUksAKUxXVF9PUFRJTUlaQVRJT05fTUVUQV9CSVRfTUFTSz0yMTQ3NDgzNjQ4ABdTQ1lMTEFfUkFURV9MSU1JVF9FUlJPUgAA"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlNLQr3A"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAAA4AAAANFNFTEVDVCB2IEZST00gc2luZ2xlX2Nvbm5fYmVuY2gudGFibGUxIFdIRVJFIHBrID0gPzs="} {"stream_id":320,"data":"BAABQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAAAAE4gABiZTS0LPsg=="} {"stream_id":384,"data":"BAABgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAQAAE4gABiZTS0LUmg=="} {"stream_id":448,"data":"BAABwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAgAAE4gABiZTS0LYsA=="} {"stream_id":512,"data":"BAACAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAwAAE4gABiZTS0Ld3Q=="} {"stream_id":576,"data":"BAACQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABAAAE4gABiZTS0LimA=="} {"stream_id":640,"data":"BAACgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABQAAE4gABiZTS0LnSg=="} {"stream_id":704,"data":"BAACwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABgAAE4gABiZTS0LrUg=="} {"stream_id":768,"data":"BAADAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABwAAE4gABiZTS0Lupg=="} {"stream_id":832,"data":"BAADQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACAAAE4gABiZTS0LzLw=="} {"stream_id":896,"data":"BAADgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACQAAE4gABiZTS0L4rg=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYADkRSSVZFUl9WRVJTSU9OAAAAHFNDWUxMQV9MV1RfQUREX01FVEFEQVRBX01BUksAKUxXVF9PUFRJTUlaQVRJT05fTUVUQV9CSVRfTUFTSz0yMTQ3NDgzNjQ4ABdTQ1lMTEFfUkFURV9MSU1JVF9FUlJPUgAAABJUQUJMRVRTX1JPVVRJTkdfVjEAAAALQ1FMX1ZFUlNJT04ABTMuMC4wAAtEUklWRVJfTkFNRQAVU2N5bGxhREIgR29DUUwgRHJpdmVy"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlNNo9bE"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAAA4AAAANFNFTEVDVCB2IEZST00gc2luZ2xlX2Nvbm5fYmVuY2gudGFibGUxIFdIRVJFIHBrID0gPzs="} {"stream_id":320,"data":"BAABQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAAAAE4gABiZTTaPc2A=="} {"stream_id":384,"data":"BAABgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAQAAE4gABiZTTaPeyw=="} {"stream_id":448,"data":"BAABwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAgAAE4gABiZTTaPgwg=="} {"stream_id":512,"data":"BAACAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAwAAE4gABiZTTaPirQ=="} {"stream_id":576,"data":"BAACQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABAAAE4gABiZTTaPkUA=="} {"stream_id":640,"data":"BAACgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABQAAE4gABiZTTaPl6Q=="} {"stream_id":704,"data":"BAACwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABgAAE4gABiZTTaPnXQ=="} {"stream_id":768,"data":"BAADAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABwAAE4gABiZTTaPo8Q=="} {"stream_id":832,"data":"BAADQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACAAAE4gABiZTTaPqYg=="} {"stream_id":896,"data":"BAADgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACQAAE4gABiZTTaPsAA=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAF1NDWUxMQV9SQVRFX0xJTUlUX0VSUk9SAAAAElRBQkxFVFNfUk9VVElOR19WMQAAAAtDUUxfVkVSU0lPTgAFMy4wLjAAC0RSSVZFUl9OQU1FABVTY3lsbGFEQiBHb0NRTCBEcml2ZXIADkRSSVZFUl9WRVJTSU9OAAAAHFNDWUxMQV9MV1RfQUREX01FVEFEQVRBX01BUksAKUxXVF9PUFRJTUlaQVRJT05fTUVUQV9CSVRfTUFTSz0yMTQ3NDgzNjQ4"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlNPwKVZ"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAAA4AAAANFNFTEVDVCB2IEZST00gc2luZ2xlX2Nvbm5fYmVuY2gudGFibGUxIFdIRVJFIHBrID0gPzs="} {"stream_id":320,"data":"BAABQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAAAAE4gABiZTT8CyRg=="} {"stream_id":384,"data":"BAABgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAQAAE4gABiZTT8C1bw=="} {"stream_id":448,"data":"BAABwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAgAAE4gABiZTT8C38Q=="} {"stream_id":512,"data":"BAACAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAwAAE4gABiZTT8C/ow=="} {"stream_id":576,"data":"BAACQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABAAAE4gABiZTT8DJuw=="} {"stream_id":640,"data":"BAACgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABQAAE4gABiZTT8DNaw=="} {"stream_id":704,"data":"BAACwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABgAAE4gABiZTT8DR1Q=="} {"stream_id":768,"data":"BAADAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABwAAE4gABiZTT8DWKQ=="} {"stream_id":832,"data":"BAADQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACAAAE4gABiZTT8DaSw=="} {"stream_id":896,"data":"BAADgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACQAAE4gABiZTT8DdLg=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAC0NRTF9WRVJTSU9OAAUzLjAuMAALRFJJVkVSX05BTUUAFVNjeWxsYURCIEdvQ1FMIERyaXZlcgAORFJJVkVSX1ZFUlNJT04AAAAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwApTFdUX09QVElNSVpBVElPTl9NRVRBX0JJVF9NQVNLPTIxNDc0ODM2NDgAF1NDWUxMQV9SQVRFX0xJTUlUX0VSUk9SAAAAElRBQkxFVFNfUk9VVElOR19WMQAA"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlNY9Oej"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAAA4AAAANFNFTEVDVCB2IEZST00gc2luZ2xlX2Nvbm5fYmVuY2gudGFibGUxIFdIRVJFIHBrID0gPzs="} {"stream_id":320,"data":"BAABQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAAAAE4gABiZTWPTwFw=="} {"stream_id":384,"data":"BAABgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAQAAE4gABiZTWPTyWg=="} {"stream_id":448,"data":"BAABwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAgAAE4gABiZTWPT0QA=="} {"stream_id":512,"data":"BAACAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAwAAE4gABiZTWPT29g=="} {"stream_id":576,"data":"BAACQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABAAAE4gABiZTWPT5XA=="} {"stream_id":640,"data":"BAACgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABQAAE4gABiZTWPT7tw=="} {"stream_id":704,"data":"BAACwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABgAAE4gABiZTWPT93A=="} {"stream_id":768,"data":"BAADAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABwAAE4gABiZTWPT/rQ=="} {"stream_id":832,"data":"BAADQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACAAAE4gABiZTWPUBTw=="} {"stream_id":896,"data":"BAADgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACQAAE4gABiZTWPUCsQ=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYADkRSSVZFUl9WRVJTSU9OAAAAHFNDWUxMQV9MV1RfQUREX01FVEFEQVRBX01BUksAKUxXVF9PUFRJTUlaQVRJT05fTUVUQV9CSVRfTUFTSz0yMTQ3NDgzNjQ4ABdTQ1lMTEFfUkFURV9MSU1JVF9FUlJPUgAAABJUQUJMRVRTX1JPVVRJTkdfVjEAAAALQ1FMX1ZFUlNJT04ABTMuMC4wAAtEUklWRVJfTkFNRQAVU2N5bGxhREIgR29DUUwgRHJpdmVy"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlNckxhK"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAAA4AAAANFNFTEVDVCB2IEZST00gc2luZ2xlX2Nvbm5fYmVuY2gudGFibGUxIFdIRVJFIHBrID0gPzs="} {"stream_id":320,"data":"BAABQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAAAAE4gABiZTXJMlvg=="} {"stream_id":384,"data":"BAABgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAQAAE4gABiZTXJMpiw=="} {"stream_id":448,"data":"BAABwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAgAAE4gABiZTXJMs8A=="} {"stream_id":512,"data":"BAACAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAwAAE4gABiZTXJMx6Q=="} {"stream_id":576,"data":"BAACQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABAAAE4gABiZTXJM2Kw=="} {"stream_id":640,"data":"BAACgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABQAAE4gABiZTXJM6bA=="} {"stream_id":704,"data":"BAACwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABgAAE4gABiZTXJM/CQ=="} {"stream_id":768,"data":"BAADAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABwAAE4gABiZTXJNDYA=="} {"stream_id":832,"data":"BAADQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACAAAE4gABiZTXJNJKA=="} {"stream_id":896,"data":"BAADgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACQAAE4gABiZTXJNMRA=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAHFNDWUxMQV9MV1RfQUREX01FVEFEQVRBX01BUksAKUxXVF9PUFRJTUlaQVRJT05fTUVUQV9CSVRfTUFTSz0yMTQ3NDgzNjQ4ABdTQ1lMTEFfUkFURV9MSU1JVF9FUlJPUgAAABJUQUJMRVRTX1JPVVRJTkdfVjEAAAALQ1FMX1ZFUlNJT04ABTMuMC4wAAtEUklWRVJfTkFNRQAVU2N5bGxhREIgR29DUUwgRHJpdmVyAA5EUklWRVJfVkVSU0lPTgAA"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlNixW30"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAAA4AAAANFNFTEVDVCB2IEZST00gc2luZ2xlX2Nvbm5fYmVuY2gudGFibGUxIFdIRVJFIHBrID0gPzs="} {"stream_id":320,"data":"BAABQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAAAAE4gABiZTYsV4Dw=="} {"stream_id":384,"data":"BAABgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAQAAE4gABiZTYsV7Zg=="} {"stream_id":448,"data":"BAABwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAgAAE4gABiZTYsV9zA=="} {"stream_id":512,"data":"BAACAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAwAAE4gABiZTYsWFYw=="} {"stream_id":576,"data":"BAACQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABAAAE4gABiZTYsWO9w=="} {"stream_id":640,"data":"BAACgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABQAAE4gABiZTYsWRiw=="} {"stream_id":704,"data":"BAACwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABgAAE4gABiZTYsWU8w=="} {"stream_id":768,"data":"BAADAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABwAAE4gABiZTYsWX5g=="} {"stream_id":832,"data":"BAADQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACAAAE4gABiZTYsWbOA=="} {"stream_id":896,"data":"BAADgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACQAAE4gABiZTYsWd0w=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAC0NRTF9WRVJTSU9OAAUzLjAuMAALRFJJVkVSX05BTUUAFVNjeWxsYURCIEdvQ1FMIERyaXZlcgAORFJJVkVSX1ZFUlNJT04AAAAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwApTFdUX09QVElNSVpBVElPTl9NRVRBX0JJVF9NQVNLPTIxNDc0ODM2NDgAF1NDWUxMQV9SQVRFX0xJTUlUX0VSUk9SAAAAElRBQkxFVFNfUk9VVElOR19WMQAA"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlNrO3sz"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAAA4AAAANFNFTEVDVCB2IEZST00gc2luZ2xlX2Nvbm5fYmVuY2gudGFibGUxIFdIRVJFIHBrID0gPzs="} {"stream_id":320,"data":"BAABQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAAAAE4gABiZTazuEAw=="} {"stream_id":384,"data":"BAABgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAQAAE4gABiZTazuGiQ=="} {"stream_id":448,"data":"BAABwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAgAAE4gABiZTazuJJA=="} {"stream_id":512,"data":"BAACAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAwAAE4gABiZTazuL5w=="} {"stream_id":576,"data":"BAACQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABAAAE4gABiZTazuOzA=="} {"stream_id":640,"data":"BAACgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABQAAE4gABiZTazuR8g=="} {"stream_id":704,"data":"BAACwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABgAAE4gABiZTazuV2w=="} {"stream_id":768,"data":"BAADAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABwAAE4gABiZTazuYnA=="} {"stream_id":832,"data":"BAADQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACAAAE4gABiZTazuaVw=="} {"stream_id":896,"data":"BAADgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACQAAE4gABiZTazub6A=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAF1NDWUxMQV9SQVRFX0xJTUlUX0VSUk9SAAAAElRBQkxFVFNfUk9VVElOR19WMQAAAAtDUUxfVkVSU0lPTgAFMy4wLjAAC0RSSVZFUl9OQU1FABVTY3lsbGFEQiBHb0NRTCBEcml2ZXIADkRSSVZFUl9WRVJTSU9OAAAAHFNDWUxMQV9MV1RfQUREX01FVEFEQVRBX01BUksAKUxXVF9PUFRJTUlaQVRJT05fTUVUQV9CSVRfTUFTSz0yMTQ3NDgzNjQ4"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlNuYNWp"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAAA4AAAANFNFTEVDVCB2IEZST00gc2luZ2xlX2Nvbm5fYmVuY2gudGFibGUxIFdIRVJFIHBrID0gPzs="} {"stream_id":320,"data":"BAABQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAAAAE4gABiZTbmDdoA=="} {"stream_id":384,"data":"BAABgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAQAAE4gABiZTbmDf2g=="} {"stream_id":448,"data":"BAABwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAgAAE4gABiZTbmDhhw=="} {"stream_id":512,"data":"BAACAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAwAAE4gABiZTbmDjbQ=="} {"stream_id":576,"data":"BAACQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABAAAE4gABiZTbmDleQ=="} {"stream_id":640,"data":"BAACgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABQAAE4gABiZTbmDnGw=="} {"stream_id":704,"data":"BAACwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABgAAE4gABiZTbmDpEA=="} {"stream_id":768,"data":"BAADAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABwAAE4gABiZTbmDq9w=="} {"stream_id":832,"data":"BAADQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACAAAE4gABiZTbmDtVQ=="} {"stream_id":896,"data":"BAADgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACQAAE4gABiZTbmDv6Q=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAElRBQkxFVFNfUk9VVElOR19WMQAAAAtDUUxfVkVSU0lPTgAFMy4wLjAAC0RSSVZFUl9OQU1FABVTY3lsbGFEQiBHb0NRTCBEcml2ZXIADkRSSVZFUl9WRVJTSU9OAAAAHFNDWUxMQV9MV1RfQUREX01FVEFEQVRBX01BUksAKUxXVF9PUFRJTUlaQVRJT05fTUVUQV9CSVRfTUFTSz0yMTQ3NDgzNjQ4ABdTQ1lMTEFfUkFURV9MSU1JVF9FUlJPUgAA"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlN2nCMJ"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAAA4AAAANFNFTEVDVCB2IEZST00gc2luZ2xlX2Nvbm5fYmVuY2gudGFibGUxIFdIRVJFIHBrID0gPzs="} {"stream_id":320,"data":"BAABQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAAAAE4gABiZTdpwtsA=="} {"stream_id":384,"data":"BAABgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAQAAE4gABiZTdpwwcQ=="} {"stream_id":448,"data":"BAABwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAgAAE4gABiZTdpwyvA=="} {"stream_id":512,"data":"BAACAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAwAAE4gABiZTdpw1Zg=="} {"stream_id":576,"data":"BAACQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABAAAE4gABiZTdpw4AA=="} {"stream_id":640,"data":"BAACgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABQAAE4gABiZTdpw6+g=="} {"stream_id":704,"data":"BAACwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABgAAE4gABiZTdpw9Lg=="} {"stream_id":768,"data":"BAADAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABwAAE4gABiZTdpxAGQ=="} {"stream_id":832,"data":"BAADQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACAAAE4gABiZTdpxCTw=="} {"stream_id":896,"data":"BAADgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACQAAE4gABiZTdpxFIw=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAElRBQkxFVFNfUk9VVElOR19WMQAAAAtDUUxfVkVSU0lPTgAFMy4wLjAAC0RSSVZFUl9OQU1FABVTY3lsbGFEQiBHb0NRTCBEcml2ZXIADkRSSVZFUl9WRVJTSU9OAAAAHFNDWUxMQV9MV1RfQUREX01FVEFEQVRBX01BUksAKUxXVF9PUFRJTUlaQVRJT05fTUVUQV9CSVRfTUFTSz0yMTQ3NDgzNjQ4ABdTQ1lMTEFfUkFURV9MSU1JVF9FUlJPUgAA"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlN+Wu8/"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAAA4AAAANFNFTEVDVCB2IEZST00gc2luZ2xlX2Nvbm5fYmVuY2gudGFibGUxIFdIRVJFIHBrID0gPzs="} {"stream_id":320,"data":"BAABQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAAAAE4gABiZTflr6IQ=="} {"stream_id":384,"data":"BAABgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAQAAE4gABiZTflr8nA=="} {"stream_id":448,"data":"BAABwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAgAAE4gABiZTflr/AQ=="} {"stream_id":512,"data":"BAACAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAwAAE4gABiZTflsBuw=="} {"stream_id":576,"data":"BAACQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABAAAE4gABiZTflsEGA=="} {"stream_id":640,"data":"BAACgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABQAAE4gABiZTflsGFg=="} {"stream_id":704,"data":"BAACwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABgAAE4gABiZTflsICA=="} {"stream_id":768,"data":"BAADAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABwAAE4gABiZTflsKaw=="} {"stream_id":832,"data":"BAADQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACAAAE4gABiZTflsL+g=="} {"stream_id":896,"data":"BAADgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACQAAE4gABiZTflsNgg=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAC0NRTF9WRVJTSU9OAAUzLjAuMAALRFJJVkVSX05BTUUAFVNjeWxsYURCIEdvQ1FMIERyaXZlcgAORFJJVkVSX1ZFUlNJT04AAAAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwApTFdUX09QVElNSVpBVElPTl9NRVRBX0JJVF9NQVNLPTIxNDc0ODM2NDgAF1NDWUxMQV9SQVRFX0xJTUlUX0VSUk9SAAAAElRBQkxFVFNfUk9VVElOR19WMQAA"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlSFSZPF"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAAA4AAAANFNFTEVDVCB2IEZST00gc2luZ2xlX2Nvbm5fYmVuY2gudGFibGUxIFdIRVJFIHBrID0gPzs="} {"stream_id":320,"data":"BAABQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAAAAE4gABiZUhUmWTg=="} {"stream_id":384,"data":"BAABgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAQAAE4gABiZUhUmXBw=="} {"stream_id":448,"data":"BAABwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAgAAE4gABiZUhUmXlQ=="} {"stream_id":512,"data":"BAACAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAwAAE4gABiZUhUmYPg=="} {"stream_id":576,"data":"BAACQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABAAAE4gABiZUhUmY1Q=="} {"stream_id":640,"data":"BAACgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABQAAE4gABiZUhUmZZQ=="} {"stream_id":704,"data":"BAACwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABgAAE4gABiZUhUmaDw=="} {"stream_id":768,"data":"BAADAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABwAAE4gABiZUhUmang=="} {"stream_id":832,"data":"BAADQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACAAAE4gABiZUhUmbMA=="} {"stream_id":896,"data":"BAADgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACQAAE4gABiZUhUmbtQ=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAC0NRTF9WRVJTSU9OAAUzLjAuMAALRFJJVkVSX05BTUUAFVNjeWxsYURCIEdvQ1FMIERyaXZlcgAORFJJVkVSX1ZFUlNJT04AAAAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwApTFdUX09QVElNSVpBVElPTl9NRVRBX0JJVF9NQVNLPTIxNDc0ODM2NDgAF1NDWUxMQV9SQVRFX0xJTUlUX0VSUk9SAAAAElRBQkxFVFNfUk9VVElOR19WMQAA"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlSPLhBy"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAAA4AAAANFNFTEVDVCB2IEZST00gc2luZ2xlX2Nvbm5fYmVuY2gudGFibGUxIFdIRVJFIHBrID0gPzs="} {"stream_id":320,"data":"BAABQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAAAAE4gABiZUjy4W0A=="} {"stream_id":384,"data":"BAABgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAQAAE4gABiZUjy4YUA=="} {"stream_id":448,"data":"BAABwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAgAAE4gABiZUjy4ZWA=="} {"stream_id":512,"data":"BAACAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAwAAE4gABiZUjy4aiA=="} {"stream_id":576,"data":"BAACQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABAAAE4gABiZUjy4bZQ=="} {"stream_id":640,"data":"BAACgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABQAAE4gABiZUjy4cbQ=="} {"stream_id":704,"data":"BAACwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABgAAE4gABiZUjy4eAw=="} {"stream_id":768,"data":"BAADAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABwAAE4gABiZUjy4fTg=="} {"stream_id":832,"data":"BAADQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACAAAE4gABiZUjy4hhw=="} {"stream_id":896,"data":"BAADgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACQAAE4gABiZUjy4jJA=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAF1NDWUxMQV9SQVRFX0xJTUlUX0VSUk9SAAAAElRBQkxFVFNfUk9VVElOR19WMQAAAAtDUUxfVkVSU0lPTgAFMy4wLjAAC0RSSVZFUl9OQU1FABVTY3lsbGFEQiBHb0NRTCBEcml2ZXIADkRSSVZFUl9WRVJTSU9OAAAAHFNDWUxMQV9MV1RfQUREX01FVEFEQVRBX01BUksAKUxXVF9PUFRJTUlaQVRJT05fTUVUQV9CSVRfTUFTSz0yMTQ3NDgzNjQ4"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlSSrFr8"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAAA4AAAANFNFTEVDVCB2IEZST00gc2luZ2xlX2Nvbm5fYmVuY2gudGFibGUxIFdIRVJFIHBrID0gPzs="} {"stream_id":320,"data":"BAABQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAAAAE4gABiZUkqxdHA=="} {"stream_id":384,"data":"BAABgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAQAAE4gABiZUkqxduw=="} {"stream_id":448,"data":"BAABwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAgAAE4gABiZUkqxeRg=="} {"stream_id":512,"data":"BAACAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAwAAE4gABiZUkqxfBg=="} {"stream_id":576,"data":"BAACQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABAAAE4gABiZUkqxfoQ=="} {"stream_id":640,"data":"BAACgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABQAAE4gABiZUkqxgMA=="} {"stream_id":704,"data":"BAACwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABgAAE4gABiZUkqxgxA=="} {"stream_id":768,"data":"BAADAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABwAAE4gABiZUkqxhVw=="} {"stream_id":832,"data":"BAADQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACAAAE4gABiZUkqxh8A=="} {"stream_id":896,"data":"BAADgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACQAAE4gABiZUkqxifA=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYADkRSSVZFUl9WRVJTSU9OAAAAHFNDWUxMQV9MV1RfQUREX01FVEFEQVRBX01BUksAKUxXVF9PUFRJTUlaQVRJT05fTUVUQV9CSVRfTUFTSz0yMTQ3NDgzNjQ4ABdTQ1lMTEFfUkFURV9MSU1JVF9FUlJPUgAAABJUQUJMRVRTX1JPVVRJTkdfVjEAAAALQ1FMX1ZFUlNJT04ABTMuMC4wAAtEUklWRVJfTkFNRQAVU2N5bGxhREIgR29DUUwgRHJpdmVy"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlSX9lny"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAAA4AAAANFNFTEVDVCB2IEZST00gc2luZ2xlX2Nvbm5fYmVuY2gudGFibGUxIFdIRVJFIHBrID0gPzs="} {"stream_id":320,"data":"BAABQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAAAAE4gABiZUl/ZdvQ=="} {"stream_id":384,"data":"BAABgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAQAAE4gABiZUl/ZevQ=="} {"stream_id":448,"data":"BAABwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAgAAE4gABiZUl/ZfcQ=="} {"stream_id":512,"data":"BAACAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAwAAE4gABiZUl/Zh+w=="} {"stream_id":576,"data":"BAACQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABAAAE4gABiZUl/Zi3Q=="} {"stream_id":640,"data":"BAACgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABQAAE4gABiZUl/ZnXA=="} {"stream_id":704,"data":"BAACwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABgAAE4gABiZUl/Zoaw=="} {"stream_id":768,"data":"BAADAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABwAAE4gABiZUl/ZpKw=="} {"stream_id":832,"data":"BAADQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACAAAE4gABiZUl/ZqHg=="} {"stream_id":896,"data":"BAADgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACQAAE4gABiZUl/Zrig=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAC0NRTF9WRVJTSU9OAAUzLjAuMAALRFJJVkVSX05BTUUAFVNjeWxsYURCIEdvQ1FMIERyaXZlcgAORFJJVkVSX1ZFUlNJT04AAAAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwApTFdUX09QVElNSVpBVElPTl9NRVRBX0JJVF9NQVNLPTIxNDc0ODM2NDgAF1NDWUxMQV9SQVRFX0xJTUlUX0VSUk9SAAAAElRBQkxFVFNfUk9VVElOR19WMQAA"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlSgyBhC"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAAA4AAAANFNFTEVDVCB2IEZST00gc2luZ2xlX2Nvbm5fYmVuY2gudGFibGUxIFdIRVJFIHBrID0gPzs="} {"stream_id":320,"data":"BAABQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAAAAE4gABiZUoMgbag=="} {"stream_id":384,"data":"BAABgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAQAAE4gABiZUoMgcRA=="} {"stream_id":448,"data":"BAABwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAgAAE4gABiZUoMgdCA=="} {"stream_id":512,"data":"BAACAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAwAAE4gABiZUoMgdwg=="} {"stream_id":576,"data":"BAACQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABAAAE4gABiZUoMgeig=="} {"stream_id":640,"data":"BAACgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABQAAE4gABiZUoMgfSQ=="} {"stream_id":704,"data":"BAACwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABgAAE4gABiZUoMghKQ=="} {"stream_id":768,"data":"BAADAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABwAAE4gABiZUoMgiFw=="} {"stream_id":832,"data":"BAADQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACAAAE4gABiZUoMgjdw=="} {"stream_id":896,"data":"BAADgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACQAAE4gABiZUoMgkVg=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAC0NRTF9WRVJTSU9OAAUzLjAuMAALRFJJVkVSX05BTUUAFVNjeWxsYURCIEdvQ1FMIERyaXZlcgAORFJJVkVSX1ZFUlNJT04AAAAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwApTFdUX09QVElNSVpBVElPTl9NRVRBX0JJVF9NQVNLPTIxNDc0ODM2NDgAF1NDWUxMQV9SQVRFX0xJTUlUX0VSUk9SAAAAElRBQkxFVFNfUk9VVElOR19WMQAA"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlSrWkg6"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAAA4AAAANFNFTEVDVCB2IEZST00gc2luZ2xlX2Nvbm5fYmVuY2gudGFibGUxIFdIRVJFIHBrID0gPzs="} {"stream_id":320,"data":"BAABQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAAAAE4gABiZUq1pMFg=="} {"stream_id":384,"data":"BAABgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAQAAE4gABiZUq1pNEw=="} {"stream_id":448,"data":"BAABwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAgAAE4gABiZUq1pNyw=="} {"stream_id":512,"data":"BAACAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAwAAE4gABiZUq1pOpA=="} {"stream_id":576,"data":"BAACQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABAAAE4gABiZUq1pPnQ=="} {"stream_id":640,"data":"BAACgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABQAAE4gABiZUq1pQaw=="} {"stream_id":704,"data":"BAACwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABgAAE4gABiZUq1pRLA=="} {"stream_id":768,"data":"BAADAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABwAAE4gABiZUq1pR+w=="} {"stream_id":832,"data":"BAADQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACAAAE4gABiZUq1pStQ=="} {"stream_id":896,"data":"BAADgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACQAAE4gABiZUq1pTjA=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAC0NRTF9WRVJTSU9OAAUzLjAuMAALRFJJVkVSX05BTUUAFVNjeWxsYURCIEdvQ1FMIERyaXZlcgAORFJJVkVSX1ZFUlNJT04AAAAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwApTFdUX09QVElNSVpBVElPTl9NRVRBX0JJVF9NQVNLPTIxNDc0ODM2NDgAF1NDWUxMQV9SQVRFX0xJTUlUX0VSUk9SAAAAElRBQkxFVFNfUk9VVElOR19WMQAA"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlSxQV1z"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAAA4AAAANFNFTEVDVCB2IEZST00gc2luZ2xlX2Nvbm5fYmVuY2gudGFibGUxIFdIRVJFIHBrID0gPzs="} {"stream_id":320,"data":"BAABQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAAAAE4gABiZUsUFgFg=="} {"stream_id":384,"data":"BAABgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAQAAE4gABiZUsUFg4w=="} {"stream_id":448,"data":"BAABwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAgAAE4gABiZUsUFhkQ=="} {"stream_id":512,"data":"BAACAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAwAAE4gABiZUsUFiWg=="} {"stream_id":576,"data":"BAACQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABAAAE4gABiZUsUFi/Q=="} {"stream_id":640,"data":"BAACgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABQAAE4gABiZUsUFjkA=="} {"stream_id":704,"data":"BAACwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABgAAE4gABiZUsUFkLA=="} {"stream_id":768,"data":"BAADAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABwAAE4gABiZUsUFktg=="} {"stream_id":832,"data":"BAADQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACAAAE4gABiZUsUFlTA=="} {"stream_id":896,"data":"BAADgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACQAAE4gABiZUsUFl1g=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYADkRSSVZFUl9WRVJTSU9OAAAAHFNDWUxMQV9MV1RfQUREX01FVEFEQVRBX01BUksAKUxXVF9PUFRJTUlaQVRJT05fTUVUQV9CSVRfTUFTSz0yMTQ3NDgzNjQ4ABdTQ1lMTEFfUkFURV9MSU1JVF9FUlJPUgAAABJUQUJMRVRTX1JPVVRJTkdfVjEAAAALQ1FMX1ZFUlNJT04ABTMuMC4wAAtEUklWRVJfTkFNRQAVU2N5bGxhREIgR29DUUwgRHJpdmVy"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlS1dGUY"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAAA4AAAANFNFTEVDVCB2IEZST00gc2luZ2xlX2Nvbm5fYmVuY2gudGFibGUxIFdIRVJFIHBrID0gPzs="} {"stream_id":320,"data":"BAABQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAAAAE4gABiZUtXRoSQ=="} {"stream_id":384,"data":"BAABgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAQAAE4gABiZUtXRo5A=="} {"stream_id":448,"data":"BAABwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAgAAE4gABiZUtXRpZw=="} {"stream_id":512,"data":"BAACAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAwAAE4gABiZUtXRqCA=="} {"stream_id":576,"data":"BAACQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABAAAE4gABiZUtXRqwQ=="} {"stream_id":640,"data":"BAACgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABQAAE4gABiZUtXRrkw=="} {"stream_id":704,"data":"BAACwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABgAAE4gABiZUtXRsRw=="} {"stream_id":768,"data":"BAADAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABwAAE4gABiZUtXRtpw=="} {"stream_id":832,"data":"BAADQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACAAAE4gABiZUtXRuXg=="} {"stream_id":896,"data":"BAADgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACQAAE4gABiZUtXRvCg=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAC0NRTF9WRVJTSU9OAAUzLjAuMAALRFJJVkVSX05BTUUAFVNjeWxsYURCIEdvQ1FMIERyaXZlcgAORFJJVkVSX1ZFUlNJT04AAAAcU0NZTExBX0xXVF9BRERfTUVUQURBVEFfTUFSSwApTFdUX09QVElNSVpBVElPTl9NRVRBX0JJVF9NQVNLPTIxNDc0ODM2NDgAF1NDWUxMQV9SQVRFX0xJTUlUX0VSUk9SAAAAElRBQkxFVFNfUk9VVElOR19WMQAA"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlS9RvD0"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAAA4AAAANFNFTEVDVCB2IEZST00gc2luZ2xlX2Nvbm5fYmVuY2gudGFibGUxIFdIRVJFIHBrID0gPzs="} {"stream_id":320,"data":"BAABQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAAAAE4gABiZUvUbzow=="} {"stream_id":384,"data":"BAABgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAQAAE4gABiZUvUb0VQ=="} {"stream_id":448,"data":"BAABwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAgAAE4gABiZUvUb0/w=="} {"stream_id":512,"data":"BAACAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAwAAE4gABiZUvUb1zg=="} {"stream_id":576,"data":"BAACQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABAAAE4gABiZUvUb2Zg=="} {"stream_id":640,"data":"BAACgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABQAAE4gABiZUvUb3BA=="} {"stream_id":704,"data":"BAACwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABgAAE4gABiZUvUb3ow=="} {"stream_id":768,"data":"BAADAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABwAAE4gABiZUvUb4RQ=="} {"stream_id":832,"data":"BAADQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACAAAE4gABiZUvUb44g=="} {"stream_id":896,"data":"BAADgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACQAAE4gABiZUvUb5nw=="} {"stream_id":1,"data":"BAAAAQUAAAAA"} {"stream_id":64,"data":"BAAAQAEAAADGAAYAHFNDWUxMQV9MV1RfQUREX01FVEFEQVRBX01BUksAKUxXVF9PUFRJTUlaQVRJT05fTUVUQV9CSVRfTUFTSz0yMTQ3NDgzNjQ4ABdTQ1lMTEFfUkFURV9MSU1JVF9FUlJPUgAAABJUQUJMRVRTX1JPVVRJTkdfVjEAAAALQ1FMX1ZFUlNJT04ABTMuMC4wAAtEUklWRVJfTkFNRQAVU2N5bGxhREIgR29DUUwgRHJpdmVyAA5EUklWRVJfVkVSU0lPTgAA"} {"stream_id":128,"data":"BAAAgAcAAAA/AAAALFNFTEVDVCAqIEZST00gc3lzdGVtLmxvY2FsIFdIRVJFIGtleT0nbG9jYWwnAAEkAAATiAAGJlS/6kxJ"} {"stream_id":192,"data":"BAAAwAsAAAAxAAMAD1RPUE9MT0dZX0NIQU5HRQANU1RBVFVTX0NIQU5HRQANU0NIRU1BX0NIQU5HRQ=="} {"stream_id":256,"data":"BAABAAkAAAA4AAAANFNFTEVDVCB2IEZST00gc2luZ2xlX2Nvbm5fYmVuY2gudGFibGUxIFdIRVJFIHBrID0gPzs="} {"stream_id":320,"data":"BAABQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAAAAE4gABiZUv+pUrQ=="} {"stream_id":384,"data":"BAABgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAQAAE4gABiZUv+pW3A=="} {"stream_id":448,"data":"BAABwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAgAAE4gABiZUv+pYrQ=="} {"stream_id":512,"data":"BAACAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAAAwAAE4gABiZUv+pa2A=="} {"stream_id":576,"data":"BAACQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABAAAE4gABiZUv+pcoQ=="} {"stream_id":640,"data":"BAACgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABQAAE4gABiZUv+peKA=="} {"stream_id":704,"data":"BAACwAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABgAAE4gABiZUv+pfvA=="} {"stream_id":768,"data":"BAADAAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAABwAAE4gABiZUv+phMw=="} {"stream_id":832,"data":"BAADQAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACAAAE4gABiZUv+pioA=="} {"stream_id":896,"data":"BAADgAoAAAArABCqSnvTBA56Hg4siUOJVPrfAAElAAEAAAAEAAAACQAAE4gABiZUv+pjuw=="} ================================================ FILE: tests/serialization/marshal_0_unset_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "testing" "github.com/gocql/gocql" ) func TestMarshalUnsetColumn(t *testing.T) { t.Parallel() type tCase struct { tp gocql.TypeInfo nilData bool err bool } elem := gocql.NewNativeType(3, gocql.TypeSmallInt) cases := []tCase{ {gocql.NewNativeType(4, gocql.TypeBoolean), true, false}, {gocql.NewNativeType(4, gocql.TypeTinyInt), true, false}, {gocql.NewNativeType(4, gocql.TypeSmallInt), true, false}, {gocql.NewNativeType(4, gocql.TypeInt), true, false}, {gocql.NewNativeType(4, gocql.TypeBigInt), true, false}, {gocql.NewNativeType(4, gocql.TypeCounter), true, false}, {gocql.NewNativeType(4, gocql.TypeVarint), true, false}, {gocql.NewNativeType(4, gocql.TypeFloat), true, false}, {gocql.NewNativeType(4, gocql.TypeDouble), true, false}, {gocql.NewNativeType(4, gocql.TypeDecimal), true, false}, {gocql.NewNativeType(4, gocql.TypeVarchar), true, false}, {gocql.NewNativeType(4, gocql.TypeText), true, false}, {gocql.NewNativeType(4, gocql.TypeBlob), true, false}, {gocql.NewNativeType(4, gocql.TypeAscii), true, false}, {gocql.NewNativeType(4, gocql.TypeUUID), true, false}, {gocql.NewNativeType(4, gocql.TypeTimeUUID), true, false}, {gocql.NewNativeType(4, gocql.TypeInet), true, false}, {gocql.NewNativeType(4, gocql.TypeTime), true, false}, {gocql.NewNativeType(4, gocql.TypeTimestamp), true, false}, {gocql.NewNativeType(4, gocql.TypeDate), true, false}, {gocql.NewNativeType(4, gocql.TypeDuration), true, false}, {gocql.NewCollectionType(gocql.NewNativeType(3, gocql.TypeList), nil, elem), true, false}, {gocql.NewCollectionType(gocql.NewNativeType(3, gocql.TypeSet), nil, elem), true, false}, {gocql.NewCollectionType(gocql.NewNativeType(3, gocql.TypeMap), elem, elem), true, false}, {gocql.NewUDTType(3, "udt1", "", gocql.UDTField{Name: "1", Type: elem}), true, true}, {gocql.NewTupleType(gocql.NewNativeType(3, gocql.TypeTuple), elem), true, true}, } for _, expected := range cases { data, err := gocql.Marshal(expected.tp, gocql.UnsetValue) if expected.nilData && data != nil { t.Errorf("marshallig unsetColumn for the cqltype %s should return nil data", expected.tp.Type()) } if !expected.nilData && data == nil { t.Errorf("marshallig unsetColumn for the cqltype %s should return not nil data", expected.tp.Type()) } if expected.err && err == nil { t.Errorf("marshallig unsetColumn for the cqltype %s should return an error", expected.tp.Type()) } if !expected.err && err != nil { t.Errorf("marshallig unsetColumn for the cqltype %s should not return an error", expected.tp.Type()) } } } ================================================ FILE: tests/serialization/marshal_10_decimal_corrupt_test.go ================================================ package serialization_test import ( "testing" "gopkg.in/inf.v0" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" "github.com/gocql/gocql/serialization/decimal" ) func TestMarshalDecimalCorrupt(t *testing.T) { type testSuite struct { marshal func(any) ([]byte, error) unmarshal func(bytes []byte, i any) error name string } tType := gocql.NewNativeType(4, gocql.TypeDecimal) testSuites := [2]testSuite{ { name: "serialization.decimal", marshal: decimal.Marshal, unmarshal: decimal.Unmarshal, }, { name: "glob", marshal: func(i any) ([]byte, error) { return gocql.Marshal(tType, i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) }, }, } for _, tSuite := range testSuites { marshal := tSuite.marshal unmarshal := tSuite.unmarshal t.Run(tSuite.name, func(t *testing.T) { t.Parallel() serialization.NegativeMarshalSet{ Values: mod.Values{"1s2", "1s", "-1s", ",1", "0,1"}.AddVariants(mod.All...), }.Run("corrupt_vals", t, marshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x00\x00\x7f"), Values: mod.Values{*inf.NewDec(0, 0), ""}.AddVariants(mod.All...), }.Run("corrupt_data+", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x00\xff\x80"), Values: mod.Values{*inf.NewDec(0, 0), ""}.AddVariants(mod.All...), }.Run("corrupt_data-", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x00"), Values: mod.Values{*inf.NewDec(0, 0), ""}.AddVariants(mod.All...), }.Run("small_data1", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00"), Values: mod.Values{*inf.NewDec(0, 0), ""}.AddVariants(mod.All...), }.Run("small_data2", t, unmarshal) }) } } ================================================ FILE: tests/serialization/marshal_10_decimal_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "fmt" "math" "math/big" "testing" "gopkg.in/inf.v0" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" "github.com/gocql/gocql/serialization/decimal" ) func TestMarshalDecimal(t *testing.T) { t.Parallel() tType := gocql.NewNativeType(4, gocql.TypeDecimal) type testSuite struct { name string marshal func(any) ([]byte, error) unmarshal func(bytes []byte, i any) error } testSuites := [2]testSuite{ { name: "serialization.decimal", marshal: decimal.Marshal, unmarshal: decimal.Unmarshal, }, { name: "glob", marshal: func(i any) ([]byte, error) { return gocql.Marshal(tType, i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) }, }, } getValues := func(scale inf.Scale, unscaled ...int64) mod.Values { out := make(mod.Values, 2) switch len(unscaled) { case 0: panic("unscaled should be") case 1: out[0] = *inf.NewDec(unscaled[0], scale) out[1] = fmt.Sprintf("%d;%d", scale, unscaled[0]) default: bg := new(big.Int) for _, u := range unscaled { bg = bg.Add(bg, big.NewInt(u)) } out[0] = *inf.NewDecBig(bg, scale) out[1] = fmt.Sprintf("%d;%s", scale, bg.String()) } return out } for _, tSuite := range testSuites { marshal := tSuite.marshal unmarshal := tSuite.unmarshal t.Run(tSuite.name, func(t *testing.T) { t.Parallel() serialization.PositiveSet{ Data: nil, Values: mod.Values{(*inf.Dec)(nil), ""}.AddVariants(mod.CustomType), }.Run("[nil]nullable", t, marshal, unmarshal) serialization.PositiveSet{ Data: nil, Values: mod.Values{*inf.NewDec(0, 0), ""}.AddVariants(mod.CustomType), }.Run("[nil]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: make([]byte, 0), Values: getValues(0, 0).AddVariants(mod.All...), }.Run("[]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00\x00"), Values: getValues(0, 0).AddVariants(mod.All...), }.Run("zeros", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00\x7f\xff\xff\xff\xff\xff\xff\xff"), Values: getValues(0, math.MaxInt64).AddVariants(mod.All...), }.Run("scale0_maxInt64", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x01\x7f\xff\xff\xff\xff\xff\xff\xff"), Values: getValues(1, math.MaxInt64).AddVariants(mod.All...), }.Run("scale+1_maxInt64", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\xff\xff\xff\x7f\xff\xff\xff\xff\xff\xff\xff"), Values: getValues(-1, math.MaxInt64).AddVariants(mod.All...), }.Run("scale-1_maxInt64", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x7f\xff\xff\xff\x7f\xff\xff\xff\xff\xff\xff\xff"), Values: getValues(math.MaxInt32, math.MaxInt64).AddVariants(mod.All...), }.Run("maxInt32_maxInt64", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x80\x00\x00\x00\x7f\xff\xff\xff\xff\xff\xff\xff"), Values: getValues(math.MinInt32, math.MaxInt64).AddVariants(mod.All...), }.Run("minInt32_maxInt64", t, marshal, unmarshal) scale := inf.Scale(math.MaxInt16) t.Run("scaleMaxInt16", func(t *testing.T) { t.Parallel() serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x01"), Values: getValues(scale, 1).AddVariants(mod.All...), }.Run("+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\xff"), Values: getValues(scale, -1).AddVariants(mod.All...), }.Run("-1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x7f"), Values: getValues(scale, 127).AddVariants(mod.All...), }.Run("maxInt8", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x80"), Values: getValues(scale, -128).AddVariants(mod.All...), }.Run("minInt8", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x00\x80"), Values: getValues(scale, 128).AddVariants(mod.All...), }.Run("maxInt8+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\xff\x7f"), Values: getValues(scale, -129).AddVariants(mod.All...), }.Run("minInt8-1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x7f\xff"), Values: getValues(scale, 32767).AddVariants(mod.All...), }.Run("maxInt16", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x80\x00"), Values: getValues(scale, -32768).AddVariants(mod.All...), }.Run("minInt16", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x00\x80\x00"), Values: getValues(scale, 32768).AddVariants(mod.All...), }.Run("maxInt16+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\xff\x7f\xff"), Values: getValues(scale, -32769).AddVariants(mod.All...), }.Run("minInt16-1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x7f\xff\xff"), Values: getValues(scale, 8388607).AddVariants(mod.All...), }.Run("maxInt24", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x80\x00\x00"), Values: getValues(scale, -8388608).AddVariants(mod.All...), }.Run("minInt24", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x00\x80\x00\x00"), Values: getValues(scale, 8388608).AddVariants(mod.All...), }.Run("maxInt24+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\xff\x7f\xff\xff"), Values: getValues(scale, -8388609).AddVariants(mod.All...), }.Run("minInt24-1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x7f\xff\xff\xff"), Values: getValues(scale, 2147483647).AddVariants(mod.All...), }.Run("maxInt32", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x80\x00\x00\x00"), Values: getValues(scale, -2147483648).AddVariants(mod.All...), }.Run("minInt32", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x00\x80\x00\x00\x00"), Values: getValues(scale, 2147483648).AddVariants(mod.All...), }.Run("maxInt32+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\xff\x7f\xff\xff\xff"), Values: getValues(scale, -2147483649).AddVariants(mod.All...), }.Run("minInt32-1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x7f\xff\xff\xff\xff"), Values: getValues(scale, 549755813887).AddVariants(mod.All...), }.Run("maxInt40", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x80\x00\x00\x00\x00"), Values: getValues(scale, -549755813888).AddVariants(mod.All...), }.Run("minInt40", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x00\x80\x00\x00\x00\x00"), Values: getValues(scale, 549755813888).AddVariants(mod.All...), }.Run("maxInt40+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\xff\x7f\xff\xff\xff\xff"), Values: getValues(scale, -549755813889).AddVariants(mod.All...), }.Run("minInt40-1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x7f\xff\xff\xff\xff\xff"), Values: getValues(scale, 140737488355327).AddVariants(mod.All...), }.Run("maxInt48", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x80\x00\x00\x00\x00\x00"), Values: getValues(scale, -140737488355328).AddVariants(mod.All...), }.Run("minInt48", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x00\x80\x00\x00\x00\x00\x00"), Values: getValues(scale, 140737488355328).AddVariants(mod.All...), }.Run("maxInt48+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\xff\x7f\xff\xff\xff\xff\xff"), Values: getValues(scale, -140737488355329).AddVariants(mod.All...), }.Run("minInt48-1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x7f\xff\xff\xff\xff\xff\xff"), Values: getValues(scale, 36028797018963967).AddVariants(mod.All...), }.Run("maxInt56", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x80\x00\x00\x00\x00\x00\x00"), Values: getValues(scale, -36028797018963968).AddVariants(mod.All...), }.Run("minInt56", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x00\x80\x00\x00\x00\x00\x00\x00"), Values: getValues(scale, 36028797018963968).AddVariants(mod.All...), }.Run("maxInt56+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\xff\x7f\xff\xff\xff\xff\xff\xff"), Values: getValues(scale, -36028797018963969).AddVariants(mod.All...), }.Run("minInt56-1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x7f\xff\xff\xff\xff\xff\xff\xff"), Values: getValues(scale, 9223372036854775807).AddVariants(mod.All...), }.Run("maxInt64", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x80\x00\x00\x00\x00\x00\x00\x00"), Values: getValues(scale, -9223372036854775808).AddVariants(mod.All...), }.Run("minInt64", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x00\x80\x00\x00\x00\x00\x00\x00\x00"), Values: getValues(scale, 9223372036854775807, 1).AddVariants(mod.All...), }.Run("maxInt64+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\xff\x7f\xff\xff\xff\xff\xff\xff\xff"), Values: getValues(scale, -9223372036854775808, -1).AddVariants(mod.All...), }.Run("minInt64-1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x00\xff"), Values: getValues(scale, 255).AddVariants(mod.All...), }.Run("maxUint8", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x01\x00"), Values: getValues(scale, 256).AddVariants(mod.All...), }.Run("maxUint8+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x00\xff\xff"), Values: getValues(scale, 65535).AddVariants(mod.All...), }.Run("maxUint16", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x01\x00\x00"), Values: getValues(scale, 65536).AddVariants(mod.All...), }.Run("maxUint16+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x00\xff\xff\xff"), Values: getValues(scale, 16777215).AddVariants(mod.All...), }.Run("maxUint24", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x01\x00\x00\x00"), Values: getValues(scale, 16777216).AddVariants(mod.All...), }.Run("maxUint24+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x00\xff\xff\xff\xff"), Values: getValues(scale, 4294967295).AddVariants(mod.All...), }.Run("maxUint32", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x01\x00\x00\x00\x00"), Values: getValues(scale, 4294967296).AddVariants(mod.All...), }.Run("maxUint32+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x00\xff\xff\xff\xff\xff"), Values: getValues(scale, 1099511627775).AddVariants(mod.All...), }.Run("maxUint40", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x01\x00\x00\x00\x00\x00"), Values: getValues(scale, 1099511627776).AddVariants(mod.All...), }.Run("maxUint40+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x00\xff\xff\xff\xff\xff\xff"), Values: getValues(scale, 281474976710655).AddVariants(mod.All...), }.Run("maxUint48", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x01\x00\x00\x00\x00\x00\x00"), Values: getValues(scale, 281474976710656).AddVariants(mod.All...), }.Run("maxUint48+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x00\xff\xff\xff\xff\xff\xff\xff"), Values: getValues(scale, 72057594037927935).AddVariants(mod.All...), }.Run("maxUint56", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x01\x00\x00\x00\x00\x00\x00\x00"), Values: getValues(scale, 72057594037927936).AddVariants(mod.All...), }.Run("maxUint56+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff"), Values: getValues(scale, 9223372036854775807, 9223372036854775807, 1).AddVariants(mod.All...), }.Run("maxUint64", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff\x01\x00\x00\x00\x00\x00\x00\x00\x00"), Values: getValues(scale, 9223372036854775807, 9223372036854775807, 2).AddVariants(mod.All...), }.Run("maxUint64+1", t, marshal, unmarshal) }) }) } } ================================================ FILE: tests/serialization/marshal_11_texts_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "testing" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" "github.com/gocql/gocql/serialization/blob" "github.com/gocql/gocql/serialization/text" "github.com/gocql/gocql/serialization/varchar" ) func TestMarshalTexts(t *testing.T) { t.Parallel() type testSuite struct { name string marshal func(any) ([]byte, error) unmarshal func(bytes []byte, i any) error } testSuites := []testSuite{ { name: "serialization.varchar", marshal: varchar.Marshal, unmarshal: varchar.Unmarshal, }, { name: "serialization.text", marshal: text.Marshal, unmarshal: text.Unmarshal, }, { name: "serialization.blob", marshal: blob.Marshal, unmarshal: blob.Unmarshal, }, { name: "glob.varchar", marshal: func(i any) ([]byte, error) { return gocql.Marshal(gocql.NewNativeType(4, gocql.TypeVarchar), i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(gocql.NewNativeType(4, gocql.TypeVarchar), bytes, i) }, }, { name: "glob.text", marshal: func(i any) ([]byte, error) { return gocql.Marshal(gocql.NewNativeType(4, gocql.TypeText), i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(gocql.NewNativeType(4, gocql.TypeText), bytes, i) }, }, { name: "glob.blob", marshal: func(i any) ([]byte, error) { return gocql.Marshal(gocql.NewNativeType(4, gocql.TypeBlob), i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(gocql.NewNativeType(4, gocql.TypeBlob), bytes, i) }, }, } for _, tSuite := range testSuites { marshal := tSuite.marshal unmarshal := tSuite.unmarshal t.Run(tSuite.name, func(t *testing.T) { t.Parallel() serialization.PositiveSet{ Data: nil, Values: mod.Values{ ([]byte)(nil), (*[]byte)(nil), (*string)(nil), }.AddVariants(mod.CustomType), }.Run("[nil]nullable", t, marshal, unmarshal) serialization.PositiveSet{ Data: nil, Values: mod.Values{""}.AddVariants(mod.CustomType), }.Run("[nil]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: make([]byte, 0), Values: mod.Values{make([]byte, 0), ""}.AddVariants(mod.All...), }.Run("[]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: []byte("$test text string$"), Values: mod.Values{[]byte("$test text string$"), "$test text string$"}.AddVariants(mod.All...), }.Run("text", t, marshal, unmarshal) }) } } ================================================ FILE: tests/serialization/marshal_12_ascii_corrupt_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "testing" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" "github.com/gocql/gocql/serialization/ascii" ) func TestMarshalAsciiMustFail(t *testing.T) { t.Parallel() tType := gocql.NewNativeType(4, gocql.TypeAscii) type testSuite struct { name string marshal func(any) ([]byte, error) unmarshal func(bytes []byte, i any) error } testSuites := [2]testSuite{ { name: "serialization.ascii", marshal: ascii.Marshal, unmarshal: ascii.Unmarshal, }, { name: "glob", marshal: func(i any) ([]byte, error) { return gocql.Marshal(tType, i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) }, }, } for _, tSuite := range testSuites { unmarshal := tSuite.unmarshal t.Run(tSuite.name, func(t *testing.T) { t.Parallel() serialization.NegativeUnmarshalSet{ Data: []byte{255}, Values: mod.Values{[]byte{}, ""}.AddVariants(mod.All...), }.Run("corrupt_data1", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte{127, 255, 127}, Values: mod.Values{[]byte{}, ""}.AddVariants(mod.All...), }.Run("corrupt_data2", t, unmarshal) }) } } ================================================ FILE: tests/serialization/marshal_12_ascii_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "testing" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" "github.com/gocql/gocql/serialization/ascii" ) func TestMarshalAscii(t *testing.T) { tType := gocql.NewNativeType(4, gocql.TypeAscii) type testSuite struct { name string marshal func(any) ([]byte, error) unmarshal func(bytes []byte, i any) error } testSuites := [2]testSuite{ { name: "serialization.int", marshal: ascii.Marshal, unmarshal: ascii.Unmarshal, }, { name: "glob", marshal: func(i any) ([]byte, error) { return gocql.Marshal(tType, i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) }, }, } for _, tSuite := range testSuites { marshal := tSuite.marshal unmarshal := tSuite.unmarshal t.Run(tSuite.name, func(t *testing.T) { t.Parallel() serialization.PositiveSet{ Data: nil, Values: mod.Values{ ([]byte)(nil), (*[]byte)(nil), (*string)(nil), }.AddVariants(mod.CustomType), }.Run("[nil]nullable", t, marshal, unmarshal) serialization.PositiveSet{ Data: nil, Values: mod.Values{""}.AddVariants(mod.CustomType), }.Run("[nil]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: make([]byte, 0), Values: mod.Values{make([]byte, 0), ""}.AddVariants(mod.All...), }.Run("[]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: []byte("test text string"), Values: mod.Values{[]byte("test text string"), "test text string"}.AddVariants(mod.All...), }.Run("text", t, nil, unmarshal) }) } } ================================================ FILE: tests/serialization/marshal_13_uuids_corrupt_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "testing" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" "github.com/gocql/gocql/serialization/timeuuid" "github.com/gocql/gocql/serialization/uuid" ) func TestMarshalUUIDsMustFail(t *testing.T) { t.Parallel() tTypes := []gocql.NativeType{ gocql.NewNativeType(4, gocql.TypeUUID), gocql.NewNativeType(4, gocql.TypeTimeUUID), } type testSuite struct { name string marshal func(any) ([]byte, error) unmarshal func(bytes []byte, i any) error } testSuites := [4]testSuite{ { name: "serialization.uuid", marshal: uuid.Marshal, unmarshal: uuid.Unmarshal, }, { name: "glob.uuid", marshal: func(i any) ([]byte, error) { return gocql.Marshal(tTypes[0], i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(tTypes[0], bytes, i) }, }, { name: "serialization.timeuuid", marshal: timeuuid.Marshal, unmarshal: timeuuid.Unmarshal, }, { name: "glob.timeuuid", marshal: func(i any) ([]byte, error) { return gocql.Marshal(tTypes[1], i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(tTypes[1], bytes, i) }, }, } for _, tSuite := range testSuites { marshal := tSuite.marshal unmarshal := tSuite.unmarshal t.Run(tSuite.name, func(t *testing.T) { t.Parallel() serialization.NegativeMarshalSet{ Values: mod.Values{ "b6b77c23-c776-40ff-828d-a385f3e8a2aff", "00000000-0000-0000-0000-0000000000000", []byte{182, 183, 124, 35, 199, 118, 64, 255, 130, 141, 163, 133, 243, 232, 162, 175, 175}, []byte{00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, [17]byte{}, }.AddVariants(mod.All...), }.Run("big_vals", t, marshal) serialization.NegativeMarshalSet{ Values: mod.Values{ "b6b77c23-c776-40ff-828d-a385f3e8a2a", "00000000-0000-0000-0000-00000000000", []byte{182, 183, 124, 35, 199, 118, 64, 255, 130, 141, 163, 133, 243, 232, 162}, []byte{00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, [15]byte{}, }.AddVariants(mod.All...), }.Run("small_vals", t, marshal) serialization.NegativeMarshalSet{ Values: mod.Values{ "b6b77c@3-c776-40ff-828d-a385f3e8a2a", "00000000-0000-0000-0000-0#0000000000", }.AddVariants(mod.All...), }.Run("corrupt_vals", t, marshal) serialization.NegativeUnmarshalSet{ Data: []byte("\xb6\xb7\x7c\x23\xc7\x76\x40\xff\x82\x8d\xa3\x85\xf3\xe8\xa2\xaf\xaf"), Values: mod.Values{"", make([]byte, 0), [16]byte{}, gocql.UUID{}}.AddVariants(mod.All...), }.Run("big_data", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\xb6\xb7\x7c\x23\xc7\x76\x40\xff\x82\x8d\xa3\x85\xf3\xe8\xa2"), Values: mod.Values{"", make([]byte, 0), [16]byte{}, gocql.UUID{}}.AddVariants(mod.All...), }.Run("small_data1", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00"), Values: mod.Values{"", make([]byte, 0), [16]byte{}, gocql.UUID{}}.AddVariants(mod.All...), }.Run("small_data2", t, unmarshal) }) } } ================================================ FILE: tests/serialization/marshal_13_uuids_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "testing" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" "github.com/gocql/gocql/serialization/timeuuid" "github.com/gocql/gocql/serialization/uuid" ) func TestMarshalUUIDs(t *testing.T) { t.Parallel() tTypes := []gocql.NativeType{ gocql.NewNativeType(4, gocql.TypeUUID), gocql.NewNativeType(4, gocql.TypeTimeUUID), } type testSuite struct { name string marshal func(any) ([]byte, error) unmarshal func(bytes []byte, i any) error } testSuites := [4]testSuite{ { name: "serialization.uuid", marshal: uuid.Marshal, unmarshal: uuid.Unmarshal, }, { name: "glob.uuid", marshal: func(i any) ([]byte, error) { return gocql.Marshal(tTypes[0], i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(tTypes[0], bytes, i) }, }, { name: "serialization.timeuuid", marshal: timeuuid.Marshal, unmarshal: timeuuid.Unmarshal, }, { name: "glob.timeuuid", marshal: func(i any) ([]byte, error) { return gocql.Marshal(tTypes[1], i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(tTypes[1], bytes, i) }, }, } for _, tSuite := range testSuites { marshal := tSuite.marshal unmarshal := tSuite.unmarshal t.Run(tSuite.name, func(t *testing.T) { t.Parallel() serialization.PositiveSet{ Data: nil, Values: mod.Values{ ([]byte)(nil), (*[]byte)(nil), "", (*string)(nil), (*[16]byte)(nil), (*gocql.UUID)(nil), }.AddVariants(mod.CustomType), }.Run("[nil]nullable", t, marshal, unmarshal) serialization.PositiveSet{ Data: nil, Values: mod.Values{ [16]byte{}, gocql.UUID{}, }.AddVariants(mod.CustomType), }.Run("[nil]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: make([]byte, 0), Values: mod.Values{ "00000000-0000-0000-0000-000000000000", make([]byte, 0), [16]byte{}, gocql.UUID{}, }.AddVariants(mod.All...), }.Run("[]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, Values: mod.Values{ []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, [16]byte{}, gocql.UUID{}, }.AddVariants(mod.All...), }.Run("zeros", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xe9\x39\xf5\x2a\xd6\x90\x11\xef\x9c\xd2\x02\x42\xac\x12\x00\x02"), Values: mod.Values{ "e939f52a-d690-11ef-9cd2-0242ac120002", []byte{233, 57, 245, 42, 214, 144, 17, 239, 156, 210, 2, 66, 172, 18, 0, 2}, [16]byte{233, 57, 245, 42, 214, 144, 17, 239, 156, 210, 2, 66, 172, 18, 0, 2}, gocql.UUID{233, 57, 245, 42, 214, 144, 17, 239, 156, 210, 2, 66, 172, 18, 0, 2}, }.AddVariants(mod.All...), }.Run("uuid", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"), Values: mod.Values{ "ffffffff-ffff-ffff-ffff-ffffffffffff", []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, [16]byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, gocql.UUID{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, }.AddVariants(mod.All...), }.Run("max", t, marshal, unmarshal) }) } } func TestMarshalTimeUUID(t *testing.T) { t.Parallel() tType := gocql.NewNativeType(4, gocql.TypeTimeUUID) type testSuite struct { name string marshal func(any) ([]byte, error) unmarshal func(bytes []byte, i any) error } testSuites := [4]testSuite{ { name: "serialization.timeuuid", marshal: timeuuid.Marshal, unmarshal: timeuuid.Unmarshal, }, { name: "glob.timeuuid", marshal: func(i any) ([]byte, error) { return gocql.Marshal(tType, i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) }, }, } for _, tSuite := range testSuites { marshal := tSuite.marshal unmarshal := tSuite.unmarshal t.Run(tSuite.name, func(t *testing.T) { t.Parallel() serialization.PositiveSet{ Data: make([]byte, 0), Values: mod.Values{ "00000000-0000-0000-0000-000000000000", }.AddVariants(mod.All...), }.Run("zero", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\xff\xff\xff\xff\xff\x1f\xff\xff\xff\xff\xff\xff\xff\xff\xff"), Values: mod.Values{ "ffffffff-ffff-1fff-ffff-ffffffffffff", []byte{255, 255, 255, 255, 255, 255, 31, 255, 255, 255, 255, 255, 255, 255, 255, 255}, [16]byte{255, 255, 255, 255, 255, 255, 31, 255, 255, 255, 255, 255, 255, 255, 255, 255}, gocql.UUID{255, 255, 255, 255, 255, 255, 31, 255, 255, 255, 255, 255, 255, 255, 255, 255}, }.AddVariants(mod.All...), }.Run("max", t, marshal, unmarshal) }) } } ================================================ FILE: tests/serialization/marshal_14_inet_corrupt_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "net" "testing" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" "github.com/gocql/gocql/serialization/inet" ) func TestMarshalsInetMustFail(t *testing.T) { t.Parallel() tType := gocql.NewNativeType(4, gocql.TypeInet) type testSuite struct { name string marshal func(any) ([]byte, error) unmarshal func(bytes []byte, i any) error } testSuites := [2]testSuite{ { name: "serialization.inet", marshal: inet.Marshal, unmarshal: inet.Unmarshal, }, { name: "glob", marshal: func(i any) ([]byte, error) { return gocql.Marshal(tType, i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) }, }, } for _, tSuite := range testSuites { marshal := tSuite.marshal unmarshal := tSuite.unmarshal t.Run(tSuite.name, func(t *testing.T) { t.Parallel() serialization.NegativeMarshalSet{ Values: mod.Values{ "192.168.0.1.1", net.IP{192, 168, 0, 1, 1}, []byte{192, 168, 0, 1, 1}, [5]byte{192, 168, 0, 1, 1}, }.AddVariants(mod.All...), }.Run("big_valsV4", t, marshal) serialization.NegativeMarshalSet{ Values: mod.Values{ "fe80:cd00:0:cde:1257:0:211e:729cc", net.IP("\xb6\xb7\x7c\x23\xc7\x76\x40\xff\x82\x8d\xa3\x85\xf3\xe8\xa2\xaf\xaf"), []byte("\xb6\xb7\x7c\x23\xc7\x76\x40\xff\x82\x8d\xa3\x85\xf3\xe8\xa2\xaf\xaf"), [17]byte{254, 128, 205, 0, 0, 0, 12, 222, 18, 87, 0, 0, 33, 30, 114, 156, 156}, }.AddVariants(mod.All...), }.Run("big_valsV6", t, marshal) serialization.NegativeMarshalSet{ Values: mod.Values{ "192.168.0", net.IP{192, 168, 0}, []byte{192, 168, 0}, [3]byte{192, 168, 0}, }.AddVariants(mod.All...), }.Run("small_valsV4", t, marshal) serialization.NegativeMarshalSet{ Values: mod.Values{ "fe80:cd00:0:cde:1257:0:211e", net.IP("\xb6\xb7\x7c\x23\xc7\x76\x40\xff\x82\x8d\xa3\x85\xf3\xe8\xa2"), []byte("\xb6\xb7\x7c\x23\xc7\x76\x40\xff\x82\x8d\xa3\x85\xf3\xe8\xa2"), [15]byte{254, 128, 205, 0, 0, 0, 12, 222, 18, 87, 0, 0, 33, 30, 114}, }.AddVariants(mod.All...), }.Run("small_valsV6", t, marshal) serialization.NegativeMarshalSet{ Values: mod.Values{ "b6b77c@3-c776-40ff-828d-a385f3e8a2a", "00000000-0000-0000-0000-0#0000000000", "192.168.a.1", }.AddVariants(mod.All...), }.Run("corrupt_vals", t, marshal) serialization.NegativeUnmarshalSet{ Data: []byte{192, 168, 0, 1, 1}, Values: mod.Values{ "", net.IP{}, []byte{}, [4]byte{}, }.AddVariants(mod.All...), }.Run("big_dataV4", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\xb6\xb7\x7c\x23\xc7\x76\x40\xff\x82\x8d\xa3\x85\xf3\xe8\xa2\xaf\xaf"), Values: mod.Values{ "", net.IP{}, []byte{}, [16]byte{}, }.AddVariants(mod.All...), }.Run("big_dataV6", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\xb6\xb7\x7c\x23\xc7\x76\x40\xff\x82\x8d\xa3\x85\xf3\xe8\xa2\xaf"), Values: mod.Values{ [4]byte{}, }.AddVariants(mod.All...), }.Run("big_dataV6Array4", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte{192, 168, 0}, Values: mod.Values{ "", net.IP{}, []byte{}, [4]byte{}, }.AddVariants(mod.All...), }.Run("small_dataV4", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\xb6\xb7\x7c\x23\xc7\x76\x40\xff\x82\x8d\xa3\x85\xf3\xe8\xa2"), Values: mod.Values{ "", net.IP{}, []byte{}, [16]byte{}, }.AddVariants(mod.All...), }.Run("small_dataV6", t, unmarshal) }) } } ================================================ FILE: tests/serialization/marshal_14_inet_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "net" "testing" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" "github.com/gocql/gocql/serialization/inet" ) func TestMarshalsInet(t *testing.T) { t.Parallel() tType := gocql.NewNativeType(4, gocql.TypeInet) type testSuite struct { name string marshal func(any) ([]byte, error) unmarshal func(bytes []byte, i any) error } testSuites := [2]testSuite{ { name: "serialization.inet", marshal: inet.Marshal, unmarshal: inet.Unmarshal, }, { name: "glob", marshal: func(i any) ([]byte, error) { return gocql.Marshal(tType, i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) }, }, } for _, tSuite := range testSuites { marshal := tSuite.marshal unmarshal := tSuite.unmarshal t.Run(tSuite.name, func(t *testing.T) { t.Parallel() serialization.PositiveSet{ Data: nil, Values: mod.Values{ ([]byte)(nil), (*[]byte)(nil), (*[4]byte)(nil), (*[16]byte)(nil), (net.IP)(nil), (*net.IP)(nil), "", (*string)(nil), }.AddVariants(mod.CustomType), }.Run("[nil]nullable", t, marshal, unmarshal) serialization.PositiveSet{ Data: nil, Values: mod.Values{ [4]byte{}, [16]byte{}, }.AddVariants(mod.CustomType), }.Run("[nil]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: make([]byte, 0), Values: mod.Values{ make([]byte, 0), [4]byte{}, [16]byte{}, make(net.IP, 0), "0.0.0.0", }.AddVariants(mod.All...), }.Run("[]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: []byte{0, 0, 0, 0}, Values: mod.Values{ "0.0.0.0", []byte{0, 0, 0, 0}, net.IP{0, 0, 0, 0}, [4]byte{}, }.AddVariants(mod.All...), }.Run("v4zeros", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte{0, 0, 0, 0}, Values: mod.Values{ [16]byte{}, }.AddVariants(mod.All...), }.Run("v4zerosUnmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: []byte{192, 168, 0, 1}, Values: mod.Values{ "192.168.0.1", []byte{192, 168, 0, 1}, net.IP{192, 168, 0, 1}, [4]byte{192, 168, 0, 1}, }.AddVariants(mod.All...), }.Run("v4", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte{192, 168, 0, 1}, Values: mod.Values{ [16]byte{192, 168, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, }.AddVariants(mod.All...), }.Run("v4unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: []byte{255, 255, 255, 255}, Values: mod.Values{ "255.255.255.255", []byte{255, 255, 255, 255}, net.IP{255, 255, 255, 255}, [4]byte{255, 255, 255, 255}, }.AddVariants(mod.All...), }.Run("v4max", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte{255, 255, 255, 255}, Values: mod.Values{ [16]byte{255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, }.AddVariants(mod.All...), }.Run("v4maxUnmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, Values: mod.Values{ "::", []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, }.AddVariants(mod.All...), }.Run("v6zeros", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, Values: mod.Values{ [4]byte{0, 0, 0, 0}, }.AddVariants(mod.All...), }.Run("v6zerosUnmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: []byte("\xfe\x80\xcd\x00\x00\x00\x0c\xde\x12\x57\x00\x00\x21\x1e\x72\x9c"), Values: mod.Values{ "fe80:cd00:0:cde:1257:0:211e:729c", []byte("\xfe\x80\xcd\x00\x00\x00\x0c\xde\x12\x57\x00\x00\x21\x1e\x72\x9c"), net.IP("\xfe\x80\xcd\x00\x00\x00\x0c\xde\x12\x57\x00\x00\x21\x1e\x72\x9c"), [16]byte{254, 128, 205, 0, 0, 0, 12, 222, 18, 87, 0, 0, 33, 30, 114, 156}, }.AddVariants(mod.All...), }.Run("v6", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"), Values: mod.Values{ "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", []byte("\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"), net.IP("\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"), [16]byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, }.AddVariants(mod.All...), }.Run("v6max", t, marshal, unmarshal) }) } } ================================================ FILE: tests/serialization/marshal_15_time_corrupt_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "math" "testing" "time" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" "github.com/gocql/gocql/serialization/cqltime" ) func TestMarshalTimeCorrupt(t *testing.T) { t.Parallel() tType := gocql.NewNativeType(4, gocql.TypeTime) type testSuite struct { name string marshal func(any) ([]byte, error) unmarshal func(bytes []byte, i any) error } testSuites := [2]testSuite{ { name: "serialization.cqltime", marshal: cqltime.Marshal, unmarshal: cqltime.Unmarshal, }, { name: "glob", marshal: func(i any) ([]byte, error) { return gocql.Marshal(tType, i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) }, }, } for _, tSuite := range testSuites { marshal := tSuite.marshal unmarshal := tSuite.unmarshal t.Run(tSuite.name, func(t *testing.T) { t.Parallel() // marshal, unmarshal of all supported `go types` does not return an error on all type of corruption. //brokenTypes := serialization.GetTypes(int64(0), (*int64)(nil), mod.Int64(0), (*mod.Int64)(nil), time.Duration(0), (*time.Duration)(nil)) serialization.NegativeMarshalSet{ Values: mod.Values{ int64(86400000000000), time.Duration(86400000000000), int64(86500000000000), time.Duration(86500000000000), int64(math.MaxInt64), time.Duration(math.MaxInt64), }.AddVariants(mod.All...), }.Run("big_vals", t, marshal) serialization.NegativeMarshalSet{ Values: mod.Values{ int64(-1), time.Duration(-1), int64(math.MinInt8), time.Duration(math.MinInt8), int64(math.MinInt16), time.Duration(math.MinInt16), int64(math.MinInt32), time.Duration(math.MinInt32), int64(math.MinInt64), time.Duration(math.MinInt64), }.AddVariants(mod.All...), }.Run("small_vals", t, marshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x4e\x94\x91\x4e\xff\xff\xff"), Values: mod.Values{ int64(0), time.Duration(0), }.AddVariants(mod.All...), }.Run("big_data_len", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x4e\x94\x91\x4e\xff"), Values: mod.Values{ int64(0), time.Duration(0), }.AddVariants(mod.All...), }.Run("small_data_len1", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00"), Values: mod.Values{ int64(0), time.Duration(0), }.AddVariants(mod.All...), }.Run("small_data_len2", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x4e\x94\x91\x4f\x00\x00"), Values: mod.Values{ int64(0), time.Duration(0), }.AddVariants(mod.All...), }.Run("big_data1", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x7f\xff\xff\xff\xff\xff\xff\xff"), Values: mod.Values{ int64(0), time.Duration(0), }.AddVariants(mod.All...), }.Run("big_data2", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\xff\xff\xff\xff\xff\xff\xff\xff"), Values: mod.Values{ int64(0), time.Duration(0), }.AddVariants(mod.All...), }.Run("small_data1", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x80\x00\x00\x00\x00\x00\x00\x00"), Values: mod.Values{ int64(0), time.Duration(0), }.AddVariants(mod.All...), }.Run("small_data2", t, unmarshal) }) } } ================================================ FILE: tests/serialization/marshal_15_time_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "testing" "time" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" "github.com/gocql/gocql/serialization/cqltime" ) func TestMarshalsTime(t *testing.T) { t.Parallel() tType := gocql.NewNativeType(4, gocql.TypeTime) type testSuite struct { name string marshal func(any) ([]byte, error) unmarshal func(bytes []byte, i any) error } testSuites := [2]testSuite{ { name: "serialization.cqltime", marshal: cqltime.Marshal, unmarshal: cqltime.Unmarshal, }, { name: "glob", marshal: func(i any) ([]byte, error) { return gocql.Marshal(tType, i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) }, }, } for _, tSuite := range testSuites { marshal := tSuite.marshal unmarshal := tSuite.unmarshal t.Run(tSuite.name, func(t *testing.T) { t.Parallel() serialization.PositiveSet{ Data: nil, Values: mod.Values{ (*int64)(nil), (*time.Duration)(nil), }.AddVariants(mod.CustomType), }.Run("[nil]nullable", t, marshal, unmarshal) serialization.PositiveSet{ Data: nil, Values: mod.Values{ int64(0), time.Duration(0), }.AddVariants(mod.CustomType), }.Run("[nil]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: make([]byte, 0), Values: mod.Values{ int64(0), time.Duration(0), }.AddVariants(mod.All...), }.Run("[]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00\x00\x00\x00\x00"), Values: mod.Values{ int64(0), time.Duration(0), }.AddVariants(mod.All...), }.Run("zeros", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x4e\x94\x91\x4e\xff\xff"), Values: mod.Values{ int64(86399999999999), time.Duration(86399999999999), }.AddVariants(mod.All...), }.Run("max", t, marshal, unmarshal) }) } } ================================================ FILE: tests/serialization/marshal_16_timestamp_corrupt_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "testing" "time" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" "github.com/gocql/gocql/serialization/timestamp" ) func TestMarshalTimestampCorrupt(t *testing.T) { t.Parallel() tType := gocql.NewNativeType(4, gocql.TypeTimestamp) type testSuite struct { name string marshal func(any) ([]byte, error) unmarshal func(bytes []byte, i any) error } testSuites := [2]testSuite{ { name: "serialization.timestamp", marshal: timestamp.Marshal, unmarshal: timestamp.Unmarshal, }, { name: "glob", marshal: func(i any) ([]byte, error) { return gocql.Marshal(tType, i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) }, }, } for _, tSuite := range testSuites { marshal := tSuite.marshal unmarshal := tSuite.unmarshal t.Run(tSuite.name, func(t *testing.T) { t.Parallel() serialization.NegativeMarshalSet{ Values: mod.Values{ time.Date(292278994, 8, 17, 7, 12, 55, 808*1000000, time.UTC), time.Date(292278994, 8, 17, 7, 12, 56, 807*1000000, time.UTC), time.Date(292278994, 8, 17, 7, 13, 55, 807*1000000, time.UTC), time.Date(292278994, 8, 17, 8, 12, 55, 807*1000000, time.UTC), time.Date(292278994, 8, 18, 7, 12, 55, 807*1000000, time.UTC), time.Date(292278994, 9, 17, 7, 12, 55, 807*1000000, time.UTC), time.Date(292278995, 8, 17, 7, 12, 55, 807*1000000, time.UTC), }.AddVariants(mod.All...), }.Run("big_vals", t, marshal) serialization.NegativeMarshalSet{ Values: mod.Values{ time.Date(-292275055, 5, 16, 16, 47, 4, 191*1000000, time.UTC), time.Date(-292275055, 5, 16, 16, 47, 3, 192*1000000, time.UTC), time.Date(-292275055, 5, 16, 16, 46, 4, 192*1000000, time.UTC), time.Date(-292275055, 5, 16, 15, 47, 4, 192*1000000, time.UTC), time.Date(-292275055, 5, 15, 16, 47, 4, 192*1000000, time.UTC), time.Date(-292275055, 4, 16, 16, 47, 4, 192*1000000, time.UTC), time.Date(-292275056, 5, 16, 16, 47, 4, 192*1000000, time.UTC), }.AddVariants(mod.All...), }.Run("small_vals", t, marshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x7f\xff\xff\xff\xff\xff\xff\xff\xff"), Values: mod.Values{ int64(0), time.Time{}, }.AddVariants(mod.All...), }.Run("big_data", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\xff\xff\xff\xff\xff\xff\xff"), Values: mod.Values{ int64(0), time.Time{}, }.AddVariants(mod.All...), }.Run("small_data1", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00"), Values: mod.Values{ int64(0), time.Time{}, }.AddVariants(mod.All...), }.Run("small_data2", t, unmarshal) }) } } ================================================ FILE: tests/serialization/marshal_16_timestamp_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "math" "testing" "time" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" "github.com/gocql/gocql/serialization/timestamp" ) func TestMarshalsTimestamp(t *testing.T) { t.Parallel() tType := gocql.NewNativeType(4, gocql.TypeTimestamp) type testSuite struct { name string marshal func(any) ([]byte, error) unmarshal func(bytes []byte, i any) error } testSuites := [2]testSuite{ { name: "serialization.timestamp", marshal: timestamp.Marshal, unmarshal: timestamp.Unmarshal, }, { name: "glob", marshal: func(i any) ([]byte, error) { return gocql.Marshal(tType, i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) }, }, } zeroTimestamp := time.Unix(0, 0).UTC() for _, tSuite := range testSuites { marshal := tSuite.marshal unmarshal := tSuite.unmarshal t.Run(tSuite.name, func(t *testing.T) { t.Parallel() serialization.PositiveSet{ Data: nil, Values: mod.Values{ (*int64)(nil), (*time.Time)(nil), }.AddVariants(mod.CustomType), }.Run("[nil]nullable", t, marshal, unmarshal) serialization.PositiveSet{ Data: nil, Values: mod.Values{ int64(0), time.Time{}, }.AddVariants(mod.CustomType), }.Run("[nil]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: make([]byte, 0), Values: mod.Values{ int64(0), time.Time{}, }.AddVariants(mod.All...), }.Run("[]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00\x00\x00\x00\x00"), Values: mod.Values{ int64(0), zeroTimestamp, }.AddVariants(mod.All...), }.Run("zeros", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x7f\xff\xff\xff\xff\xff\xff\xff"), Values: mod.Values{ int64(math.MaxInt64), time.UnixMilli(math.MaxInt64).UTC(), }.AddVariants(mod.All...), }.Run("max", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x80\x00\x00\x00\x00\x00\x00\x00"), Values: mod.Values{ int64(math.MinInt64), time.UnixMilli(math.MinInt64).UTC(), }.AddVariants(mod.All...), }.Run("min", t, marshal, unmarshal) }) } } ================================================ FILE: tests/serialization/marshal_17_date_corrupt_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "testing" "time" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" "github.com/gocql/gocql/serialization/date" ) func TestMarshalDateCorrupt(t *testing.T) { t.Parallel() tType := gocql.NewNativeType(4, gocql.TypeDate) type testSuite struct { name string marshal func(any) ([]byte, error) unmarshal func(bytes []byte, i any) error } testSuites := [2]testSuite{ { name: "serialization.date", marshal: date.Marshal, unmarshal: date.Unmarshal, }, { name: "glob", marshal: func(i any) ([]byte, error) { return gocql.Marshal(tType, i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) }, }, } for _, tSuite := range testSuites { marshal := tSuite.marshal unmarshal := tSuite.unmarshal t.Run(tSuite.name, func(t *testing.T) { t.Parallel() serialization.NegativeMarshalSet{ Values: mod.Values{ time.Date(5881580, 7, 12, 0, 0, 0, 0, time.UTC).UnixMilli(), time.Date(5881580, 8, 11, 0, 0, 0, 0, time.UTC).UnixMilli(), time.Date(5881581, 7, 11, 0, 0, 0, 0, time.UTC).UnixMilli(), time.Date(5883581, 12, 20, 0, 0, 0, 0, time.UTC).UnixMilli(), "5881580-07-12", "5881580-08-11", "5881581-07-11", "9223372036854775807-07-12", time.Date(5881580, 7, 12, 0, 0, 0, 0, time.UTC).UTC(), time.Date(5881580, 8, 11, 0, 0, 0, 0, time.UTC).UTC(), time.Date(5881581, 7, 11, 0, 0, 0, 0, time.UTC).UTC(), time.Date(5883581, 12, 20, 0, 0, 0, 0, time.UTC).UTC(), }.AddVariants(mod.All...), }.Run("big_vals", t, marshal) serialization.NegativeMarshalSet{ Values: mod.Values{ time.Date(-5877641, 06, 22, 0, 0, 0, 0, time.UTC).UnixMilli(), time.Date(-5877641, 05, 23, 0, 0, 0, 0, time.UTC).UnixMilli(), time.Date(-5877642, 06, 23, 0, 0, 0, 0, time.UTC).UnixMilli(), time.Date(-5887641, 06, 23, 0, 0, 0, 0, time.UTC).UnixMilli(), "-5877641-06-22", "-5877641-05-23", "-5877642-06-23", "-9223372036854775807-07-12", time.Date(-5877641, 06, 22, 0, 0, 0, 0, time.UTC), time.Date(-5877641, 05, 23, 0, 0, 0, 0, time.UTC), time.Date(-5877642, 06, 23, 0, 0, 0, 0, time.UTC), time.Date(-5887641, 06, 23, 0, 0, 0, 0, time.UTC), }.AddVariants(mod.All...), }.Run("small_vals", t, marshal) serialization.NegativeMarshalSet{ Values: mod.Values{ "a1580-07-11", "1970-0d-11", "02-11", "1970-11", }.AddVariants(mod.All...), }.Run("corrupt_vals", t, marshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x00\x00"), Values: mod.Values{ int64(0), time.Time{}, "", }.AddVariants(mod.All...), }.Run("big_data1", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x4e\x94\x91\x4e\xff\xff\xff"), Values: mod.Values{ int64(0), time.Time{}, "", }.AddVariants(mod.All...), }.Run("big_data2", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00"), Values: mod.Values{ int64(0), time.Time{}, "", }.AddVariants(mod.All...), }.Run("small_data1", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00"), Values: mod.Values{ int64(0), time.Time{}, "", }.AddVariants(mod.All...), }.Run("small_data2", t, unmarshal) }) } } ================================================ FILE: tests/serialization/marshal_17_date_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "math" "testing" "time" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" "github.com/gocql/gocql/serialization/date" ) func TestMarshalsDate(t *testing.T) { t.Parallel() tType := gocql.NewNativeType(4, gocql.TypeDate) type testSuite struct { name string marshal func(any) ([]byte, error) unmarshal func(bytes []byte, i any) error } testSuites := [2]testSuite{ { name: "serialization.date", marshal: date.Marshal, unmarshal: date.Unmarshal, }, { name: "glob", marshal: func(i any) ([]byte, error) { return gocql.Marshal(tType, i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) }, }, } zeroDate := time.Date(-5877641, 06, 23, 0, 0, 0, 0, time.UTC).UTC() middleDate := time.UnixMilli(0).UTC() maxDate := time.Date(5881580, 07, 11, 0, 0, 0, 0, time.UTC).UTC() for _, tSuite := range testSuites { marshal := tSuite.marshal unmarshal := tSuite.unmarshal t.Run(tSuite.name, func(t *testing.T) { t.Parallel() serialization.PositiveSet{ Data: nil, Values: mod.Values{ (*uint32)(nil), (*int32)(nil), (*int64)(nil), (*string)(nil), "", (*time.Time)(nil), }.AddVariants(mod.CustomType), }.Run("[nil]nullable", t, marshal, unmarshal) serialization.PositiveSet{ Data: nil, Values: mod.Values{ uint32(0), int32(0), zeroDate.UnixMilli(), "", zeroDate, }.AddVariants(mod.CustomType), }.Run("[nil]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: make([]byte, 0), Values: mod.Values{ uint32(0), int32(0), zeroDate.UnixMilli(), zeroDate, "-5877641-06-23", }.AddVariants(mod.All...), }.Run("[]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00"), Values: mod.Values{ uint32(0), int32(0), zeroDate.UnixMilli(), zeroDate, "-5877641-06-23", }.AddVariants(mod.All...), }.Run("zeros", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x01"), Values: mod.Values{ uint32(1), int32(1), zeroDate.Add(time.Hour * 24).UnixMilli(), zeroDate.Add(time.Hour * 24), "-5877641-06-24", }.AddVariants(mod.All...), }.Run("1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x80\x00\x00\x00"), Values: mod.Values{ uint32(1 << 31), int32(math.MinInt32), middleDate.UnixMilli(), middleDate, "1970-01-01", }.AddVariants(mod.All...), }.Run("middle", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\xff\xff\xff"), Values: mod.Values{ uint32(math.MaxUint32), int32(-1), maxDate.UnixMilli(), maxDate, "5881580-07-11", }.AddVariants(mod.All...), }.Run("max", t, marshal, unmarshal) }) } } ================================================ FILE: tests/serialization/marshal_18_duration_corrupt_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "testing" "time" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" ) func TestMarshalDurationCorrupt(t *testing.T) { t.Parallel() tType := gocql.NewNativeType(4, gocql.TypeDuration) marshal := func(i any) ([]byte, error) { return gocql.Marshal(tType, i) } unmarshal := func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) } serialization.NegativeMarshalSet{ Values: mod.Values{ "23123113f", "sda", gocql.Duration{Months: -1, Days: 1, Nanoseconds: 0}, gocql.Duration{Months: -1, Days: 1, Nanoseconds: 1}, gocql.Duration{Months: -1, Days: 1, Nanoseconds: -1}, gocql.Duration{Months: -1, Days: -1, Nanoseconds: 1}, gocql.Duration{Months: -1, Days: 0, Nanoseconds: 1}, gocql.Duration{Months: 1, Days: -1, Nanoseconds: 0}, gocql.Duration{Months: 1, Days: -1, Nanoseconds: 1}, gocql.Duration{Months: 1, Days: -1, Nanoseconds: -1}, gocql.Duration{Months: 1, Days: 1, Nanoseconds: -1}, gocql.Duration{Months: 1, Days: 0, Nanoseconds: -1}, }.AddVariants(mod.All...), }.Run("corrupt_vals", t, marshal) serialization.NegativeMarshalSet{ Values: mod.Values{ "178956971y7mo306783378w1d2562047h47m16.854775807s", "178956970y8mo306783378w1d2562047h47m16.854775807s", "178956970y7mo306783379w1d2562047h47m16.854775807s", "178956970y7mo306783378w2d2562047h47m16.854775807s", "178956970y7mo306783378w1d2562048h47m16.854775807s", "178956970y7mo306783378w1d2562047h48m16.854775807s", "178956970y7mo306783378w1d2562047h47m17.854775807s", "178956970y7mo306783378w1d2562047h47m16.854775808s", "-178956971y8mo306783378w2d2562047h47m16.854775808s", "-178956970y9mo306783378w2d2562047h47m16.854775808s", "-178956970y8mo306783379w2d2562047h47m16.854775808s", "-178956970y8mo306783378w3d2562047h47m16.854775808s", "-178956970y8mo306783378w2d2562048h47m16.854775808s", "-178956970y8mo306783378w2d2562047h48m16.854775808s", "-178956970y8mo306783378w2d2562047h47m17.854775808s", "-178956970y8mo306783378w2d2562047h47m16.854775809s", }.AddVariants(mod.All...), }.Run("big_vals", t, marshal) serialization.NegativeUnmarshalSet{ Data: []byte("\xf1\x00\x00\x00\x00\x00\x00"), Values: mod.Values{ int64(0), time.Duration(0), "", gocql.Duration{}, }.AddVariants(mod.All...), }.Run("big_data_month1", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\xf1\x00\x00\x00\x01\x00\x00"), Values: mod.Values{ int64(0), time.Duration(0), "", gocql.Duration{}, }.AddVariants(mod.All...), }.Run("big_data_month2", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\xf1\x00\x00\x00\x00\x00"), Values: mod.Values{ int64(0), time.Duration(0), "", gocql.Duration{}, }.AddVariants(mod.All...), }.Run("big_data_day1", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\xf1\x00\x00\x00\x01\x00"), Values: mod.Values{ int64(0), time.Duration(0), "", gocql.Duration{}, }.AddVariants(mod.All...), }.Run("big_data_day2", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x01\xff\xff\xff\xff\xff\xff\xff\xff\xff"), Values: mod.Values{ int64(0), time.Duration(0), }.AddVariants(mod.All...), }.Run("big_data_nano1", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x01\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff"), Values: mod.Values{ int64(0), time.Duration(0), }.AddVariants(mod.All...), }.Run("big_data_nano2", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x01\x00\x41\xfd\xfc\x9b\xc5\xc4\x9e\x00\x00"), Values: mod.Values{ int64(0), time.Duration(0), "", gocql.Duration{}, }.AddVariants(mod.All...), }.Run("big_data_nano3", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\xc3\x41\xfd\xfc\x9b\xc5\xc4\x9e\x00\x01"), Values: mod.Values{ int64(0), time.Duration(0), }.AddVariants(mod.All...), }.Run("big_data_nano4", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x00"), Values: mod.Values{ int64(0), time.Duration(0), "", gocql.Duration{}, }.AddVariants(mod.All...), }.Run("big_data_len1", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00"), Values: mod.Values{ int64(0), time.Duration(0), "", gocql.Duration{}, }.AddVariants(mod.All...), }.Run("big_data_len2", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfd\x00"), Values: mod.Values{ int64(0), time.Duration(0), "", gocql.Duration{}, }.AddVariants(mod.All...), }.Run("big_data_len3", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00"), Values: mod.Values{ int64(0), time.Duration(0), "", gocql.Duration{}, }.AddVariants(mod.All...), }.Run("small_data_len1", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00"), Values: mod.Values{ int64(0), time.Duration(0), "", gocql.Duration{}, }.AddVariants(mod.All...), }.Run("small_data_len2", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\xf0\xff\xff\xff\xfe\x00"), Values: mod.Values{ int64(0), time.Duration(0), "", gocql.Duration{}, }.AddVariants(mod.All...), }.Run("small_data_len2", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\xf0\xff\xff\xff\xfe"), Values: mod.Values{ int64(0), time.Duration(0), "", gocql.Duration{}, }.AddVariants(mod.All...), }.Run("small_data_len3", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff"), Values: mod.Values{ int64(0), time.Duration(0), "", gocql.Duration{}, }.AddVariants(mod.All...), }.Run("small_data_len_nanos", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\xf0\xff\xff\xff\x00"), Values: mod.Values{ int64(0), time.Duration(0), "", gocql.Duration{}, }.AddVariants(mod.All...), }.Run("small_data_len_days", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\xf0\xff\xff\xff\x00\x00"), Values: mod.Values{ int64(0), time.Duration(0), "", gocql.Duration{}, }.AddVariants(mod.All...), }.Run("small_data_len_months", t, unmarshal) } ================================================ FILE: tests/serialization/marshal_18_duration_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "math" "testing" "time" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" ) func TestMarshalsDuration(t *testing.T) { tType := gocql.NewNativeType(4, gocql.TypeDuration) const nanoDay = 24 * 60 * 60 * 1000 * 1000 * 1000 marshal := func(i any) ([]byte, error) { return gocql.Marshal(tType, i) } unmarshal := func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) } serialization.PositiveSet{ Data: nil, Values: mod.Values{ (*int64)(nil), (*time.Duration)(nil), (*string)(nil), "", (*gocql.Duration)(nil), }.AddVariants(mod.CustomType), BrokenUnmarshalTypes: serialization.GetTypes(int64(0)), }.Run("[nil]nullable", t, marshal, unmarshal) serialization.PositiveSet{ Data: nil, Values: mod.Values{ int64(0), time.Duration(0), gocql.Duration{}, }.AddVariants(mod.CustomType), }.Run("[nil]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: make([]byte, 0), Values: mod.Values{ int64(0), time.Duration(0), "0s", gocql.Duration{}, }.AddVariants(mod.All...), }.Run("[]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00"), Values: mod.Values{ int64(0), time.Duration(0), "0s", gocql.Duration{}, }.AddVariants(mod.All...), }.Run("zeros", t, marshal, unmarshal) // sets for months serialization.PositiveSet{ Data: []byte("\x02\x00\x00"), Values: mod.Values{ gocql.Duration{Months: 1, Days: 0, Nanoseconds: 0}, "1mo", }.AddVariants(mod.All...), }.Run("months1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x01\x00\x00"), Values: mod.Values{ gocql.Duration{Months: -1, Days: 0, Nanoseconds: 0}, "-1mo", }.AddVariants(mod.All...), }.Run("months-1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x80\xfe\x00\x00"), Values: mod.Values{ gocql.Duration{Months: math.MaxInt8, Days: 0, Nanoseconds: 0}, "10y7mo", }.AddVariants(mod.All...), }.Run("monthsMaxInt8", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x80\xff\x00\x00"), Values: mod.Values{ gocql.Duration{Months: math.MinInt8, Days: 0, Nanoseconds: 0}, "-10y8mo", }.AddVariants(mod.All...), }.Run("monthsMinInt8", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x81\xfe\x00\x00"), Values: mod.Values{ gocql.Duration{Months: math.MaxUint8, Days: 0, Nanoseconds: 0}, "21y3mo", }.AddVariants(mod.All...), }.Run("monthsMaxUint8", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x81\xfd\x00\x00"), Values: mod.Values{ gocql.Duration{Months: -math.MaxUint8, Days: 0, Nanoseconds: 0}, "-21y3mo", }.AddVariants(mod.All...), }.Run("monthsMinUint8", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xc0\xff\xfe\x00\x00"), Values: mod.Values{ gocql.Duration{Months: math.MaxInt16, Days: 0, Nanoseconds: 0}, "2730y7mo", }.AddVariants(mod.All...), }.Run("monthsMaxInt16", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xc0\xff\xff\x00\x00"), Values: mod.Values{ gocql.Duration{Months: math.MinInt16, Days: 0, Nanoseconds: 0}, "-2730y8mo", }.AddVariants(mod.All...), }.Run("monthsMinInt16", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xc1\xff\xfe\x00\x00"), Values: mod.Values{ gocql.Duration{Months: math.MaxUint16, Days: 0, Nanoseconds: 0}, "5461y3mo", }.AddVariants(mod.All...), }.Run("monthsMaxUint16", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xc1\xff\xfd\x00\x00"), Values: mod.Values{ gocql.Duration{Months: -math.MaxUint16, Days: 0, Nanoseconds: 0}, "-5461y3mo", }.AddVariants(mod.All...), }.Run("monthsMinUint16", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xf0\xff\xff\xff\xfe\x00\x00"), Values: mod.Values{ gocql.Duration{Months: math.MaxInt32, Days: 0, Nanoseconds: 0}, "178956970y7mo", }.AddVariants(mod.All...), }.Run("monthsMaxInt32", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xf0\xff\xff\xff\xff\x00\x00"), Values: mod.Values{ gocql.Duration{Months: math.MinInt32, Days: 0, Nanoseconds: 0}, "-178956970y8mo", }.AddVariants(mod.All...), }.Run("monthsMinInt32", t, marshal, unmarshal) // sets for days serialization.PositiveSet{ Data: []byte("\x00\x02\x00"), Values: mod.Values{ gocql.Duration{Months: 0, Days: 1, Nanoseconds: 0}, "1d", }.AddVariants(mod.All...), }.Run("days1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x01\x00"), Values: mod.Values{ gocql.Duration{Months: 0, Days: -1, Nanoseconds: 0}, "-1d", }.AddVariants(mod.All...), }.Run("days-1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x80\xfe\x00"), Values: mod.Values{ gocql.Duration{Months: 0, Days: math.MaxInt8, Nanoseconds: 0}, "18w1d", }.AddVariants(mod.All...), }.Run("daysMaxInt8", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x80\xff\x00"), Values: mod.Values{ gocql.Duration{Months: 0, Days: math.MinInt8, Nanoseconds: 0}, "-18w2d", }.AddVariants(mod.All...), }.Run("daysMinInt8", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x81\xfe\x00"), Values: mod.Values{ gocql.Duration{Months: 0, Days: math.MaxUint8, Nanoseconds: 0}, "36w3d", }.AddVariants(mod.All...), }.Run("daysMaxUint8", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x81\xfd\x00"), Values: mod.Values{ gocql.Duration{Months: 0, Days: -math.MaxUint8, Nanoseconds: 0}, "-36w3d", }.AddVariants(mod.All...), }.Run("daysMinUint8", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\xc0\xff\xfe\x00"), Values: mod.Values{ gocql.Duration{Months: 0, Days: math.MaxInt16, Nanoseconds: 0}, "4680w7d", }.AddVariants(mod.All...), }.Run("daysMaxInt16", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\xc0\xff\xff\x00"), Values: mod.Values{ gocql.Duration{Months: 0, Days: math.MinInt16, Nanoseconds: 0}, "-4681w1d", }.AddVariants(mod.All...), }.Run("daysMinInt16", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\xc1\xff\xfe\x00"), Values: mod.Values{ gocql.Duration{Months: 0, Days: math.MaxUint16, Nanoseconds: 0}, "9362w1d", }.AddVariants(mod.All...), }.Run("daysMaxUint16", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\xc1\xff\xfd\x00"), Values: mod.Values{ gocql.Duration{Months: 0, Days: -math.MaxUint16, Nanoseconds: 0}, "-9362w1d", }.AddVariants(mod.All...), }.Run("daysMinUint16", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\xf0\xff\xff\xff\xfe\x00"), Values: mod.Values{ gocql.Duration{Months: 0, Days: math.MaxInt32, Nanoseconds: 0}, "306783378w1d", }.AddVariants(mod.All...), }.Run("daysMaxInt32", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\xf0\xff\xff\xff\xff\x00"), Values: mod.Values{ gocql.Duration{Months: 0, Days: math.MinInt32, Nanoseconds: 0}, "-306783378w2d", }.AddVariants(mod.All...), }.Run("daysMinInt32", t, marshal, unmarshal) //sets for nanoseconds serialization.PositiveSet{ Data: []byte("\x00\x00\x02"), Values: mod.Values{ int64(1), time.Duration(1), time.Duration(1).String(), gocql.Duration{Months: 0, Days: 0, Nanoseconds: 1}, }.AddVariants(mod.All...), }.Run("nanos1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x01"), Values: mod.Values{ int64(-1), time.Duration(-1), time.Duration(-1).String(), gocql.Duration{Months: 0, Days: 0, Nanoseconds: -1}, }.AddVariants(mod.All...), }.Run("nanos-1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x80\xfe"), Values: mod.Values{ int64(math.MaxInt8), time.Duration(math.MaxInt8), time.Duration(math.MaxInt8).String(), gocql.Duration{Months: 0, Days: 0, Nanoseconds: math.MaxInt8}, }.AddVariants(mod.All...), }.Run("nanosMaxInt8", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x80\xff"), Values: mod.Values{ int64(math.MinInt8), time.Duration(math.MinInt8), time.Duration(math.MinInt8).String(), gocql.Duration{Months: 0, Days: 0, Nanoseconds: math.MinInt8}, }.AddVariants(mod.All...), }.Run("nanosMinInt8", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x81\xfe"), Values: mod.Values{ int64(math.MaxUint8), time.Duration(math.MaxUint8), time.Duration(math.MaxUint8).String(), gocql.Duration{Months: 0, Days: 0, Nanoseconds: math.MaxUint8}, }.AddVariants(mod.All...), }.Run("nanosMaxUint8", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x81\xfd"), Values: mod.Values{ int64(-math.MaxUint8), time.Duration(-math.MaxUint8), time.Duration(-math.MaxUint8).String(), gocql.Duration{Months: 0, Days: 0, Nanoseconds: -math.MaxUint8}, }.AddVariants(mod.All...), }.Run("nanosMinUint8", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\xc0\xff\xfe"), Values: mod.Values{ int64(math.MaxInt16), time.Duration(math.MaxInt16), "32.767µs", gocql.Duration{Months: 0, Days: 0, Nanoseconds: math.MaxInt16}, }.AddVariants(mod.All...), }.Run("nanosMaxInt16", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\xc0\xff\xff"), Values: mod.Values{ int64(math.MinInt16), time.Duration(math.MinInt16), time.Duration(math.MinInt16).String(), gocql.Duration{Months: 0, Days: 0, Nanoseconds: math.MinInt16}, }.AddVariants(mod.All...), }.Run("nanosMinInt16", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\xc1\xff\xfe"), Values: mod.Values{ int64(math.MaxUint16), time.Duration(math.MaxUint16), time.Duration(math.MaxUint16).String(), gocql.Duration{Months: 0, Days: 0, Nanoseconds: math.MaxUint16}, }.AddVariants(mod.All...), }.Run("nanosMaxUint16", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\xc1\xff\xfd"), Values: mod.Values{ int64(-math.MaxUint16), time.Duration(-math.MaxUint16), time.Duration(-math.MaxUint16).String(), gocql.Duration{Months: 0, Days: 0, Nanoseconds: -math.MaxUint16}, }.AddVariants(mod.All...), }.Run("nanosMinUint16", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\xf0\xff\xff\xff\xfe"), Values: mod.Values{ int64(math.MaxInt32), time.Duration(math.MaxInt32), time.Duration(math.MaxInt32).String(), gocql.Duration{Months: 0, Days: 0, Nanoseconds: math.MaxInt32}, }.AddVariants(mod.All...), }.Run("nanosMaxInt32", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\xf0\xff\xff\xff\xff"), Values: mod.Values{ int64(math.MinInt32), time.Duration(math.MinInt32), time.Duration(math.MinInt32).String(), gocql.Duration{Months: 0, Days: 0, Nanoseconds: math.MinInt32}, }.AddVariants(mod.All...), }.Run("nanosMinInt32", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe"), Values: mod.Values{ gocql.Duration{Months: 0, Days: 0, Nanoseconds: math.MaxInt64}, "2562047h47m16.854775807s", }.AddVariants(mod.All...), }.Run("nanosMaxInt64", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff"), Values: mod.Values{ gocql.Duration{Months: 0, Days: 0, Nanoseconds: math.MinInt64}, "-2562047h47m16.854775808s", }.AddVariants(mod.All...), }.Run("nanosMinInt64", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\xc3\x41\xfe\xfc\x9b\xc5\xc4\x9d\xff\xfe"), Values: mod.Values{ gocql.Duration{Days: 106751, Months: 0, Nanoseconds: 85636854775807}, int64(math.MaxInt64), time.Duration(math.MaxInt64), "15250w1d23h47m16.854775807s", }.AddVariants(mod.All...), }.Run("nanosMax", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\xc3\x41\xfd\xfc\x9b\xc5\xc4\x9d\xff\xff"), Values: mod.Values{ gocql.Duration{Days: -106751, Months: 0, Nanoseconds: -85636854775808}, int64(math.MinInt64), time.Duration(math.MinInt64), "-15250w1d23h47m16.854775808s", }.AddVariants(mod.All...), }.Run("nanosMin", t, marshal, unmarshal) // sets for full range serialization.PositiveSet{ Data: []byte("\x02\x02\x02"), Values: mod.Values{ gocql.Duration{Days: 1, Months: 1, Nanoseconds: 1}, "1mo1d1ns", }.AddVariants(mod.All...), }.Run("111", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x01\x01\x01"), Values: mod.Values{ gocql.Duration{Days: -1, Months: -1, Nanoseconds: -1}, "-1mo1d1ns", }.AddVariants(mod.All...), }.Run("-111", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xf0\xff\xff\xff\xfe\xf0\xff\xff\xff\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xfe"), Values: mod.Values{ gocql.Duration{Days: math.MaxInt32, Months: math.MaxInt32, Nanoseconds: math.MaxInt64}, "178956970y7mo306783378w1d2562047h47m16.854775807s", }.AddVariants(mod.All...), }.Run("max", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xf0\xff\xff\xff\xff\xf0\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"), Values: mod.Values{ gocql.Duration{Days: math.MinInt32, Months: math.MinInt32, Nanoseconds: math.MinInt64}, "-178956970y8mo306783378w2d2562047h47m16.854775808s", }.AddVariants(mod.All...), }.Run("min", t, marshal, unmarshal) } ================================================ FILE: tests/serialization/marshal_19_list_set_v3_corrupt_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "math" "testing" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" ) func TestMarshalSetListV3Corrupt(t *testing.T) { t.Parallel() elem := gocql.NewNativeType(3, gocql.TypeSmallInt) tTypes := []gocql.TypeInfo{ gocql.NewCollectionType(gocql.NewNativeType(3, gocql.TypeList), nil, elem), gocql.NewCollectionType(gocql.NewNativeType(3, gocql.TypeSet), nil, elem), } // unmarshal data than bigger the normal data, does not return error. brokenBigData := serialization.GetTypes(mod.Values{ []int16{}, []*int16{}, []mod.Int16{}, []*mod.Int16{}, [1]int16{}, [1]*int16{}, [1]mod.Int16{}, [1]*mod.Int16{}, }.AddVariants(mod.All...)...) brokenBigDataSlices := serialization.GetTypes(mod.Values{ []int16{}, []*int16{}, []mod.Int16{}, []*mod.Int16{}, }.AddVariants(mod.All...)...) refInt32 := func(v int32) *int32 { return &v } refModInt32 := func(v mod.Int32) *mod.Int32 { return &v } for _, tType := range tTypes { marshal := func(i any) ([]byte, error) { return gocql.Marshal(tType, i) } unmarshal := func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) } t.Run(tType.Type().String(), func(t *testing.T) { val := int32(math.MaxInt16 + 1) valc := mod.Int32(val) serialization.NegativeMarshalSet{ Values: mod.Values{ []int32{val}, []*int32{refInt32(val)}, [1]int32{val}, [1]*int32{refInt32(val)}, []mod.Int32{valc}, []*mod.Int32{refModInt32(valc)}, [1]mod.Int32{valc}, [1]*mod.Int32{refModInt32(valc)}, }.AddVariants(mod.All...), }.Run("big_vals", t, marshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x01\x00\x00\x00\x02\xff\xff\x01"), Values: mod.Values{ []int16{}, []*int16{}, []mod.Int16{}, []*mod.Int16{}, [1]int16{}, [1]*int16{}, [1]mod.Int16{}, [1]*mod.Int16{}, }.AddVariants(mod.All...), BrokenTypes: brokenBigData, }.Run("big_data_elem1+", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x01\x00\x00\x00\x00\xff"), Values: mod.Values{ []int16{}, []*int16{}, []mod.Int16{}, []*mod.Int16{}, [1]int16{}, [1]*int16{}, [1]mod.Int16{}, [1]*mod.Int16{}, }.AddVariants(mod.All...), BrokenTypes: brokenBigData, }.Run("big_data_zeroElem1+", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x00\x01"), Values: mod.Values{ []int16{}, []*int16{}, []mod.Int16{}, []*mod.Int16{}, [1]int16{}, [1]*int16{}, [1]mod.Int16{}, [1]*mod.Int16{}, }.AddVariants(mod.All...), BrokenTypes: brokenBigDataSlices, }.Run("big_data_elem0+", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x01\x00\x00\x00\x02\xff"), Values: mod.Values{ []int16{}, []*int16{}, []mod.Int16{}, []*mod.Int16{}, [1]int16{}, [1]*int16{}, [1]mod.Int16{}, [1]*mod.Int16{}, }.AddVariants(mod.All...), }.Run("small_data_elem_value-", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x01\x00\x00\x00\x02"), Values: mod.Values{ []int16{}, []*int16{}, []mod.Int16{}, []*mod.Int16{}, [1]int16{}, [1]*int16{}, [1]mod.Int16{}, [1]*mod.Int16{}, }.AddVariants(mod.All...), }.Run("small_data_elem_value--", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x01\x00"), Values: mod.Values{ []int16{}, []*int16{}, []mod.Int16{}, []*mod.Int16{}, [1]int16{}, [1]*int16{}, [1]mod.Int16{}, [1]*mod.Int16{}, }.AddVariants(mod.All...), }.Run("small_data_elem_len-", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x01"), Values: mod.Values{ []int16{}, []*int16{}, []mod.Int16{}, []*mod.Int16{}, [1]int16{}, [1]*int16{}, [1]mod.Int16{}, [1]*mod.Int16{}, }.AddVariants(mod.All...), }.Run("small_data_elem-", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00"), Values: mod.Values{ []int16{}, []*int16{}, []mod.Int16{}, []*mod.Int16{}, [1]int16{}, [1]*int16{}, [1]mod.Int16{}, [1]*mod.Int16{}, }.AddVariants(mod.All...), }.Run("small_data_elems-", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: nil, Values: mod.Values{ [1]int16{}, [1]*int16{}, [1]mod.Int16{}, [1]*mod.Int16{}, }.AddVariants(mod.CustomType), }.Run("nil_data_to_array", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: make([]byte, 0), Values: mod.Values{ [1]int16{}, [1]*int16{}, [1]mod.Int16{}, [1]*mod.Int16{}, }.AddVariants(mod.CustomType), }.Run("zero_data_to_array", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x00"), Values: mod.Values{ [1]int16{}, [1]*int16{}, [1]mod.Int16{}, [1]*mod.Int16{}, }.AddVariants(mod.All...), }.Run("zero_elems_to_array", t, unmarshal) }) } } ================================================ FILE: tests/serialization/marshal_19_list_set_v3_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "testing" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" ) func TestMarshalSetListV3(t *testing.T) { t.Parallel() elem := gocql.NewNativeType(3, gocql.TypeSmallInt) tTypes := []gocql.TypeInfo{ gocql.NewCollectionType(gocql.NewNativeType(3, gocql.TypeList), nil, elem), gocql.NewCollectionType(gocql.NewNativeType(3, gocql.TypeSet), nil, elem), } // unmarshal `zero` data return an error brokenZeroDataUnmarshal := serialization.GetTypes(mod.Values{ []int16{}, []*int16{}, []mod.Int16{}, []*mod.Int16{}, &[]int16{}, &[]*int16{}, &[]mod.Int16{}, &[]*mod.Int16{}, (*[1]int16)(nil), (*[1]*int16)(nil), (*[1]mod.Int16)(nil), (*[1]*mod.Int16)(nil), }.AddVariants(mod.CustomType)...) refInt16 := func(v int16) *int16 { return &v } refModInt16 := func(v mod.Int16) *mod.Int16 { return &v } for _, tType := range tTypes { marshal := func(i any) ([]byte, error) { return gocql.Marshal(tType, i) } unmarshal := func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) } t.Run(tType.Type().String(), func(t *testing.T) { serialization.PositiveSet{ Data: nil, Values: mod.Values{ ([]int16)(nil), ([]*int16)(nil), ([]mod.Int16)(nil), ([]*mod.Int16)(nil), (*[]int16)(nil), (*[]*int16)(nil), (*[]mod.Int16)(nil), (*[]*mod.Int16)(nil), (*[1]int16)(nil), (*[1]*int16)(nil), (*[1]mod.Int16)(nil), (*[1]*mod.Int16)(nil), }.AddVariants(mod.CustomType), }.Run("[nil]nullable", t, marshal, unmarshal) serialization.PositiveSet{ Data: make([]byte, 0), Values: mod.Values{ []int16{}, []*int16{}, []mod.Int16{}, []*mod.Int16{}, &[]int16{}, &[]*int16{}, &[]mod.Int16{}, &[]*mod.Int16{}, (*[1]int16)(nil), (*[1]*int16)(nil), (*[1]mod.Int16)(nil), (*[1]*mod.Int16)(nil), }.AddVariants(mod.CustomType), BrokenUnmarshalTypes: brokenZeroDataUnmarshal, }.Run("[]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00"), Values: mod.Values{ []int16{}, []*int16{}, []mod.Int16{}, []*mod.Int16{}, }.AddVariants(mod.All...), }.Run("zero elems", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00"), Values: mod.Values{ []int16{0}, []*int16{refInt16(0)}, []mod.Int16{0}, []*mod.Int16{refModInt16(0)}, [1]int16{0}, [1]*int16{refInt16(0)}, [1]mod.Int16{0}, [1]*mod.Int16{refModInt16(0)}, }.AddVariants(mod.All...), }.Run("[]{0}", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x01\x00\x00\x00\x00"), Values: mod.Values{ []int16{0}, []*int16{refInt16(0)}, []mod.Int16{0}, []*mod.Int16{refModInt16(0)}, [1]int16{0}, [1]*int16{refInt16(0)}, [1]mod.Int16{0}, [1]*mod.Int16{refModInt16(0)}, }.AddVariants(mod.All...), }.Run("[]{zero elem}unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x01\x00\x00\x00\x02\x7f\xff"), Values: mod.Values{ []int16{32767}, []*int16{refInt16(32767)}, []mod.Int16{32767}, []*mod.Int16{refModInt16(32767)}, [1]int16{32767}, [1]*int16{refInt16(32767)}, [1]mod.Int16{32767}, [1]*mod.Int16{refModInt16(32767)}, }.AddVariants(mod.All...), }.Run("[]{max}", t, marshal, unmarshal) }) } } ================================================ FILE: tests/serialization/marshal_1_boolean_corrupt_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "testing" "github.com/gocql/gocql/serialization/boolean" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" ) func TestMarshalBooleanCorrupt(t *testing.T) { t.Parallel() tType := gocql.NewNativeType(4, gocql.TypeBoolean) type testSuite struct { name string marshal func(any) ([]byte, error) unmarshal func(bytes []byte, i any) error } testSuites := [2]testSuite{ { name: "serialization.boolean", marshal: boolean.Marshal, unmarshal: boolean.Unmarshal, }, { name: "glob", marshal: func(i any) ([]byte, error) { return gocql.Marshal(tType, i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) }, }, } for _, tSuite := range testSuites { unmarshal := tSuite.unmarshal t.Run(tSuite.name, func(t *testing.T) { t.Parallel() serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00"), Values: mod.Values{ false, }.AddVariants(mod.All...), }.Run("big_data", t, unmarshal) }) } } ================================================ FILE: tests/serialization/marshal_1_boolean_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "testing" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" "github.com/gocql/gocql/serialization/boolean" ) func TestMarshalBoolean(t *testing.T) { t.Parallel() tType := gocql.NewNativeType(4, gocql.TypeBoolean) type testSuite struct { name string marshal func(any) ([]byte, error) unmarshal func(bytes []byte, i any) error } testSuites := [2]testSuite{ { name: "serialization.boolean", marshal: boolean.Marshal, unmarshal: boolean.Unmarshal, }, { name: "glob", marshal: func(i any) ([]byte, error) { return gocql.Marshal(tType, i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) }, }, } for _, tSuite := range testSuites { marshal := tSuite.marshal unmarshal := tSuite.unmarshal t.Run(tSuite.name, func(t *testing.T) { t.Parallel() serialization.PositiveSet{ Data: nil, Values: mod.Values{(*bool)(nil)}.AddVariants(mod.CustomType), }.Run("[nil]nullable", t, marshal, unmarshal) serialization.PositiveSet{ Data: nil, Values: mod.Values{false}.AddVariants(mod.CustomType), }.Run("[nil]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: make([]byte, 0), Values: mod.Values{false}.AddVariants(mod.All...), }.Run("[]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: []byte("\x00"), Values: mod.Values{false}.AddVariants(mod.All...), }.Run("zeros", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x01"), Values: mod.Values{true}.AddVariants(mod.All...), }.Run("[1]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: []byte("\xff"), Values: mod.Values{true}.AddVariants(mod.All...), }.Run("[255]", t, nil, unmarshal) }) } } ================================================ FILE: tests/serialization/marshal_20_map_v3_corrupt_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "math" "testing" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" ) func TestMarshalMapV3Corrupt(t *testing.T) { t.Parallel() elem := gocql.NewNativeType(3, gocql.TypeSmallInt) tType := gocql.NewCollectionType(gocql.NewNativeType(3, gocql.TypeMap), elem, elem) //unmarshal data than bigger the normal data, does not return error. brokenBigData := serialization.GetTypes(mod.Values{ make(map[int16]int16), make(map[int16]*int16), make(map[mod.Int16]mod.Int16), make(map[mod.Int16]*mod.Int16), }.AddVariants(mod.All...)...) refInt32 := func(v int32) *int32 { return &v } refModInt32 := func(v mod.Int32) *mod.Int32 { return &v } marshal := func(i any) ([]byte, error) { return gocql.Marshal(tType, i) } unmarshal := func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) } val := int32(math.MaxInt16 + 1) valc := mod.Int32(val) serialization.NegativeMarshalSet{ Values: mod.Values{ map[int32]int32{val: val}, map[int32]int32{val: 0}, map[int32]int32{0: val}, map[int32]*int32{val: refInt32(val)}, map[int32]*int32{val: refInt32(0)}, map[int32]*int32{0: refInt32(val)}, map[mod.Int32]mod.Int32{valc: valc}, map[mod.Int32]mod.Int32{valc: 0}, map[mod.Int32]mod.Int32{0: valc}, map[mod.Int32]*mod.Int32{valc: refModInt32(valc)}, map[mod.Int32]*mod.Int32{valc: refModInt32(0)}, map[mod.Int32]*mod.Int32{0: refModInt32(valc)}, }.AddVariants(mod.All...), }.Run("big_vals", t, marshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x01\x00\x00\x00\x02\xff\xff\x00\x00\x00\x02\xff\xff\x01"), Values: mod.Values{ make(map[int16]int16), make(map[int16]*int16), make(map[mod.Int16]mod.Int16), make(map[mod.Int16]*mod.Int16), }.AddVariants(mod.All...), BrokenTypes: brokenBigData, }.Run("big_data_elem1+", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\xff"), Values: mod.Values{ make(map[int16]int16), make(map[int16]*int16), make(map[mod.Int16]mod.Int16), make(map[mod.Int16]*mod.Int16), }.AddVariants(mod.All...), BrokenTypes: brokenBigData, }.Run("big_data_zeroElem1+", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x00\x01"), Values: mod.Values{ make(map[int16]int16), make(map[int16]*int16), make(map[mod.Int16]mod.Int16), make(map[mod.Int16]*mod.Int16), }.AddVariants(mod.All...), BrokenTypes: brokenBigData, }.Run("big_data_elems0+", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x01\x00\x00\x00\x02\xff\xff\x00\x00\x00\x02\xff"), Values: mod.Values{ make(map[int16]int16), make(map[int16]*int16), make(map[mod.Int16]mod.Int16), make(map[mod.Int16]*mod.Int16), }.AddVariants(mod.All...), }.Run("small_data_val_value-", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x01\x00\x00\x00\x02\xff\xff\x00\x00\x00\x02"), Values: mod.Values{ make(map[int16]int16), make(map[int16]*int16), make(map[mod.Int16]mod.Int16), make(map[mod.Int16]*mod.Int16), }.AddVariants(mod.All...), }.Run("small_data_val_len", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x01\x00\x00\x00\x02\xff\xff\x00\x00"), Values: mod.Values{ make(map[int16]int16), make(map[int16]*int16), make(map[mod.Int16]mod.Int16), make(map[mod.Int16]*mod.Int16), }.AddVariants(mod.All...), }.Run("small_data_val_len-", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x01\x00\x00\x00\x02\xff\xff"), Values: mod.Values{ make(map[int16]int16), make(map[int16]*int16), make(map[mod.Int16]mod.Int16), make(map[mod.Int16]*mod.Int16), }.AddVariants(mod.All...), }.Run("small_data_val-", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x01\x00\x00\x00\x02\xff"), Values: mod.Values{ make(map[int16]int16), make(map[int16]*int16), make(map[mod.Int16]mod.Int16), make(map[mod.Int16]*mod.Int16), }.AddVariants(mod.All...), }.Run("small_data_key_value-", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x01\x00\x00\x00\x02"), Values: mod.Values{ make(map[int16]int16), make(map[int16]*int16), make(map[mod.Int16]mod.Int16), make(map[mod.Int16]*mod.Int16), }.AddVariants(mod.All...), }.Run("small_data_key_len", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x01\x00\x00"), Values: mod.Values{ make(map[int16]int16), make(map[int16]*int16), make(map[mod.Int16]mod.Int16), make(map[mod.Int16]*mod.Int16), }.AddVariants(mod.All...), }.Run("small_data_key_len-", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x01"), Values: mod.Values{ make(map[int16]int16), make(map[int16]*int16), make(map[mod.Int16]mod.Int16), make(map[mod.Int16]*mod.Int16), }.AddVariants(mod.All...), }.Run("small_data_pair-", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00"), Values: mod.Values{ make(map[int16]int16), make(map[int16]*int16), make(map[mod.Int16]mod.Int16), make(map[mod.Int16]*mod.Int16), }.AddVariants(mod.All...), }.Run("small_data_elems-", t, unmarshal) } ================================================ FILE: tests/serialization/marshal_20_map_v3_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "testing" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" ) func TestMarshalMapV3(t *testing.T) { t.Parallel() elem := gocql.NewNativeType(3, gocql.TypeSmallInt) tType := gocql.NewCollectionType(gocql.NewNativeType(3, gocql.TypeMap), elem, elem) refInt16 := func(v int16) *int16 { return &v } refModInt16 := func(v mod.Int16) *mod.Int16 { return &v } marshal := func(i any) ([]byte, error) { return gocql.Marshal(tType, i) } unmarshal := func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) } serialization.PositiveSet{ Data: nil, Values: mod.Values{ (map[int16]int16)(nil), (map[int16]*int16)(nil), (map[mod.Int16]mod.Int16)(nil), (map[mod.Int16]*mod.Int16)(nil), (*map[int16]int16)(nil), (*map[int16]*int16)(nil), (*map[mod.Int16]mod.Int16)(nil), (*map[mod.Int16]*mod.Int16)(nil), }.AddVariants(mod.CustomType), }.Run("[nil]nullable", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00"), Values: mod.Values{ make(map[int16]int16), make(map[int16]*int16), make(map[mod.Int16]mod.Int16), make(map[mod.Int16]*mod.Int16), }.AddVariants(mod.All...), }.Run("zero elems", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00"), Values: mod.Values{ map[int16]int16{0: 0}, map[int16]*int16{0: refInt16(0)}, map[mod.Int16]mod.Int16{0: 0}, map[mod.Int16]*mod.Int16{0: refModInt16(0)}, }.AddVariants(mod.All...), }.Run("[]{zero elem}unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x00\x00\x02\x00\x00"), Values: mod.Values{ map[int16]int16{0: 0}, map[int16]*int16{0: refInt16(0)}, map[mod.Int16]mod.Int16{0: 0}, map[mod.Int16]*mod.Int16{0: refModInt16(0)}, }.AddVariants(mod.All...), }.Run("[]{0:0}", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x01\x00\x00\x00\x02\x7f\xff\x00\x00\x00\x02\x7f\xff"), Values: mod.Values{ map[int16]int16{32767: 32767}, map[int16]*int16{32767: refInt16(32767)}, map[mod.Int16]mod.Int16{32767: 32767}, map[mod.Int16]*mod.Int16{32767: refModInt16(32767)}, }.AddVariants(mod.All...), }.Run("[]{max:max}", t, marshal, unmarshal) } ================================================ FILE: tests/serialization/marshal_2_tinyint_corrupt_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "math/big" "testing" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" "github.com/gocql/gocql/serialization/tinyint" ) func TestMarshalTinyintCorrupt(t *testing.T) { t.Parallel() type testSuite struct { name string marshal func(any) ([]byte, error) unmarshal func(bytes []byte, i any) error } tType := gocql.NewNativeType(4, gocql.TypeTinyInt) testSuites := [2]testSuite{ { name: "serialization.tinyint", marshal: tinyint.Marshal, unmarshal: tinyint.Unmarshal, }, { name: "glob", marshal: func(i any) ([]byte, error) { return gocql.Marshal(tType, i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) }, }, } for _, tSuite := range testSuites { marshal := tSuite.marshal unmarshal := tSuite.unmarshal t.Run(tSuite.name, func(t *testing.T) { t.Parallel() serialization.NegativeMarshalSet{ Values: mod.Values{ int16(128), int32(128), int64(128), int(128), "128", *big.NewInt(128), int16(-129), int32(-129), int64(-129), int(-129), "-129", *big.NewInt(-129), uint16(256), uint32(256), uint64(256), uint(256), }.AddVariants(mod.All...), }.Run("big_vals", t, marshal) serialization.NegativeMarshalSet{ Values: mod.Values{"1s2", "1s", "-1s", ".1", ",1", "0.1", "0,1"}.AddVariants(mod.All...), }.Run("corrupt_vals", t, marshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x80\x00"), Values: mod.Values{ int8(0), int16(0), int32(0), int64(0), int(0), uint8(0), uint16(0), uint32(0), uint64(0), uint(0), "", *big.NewInt(0), }.AddVariants(mod.All...), }.Run("big_data", t, unmarshal) }) } } ================================================ FILE: tests/serialization/marshal_2_tinyint_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "math/big" "testing" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" "github.com/gocql/gocql/serialization/tinyint" ) func TestMarshalTinyint(t *testing.T) { t.Parallel() type testSuite struct { name string marshal func(any) ([]byte, error) unmarshal func(bytes []byte, i any) error } tType := gocql.NewNativeType(4, gocql.TypeTinyInt) testSuites := [2]testSuite{ { name: "serialization.tinyint", marshal: tinyint.Marshal, unmarshal: tinyint.Unmarshal, }, { name: "glob", marshal: func(i any) ([]byte, error) { return gocql.Marshal(tType, i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) }, }, } for _, tSuite := range testSuites { marshal := tSuite.marshal unmarshal := tSuite.unmarshal t.Run(tSuite.name, func(t *testing.T) { t.Parallel() serialization.PositiveSet{ Data: nil, Values: mod.Values{ (*int8)(nil), (*int16)(nil), (*int32)(nil), (*int64)(nil), (*int)(nil), (*uint8)(nil), (*uint16)(nil), (*uint32)(nil), (*uint64)(nil), (*uint)(nil), (*string)(nil), (*big.Int)(nil), string(""), }.AddVariants(mod.CustomType), }.Run("[nil]nullable", t, marshal, unmarshal) serialization.PositiveSet{ Data: nil, Values: mod.Values{ int8(0), int16(0), int32(0), int64(0), int(0), uint8(0), uint16(0), uint32(0), uint64(0), uint(0), "", big.Int{}, }.AddVariants(mod.CustomType), }.Run("[nil]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: make([]byte, 0), Values: mod.Values{ int8(0), int16(0), int32(0), int64(0), int(0), uint8(0), uint16(0), uint32(0), uint64(0), uint(0), "0", *big.NewInt(0), }.AddVariants(mod.All...), }.Run("[]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: []byte("\x00"), Values: mod.Values{ int8(0), int16(0), int32(0), int64(0), int(0), uint8(0), uint16(0), uint32(0), uint64(0), uint(0), "0", *big.NewInt(0), }.AddVariants(mod.All...), }.Run("zeros", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x01"), Values: mod.Values{ int8(1), int16(1), int32(1), int64(1), int(1), uint8(1), uint16(1), uint32(1), uint64(1), uint(1), "1", *big.NewInt(1)}.AddVariants(mod.All...), }.Run("1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff"), Values: mod.Values{int8(-1), int16(-1), int32(-1), int64(-1), int(-1), "-1", *big.NewInt(-1)}.AddVariants(mod.All...), }.Run("-1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x7f"), Values: mod.Values{ int8(127), int16(127), int32(127), int64(127), int(127), uint8(127), uint16(127), uint32(127), uint64(127), uint(127), "127", *big.NewInt(127)}.AddVariants(mod.All...), }.Run("127", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x80"), Values: mod.Values{int8(-128), int16(-128), int32(-128), int64(-128), int(-128), "-128", *big.NewInt(-128)}.AddVariants(mod.All...), }.Run("-128", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff"), Values: mod.Values{uint8(255), uint16(255), uint32(255), uint64(255), uint(255)}.AddVariants(mod.All...), }.Run("255", t, marshal, unmarshal) }) } } ================================================ FILE: tests/serialization/marshal_3_smallint_corrupt_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "math/big" "testing" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" "github.com/gocql/gocql/serialization/smallint" ) func TestMarshalSmallintCorrupt(t *testing.T) { t.Parallel() type testSuite struct { name string marshal func(any) ([]byte, error) unmarshal func(bytes []byte, i any) error } tType := gocql.NewNativeType(4, gocql.TypeSmallInt) testSuites := [2]testSuite{ { name: "serialization.smallint", marshal: smallint.Marshal, unmarshal: smallint.Unmarshal, }, { name: "glob", marshal: func(i any) ([]byte, error) { return gocql.Marshal(tType, i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) }, }, } for _, tSuite := range testSuites { marshal := tSuite.marshal unmarshal := tSuite.unmarshal t.Run(tSuite.name, func(t *testing.T) { t.Parallel() serialization.NegativeMarshalSet{ Values: mod.Values{ int32(32768), int64(32768), int(32768), "32768", *big.NewInt(32768), int32(-32769), int64(-32769), int(-32769), "-32769", *big.NewInt(-32769), uint32(65536), uint64(65536), uint(65536), }.AddVariants(mod.All...), }.Run("big_vals", t, marshal) serialization.NegativeMarshalSet{ Values: mod.Values{"1s2", "1s", "-1s", ".1", ",1", "0.1", "0,1"}.AddVariants(mod.All...), }.Run("corrupt_vals", t, marshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x80\x00\x00"), Values: mod.Values{ int8(0), int16(0), int32(0), int64(0), int(0), uint8(0), uint16(0), uint32(0), uint64(0), uint(0), "", *big.NewInt(0), }.AddVariants(mod.All...), }.Run("big_data", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x80"), Values: mod.Values{ int8(0), int16(0), int32(0), int64(0), int(0), uint8(0), uint16(0), uint32(0), uint64(0), uint(0), "", *big.NewInt(0), }.AddVariants(mod.All...), }.Run("small_data", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x80"), Values: mod.Values{int8(0)}.AddVariants(mod.All...), }.Run("small_type_int8_128", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x7f\xff"), Values: mod.Values{int8(0)}.AddVariants(mod.All...), }.Run("small_type_int8_32767", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\xff\x7f"), Values: mod.Values{int8(0)}.AddVariants(mod.All...), }.Run("small_type_int8_-129", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x7f\xff"), Values: mod.Values{int8(0)}.AddVariants(mod.All...), }.Run("small_type_int8_-32768", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x01\x00"), Values: mod.Values{uint8(0)}.AddVariants(mod.All...), }.Run("small_type_uint_256", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\xff\xff"), Values: mod.Values{uint8(0)}.AddVariants(mod.All...), }.Run("small_type_uint_65535", t, unmarshal) }) } } ================================================ FILE: tests/serialization/marshal_3_smallint_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "math/big" "testing" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" "github.com/gocql/gocql/serialization/smallint" ) func TestMarshalSmallint(t *testing.T) { t.Parallel() type testSuite struct { name string marshal func(any) ([]byte, error) unmarshal func(bytes []byte, i any) error } tType := gocql.NewNativeType(4, gocql.TypeSmallInt) testSuites := [2]testSuite{ { name: "serialization.smallint", marshal: smallint.Marshal, unmarshal: smallint.Unmarshal, }, { name: "glob", marshal: func(i any) ([]byte, error) { return gocql.Marshal(tType, i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) }, }, } for _, tSuite := range testSuites { marshal := tSuite.marshal unmarshal := tSuite.unmarshal t.Run(tSuite.name, func(t *testing.T) { t.Parallel() serialization.PositiveSet{ Data: nil, Values: mod.Values{ (*int8)(nil), (*int16)(nil), (*int32)(nil), (*int64)(nil), (*int)(nil), (*uint8)(nil), (*uint16)(nil), (*uint32)(nil), (*uint64)(nil), (*uint)(nil), (*string)(nil), (*big.Int)(nil), "", }.AddVariants(mod.CustomType), }.Run("[nil]nullable", t, marshal, unmarshal) serialization.PositiveSet{ Data: nil, Values: mod.Values{ int8(0), int16(0), int32(0), int64(0), int(0), uint8(0), uint16(0), uint32(0), uint64(0), uint(0), "", big.Int{}, }.AddVariants(mod.CustomType), }.Run("[nil]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: make([]byte, 0), Values: mod.Values{ int8(0), int16(0), int32(0), int64(0), int(0), uint8(0), uint16(0), uint32(0), uint64(0), uint(0), "0", *big.NewInt(0), }.AddVariants(mod.All...), }.Run("[]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00"), Values: mod.Values{ int8(0), int16(0), int32(0), int64(0), int(0), uint8(0), uint16(0), uint32(0), uint64(0), uint(0), "0", *big.NewInt(0), }.AddVariants(mod.All...), }.Run("zeros", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x01"), Values: mod.Values{ int8(1), int16(1), int32(1), int64(1), int(1), uint8(1), uint16(1), uint32(1), uint64(1), uint(1), "1", *big.NewInt(1), }.AddVariants(mod.All...), }.Run("1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\xff"), Values: mod.Values{ int8(-1), int16(-1), int32(-1), int64(-1), int(-1), "-1", *big.NewInt(-1), }.AddVariants(mod.All...), }.Run("-1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x7f"), Values: mod.Values{ int8(127), int16(127), int32(127), int64(127), int(127), uint16(127), uint32(127), uint64(127), uint(127), uint(127), "127", *big.NewInt(127), }.AddVariants(mod.All...), }.Run("maxInt8", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\x80"), Values: mod.Values{ int8(-128), int16(-128), int32(-128), int64(-128), int(-128), "-128", *big.NewInt(-128), }.AddVariants(mod.All...), }.Run("minInt8", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x80"), Values: mod.Values{ int16(128), int32(128), int64(128), int(128), uint16(128), uint32(128), uint64(128), uint(128), uint(128), "128", *big.NewInt(128)}.AddVariants(mod.All...), }.Run("maxInt8+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\x7f"), Values: mod.Values{ int16(-129), int32(-129), int64(-129), int(-129), "-129", *big.NewInt(-129), }.AddVariants(mod.All...), }.Run("minInt8-1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x7f\xff"), Values: mod.Values{ int16(32767), int32(32767), int64(32767), int(32767), uint16(32767), uint32(32767), uint64(32767), uint(32767), "32767", *big.NewInt(32767)}.AddVariants(mod.All...), }.Run("maxInt16", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x80\x00"), Values: mod.Values{ int16(-32768), int32(-32768), int64(-32768), int(-32768), "-32768", *big.NewInt(-32768), }.AddVariants(mod.All...), }.Run("minInt16", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\xff"), Values: mod.Values{ uint8(255), uint16(255), uint32(255), uint64(255), uint(255), int16(255), int32(255), int64(255), int(255), "255", *big.NewInt(255), }.AddVariants(mod.All...), }.Run("maxUint8", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x01\x00"), Values: mod.Values{ uint16(256), uint32(256), uint64(256), uint(256), int16(256), int32(256), int64(256), int(256), "256", *big.NewInt(256), }.AddVariants(mod.All...), }.Run("maxUint8+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\xff"), Values: mod.Values{ uint16(65535), uint32(65535), uint64(65535), uint(65535), }.AddVariants(mod.All...), }.Run("maxUint16", t, marshal, unmarshal) }) } } ================================================ FILE: tests/serialization/marshal_4_int_corrupt_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "math/big" "testing" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" "github.com/gocql/gocql/serialization/cqlint" ) func TestMarshalIntCorrupt(t *testing.T) { t.Parallel() type testSuite struct { name string marshal func(any) ([]byte, error) unmarshal func(bytes []byte, i any) error } tType := gocql.NewNativeType(4, gocql.TypeInt) testSuites := [2]testSuite{ { name: "serialization.int", marshal: cqlint.Marshal, unmarshal: cqlint.Unmarshal, }, { name: "glob", marshal: func(i any) ([]byte, error) { return gocql.Marshal(tType, i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) }, }, } for _, tSuite := range testSuites { marshal := tSuite.marshal unmarshal := tSuite.unmarshal t.Run(tSuite.name, func(t *testing.T) { t.Parallel() serialization.NegativeMarshalSet{ Values: mod.Values{ int64(2147483648), int(2147483648), "2147483648", *big.NewInt(2147483648), int64(-2147483649), int(-2147483649), "-2147483649", *big.NewInt(-2147483649), uint64(4294967296), uint(4294967296), }.AddVariants(mod.All...), }.Run("big_vals", t, marshal) serialization.NegativeMarshalSet{ Values: mod.Values{"1s2", "1s", "-1s", ".1", ",1", "0.1", "0,1"}.AddVariants(mod.All...), }.Run("corrupt_vals", t, marshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x80\x00\x00\x00\x00"), Values: mod.Values{ int8(0), int16(0), int32(0), int64(0), int(0), uint8(0), uint16(0), uint32(0), uint64(0), uint(0), "", *big.NewInt(0), }.AddVariants(mod.All...), }.Run("big_data", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x80"), Values: mod.Values{ int8(0), int16(0), int32(0), int64(0), int(0), uint8(0), uint16(0), uint32(0), uint64(0), uint(0), "", *big.NewInt(0), }.AddVariants(mod.All...), }.Run("small_data1", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x80\x00\x00"), Values: mod.Values{ int8(0), int16(0), int32(0), int64(0), int(0), uint8(0), uint16(0), uint32(0), uint64(0), uint(0), "", *big.NewInt(0), }.AddVariants(mod.All...), }.Run("small_data2", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x80\x00\x00\x00"), Values: mod.Values{int8(0), int16(0)}.AddVariants(mod.All...), }.Run("small_types_int_2147483648", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x7f\xff\xff\xff"), Values: mod.Values{int8(0), int16(0)}.AddVariants(mod.All...), }.Run("small_types_int_-2147483647", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x80\x00"), Values: mod.Values{int8(0), int16(0)}.AddVariants(mod.All...), }.Run("small_types_int_32768", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\xff\xff\x7f\xff"), Values: mod.Values{int8(0), int16(0)}.AddVariants(mod.All...), }.Run("small_types_int_-32769", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x80"), Values: mod.Values{int8(0)}.AddVariants(mod.All...), }.Run("small_type_int8_128", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\xff\xff\xff\x7f"), Values: mod.Values{int8(0)}.AddVariants(mod.All...), }.Run("small_type_int8_-129", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\xff\xff\xff\xff"), Values: mod.Values{uint8(0), uint16(0)}.AddVariants(mod.All...), }.Run("small_types_uint_4294967295", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x01\x00\x00"), Values: mod.Values{uint8(0), uint16(0)}.AddVariants(mod.All...), }.Run("small_types_uint_65536", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x01\x00"), Values: mod.Values{uint8(0)}.AddVariants(mod.All...), }.Run("small_type_uint_256", t, unmarshal) }) } } ================================================ FILE: tests/serialization/marshal_4_int_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "math/big" "testing" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" "github.com/gocql/gocql/serialization/cqlint" ) func TestMarshalInt(t *testing.T) { t.Parallel() type testSuite struct { name string marshal func(any) ([]byte, error) unmarshal func(bytes []byte, i any) error } tType := gocql.NewNativeType(4, gocql.TypeInt) testSuites := [2]testSuite{ { name: "serialization.int", marshal: cqlint.Marshal, unmarshal: cqlint.Unmarshal, }, { name: "glob", marshal: func(i any) ([]byte, error) { return gocql.Marshal(tType, i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) }, }, } for _, tSuite := range testSuites { marshal := tSuite.marshal unmarshal := tSuite.unmarshal t.Run(tSuite.name, func(t *testing.T) { t.Parallel() serialization.PositiveSet{ Data: nil, Values: mod.Values{ (*int8)(nil), (*int16)(nil), (*int32)(nil), (*int64)(nil), (*int)(nil), (*uint8)(nil), (*uint16)(nil), (*uint32)(nil), (*uint64)(nil), (*uint)(nil), (*string)(nil), (*big.Int)(nil), "", }.AddVariants(mod.CustomType), }.Run("[nil]nullable", t, marshal, unmarshal) serialization.PositiveSet{ Data: nil, Values: mod.Values{ int8(0), int16(0), int32(0), int64(0), int(0), uint8(0), uint16(0), uint32(0), uint64(0), uint(0), "", big.Int{}, }.AddVariants(mod.CustomType), }.Run("[nil]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: make([]byte, 0), Values: mod.Values{ int8(0), int16(0), int32(0), int64(0), int(0), uint8(0), uint16(0), uint32(0), uint64(0), uint(0), "0", *big.NewInt(0), }.AddVariants(mod.All...), }.Run("[]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00"), Values: mod.Values{ int8(0), int16(0), int32(0), int64(0), int(0), uint8(0), uint16(0), uint32(0), uint64(0), uint(0), "0", *big.NewInt(0), }.AddVariants(mod.All...), }.Run("zeros", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x01"), Values: mod.Values{ int8(1), int16(1), int32(1), int64(1), int(1), uint8(1), uint16(1), uint32(1), uint64(1), uint(1), "1", *big.NewInt(1), }.AddVariants(mod.All...), }.Run("+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\xff\xff\xff"), Values: mod.Values{ int16(-1), int32(-1), int64(-1), int(-1), "-1", *big.NewInt(-1), }.AddVariants(mod.All...), }.Run("-1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x7f"), Values: mod.Values{ int8(127), int16(127), int32(127), int64(127), int(127), uint8(127), uint16(127), uint32(127), uint64(127), uint(127), "127", *big.NewInt(127), }.AddVariants(mod.All...), }.Run("maxInt8", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\xff\xff\x80"), Values: mod.Values{ int8(-128), int16(-128), int32(-128), int64(-128), int(-128), "-128", *big.NewInt(-128), }.AddVariants(mod.All...), }.Run("minInt8", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x80"), Values: mod.Values{ int16(128), int32(128), int64(128), int(128), uint16(128), uint32(128), uint64(128), uint(128), "128", *big.NewInt(128), }.AddVariants(mod.All...), }.Run("maxInt8+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\xff\xff\x7f"), Values: mod.Values{ int16(-129), int32(-129), int64(-129), int(-129), "-129", *big.NewInt(-129), }.AddVariants(mod.All...), }.Run("minInt8-1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x7f\xff"), Values: mod.Values{ int16(32767), int32(32767), int64(32767), int(32767), uint16(32767), uint32(32767), uint64(32767), uint(32767), "32767", *big.NewInt(32767), }.AddVariants(mod.All...), }.Run("maxInt16", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\xff\x80\x00"), Values: mod.Values{ int16(-32768), int32(-32768), int64(-32768), int(-32768), "-32768", *big.NewInt(-32768), }.AddVariants(mod.All...), }.Run("minInt16", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x80\x00"), Values: mod.Values{ int32(32768), int64(32768), int(32768), uint32(32768), uint64(32768), uint(32768), "32768", *big.NewInt(32768), }.AddVariants(mod.All...), }.Run("maxInt16+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\xff\x7f\xff"), Values: mod.Values{ int32(-32769), int64(-32769), int(-32769), "-32769", *big.NewInt(-32769), }.AddVariants(mod.All...), }.Run("minInt16-1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x7f\xff\xff\xff"), Values: mod.Values{ int32(2147483647), int64(2147483647), int(2147483647), uint32(2147483647), uint64(2147483647), uint(2147483647), "2147483647", *big.NewInt(2147483647), }.AddVariants(mod.All...), }.Run("maxInt32", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x80\x00\x00\x00"), Values: mod.Values{ int32(-2147483648), int64(-2147483648), int(-2147483648), "-2147483648", *big.NewInt(-2147483648), }.AddVariants(mod.All...), }.Run("minInt32", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\xff"), Values: mod.Values{ uint8(255), uint16(255), uint32(255), uint64(255), uint(255), int16(255), int32(255), int64(255), int(255), "255", *big.NewInt(255), }.AddVariants(mod.All...), }.Run("maxUint8", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x01\x00"), Values: mod.Values{ uint16(256), uint32(256), uint64(256), uint(256), int16(256), int32(256), int64(256), int(256), "256", *big.NewInt(256), }.AddVariants(mod.All...), }.Run("maxUint8+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\xff\xff"), Values: mod.Values{ uint16(65535), uint32(65535), uint64(65535), uint(65535), int32(65535), int64(65535), int(65535), "65535", *big.NewInt(65535), }.AddVariants(mod.All...), }.Run("maxUint16", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x01\x00\x00"), Values: mod.Values{ uint32(65536), uint64(65536), uint(65536), int32(65536), int64(65536), int(65536), "65536", *big.NewInt(65536), }.AddVariants(mod.All...), }.Run("maxUint16+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\xff\xff\xff"), Values: mod.Values{ uint32(4294967295), uint64(4294967295), uint(4294967295), }.AddVariants(mod.All...), }.Run("maxUint32", t, marshal, unmarshal) }) } } ================================================ FILE: tests/serialization/marshal_5_bigint_corrupt_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "math/big" "testing" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" "github.com/gocql/gocql/serialization/bigint" ) func TestMarshalBigIntCorrupt(t *testing.T) { t.Parallel() type testSuite struct { name string marshal func(any) ([]byte, error) unmarshal func(bytes []byte, i any) error } tType := gocql.NewNativeType(4, gocql.TypeBigInt) testSuites := [2]testSuite{ { name: "serialization.bigint", marshal: bigint.Marshal, unmarshal: bigint.Unmarshal, }, { name: "glob", marshal: func(i any) ([]byte, error) { return gocql.Marshal(tType, i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) }, }, } for _, tSuite := range testSuites { marshal := tSuite.marshal unmarshal := tSuite.unmarshal serialization.NegativeMarshalSet{ Values: mod.Values{ "9223372036854775808", "-9223372036854775809", *big.NewInt(0).Add(big.NewInt(9223372036854775807), big.NewInt(1)), *big.NewInt(0).Add(big.NewInt(-9223372036854775808), big.NewInt(-1)), }.AddVariants(mod.All...), }.Run("big_vals", t, marshal) serialization.NegativeMarshalSet{ Values: mod.Values{"1s2", "1s", "-1s", ".1", ",1", "0.1", "0,1"}.AddVariants(mod.All...), }.Run("corrupt_vals", t, marshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x80\x00\x00\x00\x00\x00\x00\x00\x00"), Values: mod.Values{ int8(0), int16(0), int32(0), int64(0), int(0), uint8(0), uint16(0), uint32(0), uint64(0), uint(0), "", *big.NewInt(0), }.AddVariants(mod.All...), }.Run("big_data", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x80\x00\x00\x00\x00\x00\x00"), Values: mod.Values{ int8(0), int16(0), int32(0), int64(0), int(0), uint8(0), uint16(0), uint32(0), uint64(0), uint(0), "", *big.NewInt(0), }.AddVariants(mod.All...), }.Run("small_data", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x80"), Values: mod.Values{ int8(0), int16(0), int32(0), int64(0), int(0), uint8(0), uint16(0), uint32(0), uint64(0), uint(0), "", *big.NewInt(0), }.AddVariants(mod.All...), }.Run("small_data2", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x00\x00\x00\x00\x80"), Values: mod.Values{int8(0)}.AddVariants(mod.All...), }.Run("small_type_int8_128", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\xff\xff\xff\xff\xff\xff\xff\x7f"), Values: mod.Values{int8(0)}.AddVariants(mod.All...), }.Run("small_type_int8_-129", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x00\x00\x00\x80\x00"), Values: mod.Values{int8(0), int16(0)}.AddVariants(mod.All...), }.Run("small_type_int_32768", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\xff\xff\xff\xff\xff\xff\x7f\xff"), Values: mod.Values{int8(0)}.AddVariants(mod.All...), }.Run("small_type_int8_-32769", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x00\x80\x00\x00\x00"), Values: mod.Values{int8(0), int16(0), int32(0)}.AddVariants(mod.All...), }.Run("small_type_int_2147483647", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\xff\xff\xff\x7f\xff\xff\xff\xff"), Values: mod.Values{int8(0), int16(0), int32(0)}.AddVariants(mod.All...), }.Run("small_type_int_-2147483648", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x00\x00\x00\x01\x00"), Values: mod.Values{uint8(0)}.AddVariants(mod.All...), }.Run("small_type_uint8_256", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x00\x00\x01\x00\x00"), Values: mod.Values{uint8(0), uint16(0)}.AddVariants(mod.All...), }.Run("small_type_uint_65536", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x01\x00\x00\x00\x00"), Values: mod.Values{uint8(0), uint16(0), uint32(0)}.AddVariants(mod.All...), }.Run("small_type_uint_4294967296", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\xff\xff\xff\xff\xff\xff\xff\xff"), Values: mod.Values{uint8(0), uint16(0), uint32(0)}.AddVariants(mod.All...), }.Run("small_type_uint_max", t, unmarshal) } } ================================================ FILE: tests/serialization/marshal_5_bigint_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "math/big" "testing" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" "github.com/gocql/gocql/serialization/bigint" ) func TestMarshalBigInt(t *testing.T) { t.Parallel() type testSuite struct { name string marshal func(any) ([]byte, error) unmarshal func(bytes []byte, i any) error } tType := gocql.NewNativeType(4, gocql.TypeBigInt) testSuites := [2]testSuite{ { name: "serialization.bigint", marshal: bigint.Marshal, unmarshal: bigint.Unmarshal, }, { name: "glob", marshal: func(i any) ([]byte, error) { return gocql.Marshal(tType, i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) }, }, } for _, tSuite := range testSuites { marshal := tSuite.marshal unmarshal := tSuite.unmarshal t.Run(tSuite.name, func(t *testing.T) { t.Parallel() serialization.PositiveSet{ Data: nil, Values: mod.Values{ (*int8)(nil), (*int16)(nil), (*int32)(nil), (*int64)(nil), (*int)(nil), (*uint8)(nil), (*uint16)(nil), (*uint32)(nil), (*uint64)(nil), (*uint)(nil), (*string)(nil), (*big.Int)(nil), "", }.AddVariants(mod.CustomType), }.Run("[nil]nullable", t, marshal, unmarshal) serialization.PositiveSet{ Data: nil, Values: mod.Values{ int8(0), int16(0), int32(0), int64(0), int(0), uint8(0), uint16(0), uint32(0), uint64(0), uint(0), "", big.Int{}, }.AddVariants(mod.CustomType), }.Run("[nil]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: make([]byte, 0), Values: mod.Values{ int8(0), int16(0), int32(0), int64(0), int(0), uint8(0), uint16(0), uint32(0), uint64(0), uint(0), "0", *big.NewInt(0), }.AddVariants(mod.All...), }.Run("[]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00\x00\x00\x00\x00"), Values: mod.Values{ int8(0), int16(0), int32(0), int64(0), int(0), uint8(0), uint16(0), uint32(0), uint64(0), uint(0), "0", *big.NewInt(0), }.AddVariants(mod.All...), }.Run("zeros", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00\x00\x00\x00\x01"), Values: mod.Values{ int8(1), int16(1), int32(1), int64(1), int(1), uint8(1), uint16(1), uint32(1), uint64(1), uint(1), "1", *big.NewInt(1), }.AddVariants(mod.All...), }.Run("+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\xff\xff\xff\xff\xff\xff\xff"), Values: mod.Values{ int8(-1), int16(-1), int32(-1), int64(-1), int(-1), "-1", *big.NewInt(-1), }.AddVariants(mod.All...), }.Run("-1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00\x00\x00\x00\x7f"), Values: mod.Values{ int8(127), int16(127), int32(127), int64(127), int(127), uint8(127), uint16(127), uint32(127), uint64(127), uint(127), "127", *big.NewInt(127), }.AddVariants(mod.All...), }.Run("maxInt8", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\xff\xff\xff\xff\xff\xff\x80"), Values: mod.Values{ int8(-128), int16(-128), int32(-128), int64(-128), int(-128), "-128", *big.NewInt(-128), }.AddVariants(mod.All...), }.Run("minInt8", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00\x00\x00\x00\x80"), Values: mod.Values{ int16(128), int32(128), int64(128), int(128), uint8(128), uint16(128), uint32(128), uint64(128), uint(128), "128", *big.NewInt(128), }.AddVariants(mod.All...), }.Run("maxInt8+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\xff\xff\xff\xff\xff\xff\x7f"), Values: mod.Values{ int16(-129), int32(-129), int64(-129), int(-129), "-129", *big.NewInt(-129), }.AddVariants(mod.All...), }.Run("minInt8-1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00\x00\x00\x7f\xff"), Values: mod.Values{ int16(32767), int32(32767), int64(32767), int(32767), uint16(32767), uint32(32767), uint64(32767), uint(32767), "32767", *big.NewInt(32767), }.AddVariants(mod.All...), }.Run("maxInt16", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\xff\xff\xff\xff\xff\x80\x00"), Values: mod.Values{ int16(-32768), int32(-32768), int64(-32768), int(-32768), "-32768", *big.NewInt(-32768), }.AddVariants(mod.All...), }.Run("minInt16", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00\x00\x00\x80\x00"), Values: mod.Values{ int32(32768), int64(32768), int(32768), uint16(32768), uint32(32768), uint64(32768), uint(32768), "32768", *big.NewInt(32768), }.AddVariants(mod.All...), }.Run("maxInt16+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\xff\xff\xff\xff\xff\x7f\xff"), Values: mod.Values{ int32(-32769), int64(-32769), int(-32769), "-32769", *big.NewInt(-32769), }.AddVariants(mod.All...), }.Run("minInt16-1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00\x7f\xff\xff\xff"), Values: mod.Values{ int32(2147483647), int64(2147483647), int(2147483647), uint32(2147483647), uint64(2147483647), uint(2147483647), "2147483647", *big.NewInt(2147483647), }.AddVariants(mod.All...), }.Run("maxInt32", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\xff\xff\xff\x80\x00\x00\x00"), Values: mod.Values{ int32(-2147483648), int64(-2147483648), int(-2147483648), "-2147483648", *big.NewInt(-2147483648), }.AddVariants(mod.All...), }.Run("minInt32", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00\x80\x00\x00\x00"), Values: mod.Values{ int64(2147483648), int(2147483648), uint32(2147483648), uint64(2147483648), uint(2147483648), "2147483648", *big.NewInt(2147483648), }.AddVariants(mod.All...), }.Run("maxInt32+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\xff\xff\xff\x7f\xff\xff\xff"), Values: mod.Values{ int64(-2147483649), int(-2147483649), "-2147483649", *big.NewInt(-2147483649), }.AddVariants(mod.All...), }.Run("minInt32-1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x7f\xff\xff\xff\xff\xff\xff\xff"), Values: mod.Values{ int64(9223372036854775807), int(9223372036854775807), uint64(9223372036854775807), uint(9223372036854775807), "9223372036854775807", *big.NewInt(9223372036854775807), }.AddVariants(mod.All...), }.Run("maxInt64", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x80\x00\x00\x00\x00\x00\x00\x00"), Values: mod.Values{ int64(-9223372036854775808), int(-9223372036854775808), "-9223372036854775808", *big.NewInt(-9223372036854775808), }.AddVariants(mod.All...), }.Run("minInt64", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00\x00\x00\x00\xff"), Values: mod.Values{ uint8(255), uint16(255), uint32(255), uint64(255), uint(255), int16(255), int32(255), int64(255), int(255), "255", *big.NewInt(255), }.AddVariants(mod.All...), }.Run("maxUint8", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00\x00\x00\x01\x00"), Values: mod.Values{ uint16(256), uint32(256), uint64(256), uint(256), int16(256), int32(256), int64(256), int(256), "256", *big.NewInt(256), }.AddVariants(mod.All...), }.Run("maxUint8+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00\x00\x00\xff\xff"), Values: mod.Values{ uint16(65535), uint32(65535), uint64(65535), uint(65535), int32(65535), int64(65535), int(65535), "65535", *big.NewInt(65535), }.AddVariants(mod.All...), }.Run("maxUint16", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00\x00\x01\x00\x00"), Values: mod.Values{ uint32(65536), uint64(65536), uint(65536), int32(65536), int64(65536), int(65536), "65536", *big.NewInt(65536), }.AddVariants(mod.All...), }.Run("maxUint16+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00\xff\xff\xff\xff"), Values: mod.Values{ uint32(4294967295), uint64(4294967295), uint(4294967295), int64(4294967295), int(4294967295), "4294967295", *big.NewInt(4294967295), }.AddVariants(mod.All...), }.Run("maxUint32", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x01\x00\x00\x00\x00"), Values: mod.Values{ uint64(4294967296), uint(4294967296), int64(4294967296), int(4294967296), "4294967296", *big.NewInt(4294967296), }.AddVariants(mod.All...), }.Run("maxUint32+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\xff\xff\xff\xff\xff\xff\xff"), Values: mod.Values{ uint64(18446744073709551615), uint(18446744073709551615), }.AddVariants(mod.All...), }.Run("maxUint64", t, marshal, unmarshal) }) } } ================================================ FILE: tests/serialization/marshal_6_counter_corrupt_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "math/big" "testing" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" "github.com/gocql/gocql/serialization/counter" ) func TestMarshalCounterCorrupt(t *testing.T) { t.Parallel() type testSuite struct { name string marshal func(any) ([]byte, error) unmarshal func(bytes []byte, i any) error } tType := gocql.NewNativeType(4, gocql.TypeCounter) testSuites := [2]testSuite{ { name: "serialization.counter", marshal: counter.Marshal, unmarshal: counter.Unmarshal, }, { name: "glob", marshal: func(i any) ([]byte, error) { return gocql.Marshal(tType, i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) }, }, } for _, tSuite := range testSuites { marshal := tSuite.marshal unmarshal := tSuite.unmarshal serialization.NegativeMarshalSet{ Values: mod.Values{ "9223372036854775808", "-9223372036854775809", *big.NewInt(0).Add(big.NewInt(9223372036854775807), big.NewInt(1)), *big.NewInt(0).Add(big.NewInt(-9223372036854775808), big.NewInt(-1)), }.AddVariants(mod.All...), }.Run("big_vals", t, marshal) serialization.NegativeMarshalSet{ Values: mod.Values{"1s2", "1s", "-1s", ".1", ",1", "0.1", "0,1"}.AddVariants(mod.All...), }.Run("corrupt_vals", t, marshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x80\x00\x00\x00\x00\x00\x00\x00\x00"), Values: mod.Values{ int8(0), int16(0), int32(0), int64(0), int(0), uint8(0), uint16(0), uint32(0), uint64(0), uint(0), "", *big.NewInt(0), }.AddVariants(mod.All...), }.Run("big_data", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x80\x00\x00\x00\x00\x00\x00"), Values: mod.Values{ int8(0), int16(0), int32(0), int64(0), int(0), uint8(0), uint16(0), uint32(0), uint64(0), uint(0), "", *big.NewInt(0), }.AddVariants(mod.All...), }.Run("small_data", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x80"), Values: mod.Values{ int8(0), int16(0), int32(0), int64(0), int(0), uint8(0), uint16(0), uint32(0), uint64(0), uint(0), "", *big.NewInt(0), }.AddVariants(mod.All...), }.Run("small_data2", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x00\x00\x00\x00\x80"), Values: mod.Values{int8(0)}.AddVariants(mod.All...), }.Run("small_type_int8_128", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\xff\xff\xff\xff\xff\xff\xff\x7f"), Values: mod.Values{int8(0)}.AddVariants(mod.All...), }.Run("small_type_int8_-129", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x00\x00\x00\x80\x00"), Values: mod.Values{int8(0), int16(0)}.AddVariants(mod.All...), }.Run("small_type_int_32768", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\xff\xff\xff\xff\xff\xff\x7f\xff"), Values: mod.Values{int8(0)}.AddVariants(mod.All...), }.Run("small_type_int8_-32769", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x00\x80\x00\x00\x00"), Values: mod.Values{int8(0), int16(0), int32(0)}.AddVariants(mod.All...), }.Run("small_type_int_2147483647", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\xff\xff\xff\x7f\xff\xff\xff\xff"), Values: mod.Values{int8(0), int16(0), int32(0)}.AddVariants(mod.All...), }.Run("small_type_int_-2147483648", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x00\x00\x00\x01\x00"), Values: mod.Values{uint8(0)}.AddVariants(mod.All...), }.Run("small_type_uint8_256", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x00\x00\x01\x00\x00"), Values: mod.Values{uint8(0), uint16(0)}.AddVariants(mod.All...), }.Run("small_type_uint_65536", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x00\x00\x01\x00\x00\x00\x00"), Values: mod.Values{uint8(0), uint16(0), uint32(0)}.AddVariants(mod.All...), }.Run("small_type_uint_4294967296", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\xff\xff\xff\xff\xff\xff\xff\xff"), Values: mod.Values{uint8(0), uint16(0), uint32(0)}.AddVariants(mod.All...), }.Run("small_type_uint_max", t, unmarshal) } } ================================================ FILE: tests/serialization/marshal_6_counter_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "math/big" "testing" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" "github.com/gocql/gocql/serialization/counter" ) func TestMarshalCounter(t *testing.T) { t.Parallel() type testSuite struct { name string marshal func(any) ([]byte, error) unmarshal func(bytes []byte, i any) error } tType := gocql.NewNativeType(4, gocql.TypeCounter) testSuites := [2]testSuite{ { name: "serialization.counter", marshal: counter.Marshal, unmarshal: counter.Unmarshal, }, { name: "glob", marshal: func(i any) ([]byte, error) { return gocql.Marshal(tType, i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) }, }, } for _, tSuite := range testSuites { marshal := tSuite.marshal unmarshal := tSuite.unmarshal t.Run(tSuite.name, func(t *testing.T) { t.Parallel() serialization.PositiveSet{ Data: nil, Values: mod.Values{ (*int8)(nil), (*int16)(nil), (*int32)(nil), (*int64)(nil), (*int)(nil), (*uint8)(nil), (*uint16)(nil), (*uint32)(nil), (*uint64)(nil), (*uint)(nil), (*string)(nil), (*big.Int)(nil), "", }.AddVariants(mod.CustomType), }.Run("[nil]nullable", t, marshal, unmarshal) serialization.PositiveSet{ Data: nil, Values: mod.Values{ int8(0), int16(0), int32(0), int64(0), int(0), uint8(0), uint16(0), uint32(0), uint64(0), uint(0), "", big.Int{}, }.AddVariants(mod.CustomType), }.Run("[nil]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: make([]byte, 0), Values: mod.Values{ int8(0), int16(0), int32(0), int64(0), int(0), uint8(0), uint16(0), uint32(0), uint64(0), uint(0), "0", *big.NewInt(0), }.AddVariants(mod.All...), }.Run("[]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00\x00\x00\x00\x00"), Values: mod.Values{ int8(0), int16(0), int32(0), int64(0), int(0), uint8(0), uint16(0), uint32(0), uint64(0), uint(0), "0", *big.NewInt(0), }.AddVariants(mod.All...), }.Run("zeros", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00\x00\x00\x00\x01"), Values: mod.Values{ int8(1), int16(1), int32(1), int64(1), int(1), uint8(1), uint16(1), uint32(1), uint64(1), uint(1), "1", *big.NewInt(1), }.AddVariants(mod.All...), }.Run("1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\xff\xff\xff\xff\xff\xff\xff"), Values: mod.Values{ int8(-1), int16(-1), int32(-1), int64(-1), int(-1), "-1", *big.NewInt(-1), }.AddVariants(mod.All...), }.Run("-1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00\x00\x00\x00\x7f"), Values: mod.Values{ int8(127), int16(127), int32(127), int64(127), int(127), uint8(127), uint16(127), uint32(127), uint64(127), uint(127), "127", *big.NewInt(127), }.AddVariants(mod.All...), }.Run("maxInt8", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\xff\xff\xff\xff\xff\xff\x80"), Values: mod.Values{ int8(-128), int16(-128), int32(-128), int64(-128), int(-128), "-128", *big.NewInt(-128), }.AddVariants(mod.All...), }.Run("minInt8", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00\x00\x00\x00\x80"), Values: mod.Values{ int16(128), int32(128), int64(128), int(128), uint8(128), uint16(128), uint32(128), uint64(128), uint(128), "128", *big.NewInt(128), }.AddVariants(mod.All...), }.Run("maxInt8+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\xff\xff\xff\xff\xff\xff\x7f"), Values: mod.Values{ int16(-129), int32(-129), int64(-129), int(-129), "-129", *big.NewInt(-129), }.AddVariants(mod.All...), }.Run("minInt8-1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00\x00\x00\x7f\xff"), Values: mod.Values{ int16(32767), int32(32767), int64(32767), int(32767), uint16(32767), uint32(32767), uint64(32767), uint(32767), "32767", *big.NewInt(32767), }.AddVariants(mod.All...), }.Run("maxInt16", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\xff\xff\xff\xff\xff\x80\x00"), Values: mod.Values{ int16(-32768), int32(-32768), int64(-32768), int(-32768), "-32768", *big.NewInt(-32768), }.AddVariants(mod.All...), }.Run("minInt16", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00\x00\x00\x80\x00"), Values: mod.Values{ int32(32768), int64(32768), int(32768), uint16(32768), uint32(32768), uint64(32768), uint(32768), "32768", *big.NewInt(32768), }.AddVariants(mod.All...), }.Run("maxInt16+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\xff\xff\xff\xff\xff\x7f\xff"), Values: mod.Values{ int32(-32769), int64(-32769), int(-32769), "-32769", *big.NewInt(-32769), }.AddVariants(mod.All...), }.Run("minInt16-1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00\x7f\xff\xff\xff"), Values: mod.Values{ int32(2147483647), int64(2147483647), int(2147483647), uint32(2147483647), uint64(2147483647), uint(2147483647), "2147483647", *big.NewInt(2147483647), }.AddVariants(mod.All...), }.Run("maxInt32", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\xff\xff\xff\x80\x00\x00\x00"), Values: mod.Values{ int32(-2147483648), int64(-2147483648), int(-2147483648), "-2147483648", *big.NewInt(-2147483648), }.AddVariants(mod.All...), }.Run("minInt32", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00\x80\x00\x00\x00"), Values: mod.Values{ int64(2147483648), int(2147483648), uint32(2147483648), uint64(2147483648), uint(2147483648), "2147483648", *big.NewInt(2147483648), }.AddVariants(mod.All...), }.Run("maxInt32+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\xff\xff\xff\x7f\xff\xff\xff"), Values: mod.Values{ int64(-2147483649), int(-2147483649), "-2147483649", *big.NewInt(-2147483649), }.AddVariants(mod.All...), }.Run("minInt32-1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x7f\xff\xff\xff\xff\xff\xff\xff"), Values: mod.Values{ int64(9223372036854775807), int(9223372036854775807), uint64(9223372036854775807), uint(9223372036854775807), "9223372036854775807", *big.NewInt(9223372036854775807), }.AddVariants(mod.All...), }.Run("maxInt64", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x80\x00\x00\x00\x00\x00\x00\x00"), Values: mod.Values{ int64(-9223372036854775808), int(-9223372036854775808), "-9223372036854775808", *big.NewInt(-9223372036854775808), }.AddVariants(mod.All...), }.Run("minInt64", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00\x00\x00\x00\xff"), Values: mod.Values{ uint8(255), uint16(255), uint32(255), uint64(255), uint(255), int16(255), int32(255), int64(255), int(255), "255", *big.NewInt(255), }.AddVariants(mod.All...), }.Run("maxUint8", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00\x00\x00\x01\x00"), Values: mod.Values{ uint16(256), uint32(256), uint64(256), uint(256), int16(256), int32(256), int64(256), int(256), "256", *big.NewInt(256), }.AddVariants(mod.All...), }.Run("maxUint8+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00\x00\x00\xff\xff"), Values: mod.Values{ uint16(65535), uint32(65535), uint64(65535), uint(65535), int32(65535), int64(65535), int(65535), "65535", *big.NewInt(65535), }.AddVariants(mod.All...), }.Run("maxUint16", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00\x00\x01\x00\x00"), Values: mod.Values{ uint32(65536), uint64(65536), uint(65536), int32(65536), int64(65536), int(65536), "65536", *big.NewInt(65536), }.AddVariants(mod.All...), }.Run("maxUint16+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00\xff\xff\xff\xff"), Values: mod.Values{ uint32(4294967295), uint64(4294967295), uint(4294967295), int64(4294967295), int(4294967295), "4294967295", *big.NewInt(4294967295), }.AddVariants(mod.All...), }.Run("maxUint32", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x01\x00\x00\x00\x00"), Values: mod.Values{ uint64(4294967296), uint(4294967296), int64(4294967296), int(4294967296), "4294967296", *big.NewInt(4294967296), }.AddVariants(mod.All...), }.Run("maxUint32+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\xff\xff\xff\xff\xff\xff\xff"), Values: mod.Values{ uint64(18446744073709551615), uint(18446744073709551615), }.AddVariants(mod.All...), }.Run("maxUint64", t, marshal, unmarshal) }) } } ================================================ FILE: tests/serialization/marshal_7_varint_corrupt_test.go ================================================ package serialization_test import ( "math/big" "testing" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" "github.com/gocql/gocql/serialization/varint" ) func TestMarshalVarIntCorrupt(t *testing.T) { t.Parallel() type testSuite struct { marshal func(any) ([]byte, error) unmarshal func(bytes []byte, i any) error name string } tType := gocql.NewNativeType(4, gocql.TypeVarint) testSuites := [2]testSuite{ { name: "serialization.varint", marshal: varint.Marshal, unmarshal: varint.Unmarshal, }, { name: "glob", marshal: func(i any) ([]byte, error) { return gocql.Marshal(tType, i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) }, }, } for _, tSuite := range testSuites { marshal := tSuite.marshal unmarshal := tSuite.unmarshal t.Run(tSuite.name, func(t *testing.T) { t.Parallel() serialization.NegativeMarshalSet{ Values: mod.Values{"1s2", "1s", "-1s", ".1", ",1", "0.1", "0,1"}.AddVariants(mod.All...), }.Run("corrupt_vals", t, marshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x7f"), Values: mod.Values{ int8(0), int16(0), int32(0), int64(0), int(0), uint8(0), uint16(0), uint32(0), uint64(0), uint(0), "", *big.NewInt(0), }.AddVariants(mod.All...), }.Run("corrupt_data+", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\xff\x80"), Values: mod.Values{ int8(0), int16(0), int32(0), int64(0), int(0), uint8(0), uint16(0), uint32(0), uint64(0), uint(0), "", *big.NewInt(0), }.AddVariants(mod.All...), }.Run("corrupt_data-", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x80"), Values: mod.Values{int8(0)}.AddVariants(mod.All...), }.Run("small_type_maxInt8+1", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\xff\x7f"), Values: mod.Values{int8(0)}.AddVariants(mod.All...), }.Run("small_type_minInt8-1", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x80\x00"), Values: mod.Values{int8(0), int16(0)}.AddVariants(mod.All...), }.Run("small_type_maxInt16+1", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\xff\x7f\xff"), Values: mod.Values{int8(0)}.AddVariants(mod.All...), }.Run("small_type_minInt16-1", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x80\x00\x00\x00"), Values: mod.Values{int8(0), int16(0), int32(0)}.AddVariants(mod.All...), }.Run("small_type_maxInt32+1", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\xff\x7f\xff\xff\xff\xff"), Values: mod.Values{int8(0), int16(0), int32(0)}.AddVariants(mod.All...), }.Run("small_type_minInt32-1", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x00\x80\x00\x00\x00\x00\x00\x00\x00"), Values: mod.Values{int8(0), int16(0), int32(0), int64(0), int(0)}.AddVariants(mod.All...), }.Run("small_type_maxInt64+1", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\xff\x7f\xff\xff\xff\xff\xff\xff\xff\xff"), Values: mod.Values{int8(0), int16(0), int32(0), int64(0), int(0)}.AddVariants(mod.All...), }.Run("small_type_minInt64-1", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x01\x00"), Values: mod.Values{uint8(0)}.AddVariants(mod.All...), }.Run("small_type_maxUint8+1", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x01\x00\x00"), Values: mod.Values{uint8(0), uint16(0)}.AddVariants(mod.All...), }.Run("small_type_maxUint16+1", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x01\x00\x00\x00\x00"), Values: mod.Values{uint8(0), uint16(0), uint32(0)}.AddVariants(mod.All...), }.Run("small_type_maxUint32+1", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x01\x00\x00\x00\x00\x00\x00\x00\x00"), Values: mod.Values{uint8(0), uint16(0), uint32(0), uint64(0), uint(0)}.AddVariants(mod.All...), }.Run("small_type_maxUint64+1", t, unmarshal) }) } } ================================================ FILE: tests/serialization/marshal_7_varint_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "math/big" "testing" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" "github.com/gocql/gocql/serialization/varint" ) func TestMarshalVarIntNew(t *testing.T) { t.Parallel() type testSuite struct { name string marshal func(any) ([]byte, error) unmarshal func(bytes []byte, i any) error } tType := gocql.NewNativeType(4, gocql.TypeVarint) testSuites := [2]testSuite{ { name: "serialization.varint", marshal: varint.Marshal, unmarshal: varint.Unmarshal, }, { name: "glob", marshal: func(i any) ([]byte, error) { return gocql.Marshal(tType, i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) }, }, } for _, tSuite := range testSuites { marshal := tSuite.marshal unmarshal := tSuite.unmarshal t.Run(tSuite.name, func(t *testing.T) { t.Parallel() serialization.PositiveSet{ Data: nil, Values: mod.Values{ (*int8)(nil), (*int16)(nil), (*int32)(nil), (*int64)(nil), (*int)(nil), (*uint8)(nil), (*uint16)(nil), (*uint32)(nil), (*uint64)(nil), (*uint)(nil), (*string)(nil), (*big.Int)(nil), "", }.AddVariants(mod.CustomType), }.Run("[nil]nullable", t, marshal, unmarshal) serialization.PositiveSet{ Data: nil, Values: mod.Values{ int8(0), int16(0), int32(0), int64(0), int(0), uint8(0), uint16(0), uint32(0), uint64(0), uint(0), "", big.Int{}, }.AddVariants(mod.CustomType), }.Run("[nil]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: make([]byte, 0), Values: mod.Values{ int8(0), int16(0), int32(0), int64(0), int(0), uint8(0), uint16(0), uint32(0), uint64(0), uint(0), "0", *big.NewInt(0), }.AddVariants(mod.All...), }.Run("[]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: []byte("\x00"), Values: mod.Values{ int8(0), int16(0), int32(0), int64(0), int(0), uint8(0), uint16(0), uint32(0), uint64(0), uint(0), "0", *big.NewInt(0), }.AddVariants(mod.All...), }.Run("zeros", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x01"), Values: mod.Values{ int8(1), int16(1), int32(1), int64(1), int(1), uint8(1), uint16(1), uint32(1), uint64(1), uint(1), "1", *big.NewInt(1), }.AddVariants(mod.All...), }.Run("+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff"), Values: mod.Values{ int8(-1), int16(-1), int32(-1), int64(-1), int(-1), "-1", *big.NewInt(-1), }.AddVariants(mod.All...), }.Run("-1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x7f"), Values: mod.Values{ int8(127), int16(127), int32(127), int64(127), int(127), uint8(127), uint16(127), uint32(127), uint64(127), uint(127), "127", *big.NewInt(127), }.AddVariants(mod.All...), }.Run("maxInt8", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x80"), Values: mod.Values{ int8(-128), int16(-128), int32(-128), int64(-128), int(-128), "-128", *big.NewInt(-128), }.AddVariants(mod.All...), }.Run("minInt8", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x80"), Values: mod.Values{ int16(128), int32(128), int64(128), int(128), uint8(128), uint16(128), uint32(128), uint64(128), uint(128), "128", *big.NewInt(128), }.AddVariants(mod.All...), }.Run("maxInt8+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\x7f"), Values: mod.Values{ int16(-129), int32(-129), int64(-129), int(-129), "-129", *big.NewInt(-129), }.AddVariants(mod.All...), }.Run("minInt8-1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x7f\xff"), Values: mod.Values{ int16(32767), int32(32767), int64(32767), int(32767), uint16(32767), uint32(32767), uint64(32767), uint(32767), "32767", *big.NewInt(32767), }.AddVariants(mod.All...), }.Run("maxInt16", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x80\x00"), Values: mod.Values{ int16(-32768), int32(-32768), int64(-32768), int(-32768), "-32768", *big.NewInt(-32768), }.AddVariants(mod.All...), }.Run("minInt16", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x80\x00"), Values: mod.Values{ int32(32768), int64(32768), int(32768), uint16(32768), uint32(32768), uint64(32768), uint(32768), "32768", *big.NewInt(32768), }.AddVariants(mod.All...), }.Run("maxInt16+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\x7f\xff"), Values: mod.Values{ int32(-32769), int64(-32769), int(-32769), "-32769", *big.NewInt(-32769), }.AddVariants(mod.All...), }.Run("minInt16-1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x7f\xff\xff"), Values: mod.Values{ int32(8388607), int64(8388607), int(8388607), uint32(8388607), uint64(8388607), uint(8388607), "8388607", *big.NewInt(8388607), }.AddVariants(mod.All...), }.Run("maxInt24", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x80\x00\x00"), Values: mod.Values{ int32(-8388608), int64(-8388608), int(-8388608), "-8388608", *big.NewInt(-8388608), }.AddVariants(mod.All...), }.Run("minInt24", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x80\x00\x00"), Values: mod.Values{ int64(8388608), int(8388608), uint32(8388608), uint64(8388608), uint(8388608), "8388608", *big.NewInt(8388608), }.AddVariants(mod.All...), }.Run("maxInt24+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\x7f\xff\xff"), Values: mod.Values{ int64(-8388609), int(-8388609), "-8388609", *big.NewInt(-8388609), }.AddVariants(mod.All...), }.Run("minInt24-1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x7f\xff\xff\xff"), Values: mod.Values{ int32(2147483647), int64(2147483647), int(2147483647), uint32(2147483647), uint64(2147483647), uint(2147483647), "2147483647", *big.NewInt(2147483647), }.AddVariants(mod.All...), }.Run("maxInt32", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x80\x00\x00\x00"), Values: mod.Values{ int32(-2147483648), int64(-2147483648), int(-2147483648), "-2147483648", *big.NewInt(-2147483648), }.AddVariants(mod.All...), }.Run("minInt32", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x80\x00\x00\x00"), Values: mod.Values{ int64(2147483648), int(2147483648), uint32(2147483648), uint64(2147483648), uint(2147483648), "2147483648", *big.NewInt(2147483648), }.AddVariants(mod.All...), }.Run("maxInt32+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\x7f\xff\xff\xff"), Values: mod.Values{ int64(-2147483649), int(-2147483649), "-2147483649", *big.NewInt(-2147483649), }.AddVariants(mod.All...), }.Run("minInt32-1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x7f\xff\xff\xff\xff"), Values: mod.Values{ int64(549755813887), int(549755813887), uint64(549755813887), uint(549755813887), "549755813887", *big.NewInt(549755813887), }.AddVariants(mod.All...), }.Run("maxInt40", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x80\x00\x00\x00\x00"), Values: mod.Values{ int64(-549755813888), int(-549755813888), "-549755813888", *big.NewInt(-549755813888), }.AddVariants(mod.All...), }.Run("minInt40", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x80\x00\x00\x00\x00"), Values: mod.Values{ int64(549755813888), int(549755813888), uint64(549755813888), uint(549755813888), "549755813888", *big.NewInt(549755813888), }.AddVariants(mod.All...), }.Run("maxInt40+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\x7f\xff\xff\xff\xff"), Values: mod.Values{ int64(-549755813889), int(-549755813889), "-549755813889", *big.NewInt(-549755813889), }.AddVariants(mod.All...), }.Run("minInt40-1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x7f\xff\xff\xff\xff\xff"), Values: mod.Values{ int64(140737488355327), int(140737488355327), uint64(140737488355327), uint(140737488355327), "140737488355327", *big.NewInt(140737488355327), }.AddVariants(mod.All...), }.Run("maxInt48", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x80\x00\x00\x00\x00\x00"), Values: mod.Values{ int64(-140737488355328), int(-140737488355328), "-140737488355328", *big.NewInt(-140737488355328), }.AddVariants(mod.All...), }.Run("minInt48", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x80\x00\x00\x00\x00\x00"), Values: mod.Values{ int64(140737488355328), int(140737488355328), uint64(140737488355328), uint(140737488355328), "140737488355328", *big.NewInt(140737488355328), }.AddVariants(mod.All...), }.Run("maxInt48+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\x7f\xff\xff\xff\xff\xff"), Values: mod.Values{ int64(-140737488355329), int(-140737488355329), "-140737488355329", *big.NewInt(-140737488355329), }.AddVariants(mod.All...), }.Run("minInt48-1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x7f\xff\xff\xff\xff\xff\xff"), Values: mod.Values{ int64(36028797018963967), int(36028797018963967), uint64(36028797018963967), uint(36028797018963967), "36028797018963967", *big.NewInt(36028797018963967), }.AddVariants(mod.All...), }.Run("maxInt56", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x80\x00\x00\x00\x00\x00\x00"), Values: mod.Values{ int64(-36028797018963968), int(-36028797018963968), "-36028797018963968", *big.NewInt(-36028797018963968), }.AddVariants(mod.All...), }.Run("minInt56", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x80\x00\x00\x00\x00\x00\x00"), Values: mod.Values{ int64(36028797018963968), int(36028797018963968), uint64(36028797018963968), uint(36028797018963968), "36028797018963968", *big.NewInt(36028797018963968), }.AddVariants(mod.All...), }.Run("maxInt56+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\x7f\xff\xff\xff\xff\xff\xff"), Values: mod.Values{ int64(-36028797018963969), int(-36028797018963969), "-36028797018963969", *big.NewInt(-36028797018963969), }.AddVariants(mod.All...), }.Run("minInt56-1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x7f\xff\xff\xff\xff\xff\xff\xff"), Values: mod.Values{ int64(9223372036854775807), int(9223372036854775807), uint64(9223372036854775807), uint(9223372036854775807), "9223372036854775807", *big.NewInt(9223372036854775807), }.AddVariants(mod.All...), }.Run("maxInt64", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x80\x00\x00\x00\x00\x00\x00\x00"), Values: mod.Values{ int64(-9223372036854775808), int(-9223372036854775808), "-9223372036854775808", *big.NewInt(-9223372036854775808), }.AddVariants(mod.All...), }.Run("minInt64", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x80\x00\x00\x00\x00\x00\x00\x00"), Values: mod.Values{ "9223372036854775808", *big.NewInt(0).Add(big.NewInt(1), big.NewInt(9223372036854775807)), }.AddVariants(mod.All...), }.Run("maxInt64+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\x7f\xff\xff\xff\xff\xff\xff\xff"), Values: mod.Values{ "-9223372036854775809", *big.NewInt(0).Add(big.NewInt(-1), big.NewInt(-9223372036854775808)), }.AddVariants(mod.All...), }.Run("minInt64-1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\xff"), Values: mod.Values{ uint8(255), uint16(255), uint32(255), uint64(255), uint(255), int16(255), int32(255), int64(255), int(255), "255", *big.NewInt(255), }.AddVariants(mod.All...), }.Run("maxUint8", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x01\x00"), Values: mod.Values{ uint16(256), uint32(256), uint64(256), uint(256), int16(256), int32(256), int64(256), int(256), "256", *big.NewInt(256), }.AddVariants(mod.All...), }.Run("maxUint8+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\xff\xff"), Values: mod.Values{ uint16(65535), uint32(65535), uint64(65535), uint(65535), int32(65535), int64(65535), int(65535), "65535", *big.NewInt(65535), }.AddVariants(mod.All...), }.Run("maxUint16", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x01\x00\x00"), Values: mod.Values{ uint32(65536), uint64(65536), uint(65536), int32(65536), int64(65536), int(65536), "65536", *big.NewInt(65536), }.AddVariants(mod.All...), }.Run("maxUint16+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\xff\xff\xff"), Values: mod.Values{ uint32(16777215), uint64(16777215), uint(16777215), int32(16777215), int64(16777215), int(16777215), "16777215", *big.NewInt(16777215), }.AddVariants(mod.All...), }.Run("maxUint24", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x01\x00\x00\x00"), Values: mod.Values{ uint32(16777216), uint64(16777216), uint(16777216), int32(16777216), int64(16777216), int(16777216), "16777216", *big.NewInt(16777216), }.AddVariants(mod.All...), }.Run("maxUint24+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\xff\xff\xff\xff"), Values: mod.Values{ uint32(4294967295), uint64(4294967295), uint(4294967295), int64(4294967295), int(4294967295), "4294967295", *big.NewInt(4294967295), }.AddVariants(mod.All...), }.Run("maxUint32", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x01\x00\x00\x00\x00"), Values: mod.Values{ uint64(4294967296), uint(4294967296), int64(4294967296), int(4294967296), "4294967296", *big.NewInt(4294967296), }.AddVariants(mod.All...), }.Run("maxUint32+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\xff\xff\xff\xff\xff"), Values: mod.Values{ uint64(1099511627775), uint(1099511627775), int64(1099511627775), int(1099511627775), "1099511627775", *big.NewInt(1099511627775), }.AddVariants(mod.All...), }.Run("maxUint40", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x01\x00\x00\x00\x00\x00"), Values: mod.Values{ uint64(1099511627776), uint(1099511627776), int64(1099511627776), int(1099511627776), "1099511627776", *big.NewInt(1099511627776), }.AddVariants(mod.All...), }.Run("maxUint40+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\xff\xff\xff\xff\xff\xff"), Values: mod.Values{ uint64(281474976710655), uint(281474976710655), int64(281474976710655), int(281474976710655), "281474976710655", *big.NewInt(281474976710655), }.AddVariants(mod.All...), }.Run("maxUint48", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x01\x00\x00\x00\x00\x00\x00"), Values: mod.Values{ uint64(281474976710656), uint(281474976710656), int64(281474976710656), int(281474976710656), "281474976710656", *big.NewInt(281474976710656), }.AddVariants(mod.All...), }.Run("maxUint48+1", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\xff\xff\xff\xff\xff\xff\xff"), Values: mod.Values{ uint64(72057594037927935), uint(72057594037927935), int64(72057594037927935), int(72057594037927935), "72057594037927935", *big.NewInt(72057594037927935), }.AddVariants(mod.All...), }.Run("maxUint56", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x01\x00\x00\x00\x00\x00\x00\x00"), Values: mod.Values{ uint64(72057594037927936), uint(72057594037927936), int64(72057594037927936), int(72057594037927936), "72057594037927936", *big.NewInt(72057594037927936), }.AddVariants(mod.All...), }.Run("maxUint56+1", t, marshal, unmarshal) bigMaxUint64 := new(big.Int) bigMaxUint64.SetString("18446744073709551615", 10) serialization.PositiveSet{ Data: []byte("\x00\xff\xff\xff\xff\xff\xff\xff\xff"), Values: mod.Values{ uint64(18446744073709551615), uint(18446744073709551615), "18446744073709551615", *bigMaxUint64, }.AddVariants(mod.All...), }.Run("maxUint64", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x01\x00\x00\x00\x00\x00\x00\x00\x00"), Values: mod.Values{ "18446744073709551616", *big.NewInt(0).Add(bigMaxUint64, big.NewInt(1)), }.AddVariants(mod.All...), }.Run("maxUint64+1", t, marshal, unmarshal) }) } } ================================================ FILE: tests/serialization/marshal_8_float_corrupt_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "testing" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" "github.com/gocql/gocql/serialization/float" ) func TestMarshalFloatCorrupt(t *testing.T) { t.Parallel() type testSuite struct { name string marshal func(any) ([]byte, error) unmarshal func(bytes []byte, i any) error } tType := gocql.NewNativeType(4, gocql.TypeFloat) testSuites := [2]testSuite{ { name: "serialization.float", marshal: float.Marshal, unmarshal: float.Unmarshal, }, { name: "glob", marshal: func(i any) ([]byte, error) { return gocql.Marshal(tType, i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) }, }, } for _, tSuite := range testSuites { unmarshal := tSuite.unmarshal t.Run(tSuite.name, func(t *testing.T) { t.Parallel() serialization.NegativeUnmarshalSet{ Data: []byte("\x80\x00\x00\x00\x00"), Values: mod.Values{float32(0)}.AddVariants(mod.All...), }.Run("big_data", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x80"), Values: mod.Values{float32(0)}.AddVariants(mod.All...), }.Run("small_data1", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x80\x00\x00"), Values: mod.Values{float32(0)}.AddVariants(mod.All...), }.Run("small_data2", t, unmarshal) }) } } ================================================ FILE: tests/serialization/marshal_8_float_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "math" "testing" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" "github.com/gocql/gocql/serialization/float" ) func TestMarshalFloat(t *testing.T) { t.Parallel() type testSuite struct { name string marshal func(any) ([]byte, error) unmarshal func(bytes []byte, i any) error } tType := gocql.NewNativeType(4, gocql.TypeFloat) testSuites := [2]testSuite{ { name: "serialization.float", marshal: float.Marshal, unmarshal: float.Unmarshal, }, { name: "glob", marshal: func(i any) ([]byte, error) { return gocql.Marshal(tType, i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) }, }, } for _, tSuite := range testSuites { marshal := tSuite.marshal unmarshal := tSuite.unmarshal t.Run(tSuite.name, func(t *testing.T) { t.Parallel() serialization.PositiveSet{ Data: nil, Values: mod.Values{(*float32)(nil)}.AddVariants(mod.CustomType), }.Run("[nil]nullable", t, marshal, unmarshal) serialization.PositiveSet{ Data: nil, Values: mod.Values{float32(0)}.AddVariants(mod.CustomType), }.Run("[nil]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: make([]byte, 0), Values: mod.Values{float32(0)}.AddVariants(mod.All...), }.Run("[]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00"), Values: mod.Values{float32(0)}.AddVariants(mod.All...), }.Run("zeros", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x7f\x7f\xff\xff"), Values: mod.Values{float32(math.MaxFloat32)}.AddVariants(mod.All...), }.Run("max", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\x7f\xff\xff"), Values: mod.Values{float32(-math.MaxFloat32)}.AddVariants(mod.All...), }.Run("min", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x01"), Values: mod.Values{float32(math.SmallestNonzeroFloat32)}.AddVariants(mod.All...), }.Run("smallest_pos", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x80\x00\x00\x01"), Values: mod.Values{float32(-math.SmallestNonzeroFloat32)}.AddVariants(mod.All...), }.Run("smallest_neg", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x7f\x80\x00\x00"), Values: mod.Values{float32(math.Inf(1))}.AddVariants(mod.All...), }.Run("inf+", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\x80\x00\x00"), Values: mod.Values{float32(math.Inf(-1))}.AddVariants(mod.All...), }.Run("inf-", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x7f\xc0\x00\x00"), Values: mod.Values{float32(math.NaN())}.AddVariants(mod.All...), }.Run("nan", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x40\x49\x0f\xdb"), Values: mod.Values{float32(3.14159265)}.AddVariants(mod.All...), }.Run("pi", t, marshal, unmarshal) }) } } ================================================ FILE: tests/serialization/marshal_9_double_corrupt_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "testing" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" "github.com/gocql/gocql/serialization/double" ) func TestMarshalDoubleCorrupt(t *testing.T) { t.Parallel() type testSuite struct { name string marshal func(any) ([]byte, error) unmarshal func(bytes []byte, i any) error } tType := gocql.NewNativeType(4, gocql.TypeDouble) testSuites := [2]testSuite{ { name: "serialization.double", marshal: double.Marshal, unmarshal: double.Unmarshal, }, { name: "glob", marshal: func(i any) ([]byte, error) { return gocql.Marshal(tType, i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) }, }, } for _, tSuite := range testSuites { unmarshal := tSuite.unmarshal t.Run(tSuite.name, func(t *testing.T) { t.Parallel() serialization.NegativeUnmarshalSet{ Data: []byte("\x80\x00\x00\x00\x00\x00\x00\x00\x00"), Values: mod.Values{float64(0)}.AddVariants(mod.All...), }.Run("big_data", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x80"), Values: mod.Values{float64(0)}.AddVariants(mod.All...), }.Run("small_data1", t, unmarshal) serialization.NegativeUnmarshalSet{ Data: []byte("\x80\x00\x00\x00"), Values: mod.Values{float64(0)}.AddVariants(mod.All...), }.Run("small_data2", t, unmarshal) }) } } ================================================ FILE: tests/serialization/marshal_9_duble_test.go ================================================ //go:build unit // +build unit package serialization_test import ( "math" "testing" "github.com/gocql/gocql" "github.com/gocql/gocql/internal/tests/serialization" "github.com/gocql/gocql/internal/tests/serialization/mod" "github.com/gocql/gocql/serialization/double" ) func TestMarshalDouble(t *testing.T) { t.Parallel() type testSuite struct { name string marshal func(any) ([]byte, error) unmarshal func(bytes []byte, i any) error } tType := gocql.NewNativeType(4, gocql.TypeDouble) testSuites := [2]testSuite{ { name: "serialization.double", marshal: double.Marshal, unmarshal: double.Unmarshal, }, { name: "glob", marshal: func(i any) ([]byte, error) { return gocql.Marshal(tType, i) }, unmarshal: func(bytes []byte, i any) error { return gocql.Unmarshal(tType, bytes, i) }, }, } for _, tSuite := range testSuites { marshal := tSuite.marshal unmarshal := tSuite.unmarshal t.Run(tSuite.name, func(t *testing.T) { t.Parallel() serialization.PositiveSet{ Data: nil, Values: mod.Values{(*float64)(nil)}.AddVariants(mod.CustomType), }.Run("[nil]nullable", t, marshal, unmarshal) serialization.PositiveSet{ Data: nil, Values: mod.Values{float64(0)}.AddVariants(mod.CustomType), }.Run("[nil]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: make([]byte, 0), Values: mod.Values{float64(0)}.AddVariants(mod.All...), }.Run("[]unmarshal", t, nil, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00\x00\x00\x00\x00"), Values: mod.Values{float64(0)}.AddVariants(mod.All...), }.Run("zeros", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x7f\xef\xff\xff\xff\xff\xff\xff"), Values: mod.Values{float64(math.MaxFloat64)}.AddVariants(mod.All...), }.Run("max", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\xef\xff\xff\xff\xff\xff\xff"), Values: mod.Values{float64(-math.MaxFloat64)}.AddVariants(mod.All...), }.Run("min", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x00\x00\x00\x00\x00\x00\x00\x01"), Values: mod.Values{float64(math.SmallestNonzeroFloat64)}.AddVariants(mod.All...), }.Run("smallest_pos", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x80\x00\x00\x00\x00\x00\x00\x01"), Values: mod.Values{float64(-math.SmallestNonzeroFloat64)}.AddVariants(mod.All...), }.Run("smallest_neg", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x7f\xf0\x00\x00\x00\x00\x00\x00"), Values: mod.Values{float64(math.Inf(1))}.AddVariants(mod.All...), }.Run("inf+", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\xff\xf0\x00\x00\x00\x00\x00\x00"), Values: mod.Values{float64(math.Inf(-1))}.AddVariants(mod.All...), }.Run("inf-", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x7f\xf8\x00\x00\x00\x00\x00\x01"), Values: mod.Values{float64(math.NaN())}.AddVariants(mod.All...), }.Run("nan", t, marshal, unmarshal) serialization.PositiveSet{ Data: []byte("\x40\x09\x21\xfb\x53\xc8\xd4\xf1"), Values: mod.Values{float64(3.14159265)}.AddVariants(mod.All...), }.Run("pi", t, marshal, unmarshal) }) } } ================================================ FILE: token.go ================================================ // Copyright (c) 2015 The gocql Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "bytes" "crypto/md5" "fmt" "math/big" "sort" "strconv" "strings" "github.com/gocql/gocql/internal/murmur" ) // a token partitioner type Partitioner interface { Name() string Hash([]byte) Token ParseString(string) Token } // a Token type Token interface { fmt.Stringer Less(Token) bool } // murmur3 partitioner type murmur3Partitioner struct{} func (p murmur3Partitioner) Name() string { return "Murmur3Partitioner" } func (p murmur3Partitioner) Hash(partitionKey []byte) Token { h1 := murmur.Murmur3H1(partitionKey) return int64Token(h1) } // murmur3 little-endian, 128-bit hash, but returns only h1 func (p murmur3Partitioner) ParseString(str string) Token { return parseInt64Token(str) } // int64 token type int64Token int64 func parseInt64Token(str string) int64Token { val, _ := strconv.ParseInt(str, 10, 64) return int64Token(val) } func (m int64Token) String() string { return strconv.FormatInt(int64(m), 10) } func (m int64Token) Less(token Token) bool { return m < token.(int64Token) } // order preserving partitioner and token type orderedPartitioner struct{} type orderedToken string func (p orderedPartitioner) Name() string { return "OrderedPartitioner" } func (p orderedPartitioner) Hash(partitionKey []byte) Token { // the partition key is the token return orderedToken(partitionKey) } func (p orderedPartitioner) ParseString(str string) Token { return orderedToken(str) } func (o orderedToken) String() string { return string(o) } func (o orderedToken) Less(token Token) bool { return o < token.(orderedToken) } // random partitioner and token type randomPartitioner struct{} type randomToken big.Int func (r randomPartitioner) Name() string { return "RandomPartitioner" } // 2 ** 128 var maxHashInt, _ = new(big.Int).SetString("340282366920938463463374607431768211456", 10) func (p randomPartitioner) Hash(partitionKey []byte) Token { sum := md5.Sum(partitionKey) val := new(big.Int) val.SetBytes(sum[:]) if sum[0] > 127 { val.Sub(val, maxHashInt) val.Abs(val) } return (*randomToken)(val) } func (p randomPartitioner) ParseString(str string) Token { val := new(big.Int) val.SetString(str, 10) return (*randomToken)(val) } func (r *randomToken) String() string { return (*big.Int)(r).String() } func (r *randomToken) Less(token Token) bool { return -1 == (*big.Int)(r).Cmp((*big.Int)(token.(*randomToken))) } type hostToken struct { token Token host *HostInfo } func (ht hostToken) String() string { return fmt.Sprintf("{token=%v host=%v}", ht.token, ht.host.HostID()) } // a data structure for organizing the relationship between tokens and hosts type tokenRing struct { partitioner Partitioner // tokens map token range to primary replica. // The elements in tokens are sorted by token ascending. // The range for a given item in tokens starts after preceding range and ends with the token specified in // token. The end token is part of the range. // The lowest (i.e. index 0) range wraps around the ring (its preceding range is the one with largest index). tokens []hostToken hosts []*HostInfo } func newTokenRing(partitioner string, hosts []*HostInfo) (*tokenRing, error) { tokenRing := &tokenRing{ hosts: hosts, } if strings.HasSuffix(partitioner, "Murmur3Partitioner") { tokenRing.partitioner = murmur3Partitioner{} } else if strings.HasSuffix(partitioner, "OrderedPartitioner") { tokenRing.partitioner = orderedPartitioner{} } else if strings.HasSuffix(partitioner, "RandomPartitioner") { tokenRing.partitioner = randomPartitioner{} } else { return nil, fmt.Errorf("unsupported partitioner '%s'", partitioner) } for _, host := range hosts { for _, strToken := range host.Tokens() { token := tokenRing.partitioner.ParseString(strToken) tokenRing.tokens = append(tokenRing.tokens, hostToken{token: token, host: host}) } } sort.Sort(tokenRing) return tokenRing, nil } func (t *tokenRing) Len() int { return len(t.tokens) } func (t *tokenRing) Less(i, j int) bool { return t.tokens[i].token.Less(t.tokens[j].token) } func (t *tokenRing) Swap(i, j int) { t.tokens[i], t.tokens[j] = t.tokens[j], t.tokens[i] } func (t *tokenRing) String() string { buf := &bytes.Buffer{} buf.WriteString("TokenRing(") if t.partitioner != nil { buf.WriteString(t.partitioner.Name()) } buf.WriteString("){") sep := "" for i, th := range t.tokens { buf.WriteString(sep) sep = "," buf.WriteString("\n\t[") buf.WriteString(strconv.Itoa(i)) buf.WriteString("]") buf.WriteString(th.token.String()) buf.WriteString(":") buf.WriteString(th.host.ConnectAddress().String()) } buf.WriteString("\n}") return string(buf.Bytes()) } // GetHostForPartitionKey finds host information for given partition key. // // It returns two tokens. First is token that exactly corresponds to the partition key (and could be used to // determine shard, for example), second token is the endToken that corresponds to the host. func (t *tokenRing) GetHostForPartitionKey(partitionKey []byte) (host *HostInfo, token Token, endToken Token) { if t == nil { return nil, nil, nil } token = t.partitioner.Hash(partitionKey) host, endToken = t.GetHostForToken(token) return host, token, endToken } func (t *tokenRing) GetHostForToken(token Token) (host *HostInfo, endToken Token) { if t == nil || len(t.tokens) == 0 { return nil, nil } // find the primary replica p := sort.Search(len(t.tokens), func(i int) bool { return !t.tokens[i].token.Less(token) }) if p == len(t.tokens) { // wrap around to the first in the ring p = 0 } v := t.tokens[p] return v.host, v.token } ================================================ FILE: token_test.go ================================================ // Copyright (c) 2015 The gocql Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build unit // +build unit /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "bytes" "fmt" "math/big" "net" "sort" "strconv" "testing" ) // Tests of the murmur3Patitioner func TestMurmur3Partitioner(t *testing.T) { t.Parallel() token := murmur3Partitioner{}.ParseString("-1053604476080545076") if "-1053604476080545076" != token.String() { t.Errorf("Expected '-1053604476080545076' but was '%s'", token) } // at least verify that the partitioner // doesn't return nil pk, _ := marshalInt(1) token = murmur3Partitioner{}.Hash(pk) if token == nil { t.Fatal("token was nil") } } // Tests of the int64Token func TestInt64Token(t *testing.T) { t.Parallel() if int64Token(42).Less(int64Token(42)) { t.Errorf("Expected Less to return false, but was true") } if !int64Token(-42).Less(int64Token(42)) { t.Errorf("Expected Less to return true, but was false") } if int64Token(42).Less(int64Token(-42)) { t.Errorf("Expected Less to return false, but was true") } } // Tests of the orderedPartitioner func TestOrderedPartitioner(t *testing.T) { t.Parallel() // at least verify that the partitioner // doesn't return nil p := orderedPartitioner{} pk, _ := marshalInt(1) token := p.Hash(pk) if token == nil { t.Fatal("token was nil") } str := token.String() parsedToken := p.ParseString(str) if !bytes.Equal([]byte(token.(orderedToken)), []byte(parsedToken.(orderedToken))) { t.Errorf("Failed to convert to and from a string %s expected %x but was %x", str, []byte(token.(orderedToken)), []byte(parsedToken.(orderedToken)), ) } } // Tests of the orderedToken func TestOrderedToken(t *testing.T) { t.Parallel() if orderedToken([]byte{0, 0, 4, 2}).Less(orderedToken([]byte{0, 0, 4, 2})) { t.Errorf("Expected Less to return false, but was true") } if !orderedToken([]byte{0, 0, 3}).Less(orderedToken([]byte{0, 0, 4, 2})) { t.Errorf("Expected Less to return true, but was false") } if orderedToken([]byte{0, 0, 4, 2}).Less(orderedToken([]byte{0, 0, 3})) { t.Errorf("Expected Less to return false, but was true") } } // Tests of the randomPartitioner func TestRandomPartitioner(t *testing.T) { t.Parallel() // at least verify that the partitioner // doesn't return nil p := randomPartitioner{} pk, _ := marshalInt(1) token := p.Hash(pk) if token == nil { t.Fatal("token was nil") } str := token.String() parsedToken := p.ParseString(str) if (*big.Int)(token.(*randomToken)).Cmp((*big.Int)(parsedToken.(*randomToken))) != 0 { t.Errorf("Failed to convert to and from a string %s expected %v but was %v", str, token, parsedToken, ) } } func TestRandomPartitionerMatchesReference(t *testing.T) { t.Parallel() // example taken from datastax python driver // >>> from cassandra.metadata import MD5Token // >>> MD5Token.hash_fn("test") // 12707736894140473154801792860916528374L var p randomPartitioner expect := "12707736894140473154801792860916528374" actual := p.Hash([]byte("test")).String() if actual != expect { t.Errorf("expected random partitioner to generate tokens in the same way as the reference"+ " python client. Expected %s, but got %s", expect, actual) } } // Tests of the randomToken func TestRandomToken(t *testing.T) { t.Parallel() if ((*randomToken)(big.NewInt(42))).Less((*randomToken)(big.NewInt(42))) { t.Errorf("Expected Less to return false, but was true") } if !((*randomToken)(big.NewInt(41))).Less((*randomToken)(big.NewInt(42))) { t.Errorf("Expected Less to return true, but was false") } if ((*randomToken)(big.NewInt(42))).Less((*randomToken)(big.NewInt(41))) { t.Errorf("Expected Less to return false, but was true") } } type intToken int func (i intToken) String() string { return strconv.Itoa(int(i)) } func (i intToken) Less(token Token) bool { return i < token.(intToken) } // Test of the token ring implementation based on example at the start of this // page of documentation: // http://www.datastax.com/docs/0.8/cluster_architecture/partitioning func TestTokenRing_Int(t *testing.T) { t.Parallel() host0 := &HostInfo{} host25 := &HostInfo{} host50 := &HostInfo{} host75 := &HostInfo{} ring := &tokenRing{ partitioner: nil, // these tokens and hosts are out of order to test sorting tokens: []hostToken{ {intToken(0), host0}, {intToken(50), host50}, {intToken(75), host75}, {intToken(25), host25}, }, } sort.Sort(ring) if host, endToken := ring.GetHostForToken(intToken(0)); host != host0 || endToken != intToken(0) { t.Error("Expected host 0 for token 0") } if host, endToken := ring.GetHostForToken(intToken(1)); host != host25 || endToken != intToken(25) { t.Error("Expected host 25 for token 1") } if host, endToken := ring.GetHostForToken(intToken(24)); host != host25 || endToken != intToken(25) { t.Error("Expected host 25 for token 24") } if host, endToken := ring.GetHostForToken(intToken(25)); host != host25 || endToken != intToken(25) { t.Error("Expected host 25 for token 25") } if host, endToken := ring.GetHostForToken(intToken(26)); host != host50 || endToken != intToken(50) { t.Error("Expected host 50 for token 26") } if host, endToken := ring.GetHostForToken(intToken(49)); host != host50 || endToken != intToken(50) { t.Error("Expected host 50 for token 49") } if host, endToken := ring.GetHostForToken(intToken(50)); host != host50 || endToken != intToken(50) { t.Error("Expected host 50 for token 50") } if host, endToken := ring.GetHostForToken(intToken(51)); host != host75 || endToken != intToken(75) { t.Error("Expected host 75 for token 51") } if host, endToken := ring.GetHostForToken(intToken(74)); host != host75 || endToken != intToken(75) { t.Error("Expected host 75 for token 74") } if host, endToken := ring.GetHostForToken(intToken(75)); host != host75 || endToken != intToken(75) { t.Error("Expected host 75 for token 75") } if host, endToken := ring.GetHostForToken(intToken(76)); host != host0 || endToken != intToken(0) { t.Error("Expected host 0 for token 76") } if host, endToken := ring.GetHostForToken(intToken(99)); host != host0 || endToken != intToken(0) { t.Error("Expected host 0 for token 99") } if host, endToken := ring.GetHostForToken(intToken(100)); host != host0 || endToken != intToken(0) { t.Error("Expected host 0 for token 100") } } // Test for the behavior of a nil pointer to tokenRing func TestTokenRing_Nil(t *testing.T) { t.Parallel() var ring *tokenRing = nil if host, endToken := ring.GetHostForToken(nil); host != nil || endToken != nil { t.Error("Expected nil for nil token ring") } if host, token, endToken := ring.GetHostForPartitionKey(nil); host != nil || token != nil || endToken != nil { t.Error("Expected nil for nil token ring") } } // Test of the recognition of the partitioner class func TestTokenRing_UnknownPartition(t *testing.T) { t.Parallel() _, err := newTokenRing("UnknownPartitioner", nil) if err == nil { t.Error("Expected error for unknown partitioner value, but was nil") } } func hostsForTests(n int) []*HostInfo { hosts := make([]*HostInfo, n) for i := 0; i < n; i++ { host := &HostInfo{ connectAddress: net.IPv4(1, 1, 1, byte(n)), tokens: []string{fmt.Sprintf("%d", n)}, } hosts[i] = host } return hosts } // Test of the tokenRing with the Murmur3Partitioner func TestTokenRing_Murmur3(t *testing.T) { t.Parallel() // Note, strings are parsed directly to int64, they are not murmur3 hashed hosts := hostsForTests(4) ring, err := newTokenRing("Murmur3Partitioner", hosts) if err != nil { t.Fatalf("Failed to create token ring due to error: %v", err) } p := murmur3Partitioner{} for _, host := range hosts { actual, _ := ring.GetHostForToken(p.ParseString(host.tokens[0])) if !actual.ConnectAddress().Equal(host.ConnectAddress()) { t.Errorf("Expected address %v for token %q, but was %v", host.ConnectAddress(), host.tokens[0], actual.ConnectAddress()) } } actual, _ := ring.GetHostForToken(p.ParseString("12")) if !actual.ConnectAddress().Equal(hosts[1].ConnectAddress()) { t.Errorf("Expected address 1 for token \"12\", but was %s", actual.ConnectAddress()) } actual, _ = ring.GetHostForToken(p.ParseString("24324545443332")) if !actual.ConnectAddress().Equal(hosts[0].ConnectAddress()) { t.Errorf("Expected address 0 for token \"24324545443332\", but was %s", actual.ConnectAddress()) } } // Test of the tokenRing with the OrderedPartitioner func TestTokenRing_Ordered(t *testing.T) { t.Parallel() // Tokens here more or less are similar layout to the int tokens above due // to each numeric character translating to a consistently offset byte. hosts := hostsForTests(4) ring, err := newTokenRing("OrderedPartitioner", hosts) if err != nil { t.Fatalf("Failed to create token ring due to error: %v", err) } p := orderedPartitioner{} var actual *HostInfo for _, host := range hosts { actual, _ := ring.GetHostForToken(p.ParseString(host.tokens[0])) if !actual.ConnectAddress().Equal(host.ConnectAddress()) { t.Errorf("Expected address %v for token %q, but was %v", host.ConnectAddress(), host.tokens[0], actual.ConnectAddress()) } } actual, _ = ring.GetHostForToken(p.ParseString("12")) if !actual.peer.Equal(hosts[1].peer) { t.Errorf("Expected address 1 for token \"12\", but was %s", actual.ConnectAddress()) } actual, _ = ring.GetHostForToken(p.ParseString("24324545443332")) if !actual.ConnectAddress().Equal(hosts[1].ConnectAddress()) { t.Errorf("Expected address 1 for token \"24324545443332\", but was %s", actual.ConnectAddress()) } } // Test of the tokenRing with the RandomPartitioner func TestTokenRing_Random(t *testing.T) { t.Parallel() // String tokens are parsed into big.Int in base 10 hosts := hostsForTests(4) ring, err := newTokenRing("RandomPartitioner", hosts) if err != nil { t.Fatalf("Failed to create token ring due to error: %v", err) } p := randomPartitioner{} var actual *HostInfo for _, host := range hosts { actual, _ := ring.GetHostForToken(p.ParseString(host.tokens[0])) if !actual.ConnectAddress().Equal(host.ConnectAddress()) { t.Errorf("Expected address %v for token %q, but was %v", host.ConnectAddress(), host.tokens[0], actual.ConnectAddress()) } } actual, _ = ring.GetHostForToken(p.ParseString("12")) if !actual.peer.Equal(hosts[1].peer) { t.Errorf("Expected address 1 for token \"12\", but was %s", actual.ConnectAddress()) } actual, _ = ring.GetHostForToken(p.ParseString("24324545443332")) if !actual.ConnectAddress().Equal(hosts[0].ConnectAddress()) { t.Errorf("Expected address 1 for token \"24324545443332\", but was %s", actual.ConnectAddress()) } } ================================================ FILE: topology.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "fmt" "sort" "strconv" "strings" ) type hostTokens struct { // token is end (inclusive) of token range these hosts belong to token Token hosts []*HostInfo } // tokenRingReplicas maps token ranges to list of replicas. // The elements in tokenRingReplicas are sorted by token ascending. // The range for a given item in tokenRingReplicas starts after preceding range and ends with the token specified in // token. The end token is part of the range. // The lowest (i.e. index 0) range wraps around the ring (its preceding range is the one with largest index). type tokenRingReplicas []hostTokens func (h tokenRingReplicas) Less(i, j int) bool { return h[i].token.Less(h[j].token) } func (h tokenRingReplicas) Len() int { return len(h) } func (h tokenRingReplicas) Swap(i, j int) { h[i], h[j] = h[j], h[i] } func (h tokenRingReplicas) replicasFor(t Token) *hostTokens { if len(h) == 0 { return nil } p := sort.Search(len(h), func(i int) bool { return !h[i].token.Less(t) }) if p >= len(h) { // rollover p = 0 } return &h[p] } type placementStrategy interface { replicaMap(tokenRing *tokenRing) tokenRingReplicas replicationFactor(dc string) int } func getReplicationFactorFromOpts(val any) (int, error) { switch v := val.(type) { case int: if v < 0 { return 0, fmt.Errorf("invalid replication_factor %d", v) } return v, nil case string: n, err := strconv.Atoi(v) if err != nil { return 0, fmt.Errorf("invalid replication_factor %q: %v", v, err) } else if n < 0 { return 0, fmt.Errorf("invalid replication_factor %d", n) } return n, nil default: return 0, fmt.Errorf("unknown replication_factor type %T", v) } } func getStrategy(ks *KeyspaceMetadata, logger StdLogger) placementStrategy { switch { case strings.Contains(ks.StrategyClass, "SimpleStrategy"): rf, err := getReplicationFactorFromOpts(ks.StrategyOptions["replication_factor"]) if err != nil { logger.Printf("parse rf for keyspace %q: %v", ks.Name, err) return nil } return &simpleStrategy{rf: rf} case strings.Contains(ks.StrategyClass, "NetworkTopologyStrategy"): dcs := make(map[string]int) for dc, rf := range ks.StrategyOptions { if dc == "class" { continue } rf, err := getReplicationFactorFromOpts(rf) if err != nil { logger.Printf("parse rf for keyspace %q, dc %q: %v", ks.Name, dc, err) // skip DC if the rf is invalid/unsupported, so that we can at least work with other working DCs. continue } dcs[dc] = rf } return &networkTopology{dcs: dcs} case strings.Contains(ks.StrategyClass, "LocalStrategy"): return nil default: logger.Printf("parse rf for keyspace %q: unsupported strategy class: %v", ks.Name, ks.StrategyClass) return nil } } type simpleStrategy struct { rf int } func (s *simpleStrategy) replicationFactor(dc string) int { return s.rf } func (s *simpleStrategy) replicaMap(tokenRing *tokenRing) tokenRingReplicas { tokens := tokenRing.tokens ring := make(tokenRingReplicas, len(tokens)) for i, th := range tokens { replicas := make([]*HostInfo, 0, s.rf) seen := make(map[*HostInfo]bool) for j := 0; j < len(tokens) && len(replicas) < s.rf; j++ { h := tokens[(i+j)%len(tokens)] if !seen[h.host] { replicas = append(replicas, h.host) seen[h.host] = true } } ring[i] = hostTokens{token: th.token, hosts: replicas} } sort.Sort(ring) return ring } type networkTopology struct { dcs map[string]int } func (n *networkTopology) replicationFactor(dc string) int { return n.dcs[dc] } func (n *networkTopology) haveRF(replicaCounts map[string]int) bool { if len(replicaCounts) != len(n.dcs) { return false } for dc, rf := range n.dcs { if rf != replicaCounts[dc] { return false } } return true } func (n *networkTopology) replicaMap(tokenRing *tokenRing) tokenRingReplicas { dcRacks := make(map[string]map[string]struct{}, len(n.dcs)) // skipped hosts in a dc skipped := make(map[string][]*HostInfo, len(n.dcs)) // number of replicas per dc replicasInDC := make(map[string]int, len(n.dcs)) // dc -> racks seenDCRacks := make(map[string]map[string]struct{}, len(n.dcs)) for _, h := range tokenRing.hosts { dc := h.DataCenter() rack := h.Rack() racks, ok := dcRacks[dc] if !ok { racks = make(map[string]struct{}) dcRacks[dc] = racks } racks[rack] = struct{}{} } for dc, racks := range dcRacks { replicasInDC[dc] = 0 seenDCRacks[dc] = make(map[string]struct{}, len(racks)) } tokens := tokenRing.tokens replicaRing := make(tokenRingReplicas, 0, len(tokens)) var totalRF int for _, rf := range n.dcs { totalRF += rf } for i, th := range tokenRing.tokens { if rf := n.dcs[th.host.DataCenter()]; rf == 0 { // skip this token since no replica in this datacenter. continue } for k, v := range skipped { skipped[k] = v[:0] } for dc := range n.dcs { replicasInDC[dc] = 0 for rack := range seenDCRacks[dc] { delete(seenDCRacks[dc], rack) } } replicas := make([]*HostInfo, 0, totalRF) for j := 0; j < len(tokens) && (len(replicas) < totalRF && !n.haveRF(replicasInDC)); j++ { // TODO: ensure we dont add the same host twice p := i + j if p >= len(tokens) { p -= len(tokens) } h := tokens[p].host dc := h.DataCenter() rack := h.Rack() rf := n.dcs[dc] if rf == 0 { // skip this DC, dont know about it or replication factor is zero continue } else if replicasInDC[dc] >= rf { if replicasInDC[dc] > rf { panic(fmt.Sprintf("replica overflow. rf=%d have=%d in dc %q", rf, replicasInDC[dc], dc)) } // have enough replicas in this DC continue } else if _, ok := dcRacks[dc][rack]; !ok { // dont know about this rack continue } racks := seenDCRacks[dc] if _, ok := racks[rack]; ok && len(racks) == len(dcRacks[dc]) { // we have been through all the racks and dont have RF yet, add this replicas = append(replicas, h) replicasInDC[dc]++ } else if !ok { if racks == nil { racks = make(map[string]struct{}, 1) seenDCRacks[dc] = racks } // new rack racks[rack] = struct{}{} replicas = append(replicas, h) r := replicasInDC[dc] + 1 if len(racks) == len(dcRacks[dc]) { // if we have been through all the racks, drain the rest of the skipped // hosts until we have RF. The next iteration will skip in the block // above skippedHosts := skipped[dc] var k int for ; k < len(skippedHosts) && r+k < rf; k++ { sh := skippedHosts[k] replicas = append(replicas, sh) } r += k skipped[dc] = skippedHosts[k:] } replicasInDC[dc] = r } else { // already seen this rack, keep hold of this host incase // we dont get enough for rf skipped[dc] = append(skipped[dc], h) } } if len(replicas) == 0 { panic(fmt.Sprintf("no replicas for token: %v", th.token)) } else if !replicas[0].Equal(th.host) { panic(fmt.Sprintf("first replica is not the primary replica for the token: expected %v got %v", replicas[0].ConnectAddress(), th.host.ConnectAddress())) } replicaRing = append(replicaRing, hostTokens{token: th.token, hosts: replicas}) } dcsWithReplicas := 0 for _, dc := range n.dcs { if dc > 0 { dcsWithReplicas++ } } if dcsWithReplicas == len(dcRacks) && len(replicaRing) != len(tokens) { panic(fmt.Sprintf("token map different size to token ring: got %d expected %d", len(replicaRing), len(tokens))) } return replicaRing } ================================================ FILE: topology_test.go ================================================ //go:build unit // +build unit /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "fmt" "sort" "testing" ) func TestPlacementStrategy_SimpleStrategy(t *testing.T) { t.Parallel() host0 := &HostInfo{hostId: tUUID(0)} host25 := &HostInfo{hostId: tUUID(25)} host50 := &HostInfo{hostId: tUUID(50)} host75 := &HostInfo{hostId: tUUID(75)} tokens := []hostToken{ {intToken(0), host0}, {intToken(25), host25}, {intToken(50), host50}, {intToken(75), host75}, } hosts := []*HostInfo{host0, host25, host50, host75} strat := &simpleStrategy{rf: 2} tokenReplicas := strat.replicaMap(&tokenRing{hosts: hosts, tokens: tokens}) if len(tokenReplicas) != len(tokens) { t.Fatalf("expected replica map to have %d items but has %d", len(tokens), len(tokenReplicas)) } for _, replicas := range tokenReplicas { if len(replicas.hosts) != strat.rf { t.Errorf("expected to have %d replicas got %d for token=%v", strat.rf, len(replicas.hosts), replicas.token) } } for i, token := range tokens { ht := tokenReplicas.replicasFor(token.token) if ht.token != token.token { t.Errorf("token %v not in replica map: %v", token, ht.hosts) } for j, replica := range ht.hosts { exp := tokens[(i+j)%len(tokens)].host if exp != replica { t.Errorf("expected host %v to be a replica of %v got %v", exp.hostId, token, replica.hostId) } } } } func TestPlacementStrategy_NetworkStrategy(t *testing.T) { t.Parallel() const ( totalDCs = 3 racksPerDC = 3 hostsPerDC = 5 ) tests := []struct { name string strat *networkTopology expectedReplicaMapSize int }{ { name: "full", strat: &networkTopology{ dcs: map[string]int{ "dc1": 1, "dc2": 2, "dc3": 3, }, }, expectedReplicaMapSize: hostsPerDC * totalDCs, }, { name: "missing", strat: &networkTopology{ dcs: map[string]int{ "dc2": 2, "dc3": 3, }, }, expectedReplicaMapSize: hostsPerDC * 2, }, { name: "zero", strat: &networkTopology{ dcs: map[string]int{ "dc1": 0, "dc2": 2, "dc3": 3, }, }, expectedReplicaMapSize: hostsPerDC * 2, }, } for _, test := range tests { test := test t.Run(test.name, func(t *testing.T) { var ( hosts []*HostInfo tokens []hostToken ) dcRing := make(map[string][]hostToken, totalDCs) hostIdx := 0 for i := 0; i < totalDCs; i++ { var dcTokens []hostToken dc := fmt.Sprintf("dc%d", i+1) for j := 0; j < hostsPerDC; j++ { rack := fmt.Sprintf("rack%d", (j%racksPerDC)+1) tokenStr := fmt.Sprintf("%s:%s:%d", dc, rack, j) h := &HostInfo{hostId: tUUID(hostIdx), dataCenter: dc, rack: rack} hostIdx++ token := hostToken{ token: orderedToken(tokenStr), host: h, } tokens = append(tokens, token) dcTokens = append(dcTokens, token) hosts = append(hosts, h) } sort.Sort(&tokenRing{tokens: dcTokens}) dcRing[dc] = dcTokens } if len(tokens) != hostsPerDC*totalDCs { t.Fatalf("expected %d tokens in the ring got %d", hostsPerDC*totalDCs, len(tokens)) } sort.Sort(&tokenRing{tokens: tokens}) var expReplicas int for _, rf := range test.strat.dcs { expReplicas += rf } tokenReplicas := test.strat.replicaMap(&tokenRing{hosts: hosts, tokens: tokens}) if len(tokenReplicas) != test.expectedReplicaMapSize { t.Fatalf("expected replica map to have %d items but has %d", test.expectedReplicaMapSize, len(tokenReplicas)) } if !sort.IsSorted(tokenReplicas) { t.Fatal("replica map was not sorted by token") } for token, replicas := range tokenReplicas { if len(replicas.hosts) != expReplicas { t.Fatalf("expected to have %d replicas got %d for token=%v", expReplicas, len(replicas.hosts), token) } } for dc, rf := range test.strat.dcs { if rf == 0 { continue } dcTokens := dcRing[dc] for i, th := range dcTokens { token := th.token allReplicas := tokenReplicas.replicasFor(token) if allReplicas.token != token { t.Fatalf("token %v not in replica map", token) } var replicas []*HostInfo for _, replica := range allReplicas.hosts { if replica.dataCenter == dc { replicas = append(replicas, replica) } } if len(replicas) != rf { t.Fatalf("expected %d replicas in dc %q got %d", rf, dc, len(replicas)) } var lastRack string for j, replica := range replicas { // expected is in the next rack var exp *HostInfo if lastRack == "" { // primary, first replica exp = dcTokens[(i+j)%len(dcTokens)].host } else { for k := 0; k < len(dcTokens); k++ { // walk around the ring from i + j to find the next host the // next rack p := (i + j + k) % len(dcTokens) h := dcTokens[p].host if h.rack != lastRack { exp = h break } } if exp.rack == lastRack { t.Fatal("no more racks") } } lastRack = replica.rack } } } }) } } ================================================ FILE: tracer.go ================================================ package gocql import ( "fmt" "io" "sync" "time" ) // Tracer is the interface implemented by query tracers. Tracers have the // ability to obtain a detailed event log of all events that happened during // the execution of a query from Cassandra. Gathering this information might // be essential for debugging and optimizing queries, but this feature should // not be used on production systems with very high load. type Tracer interface { Trace(traceId []byte) } type TraceWriter struct { session *Session w io.Writer mu sync.Mutex maxAttempts int sleepInterval time.Duration } // NewTraceWriter returns a simple Tracer implementation that outputs // the event log in a textual format. func NewTraceWriter(session *Session, w io.Writer) *TraceWriter { return &TraceWriter{session: session, w: w, maxAttempts: 5, sleepInterval: 3 * time.Millisecond} } func (t *TraceWriter) SetMaxAttempts(maxAttempts int) { t.mu.Lock() defer t.mu.Unlock() t.maxAttempts = maxAttempts } func (t *TraceWriter) SetSleepInterval(sleepInterval time.Duration) { t.mu.Lock() defer t.mu.Unlock() t.sleepInterval = sleepInterval } func (t *TraceWriter) Trace(traceId []byte) { var ( timestamp time.Time activity string source string elapsed int thread string ) t.mu.Lock() defer t.mu.Unlock() fetchAttempts := 1 if t.maxAttempts > 0 { fetchAttempts = t.maxAttempts } isDone := false for i := 0; i < fetchAttempts; i++ { var duration int iter := t.session.control.querySystem(`SELECT duration FROM system_traces.sessions WHERE session_id = ?`, traceId) iter.Scan(&duration) if duration > 0 { isDone = true } if err := iter.Close(); err != nil { fmt.Fprintln(t.w, "Error:", err) return } if isDone || i == fetchAttempts-1 { break } time.Sleep(t.sleepInterval) } if !isDone { fmt.Fprintln(t.w, "Error: failed to wait tracing to complete. !!! Tracing is incomplete !!!") } var ( coordinator string duration int ) iter := t.session.control.querySystem(`SELECT coordinator, duration FROM system_traces.sessions WHERE session_id = ?`, traceId) iter.Scan(&coordinator, &duration) if err := iter.Close(); err != nil { fmt.Fprintln(t.w, "Error:", err) return } fmt.Fprintf(t.w, "Tracing session %016x (coordinator: %s, duration: %v):\n", traceId, coordinator, time.Duration(duration)*time.Microsecond) iter = t.session.control.querySystem(`SELECT event_id, activity, source, source_elapsed, thread FROM system_traces.events WHERE session_id = ?`, traceId) for iter.Scan(×tamp, &activity, &source, &elapsed, &thread) { fmt.Fprintf(t.w, "%s: %s [%s] (source: %s, elapsed: %d)\n", timestamp.Format("2006/01/02 15:04:05.999999"), activity, thread, source, elapsed) } if err := iter.Close(); err != nil { fmt.Fprintln(t.w, "Error:", err) } } type TracerEnhanced struct { session *Session traceIDs [][]byte mu sync.Mutex } func NewTracer(session *Session) *TracerEnhanced { return &TracerEnhanced{session: session} } func (t *TracerEnhanced) Trace(traceId []byte) { t.mu.Lock() defer t.mu.Unlock() t.traceIDs = append(t.traceIDs, traceId) } func (t *TracerEnhanced) AllTraceIDs() [][]byte { t.mu.Lock() defer t.mu.Unlock() return t.traceIDs } func (t *TracerEnhanced) IsReady(traceId []byte) (bool, error) { isDone := false var duration int iter := t.session.control.querySystem(`SELECT duration FROM system_traces.sessions WHERE session_id = ?`, traceId) iter.Scan(&duration) if duration > 0 { isDone = true } if err := iter.Close(); err != nil { return false, err } if isDone { return true, nil } return false, nil } func (t *TracerEnhanced) GetCoordinatorTime(traceId []byte) (string, time.Duration, error) { var ( coordinator string duration int ) iter := t.session.control.querySystem(`SELECT coordinator, duration FROM system_traces.sessions WHERE session_id = ?`, traceId) iter.Scan(&coordinator, &duration) if err := iter.Close(); err != nil { return coordinator, time.Duration(duration) * time.Microsecond, err } return coordinator, time.Duration(duration) * time.Microsecond, nil } type TraceEntry struct { Timestamp time.Time Activity string Source string Thread string Elapsed int } func (t *TracerEnhanced) GetActivities(traceId []byte) ([]TraceEntry, error) { iter := t.session.control.querySystem(`SELECT event_id, activity, source, source_elapsed, thread FROM system_traces.events WHERE session_id = ?`, traceId) var ( timestamp time.Time activity string source string elapsed int thread string ) var activities []TraceEntry for iter.Scan(×tamp, &activity, &source, &elapsed, &thread) { activities = append(activities, TraceEntry{Timestamp: timestamp, Activity: activity, Source: source, Elapsed: elapsed, Thread: thread}) } if err := iter.Close(); err != nil { return nil, err } return activities, nil } ================================================ FILE: tracer_test.go ================================================ //go:build integration // +build integration package gocql import ( "fmt" "testing" ) func TestTracingNewAPI(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) if err := createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s (id int primary key)`, table)); err != nil { t.Fatal("create:", err) } trace := NewTracer(session) if err := session.Query(fmt.Sprintf(`INSERT INTO %s (id) VALUES (?)`, table), 42).Trace(trace).Exec(); err != nil { t.Fatal("insert:", err) } var value int if err := session.Query(fmt.Sprintf(`SELECT id FROM %s WHERE id = ?`, table), 42).Trace(trace).Scan(&value); err != nil { t.Fatal("select:", err) } else if value != 42 { t.Fatalf("value: expected %d, got %d", 42, value) } for _, traceID := range trace.AllTraceIDs() { var ( isReady bool err error ) for !isReady { isReady, err = trace.IsReady(traceID) if err != nil { t.Fatal("Error: ", err) } } activities, err := trace.GetActivities(traceID) if err != nil { t.Fatal(err) } coordinator, _, err := trace.GetCoordinatorTime(traceID) if err != nil { t.Fatal(err) } if len(activities) == 0 { t.Fatal("Failed to obtain any tracing for tradeID: ", traceID) } else if coordinator == "" { t.Fatal("Failed to obtain coordinator for traceID: ", traceID) } } } ================================================ FILE: tuple_test.go ================================================ //go:build integration // +build integration /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "fmt" "reflect" "testing" ) func TestTupleSimple(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) err := createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s( id int, coord frozen>, primary key(id))`, table)) if err != nil { t.Fatal(err) } err = session.Query(fmt.Sprintf("INSERT INTO %s(id, coord) VALUES(?, (?, ?))", table), 1, 100, -100).Exec() if err != nil { t.Fatal(err) } var ( id int coord struct { x int y int } ) iter := session.Query(fmt.Sprintf("SELECT id, coord FROM %s WHERE id=?", table), 1) if err := iter.Scan(&id, &coord.x, &coord.y); err != nil { t.Fatal(err) } if id != 1 { t.Errorf("expected to get id=1 got: %v", id) } else if coord.x != 100 { t.Errorf("expected to get coord.x=100 got: %v", coord.x) } else if coord.y != -100 { t.Errorf("expected to get coord.y=-100 got: %v", coord.y) } } func TestTuple_NullTuple(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) err := createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s( id int, coord frozen>, primary key(id))`, table)) if err != nil { t.Fatal(err) } const id = 1 err = session.Query(fmt.Sprintf("INSERT INTO %s(id, coord) VALUES(?, (?, ?))", table), id, nil, nil).Exec() if err != nil { t.Fatal(err) } x := new(int) y := new(int) iter := session.Query(fmt.Sprintf("SELECT coord FROM %s WHERE id=?", table), id) if err := iter.Scan(&x, &y); err != nil { t.Fatal(err) } if x != nil { t.Fatalf("should be nil got %+#v", x) } else if y != nil { t.Fatalf("should be nil got %+#v", y) } } func TestTuple_TupleNotSet(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) err := createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s( id int, coord frozen>, primary key(id))`, table)) if err != nil { t.Fatal(err) } const id = 1 err = session.Query(fmt.Sprintf("INSERT INTO %s(id,coord) VALUES(?, (?,?))", table), id, 1, 2).Exec() if err != nil { t.Fatal(err) } err = session.Query(fmt.Sprintf("INSERT INTO %s(id) VALUES(?)", table), id+1).Exec() if err != nil { t.Fatal(err) } x := new(int) y := new(int) iter := session.Query(fmt.Sprintf("SELECT coord FROM %s WHERE id=?", table), id) if err := iter.Scan(x, y); err != nil { t.Fatal(err) } if *x != 1 { t.Fatalf("x should be %d got %+#v, value=%d", 1, x, *x) } if *y != 2 { t.Fatalf("y should be %d got %+#v, value=%d", 2, y, *y) } // Check if the supplied targets are reset to nil iter = session.Query(fmt.Sprintf("SELECT coord FROM %s WHERE id=?", table), id+1) if err := iter.Scan(x, y); err != nil { t.Fatal(err) } if *x != 0 { t.Fatalf("x should be %d got %+#v, value=%d", 0, x, *x) } if *y != 0 { t.Fatalf("y should be %d got %+#v, value=%d", 0, y, *y) } } func TestTupleMapScan(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) err := createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s( id int, val frozen>, primary key(id))`, table)) if err != nil { t.Fatal(err) } if err := session.Query(fmt.Sprintf(`INSERT INTO %s (id, val) VALUES (1, (1, 2));`, table)).Exec(); err != nil { t.Fatal(err) } m := make(map[string]any) err = session.Query(fmt.Sprintf(`SELECT * FROM %s`, table)).MapScan(m) if err != nil { t.Fatal(err) } if m["val[0]"] != 1 { t.Fatalf("expacted val[0] to be %d but was %d", 1, m["val[0]"]) } if m["val[1]"] != 2 { t.Fatalf("expacted val[1] to be %d but was %d", 2, m["val[1]"]) } } func TestTupleMapScanNil(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) err := createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s( id int, val frozen>, primary key(id))`, table)) if err != nil { t.Fatal(err) } if err := session.Query(fmt.Sprintf(`INSERT INTO %s (id, val) VALUES (?,(?,?));`, table), 1, nil, nil).Exec(); err != nil { t.Fatal(err) } m := make(map[string]any) err = session.Query(fmt.Sprintf(`SELECT * FROM %s`, table)).MapScan(m) if err != nil { t.Fatal(err) } if m["val[0]"] != 0 { t.Fatalf("expacted val[0] to be %d but was %d", 0, m["val[0]"]) } if m["val[1]"] != 0 { t.Fatalf("expacted val[1] to be %d but was %d", 0, m["val[1]"]) } } func TestTupleMapScanNotSet(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) err := createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s( id int, val frozen>, primary key(id))`, table)) if err != nil { t.Fatal(err) } if err := session.Query(fmt.Sprintf(`INSERT INTO %s (id) VALUES (?);`, table), 1).Exec(); err != nil { t.Fatal(err) } m := make(map[string]any) err = session.Query(fmt.Sprintf(`SELECT * FROM %s`, table)).MapScan(m) if err != nil { t.Fatal(err) } if m["val[0]"] != 0 { t.Fatalf("expacted val[0] to be %d but was %d", 0, m["val[0]"]) } if m["val[1]"] != 0 { t.Fatalf("expacted val[1] to be %d but was %d", 0, m["val[1]"]) } } func TestTupleLastFieldEmpty(t *testing.T) { t.Parallel() // Regression test - empty value used to be treated as NULL value in the last tuple field session := createSession(t) defer session.Close() table := testTableName(t) err := createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s( id int, val frozen>, primary key(id))`, table)) if err != nil { t.Fatal(err) } if err := session.Query(fmt.Sprintf(`INSERT INTO %s (id, val) VALUES (?,(?,?));`, table), 1, "abc", "").Exec(); err != nil { t.Fatal(err) } var e1, e2 *string if err := session.Query(fmt.Sprintf("SELECT val FROM %s WHERE id = ?", table), 1).Scan(&e1, &e2); err != nil { t.Fatal(err) } if e1 == nil { t.Fatal("expected e1 not to be nil") } if *e1 != "abc" { t.Fatalf("expected e1 to be equal to \"abc\", but is %v", *e2) } if e2 == nil { t.Fatal("expected e2 not to be nil") } if *e2 != "" { t.Fatalf("expected e2 to be an empty string, but is %v", *e2) } } func TestTuple_NestedCollection(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) err := createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s( id int, val list>>, primary key(id))`, table)) if err != nil { t.Fatal(err) } type typ struct { A int B string } tests := []struct { name string val any }{ {name: "slice", val: [][]any{{1, "2"}, {3, "4"}}}, {name: "array", val: [][2]any{{1, "2"}, {3, "4"}}}, {name: "struct", val: []typ{{1, "2"}, {3, "4"}}}, } for i, test := range tests { t.Run(test.name, func(t *testing.T) { if err := session.Query(fmt.Sprintf(`INSERT INTO %s (id, val) VALUES (?, ?);`, table), i, test.val).Exec(); err != nil { t.Fatal(err) } rv := reflect.ValueOf(test.val) res := reflect.New(rv.Type()).Elem().Addr().Interface() err = session.Query(fmt.Sprintf(`SELECT val FROM %s WHERE id=?`, table), i).Scan(res) if err != nil { t.Fatal(err) } resVal := reflect.ValueOf(res).Elem().Interface() if !reflect.DeepEqual(test.val, resVal) { t.Fatalf("unmarshaled value not equal to the original value: expected %#v, got %#v", test.val, resVal) } }) } } func TestTuple_NullableNestedCollection(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) err := createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s( id int, val list>>, primary key(id))`, table)) if err != nil { t.Fatal(err) } type typ struct { A *string B *string } ptrStr := func(s string) *string { ret := new(string) *ret = s return ret } tests := []struct { name string val any }{ {name: "slice", val: [][]*string{{ptrStr("1"), nil}, {nil, ptrStr("2")}, {ptrStr("3"), ptrStr("")}}}, {name: "array", val: [][2]*string{{ptrStr("1"), nil}, {nil, ptrStr("2")}, {ptrStr("3"), ptrStr("")}}}, {name: "struct", val: []typ{{ptrStr("1"), nil}, {nil, ptrStr("2")}, {ptrStr("3"), ptrStr("")}}}, } for i, test := range tests { t.Run(test.name, func(t *testing.T) { if err := session.Query(fmt.Sprintf(`INSERT INTO %s (id, val) VALUES (?, ?);`, table), i, test.val).Exec(); err != nil { t.Fatal(err) } rv := reflect.ValueOf(test.val) res := reflect.New(rv.Type()).Interface() err = session.Query(fmt.Sprintf(`SELECT val FROM %s WHERE id=?`, table), i).Scan(res) if err != nil { t.Fatal(err) } resVal := reflect.ValueOf(res).Elem().Interface() if !reflect.DeepEqual(test.val, resVal) { t.Fatalf("unmarshaled value not equal to the original value: expected %#v, got %#v", test.val, resVal) } }) } } ================================================ FILE: udt_test.go ================================================ //go:build integration // +build integration /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "fmt" "strings" "testing" "time" ) type position struct { Lat int `cql:"lat"` Lon int `cql:"lon"` Padding string `json:"padding"` } // NOTE: due to current implementation details it is not currently possible to use // a pointer receiver type for the UDTMarshaler interface to handle UDT's func (p position) MarshalUDT(name string, info TypeInfo) ([]byte, error) { switch name { case "lat": return Marshal(info, p.Lat) case "lon": return Marshal(info, p.Lon) case "padding": return Marshal(info, p.Padding) default: return nil, fmt.Errorf("unknown column for position: %q", name) } } func (p *position) UnmarshalUDT(name string, info TypeInfo, data []byte) error { switch name { case "lat": return Unmarshal(info, data, &p.Lat) case "lon": return Unmarshal(info, data, &p.Lon) case "padding": return Unmarshal(info, data, &p.Padding) default: return fmt.Errorf("unknown column for position: %q", name) } } func TestUDT_Marshaler(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) typeName := testTypeName(t) err := createTable(session, fmt.Sprintf(`CREATE TYPE gocql_test.%s( lat int, lon int, padding text);`, typeName)) if err != nil { t.Fatal(err) } err = createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s( id int, name text, loc frozen<%s>, primary key(id) );`, table, typeName)) if err != nil { t.Fatal(err) } const ( expLat = -1 expLon = 2 ) pad := strings.Repeat("X", 1000) err = session.Query(fmt.Sprintf("INSERT INTO %s(id, name, loc) VALUES(?, ?, ?)", table), 1, "test", &position{expLat, expLon, pad}).Exec() if err != nil { t.Fatal(err) } pos := &position{} err = session.Query(fmt.Sprintf("SELECT loc FROM %s WHERE id = ?", table), 1).Scan(pos) if err != nil { t.Fatal(err) } if pos.Lat != expLat { t.Errorf("expeceted lat to be be %d got %d", expLat, pos.Lat) } if pos.Lon != expLon { t.Errorf("expeceted lon to be be %d got %d", expLon, pos.Lon) } if pos.Padding != pad { t.Errorf("expected to get padding %q got %q\n", pad, pos.Padding) } } func TestUDT_Reflect(t *testing.T) { t.Parallel() // Uses reflection instead of implementing the marshaling type session := createSession(t) defer session.Close() table := testTableName(t) typeName := testTypeName(t) err := createTable(session, fmt.Sprintf(`CREATE TYPE gocql_test.%s( name text, owner text);`, typeName)) if err != nil { t.Fatal(err) } err = createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s( position int, horse frozen<%s>, primary key(position) );`, table, typeName)) if err != nil { t.Fatal(err) } type horse struct { Name string `cql:"name"` Owner string `cql:"owner"` } insertedHorse := &horse{ Name: "pony", Owner: "jim", } err = session.Query(fmt.Sprintf("INSERT INTO %s(position, horse) VALUES(?, ?)", table), 1, insertedHorse).Exec() if err != nil { t.Fatal(err) } retrievedHorse := &horse{} err = session.Query(fmt.Sprintf("SELECT horse FROM %s WHERE position = ?", table), 1).Scan(retrievedHorse) if err != nil { t.Fatal(err) } if *retrievedHorse != *insertedHorse { t.Fatalf("expected to get %+v got %+v", insertedHorse, retrievedHorse) } } func TestUDT_NullObject(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) typeName := testTypeName(t) err := createTable(session, fmt.Sprintf(`CREATE TYPE gocql_test.%s( name text, owner text);`, typeName)) if err != nil { t.Fatal(err) } err = createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s( id uuid, udt_col frozen<%s>, primary key(id) );`, table, typeName)) if err != nil { t.Fatal(err) } type col struct { Name string `cql:"name"` Owner string `cql:"owner"` } id := TimeUUID() err = session.Query(fmt.Sprintf("INSERT INTO %s(id) VALUES(?)", table), id).Exec() if err != nil { t.Fatal(err) } readCol := &col{ Name: "temp", Owner: "temp", } err = session.Query(fmt.Sprintf("SELECT udt_col FROM %s WHERE id = ?", table), id).Scan(readCol) if err != nil { t.Fatal(err) } if readCol.Name != "" { t.Errorf("expected empty string to be returned for null udt: got %q", readCol.Name) } if readCol.Owner != "" { t.Errorf("expected empty string to be returned for null udt: got %q", readCol.Owner) } } func TestMapScanUDT(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) typeName := testTypeName(t) err := createTable(session, fmt.Sprintf(`CREATE TYPE gocql_test.%s ( created_timestamp timestamp, message text );`, typeName)) if err != nil { t.Fatal(err) } err = createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s ( id uuid PRIMARY KEY, type int, log_entries list> );`, table, typeName)) if err != nil { t.Fatal(err) } entry := []struct { CreatedTimestamp time.Time `cql:"created_timestamp"` Message string `cql:"message"` }{ { CreatedTimestamp: time.Now().Truncate(time.Millisecond), Message: "test time now", }, } id, _ := RandomUUID() const typ = 1 err = session.Query(fmt.Sprintf("INSERT INTO %s(id, type, log_entries) VALUES (?, ?, ?)", table), id, typ, entry).Exec() if err != nil { t.Fatal(err) } rawResult := map[string]any{} err = session.Query(fmt.Sprintf(`SELECT * FROM %s WHERE id = ?`, table), id).MapScan(rawResult) if err != nil { t.Fatal(err) } logEntries, ok := rawResult["log_entries"].([]map[string]any) if !ok { t.Fatal("log_entries not in scanned map") } if len(logEntries) != 1 { t.Fatalf("expected to get 1 log_entry got %d", len(logEntries)) } logEntry := logEntries[0] timestamp, ok := logEntry["created_timestamp"] if !ok { t.Error("created_timestamp not unmarshalled into map") } else { if ts, ok := timestamp.(time.Time); ok { if !ts.In(time.UTC).Equal(entry[0].CreatedTimestamp.In(time.UTC)) { t.Errorf("created_timestamp not equal to stored: got %v expected %v", ts.In(time.UTC), entry[0].CreatedTimestamp.In(time.UTC)) } } else { t.Errorf("created_timestamp was not time.Time got: %T", timestamp) } } message, ok := logEntry["message"] if !ok { t.Error("message not unmarshalled into map") } else { if ts, ok := message.(string); ok { if ts != message { t.Errorf("message not equal to stored: got %v expected %v", ts, entry[0].Message) } } else { t.Errorf("message was not string got: %T", message) } } } func TestUDT_MissingField(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) typeName := testTypeName(t) err := createTable(session, fmt.Sprintf(`CREATE TYPE gocql_test.%s( name text, owner text);`, typeName)) if err != nil { t.Fatal(err) } err = createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s( id uuid, udt_col frozen<%s>, primary key(id) );`, table, typeName)) if err != nil { t.Fatal(err) } type col struct { Name string `cql:"name"` } writeCol := &col{ Name: "test", } id := TimeUUID() err = session.Query(fmt.Sprintf("INSERT INTO %s(id, udt_col) VALUES(?, ?)", table), id, writeCol).Exec() if err != nil { t.Fatal(err) } readCol := &col{} err = session.Query(fmt.Sprintf("SELECT udt_col FROM %s WHERE id = ?", table), id).Scan(readCol) if err != nil { t.Fatal(err) } if readCol.Name != writeCol.Name { t.Errorf("expected %q: got %q", writeCol.Name, readCol.Name) } } func TestUDT_EmptyCollections(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) typeName := testTypeName(t) err := createTable(session, fmt.Sprintf(`CREATE TYPE gocql_test.%s( a list, b map, c set );`, typeName)) if err != nil { t.Fatal(err) } err = createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s( id uuid, udt_col frozen<%s>, primary key(id) );`, table, typeName)) if err != nil { t.Fatal(err) } type udt struct { A []string `cql:"a"` B map[string]string `cql:"b"` C []string `cql:"c"` } id := TimeUUID() err = session.Query(fmt.Sprintf("INSERT INTO %s(id, udt_col) VALUES(?, ?)", table), id, &udt{}).Exec() if err != nil { t.Fatal(err) } var val udt err = session.Query(fmt.Sprintf("SELECT udt_col FROM %s WHERE id=?", table), id).Scan(&val) if err != nil { t.Fatal(err) } if val.A != nil { t.Errorf("expected to get nil got %#+v", val.A) } if val.B != nil { t.Errorf("expected to get nil got %#+v", val.B) } if val.C != nil { t.Errorf("expected to get nil got %#+v", val.C) } } func TestUDT_UpdateField(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) typeName := testTypeName(t) err := createTable(session, fmt.Sprintf(`CREATE TYPE gocql_test.%s( name text, owner text);`, typeName)) if err != nil { t.Fatal(err) } err = createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s( id uuid, udt_col frozen<%s>, primary key(id) );`, table, typeName)) if err != nil { t.Fatal(err) } type col struct { Name string `cql:"name"` Owner string `cql:"owner"` Data string `cql:"data"` } writeCol := &col{ Name: "test-name", Owner: "test-owner", } id := TimeUUID() err = session.Query(fmt.Sprintf("INSERT INTO %s(id, udt_col) VALUES(?, ?)", table), id, writeCol).Exec() if err != nil { t.Fatal(err) } if err := createTable(session, fmt.Sprintf(`ALTER TYPE gocql_test.%s ADD data text;`, typeName)); err != nil { t.Fatal(err) } readCol := &col{} err = session.Query(fmt.Sprintf("SELECT udt_col FROM %s WHERE id = ?", table), id).Scan(readCol) if err != nil { t.Fatal(err) } if *readCol != *writeCol { t.Errorf("expected %+v: got %+v", *writeCol, *readCol) } } func TestUDT_ScanNullUDT(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() table := testTableName(t) typeName := testTypeName(t) err := createTable(session, fmt.Sprintf(`CREATE TYPE gocql_test.%s( lat int, lon int, padding text);`, typeName)) if err != nil { t.Fatal(err) } err = createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s( id int, name text, loc frozen<%s>, primary key(id) );`, table, typeName)) if err != nil { t.Fatal(err) } err = session.Query(fmt.Sprintf("INSERT INTO %s(id, name) VALUES(?, ?)", table), 1, "test").Exec() if err != nil { t.Fatal(err) } pos := &position{} err = session.Query(fmt.Sprintf("SELECT loc FROM %s WHERE id = ?", table), 1).Scan(pos) if err != nil { t.Fatal(err) } } ================================================ FILE: uuid.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2012, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql // The uuid package can be used to generate and parse universally unique // identifiers, a standardized format in the form of a 128 bit number. // // http://tools.ietf.org/html/rfc4122 import ( "crypto/rand" "errors" "fmt" "io" "net" "strings" "sync/atomic" "time" ) type UUID [16]byte var hardwareAddr []byte var clockSeq uint32 const ( VariantNCSCompat = 0 VariantIETF = 2 VariantMicrosoft = 6 VariantFuture = 7 ) func init() { if interfaces, err := net.Interfaces(); err == nil { for _, i := range interfaces { if i.Flags&net.FlagLoopback == 0 && len(i.HardwareAddr) > 0 { hardwareAddr = i.HardwareAddr break } } } if hardwareAddr == nil { // If we failed to obtain the MAC address of the current computer, // we will use a randomly generated 6 byte sequence instead and set // the multicast bit as recommended in RFC 4122. hardwareAddr = make([]byte, 6) _, err := io.ReadFull(rand.Reader, hardwareAddr) if err != nil { panic(err) } hardwareAddr[0] = hardwareAddr[0] | 0x01 } // initialize the clock sequence with a random number var clockSeqRand [2]byte io.ReadFull(rand.Reader, clockSeqRand[:]) clockSeq = uint32(clockSeqRand[1])<<8 | uint32(clockSeqRand[0]) } // ParseUUID parses a 32 digit hexadecimal number (that might contain hypens) // representing an UUID. func ParseUUID(input string) (UUID, error) { var u UUID j := 0 for _, r := range input { switch { case r == '-' && j&1 == 0: continue case r >= '0' && r <= '9' && j < 32: u[j/2] |= byte(r-'0') << uint(4-j&1*4) case r >= 'a' && r <= 'f' && j < 32: u[j/2] |= byte(r-'a'+10) << uint(4-j&1*4) case r >= 'A' && r <= 'F' && j < 32: u[j/2] |= byte(r-'A'+10) << uint(4-j&1*4) default: return UUID{}, fmt.Errorf("invalid UUID %q", input) } j += 1 } if j != 32 { return UUID{}, fmt.Errorf("invalid UUID %q", input) } return u, nil } func ParseUUIDMust(input string) UUID { uuid, err := ParseUUID(input) if err != nil { panic(err) } return uuid } // UUIDFromBytes converts a raw byte slice to an UUID. func UUIDFromBytes(input []byte) (UUID, error) { var u UUID if len(input) != 16 { return u, errors.New("UUIDs must be exactly 16 bytes long") } copy(u[:], input) return u, nil } func MustRandomUUID() UUID { uuid, err := RandomUUID() if err != nil { panic(err) } return uuid } // RandomUUID generates a totally random UUID (version 4) as described in // RFC 4122. func RandomUUID() (UUID, error) { var u UUID _, err := io.ReadFull(rand.Reader, u[:]) if err != nil { return u, err } u[6] &= 0x0F // clear version u[6] |= 0x40 // set version to 4 (random uuid) u[8] &= 0x3F // clear variant u[8] |= 0x80 // set to IETF variant return u, nil } var timeBase = time.Date(1582, time.October, 15, 0, 0, 0, 0, time.UTC).Unix() // getTimestamp converts time to UUID (version 1) timestamp. // It must be an interval of 100-nanoseconds since timeBase. func getTimestamp(t time.Time) int64 { utcTime := t.In(time.UTC) ts := int64(utcTime.Unix()-timeBase)*10000000 + int64(utcTime.Nanosecond()/100) return ts } // TimeUUID generates a new time based UUID (version 1) using the current // time as the timestamp. func TimeUUID() UUID { return UUIDFromTime(time.Now()) } // The min and max clock values for a UUID. // // Cassandra's TimeUUIDType compares the lsb parts as signed byte arrays. // Thus, the min value for each byte is -128 and the max is +127. const ( minClock = 0x8080 maxClock = 0x7f7f ) // The min and max node values for a UUID. // // See explanation about Cassandra's TimeUUIDType comparison logic above. var ( minNode = []byte{0x80, 0x80, 0x80, 0x80, 0x80, 0x80} maxNode = []byte{0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f} ) // MinTimeUUID generates a "fake" time based UUID (version 1) which will be // the smallest possible UUID generated for the provided timestamp. // // UUIDs generated by this function are not unique and are mostly suitable only // in queries to select a time range of a Cassandra's TimeUUID column. func MinTimeUUID(t time.Time) UUID { return TimeUUIDWith(getTimestamp(t), minClock, minNode) } // MaxTimeUUID generates a "fake" time based UUID (version 1) which will be // the biggest possible UUID generated for the provided timestamp. // // UUIDs generated by this function are not unique and are mostly suitable only // in queries to select a time range of a Cassandra's TimeUUID column. func MaxTimeUUID(t time.Time) UUID { return TimeUUIDWith(getTimestamp(t), maxClock, maxNode) } // UUIDFromTime generates a new time based UUID (version 1) as described in // RFC 4122. This UUID contains the MAC address of the node that generated // the UUID, the given timestamp and a sequence number. func UUIDFromTime(t time.Time) UUID { ts := getTimestamp(t) clock := atomic.AddUint32(&clockSeq, 1) return TimeUUIDWith(ts, clock, hardwareAddr) } // TimeUUIDWith generates a new time based UUID (version 1) as described in // RFC4122 with given parameters. t is the number of 100's of nanoseconds // since 15 Oct 1582 (60bits). clock is the number of clock sequence (14bits). // node is a slice to gurarantee the uniqueness of the UUID (up to 6bytes). // Note: calling this function does not increment the static clock sequence. func TimeUUIDWith(t int64, clock uint32, node []byte) UUID { var u UUID u[0], u[1], u[2], u[3] = byte(t>>24), byte(t>>16), byte(t>>8), byte(t) u[4], u[5] = byte(t>>40), byte(t>>32) u[6], u[7] = byte(t>>56)&0x0F, byte(t>>48) u[8] = byte(clock >> 8) u[9] = byte(clock) copy(u[10:], node) u[6] |= 0x10 // set version to 1 (time based uuid) u[8] &= 0x3F // clear variant u[8] |= 0x80 // set to IETF variant return u } // String returns the UUID in it's canonical form, a 32 digit hexadecimal // number in the form of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. func (u UUID) String() string { var offsets = [...]int{0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34} const hexString = "0123456789abcdef" r := make([]byte, 36) for i, b := range u { r[offsets[i]] = hexString[b>>4] r[offsets[i]+1] = hexString[b&0xF] } r[8] = '-' r[13] = '-' r[18] = '-' r[23] = '-' return string(r) } // Bytes returns the raw byte slice for this UUID. A UUID is always 128 bits // (16 bytes) long. func (u UUID) Bytes() []byte { return u[:] } var emptyUUID = UUID{} func (u UUID) IsEmpty() bool { return u == emptyUUID } // Variant returns the variant of this UUID. This package will only generate // UUIDs in the IETF variant. func (u UUID) Variant() int { x := u[8] if x&0x80 == 0 { return VariantNCSCompat } if x&0x40 == 0 { return VariantIETF } if x&0x20 == 0 { return VariantMicrosoft } return VariantFuture } // Version extracts the version of this UUID variant. The RFC 4122 describes // five kinds of UUIDs. func (u UUID) Version() int { return int(u[6] & 0xF0 >> 4) } // Node extracts the MAC address of the node who generated this UUID. It will // return nil if the UUID is not a time based UUID (version 1). func (u UUID) Node() []byte { if u.Version() != 1 { return nil } return u[10:] } // Clock extracts the clock sequence of this UUID. It will return zero if the // UUID is not a time based UUID (version 1). func (u UUID) Clock() uint32 { if u.Version() != 1 { return 0 } // Clock sequence is the lower 14bits of u[8:10] return uint32(u[8]&0x3F)<<8 | uint32(u[9]) } // Timestamp extracts the timestamp information from a time based UUID // (version 1). func (u UUID) Timestamp() int64 { if u.Version() != 1 { return 0 } return int64(uint64(u[0])<<24|uint64(u[1])<<16| uint64(u[2])<<8|uint64(u[3])) + int64(uint64(u[4])<<40|uint64(u[5])<<32) + int64(uint64(u[6]&0x0F)<<56|uint64(u[7])<<48) } // Time is like Timestamp, except that it returns a time.Time. func (u UUID) Time() time.Time { if u.Version() != 1 { return time.Time{} } t := u.Timestamp() sec := t / 1e7 nsec := (t % 1e7) * 100 return time.Unix(sec+timeBase, nsec).UTC() } // Marshaling for JSON func (u UUID) MarshalJSON() ([]byte, error) { return []byte(`"` + u.String() + `"`), nil } // Unmarshaling for JSON func (u *UUID) UnmarshalJSON(data []byte) error { str := strings.Trim(string(data), `"`) if len(str) > 36 { return fmt.Errorf("invalid JSON UUID %s", str) } parsed, err := ParseUUID(str) if err == nil { copy(u[:], parsed[:]) } return err } func (u UUID) MarshalText() ([]byte, error) { return []byte(u.String()), nil } func (u *UUID) UnmarshalText(text []byte) (err error) { *u, err = ParseUUID(string(text)) return } ================================================ FILE: uuid_test.go ================================================ //go:build unit // +build unit /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "bytes" "strings" "testing" "time" ) func TestUUIDNil(t *testing.T) { t.Parallel() var uuid UUID want, got := "00000000-0000-0000-0000-000000000000", uuid.String() if want != got { t.Fatalf("TestNil: expected %q got %q", want, got) } } var testsUUID = []struct { input string variant int version int }{ {"b4f00409-cef8-4822-802c-deb20704c365", VariantIETF, 4}, {"B4F00409-CEF8-4822-802C-DEB20704C365", VariantIETF, 4}, //Use capital letters {"f81d4fae-7dec-11d0-a765-00a0c91e6bf6", VariantIETF, 1}, {"00000000-7dec-11d0-a765-00a0c91e6bf6", VariantIETF, 1}, {"3051a8d7-aea7-1801-e0bf-bc539dd60cf3", VariantFuture, 1}, {"3051a8d7-aea7-2801-e0bf-bc539dd60cf3", VariantFuture, 2}, {"3051a8d7-aea7-3801-e0bf-bc539dd60cf3", VariantFuture, 3}, {"3051a8d7-aea7-4801-e0bf-bc539dd60cf3", VariantFuture, 4}, {"3051a8d7-aea7-3801-e0bf-bc539dd60cf3", VariantFuture, 5}, {"d0e817e1-e4b1-1801-3fe6-b4b60ccecf9d", VariantNCSCompat, 0}, {"d0e817e1-e4b1-1801-bfe6-b4b60ccecf9d", VariantIETF, 1}, {"d0e817e1-e4b1-1801-dfe6-b4b60ccecf9d", VariantMicrosoft, 0}, {"d0e817e1-e4b1-1801-ffe6-b4b60ccecf9d", VariantFuture, 0}, } func TestPredefinedUUID(t *testing.T) { t.Parallel() for i := range testsUUID { uuid, err := ParseUUID(testsUUID[i].input) if err != nil { t.Errorf("ParseUUID #%d: %v", i, err) continue } if str := uuid.String(); str != strings.ToLower(testsUUID[i].input) { t.Errorf("String #%d: expected %q got %q", i, testsUUID[i].input, str) continue } if variant := uuid.Variant(); variant != testsUUID[i].variant { t.Errorf("Variant #%d: expected %d got %d", i, testsUUID[i].variant, variant) } if testsUUID[i].variant == VariantIETF { if version := uuid.Version(); version != testsUUID[i].version { t.Errorf("Version #%d: expected %d got %d", i, testsUUID[i].version, version) } } json, err := uuid.MarshalJSON() if err != nil { t.Errorf("MarshalJSON #%d: %v", i, err) } expectedJson := `"` + strings.ToLower(testsUUID[i].input) + `"` if string(json) != expectedJson { t.Errorf("MarshalJSON #%d: expected %v got %v", i, expectedJson, string(json)) } var unmarshaled UUID err = unmarshaled.UnmarshalJSON(json) if err != nil { t.Errorf("UnmarshalJSON #%d: %v", i, err) } if unmarshaled != uuid { t.Errorf("UnmarshalJSON #%d: expected %v got %v", i, uuid, unmarshaled) } } } func TestInvalidUUIDCharacter(t *testing.T) { t.Parallel() _, err := ParseUUID("z4f00409-cef8-4822-802c-deb20704c365") if err == nil || !strings.Contains(err.Error(), "invalid UUID") { t.Fatalf("expected invalid UUID error, got '%v' ", err) } } func TestInvalidUUIDLength(t *testing.T) { t.Parallel() _, err := ParseUUID("4f00") if err == nil || !strings.Contains(err.Error(), "invalid UUID") { t.Fatalf("expected invalid UUID error, got '%v' ", err) } _, err = UUIDFromBytes(TimeUUID().Bytes()[:15]) if err == nil || err.Error() != "UUIDs must be exactly 16 bytes long" { t.Fatalf("expected error '%v', got '%v'", "UUIDs must be exactly 16 bytes long", err) } } func TestRandomUUID(t *testing.T) { t.Parallel() for i := 0; i < 20; i++ { uuid, err := RandomUUID() if err != nil { t.Errorf("RandomUUID: %v", err) } if variant := uuid.Variant(); variant != VariantIETF { t.Errorf("wrong variant. expected %d got %d", VariantIETF, variant) } if version := uuid.Version(); version != 4 { t.Errorf("wrong version. expected %d got %d", 4, version) } } } func TestRandomUUIDInvalidAPICalls(t *testing.T) { t.Parallel() uuid, err := RandomUUID() if err != nil { t.Fatalf("unexpected error %v", err) } if node := uuid.Node(); node != nil { t.Fatalf("expected nil, got %v", node) } if stamp := uuid.Timestamp(); stamp != 0 { t.Fatalf("expceted 0, got %v", stamp) } zeroT := time.Time{} if to := uuid.Time(); to != zeroT { t.Fatalf("expected %v, got %v", zeroT, to) } } func TestUUIDFromTime(t *testing.T) { t.Parallel() date := time.Date(1982, 5, 5, 12, 34, 56, 400, time.UTC) uuid := UUIDFromTime(date) if uuid.Time() != date { t.Errorf("embedded time incorrect. Expected %v got %v", date, uuid.Time()) } } func TestTimeUUIDWith(t *testing.T) { t.Parallel() utcTime := time.Date(1982, 5, 5, 12, 34, 56, 400, time.UTC) ts := int64(utcTime.Unix()-timeBase)*10000000 + int64(utcTime.Nanosecond()/100) clockSeq := uint32(0x3FFF) // Max number of clock sequence. node := [7]byte{0, 1, 2, 3, 4, 5, 6} // The last element should be ignored. uuid := TimeUUIDWith(ts, clockSeq, node[:]) if got := uuid.Variant(); got != VariantIETF { t.Errorf("wrong variant. expected %d got %d", VariantIETF, got) } if got, want := uuid.Version(), 1; got != want { t.Errorf("wrong version. Expected %v got %v", want, got) } if got := uuid.Timestamp(); got != int64(ts) { t.Errorf("wrong timestamp. Expected %v got %v", ts, got) } if got := uuid.Clock(); uint32(got) != clockSeq { t.Errorf("wrong clock. expected %v got %v", clockSeq, got) } if got, want := uuid.Node(), node[:6]; !bytes.Equal(got, want) { t.Errorf("wrong node. expected %x, bot %x", want, got) } } func TestParseUUID(t *testing.T) { t.Parallel() uuid := ParseUUIDMust("486f3a88-775b-11e3-ae07-d231feb1dc81") if uuid.Time() != time.Date(2014, 1, 7, 5, 19, 29, 222516000, time.UTC) { t.Errorf("Expected date of 1/7/2014 at 5:19:29.222516, got %v", uuid.Time()) } } func TestTimeUUID(t *testing.T) { t.Parallel() var node []byte timestamp := int64(0) for i := 0; i < 20; i++ { uuid := TimeUUID() if variant := uuid.Variant(); variant != VariantIETF { t.Errorf("wrong variant. expected %d got %d", VariantIETF, variant) } if version := uuid.Version(); version != 1 { t.Errorf("wrong version. expected %d got %d", 1, version) } if n := uuid.Node(); !bytes.Equal(n, node) && i > 0 { t.Errorf("wrong node. expected %x, got %x", node, n) } else if i == 0 { node = n } ts := uuid.Timestamp() if ts < timestamp { t.Errorf("timestamps must grow: timestamp=%v ts=%v", timestamp, ts) } timestamp = ts } } func TestUnmarshalJSON(t *testing.T) { t.Parallel() var withHyphens, withoutHypens, tooLong UUID withHyphens.UnmarshalJSON([]byte(`"486f3a88-775b-11e3-ae07-d231feb1dc81"`)) if withHyphens.Time().Truncate(time.Second) != time.Date(2014, 1, 7, 5, 19, 29, 0, time.UTC) { t.Errorf("Expected date of 1/7/2014 at 5:19:29, got %v", withHyphens.Time()) } withoutHypens.UnmarshalJSON([]byte(`"486f3a88775b11e3ae07d231feb1dc81"`)) if withoutHypens.Time().Truncate(time.Second) != time.Date(2014, 1, 7, 5, 19, 29, 0, time.UTC) { t.Errorf("Expected date of 1/7/2014 at 5:19:29, got %v", withoutHypens.Time()) } err := tooLong.UnmarshalJSON([]byte(`"486f3a88-775b-11e3-ae07-d231feb1dc81486f3a88"`)) if err == nil { t.Errorf("no error for invalid JSON UUID") } } func TestMarshalText(t *testing.T) { t.Parallel() u, err := ParseUUID("486f3a88-775b-11e3-ae07-d231feb1dc81") if err != nil { t.Fatal(err) } text, err := u.MarshalText() if err != nil { t.Fatal(err) } var u2 UUID if err := u2.UnmarshalText(text); err != nil { t.Fatal(err) } if u != u2 { t.Fatalf("uuids not equal after marshalling: before=%s after=%s", u, u2) } } func TestMinTimeUUID(t *testing.T) { t.Parallel() aTime := time.Now() minTimeUUID := MinTimeUUID(aTime) ts := aTime.Unix() tsFromUUID := minTimeUUID.Time().Unix() if ts != tsFromUUID { t.Errorf("timestamps are not equal: expected %d, got %d", ts, tsFromUUID) } clockFromUUID := minTimeUUID.Clock() // clear two most significant bits, as they are used for IETF variant if minClock&0x3FFF != clockFromUUID { t.Errorf("clocks are not equal: expected %08b, got %08b", minClock&0x3FFF, clockFromUUID) } nodeFromUUID := minTimeUUID.Node() if !bytes.Equal(minNode, nodeFromUUID) { t.Errorf("nodes are not equal: expected %08b, got %08b", minNode, nodeFromUUID) } } func TestMaxTimeUUID(t *testing.T) { t.Parallel() aTime := time.Now() maxTimeUUID := MaxTimeUUID(aTime) ts := aTime.Unix() tsFromUUID := maxTimeUUID.Time().Unix() if ts != tsFromUUID { t.Errorf("timestamps are not equal: expected %d, got %d", ts, tsFromUUID) } clockFromUUID := maxTimeUUID.Clock() if maxClock&0x3FFF != clockFromUUID { t.Errorf("clocks are not equal: expected %08b, got %08b", maxClock&0x3FFF, clockFromUUID) } nodeFromUUID := maxTimeUUID.Node() if !bytes.Equal(maxNode, nodeFromUUID) { t.Errorf("nodes are not equal: expected %08b, got %08b", maxNode, nodeFromUUID) } } ================================================ FILE: vector_bench_test.go ================================================ // Copyright (c) 2012 The gocql Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build all || unit // +build all unit package gocql import ( "encoding/binary" "math" "testing" ) const vectorTypePrefix = apacheCassandraTypePrefix + "VectorType(" + apacheCassandraTypePrefix + "FloatType, " const vectorTypeSuffix = ")" func makeFloatVectorType(dim int, dimStr string) VectorType { return VectorType{ NativeType: NativeType{ proto: protoVersion4, typ: TypeCustom, custom: vectorTypePrefix + dimStr + vectorTypeSuffix, }, SubType: NativeType{proto: protoVersion4, typ: TypeFloat}, Dimensions: dim, } } // BenchmarkUnmarshalVectorFloat32 measures unmarshal performance for float32 vectors // across common embedding dimensions used in AI/ML applications. func BenchmarkUnmarshalVectorFloat32(b *testing.B) { dims := []struct { dim int dimStr string }{ {dim: 128, dimStr: "128"}, {dim: 384, dimStr: "384"}, {dim: 768, dimStr: "768"}, {dim: 1536, dimStr: "1536"}, } for _, entry := range dims { dim := entry.dim dimStr := entry.dimStr b.Run("dim_"+dimStr, func(b *testing.B) { b.ReportAllocs() data := make([]byte, dim*4) for i := 0; i < dim; i++ { binary.BigEndian.PutUint32(data[i*4:], math.Float32bits(float32(i)*0.1)) } info := makeFloatVectorType(dim, dimStr) var result []float32 b.SetBytes(int64(dim * 4)) b.ResetTimer() for i := 0; i < b.N; i++ { if err := unmarshalVector(info, data, &result); err != nil { b.Fatal(err) } } }) } } // BenchmarkMarshalVectorFloat32 measures marshal performance for float32 vectors // across common embedding dimensions. func BenchmarkMarshalVectorFloat32(b *testing.B) { dims := []struct { dim int dimStr string }{ {dim: 128, dimStr: "128"}, {dim: 384, dimStr: "384"}, {dim: 768, dimStr: "768"}, {dim: 1536, dimStr: "1536"}, } for _, entry := range dims { dim := entry.dim dimStr := entry.dimStr b.Run("dim_"+dimStr, func(b *testing.B) { b.ReportAllocs() vec := make([]float32, dim) for i := range vec { vec[i] = float32(i) * 0.1 } info := makeFloatVectorType(dim, dimStr) b.SetBytes(int64(dim * 4)) b.ResetTimer() for i := 0; i < b.N; i++ { if _, err := marshalVector(info, vec); err != nil { b.Fatal(err) } } }) } } // BenchmarkVectorRoundTrip measures full marshal -> unmarshal cycle. func BenchmarkVectorRoundTrip(b *testing.B) { dims := []struct { dim int dimStr string }{ {dim: 128, dimStr: "128"}, {dim: 384, dimStr: "384"}, {dim: 768, dimStr: "768"}, {dim: 1536, dimStr: "1536"}, } for _, entry := range dims { dim := entry.dim dimStr := entry.dimStr b.Run("dim_"+dimStr, func(b *testing.B) { b.ReportAllocs() srcVec := make([]float32, dim) for i := range srcVec { srcVec[i] = float32(i) * 0.1 } info := makeFloatVectorType(dim, dimStr) var dstVec []float32 b.SetBytes(int64(dim * 4 * 2)) b.ResetTimer() for i := 0; i < b.N; i++ { data, err := marshalVector(info, srcVec) if err != nil { b.Fatal(err) } if err := unmarshalVector(info, data, &dstVec); err != nil { b.Fatal(err) } } }) } } ================================================ FILE: vector_test.go ================================================ //go:build integration // +build integration /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "fmt" "net" "reflect" "sort" "testing" "time" "github.com/stretchr/testify/require" "gopkg.in/inf.v0" "github.com/gocql/gocql/internal/tests" ) type person struct { FirstName string `cql:"first_name"` LastName string `cql:"last_name"` Age int `cql:"age"` } func (p person) String() string { return fmt.Sprintf("Person{firstName: %s, lastName: %s, Age: %d}", p.FirstName, p.LastName, p.Age) } func TestVector_Marshaler(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() if *flagDistribution == "cassandra" && flagCassVersion.Before(5, 0, 0) { t.Skip("Vector types have been introduced in Cassandra 5.0") } if *flagDistribution == "scylla" && flagCassVersion.Before(2025, 3, 0) { t.Skip("Vector types have been introduced in ScyllaDB 2025.3") } fixedTable := testTableName(t, "fixed") variableTable := testTableName(t, "variable") err := createTable(session, fmt.Sprintf(`CREATE TABLE IF NOT EXISTS gocql_test.%s(id int primary key, vec vector);`, fixedTable)) if err != nil { t.Fatal(err) } err = createTable(session, fmt.Sprintf(`CREATE TABLE IF NOT EXISTS gocql_test.%s(id int primary key, vec vector);`, variableTable)) if err != nil { t.Fatal(err) } insertFixVec := []float32{8, 2.5, -5.0} err = session.Query(fmt.Sprintf("INSERT INTO %s(id, vec) VALUES(?, ?)", fixedTable), 1, insertFixVec).Exec() if err != nil { t.Fatal(err) } var selectFixVec []float32 err = session.Query(fmt.Sprintf("SELECT vec FROM %s WHERE id = ?", fixedTable), 1).Scan(&selectFixVec) if err != nil { t.Fatal(err) } tests.AssertDeepEqual(t, "fixed size element vector", insertFixVec, selectFixVec) longText := tests.RandomText(500) insertVarVec := []string{"apache", "cassandra", longText, "gocql"} err = session.Query(fmt.Sprintf("INSERT INTO %s(id, vec) VALUES(?, ?)", variableTable), 1, insertVarVec).Exec() if err != nil { t.Fatal(err) } var selectVarVec []string err = session.Query(fmt.Sprintf("SELECT vec FROM %s WHERE id = ?", variableTable), 1).Scan(&selectVarVec) if err != nil { t.Fatal(err) } tests.AssertDeepEqual(t, "variable size element vector", insertVarVec, selectVarVec) } func TestVector_Types(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() if *flagDistribution == "cassandra" && flagCassVersion.Before(5, 0, 0) { t.Skip("Vector types have been introduced in Cassandra 5.0") } if *flagDistribution == "scylla" && flagCassVersion.Before(2025, 4, 0) { t.Skip("Vector types are useful in ScyllaDB from 2025.4 and on") } timestamp1, _ := time.Parse("2006-01-02", "2000-01-01") timestamp2, _ := time.Parse("2006-01-02 15:04:05", "2024-01-01 10:31:45") timestamp3, _ := time.Parse("2006-01-02 15:04:05.000", "2024-05-01 10:31:45.987") date1, _ := time.Parse("2006-01-02", "2000-01-01") date2, _ := time.Parse("2006-01-02", "2022-03-14") date3, _ := time.Parse("2006-01-02", "2024-12-31") time1, _ := time.Parse("15:04:05", "01:00:00") time2, _ := time.Parse("15:04:05", "15:23:59") time3, _ := time.Parse("15:04:05.000", "10:31:45.987") duration1 := Duration{0, 1, 1920000000000} duration2 := Duration{1, 1, 1920000000000} duration3 := Duration{31, 0, 60000000000} map1 := make(map[string]int) map1["a"] = 1 map1["b"] = 2 map1["c"] = 3 map2 := make(map[string]int) map2["abc"] = 123 map3 := make(map[string]int) testCases := []struct { name string cqlType string value any comparator func(*testing.T, any, any) }{ {name: "ascii", cqlType: TypeAscii.String(), value: []string{"a", "1", "Z"}}, {name: "bigint", cqlType: TypeBigInt.String(), value: []int64{1, 2, 3}}, {name: "blob", cqlType: TypeBlob.String(), value: [][]byte{[]byte{1, 2, 3}, []byte{4, 5, 6, 7}, []byte{8, 9}}}, {name: "boolean", cqlType: TypeBoolean.String(), value: []bool{true, false, true}}, {name: "counter", cqlType: TypeCounter.String(), value: []int64{5, 6, 7}}, {name: "decimal", cqlType: TypeDecimal.String(), value: []inf.Dec{*inf.NewDec(1, 0), *inf.NewDec(2, 1), *inf.NewDec(-3, 2)}}, {name: "double", cqlType: TypeDouble.String(), value: []float64{0.1, -1.2, 3}}, {name: "float", cqlType: TypeFloat.String(), value: []float32{0.1, -1.2, 3}}, {name: "int", cqlType: TypeInt.String(), value: []int32{1, 2, 3}}, {name: "text", cqlType: TypeText.String(), value: []string{"a", "b", "c"}}, {name: "timestamp", cqlType: TypeTimestamp.String(), value: []time.Time{timestamp1, timestamp2, timestamp3}}, {name: "uuid", cqlType: TypeUUID.String(), value: []UUID{MustRandomUUID(), MustRandomUUID(), MustRandomUUID()}}, {name: "varchar", cqlType: TypeVarchar.String(), value: []string{"abc", "def", "ghi"}}, {name: "varint", cqlType: TypeVarint.String(), value: []uint64{uint64(1234), uint64(123498765), uint64(18446744073709551615)}}, {name: "timeuuid", cqlType: TypeTimeUUID.String(), value: []UUID{TimeUUID(), TimeUUID(), TimeUUID()}}, { name: "inet", cqlType: TypeInet.String(), value: []net.IP{net.IPv4(127, 0, 0, 1), net.IPv4(192, 168, 1, 1), net.IPv4(8, 8, 8, 8)}, comparator: func(t *testing.T, e any, a any) { expected := e.([]net.IP) actual := a.([]net.IP) tests.AssertEqual(t, "vector size", len(expected), len(actual)) for i, _ := range expected { tests.AssertTrue(t, "vector", expected[i].Equal(actual[i])) } }, }, {name: "date", cqlType: TypeDate.String(), value: []time.Time{date1, date2, date3}}, {name: "time", cqlType: TypeTimestamp.String(), value: []time.Time{time1, time2, time3}}, {name: "smallint", cqlType: TypeSmallInt.String(), value: []int16{127, 256, -1234}}, {name: "tinyint", cqlType: TypeTinyInt.String(), value: []int8{127, 9, -123}}, {name: "duration", cqlType: TypeDuration.String(), value: []Duration{duration1, duration2, duration3}}, {name: "vector_vector_float", cqlType: "vector", value: [][]float32{{0.1, -1.2, 3, 5, 5}, {10.1, -122222.0002, 35.0, 1, 1}, {0, 0, 0, 0, 0}}}, { name: "vector_vector_set_float", cqlType: "vector, 5>", value: [][][]float32{ {{1, 2}, {2, -1}, {3}, {0}, {-1.3}}, {{2, 3}, {2, -1}, {3}, {0}, {-1.3}}, {{1, 1000.0}, {0}, {}, {12, 14, 15, 16}, {-1.3}}, }, comparator: func(t *testing.T, e any, a any) { expected := e.([][][]float32) actual := a.([][][]float32) tests.AssertEqual(t, "vector size", len(expected), len(actual)) for i := range expected { expVector := expected[i] actVector := actual[i] tests.AssertEqual(t, "vector size", len(expVector), len(actVector)) for j := range expVector { expSet := append([]float32(nil), expVector[j]...) actSet := append([]float32(nil), actVector[j]...) sort.Slice(expSet, func(a, b int) bool { return expSet[a] < expSet[b] }) sort.Slice(actSet, func(a, b int) bool { return actSet[a] < actSet[b] }) tests.AssertDeepEqual(t, "vector set", expSet, actSet) } } }, }, {name: "vector_tuple_text_int_float", cqlType: "tuple", value: [][]any{{"a", 1, float32(0.5)}, {"b", 2, float32(-1.2)}, {"c", 3, float32(0)}}}, {name: "vector_tuple_text_list_text", cqlType: "tuple>", value: [][]any{{"a", []string{"b", "c"}}, {"d", []string{"e", "g", "f"}}, {"h", []string{"i"}}}}, { name: "vector_set_text", cqlType: "set", value: [][]string{{"a", "b"}, {"c", "d"}, {"f", "e"}}, comparator: func(t *testing.T, e any, a any) { expected := e.([][]string) actual := a.([][]string) tests.AssertEqual(t, "vector size", len(expected), len(actual)) for i := range expected { expSet := append([]string(nil), expected[i]...) actSet := append([]string(nil), actual[i]...) sort.Strings(expSet) sort.Strings(actSet) tests.AssertDeepEqual(t, "vector set", expSet, actSet) } }, }, {name: "vector_list_int", cqlType: "list", value: [][]int32{{1, 2, 3}, {-1, -2, -3}, {0, 0, 0}}}, {name: "vector_map_text_int", cqlType: "map", value: []map[string]int{map1, map2, map3}}, } for _, test := range testCases { t.Run(test.name, func(t *testing.T) { tableName := testTableName(t, test.name) err := createTable(session, fmt.Sprintf(`CREATE TABLE IF NOT EXISTS gocql_test.%s(id int primary key, vec vector<%s, 3>);`, tableName, test.cqlType)) if err != nil { t.Fatal(err) } err = session.Query(fmt.Sprintf("INSERT INTO %s(id, vec) VALUES(?, ?)", tableName), 1, test.value).Exec() if err != nil { t.Fatal(err) } v := reflect.New(reflect.TypeOf(test.value)) err = session.Query(fmt.Sprintf("SELECT vec FROM %s WHERE id = ?", tableName), 1).Scan(v.Interface()) if err != nil { t.Fatal(err) } if test.comparator != nil { test.comparator(t, test.value, v.Elem().Interface()) } else { tests.AssertDeepEqual(t, "vector", test.value, v.Elem().Interface()) } }) } } func TestVector_MarshalerUDT(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() if *flagDistribution == "cassandra" && flagCassVersion.Before(5, 0, 0) { t.Skip("Vector types have been introduced in Cassandra 5.0") } if *flagDistribution == "scylla" && flagCassVersion.Before(2025, 3, 0) { t.Skip("Vector types have been introduced in ScyllaDB 2025.3") } table := testTableName(t) typeName := testTypeName(t) err := createTable(session, fmt.Sprintf(`CREATE TYPE gocql_test.%s( first_name text, last_name text, age int);`, typeName)) if err != nil { t.Fatal(err) } err = createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s( id int, couple vector<%s, 2>, primary key(id) );`, table, typeName)) if err != nil { t.Fatal(err) } p1 := person{"Johny", "Bravo", 25} p2 := person{"Capitan", "Planet", 5} insVec := []person{p1, p2} err = session.Query(fmt.Sprintf("INSERT INTO %s(id, couple) VALUES(?, ?)", table), 1, insVec).Exec() if err != nil { t.Fatal(err) } var selVec []person err = session.Query(fmt.Sprintf("SELECT couple FROM %s WHERE id = ?", table), 1).Scan(&selVec) if err != nil { t.Fatal(err) } tests.AssertDeepEqual(t, "udt", &insVec, &selVec) } func TestVector_Empty(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() if *flagDistribution == "cassandra" && flagCassVersion.Before(5, 0, 0) { t.Skip("Vector types have been introduced in Cassandra 5.0") } if *flagDistribution == "scylla" && flagCassVersion.Before(2025, 3, 0) { t.Skip("Vector types have been introduced in ScyllaDB 2025.3") } fixedTable := testTableName(t, "fixed") variableTable := testTableName(t, "variable") err := createTable(session, fmt.Sprintf(`CREATE TABLE IF NOT EXISTS gocql_test.%s(id int primary key, vec vector);`, fixedTable)) if err != nil { t.Fatal(err) } err = createTable(session, fmt.Sprintf(`CREATE TABLE IF NOT EXISTS gocql_test.%s(id int primary key, vec vector);`, variableTable)) if err != nil { t.Fatal(err) } err = session.Query(fmt.Sprintf("INSERT INTO %s(id) VALUES(?)", fixedTable), 1).Exec() if err != nil { t.Fatal(err) } var selectFixVec []float32 err = session.Query(fmt.Sprintf("SELECT vec FROM %s WHERE id = ?", fixedTable), 1).Scan(&selectFixVec) if err != nil { t.Fatal(err) } tests.AssertTrue(t, "fixed size element vector is empty", selectFixVec == nil) err = session.Query(fmt.Sprintf("INSERT INTO %s(id) VALUES(?)", variableTable), 1).Exec() if err != nil { t.Fatal(err) } var selectVarVec []string err = session.Query(fmt.Sprintf("SELECT vec FROM %s WHERE id = ?", variableTable), 1).Scan(&selectVarVec) if err != nil { t.Fatal(err) } tests.AssertTrue(t, "variable size element vector is empty", selectVarVec == nil) } func TestVector_MissingDimension(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() if *flagDistribution == "cassandra" && flagCassVersion.Before(5, 0, 0) { t.Skip("Vector types have been introduced in Cassandra 5.0") } if *flagDistribution == "scylla" && flagCassVersion.Before(2025, 3, 0) { t.Skip("Vector types have been introduced in ScyllaDB 2025.3") } table := testTableName(t) err := createTable(session, fmt.Sprintf(`CREATE TABLE IF NOT EXISTS gocql_test.%s(id int primary key, vec vector);`, table)) if err != nil { t.Fatal(err) } err = session.Query(fmt.Sprintf("INSERT INTO %s(id, vec) VALUES(?, ?)", table), 1, []float32{8, -5.0}).Exec() require.Error(t, err, "expected vector with 3 dimensions, received 2") err = session.Query(fmt.Sprintf("INSERT INTO %s(id, vec) VALUES(?, ?)", table), 1, []float32{8, -5.0, 1, 3}).Exec() require.Error(t, err, "expected vector with 3 dimensions, received 4") } func TestVector_SubTypeParsing(t *testing.T) { t.Parallel() if *flagDistribution == "scylla" && flagCassVersion.Before(2025, 4, 0) { t.Skip("Vector types are useful in ScyllaDB from 2025.4 and on") } prefix := apacheCassandraTypePrefix vectorTypePrefix := prefix + "VectorType" testCases := []struct { name string custom string expected TypeInfo }{ {name: "text", custom: prefix + "UTF8Type", expected: NativeType{typ: TypeVarchar}}, {name: "set_int", custom: prefix + "SetType(" + prefix + "Int32Type)", expected: CollectionType{NativeType: NativeType{typ: TypeSet}, Key: nil, Elem: NativeType{typ: TypeInt}}}, { name: "udt", custom: prefix + "UserType(gocql_test,706572736f6e,66697273745f6e616d65:" + prefix + "UTF8Type,6c6173745f6e616d65:" + prefix + "UTF8Type,616765:" + prefix + "Int32Type)", expected: UDTTypeInfo{ NativeType: NativeType{typ: TypeUDT}, KeySpace: "gocql_test", Name: "person", Elements: []UDTField{ UDTField{Name: "first_name", Type: NativeType{typ: TypeVarchar}}, UDTField{Name: "last_name", Type: NativeType{typ: TypeVarchar}}, UDTField{Name: "age", Type: NativeType{typ: TypeInt}}, }, }, }, { name: "tuple", custom: prefix + "TupleType(" + prefix + "UTF8Type," + prefix + "Int32Type," + prefix + "UTF8Type)", expected: TupleTypeInfo{ NativeType: NativeType{typ: TypeTuple}, Elems: []TypeInfo{ NativeType{typ: TypeVarchar}, NativeType{typ: TypeInt}, NativeType{typ: TypeVarchar}, }, }, }, { name: "vector_vector_inet", custom: prefix + "VectorType(" + prefix + "VectorType(" + prefix + "InetAddressType, 2), 3)", expected: VectorType{ NativeType: NativeType{typ: TypeCustom, custom: vectorTypePrefix}, SubType: VectorType{ NativeType: NativeType{typ: TypeCustom, custom: vectorTypePrefix}, SubType: NativeType{typ: TypeInet}, Dimensions: 2, }, Dimensions: 3, }, }, { name: "map_int_vector_text", custom: prefix + "MapType(" + prefix + "Int32Type," + prefix + "VectorType(" + prefix + "UTF8Type, 10))", expected: CollectionType{ NativeType: NativeType{typ: TypeMap}, Key: NativeType{typ: TypeInt}, Elem: VectorType{ NativeType: NativeType{typ: TypeCustom, custom: vectorTypePrefix}, SubType: NativeType{typ: TypeVarchar}, Dimensions: 10, }, }, }, { name: "set_map_vector_text_text", custom: prefix + "SetType(" + prefix + "MapType(" + prefix + "VectorType(" + prefix + "Int32Type, 10)," + prefix + "UTF8Type))", expected: CollectionType{ NativeType: NativeType{typ: TypeSet}, Key: nil, Elem: CollectionType{ NativeType: NativeType{typ: TypeMap}, Key: VectorType{ NativeType: NativeType{typ: TypeCustom, custom: vectorTypePrefix}, SubType: NativeType{typ: TypeInt}, Dimensions: 10, }, Elem: NativeType{typ: TypeVarchar}, }, }, }, } for _, test := range testCases { t.Run(test.name, func(t *testing.T) { f := newFramer(nil, 0) f.writeShort(0) f.writeString(fmt.Sprintf("%sVectorType(%s, 2)", prefix, test.custom)) parsedType := f.readTypeInfo() require.IsType(t, parsedType, VectorType{}) vectorType := parsedType.(VectorType) tests.AssertEqual(t, "dimensions", 2, vectorType.Dimensions) tests.AssertDeepEqual(t, "vector", test.expected, vectorType.SubType) }) } } ================================================ FILE: version.go ================================================ /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import "runtime/debug" const ( mainPackage = "github.com/gocql/gocql" ) var defaultDriverVersion string func init() { buildInfo, ok := debug.ReadBuildInfo() if ok { for _, d := range buildInfo.Deps { if d.Path == mainPackage { defaultDriverVersion = d.Version if d.Replace != nil { defaultDriverVersion = d.Replace.Version } break } } } } ================================================ FILE: warning_handler.go ================================================ package gocql type DefaultWarningHandler struct { logger StdLogger } func DefaultWarningHandlerBuilder(session *Session) WarningHandler { return DefaultWarningHandler{ logger: session.logger, } } func (d DefaultWarningHandler) HandleWarnings(qry ExecutableQuery, host *HostInfo, warnings []string) { if d.logger == nil { return } if host != nil && !host.hostId.IsEmpty() { d.logger.Printf("[%s] warnings: %v", host.hostId.String(), warnings) } else { d.logger.Printf("Cluster warnings: %v", warnings) } } var _ WarningHandler = DefaultWarningHandler{} func NoopWarningHandlerBuilder(session *Session) WarningHandler { return nil } ================================================ FILE: wiki_test.go ================================================ //go:build integration // +build integration /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Content before git sha 34fdeebefcbf183ed7f916f931aa0586fdaa1b40 * Copyright (c) 2016, The Gocql authors, * provided under the BSD-3-Clause License. * See the NOTICE file distributed with this work for additional information. */ package gocql import ( "fmt" "reflect" "sort" "testing" "time" "gopkg.in/inf.v0" ) type WikiPage struct { Title string RevId UUID Body string Views int64 Protected bool Modified time.Time Rating *inf.Dec Tags []string Attachments map[string]WikiAttachment } type WikiAttachment []byte var wikiTestData = []*WikiPage{ { Title: "Frontpage", RevId: TimeUUID(), Body: "Welcome to this wiki page!", Rating: inf.NewDec(131, 3), Modified: time.Date(2013, time.August, 13, 9, 52, 3, 0, time.UTC), Tags: []string{"start", "important", "test"}, Attachments: map[string]WikiAttachment{ "logo": WikiAttachment("\x00company logo\x00"), "favicon": WikiAttachment("favicon.ico"), }, }, { Title: "Foobar", RevId: TimeUUID(), Body: "foo::Foo f = new foo::Foo(foo::Foo::INIT);", Modified: time.Date(2013, time.August, 13, 9, 52, 3, 0, time.UTC), }, } type WikiTest struct { session *Session tb testing.TB table string } func CreateSchema(session *Session, tb testing.TB, table string) *WikiTest { table = testTableName(tb, table) if err := createTable(session, fmt.Sprintf("DROP TABLE IF EXISTS gocql_test.%s", table)); err != nil { tb.Fatal("CreateSchema:", err) } err := createTable(session, fmt.Sprintf(`CREATE TABLE gocql_test.%s ( title varchar, revid timeuuid, body varchar, views bigint, protected boolean, modified timestamp, rating decimal, tags set, attachments map, PRIMARY KEY (title, revid) )`, table)) if err != nil { tb.Fatal("CreateSchema:", err) } return &WikiTest{ session: session, tb: tb, table: table, } } func (w *WikiTest) CreatePages(n int) { var page WikiPage t0 := time.Now() for i := 0; i < n; i++ { page.Title = fmt.Sprintf("generated_%d", (i&16)+1) page.Modified = t0.Add(time.Duration(i-n) * time.Minute) page.RevId = UUIDFromTime(page.Modified) page.Body = fmt.Sprintf("text %d", i) if err := w.InsertPage(&page); err != nil { w.tb.Error("CreatePages:", err) } } } func (w *WikiTest) InsertPage(page *WikiPage) error { return w.session.Query(fmt.Sprintf(`INSERT INTO %s (title, revid, body, views, protected, modified, rating, tags, attachments) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, w.table), page.Title, page.RevId, page.Body, page.Views, page.Protected, page.Modified, page.Rating, page.Tags, page.Attachments).Exec() } func (w *WikiTest) SelectPage(page *WikiPage, title string, revid UUID) error { return w.session.Query(fmt.Sprintf(`SELECT title, revid, body, views, protected, modified,tags, attachments, rating FROM %s WHERE title = ? AND revid = ? LIMIT 1`, w.table), title, revid).Scan(&page.Title, &page.RevId, &page.Body, &page.Views, &page.Protected, &page.Modified, &page.Tags, &page.Attachments, &page.Rating) } func (w *WikiTest) GetPageCount() int { var count int if err := w.session.Query(fmt.Sprintf(`SELECT COUNT(*) FROM %s`, w.table)).Scan(&count); err != nil { w.tb.Error("GetPageCount", err) } return count } func TestWikiCreateSchema(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() CreateSchema(session, t, "create") } func BenchmarkWikiCreateSchema(b *testing.B) { b.StopTimer() session := createSession(b) defer func() { b.StopTimer() session.Close() }() b.StartTimer() for i := 0; i < b.N; i++ { CreateSchema(session, b, "bench_create") } } func TestWikiCreatePages(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() w := CreateSchema(session, t, "create_pages") numPages := 5 w.CreatePages(numPages) if count := w.GetPageCount(); count != numPages { t.Errorf("expected %d pages, got %d pages.", numPages, count) } } func BenchmarkWikiCreatePages(b *testing.B) { b.StopTimer() session := createSession(b) defer func() { b.StopTimer() session.Close() }() w := CreateSchema(session, b, "bench_create_pages") b.StartTimer() w.CreatePages(b.N) } func BenchmarkWikiSelectAllPages(b *testing.B) { b.StopTimer() session := createSession(b) defer func() { b.StopTimer() session.Close() }() w := CreateSchema(session, b, "bench_select_all") w.CreatePages(100) b.StartTimer() var page WikiPage for i := 0; i < b.N; i++ { iter := session.Query(fmt.Sprintf(`SELECT title, revid, body, views, protected, modified, tags, attachments, rating FROM %s`, w.table)).Iter() for iter.Scan(&page.Title, &page.RevId, &page.Body, &page.Views, &page.Protected, &page.Modified, &page.Tags, &page.Attachments, &page.Rating) { // pass } if err := iter.Close(); err != nil { b.Error(err) } } } func BenchmarkWikiSelectSinglePage(b *testing.B) { b.StopTimer() session := createSession(b) defer func() { b.StopTimer() session.Close() }() w := CreateSchema(session, b, "bench_select_single") pages := make([]WikiPage, 100) w.CreatePages(len(pages)) iter := session.Query(fmt.Sprintf(`SELECT title, revid FROM %s`, w.table)).Iter() for i := 0; i < len(pages); i++ { if !iter.Scan(&pages[i].Title, &pages[i].RevId) { pages = pages[:i] break } } if err := iter.Close(); err != nil { b.Error(err) } b.StartTimer() var page WikiPage for i := 0; i < b.N; i++ { p := &pages[i%len(pages)] if err := w.SelectPage(&page, p.Title, p.RevId); err != nil { b.Error(err) } } } func BenchmarkWikiSelectPageCount(b *testing.B) { b.StopTimer() session := createSession(b) defer func() { b.StopTimer() session.Close() }() w := CreateSchema(session, b, "bench_page_count") const numPages = 10 w.CreatePages(numPages) b.StartTimer() for i := 0; i < b.N; i++ { if count := w.GetPageCount(); count != numPages { b.Errorf("expected %d pages, got %d pages.", numPages, count) } } } func TestWikiTypicalCRUD(t *testing.T) { t.Parallel() session := createSession(t) defer session.Close() w := CreateSchema(session, t, "crud") for _, page := range wikiTestData { if err := w.InsertPage(page); err != nil { t.Error("InsertPage:", err) } } if count := w.GetPageCount(); count != len(wikiTestData) { t.Errorf("count: expected %d, got %d\n", len(wikiTestData), count) } for _, original := range wikiTestData { page := new(WikiPage) if err := w.SelectPage(page, original.Title, original.RevId); err != nil { t.Error("SelectPage:", err) continue } sort.Sort(sort.StringSlice(page.Tags)) sort.Sort(sort.StringSlice(original.Tags)) if !reflect.DeepEqual(page, original) { t.Errorf("page: expected %#v, got %#v\n", original, page) } } }