mirror of
https://github.com/gofiber/storage.git
synced 2025-10-05 08:37:10 +08:00
Added Apache Cassandra Storage Driver
This commit is contained in:
1
.github/workflows/benchmark.yml
vendored
1
.github/workflows/benchmark.yml
vendored
@@ -130,6 +130,7 @@ jobs:
|
|||||||
MSSQL_PASSWORD: MsSql!1234
|
MSSQL_PASSWORD: MsSql!1234
|
||||||
TEST_AZURITE_IMAGE: mcr.microsoft.com/azure-storage/azurite:latest
|
TEST_AZURITE_IMAGE: mcr.microsoft.com/azure-storage/azurite:latest
|
||||||
TEST_CLICKHOUSE_IMAGE: "clickhouse/clickhouse-server:23-alpine"
|
TEST_CLICKHOUSE_IMAGE: "clickhouse/clickhouse-server:23-alpine"
|
||||||
|
TEST_CASSANDRA_IMAGE: "cassandra:4.1.3"
|
||||||
TEST_COUCHBASE_IMAGE: "couchbase:enterprise-7.6.5"
|
TEST_COUCHBASE_IMAGE: "couchbase:enterprise-7.6.5"
|
||||||
TEST_DYNAMODB_IMAGE: amazon/dynamodb-local:latest
|
TEST_DYNAMODB_IMAGE: amazon/dynamodb-local:latest
|
||||||
TEST_MINIO_IMAGE: "docker.io/minio/minio:RELEASE.2024-08-17T01-24-54Z"
|
TEST_MINIO_IMAGE: "docker.io/minio/minio:RELEASE.2024-08-17T01-24-54Z"
|
||||||
|
30
.github/workflows/test-cassandra.yml
vendored
Normal file
30
.github/workflows/test-cassandra.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- main
|
||||||
|
paths:
|
||||||
|
- 'cassandra/**'
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- 'cassandra/**'
|
||||||
|
name: 'Tests Cassandra'
|
||||||
|
jobs:
|
||||||
|
Tests:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
go-version:
|
||||||
|
- 1.23.x
|
||||||
|
- 1.24.x
|
||||||
|
steps:
|
||||||
|
- name: Fetch Repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Install Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version: '${{ matrix.go-version }}'
|
||||||
|
- name: Run Test
|
||||||
|
env:
|
||||||
|
TEST_CASSANDRA_IMAGE: cassandra:4.1.3
|
||||||
|
run: cd ./cassandra && go clean -testcache && go test ./... -v -race
|
@@ -54,6 +54,7 @@ type Storage interface {
|
|||||||
- [AzureBlob](./azureblob/README.md) <a href="https://github.com/gofiber/storage/actions?query=workflow%3A%22Tests+Azure+Blob%22"> <img src="https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-azureblob.yml?branch=main&label=%F0%9F%A7%AA%20&style=flat&color=75C46B" /> </a>
|
- [AzureBlob](./azureblob/README.md) <a href="https://github.com/gofiber/storage/actions?query=workflow%3A%22Tests+Azure+Blob%22"> <img src="https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-azureblob.yml?branch=main&label=%F0%9F%A7%AA%20&style=flat&color=75C46B" /> </a>
|
||||||
- [Badger](./badger/README.md) <a href="https://github.com/gofiber/storage/actions?query=workflow%3A%22Tests+Badger%22"> <img src="https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-badger.yml?branch=main&label=%F0%9F%A7%AA%20&style=flat&color=75C46B" /> </a>
|
- [Badger](./badger/README.md) <a href="https://github.com/gofiber/storage/actions?query=workflow%3A%22Tests+Badger%22"> <img src="https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-badger.yml?branch=main&label=%F0%9F%A7%AA%20&style=flat&color=75C46B" /> </a>
|
||||||
- [Bbolt](./bbolt) <a href="https://github.com/gofiber/storage/actions?query=workflow%3A%22Tests+Bbolt%22"> <img src="https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-bbolt.yml?branch=main&label=%F0%9F%A7%AA%20&style=flat&color=75C46B" /> </a>
|
- [Bbolt](./bbolt) <a href="https://github.com/gofiber/storage/actions?query=workflow%3A%22Tests+Bbolt%22"> <img src="https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-bbolt.yml?branch=main&label=%F0%9F%A7%AA%20&style=flat&color=75C46B" /> </a>
|
||||||
|
- [Cassandra](./cassandra/README.md) <a href="https://github.com/gofiber/storage/actions?query=workflow%3A%22Tests+Cassandra%22"> <img src="https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-cassandra.yml?branch=main&label=%F0%9F%A7%AA%20&style=flat&color=75C46B" /> </a>
|
||||||
- [CloudflareKV](./cloudflarekv/README.md) <a href="https://github.com/gofiber/storage/actions?query=workflow%3A%22Tests+CloudflareKV%22"> <img src="https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-cloudflarekv.yml?branch=main&label=%F0%9F%A7%AA%20&style=flat&color=75C46B" /> </a>
|
- [CloudflareKV](./cloudflarekv/README.md) <a href="https://github.com/gofiber/storage/actions?query=workflow%3A%22Tests+CloudflareKV%22"> <img src="https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-cloudflarekv.yml?branch=main&label=%F0%9F%A7%AA%20&style=flat&color=75C46B" /> </a>
|
||||||
- [Coherence](./coherence/README.md) <a href="https://github.com/gofiber/storage/actions?query=workflow%3A%22Tests+Coherence%22"> <img src="https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-coherence.yml?branch=main&label=%F0%9F%A7%AA%20&style=flat&color=75C46B" /> </a>
|
- [Coherence](./coherence/README.md) <a href="https://github.com/gofiber/storage/actions?query=workflow%3A%22Tests+Coherence%22"> <img src="https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-coherence.yml?branch=main&label=%F0%9F%A7%AA%20&style=flat&color=75C46B" /> </a>
|
||||||
- [Couchbase](./couchbase/README.md) <a href="https://github.com/gofiber/storage/actions?query=workflow%3A%22Tests+Couchbase%22"> <img src="https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-couchbase.yml?branch=main&label=%F0%9F%A7%AA%20&style=flat&color=75C46B" /> </a>
|
- [Couchbase](./couchbase/README.md) <a href="https://github.com/gofiber/storage/actions?query=workflow%3A%22Tests+Couchbase%22"> <img src="https://img.shields.io/github/actions/workflow/status/gofiber/storage/test-couchbase.yml?branch=main&label=%F0%9F%A7%AA%20&style=flat&color=75C46B" /> </a>
|
||||||
|
108
cassandra/README.md
Normal file
108
cassandra/README.md
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
# Cassandra
|
||||||
|
|
||||||
|
A Cassandra storage driver using [https://github.com/gocql/gocql](https://github.com/apache/cassandra-gocql-driver).
|
||||||
|
|
||||||
|

|
||||||
|
[](https://gofiber.io/discord)
|
||||||
|

|
||||||
|
|
||||||
|
### Table of Contents
|
||||||
|
|
||||||
|
- [Signatures](#signatures)
|
||||||
|
- [Installation](#installation)
|
||||||
|
- [Examples](#examples)
|
||||||
|
- [Config](#config)
|
||||||
|
- [Default Config](#default-config)
|
||||||
|
|
||||||
|
### Signatures
|
||||||
|
|
||||||
|
```go
|
||||||
|
func New(config ...Config) (*Storage, error)
|
||||||
|
func (s *Storage) Get(key string) ([]byte, error)
|
||||||
|
func (s *Storage) Set(key string, val []byte, exp time.Duration) error
|
||||||
|
func (s *Storage) Delete(key string) error
|
||||||
|
func (s *Storage) Reset() error
|
||||||
|
func (s *Storage) Close() error
|
||||||
|
func (s *Storage) Conn() *Session
|
||||||
|
```
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
Cassandra is supported on the latest two versions of Go:
|
||||||
|
|
||||||
|
Install the cassandra implementation:
|
||||||
|
```bash
|
||||||
|
go get github.com/gofiber/storage/cassandra
|
||||||
|
```
|
||||||
|
|
||||||
|
### Running the tests
|
||||||
|
|
||||||
|
This module uses [Testcontainers for Go](https://github.com/testcontainers/testcontainers-go/) to run integration tests, which will start a local instance of Cassandra as a Docker container under the hood. To run the tests, you must have Docker (or another container runtime 100% compatible with the Docker APIs) installed on your machine.
|
||||||
|
|
||||||
|
### Local development
|
||||||
|
|
||||||
|
Before running this implementation, you must ensure a Cassandra cluster is available.
|
||||||
|
For local development, we recommend using the Cassandra Docker image; it contains everything
|
||||||
|
necessary for the client to operate correctly.
|
||||||
|
|
||||||
|
To start Cassandra using Docker, issue the following:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run --name cassandra --network host -d cassandra:tag
|
||||||
|
```
|
||||||
|
|
||||||
|
After running this command you're ready to start using the storage and connecting to the database.
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
You can use the following options to create a cassandra storage driver:
|
||||||
|
```go
|
||||||
|
import "github.com/gofiber/storage/cassandra"
|
||||||
|
|
||||||
|
// Initialize default config, to connect to localhost:9042 using the memory engine and with a clean table.
|
||||||
|
store := New(Config{
|
||||||
|
Hosts: []string{"localhost:9042"},
|
||||||
|
Keyspace: "test_keyspace_creation",
|
||||||
|
Table: "test_kv",
|
||||||
|
Expiration : 10 * time.Minute,
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Config
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Config defines the configuration options for the Cassandra storage
|
||||||
|
type Config struct {
|
||||||
|
// Optional. Default is localhost
|
||||||
|
// Hosts is a list of Cassandra nodes to connect to.
|
||||||
|
Hosts []string
|
||||||
|
// Optional. Default is gofiber
|
||||||
|
// Keyspace is the name of the Cassandra keyspace to use.
|
||||||
|
Keyspace string
|
||||||
|
// Optional. Default is kv_store
|
||||||
|
/// Table is the name of the Cassandra table to use.
|
||||||
|
Table string
|
||||||
|
// Optional. Default is Quorum
|
||||||
|
// Consistency is the Cassandra consistency level.
|
||||||
|
Consistency gocql.Consistency
|
||||||
|
// Optional. Default is 10 minutes
|
||||||
|
// Expiration is the time after which an entry is considered expired.
|
||||||
|
Expiration time.Duration
|
||||||
|
// Optional. Default is false
|
||||||
|
// Reset is a flag to reset the database.
|
||||||
|
Reset bool
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Default Config
|
||||||
|
|
||||||
|
```go
|
||||||
|
var ConfigDefault = Config{
|
||||||
|
Hosts: []string{"localhost:9042"},
|
||||||
|
Keyspace: "gofiber",
|
||||||
|
Table: "kv_store",
|
||||||
|
Consistency: gocql.Quorum,
|
||||||
|
Reset: false,
|
||||||
|
Expiration: 10 * time.Minute,
|
||||||
|
}
|
||||||
|
```
|
416
cassandra/cassandra.go
Normal file
416
cassandra/cassandra.go
Normal file
@@ -0,0 +1,416 @@
|
|||||||
|
package cassandra
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gocql/gocql"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Storage represents a Cassandra storage implementation
|
||||||
|
type Storage struct {
|
||||||
|
cluster *gocql.ClusterConfig
|
||||||
|
session *gocql.Session
|
||||||
|
keyspace string
|
||||||
|
table string
|
||||||
|
ttl int
|
||||||
|
}
|
||||||
|
|
||||||
|
// SchemaInfo represents the schema metadata
|
||||||
|
type SchemaInfo struct {
|
||||||
|
Version int
|
||||||
|
Description string
|
||||||
|
CreatedAt time.Time
|
||||||
|
UpdatedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new Cassandra storage instance
|
||||||
|
func New(cfg Config) *Storage {
|
||||||
|
// Create cluster config
|
||||||
|
cluster := gocql.NewCluster(cfg.Hosts...)
|
||||||
|
|
||||||
|
// Don't set keyspace initially - we need to create it first
|
||||||
|
// We'll connect to system keyspace first
|
||||||
|
|
||||||
|
// Convert expiration to seconds for TTL
|
||||||
|
ttl := 0
|
||||||
|
if cfg.Expiration > 0 {
|
||||||
|
ttl = int(cfg.Expiration.Seconds())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create storage instance
|
||||||
|
storage := &Storage{
|
||||||
|
cluster: cluster,
|
||||||
|
keyspace: cfg.Keyspace,
|
||||||
|
table: cfg.Table,
|
||||||
|
ttl: ttl,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize keyspace
|
||||||
|
if err := storage.createOrVerifyKeySpace(cfg.Reset); err != nil {
|
||||||
|
log.Printf("Failed to initialize keyspace: %v", err)
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return storage
|
||||||
|
}
|
||||||
|
|
||||||
|
// createOrVerifyKeySpace ensures the keyspace and table exist with proper keyspace
|
||||||
|
func (s *Storage) createOrVerifyKeySpace(reset bool) error {
|
||||||
|
// Connect to system keyspace first to create our keyspace if needed
|
||||||
|
systemCluster := gocql.NewCluster(s.cluster.Hosts...)
|
||||||
|
systemCluster.Consistency = s.cluster.Consistency
|
||||||
|
systemCluster.Timeout = s.cluster.Timeout
|
||||||
|
|
||||||
|
// Connect to the system keyspace
|
||||||
|
systemSession, err := systemCluster.CreateSession()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to connect to system keyspace: %w", err)
|
||||||
|
}
|
||||||
|
defer systemSession.Close()
|
||||||
|
|
||||||
|
// Create keyspace if not exists
|
||||||
|
err = s.ensureKeyspace(systemSession)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to ensure keyspace exists: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now connect to our keyspace
|
||||||
|
s.cluster.Keyspace = s.keyspace
|
||||||
|
session, err := s.cluster.CreateSession()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to connect to keyspace %s: %w", s.keyspace, err)
|
||||||
|
}
|
||||||
|
s.session = session
|
||||||
|
|
||||||
|
// Drop tables if reset is requested
|
||||||
|
if reset {
|
||||||
|
if err := s.dropTables(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create data table if necessary
|
||||||
|
if err := s.createDataTable(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensureKeyspace creates the keyspace if it doesn't exist
|
||||||
|
func (s *Storage) ensureKeyspace(systemSession *gocql.Session) error {
|
||||||
|
// Check if keyspace exists
|
||||||
|
var count int
|
||||||
|
if err := systemSession.Query(
|
||||||
|
"SELECT COUNT(*) FROM system_schema.keyspaces WHERE keyspace_name = ?",
|
||||||
|
s.keyspace,
|
||||||
|
).Scan(&count); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create keyspace if it doesn't exist
|
||||||
|
if count == 0 {
|
||||||
|
query := fmt.Sprintf(
|
||||||
|
"CREATE KEYSPACE %s WITH REPLICATION = {'class': 'SimpleStrategy', 'replication_factor': 1}",
|
||||||
|
s.keyspace,
|
||||||
|
)
|
||||||
|
if err := systemSession.Query(query).Exec(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Printf("Created keyspace: %s", s.keyspace)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// createDataTable creates the data table for key-value storage
|
||||||
|
func (s *Storage) createDataTable() error {
|
||||||
|
query := fmt.Sprintf(`
|
||||||
|
CREATE TABLE IF NOT EXISTS %s.%s (
|
||||||
|
key text PRIMARY KEY,
|
||||||
|
value blob,
|
||||||
|
created_at timestamp,
|
||||||
|
expires_at timestamp
|
||||||
|
)
|
||||||
|
`, s.keyspace, s.table)
|
||||||
|
|
||||||
|
return s.session.Query(query).Exec()
|
||||||
|
}
|
||||||
|
|
||||||
|
// dropTables drops existing tables for reset
|
||||||
|
func (s *Storage) dropTables() error {
|
||||||
|
// Drop data table
|
||||||
|
query := fmt.Sprintf("DROP TABLE IF EXISTS %s.%s", s.keyspace, s.table)
|
||||||
|
if err := s.session.Query(query).Exec(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop schema_info table
|
||||||
|
query = fmt.Sprintf("DROP TABLE IF EXISTS %s.schema_info", s.keyspace)
|
||||||
|
return s.session.Query(query).Exec()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set stores a key-value pair with optional expiration
|
||||||
|
func (s *Storage) Set(key string, value []byte, exp time.Duration) error {
|
||||||
|
// Calculate expiration time
|
||||||
|
var expiresAt *time.Time
|
||||||
|
var ttl int = -1 // Default to no TTL
|
||||||
|
|
||||||
|
if exp > 0 {
|
||||||
|
// Specific expiration provided
|
||||||
|
ttl = int(exp.Seconds())
|
||||||
|
t := time.Now().Add(exp)
|
||||||
|
expiresAt = &t
|
||||||
|
} else if exp == 0 && s.ttl > 0 {
|
||||||
|
// Use default TTL from config
|
||||||
|
ttl = s.ttl
|
||||||
|
t := time.Now().Add(time.Duration(s.ttl) * time.Second)
|
||||||
|
expiresAt = &t
|
||||||
|
}
|
||||||
|
// If exp < 0, we'll use no TTL (indefinite storage)
|
||||||
|
|
||||||
|
// Insert with TTL if specified
|
||||||
|
var query string
|
||||||
|
if ttl > 0 {
|
||||||
|
query = fmt.Sprintf("INSERT INTO %s.%s (key, value, created_at, expires_at) VALUES (?, ?, ?, ?) USING TTL %d",
|
||||||
|
s.keyspace, s.table, ttl)
|
||||||
|
} else {
|
||||||
|
query = fmt.Sprintf("INSERT INTO %s.%s (key, value, created_at, expires_at) VALUES (?, ?, ?, ?)",
|
||||||
|
s.keyspace, s.table)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.session.Query(query, key, value, time.Now(), expiresAt).Exec()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get retrieves a value by key
|
||||||
|
func (s *Storage) Get(key string) ([]byte, error) {
|
||||||
|
var value []byte
|
||||||
|
var expiresAt time.Time
|
||||||
|
|
||||||
|
query := fmt.Sprintf("SELECT value, expires_at FROM %s.%s WHERE key = ?", s.keyspace, s.table)
|
||||||
|
if err := s.session.Query(query, key).Scan(&value, &expiresAt); err != nil {
|
||||||
|
if errors.Is(err, gocql.ErrNotFound) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if expired (as a backup in case TTL didn't work)
|
||||||
|
if !expiresAt.IsZero() && expiresAt.Before(time.Now()) {
|
||||||
|
// Expired but not yet removed by TTL
|
||||||
|
err := s.Delete(key)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to delete expired key %s: %v", key, err)
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete removes a key from storage
|
||||||
|
func (s *Storage) Delete(key string) error {
|
||||||
|
query := fmt.Sprintf("DELETE FROM %s.%s WHERE key = ?", s.keyspace, s.table)
|
||||||
|
return s.session.Query(query, key).Exec()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset clears all keys from storage
|
||||||
|
func (s *Storage) Reset() error {
|
||||||
|
query := fmt.Sprintf("TRUNCATE TABLE %s.%s", s.keyspace, s.table)
|
||||||
|
return s.session.Query(query).Exec()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the storage connection
|
||||||
|
func (s *Storage) Close() {
|
||||||
|
if s.session != nil {
|
||||||
|
s.session.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test functions
|
||||||
|
|
||||||
|
// // setupCassandraContainer creates a Cassandra container using the official module
|
||||||
|
// func setupCassandraContainer(ctx context.Context) (*cassandracontainer.CassandraContainer, string, error) {
|
||||||
|
// cassandraContainer, err := cassandracontainer.RunContainer(ctx,
|
||||||
|
// testcontainers.WithImage("cassandra:4.1"),
|
||||||
|
// cassandracontainer.WithInitialWaitTimeout(2*time.Minute),
|
||||||
|
// )
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, "", err
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Get connection parameters
|
||||||
|
// host, err := cassandraContainer.Host(ctx)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, "", err
|
||||||
|
// }
|
||||||
|
|
||||||
|
// mappedPort, err := cassandraContainer.MappedPort(ctx, "9042/tcp")
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, "", err
|
||||||
|
// }
|
||||||
|
|
||||||
|
// connectionURL := host + ":" + mappedPort.Port()
|
||||||
|
// return cassandraContainer, connectionURL, nil
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func TestSchemaManagement(t *testing.T) {
|
||||||
|
// ctx := context.Background()
|
||||||
|
|
||||||
|
// // Start Cassandra container
|
||||||
|
// cassandraContainer, connectionURL, err := setupCassandraContainer(ctx)
|
||||||
|
// if err != nil {
|
||||||
|
// t.Fatalf("Failed to start Cassandra container: %v", err)
|
||||||
|
// }
|
||||||
|
// defer func() {
|
||||||
|
// if err := cassandraContainer.Terminate(ctx); err != nil {
|
||||||
|
// t.Logf("Failed to terminate container: %v", err)
|
||||||
|
// }
|
||||||
|
// }()
|
||||||
|
|
||||||
|
// // 1. Test keyspace creation
|
||||||
|
// store := New(Config{
|
||||||
|
// Hosts: []string{connectionURL},
|
||||||
|
// Keyspace: "test_keyspace_creation",
|
||||||
|
// Table: "test_table",
|
||||||
|
// SchemaVersion: 1,
|
||||||
|
// SchemaDescription: "Initial Schema",
|
||||||
|
// })
|
||||||
|
|
||||||
|
// // Verify keyspace was created
|
||||||
|
// systemCluster := gocql.NewCluster(connectionURL)
|
||||||
|
// systemSession, err := systemCluster.CreateSession()
|
||||||
|
// if err != nil {
|
||||||
|
// t.Fatalf("Failed to connect to system keyspace: %v", err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// var count int
|
||||||
|
// err = systemSession.Query(
|
||||||
|
// "SELECT COUNT(*) FROM system_schema.keyspaces WHERE keyspace_name = ?",
|
||||||
|
// "test_keyspace_creation",
|
||||||
|
// ).Scan(&count)
|
||||||
|
// assert.NoError(t, err)
|
||||||
|
// assert.Equal(t, 1, count, "Keyspace should have been created")
|
||||||
|
// systemSession.Close()
|
||||||
|
|
||||||
|
// // 2. Test table creation
|
||||||
|
// // Connect to the keyspace and check if tables exist
|
||||||
|
// cluster := gocql.NewCluster(connectionURL)
|
||||||
|
// cluster.Keyspace = "test_keyspace_creation"
|
||||||
|
// session, err := cluster.CreateSession()
|
||||||
|
// if err != nil {
|
||||||
|
// t.Fatalf("Failed to connect to keyspace: %v", err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Check for data table
|
||||||
|
// err = session.Query(
|
||||||
|
// "SELECT COUNT(*) FROM system_schema.tables WHERE keyspace_name = ? AND table_name = ?",
|
||||||
|
// "test_keyspace_creation", "test_table",
|
||||||
|
// ).Scan(&count)
|
||||||
|
// assert.NoError(t, err)
|
||||||
|
// assert.Equal(t, 1, count, "Data table should have been created")
|
||||||
|
|
||||||
|
// // Check for schema_info table
|
||||||
|
// err = session.Query(
|
||||||
|
// "SELECT COUNT(*) FROM system_schema.tables WHERE keyspace_name = ? AND table_name = ?",
|
||||||
|
// "test_keyspace_creation", "schema_info",
|
||||||
|
// ).Scan(&count)
|
||||||
|
// assert.NoError(t, err)
|
||||||
|
// assert.Equal(t, 1, count, "Schema info table should have been created")
|
||||||
|
|
||||||
|
// session.Close()
|
||||||
|
// store.Close()
|
||||||
|
|
||||||
|
// // 3. Test schema update
|
||||||
|
// // Create initial schema
|
||||||
|
// storeV1 := New(Config{
|
||||||
|
// Hosts: []string{connectionURL},
|
||||||
|
// Keyspace: "test_schema_update",
|
||||||
|
// Table: "test_table",
|
||||||
|
// SchemaVersion: 1,
|
||||||
|
// SchemaDescription: "Schema v1",
|
||||||
|
// })
|
||||||
|
|
||||||
|
// // Verify initial schema
|
||||||
|
// schemaInfo, err := storeV1.GetSchemaInfo()
|
||||||
|
// assert.NoError(t, err)
|
||||||
|
// assert.Equal(t, 1, schemaInfo.Version)
|
||||||
|
// assert.Equal(t, "Schema v1", schemaInfo.Description)
|
||||||
|
// createdAt := schemaInfo.CreatedAt
|
||||||
|
|
||||||
|
// // Close and create with higher version
|
||||||
|
// storeV1.Close()
|
||||||
|
|
||||||
|
// // Create updated schema
|
||||||
|
// storeV2 := New(Config{
|
||||||
|
// Hosts: []string{connectionURL},
|
||||||
|
// Keyspace: "test_schema_update",
|
||||||
|
// Table: "test_table",
|
||||||
|
// SchemaVersion: 2,
|
||||||
|
// SchemaDescription: "Schema v2",
|
||||||
|
// })
|
||||||
|
|
||||||
|
// // Verify schema was updated
|
||||||
|
// updatedSchema, err := storeV2.GetSchemaInfo()
|
||||||
|
// assert.NoError(t, err)
|
||||||
|
// assert.Equal(t, 2, updatedSchema.Version)
|
||||||
|
// assert.Equal(t, "Schema v2", updatedSchema.Description)
|
||||||
|
// assert.Equal(t, createdAt.Format(time.RFC3339), updatedSchema.CreatedAt.Format(time.RFC3339))
|
||||||
|
// assert.True(t, updatedSchema.UpdatedAt.After(createdAt))
|
||||||
|
|
||||||
|
// storeV2.Close()
|
||||||
|
|
||||||
|
// // 4. Test forced schema update with same version
|
||||||
|
// storeForce := New(Config{
|
||||||
|
// Hosts: []string{connectionURL},
|
||||||
|
// Keyspace: "test_schema_update",
|
||||||
|
// Table: "test_table",
|
||||||
|
// SchemaVersion: 2, // Same version
|
||||||
|
// SchemaDescription: "Schema v2 forced",
|
||||||
|
// ForceSchemaUpdate: true,
|
||||||
|
// })
|
||||||
|
|
||||||
|
// // Verify schema was updated despite same version
|
||||||
|
// forcedSchema, err := storeForce.GetSchemaInfo()
|
||||||
|
// assert.NoError(t, err)
|
||||||
|
// assert.Equal(t, 2, forcedSchema.Version)
|
||||||
|
// assert.Equal(t, "Schema v2 forced", forcedSchema.Description)
|
||||||
|
// assert.True(t, forcedSchema.UpdatedAt.After(updatedSchema.UpdatedAt))
|
||||||
|
|
||||||
|
// storeForce.Close()
|
||||||
|
|
||||||
|
// // 5. Test reset functionality
|
||||||
|
// resetStore := New(Config{
|
||||||
|
// Hosts: []string{connectionURL},
|
||||||
|
// Keyspace: "test_schema_reset",
|
||||||
|
// Table: "test_table",
|
||||||
|
// SchemaVersion: 1,
|
||||||
|
// SchemaDescription: "Initial Schema",
|
||||||
|
// })
|
||||||
|
|
||||||
|
// // Add some data
|
||||||
|
// err = resetStore.Set("key1", []byte("value1"), 0)
|
||||||
|
// assert.NoError(t, err)
|
||||||
|
|
||||||
|
// // Close and reopen with reset
|
||||||
|
// resetStore.Close()
|
||||||
|
|
||||||
|
// resetStore = New(Config{
|
||||||
|
// Hosts: []string{connectionURL},
|
||||||
|
// Keyspace: "test_schema_reset",
|
||||||
|
// Table: "test_table",
|
||||||
|
// SchemaVersion: 1,
|
||||||
|
// SchemaDescription: "Reset Schema",
|
||||||
|
// Reset: true,
|
||||||
|
// })
|
||||||
|
|
||||||
|
// // Check that data is gone
|
||||||
|
// val, err := resetStore.Get("key1")
|
||||||
|
// assert.NoError(t, err)
|
||||||
|
// assert.Nil(t, val, "Key should be gone after reset")
|
||||||
|
|
||||||
|
// resetStore.Close()
|
||||||
|
// }
|
310
cassandra/cassandra_test.go
Normal file
310
cassandra/cassandra_test.go
Normal file
@@ -0,0 +1,310 @@
|
|||||||
|
package cassandra
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gocql/gocql"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
cassandracontainer "github.com/testcontainers/testcontainers-go/modules/cassandra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// setupCassandraContainer creates a Cassandra container using the official module
|
||||||
|
func setupCassandraContainer(ctx context.Context) (*cassandracontainer.CassandraContainer, string, error) {
|
||||||
|
cassandraContainer, err := cassandracontainer.Run(ctx, "cassandra:4.1.3")
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get connection parameters
|
||||||
|
host, err := cassandraContainer.Host(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
mappedPort, err := cassandraContainer.MappedPort(ctx, "9042/tcp")
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
connectionURL := host + ":" + mappedPort.Port()
|
||||||
|
return cassandraContainer, connectionURL, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCassandraStorage(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// Start Cassandra container
|
||||||
|
cassandraContainer, connectionURL, err := setupCassandraContainer(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to start Cassandra container: %v", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := cassandraContainer.Terminate(ctx); err != nil {
|
||||||
|
t.Logf("Failed to terminate container: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Test cases
|
||||||
|
t.Run("KeyspaceCreation", func(t *testing.T) {
|
||||||
|
t.Skip("Skipping keyspace creation test")
|
||||||
|
testKeyspaceCreation(t, connectionURL)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("BasicOperations", func(t *testing.T) {
|
||||||
|
testBasicOperations(t, connectionURL)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ExpirableKeys", func(t *testing.T) {
|
||||||
|
testExpirableKeys(t, connectionURL)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Reset", func(t *testing.T) {
|
||||||
|
testReset(t, connectionURL)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("ConcurrentAccess", func(t *testing.T) {
|
||||||
|
testConcurrentAccess(t, connectionURL)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testKeyspaceCreation(t *testing.T, connectionURL string) {
|
||||||
|
// Create new storage
|
||||||
|
store := New(Config{
|
||||||
|
Hosts: []string{connectionURL},
|
||||||
|
Keyspace: "test_keyspace_creation",
|
||||||
|
Table: "test_kv",
|
||||||
|
})
|
||||||
|
defer store.Close()
|
||||||
|
|
||||||
|
// Verify keyspace was created
|
||||||
|
systemCluster := gocql.NewCluster(connectionURL)
|
||||||
|
systemSession, err := systemCluster.CreateSession()
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer systemSession.Close()
|
||||||
|
|
||||||
|
var count int
|
||||||
|
err = systemSession.Query(
|
||||||
|
"SELECT COUNT(*) FROM system_schema.keyspaces WHERE keyspace_name = ?",
|
||||||
|
"test_keyspace_creation",
|
||||||
|
).Scan(&count)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 1, count, "Keyspace should have been created")
|
||||||
|
|
||||||
|
// Verify table was created
|
||||||
|
cluster := gocql.NewCluster(connectionURL)
|
||||||
|
cluster.Keyspace = "test_keyspace_creation"
|
||||||
|
session, err := cluster.CreateSession()
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer session.Close()
|
||||||
|
|
||||||
|
err = session.Query(
|
||||||
|
"SELECT COUNT(*) FROM system_schema.tables WHERE keyspace_name = ? AND table_name = ?",
|
||||||
|
"test_keyspace_creation", "test_kv",
|
||||||
|
).Scan(&count)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 1, count, "Table should have been created")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testBasicOperations(t *testing.T, connectionURL string) {
|
||||||
|
// Create new storage
|
||||||
|
store := New(Config{
|
||||||
|
Hosts: []string{connectionURL},
|
||||||
|
Keyspace: "test_basic_ops",
|
||||||
|
Table: "test_kv",
|
||||||
|
})
|
||||||
|
defer store.Close()
|
||||||
|
|
||||||
|
// Set a key
|
||||||
|
err := store.Set("test_key", []byte("test_value"), 0)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Get the key
|
||||||
|
value, err := store.Get("test_key")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, []byte("test_value"), value)
|
||||||
|
|
||||||
|
// Get a non-existent key
|
||||||
|
value, err = store.Get("nonexistent_key")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Nil(t, value)
|
||||||
|
|
||||||
|
// Delete the key
|
||||||
|
err = store.Delete("test_key")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Get the deleted key
|
||||||
|
value, err = store.Get("test_key")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Nil(t, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// testExpirableKeys tests the expirable keys functionality.
|
||||||
|
func testExpirableKeys(t *testing.T, connectionURL string) {
|
||||||
|
// Create new storage with default expiration
|
||||||
|
store := New(Config{
|
||||||
|
Hosts: []string{connectionURL},
|
||||||
|
Keyspace: "test_expirable",
|
||||||
|
Table: "test_kv",
|
||||||
|
Expiration: 5 * time.Second, // Short default TTL for testing
|
||||||
|
})
|
||||||
|
defer store.Close()
|
||||||
|
|
||||||
|
// Set keys with different expiration settings
|
||||||
|
// Key with default TTL (exp = 0 means use default)
|
||||||
|
err := store.Set("key_default_ttl", []byte("value1"), 0)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Key with specific TTL
|
||||||
|
err = store.Set("key_specific_ttl", []byte("value2"), 1*time.Second)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Key with no TTL (overrides default)
|
||||||
|
err = store.Set("key_no_ttl", []byte("value3"), -1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Verify all keys exist initially
|
||||||
|
value, err := store.Get("key_default_ttl")
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, []byte("value1"), value)
|
||||||
|
|
||||||
|
value, err = store.Get("key_specific_ttl")
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, []byte("value2"), value)
|
||||||
|
|
||||||
|
value, err = store.Get("key_no_ttl")
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, []byte("value3"), value)
|
||||||
|
|
||||||
|
// Wait for specific TTL to expire
|
||||||
|
time.Sleep(1500 * time.Millisecond)
|
||||||
|
|
||||||
|
// Specific TTL key should be gone, others should remain
|
||||||
|
value, err = store.Get("key_specific_ttl")
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Nil(t, value, "Key with 1s TTL should have expired")
|
||||||
|
|
||||||
|
value, err = store.Get("key_default_ttl")
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, []byte("value1"), value, "Key with default TTL should still exist")
|
||||||
|
|
||||||
|
value, err = store.Get("key_no_ttl")
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, []byte("value3"), value, "Key with no TTL should still exist")
|
||||||
|
|
||||||
|
// Wait for default TTL to expire
|
||||||
|
time.Sleep(4 * time.Second)
|
||||||
|
|
||||||
|
// Default TTL key should be gone, no TTL key should remain
|
||||||
|
value, err = store.Get("key_default_ttl")
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Nil(t, value, "Key with default TTL should have expired")
|
||||||
|
|
||||||
|
value, err = store.Get("key_no_ttl")
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, []byte("value3"), value, "Key with no TTL should still exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testReset(t *testing.T, connectionURL string) {
|
||||||
|
// Create new storage
|
||||||
|
store := New(Config{
|
||||||
|
Hosts: []string{connectionURL},
|
||||||
|
Keyspace: "test_reset",
|
||||||
|
Table: "test_kv",
|
||||||
|
})
|
||||||
|
|
||||||
|
// Set some keys
|
||||||
|
err := store.Set("key1", []byte("value1"), 0)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = store.Set("key2", []byte("value2"), 0)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Verify keys exist
|
||||||
|
value, err := store.Get("key1")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, []byte("value1"), value)
|
||||||
|
|
||||||
|
// Reset storage
|
||||||
|
err = store.Reset()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Verify keys are gone
|
||||||
|
value, err = store.Get("key1")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Nil(t, value, "Key should be deleted after reset")
|
||||||
|
|
||||||
|
value, err = store.Get("key2")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Nil(t, value, "Key should be deleted after reset")
|
||||||
|
|
||||||
|
// Close the first storage
|
||||||
|
store.Close()
|
||||||
|
|
||||||
|
// Create new storage with Reset flag
|
||||||
|
store = New(Config{
|
||||||
|
Hosts: []string{connectionURL},
|
||||||
|
Keyspace: "test_reset",
|
||||||
|
Table: "test_kv",
|
||||||
|
Reset: true,
|
||||||
|
})
|
||||||
|
defer store.Close()
|
||||||
|
|
||||||
|
// Set a key
|
||||||
|
err = store.Set("key3", []byte("value3"), 0)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Verify key exists
|
||||||
|
value, err = store.Get("key3")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, []byte("value3"), value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testConcurrentAccess(t *testing.T, connectionURL string) {
|
||||||
|
// Create new storage
|
||||||
|
store := New(Config{
|
||||||
|
Hosts: []string{connectionURL},
|
||||||
|
Keyspace: "test_concurrent",
|
||||||
|
Table: "test_kv",
|
||||||
|
})
|
||||||
|
defer store.Close()
|
||||||
|
|
||||||
|
// Number of goroutines
|
||||||
|
const concurrentOps = 10
|
||||||
|
done := make(chan bool, concurrentOps)
|
||||||
|
|
||||||
|
// Run concurrent operations
|
||||||
|
for i := 0; i < concurrentOps; i++ {
|
||||||
|
go func(id int) {
|
||||||
|
// Set key
|
||||||
|
key := fmt.Sprintf("key%d", id)
|
||||||
|
value := []byte(fmt.Sprintf("value%d", id))
|
||||||
|
err := store.Set(key, value, 0)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Get key
|
||||||
|
retrievedValue, err := store.Get(key)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, value, retrievedValue)
|
||||||
|
|
||||||
|
// Delete key
|
||||||
|
err = store.Delete(key)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Verify deletion
|
||||||
|
retrievedValue, err = store.Get(key)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Nil(t, retrievedValue)
|
||||||
|
|
||||||
|
done <- true
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for all goroutines to complete
|
||||||
|
for i := 0; i < concurrentOps; i++ {
|
||||||
|
<-done
|
||||||
|
}
|
||||||
|
}
|
73
cassandra/config.go
Normal file
73
cassandra/config.go
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
package cassandra
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gocql/gocql"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config defines the configuration options for the Cassandra storage
|
||||||
|
type Config struct {
|
||||||
|
// Optional. Default is localhost
|
||||||
|
// Hosts is a list of Cassandra nodes to connect to.
|
||||||
|
Hosts []string
|
||||||
|
// Optional. Default is gofiber
|
||||||
|
// Keyspace is the name of the Cassandra keyspace to use.
|
||||||
|
Keyspace string
|
||||||
|
// Optional. Default is kv_store
|
||||||
|
/// Table is the name of the Cassandra table to use.
|
||||||
|
Table string
|
||||||
|
// Optional. Default is Quorum
|
||||||
|
// Consistency is the Cassandra consistency level.
|
||||||
|
Consistency gocql.Consistency
|
||||||
|
// Optional. Default is 10 minutes
|
||||||
|
// Expiration is the time after which an entry is considered expired.
|
||||||
|
Expiration time.Duration
|
||||||
|
// Optional. Default is false
|
||||||
|
// Reset is a flag to reset the database.
|
||||||
|
Reset bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigDefault is the default config
|
||||||
|
var ConfigDefault = Config{
|
||||||
|
Hosts: []string{"localhost:9042"},
|
||||||
|
Keyspace: "gofiber",
|
||||||
|
Table: "kv_store",
|
||||||
|
Consistency: gocql.Quorum,
|
||||||
|
Reset: false,
|
||||||
|
Expiration: 10 * time.Minute,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigDefault is the default config
|
||||||
|
func configDefault(config ...Config) Config {
|
||||||
|
// Return default config if nothing provided
|
||||||
|
if len(config) < 1 {
|
||||||
|
return ConfigDefault
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override default config
|
||||||
|
cfg := config[0]
|
||||||
|
|
||||||
|
// Set default values
|
||||||
|
if len(cfg.Hosts) == 0 {
|
||||||
|
cfg.Hosts = ConfigDefault.Hosts
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.Keyspace == "" {
|
||||||
|
cfg.Keyspace = ConfigDefault.Keyspace
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.Table == "" {
|
||||||
|
cfg.Table = ConfigDefault.Table
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.Consistency == 0 {
|
||||||
|
cfg.Consistency = ConfigDefault.Consistency
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.Expiration == 0 {
|
||||||
|
cfg.Expiration = ConfigDefault.Expiration
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfg
|
||||||
|
}
|
67
cassandra/go.mod
Normal file
67
cassandra/go.mod
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
module github.com/gofiber/storage/cassandra/v2
|
||||||
|
|
||||||
|
go 1.23.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/gocql/gocql v1.7.0
|
||||||
|
github.com/stretchr/testify v1.10.0
|
||||||
|
github.com/testcontainers/testcontainers-go/modules/cassandra v0.36.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
dario.cat/mergo v1.0.1 // indirect
|
||||||
|
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||||
|
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||||
|
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||||
|
github.com/containerd/log v0.1.0 // indirect
|
||||||
|
github.com/containerd/platforms v0.2.1 // indirect
|
||||||
|
github.com/cpuguy83/dockercfg v0.3.2 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/distribution/reference v0.6.0 // indirect
|
||||||
|
github.com/docker/docker v28.0.1+incompatible // indirect
|
||||||
|
github.com/docker/go-connections v0.5.0 // indirect
|
||||||
|
github.com/docker/go-units v0.5.0 // indirect
|
||||||
|
github.com/ebitengine/purego v0.8.2 // indirect
|
||||||
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
|
github.com/go-logr/logr v1.4.2 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
|
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||||
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
|
github.com/golang/snappy v0.0.3 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
|
||||||
|
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect
|
||||||
|
github.com/klauspost/compress v1.17.4 // indirect
|
||||||
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||||
|
github.com/magiconair/properties v1.8.9 // indirect
|
||||||
|
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||||
|
github.com/moby/patternmatcher v0.6.0 // indirect
|
||||||
|
github.com/moby/sys/sequential v0.5.0 // indirect
|
||||||
|
github.com/moby/sys/user v0.1.0 // indirect
|
||||||
|
github.com/moby/sys/userns v0.1.0 // indirect
|
||||||
|
github.com/moby/term v0.5.0 // indirect
|
||||||
|
github.com/morikuni/aec v1.0.0 // indirect
|
||||||
|
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||||
|
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||||
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||||
|
github.com/shirou/gopsutil/v4 v4.25.1 // indirect
|
||||||
|
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||||
|
github.com/testcontainers/testcontainers-go v0.36.0 // indirect
|
||||||
|
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||||
|
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||||
|
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
|
||||||
|
go.opentelemetry.io/otel v1.35.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/metric v1.35.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/sdk v1.32.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/trace v1.35.0 // indirect
|
||||||
|
golang.org/x/crypto v0.35.0 // indirect
|
||||||
|
golang.org/x/sys v0.31.0 // indirect
|
||||||
|
google.golang.org/grpc v1.70.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.36.5 // indirect
|
||||||
|
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
)
|
202
cassandra/go.sum
Normal file
202
cassandra/go.sum
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
|
||||||
|
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||||
|
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
|
||||||
|
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||||
|
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||||
|
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||||
|
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||||
|
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||||
|
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY=
|
||||||
|
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
|
||||||
|
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/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
||||||
|
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||||
|
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||||
|
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||||
|
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
|
||||||
|
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
|
||||||
|
github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA=
|
||||||
|
github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
|
||||||
|
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
||||||
|
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
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/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||||
|
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||||
|
github.com/docker/docker v28.0.1+incompatible h1:FCHjSRdXhNRFjlHMTv4jUNlIBbTeRjrWfeFuJp7jpo0=
|
||||||
|
github.com/docker/docker v28.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||||
|
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||||
|
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||||
|
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||||
|
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
|
github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
|
||||||
|
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||||
|
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||||
|
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
|
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||||
|
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
|
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||||
|
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||||
|
github.com/gocql/gocql v1.7.0 h1:O+7U7/1gSN7QTEAaMEsJc1Oq2QHXvCWoF3DFK9HDHus=
|
||||||
|
github.com/gocql/gocql v1.7.0/go.mod h1:vnlvXyFZeLBF0Wy+RS8hrOdbn0UWsWtdg07XJnFxZ+4=
|
||||||
|
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||||
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
|
github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
|
||||||
|
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
|
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
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/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
|
||||||
|
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/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
|
||||||
|
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||||
|
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||||
|
github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
|
||||||
|
github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||||
|
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||||
|
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||||
|
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
|
||||||
|
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
|
||||||
|
github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=
|
||||||
|
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
|
||||||
|
github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg=
|
||||||
|
github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU=
|
||||||
|
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
|
||||||
|
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
|
||||||
|
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
||||||
|
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||||
|
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||||
|
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
|
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||||
|
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||||
|
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
|
||||||
|
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
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/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||||
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||||
|
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||||
|
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||||
|
github.com/shirou/gopsutil/v4 v4.25.1 h1:QSWkTc+fu9LTAWfkZwZ6j8MSUk4A2LV7rbH0ZqmLjXs=
|
||||||
|
github.com/shirou/gopsutil/v4 v4.25.1/go.mod h1:RoUCUpndaJFtT+2zsZzzmhvbfGoDCJ7nFXKJf8GqJbI=
|
||||||
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||||
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
github.com/testcontainers/testcontainers-go v0.36.0 h1:YpffyLuHtdp5EUsI5mT4sRw8GZhO/5ozyDT1xWGXt00=
|
||||||
|
github.com/testcontainers/testcontainers-go v0.36.0/go.mod h1:yk73GVJ0KUZIHUtFna6MO7QS144qYpoY8lEEtU9Hed0=
|
||||||
|
github.com/testcontainers/testcontainers-go/modules/cassandra v0.36.0 h1:vIOOfizBKSHfVcs+5u/VS6Zn4Bbo1lYM28DhHYJm4i8=
|
||||||
|
github.com/testcontainers/testcontainers-go/modules/cassandra v0.36.0/go.mod h1:ZsSC3MYjRLXLccXMnBth8Qh4AkS2HWzGobVhMMY3Z/k=
|
||||||
|
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
|
||||||
|
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
|
||||||
|
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
|
||||||
|
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
|
||||||
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||||
|
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||||
|
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||||
|
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
|
||||||
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
|
||||||
|
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
||||||
|
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU=
|
||||||
|
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
||||||
|
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU=
|
||||||
|
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
||||||
|
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
||||||
|
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
|
||||||
|
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
|
||||||
|
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
|
||||||
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA=
|
||||||
|
golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||||
|
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
|
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
|
||||||
|
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||||
|
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||||
|
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=
|
||||||
|
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950=
|
||||||
|
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb h1:TLPQVbx1GJ8VKZxz52VAxl1EBgKXXbTiU9Fc5fZeLn4=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
|
||||||
|
google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
|
||||||
|
google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
|
||||||
|
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
||||||
|
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
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.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
||||||
|
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
Reference in New Issue
Block a user