snapshot of https://github.com/seaweedfs/seaweedfs.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
go: build dev binaries / cleanup (push) Successful in 4s
Codespell / Check for spelling errors (push) Failing after 3s
docker: build dev containers / build-rust-binaries (amd64, x86_64-unknown-linux-musl) (push) Failing after 3s
docker: build dev containers / build-dev-containers (push) Has been skipped
docker: build dev containers / build-rust-binaries (arm64, true, aarch64-unknown-linux-musl) (push) Failing after 3s
End to End / FUSE Mount (push) Failing after 2s
EC Integration Tests / EC Integration Tests (push) Failing after 30s
FUSE DLM Integration Tests / FUSE DLM Integration Tests (push) Failing after 3s
FUSE Volume Server Failover Tests / FUSE Volume Server Failover (push) Failing after 3s
FUSE Integration Tests / FUSE Integration Testing (push) Failing after 2s
FUSE P2P Peer Chunk Sharing Integration Tests / FUSE P2P Peer Chunk Sharing (push) Failing after 3s
go: build binary / Go Vet (push) Failing after 2s
go: build binary / Go Vet 32-bit (push) Failing after 3s
go: build binary / Build (push) Failing after 4s
go: build binary / Build cross-platform (freebsd/amd64) (push) Failing after 3s
go: build binary / Build cross-platform (windows/amd64) (push) Failing after 3s
go: build binary / Build cross-platform (darwin/arm64) (push) Failing after 3s
go: build binary / Build cross-platform (windows/arm64) (push) Failing after 3s
go: build binary / Test (push) Failing after 2s
go: build binary / Test 32-bit (push) Failing after 3s
helm: lint and test charts / lint-test (push) Failing after 3s
Java Client Integration Tests / Java Integration Tests (11) (push) Failing after 3s
Java Client Integration Tests / Java Integration Tests (17) (push) Failing after 3s
go: build dev binaries / build_dev_linux_windows (amd64, linux) (push) Failing after 2s
go: build dev binaries / build_dev_linux_windows (amd64, windows) (push) Failing after 3s
go: build dev binaries / build_dev_darwin (amd64, darwin) (push) Has been skipped
go: build dev binaries / build_dev_darwin (arm64, darwin) (push) Has been skipped
Java Client Unit Tests / Java Unit Tests (11) (push) Failing after 3s
Java Client Unit Tests / Java Unit Tests (17) (push) Failing after 2s
Java Client Unit Tests / Java Unit Tests (21) (push) Failing after 3s
Java Client Unit Tests / Java Unit Tests (8) (push) Failing after 3s
Kafka Quick Test (Load Test with Schema Registry) / Kafka Client Load Test (Quick) (push) Failing after 3s
Kafka Gateway Tests / Kafka Unit Tests (unit-tests-1) (push) Failing after 11s
Kafka Gateway Tests / Kafka Integration Tests (Critical) (integration-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka End-to-End Tests (with SMQ) (e2e-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka Consumer Group Tests (Highly Isolated) (consumer-group-1) (push) Failing after 2s
Kafka Gateway Tests / Kafka Client Compatibility (with SMQ) (client-compat-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka SMQ Integration Tests (Full Stack) (smq-integration-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka Protocol Tests (Isolated) (protocol-1) (push) Failing after 3s
KMS Tests / KMS Provider Integration Tests (push) Failing after 2s
KMS Tests / S3 KMS End-to-End Tests (push) Failing after 3s
Master Cold Start Tests / Master Cold Start Tests (push) Failing after 4s
Metadata Subscribe Integration Tests / Metadata Subscribe Integration Tests (push) Failing after 3s
Multi-Master Tests / Multi-Master Failover Tests (push) Failing after 3s
Performance / CPU and Heap Profile (push) Failing after 2s
Performance / S3 Read/Write Benchmark (rust volume) (push) Failing after 3s
pjdfstest POSIX Compliance / pjdfstest (push) Failing after 2s
Plugin Worker Integration Tests / Plugin Worker: erasure_coding (push) Failing after 3s
Plugin Worker Integration Tests / Plugin Worker: vacuum (push) Failing after 3s
Rust Volume Server Tests / Rust Integration Tests (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 1) (push) Failing after 2s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 1) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 2) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 2) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 3) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Distributed Tests (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Performance Tests (push) Failing after 3s
Performance / Throughput Benchmark (go volume) (push) Failing after 3s
Performance / Throughput Benchmark (rust volume) (push) Failing after 2s
Performance / S3 Read/Write Benchmark (go volume) (push) Failing after 3s
Plugin Worker Integration Tests / Plugin Worker: volume_balance (push) Failing after 3s
PostgreSQL Gateway Tests / PostgreSQL Basic Tests (push) Failing after 3s
Rust Volume Server Tests / Rust Unit Tests (push) Failing after 2s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 3) (push) Failing after 3s
Rust Plugin Worker Tests / Rust Plugin Worker Build and Unit Tests (push) Failing after 3s
rust: build dev volume server binaries / cleanup (push) Successful in 3s
S3 ETag and ACL Tests / S3 ETag + ACL Integration Tests (push) Failing after 3s
S3 IAM Integration Tests / IAM Unit Tests (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (advanced) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (basic) (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Integration Tests (group) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (policy-enforcement) (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Integration Tests (sts) (push) Failing after 4s
S3 Keycloak Integration Tests / S3 Keycloak Integration Tests (push) Failing after 3s
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.11) (push) Failing after 3s
S3 PyArrow Parquet Tests / Go Unit Tests (Implicit Directory) (push) Has been cancelled
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.12) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-darwin (darwin-amd64, x86_64-apple-darwin) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-darwin (darwin-arm64, aarch64-apple-darwin) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-linux (linux-amd64, x86_64-unknown-linux-gnu) (push) Has been cancelled
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.9) (push) Has been cancelled
S3 Proxy Signature Tests / S3 Proxy Signature Verification Tests (push) Has been cancelled
S3 SDK V2 Route Disambiguation Tests / S3 SDK V2 Routing Tests (push) Has been cancelled
S3 SSE Tests / S3 SSE Integration Tests (quick) (push) Has been cancelled
S3 SSE Tests / S3 SSE Compatibility Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Metadata Persistence Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Copy Operations Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Multipart Upload Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Performance Test (push) Has been cancelled
S3 SSE Tests / S3 Volume Encryption Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Integration Tests (comprehensive) (push) Has been cancelled
Spark Integration Tests / Spark Integration Tests (push) Failing after 2s
Telemetry Integration Tests / telemetry-integration-test (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 1) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 1) (push) Failing after 3s
Ceph S3 tests / Basic S3 tests (KV store) (push) Has been cancelled
Ceph S3 tests / S3 Versioning & Object Lock tests (push) Has been cancelled
S3 Policy Integration Tests / S3 Policy Unit Tests (push) Failing after 3s
Ceph S3 tests / S3 CORS tests (push) Has been cancelled
Ceph S3 tests / SeaweedFS Custom S3 Copy tests (push) Has been cancelled
Ceph S3 tests / Basic S3 tests (SQL store) (push) Has been cancelled
test s3 over https using aws-cli / awscli-tests (push) Has been cancelled
TLS Rotation Integration Tests / TLS Rotation Integration Tests (push) Has been cancelled
Vacuum Integration Tests / Vacuum Integration Tests (push) Has been cancelled
S3 Policy Integration Tests / S3 Policy Variables Integration Tests (push) Failing after 3s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (advanced-policy) (push) Failing after 3s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (basic-policy) (push) Failing after 2s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (contextual-policy) (push) Failing after 4s
S3 Policy Integration Tests / Trusted Proxy Detection Tests (push) Failing after 2s
S3 Policy Integration Tests / S3 Policy Shell Integration Tests (push) Failing after 3s
Samba on FUSE Integration / samba-integration (push) Failing after 2s
SFTP Integration Tests / SFTP Integration Testing (push) Failing after 3s
terraform: validate and test modules / fmt, validate, plan-level tests (push) Failing after 3s
terraform: validate and test modules / local cluster smoke test (real weed) (push) Failing after 2s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 3) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 2) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 2) (push) Failing after 2s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 3) (push) Failing after 5s
mount: windows conformance / WinFsp conformance (push) Has been cancelled
mount: windows / Mount on Windows (push) Has been cancelled
Some checks failed
go: build dev binaries / cleanup (push) Successful in 4s
Codespell / Check for spelling errors (push) Failing after 3s
docker: build dev containers / build-rust-binaries (amd64, x86_64-unknown-linux-musl) (push) Failing after 3s
docker: build dev containers / build-dev-containers (push) Has been skipped
docker: build dev containers / build-rust-binaries (arm64, true, aarch64-unknown-linux-musl) (push) Failing after 3s
End to End / FUSE Mount (push) Failing after 2s
EC Integration Tests / EC Integration Tests (push) Failing after 30s
FUSE DLM Integration Tests / FUSE DLM Integration Tests (push) Failing after 3s
FUSE Volume Server Failover Tests / FUSE Volume Server Failover (push) Failing after 3s
FUSE Integration Tests / FUSE Integration Testing (push) Failing after 2s
FUSE P2P Peer Chunk Sharing Integration Tests / FUSE P2P Peer Chunk Sharing (push) Failing after 3s
go: build binary / Go Vet (push) Failing after 2s
go: build binary / Go Vet 32-bit (push) Failing after 3s
go: build binary / Build (push) Failing after 4s
go: build binary / Build cross-platform (freebsd/amd64) (push) Failing after 3s
go: build binary / Build cross-platform (windows/amd64) (push) Failing after 3s
go: build binary / Build cross-platform (darwin/arm64) (push) Failing after 3s
go: build binary / Build cross-platform (windows/arm64) (push) Failing after 3s
go: build binary / Test (push) Failing after 2s
go: build binary / Test 32-bit (push) Failing after 3s
helm: lint and test charts / lint-test (push) Failing after 3s
Java Client Integration Tests / Java Integration Tests (11) (push) Failing after 3s
Java Client Integration Tests / Java Integration Tests (17) (push) Failing after 3s
go: build dev binaries / build_dev_linux_windows (amd64, linux) (push) Failing after 2s
go: build dev binaries / build_dev_linux_windows (amd64, windows) (push) Failing after 3s
go: build dev binaries / build_dev_darwin (amd64, darwin) (push) Has been skipped
go: build dev binaries / build_dev_darwin (arm64, darwin) (push) Has been skipped
Java Client Unit Tests / Java Unit Tests (11) (push) Failing after 3s
Java Client Unit Tests / Java Unit Tests (17) (push) Failing after 2s
Java Client Unit Tests / Java Unit Tests (21) (push) Failing after 3s
Java Client Unit Tests / Java Unit Tests (8) (push) Failing after 3s
Kafka Quick Test (Load Test with Schema Registry) / Kafka Client Load Test (Quick) (push) Failing after 3s
Kafka Gateway Tests / Kafka Unit Tests (unit-tests-1) (push) Failing after 11s
Kafka Gateway Tests / Kafka Integration Tests (Critical) (integration-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka End-to-End Tests (with SMQ) (e2e-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka Consumer Group Tests (Highly Isolated) (consumer-group-1) (push) Failing after 2s
Kafka Gateway Tests / Kafka Client Compatibility (with SMQ) (client-compat-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka SMQ Integration Tests (Full Stack) (smq-integration-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka Protocol Tests (Isolated) (protocol-1) (push) Failing after 3s
KMS Tests / KMS Provider Integration Tests (push) Failing after 2s
KMS Tests / S3 KMS End-to-End Tests (push) Failing after 3s
Master Cold Start Tests / Master Cold Start Tests (push) Failing after 4s
Metadata Subscribe Integration Tests / Metadata Subscribe Integration Tests (push) Failing after 3s
Multi-Master Tests / Multi-Master Failover Tests (push) Failing after 3s
Performance / CPU and Heap Profile (push) Failing after 2s
Performance / S3 Read/Write Benchmark (rust volume) (push) Failing after 3s
pjdfstest POSIX Compliance / pjdfstest (push) Failing after 2s
Plugin Worker Integration Tests / Plugin Worker: erasure_coding (push) Failing after 3s
Plugin Worker Integration Tests / Plugin Worker: vacuum (push) Failing after 3s
Rust Volume Server Tests / Rust Integration Tests (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 1) (push) Failing after 2s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 1) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 2) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 2) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 3) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Distributed Tests (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Performance Tests (push) Failing after 3s
Performance / Throughput Benchmark (go volume) (push) Failing after 3s
Performance / Throughput Benchmark (rust volume) (push) Failing after 2s
Performance / S3 Read/Write Benchmark (go volume) (push) Failing after 3s
Plugin Worker Integration Tests / Plugin Worker: volume_balance (push) Failing after 3s
PostgreSQL Gateway Tests / PostgreSQL Basic Tests (push) Failing after 3s
Rust Volume Server Tests / Rust Unit Tests (push) Failing after 2s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 3) (push) Failing after 3s
Rust Plugin Worker Tests / Rust Plugin Worker Build and Unit Tests (push) Failing after 3s
rust: build dev volume server binaries / cleanup (push) Successful in 3s
S3 ETag and ACL Tests / S3 ETag + ACL Integration Tests (push) Failing after 3s
S3 IAM Integration Tests / IAM Unit Tests (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (advanced) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (basic) (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Integration Tests (group) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (policy-enforcement) (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Integration Tests (sts) (push) Failing after 4s
S3 Keycloak Integration Tests / S3 Keycloak Integration Tests (push) Failing after 3s
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.11) (push) Failing after 3s
S3 PyArrow Parquet Tests / Go Unit Tests (Implicit Directory) (push) Has been cancelled
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.12) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-darwin (darwin-amd64, x86_64-apple-darwin) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-darwin (darwin-arm64, aarch64-apple-darwin) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-linux (linux-amd64, x86_64-unknown-linux-gnu) (push) Has been cancelled
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.9) (push) Has been cancelled
S3 Proxy Signature Tests / S3 Proxy Signature Verification Tests (push) Has been cancelled
S3 SDK V2 Route Disambiguation Tests / S3 SDK V2 Routing Tests (push) Has been cancelled
S3 SSE Tests / S3 SSE Integration Tests (quick) (push) Has been cancelled
S3 SSE Tests / S3 SSE Compatibility Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Metadata Persistence Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Copy Operations Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Multipart Upload Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Performance Test (push) Has been cancelled
S3 SSE Tests / S3 Volume Encryption Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Integration Tests (comprehensive) (push) Has been cancelled
Spark Integration Tests / Spark Integration Tests (push) Failing after 2s
Telemetry Integration Tests / telemetry-integration-test (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 1) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 1) (push) Failing after 3s
Ceph S3 tests / Basic S3 tests (KV store) (push) Has been cancelled
Ceph S3 tests / S3 Versioning & Object Lock tests (push) Has been cancelled
S3 Policy Integration Tests / S3 Policy Unit Tests (push) Failing after 3s
Ceph S3 tests / S3 CORS tests (push) Has been cancelled
Ceph S3 tests / SeaweedFS Custom S3 Copy tests (push) Has been cancelled
Ceph S3 tests / Basic S3 tests (SQL store) (push) Has been cancelled
test s3 over https using aws-cli / awscli-tests (push) Has been cancelled
TLS Rotation Integration Tests / TLS Rotation Integration Tests (push) Has been cancelled
Vacuum Integration Tests / Vacuum Integration Tests (push) Has been cancelled
S3 Policy Integration Tests / S3 Policy Variables Integration Tests (push) Failing after 3s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (advanced-policy) (push) Failing after 3s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (basic-policy) (push) Failing after 2s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (contextual-policy) (push) Failing after 4s
S3 Policy Integration Tests / Trusted Proxy Detection Tests (push) Failing after 2s
S3 Policy Integration Tests / S3 Policy Shell Integration Tests (push) Failing after 3s
Samba on FUSE Integration / samba-integration (push) Failing after 2s
SFTP Integration Tests / SFTP Integration Testing (push) Failing after 3s
terraform: validate and test modules / fmt, validate, plan-level tests (push) Failing after 3s
terraform: validate and test modules / local cluster smoke test (real weed) (push) Failing after 2s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 3) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 2) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 2) (push) Failing after 2s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 3) (push) Failing after 5s
mount: windows conformance / WinFsp conformance (push) Has been cancelled
mount: windows / Mount on Windows (push) Has been cancelled
This commit is contained in:
630
test/kafka/loadtest/mock_million_record_test.go
Normal file
630
test/kafka/loadtest/mock_million_record_test.go
Normal file
@@ -0,0 +1,630 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/keepalive"
|
||||
|
||||
"github.com/seaweedfs/seaweedfs/weed/pb"
|
||||
|
||||
"github.com/seaweedfs/seaweedfs/weed/glog"
|
||||
"github.com/seaweedfs/seaweedfs/weed/pb/mq_pb"
|
||||
"github.com/seaweedfs/seaweedfs/weed/pb/schema_pb"
|
||||
)
|
||||
|
||||
// TestRecord represents a record with reasonable fields for integration testing
|
||||
type MockTestRecord struct {
|
||||
ID string
|
||||
UserID int64
|
||||
Timestamp int64
|
||||
Event string
|
||||
Data map[string]interface{}
|
||||
Metadata map[string]string
|
||||
}
|
||||
|
||||
// GenerateTestRecord creates a realistic test record
|
||||
func GenerateMockTestRecord(id int) MockTestRecord {
|
||||
events := []string{"user_login", "user_logout", "page_view", "purchase", "signup", "profile_update", "search"}
|
||||
metadata := map[string]string{
|
||||
"source": "web",
|
||||
"version": "1.0.0",
|
||||
"region": "us-west-2",
|
||||
"client_ip": fmt.Sprintf("192.168.%d.%d", rand.Intn(255), rand.Intn(255)),
|
||||
}
|
||||
|
||||
data := map[string]interface{}{
|
||||
"session_id": fmt.Sprintf("sess_%d_%d", id, time.Now().Unix()),
|
||||
"user_agent": "Mozilla/5.0 (compatible; SeaweedFS-Test/1.0)",
|
||||
"referrer": "https://example.com/page" + strconv.Itoa(rand.Intn(100)),
|
||||
"duration": rand.Intn(3600), // seconds
|
||||
"score": rand.Float64() * 100,
|
||||
}
|
||||
|
||||
return MockTestRecord{
|
||||
ID: fmt.Sprintf("record_%d", id),
|
||||
UserID: int64(rand.Intn(10000) + 1),
|
||||
Timestamp: time.Now().UnixNano(),
|
||||
Event: events[rand.Intn(len(events))],
|
||||
Data: data,
|
||||
Metadata: metadata,
|
||||
}
|
||||
}
|
||||
|
||||
// SerializeTestRecord converts TestRecord to key-value pair for Kafka
|
||||
func SerializeMockTestRecord(record MockTestRecord) ([]byte, []byte) {
|
||||
key := fmt.Sprintf("user_%d:%s", record.UserID, record.ID)
|
||||
|
||||
// Create a realistic JSON-like value with reasonable size (200-500 bytes)
|
||||
value := fmt.Sprintf(`{
|
||||
"id": "%s",
|
||||
"user_id": %d,
|
||||
"timestamp": %d,
|
||||
"event": "%s",
|
||||
"session_id": "%v",
|
||||
"user_agent": "%v",
|
||||
"referrer": "%v",
|
||||
"duration": %v,
|
||||
"score": %.2f,
|
||||
"source": "%s",
|
||||
"version": "%s",
|
||||
"region": "%s",
|
||||
"client_ip": "%s",
|
||||
"batch_info": "This is additional data to make the record size more realistic for testing purposes. It simulates the kind of metadata and context that would typically be included in real-world event data."
|
||||
}`,
|
||||
record.ID,
|
||||
record.UserID,
|
||||
record.Timestamp,
|
||||
record.Event,
|
||||
record.Data["session_id"],
|
||||
record.Data["user_agent"],
|
||||
record.Data["referrer"],
|
||||
record.Data["duration"],
|
||||
record.Data["score"],
|
||||
record.Metadata["source"],
|
||||
record.Metadata["version"],
|
||||
record.Metadata["region"],
|
||||
record.Metadata["client_ip"],
|
||||
)
|
||||
|
||||
return []byte(key), []byte(value)
|
||||
}
|
||||
|
||||
// DirectBrokerClient connects directly to the broker without discovery
|
||||
type DirectBrokerClient struct {
|
||||
brokerAddress string
|
||||
conn *grpc.ClientConn
|
||||
client mq_pb.SeaweedMessagingClient
|
||||
|
||||
// Publisher streams: topic-partition -> stream info
|
||||
publishersLock sync.RWMutex
|
||||
publishers map[string]*PublisherSession
|
||||
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
// PublisherSession tracks a publishing stream to SeaweedMQ broker
|
||||
type PublisherSession struct {
|
||||
Topic string
|
||||
Partition int32
|
||||
Stream mq_pb.SeaweedMessaging_PublishMessageClient
|
||||
MessageCount int64 // Track messages sent for batch ack handling
|
||||
}
|
||||
|
||||
func NewDirectBrokerClient(brokerAddr string) (*DirectBrokerClient, error) {
|
||||
// Use a short-lived context for dialing so we don't store a canceled
|
||||
// context in the returned client. The client's operational context
|
||||
// (used by methods) should be cancellable independently.
|
||||
dialCtx, dialCancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer dialCancel()
|
||||
|
||||
// Add keepalive settings; use exported server constants to keep values in sync.
|
||||
conn, err := grpc.DialContext(dialCtx, brokerAddr,
|
||||
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||
grpc.WithKeepaliveParams(keepalive.ClientParameters{
|
||||
Time: pb.GrpcKeepAliveTime, // align with server MinTime
|
||||
Timeout: pb.GrpcKeepAliveTimeout, // align with server timeout
|
||||
PermitWithoutStream: false, // reduce pings when idle
|
||||
}))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to connect to broker: %v", err)
|
||||
}
|
||||
|
||||
// Create a long-lived context for the client's lifetime and store it
|
||||
// in the returned DirectBrokerClient so callers can cancel when done.
|
||||
clientCtx, clientCancel := context.WithCancel(context.Background())
|
||||
|
||||
client := mq_pb.NewSeaweedMessagingClient(conn)
|
||||
|
||||
return &DirectBrokerClient{
|
||||
brokerAddress: brokerAddr,
|
||||
conn: conn,
|
||||
client: client,
|
||||
publishers: make(map[string]*PublisherSession),
|
||||
ctx: clientCtx,
|
||||
cancel: clientCancel,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *DirectBrokerClient) Close() {
|
||||
c.cancel()
|
||||
|
||||
// Close all publisher streams
|
||||
c.publishersLock.Lock()
|
||||
for key := range c.publishers {
|
||||
delete(c.publishers, key)
|
||||
}
|
||||
c.publishersLock.Unlock()
|
||||
|
||||
c.conn.Close()
|
||||
}
|
||||
|
||||
func (c *DirectBrokerClient) ConfigureTopic(topicName string, partitions int32) error {
|
||||
topic := &schema_pb.Topic{
|
||||
Namespace: "kafka",
|
||||
Name: topicName,
|
||||
}
|
||||
|
||||
// Create schema for MockTestRecord
|
||||
recordType := &schema_pb.RecordType{
|
||||
Fields: []*schema_pb.Field{
|
||||
{
|
||||
Name: "id",
|
||||
FieldIndex: 0,
|
||||
Type: &schema_pb.Type{
|
||||
Kind: &schema_pb.Type_ScalarType{ScalarType: schema_pb.ScalarType_STRING},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "user_id",
|
||||
FieldIndex: 1,
|
||||
Type: &schema_pb.Type{
|
||||
Kind: &schema_pb.Type_ScalarType{ScalarType: schema_pb.ScalarType_INT64},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "timestamp",
|
||||
FieldIndex: 2,
|
||||
Type: &schema_pb.Type{
|
||||
Kind: &schema_pb.Type_ScalarType{ScalarType: schema_pb.ScalarType_INT64},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "event",
|
||||
FieldIndex: 3,
|
||||
Type: &schema_pb.Type{
|
||||
Kind: &schema_pb.Type_ScalarType{ScalarType: schema_pb.ScalarType_STRING},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "data",
|
||||
FieldIndex: 4,
|
||||
Type: &schema_pb.Type{
|
||||
Kind: &schema_pb.Type_ScalarType{ScalarType: schema_pb.ScalarType_STRING}, // JSON string
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "metadata",
|
||||
FieldIndex: 5,
|
||||
Type: &schema_pb.Type{
|
||||
Kind: &schema_pb.Type_ScalarType{ScalarType: schema_pb.ScalarType_STRING}, // JSON string
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Use user_id as the key column for partitioning
|
||||
keyColumns := []string{"user_id"}
|
||||
|
||||
_, err := c.client.ConfigureTopic(c.ctx, &mq_pb.ConfigureTopicRequest{
|
||||
Topic: topic,
|
||||
PartitionCount: partitions,
|
||||
MessageRecordType: recordType,
|
||||
KeyColumns: keyColumns,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *DirectBrokerClient) PublishRecord(topicName string, partition int32, key, value []byte) error {
|
||||
session, err := c.getOrCreatePublisher(topicName, partition)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Send data message using broker API format
|
||||
dataMsg := &mq_pb.DataMessage{
|
||||
Key: key,
|
||||
Value: value,
|
||||
TsNs: time.Now().UnixNano(),
|
||||
}
|
||||
|
||||
if err := session.Stream.Send(&mq_pb.PublishMessageRequest{
|
||||
Message: &mq_pb.PublishMessageRequest_Data{
|
||||
Data: dataMsg,
|
||||
},
|
||||
}); err != nil {
|
||||
return fmt.Errorf("failed to send data: %v", err)
|
||||
}
|
||||
|
||||
// Don't wait for individual acks! AckInterval=100 means acks come in batches
|
||||
// The broker will handle acknowledgments asynchronously
|
||||
return nil
|
||||
}
|
||||
|
||||
// getOrCreatePublisher gets or creates a publisher stream for a topic-partition
|
||||
func (c *DirectBrokerClient) getOrCreatePublisher(topic string, partition int32) (*PublisherSession, error) {
|
||||
key := fmt.Sprintf("%s-%d", topic, partition)
|
||||
|
||||
// Try to get existing publisher
|
||||
c.publishersLock.RLock()
|
||||
if session, exists := c.publishers[key]; exists {
|
||||
c.publishersLock.RUnlock()
|
||||
return session, nil
|
||||
}
|
||||
c.publishersLock.RUnlock()
|
||||
|
||||
// Create new publisher stream
|
||||
c.publishersLock.Lock()
|
||||
defer c.publishersLock.Unlock()
|
||||
|
||||
// Double-check after acquiring write lock
|
||||
if session, exists := c.publishers[key]; exists {
|
||||
return session, nil
|
||||
}
|
||||
|
||||
// Create the stream
|
||||
stream, err := c.client.PublishMessage(c.ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create publish stream: %v", err)
|
||||
}
|
||||
|
||||
// Get the actual partition assignment from the broker
|
||||
actualPartition, err := c.getActualPartitionAssignment(topic, partition)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get actual partition assignment: %v", err)
|
||||
}
|
||||
|
||||
// Send init message using the actual partition structure that the broker allocated
|
||||
if err := stream.Send(&mq_pb.PublishMessageRequest{
|
||||
Message: &mq_pb.PublishMessageRequest_Init{
|
||||
Init: &mq_pb.PublishMessageRequest_InitMessage{
|
||||
Topic: &schema_pb.Topic{
|
||||
Namespace: "kafka",
|
||||
Name: topic,
|
||||
},
|
||||
Partition: actualPartition,
|
||||
AckInterval: 200, // Ack every 200 messages for better balance
|
||||
PublisherName: "direct-test",
|
||||
},
|
||||
},
|
||||
}); err != nil {
|
||||
return nil, fmt.Errorf("failed to send init message: %v", err)
|
||||
}
|
||||
|
||||
session := &PublisherSession{
|
||||
Topic: topic,
|
||||
Partition: partition,
|
||||
Stream: stream,
|
||||
MessageCount: 0,
|
||||
}
|
||||
|
||||
c.publishers[key] = session
|
||||
return session, nil
|
||||
}
|
||||
|
||||
// getActualPartitionAssignment looks up the actual partition assignment from the broker configuration
|
||||
func (c *DirectBrokerClient) getActualPartitionAssignment(topic string, kafkaPartition int32) (*schema_pb.Partition, error) {
|
||||
// Look up the topic configuration from the broker to get the actual partition assignments
|
||||
lookupResp, err := c.client.LookupTopicBrokers(c.ctx, &mq_pb.LookupTopicBrokersRequest{
|
||||
Topic: &schema_pb.Topic{
|
||||
Namespace: "kafka",
|
||||
Name: topic,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to lookup topic brokers: %v", err)
|
||||
}
|
||||
|
||||
if len(lookupResp.BrokerPartitionAssignments) == 0 {
|
||||
return nil, fmt.Errorf("no partition assignments found for topic %s", topic)
|
||||
}
|
||||
|
||||
totalPartitions := int32(len(lookupResp.BrokerPartitionAssignments))
|
||||
if kafkaPartition >= totalPartitions {
|
||||
return nil, fmt.Errorf("kafka partition %d out of range, topic %s has %d partitions",
|
||||
kafkaPartition, topic, totalPartitions)
|
||||
}
|
||||
|
||||
// Calculate expected range for this Kafka partition
|
||||
// Ring is divided equally among partitions, with last partition getting any remainder
|
||||
const ringSize = int32(2520) // MaxPartitionCount constant
|
||||
rangeSize := ringSize / totalPartitions
|
||||
expectedRangeStart := kafkaPartition * rangeSize
|
||||
var expectedRangeStop int32
|
||||
|
||||
if kafkaPartition == totalPartitions-1 {
|
||||
// Last partition gets the remainder to fill the entire ring
|
||||
expectedRangeStop = ringSize
|
||||
} else {
|
||||
expectedRangeStop = (kafkaPartition + 1) * rangeSize
|
||||
}
|
||||
|
||||
// Find the broker assignment that matches this range
|
||||
for _, assignment := range lookupResp.BrokerPartitionAssignments {
|
||||
if assignment.Partition == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if this assignment's range matches our expected range
|
||||
if assignment.Partition.RangeStart == expectedRangeStart && assignment.Partition.RangeStop == expectedRangeStop {
|
||||
return assignment.Partition, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("no broker assignment found for Kafka partition %d with expected range [%d, %d]",
|
||||
kafkaPartition, expectedRangeStart, expectedRangeStop)
|
||||
}
|
||||
|
||||
// TestDirectBroker_MillionRecordsIntegration tests the broker directly without discovery
|
||||
func TestDirectBroker_MillionRecordsIntegration(t *testing.T) {
|
||||
// Skip by default - this is a large integration test
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping million-record integration test in short mode")
|
||||
}
|
||||
|
||||
// Configuration
|
||||
const (
|
||||
totalRecords = 1000000
|
||||
numPartitions = int32(8) // Use multiple partitions for better performance
|
||||
numProducers = 4 // Concurrent producers
|
||||
brokerAddr = "localhost:17777"
|
||||
)
|
||||
|
||||
// Create direct broker client for topic configuration
|
||||
configClient, err := NewDirectBrokerClient(brokerAddr)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create direct broker client: %v", err)
|
||||
}
|
||||
defer configClient.Close()
|
||||
|
||||
topicName := fmt.Sprintf("million-records-direct-test-%d", time.Now().Unix())
|
||||
|
||||
// Create topic
|
||||
glog.Infof("Creating topic %s with %d partitions", topicName, numPartitions)
|
||||
err = configClient.ConfigureTopic(topicName, numPartitions)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to configure topic: %v", err)
|
||||
}
|
||||
|
||||
// Performance tracking
|
||||
var totalProduced int64
|
||||
var totalErrors int64
|
||||
startTime := time.Now()
|
||||
|
||||
// Progress tracking
|
||||
ticker := time.NewTicker(10 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
produced := atomic.LoadInt64(&totalProduced)
|
||||
errors := atomic.LoadInt64(&totalErrors)
|
||||
elapsed := time.Since(startTime)
|
||||
rate := float64(produced) / elapsed.Seconds()
|
||||
glog.Infof("Progress: %d/%d records (%.1f%%), rate: %.0f records/sec, errors: %d",
|
||||
produced, totalRecords, float64(produced)/float64(totalRecords)*100, rate, errors)
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Producer function
|
||||
producer := func(producerID int, recordsPerProducer int) error {
|
||||
defer func() {
|
||||
glog.Infof("Producer %d finished", producerID)
|
||||
}()
|
||||
|
||||
// Create dedicated client for this producer
|
||||
producerClient, err := NewDirectBrokerClient(brokerAddr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Producer %d failed to create client: %v", producerID, err)
|
||||
}
|
||||
defer producerClient.Close()
|
||||
|
||||
// Add timeout context for each producer
|
||||
producerCtx, producerCancel := context.WithTimeout(ctx, 10*time.Minute)
|
||||
defer producerCancel()
|
||||
|
||||
glog.Infof("Producer %d: About to start producing %d records with dedicated client", producerID, recordsPerProducer)
|
||||
|
||||
for i := 0; i < recordsPerProducer; i++ {
|
||||
// Check if context is cancelled or timed out
|
||||
select {
|
||||
case <-producerCtx.Done():
|
||||
glog.Errorf("Producer %d timed out or cancelled after %d records", producerID, i)
|
||||
return producerCtx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
// Debug progress for all producers every 50k records
|
||||
if i > 0 && i%50000 == 0 {
|
||||
glog.Infof("Producer %d: Progress %d/%d records (%.1f%%)", producerID, i, recordsPerProducer, float64(i)/float64(recordsPerProducer)*100)
|
||||
}
|
||||
// Calculate global record ID
|
||||
recordID := producerID*recordsPerProducer + i
|
||||
|
||||
// Generate test record
|
||||
testRecord := GenerateMockTestRecord(recordID)
|
||||
key, value := SerializeMockTestRecord(testRecord)
|
||||
|
||||
// Distribute across partitions based on user ID
|
||||
partition := int32(testRecord.UserID % int64(numPartitions))
|
||||
|
||||
// Debug first few records for each producer
|
||||
if i < 3 {
|
||||
glog.Infof("Producer %d: Record %d -> UserID %d -> Partition %d", producerID, i, testRecord.UserID, partition)
|
||||
}
|
||||
|
||||
// Produce the record with retry logic
|
||||
var err error
|
||||
maxRetries := 3
|
||||
for retry := 0; retry < maxRetries; retry++ {
|
||||
err = producerClient.PublishRecord(topicName, partition, key, value)
|
||||
if err == nil {
|
||||
break // Success
|
||||
}
|
||||
|
||||
// If it's an EOF error, wait a bit before retrying
|
||||
if err.Error() == "failed to send data: EOF" {
|
||||
time.Sleep(time.Duration(retry+1) * 100 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
|
||||
// For other errors, don't retry
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
atomic.AddInt64(&totalErrors, 1)
|
||||
errorCount := atomic.LoadInt64(&totalErrors)
|
||||
if errorCount < 20 { // Log first 20 errors to get more insight
|
||||
glog.Errorf("Producer %d failed to produce record %d (i=%d) after %d retries: %v", producerID, recordID, i, maxRetries, err)
|
||||
}
|
||||
// Don't continue - this might be causing producers to exit early
|
||||
// Let's see what happens if we return the error instead
|
||||
if errorCount > 1000 { // If too many errors, give up
|
||||
glog.Errorf("Producer %d giving up after %d errors", producerID, errorCount)
|
||||
return fmt.Errorf("too many errors: %d", errorCount)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
atomic.AddInt64(&totalProduced, 1)
|
||||
|
||||
// Log progress for first producer
|
||||
if producerID == 0 && (i+1)%10000 == 0 {
|
||||
glog.Infof("Producer %d: produced %d records", producerID, i+1)
|
||||
}
|
||||
}
|
||||
|
||||
glog.Infof("Producer %d: Completed loop, produced %d records successfully", producerID, recordsPerProducer)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Start concurrent producers
|
||||
glog.Infof("Starting %d concurrent producers to produce %d records", numProducers, totalRecords)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
recordsPerProducer := totalRecords / numProducers
|
||||
|
||||
for i := 0; i < numProducers; i++ {
|
||||
wg.Add(1)
|
||||
go func(producerID int) {
|
||||
defer wg.Done()
|
||||
glog.Infof("Producer %d starting with %d records to produce", producerID, recordsPerProducer)
|
||||
if err := producer(producerID, recordsPerProducer); err != nil {
|
||||
glog.Errorf("Producer %d failed: %v", producerID, err)
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
|
||||
// Wait for all producers to complete
|
||||
wg.Wait()
|
||||
cancel() // Stop progress reporting
|
||||
|
||||
produceTime := time.Since(startTime)
|
||||
finalProduced := atomic.LoadInt64(&totalProduced)
|
||||
finalErrors := atomic.LoadInt64(&totalErrors)
|
||||
|
||||
glog.Infof("Production completed: %d records in %v (%.0f records/sec), errors: %d",
|
||||
finalProduced, produceTime, float64(finalProduced)/produceTime.Seconds(), finalErrors)
|
||||
|
||||
// Performance summary
|
||||
if finalProduced > 0 {
|
||||
glog.Infof("\n"+
|
||||
"=== PERFORMANCE SUMMARY ===\n"+
|
||||
"Records produced: %d\n"+
|
||||
"Production time: %v\n"+
|
||||
"Production rate: %.0f records/sec\n"+
|
||||
"Errors: %d (%.2f%%)\n"+
|
||||
"Partitions: %d\n"+
|
||||
"Concurrent producers: %d\n"+
|
||||
"Average record size: ~300 bytes\n"+
|
||||
"Total data: ~%.1f MB\n"+
|
||||
"Throughput: ~%.1f MB/sec\n",
|
||||
finalProduced,
|
||||
produceTime,
|
||||
float64(finalProduced)/produceTime.Seconds(),
|
||||
finalErrors,
|
||||
float64(finalErrors)/float64(totalRecords)*100,
|
||||
numPartitions,
|
||||
numProducers,
|
||||
float64(finalProduced)*300/(1024*1024),
|
||||
float64(finalProduced)*300/(1024*1024)/produceTime.Seconds(),
|
||||
)
|
||||
}
|
||||
|
||||
// Test assertions
|
||||
if finalProduced < int64(totalRecords*0.95) { // Allow 5% tolerance for errors
|
||||
t.Errorf("Too few records produced: %d < %d (95%% of target)", finalProduced, int64(float64(totalRecords)*0.95))
|
||||
}
|
||||
|
||||
if finalErrors > int64(totalRecords*0.05) { // Error rate should be < 5%
|
||||
t.Errorf("Too many errors: %d > %d (5%% of target)", finalErrors, int64(float64(totalRecords)*0.05))
|
||||
}
|
||||
|
||||
glog.Infof("Direct broker million-record integration test completed successfully!")
|
||||
}
|
||||
|
||||
// BenchmarkDirectBroker_ProduceThroughput benchmarks the production throughput
|
||||
func BenchmarkDirectBroker_ProduceThroughput(b *testing.B) {
|
||||
if testing.Short() {
|
||||
b.Skip("Skipping benchmark in short mode")
|
||||
}
|
||||
|
||||
client, err := NewDirectBrokerClient("localhost:17777")
|
||||
if err != nil {
|
||||
b.Fatalf("Failed to create client: %v", err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
topicName := fmt.Sprintf("benchmark-topic-%d", time.Now().Unix())
|
||||
err = client.ConfigureTopic(topicName, 1)
|
||||
if err != nil {
|
||||
b.Fatalf("Failed to configure topic: %v", err)
|
||||
}
|
||||
|
||||
// Pre-generate test data
|
||||
records := make([]MockTestRecord, b.N)
|
||||
for i := 0; i < b.N; i++ {
|
||||
records[i] = GenerateMockTestRecord(i)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
key, value := SerializeMockTestRecord(records[i])
|
||||
err := client.PublishRecord(topicName, 0, key, value)
|
||||
if err != nil {
|
||||
b.Fatalf("Failed to produce record %d: %v", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
b.StopTimer()
|
||||
}
|
||||
139
test/kafka/loadtest/quick_performance_test.go
Normal file
139
test/kafka/loadtest/quick_performance_test.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/seaweedfs/seaweedfs/weed/glog"
|
||||
)
|
||||
|
||||
// TestQuickPerformance_10K tests the fixed broker with 10K records
|
||||
func TestQuickPerformance_10K(t *testing.T) {
|
||||
const (
|
||||
totalRecords = 10000 // 10K records for quick test
|
||||
numPartitions = int32(4)
|
||||
numProducers = 4
|
||||
brokerAddr = "localhost:17777"
|
||||
)
|
||||
|
||||
// Create direct broker client
|
||||
client, err := NewDirectBrokerClient(brokerAddr)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create direct broker client: %v", err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
topicName := fmt.Sprintf("quick-test-%d", time.Now().Unix())
|
||||
|
||||
// Create topic
|
||||
glog.Infof("Creating topic %s with %d partitions", topicName, numPartitions)
|
||||
err = client.ConfigureTopic(topicName, numPartitions)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to configure topic: %v", err)
|
||||
}
|
||||
|
||||
// Performance tracking
|
||||
var totalProduced int64
|
||||
var totalErrors int64
|
||||
startTime := time.Now()
|
||||
|
||||
// Producer function
|
||||
producer := func(producerID int, recordsPerProducer int) error {
|
||||
for i := 0; i < recordsPerProducer; i++ {
|
||||
recordID := producerID*recordsPerProducer + i
|
||||
|
||||
// Generate test record
|
||||
testRecord := GenerateMockTestRecord(recordID)
|
||||
key, value := SerializeMockTestRecord(testRecord)
|
||||
|
||||
partition := int32(testRecord.UserID % int64(numPartitions))
|
||||
|
||||
// Produce the record (now async!)
|
||||
err := client.PublishRecord(topicName, partition, key, value)
|
||||
if err != nil {
|
||||
atomic.AddInt64(&totalErrors, 1)
|
||||
if atomic.LoadInt64(&totalErrors) < 5 {
|
||||
glog.Errorf("Producer %d failed to produce record %d: %v", producerID, recordID, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
atomic.AddInt64(&totalProduced, 1)
|
||||
|
||||
// Log progress
|
||||
if (i+1)%1000 == 0 {
|
||||
elapsed := time.Since(startTime)
|
||||
rate := float64(atomic.LoadInt64(&totalProduced)) / elapsed.Seconds()
|
||||
glog.Infof("Producer %d: %d records, current rate: %.0f records/sec",
|
||||
producerID, i+1, rate)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Start concurrent producers
|
||||
glog.Infof("Starting %d producers for %d records total", numProducers, totalRecords)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
recordsPerProducer := totalRecords / numProducers
|
||||
|
||||
for i := 0; i < numProducers; i++ {
|
||||
wg.Add(1)
|
||||
go func(producerID int) {
|
||||
defer wg.Done()
|
||||
if err := producer(producerID, recordsPerProducer); err != nil {
|
||||
glog.Errorf("Producer %d failed: %v", producerID, err)
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
|
||||
// Wait for completion
|
||||
wg.Wait()
|
||||
|
||||
produceTime := time.Since(startTime)
|
||||
finalProduced := atomic.LoadInt64(&totalProduced)
|
||||
finalErrors := atomic.LoadInt64(&totalErrors)
|
||||
|
||||
// Performance results
|
||||
throughputPerSec := float64(finalProduced) / produceTime.Seconds()
|
||||
dataVolumeMB := float64(finalProduced) * 300 / (1024 * 1024) // ~300 bytes per record
|
||||
throughputMBPerSec := dataVolumeMB / produceTime.Seconds()
|
||||
|
||||
glog.Infof("\n"+
|
||||
"QUICK PERFORMANCE TEST RESULTS\n"+
|
||||
"=====================================\n"+
|
||||
"Records produced: %d / %d\n"+
|
||||
"Production time: %v\n"+
|
||||
"Throughput: %.0f records/sec\n"+
|
||||
"Data volume: %.1f MB\n"+
|
||||
"Bandwidth: %.1f MB/sec\n"+
|
||||
"Errors: %d (%.2f%%)\n"+
|
||||
"Success rate: %.1f%%\n",
|
||||
finalProduced, totalRecords,
|
||||
produceTime,
|
||||
throughputPerSec,
|
||||
dataVolumeMB,
|
||||
throughputMBPerSec,
|
||||
finalErrors,
|
||||
float64(finalErrors)/float64(totalRecords)*100,
|
||||
float64(finalProduced)/float64(totalRecords)*100,
|
||||
)
|
||||
|
||||
// Assertions
|
||||
if finalProduced < int64(totalRecords*0.90) { // Allow 10% tolerance
|
||||
t.Errorf("Too few records produced: %d < %d (90%% of target)", finalProduced, int64(float64(totalRecords)*0.90))
|
||||
}
|
||||
|
||||
if throughputPerSec < 100 { // Should be much higher than 1 record/sec now!
|
||||
t.Errorf("Throughput too low: %.0f records/sec (expected > 100)", throughputPerSec)
|
||||
}
|
||||
|
||||
if finalErrors > int64(totalRecords*0.10) { // Error rate should be < 10%
|
||||
t.Errorf("Too many errors: %d > %d (10%% of target)", finalErrors, int64(float64(totalRecords)*0.10))
|
||||
}
|
||||
|
||||
glog.Infof("Performance test passed! Ready for million-record test.")
|
||||
}
|
||||
207
test/kafka/loadtest/resume_million_test.go
Normal file
207
test/kafka/loadtest/resume_million_test.go
Normal file
@@ -0,0 +1,207 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/seaweedfs/seaweedfs/weed/glog"
|
||||
)
|
||||
|
||||
// TestResumeMillionRecords_Fixed - Fixed version with better concurrency handling
|
||||
func TestResumeMillionRecords_Fixed(t *testing.T) {
|
||||
const (
|
||||
totalRecords = 1000000
|
||||
numPartitions = int32(8)
|
||||
numProducers = 4
|
||||
brokerAddr = "localhost:17777"
|
||||
batchSize = 100 // Process in smaller batches to avoid overwhelming
|
||||
)
|
||||
|
||||
// Create direct broker client
|
||||
client, err := NewDirectBrokerClient(brokerAddr)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create direct broker client: %v", err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
topicName := fmt.Sprintf("resume-million-test-%d", time.Now().Unix())
|
||||
|
||||
// Create topic
|
||||
glog.Infof("Creating topic %s with %d partitions for RESUMED test", topicName, numPartitions)
|
||||
err = client.ConfigureTopic(topicName, numPartitions)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to configure topic: %v", err)
|
||||
}
|
||||
|
||||
// Performance tracking
|
||||
var totalProduced int64
|
||||
var totalErrors int64
|
||||
startTime := time.Now()
|
||||
|
||||
// Progress tracking
|
||||
ticker := time.NewTicker(5 * time.Second) // More frequent updates
|
||||
defer ticker.Stop()
|
||||
|
||||
go func() {
|
||||
for range ticker.C {
|
||||
produced := atomic.LoadInt64(&totalProduced)
|
||||
errors := atomic.LoadInt64(&totalErrors)
|
||||
elapsed := time.Since(startTime)
|
||||
rate := float64(produced) / elapsed.Seconds()
|
||||
progressPercent := float64(produced) / float64(totalRecords) * 100
|
||||
|
||||
glog.Infof("PROGRESS: %d/%d records (%.1f%%), rate: %.0f records/sec, errors: %d",
|
||||
produced, totalRecords, progressPercent, rate, errors)
|
||||
|
||||
if produced >= totalRecords {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Fixed producer function with better error handling
|
||||
producer := func(producerID int, recordsPerProducer int) error {
|
||||
defer glog.Infof("Producer %d FINISHED", producerID)
|
||||
|
||||
// Create dedicated clients per producer to avoid contention
|
||||
producerClient, err := NewDirectBrokerClient(brokerAddr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("producer %d failed to create client: %v", producerID, err)
|
||||
}
|
||||
defer producerClient.Close()
|
||||
|
||||
successCount := 0
|
||||
for i := 0; i < recordsPerProducer; i++ {
|
||||
recordID := producerID*recordsPerProducer + i
|
||||
|
||||
// Generate test record
|
||||
testRecord := GenerateMockTestRecord(recordID)
|
||||
key, value := SerializeMockTestRecord(testRecord)
|
||||
|
||||
partition := int32(testRecord.UserID % int64(numPartitions))
|
||||
|
||||
// Produce with retry logic
|
||||
maxRetries := 3
|
||||
var lastErr error
|
||||
success := false
|
||||
|
||||
for retry := 0; retry < maxRetries; retry++ {
|
||||
err := producerClient.PublishRecord(topicName, partition, key, value)
|
||||
if err == nil {
|
||||
success = true
|
||||
break
|
||||
}
|
||||
lastErr = err
|
||||
time.Sleep(time.Duration(retry+1) * 100 * time.Millisecond) // Exponential backoff
|
||||
}
|
||||
|
||||
if success {
|
||||
atomic.AddInt64(&totalProduced, 1)
|
||||
successCount++
|
||||
} else {
|
||||
atomic.AddInt64(&totalErrors, 1)
|
||||
if atomic.LoadInt64(&totalErrors) < 10 {
|
||||
glog.Errorf("Producer %d failed record %d after retries: %v", producerID, recordID, lastErr)
|
||||
}
|
||||
}
|
||||
|
||||
// Batch progress logging
|
||||
if successCount > 0 && successCount%10000 == 0 {
|
||||
glog.Infof("Producer %d: %d/%d records completed", producerID, successCount, recordsPerProducer)
|
||||
}
|
||||
|
||||
// Small delay to prevent overwhelming the broker
|
||||
if i > 0 && i%batchSize == 0 {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
glog.Infof("Producer %d completed: %d successful, %d errors",
|
||||
producerID, successCount, recordsPerProducer-successCount)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Start concurrent producers
|
||||
glog.Infof("Starting FIXED %d producers for %d records total", numProducers, totalRecords)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
recordsPerProducer := totalRecords / numProducers
|
||||
|
||||
for i := 0; i < numProducers; i++ {
|
||||
wg.Add(1)
|
||||
go func(producerID int) {
|
||||
defer wg.Done()
|
||||
if err := producer(producerID, recordsPerProducer); err != nil {
|
||||
glog.Errorf("Producer %d FAILED: %v", producerID, err)
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
|
||||
// Wait for completion with timeout
|
||||
done := make(chan bool)
|
||||
go func() {
|
||||
wg.Wait()
|
||||
done <- true
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
glog.Infof("All producers completed normally")
|
||||
case <-time.After(30 * time.Minute): // 30-minute timeout
|
||||
glog.Errorf("Test timed out after 30 minutes")
|
||||
t.Errorf("Test timed out")
|
||||
return
|
||||
}
|
||||
|
||||
produceTime := time.Since(startTime)
|
||||
finalProduced := atomic.LoadInt64(&totalProduced)
|
||||
finalErrors := atomic.LoadInt64(&totalErrors)
|
||||
|
||||
// Performance results
|
||||
throughputPerSec := float64(finalProduced) / produceTime.Seconds()
|
||||
dataVolumeMB := float64(finalProduced) * 300 / (1024 * 1024)
|
||||
throughputMBPerSec := dataVolumeMB / produceTime.Seconds()
|
||||
successRate := float64(finalProduced) / float64(totalRecords) * 100
|
||||
|
||||
glog.Infof("\n"+
|
||||
"=== FINAL MILLION RECORD TEST RESULTS ===\n"+
|
||||
"==========================================\n"+
|
||||
"Records produced: %d / %d\n"+
|
||||
"Production time: %v\n"+
|
||||
"Average throughput: %.0f records/sec\n"+
|
||||
"Data volume: %.1f MB\n"+
|
||||
"Bandwidth: %.1f MB/sec\n"+
|
||||
"Errors: %d (%.2f%%)\n"+
|
||||
"Success rate: %.1f%%\n"+
|
||||
"Partitions used: %d\n"+
|
||||
"Concurrent producers: %d\n",
|
||||
finalProduced, totalRecords,
|
||||
produceTime,
|
||||
throughputPerSec,
|
||||
dataVolumeMB,
|
||||
throughputMBPerSec,
|
||||
finalErrors,
|
||||
float64(finalErrors)/float64(totalRecords)*100,
|
||||
successRate,
|
||||
numPartitions,
|
||||
numProducers,
|
||||
)
|
||||
|
||||
// Test assertions
|
||||
if finalProduced < int64(totalRecords*0.95) { // Allow 5% tolerance
|
||||
t.Errorf("Too few records produced: %d < %d (95%% of target)", finalProduced, int64(float64(totalRecords)*0.95))
|
||||
}
|
||||
|
||||
if finalErrors > int64(totalRecords*0.05) { // Error rate should be < 5%
|
||||
t.Errorf("Too many errors: %d > %d (5%% of target)", finalErrors, int64(float64(totalRecords)*0.05))
|
||||
}
|
||||
|
||||
if throughputPerSec < 100 {
|
||||
t.Errorf("Throughput too low: %.0f records/sec (expected > 100)", throughputPerSec)
|
||||
}
|
||||
|
||||
glog.Infof("🏆 MILLION RECORD KAFKA INTEGRATION TEST COMPLETED SUCCESSFULLY!")
|
||||
}
|
||||
115
test/kafka/loadtest/run_million_record_test.sh
Executable file
115
test/kafka/loadtest/run_million_record_test.sh
Executable file
@@ -0,0 +1,115 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script to run the Kafka Gateway Million Record Integration Test
|
||||
# This test requires a running SeaweedFS infrastructure (Master, Filer, MQ Broker)
|
||||
|
||||
set -e
|
||||
|
||||
echo "=== SeaweedFS Kafka Gateway Million Record Integration Test ==="
|
||||
echo "Test Date: $(date)"
|
||||
echo "Hostname: $(hostname)"
|
||||
echo ""
|
||||
|
||||
# Configuration
|
||||
MASTERS=${SEAWEED_MASTERS:-"localhost:9333"}
|
||||
FILER_GROUP=${SEAWEED_FILER_GROUP:-"default"}
|
||||
TEST_DIR="."
|
||||
TEST_NAME="TestDirectBroker_MillionRecordsIntegration"
|
||||
|
||||
echo "Configuration:"
|
||||
echo " Masters: $MASTERS"
|
||||
echo " Filer Group: $FILER_GROUP"
|
||||
echo " Test Directory: $TEST_DIR"
|
||||
echo ""
|
||||
|
||||
# Check if SeaweedFS infrastructure is running
|
||||
echo "=== Checking Infrastructure ==="
|
||||
|
||||
# Function to check if a service is running
|
||||
check_service() {
|
||||
local host_port=$1
|
||||
local service_name=$2
|
||||
|
||||
if timeout 3 bash -c "</dev/tcp/${host_port//://}" 2>/dev/null; then
|
||||
echo "✓ $service_name is running on $host_port"
|
||||
return 0
|
||||
else
|
||||
echo "✗ $service_name is NOT running on $host_port"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Check each master
|
||||
IFS=',' read -ra MASTER_ARRAY <<< "$MASTERS"
|
||||
MASTERS_OK=true
|
||||
for master in "${MASTER_ARRAY[@]}"; do
|
||||
if ! check_service "$master" "SeaweedFS Master"; then
|
||||
MASTERS_OK=false
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$MASTERS_OK" = false ]; then
|
||||
echo ""
|
||||
echo "ERROR: One or more SeaweedFS Masters are not running."
|
||||
echo "Please start your SeaweedFS infrastructure before running this test."
|
||||
echo ""
|
||||
echo "Example commands to start SeaweedFS:"
|
||||
echo " # Terminal 1: Start Master"
|
||||
echo " weed master -defaultReplication=001 -mdir=/tmp/seaweedfs/master -peers=none"
|
||||
echo ""
|
||||
echo " # Terminal 2: Start Filer"
|
||||
echo " weed filer -master=localhost:9333 -filer.dir=/tmp/seaweedfs/filer"
|
||||
echo ""
|
||||
echo " # Terminal 3: Start MQ Broker"
|
||||
echo " weed mq.broker -filer=localhost:8888 -master=localhost:9333"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== Infrastructure Check Passed ==="
|
||||
echo ""
|
||||
|
||||
# Change to the correct directory
|
||||
cd "$TEST_DIR"
|
||||
|
||||
# Set environment variables for the test
|
||||
export SEAWEED_MASTERS="$MASTERS"
|
||||
export SEAWEED_FILER_GROUP="$FILER_GROUP"
|
||||
|
||||
# Run the test with verbose output
|
||||
echo "=== Running Million Record Integration Test ==="
|
||||
echo "This may take several minutes..."
|
||||
echo ""
|
||||
|
||||
# Run the specific test with timeout and verbose output
|
||||
timeout 1800 go test -v -run "$TEST_NAME" -timeout=30m 2>&1 | tee /tmp/seaweed_million_record_test.log
|
||||
|
||||
TEST_EXIT_CODE=${PIPESTATUS[0]}
|
||||
|
||||
echo ""
|
||||
echo "=== Test Completed ==="
|
||||
echo "Exit Code: $TEST_EXIT_CODE"
|
||||
echo "Full log available at: /tmp/seaweed_million_record_test.log"
|
||||
echo ""
|
||||
|
||||
# Show summary from the log
|
||||
echo "=== Performance Summary ==="
|
||||
if grep -q "PERFORMANCE SUMMARY" /tmp/seaweed_million_record_test.log; then
|
||||
grep -A 15 "PERFORMANCE SUMMARY" /tmp/seaweed_million_record_test.log
|
||||
else
|
||||
echo "Performance summary not found in log"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
if [ $TEST_EXIT_CODE -eq 0 ]; then
|
||||
echo "🎉 TEST PASSED: Million record integration test completed successfully!"
|
||||
else
|
||||
echo "❌ TEST FAILED: Million record integration test failed with exit code $TEST_EXIT_CODE"
|
||||
echo "Check the log file for details: /tmp/seaweed_million_record_test.log"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== Test Run Complete ==="
|
||||
exit $TEST_EXIT_CODE
|
||||
131
test/kafka/loadtest/setup_seaweed_infrastructure.sh
Executable file
131
test/kafka/loadtest/setup_seaweed_infrastructure.sh
Executable file
@@ -0,0 +1,131 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script to set up SeaweedFS infrastructure for Kafka Gateway testing
|
||||
# This script will start Master, Filer, and MQ Broker components
|
||||
|
||||
set -e
|
||||
|
||||
BASE_DIR="/tmp/seaweedfs"
|
||||
LOG_DIR="$BASE_DIR/logs"
|
||||
DATA_DIR="$BASE_DIR/data"
|
||||
|
||||
echo "=== SeaweedFS Infrastructure Setup ==="
|
||||
echo "Setup Date: $(date)"
|
||||
echo "Base Directory: $BASE_DIR"
|
||||
echo ""
|
||||
|
||||
# Create directories
|
||||
mkdir -p "$BASE_DIR/master" "$BASE_DIR/filer" "$BASE_DIR/broker" "$LOG_DIR"
|
||||
|
||||
# Function to check if a service is running
|
||||
check_service() {
|
||||
local host_port=$1
|
||||
local service_name=$2
|
||||
|
||||
if timeout 3 bash -c "</dev/tcp/${host_port//://}" 2>/dev/null; then
|
||||
echo "✓ $service_name is already running on $host_port"
|
||||
return 0
|
||||
else
|
||||
echo "✗ $service_name is NOT running on $host_port"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to start a service in background
|
||||
start_service() {
|
||||
local cmd="$1"
|
||||
local service_name="$2"
|
||||
local log_file="$3"
|
||||
local check_port="$4"
|
||||
|
||||
echo "Starting $service_name..."
|
||||
echo "Command: $cmd"
|
||||
echo "Log: $log_file"
|
||||
|
||||
# Start in background
|
||||
nohup $cmd > "$log_file" 2>&1 &
|
||||
local pid=$!
|
||||
echo "PID: $pid"
|
||||
|
||||
# Wait for service to be ready
|
||||
local retries=30
|
||||
while [ $retries -gt 0 ]; do
|
||||
if check_service "$check_port" "$service_name" 2>/dev/null; then
|
||||
echo "✓ $service_name is ready"
|
||||
return 0
|
||||
fi
|
||||
retries=$((retries - 1))
|
||||
sleep 1
|
||||
echo -n "."
|
||||
done
|
||||
echo ""
|
||||
echo "❌ $service_name failed to start within 30 seconds"
|
||||
return 1
|
||||
}
|
||||
|
||||
# Stop any existing processes
|
||||
echo "=== Cleaning up existing processes ==="
|
||||
pkill -f "weed master" || true
|
||||
pkill -f "weed filer" || true
|
||||
pkill -f "weed mq.broker" || true
|
||||
sleep 2
|
||||
|
||||
echo ""
|
||||
echo "=== Starting SeaweedFS Components ==="
|
||||
|
||||
# Start Master
|
||||
if ! check_service "localhost:9333" "SeaweedFS Master"; then
|
||||
start_service \
|
||||
"weed master -defaultReplication=001 -mdir=$BASE_DIR/master -peers=none" \
|
||||
"SeaweedFS Master" \
|
||||
"$LOG_DIR/master.log" \
|
||||
"localhost:9333"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Start Filer
|
||||
if ! check_service "localhost:8888" "SeaweedFS Filer"; then
|
||||
start_service \
|
||||
"weed filer -master=localhost:9333 -filer.dir=$BASE_DIR/filer" \
|
||||
"SeaweedFS Filer" \
|
||||
"$LOG_DIR/filer.log" \
|
||||
"localhost:8888"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Start MQ Broker
|
||||
if ! check_service "localhost:17777" "SeaweedFS MQ Broker"; then
|
||||
start_service \
|
||||
"weed mq.broker -filer=localhost:8888 -master=localhost:9333" \
|
||||
"SeaweedFS MQ Broker" \
|
||||
"$LOG_DIR/broker.log" \
|
||||
"localhost:17777"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
echo "=== Infrastructure Status ==="
|
||||
check_service "localhost:9333" "Master (gRPC)"
|
||||
check_service "localhost:9334" "Master (HTTP)"
|
||||
check_service "localhost:8888" "Filer (HTTP)"
|
||||
check_service "localhost:18888" "Filer (gRPC)"
|
||||
check_service "localhost:17777" "MQ Broker"
|
||||
|
||||
echo ""
|
||||
echo "=== Infrastructure Ready ==="
|
||||
echo "Log files:"
|
||||
echo " Master: $LOG_DIR/master.log"
|
||||
echo " Filer: $LOG_DIR/filer.log"
|
||||
echo " Broker: $LOG_DIR/broker.log"
|
||||
echo ""
|
||||
echo "To view logs in real-time:"
|
||||
echo " tail -f $LOG_DIR/master.log"
|
||||
echo " tail -f $LOG_DIR/filer.log"
|
||||
echo " tail -f $LOG_DIR/broker.log"
|
||||
echo ""
|
||||
echo "To stop all services:"
|
||||
echo " pkill -f \"weed master\""
|
||||
echo " pkill -f \"weed filer\""
|
||||
echo " pkill -f \"weed mq.broker\""
|
||||
echo ""
|
||||
echo "[OK] SeaweedFS infrastructure is ready for testing!"
|
||||
|
||||
Reference in New Issue
Block a user