Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 194 additions & 2 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,205 @@ jobs:

build:
runs-on: ubuntu-latest

services:
# Database service containers
mariadb-vector:
image: mariadb:11.7.2
env:
MARIADB_DATABASE: perfkit_db_ci
MARIADB_USER: user
MARIADB_PASSWORD: password # example value of a secret
MARIADB_ROOT_PASSWORD: password # example value of a secret
ports:
- 3306:3306
# Additional options to handle GitHub Actions environment limitations
options: >-
--health-cmd="healthcheck.sh --connect --innodb_initialized"
--health-interval=10s
--health-timeout=5s
--health-retries=3

postgres:
image: ankane/pgvector:v0.5.1
env:
POSTGRES_USER: root
POSTGRES_PASSWORD: password # example value of a secret
POSTGRES_DB: perfkit_pg_vector_db_ci
ports:
- 5432:5432
# Health check to wait until postgres is ready
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5

mssql:
image: mcr.microsoft.com/mssql/server:2019-latest
env:
ACCEPT_EULA: 'Y'
MSSQL_SA_PASSWORD: MyP@ssw0rd123 # example value of a secret compliant with MS SQL Server password policy
MSSQL_PID: Developer
MSSQL_TCP_PORT: 1433
MSSQL_COLLATION: SQL_Latin1_General_CP1_CI_AS
MSSQL_DATA_DIR: /var/opt/mssql/data
MSSQL_LOG_DIR: /var/opt/mssql/log
MSSQL_BACKUP_DIR: /var/opt/mssql/backup
MSSQL_AGENT_ENABLED: true
ports:
- 1433:1433

cassandra:
image: cassandra:4.0
env:
CASSANDRA_USER: admin
CASSANDRA_PASSWORD: password # example value of a secret
ports:
- "9042:9042"
options: >-
--health-cmd="cqlsh -u cassandra -p cassandra 127.0.0.1 9042 --execute='describe keyspaces'"
--health-interval=20s
--health-timeout=10s
--health-retries=15
--health-start-period=60s

clickhouse:
image: clickhouse/clickhouse-server:24.10-alpine
env:
CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT: 1
CLICKHOUSE_DB: perfkit_db_ci
CLICKHOUSE_USER: username
CLICKHOUSE_PASSWORD: password # example value of a secret
ports:
- "8123:8123"
- "9000:9000"

elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.15.1
env:
node.name: es-test
cluster.name: es-docker-cluster
bootstrap.memory_lock: true
discovery.type: single-node
ES_JAVA_OPTS: -Xms1g -Xmx1g
xpack.security.enabled: false
xpack.security.http.ssl.enabled: false
xpack.security.transport.ssl.enabled: false
action.auto_create_index: true
ports:
- 9200:9200
# Health check for Elasticsearch
options: >-
--health-cmd "curl -s http://127.0.0.1:9200/_cluster/health?wait_for_status=yellow&timeout=30s || exit 1"
--health-interval 10s
--health-timeout 5s
--health-retries 10

opensearch:
image: opensearchproject/opensearch:2.18.0
env:
node.name: os-test
discovery.type: single-node
OPENSEARCH_JAVA_OPTS: -Xms512m -Xmx512m
OPENSEARCH_INITIAL_ADMIN_PASSWORD: bgnYFGR2RhN3SCX # example value of a secret compliant with OpenSearch password policy
plugins.security.ssl.http.enabled: false
ports:
- 9201:9200
- 9600:9600
# Health check for OpenSearch
options: >-
--health-cmd "curl -s -u admin:bgnYFGR2RhN3SCX http://127.0.0.1:9201/_cluster/health?wait_for_status=yellow&timeout=30s || exit 1"
--health-interval 20s
--health-timeout 30s
--health-retries 15
--health-start-period 60s

steps:
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.22'

- name: Install database clients
run: |
# Install PostgreSQL client
sudo apt-get update
sudo apt-get install -y postgresql-client
# Install SQL Server tools
curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add -
curl https://packages.microsoft.com/config/ubuntu/20.04/prod.list | sudo tee /etc/apt/sources.list.d/msprod.list
sudo apt-get update
sudo ACCEPT_EULA=Y apt-get install -y mssql-tools unixodbc-dev

- name: Test
run: go test -v ./benchmark/...
- name: Create SQL Server user and database
run: |
# Wait for SQL Server to be ready (max 30 attempts)
for i in {1..30}; do
if /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P MyP@ssw0rd123 -Q "SELECT 1" -b -o /dev/null; then
echo "SQL Server is ready!"
break
fi
echo "Waiting for SQL Server... (attempt $i/30)";
sleep 5;
done

# Create database and user
/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P MyP@ssw0rd123 -Q "
IF NOT EXISTS (SELECT * FROM sys.databases WHERE name = 'perfkit_db_ci')
BEGIN
CREATE DATABASE perfkit_db_ci;
END
GO
USE perfkit_db_ci;
GO
IF NOT EXISTS (SELECT * FROM sys.server_principals WHERE name = 'perfkit_db_runner')
BEGIN
CREATE LOGIN perfkit_db_runner WITH PASSWORD = 'MyP@ssw0rd123';
END
GO
IF NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = 'perfkit_db_runner')
BEGIN
CREATE USER perfkit_db_runner FOR LOGIN perfkit_db_runner;
END
GO
ALTER ROLE db_owner ADD MEMBER perfkit_db_runner;
GO
"
echo "Database and user created successfully"

- name: Create vector extension
run: PGPASSWORD=password psql -h localhost -U root -d perfkit_pg_vector_db_ci -c "CREATE EXTENSION vector;"

- name: Create Cassandra keyspace
run: |
# Wait for Cassandra to be ready (max 30 attempts)
for i in {1..30}; do
if printf "" 2>>/dev/null >>/dev/tcp/127.0.0.1/9042; then
echo "Cassandra is ready!"
break
fi
echo "Waiting for cassandra... (attempt $i/30)";
sleep 5;
done

echo "Creating keyspace..."
docker exec ${{ job.services.cassandra.id }} cqlsh -u cassandra -p cassandra -e "CREATE KEYSPACE IF NOT EXISTS perfkit_db_ci WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1};"
echo "Keyspace created"

- name: Test with Coverage
run: |
go test -v -coverprofile=benchmark_coverage.txt -covermode=atomic ./benchmark/...
go test -v -coverprofile=logger_coverage.txt -covermode=atomic ./logger/...
go test -v -coverprofile=restrelay_coverage.txt -covermode=atomic ./acronis-restrelay-bench/...
go test -v -coverprofile=db_coverage.txt -covermode=atomic ./db/...
go test -v -coverprofile=db_bench_coverage.txt -covermode=atomic ./acronis-db-bench/...

- name: Upload results to Codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: benchmark_coverage.txt,logger_coverage.txt,restrelay_coverage.txt,db_coverage.txt,db_bench_coverage.txt
fail_ci_if_error: true
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
const (
sqliteConnString = "sqlite://:memory:"
mariaDBConnString = "mysql://user:password@tcp(localhost:3306)/perfkit_db_ci"
postgresqlConnString = "postgresql://root:password@localhost:5432/perfkit_db_ci?sslmode=disable"
postgresqlConnString = "postgresql://root:password@localhost:5432/perfkit_pg_vector_db_ci?sslmode=disable"
)

type TestingSuite struct {
Expand Down
10 changes: 10 additions & 0 deletions benchmark/helpers_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,24 @@ package benchmark
import (
"bytes"
"fmt"
"os"
"os/exec"
"strconv"
"strings"
"syscall"
)

func isGitHubCI() bool {
return os.Getenv("GITHUB_ACTIONS") == "true"
}

// adjustFilenoUlimit adjusts file descriptor limits on Linux and Darwin
func (b *Benchmark) adjustFilenoUlimit() int {
// Skip adjustment in GitHub Actions CI
if isGitHubCI() {
return 0
}

var rLimit syscall.Rlimit
fileno := uint64(1048576)

Expand Down
6 changes: 0 additions & 6 deletions benchmark/helpers_unix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
package benchmark

import (
"os"
"os/exec"
"runtime"
"syscall"
Expand All @@ -15,11 +14,6 @@ import (

type LogLevel int

// isGitHubCI returns true if running in GitHub Actions CI environment
func isGitHubCI() bool {
return os.Getenv("GITHUB_ACTIONS") == "true"
}

func TestAdjustFilenoUlimit(t *testing.T) {
// Skip test in GitHub Actions
if isGitHubCI() {
Expand Down
8 changes: 7 additions & 1 deletion db/es/elasticsearch.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"os"
"strconv"
"strings"
"time"

es8 "github.com/elastic/go-elasticsearch/v8"
"github.com/elastic/go-elasticsearch/v8/esapi"
Expand All @@ -39,6 +40,10 @@ func (d *elasticSearchDialect) name() db.DialectName {
return db.ELASTICSEARCH
}

func (d *elasticSearchDialect) getVectorType() fieldType {
return "dense_vector"
}

// nolint:gocritic //TODO refactor unnamed returns
func elasticCredentialsAndConnString(cs string, tlsEnabled bool) (string, string, string, error) {
var u, err = url.Parse(cs)
Expand Down Expand Up @@ -261,7 +266,8 @@ func (q *esQuerier) insert(ctx context.Context, idxName indexName, query *BulkIn
var res, err = q.es.Bulk(query.Reader(),
q.es.Bulk.WithContext(ctx),
q.es.Bulk.WithIndex(string(idxName)),
q.es.Bulk.WithRefresh("wait_for"))
q.es.Bulk.WithRefresh("wait_for"),
q.es.Bulk.WithTimeout(30*time.Second))
if err != nil {
return nil, 0, fmt.Errorf("error from elasticsearch while performing bulk insert: %v", err)
} else if res.IsError() {
Expand Down
3 changes: 2 additions & 1 deletion db/es/es.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func (d *esDatabase) TableExists(tableName string) (bool, error) {
}

func (d *esDatabase) CreateTable(tableName string, tableDefinition *db.TableDefinition, tableMigrationDDL string) error {
return createIndex(d.mig, tableName, tableDefinition, tableMigrationDDL)
return createIndex(d.dialect, d.mig, tableName, tableDefinition, tableMigrationDDL)
}

func (d *esDatabase) DropTable(name string) error {
Expand Down Expand Up @@ -213,4 +213,5 @@ func (tq timedQuerier) count(ctx context.Context, idxName indexName, request *Co

type dialect interface {
name() db.DialectName
getVectorType() fieldType
}
38 changes: 32 additions & 6 deletions db/es/es_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package es

import (
"context"
"fmt"
"testing"
"time"

Expand All @@ -13,8 +14,8 @@ import (
)

const (
esConnString = "es://0.0.0.0:9200"
openSearchConnString = "opensearch://admin:%22ScoRpi0n$%22@0.0.0.0:9200" // example value of a secret compliant with OpenSearch password requirements
esConnString = "es://localhost:9200"
openSearchConnString = "opensearch://admin:bgnYFGR2RhN3SCX@localhost:9201" // example value of a secret compliant with OpenSearch password requirements
)

type TestingSuite struct {
Expand All @@ -26,13 +27,10 @@ func TestDatabaseSuiteElasticSearch(t *testing.T) {
suite.Run(t, &TestingSuite{ConnString: esConnString})
}

/*
func TestDatabaseSuiteOpenSearch(t *testing.T) {
suite.Run(t, &TestingSuite{ConnString: openSearchConnString})
}

*/

type testLogger struct {
t *testing.T
}
Expand Down Expand Up @@ -62,14 +60,16 @@ func (suite *TestingSuite) makeTestSession() (db.Database, db.Session, *db.Conte
dbo, err := db.Open(db.Config{
ConnString: suite.ConnString,
MaxOpenConns: 16,
MaxConnLifetime: 100 * time.Millisecond,
MaxConnLifetime: 1000 * time.Millisecond,
QueryLogger: logger,
})

require.NoError(suite.T(), err, "making test esSession")

var tableSpec = testTableDefinition()

time.Sleep(1 * time.Second)

if err = dbo.CreateTable("perf_table", tableSpec, ""); err != nil {
require.NoError(suite.T(), err, "init scheme")
}
Expand All @@ -95,8 +95,34 @@ func logDbTime(t *testing.T, c *db.Context) {
func cleanup(t *testing.T, dbo db.Database) {
t.Helper()

exists, err := dbo.TableExists("perf_table")
if err != nil {
t.Error("check table exists", err)
return
}

if !exists {
return
}

if err := dbo.DropTable("perf_table"); err != nil {
t.Error("drop table", err)
return
}
}

func dbDialect(connString string) (dialect, error) {
scheme, _, err := db.ParseScheme(connString)
if err != nil {
return nil, fmt.Errorf("cannot parse connection string scheme '%v', error: %v", connString, err)
}

switch scheme {
case "es", "elastic", "elasticsearch":
return &elasticSearchDialect{}, nil
case "os", "opensearch":
return &openSearchDialect{}, nil
default:
return nil, fmt.Errorf("db: unsupported backend '%v'", scheme)
}
}
Loading
Loading