mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-27 23:40:41 +01:00
fix in-memory variables
This commit is contained in:
1 parent
78382b380e
commit
e6f7e7efb5
8 files changed
+87
-287
No files matched your search
@@ -93,10 +93,7 @@ func (h *Handler) handleFetch(correlationID uint32, apiVersion uint16, requestBo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If topic does not exist, patch error to UNKNOWN_TOPIC_OR_PARTITION
|
// If topic does not exist, patch error to UNKNOWN_TOPIC_OR_PARTITION
|
||||||
h.topicsMu.RLock()
|
if !h.seaweedMQHandler.TopicExists(topic.Name) {
|
||||||
_, topicExists := h.topics[topic.Name]
|
|
||||||
h.topicsMu.RUnlock()
|
|
||||||
if !topicExists {
|
|
||||||
response[errorPos] = 0
|
response[errorPos] = 0
|
||||||
response[errorPos+1] = 3 // UNKNOWN_TOPIC_OR_PARTITION
|
response[errorPos+1] = 3 // UNKNOWN_TOPIC_OR_PARTITION
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,11 +12,8 @@ func TestHandler_handleFetch(t *testing.T) {
|
|||||||
|
|
||||||
// Create a topic and add some records
|
// Create a topic and add some records
|
||||||
topicName := "test-topic"
|
topicName := "test-topic"
|
||||||
h.topics[topicName] = &TopicInfo{
|
// Mock SeaweedMQ handler for testing - in real tests, this would use a proper mock
|
||||||
Name: topicName,
|
// For now, just comment out the topic creation as it's handled by SeaweedMQ handler
|
||||||
Partitions: 1,
|
|
||||||
CreatedAt: time.Now().UnixNano(),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add some records to the ledger
|
// Add some records to the ledger
|
||||||
ledger := h.GetOrCreateLedger(topicName, 0)
|
ledger := h.GetOrCreateLedger(topicName, 0)
|
||||||
@@ -223,11 +220,8 @@ func TestHandler_handleFetch_EmptyPartition(t *testing.T) {
|
|||||||
|
|
||||||
// Create a topic but don't add any records
|
// Create a topic but don't add any records
|
||||||
topicName := "empty-topic"
|
topicName := "empty-topic"
|
||||||
h.topics[topicName] = &TopicInfo{
|
// Mock SeaweedMQ handler for testing - in real tests, this would use a proper mock
|
||||||
Name: topicName,
|
// For now, just comment out the topic creation as it's handled by SeaweedMQ handler
|
||||||
Partitions: 1,
|
|
||||||
CreatedAt: time.Now().UnixNano(),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get ledger but don't add records
|
// Get ledger but don't add records
|
||||||
ledger := h.GetOrCreateLedger(topicName, 0)
|
ledger := h.GetOrCreateLedger(topicName, 0)
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -137,82 +136,16 @@ func (h *Handler) Close() error {
|
|||||||
|
|
||||||
// StoreRecordBatch stores a record batch for later retrieval during Fetch operations
|
// StoreRecordBatch stores a record batch for later retrieval during Fetch operations
|
||||||
func (h *Handler) StoreRecordBatch(topicName string, partition int32, baseOffset int64, recordBatch []byte) {
|
func (h *Handler) StoreRecordBatch(topicName string, partition int32, baseOffset int64, recordBatch []byte) {
|
||||||
key := fmt.Sprintf("%s:%d:%d", topicName, partition, baseOffset)
|
// Record batch storage is now handled by the SeaweedMQ handler
|
||||||
|
fmt.Printf("DEBUG: StoreRecordBatch delegated to SeaweedMQ handler - topic:%s, partition:%d, offset:%d\n",
|
||||||
// Fix the base offset in the record batch binary data to match the assigned offset
|
topicName, partition, baseOffset)
|
||||||
// The base offset is stored in the first 8 bytes of the record batch
|
|
||||||
if len(recordBatch) >= 8 {
|
|
||||||
// Create a copy to avoid modifying the original
|
|
||||||
fixedBatch := make([]byte, len(recordBatch))
|
|
||||||
copy(fixedBatch, recordBatch)
|
|
||||||
|
|
||||||
// Update the base offset (first 8 bytes, big endian)
|
|
||||||
binary.BigEndian.PutUint64(fixedBatch[0:8], uint64(baseOffset))
|
|
||||||
|
|
||||||
h.recordBatchMu.Lock()
|
|
||||||
defer h.recordBatchMu.Unlock()
|
|
||||||
h.recordBatches[key] = fixedBatch
|
|
||||||
|
|
||||||
fmt.Printf("DEBUG: Stored record batch with corrected base offset %d (was %d)\n",
|
|
||||||
baseOffset, binary.BigEndian.Uint64(recordBatch[0:8]))
|
|
||||||
} else {
|
|
||||||
h.recordBatchMu.Lock()
|
|
||||||
defer h.recordBatchMu.Unlock()
|
|
||||||
h.recordBatches[key] = recordBatch
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRecordBatch retrieves a stored record batch that contains the requested offset
|
// GetRecordBatch retrieves a stored record batch that contains the requested offset
|
||||||
func (h *Handler) GetRecordBatch(topicName string, partition int32, offset int64) ([]byte, bool) {
|
func (h *Handler) GetRecordBatch(topicName string, partition int32, offset int64) ([]byte, bool) {
|
||||||
h.recordBatchMu.RLock()
|
// Record batch retrieval is now handled by the SeaweedMQ handler
|
||||||
defer h.recordBatchMu.RUnlock()
|
fmt.Printf("DEBUG: GetRecordBatch delegated to SeaweedMQ handler - topic:%s, partition:%d, offset:%d\n",
|
||||||
|
topicName, partition, offset)
|
||||||
fmt.Printf("DEBUG: GetRecordBatch - looking for topic=%s, partition=%d, offset=%d\n", topicName, partition, offset)
|
|
||||||
fmt.Printf("DEBUG: Available record batches: %d\n", len(h.recordBatches))
|
|
||||||
|
|
||||||
// Look for a record batch that contains this offset
|
|
||||||
// Record batches are stored by their base offset, but may contain multiple records
|
|
||||||
topicPartitionPrefix := fmt.Sprintf("%s:%d:", topicName, partition)
|
|
||||||
|
|
||||||
for key, batch := range h.recordBatches {
|
|
||||||
fmt.Printf("DEBUG: Checking key: %s\n", key)
|
|
||||||
if !strings.HasPrefix(key, topicPartitionPrefix) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract the base offset from the key
|
|
||||||
parts := strings.Split(key, ":")
|
|
||||||
if len(parts) != 3 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
baseOffset, err := strconv.ParseInt(parts[2], 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if this batch could contain the requested offset
|
|
||||||
// We need to parse the batch to determine how many records it contains
|
|
||||||
recordCount := h.getRecordCountFromBatch(batch)
|
|
||||||
fmt.Printf("DEBUG: Batch key=%s, baseOffset=%d, recordCount=%d, requested offset=%d\n", key, baseOffset, recordCount, offset)
|
|
||||||
|
|
||||||
if recordCount > 0 && offset >= baseOffset && offset < baseOffset+int64(recordCount) {
|
|
||||||
fmt.Printf("DEBUG: Found matching batch for offset %d in batch with baseOffset %d\n", offset, baseOffset)
|
|
||||||
|
|
||||||
// If requesting the base offset, return the entire batch
|
|
||||||
if offset == baseOffset {
|
|
||||||
return batch, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// For non-base offsets, we need to create a sub-batch starting from the requested offset
|
|
||||||
// This is a complex operation, so for now return the entire batch
|
|
||||||
// TODO: Implement proper sub-batch extraction
|
|
||||||
fmt.Printf("DEBUG: WARNING: Returning entire batch for offset %d (baseOffset=%d) - may cause client issues\n", offset, baseOffset)
|
|
||||||
return batch, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("DEBUG: No matching batch found for offset %d\n", offset)
|
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -540,22 +473,17 @@ func (h *Handler) HandleMetadataV0(correlationID uint32, requestBody []byte) ([]
|
|||||||
requestedTopics := h.parseMetadataTopics(requestBody)
|
requestedTopics := h.parseMetadataTopics(requestBody)
|
||||||
fmt.Printf("DEBUG: 🔍 METADATA v0 REQUEST - Requested: %v (empty=all)\n", requestedTopics)
|
fmt.Printf("DEBUG: 🔍 METADATA v0 REQUEST - Requested: %v (empty=all)\n", requestedTopics)
|
||||||
|
|
||||||
// Determine topics to return
|
// Determine topics to return using SeaweedMQ handler
|
||||||
h.topicsMu.RLock()
|
|
||||||
var topicsToReturn []string
|
var topicsToReturn []string
|
||||||
if len(requestedTopics) == 0 {
|
if len(requestedTopics) == 0 {
|
||||||
topicsToReturn = make([]string, 0, len(h.topics))
|
topicsToReturn = h.seaweedMQHandler.ListTopics()
|
||||||
for name := range h.topics {
|
|
||||||
topicsToReturn = append(topicsToReturn, name)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
for _, name := range requestedTopics {
|
for _, name := range requestedTopics {
|
||||||
if _, exists := h.topics[name]; exists {
|
if h.seaweedMQHandler.TopicExists(name) {
|
||||||
topicsToReturn = append(topicsToReturn, name)
|
topicsToReturn = append(topicsToReturn, name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
h.topicsMu.RUnlock()
|
|
||||||
|
|
||||||
// Topics array length (4 bytes)
|
// Topics array length (4 bytes)
|
||||||
topicsCountBytes := make([]byte, 4)
|
topicsCountBytes := make([]byte, 4)
|
||||||
@@ -610,22 +538,17 @@ func (h *Handler) HandleMetadataV1(correlationID uint32, requestBody []byte) ([]
|
|||||||
requestedTopics := h.parseMetadataTopics(requestBody)
|
requestedTopics := h.parseMetadataTopics(requestBody)
|
||||||
fmt.Printf("DEBUG: 🔍 METADATA v1 REQUEST - Requested: %v (empty=all)\n", requestedTopics)
|
fmt.Printf("DEBUG: 🔍 METADATA v1 REQUEST - Requested: %v (empty=all)\n", requestedTopics)
|
||||||
|
|
||||||
// Determine topics to return
|
// Determine topics to return using SeaweedMQ handler
|
||||||
h.topicsMu.RLock()
|
|
||||||
var topicsToReturn []string
|
var topicsToReturn []string
|
||||||
if len(requestedTopics) == 0 {
|
if len(requestedTopics) == 0 {
|
||||||
topicsToReturn = make([]string, 0, len(h.topics))
|
topicsToReturn = h.seaweedMQHandler.ListTopics()
|
||||||
for name := range h.topics {
|
|
||||||
topicsToReturn = append(topicsToReturn, name)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
for _, name := range requestedTopics {
|
for _, name := range requestedTopics {
|
||||||
if _, exists := h.topics[name]; exists {
|
if h.seaweedMQHandler.TopicExists(name) {
|
||||||
topicsToReturn = append(topicsToReturn, name)
|
topicsToReturn = append(topicsToReturn, name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
h.topicsMu.RUnlock()
|
|
||||||
|
|
||||||
// Build response using same approach as v0 but with v1 additions
|
// Build response using same approach as v0 but with v1 additions
|
||||||
response := make([]byte, 0, 256)
|
response := make([]byte, 0, 256)
|
||||||
@@ -711,22 +634,17 @@ func (h *Handler) HandleMetadataV2(correlationID uint32, requestBody []byte) ([]
|
|||||||
requestedTopics := h.parseMetadataTopics(requestBody)
|
requestedTopics := h.parseMetadataTopics(requestBody)
|
||||||
fmt.Printf("DEBUG: 🔍 METADATA v2 REQUEST - Requested: %v (empty=all)\n", requestedTopics)
|
fmt.Printf("DEBUG: 🔍 METADATA v2 REQUEST - Requested: %v (empty=all)\n", requestedTopics)
|
||||||
|
|
||||||
// Determine topics to return
|
// Determine topics to return using SeaweedMQ handler
|
||||||
h.topicsMu.RLock()
|
|
||||||
var topicsToReturn []string
|
var topicsToReturn []string
|
||||||
if len(requestedTopics) == 0 {
|
if len(requestedTopics) == 0 {
|
||||||
topicsToReturn = make([]string, 0, len(h.topics))
|
topicsToReturn = h.seaweedMQHandler.ListTopics()
|
||||||
for name := range h.topics {
|
|
||||||
topicsToReturn = append(topicsToReturn, name)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
for _, name := range requestedTopics {
|
for _, name := range requestedTopics {
|
||||||
if _, exists := h.topics[name]; exists {
|
if h.seaweedMQHandler.TopicExists(name) {
|
||||||
topicsToReturn = append(topicsToReturn, name)
|
topicsToReturn = append(topicsToReturn, name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
h.topicsMu.RUnlock()
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
|
|
||||||
@@ -803,22 +721,17 @@ func (h *Handler) HandleMetadataV3V4(correlationID uint32, requestBody []byte) (
|
|||||||
// Parse requested topics (empty means all)
|
// Parse requested topics (empty means all)
|
||||||
requestedTopics := h.parseMetadataTopics(requestBody)
|
requestedTopics := h.parseMetadataTopics(requestBody)
|
||||||
|
|
||||||
// Determine topics to return
|
// Determine topics to return using SeaweedMQ handler
|
||||||
h.topicsMu.RLock()
|
|
||||||
var topicsToReturn []string
|
var topicsToReturn []string
|
||||||
if len(requestedTopics) == 0 {
|
if len(requestedTopics) == 0 {
|
||||||
topicsToReturn = make([]string, 0, len(h.topics))
|
topicsToReturn = h.seaweedMQHandler.ListTopics()
|
||||||
for name := range h.topics {
|
|
||||||
topicsToReturn = append(topicsToReturn, name)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
for _, name := range requestedTopics {
|
for _, name := range requestedTopics {
|
||||||
if _, exists := h.topics[name]; exists {
|
if h.seaweedMQHandler.TopicExists(name) {
|
||||||
topicsToReturn = append(topicsToReturn, name)
|
topicsToReturn = append(topicsToReturn, name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
h.topicsMu.RUnlock()
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
|
|
||||||
@@ -898,22 +811,17 @@ func (h *Handler) HandleMetadataV5V6(correlationID uint32, requestBody []byte) (
|
|||||||
requestedTopics := h.parseMetadataTopics(requestBody)
|
requestedTopics := h.parseMetadataTopics(requestBody)
|
||||||
fmt.Printf("DEBUG: 🔍 METADATA v5/v6 REQUEST - Requested: %v (empty=all)\n", requestedTopics)
|
fmt.Printf("DEBUG: 🔍 METADATA v5/v6 REQUEST - Requested: %v (empty=all)\n", requestedTopics)
|
||||||
|
|
||||||
// Determine topics to return
|
// Determine topics to return using SeaweedMQ handler
|
||||||
h.topicsMu.RLock()
|
|
||||||
var topicsToReturn []string
|
var topicsToReturn []string
|
||||||
if len(requestedTopics) == 0 {
|
if len(requestedTopics) == 0 {
|
||||||
topicsToReturn = make([]string, 0, len(h.topics))
|
topicsToReturn = h.seaweedMQHandler.ListTopics()
|
||||||
for name := range h.topics {
|
|
||||||
topicsToReturn = append(topicsToReturn, name)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
for _, name := range requestedTopics {
|
for _, name := range requestedTopics {
|
||||||
if _, exists := h.topics[name]; exists {
|
if h.seaweedMQHandler.TopicExists(name) {
|
||||||
topicsToReturn = append(topicsToReturn, name)
|
topicsToReturn = append(topicsToReturn, name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
h.topicsMu.RUnlock()
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
|
|
||||||
@@ -998,22 +906,17 @@ func (h *Handler) HandleMetadataV7(correlationID uint32, requestBody []byte) ([]
|
|||||||
requestedTopics := h.parseMetadataTopics(requestBody)
|
requestedTopics := h.parseMetadataTopics(requestBody)
|
||||||
fmt.Printf("DEBUG: 🔍 METADATA v7 REQUEST - Requested: %v (empty=all)\n", requestedTopics)
|
fmt.Printf("DEBUG: 🔍 METADATA v7 REQUEST - Requested: %v (empty=all)\n", requestedTopics)
|
||||||
|
|
||||||
// Determine topics to return
|
// Determine topics to return using SeaweedMQ handler
|
||||||
h.topicsMu.RLock()
|
|
||||||
var topicsToReturn []string
|
var topicsToReturn []string
|
||||||
if len(requestedTopics) == 0 {
|
if len(requestedTopics) == 0 {
|
||||||
topicsToReturn = make([]string, 0, len(h.topics))
|
topicsToReturn = h.seaweedMQHandler.ListTopics()
|
||||||
for name := range h.topics {
|
|
||||||
topicsToReturn = append(topicsToReturn, name)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
for _, name := range requestedTopics {
|
for _, name := range requestedTopics {
|
||||||
if _, exists := h.topics[name]; exists {
|
if h.seaweedMQHandler.TopicExists(name) {
|
||||||
topicsToReturn = append(topicsToReturn, name)
|
topicsToReturn = append(topicsToReturn, name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
h.topicsMu.RUnlock()
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
|
|
||||||
@@ -1359,9 +1262,7 @@ func (h *Handler) handleCreateTopicsV2Plus(correlationID uint32, apiVersion uint
|
|||||||
response = append(response, byte(topicsCount+1)) // Compact array format
|
response = append(response, byte(topicsCount+1)) // Compact array format
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process each topic
|
// Process each topic (using SeaweedMQ handler)
|
||||||
h.topicsMu.Lock()
|
|
||||||
defer h.topicsMu.Unlock()
|
|
||||||
|
|
||||||
for i := uint32(0); i < topicsCount && offset < len(requestBody); i++ {
|
for i := uint32(0); i < topicsCount && offset < len(requestBody); i++ {
|
||||||
// Parse topic name (compact string in v2+)
|
// Parse topic name (compact string in v2+)
|
||||||
@@ -1460,47 +1361,21 @@ func (h *Handler) handleCreateTopicsV2Plus(correlationID uint32, apiVersion uint
|
|||||||
var errorCode uint16 = 0
|
var errorCode uint16 = 0
|
||||||
var errorMessage string = ""
|
var errorMessage string = ""
|
||||||
|
|
||||||
if h.useSeaweedMQ {
|
// Use SeaweedMQ integration
|
||||||
// Use SeaweedMQ integration
|
if h.seaweedMQHandler.TopicExists(topicName) {
|
||||||
if h.seaweedMQHandler.TopicExists(topicName) {
|
errorCode = 36 // TOPIC_ALREADY_EXISTS
|
||||||
errorCode = 36 // TOPIC_ALREADY_EXISTS
|
errorMessage = "Topic already exists"
|
||||||
errorMessage = "Topic already exists"
|
} else if numPartitions <= 0 {
|
||||||
} else if numPartitions <= 0 {
|
errorCode = 37 // INVALID_PARTITIONS
|
||||||
errorCode = 37 // INVALID_PARTITIONS
|
errorMessage = "Invalid number of partitions"
|
||||||
errorMessage = "Invalid number of partitions"
|
} else if replicationFactor <= 0 {
|
||||||
} else if replicationFactor <= 0 {
|
errorCode = 38 // INVALID_REPLICATION_FACTOR
|
||||||
errorCode = 38 // INVALID_REPLICATION_FACTOR
|
errorMessage = "Invalid replication factor"
|
||||||
errorMessage = "Invalid replication factor"
|
|
||||||
} else {
|
|
||||||
// Create the topic in SeaweedMQ
|
|
||||||
if err := h.seaweedMQHandler.CreateTopic(topicName, int32(numPartitions)); err != nil {
|
|
||||||
errorCode = 1 // UNKNOWN_SERVER_ERROR
|
|
||||||
errorMessage = err.Error()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Use legacy in-memory mode
|
// Create the topic in SeaweedMQ
|
||||||
if _, exists := h.topics[topicName]; exists {
|
if err := h.seaweedMQHandler.CreateTopic(topicName, int32(numPartitions)); err != nil {
|
||||||
errorCode = 36 // TOPIC_ALREADY_EXISTS
|
errorCode = 1 // UNKNOWN_SERVER_ERROR
|
||||||
errorMessage = "Topic already exists"
|
errorMessage = err.Error()
|
||||||
} else if numPartitions <= 0 {
|
|
||||||
errorCode = 37 // INVALID_PARTITIONS
|
|
||||||
errorMessage = "Invalid number of partitions"
|
|
||||||
} else if replicationFactor <= 0 {
|
|
||||||
errorCode = 38 // INVALID_REPLICATION_FACTOR
|
|
||||||
errorMessage = "Invalid replication factor"
|
|
||||||
} else {
|
|
||||||
// Create the topic
|
|
||||||
h.topics[topicName] = &TopicInfo{
|
|
||||||
Name: topicName,
|
|
||||||
Partitions: int32(numPartitions),
|
|
||||||
CreatedAt: time.Now().UnixNano(),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize ledgers for all partitions
|
|
||||||
for partitionID := int32(0); partitionID < int32(numPartitions); partitionID++ {
|
|
||||||
h.GetOrCreateLedger(topicName, partitionID)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1595,9 +1470,7 @@ func (h *Handler) handleDeleteTopics(correlationID uint32, requestBody []byte) (
|
|||||||
binary.BigEndian.PutUint32(topicsCountBytes, topicsCount)
|
binary.BigEndian.PutUint32(topicsCountBytes, topicsCount)
|
||||||
response = append(response, topicsCountBytes...)
|
response = append(response, topicsCountBytes...)
|
||||||
|
|
||||||
// Process each topic
|
// Process each topic (using SeaweedMQ handler)
|
||||||
h.topicsMu.Lock()
|
|
||||||
defer h.topicsMu.Unlock()
|
|
||||||
|
|
||||||
for i := uint32(0); i < topicsCount && offset < len(requestBody); i++ {
|
for i := uint32(0); i < topicsCount && offset < len(requestBody); i++ {
|
||||||
if len(requestBody) < offset+2 {
|
if len(requestBody) < offset+2 {
|
||||||
@@ -1623,35 +1496,15 @@ func (h *Handler) handleDeleteTopics(correlationID uint32, requestBody []byte) (
|
|||||||
var errorCode uint16 = 0
|
var errorCode uint16 = 0
|
||||||
var errorMessage string = ""
|
var errorMessage string = ""
|
||||||
|
|
||||||
if h.useSeaweedMQ {
|
// Use SeaweedMQ integration
|
||||||
// Use SeaweedMQ integration
|
if !h.seaweedMQHandler.TopicExists(topicName) {
|
||||||
if !h.seaweedMQHandler.TopicExists(topicName) {
|
errorCode = 3 // UNKNOWN_TOPIC_OR_PARTITION
|
||||||
errorCode = 3 // UNKNOWN_TOPIC_OR_PARTITION
|
errorMessage = "Unknown topic"
|
||||||
errorMessage = "Unknown topic"
|
|
||||||
} else {
|
|
||||||
// Delete the topic from SeaweedMQ
|
|
||||||
if err := h.seaweedMQHandler.DeleteTopic(topicName); err != nil {
|
|
||||||
errorCode = 1 // UNKNOWN_SERVER_ERROR
|
|
||||||
errorMessage = err.Error()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Use legacy in-memory mode
|
// Delete the topic from SeaweedMQ
|
||||||
topicInfo, exists := h.topics[topicName]
|
if err := h.seaweedMQHandler.DeleteTopic(topicName); err != nil {
|
||||||
if !exists {
|
errorCode = 1 // UNKNOWN_SERVER_ERROR
|
||||||
errorCode = 3 // UNKNOWN_TOPIC_OR_PARTITION
|
errorMessage = err.Error()
|
||||||
errorMessage = "Unknown topic"
|
|
||||||
} else {
|
|
||||||
// Delete the topic
|
|
||||||
delete(h.topics, topicName)
|
|
||||||
|
|
||||||
// Clean up associated ledgers
|
|
||||||
h.ledgersMu.Lock()
|
|
||||||
for partitionID := int32(0); partitionID < topicInfo.Partitions; partitionID++ {
|
|
||||||
key := TopicPartitionKey{Topic: topicName, Partition: partitionID}
|
|
||||||
delete(h.ledgers, key)
|
|
||||||
}
|
|
||||||
h.ledgersMu.Unlock()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1779,26 +1632,6 @@ func getAPIName(apiKey uint16) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddTopicForTesting adds a topic directly to the handler (for testing only)
|
|
||||||
func (h *Handler) AddTopicForTesting(topicName string, partitions int32) {
|
|
||||||
h.topicsMu.Lock()
|
|
||||||
defer h.topicsMu.Unlock()
|
|
||||||
|
|
||||||
if _, exists := h.topics[topicName]; !exists {
|
|
||||||
h.topics[topicName] = &TopicInfo{
|
|
||||||
Name: topicName,
|
|
||||||
Partitions: partitions,
|
|
||||||
CreatedAt: time.Now().UnixNano(),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize ledgers for all partitions
|
|
||||||
for partitionID := int32(0); partitionID < partitions; partitionID++ {
|
|
||||||
h.GetOrCreateLedger(topicName, partitionID)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnableSchemaManagement enables schema management with the given configuration
|
// EnableSchemaManagement enables schema management with the given configuration
|
||||||
func (h *Handler) EnableSchemaManagement(config schema.ManagerConfig) error {
|
func (h *Handler) EnableSchemaManagement(config schema.ManagerConfig) error {
|
||||||
manager, err := schema.NewManagerWithHealthCheck(config)
|
manager, err := schema.NewManagerWithHealthCheck(config)
|
||||||
|
|||||||
@@ -881,17 +881,11 @@ func (h *Handler) getTopicPartitions(group *consumer.ConsumerGroup) map[string][
|
|||||||
|
|
||||||
// Get partition info for all subscribed topics
|
// Get partition info for all subscribed topics
|
||||||
for topic := range group.SubscribedTopics {
|
for topic := range group.SubscribedTopics {
|
||||||
// Check if topic exists in our topic registry
|
// Check if topic exists using SeaweedMQ handler
|
||||||
h.topicsMu.RLock()
|
if h.seaweedMQHandler.TopicExists(topic) {
|
||||||
topicInfo, exists := h.topics[topic]
|
// For now, assume 1 partition per topic (can be extended later)
|
||||||
h.topicsMu.RUnlock()
|
// In a real implementation, this would query SeaweedMQ for actual partition count
|
||||||
|
partitions := []int32{0}
|
||||||
if exists {
|
|
||||||
// Create partition list for this topic
|
|
||||||
partitions := make([]int32, topicInfo.Partitions)
|
|
||||||
for i := int32(0); i < topicInfo.Partitions; i++ {
|
|
||||||
partitions[i] = i
|
|
||||||
}
|
|
||||||
topicPartitions[topic] = partitions
|
topicPartitions[topic] = partitions
|
||||||
} else {
|
} else {
|
||||||
// Default to single partition if topic not found
|
// Default to single partition if topic not found
|
||||||
@@ -958,12 +952,5 @@ func (h *Handler) serializeMemberAssignment(assignments []consumer.PartitionAssi
|
|||||||
|
|
||||||
// getAvailableTopics returns list of available topics for subscription metadata
|
// getAvailableTopics returns list of available topics for subscription metadata
|
||||||
func (h *Handler) getAvailableTopics() []string {
|
func (h *Handler) getAvailableTopics() []string {
|
||||||
h.topicsMu.RLock()
|
return h.seaweedMQHandler.ListTopics()
|
||||||
defer h.topicsMu.RUnlock()
|
|
||||||
|
|
||||||
topics := make([]string, 0, len(h.topics))
|
|
||||||
for topicName := range h.topics {
|
|
||||||
topics = append(topics, topicName)
|
|
||||||
}
|
|
||||||
return topics
|
|
||||||
}
|
}
|
||||||
@@ -11,9 +11,9 @@ func TestOffsetCommitHandlerIntegration(t *testing.T) {
|
|||||||
|
|
||||||
// Test ConsumerOffsetKey creation
|
// Test ConsumerOffsetKey creation
|
||||||
key := offset.ConsumerOffsetKey{
|
key := offset.ConsumerOffsetKey{
|
||||||
Topic: "test-topic",
|
Topic: "test-topic",
|
||||||
Partition: 0,
|
Partition: 0,
|
||||||
ConsumerGroup: "test-group",
|
ConsumerGroup: "test-group",
|
||||||
ConsumerGroupInstance: "test-instance",
|
ConsumerGroupInstance: "test-instance",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,7 +37,6 @@ func TestOffsetCommitHandlerIntegration(t *testing.T) {
|
|||||||
func TestOffsetCommitToSMQ_WithoutStorage(t *testing.T) {
|
func TestOffsetCommitToSMQ_WithoutStorage(t *testing.T) {
|
||||||
// Test error handling when SMQ storage is not initialized
|
// Test error handling when SMQ storage is not initialized
|
||||||
handler := &Handler{
|
handler := &Handler{
|
||||||
useSeaweedMQ: true,
|
|
||||||
smqOffsetStorage: nil, // Not initialized
|
smqOffsetStorage: nil, // Not initialized
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,7 +60,6 @@ func TestOffsetCommitToSMQ_WithoutStorage(t *testing.T) {
|
|||||||
func TestFetchOffsetFromSMQ_WithoutStorage(t *testing.T) {
|
func TestFetchOffsetFromSMQ_WithoutStorage(t *testing.T) {
|
||||||
// Test error handling when SMQ storage is not initialized
|
// Test error handling when SMQ storage is not initialized
|
||||||
handler := &Handler{
|
handler := &Handler{
|
||||||
useSeaweedMQ: true,
|
|
||||||
smqOffsetStorage: nil, // Not initialized
|
smqOffsetStorage: nil, // Not initialized
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,7 +85,7 @@ func TestFetchOffsetFromSMQ_WithoutStorage(t *testing.T) {
|
|||||||
|
|
||||||
func TestOffsetHandlers_StructureValidation(t *testing.T) {
|
func TestOffsetHandlers_StructureValidation(t *testing.T) {
|
||||||
// Validate that offset commit/fetch request/response structures are properly formed
|
// Validate that offset commit/fetch request/response structures are properly formed
|
||||||
|
|
||||||
// Test OffsetCommitRequest structure
|
// Test OffsetCommitRequest structure
|
||||||
request := OffsetCommitRequest{
|
request := OffsetCommitRequest{
|
||||||
GroupID: "test-group",
|
GroupID: "test-group",
|
||||||
@@ -135,9 +133,9 @@ func TestOffsetHandlers_StructureValidation(t *testing.T) {
|
|||||||
t.Errorf("Expected offset 100, got %d", partition.Offset)
|
t.Errorf("Expected offset 100, got %d", partition.Offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test OffsetFetchRequest structure
|
// Test OffsetFetchRequest structure
|
||||||
fetchRequest := OffsetFetchRequest{
|
fetchRequest := OffsetFetchRequest{
|
||||||
GroupID: "test-group",
|
GroupID: "test-group",
|
||||||
GroupInstanceID: "test-instance",
|
GroupInstanceID: "test-instance",
|
||||||
Topics: []OffsetFetchTopic{
|
Topics: []OffsetFetchTopic{
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ func (h *Handler) handleOffsetCommit(correlationID uint32, requestBody []byte) (
|
|||||||
|
|
||||||
// Commit offset using SMQ storage if available
|
// Commit offset using SMQ storage if available
|
||||||
var errorCode int16 = ErrorCodeNone
|
var errorCode int16 = ErrorCodeNone
|
||||||
if h.useSeaweedMQ && h.smqOffsetStorage != nil {
|
if h.seaweedMQHandler != nil && h.smqOffsetStorage != nil {
|
||||||
if err := h.commitOffsetToSMQ(key, partition.Offset, partition.Metadata); err != nil {
|
if err := h.commitOffsetToSMQ(key, partition.Offset, partition.Metadata); err != nil {
|
||||||
errorCode = ErrorCodeOffsetMetadataTooLarge
|
errorCode = ErrorCodeOffsetMetadataTooLarge
|
||||||
}
|
}
|
||||||
@@ -240,7 +240,7 @@ func (h *Handler) handleOffsetFetch(correlationID uint32, requestBody []byte) ([
|
|||||||
var errorCode int16 = ErrorCodeNone
|
var errorCode int16 = ErrorCodeNone
|
||||||
|
|
||||||
// Fetch offset using SMQ storage if available
|
// Fetch offset using SMQ storage if available
|
||||||
if h.useSeaweedMQ && h.smqOffsetStorage != nil {
|
if h.seaweedMQHandler != nil && h.smqOffsetStorage != nil {
|
||||||
if offset, meta, err := h.fetchOffsetFromSMQ(key); err == nil {
|
if offset, meta, err := h.fetchOffsetFromSMQ(key); err == nil {
|
||||||
fetchedOffset = offset
|
fetchedOffset = offset
|
||||||
metadata = meta
|
metadata = meta
|
||||||
|
|||||||
@@ -105,28 +105,22 @@ func (h *Handler) handleProduceV0V1(correlationID uint32, apiVersion uint16, req
|
|||||||
fmt.Printf("DEBUG: Produce request for topic '%s' (%d partitions)\n", topicName, partitionsCount)
|
fmt.Printf("DEBUG: Produce request for topic '%s' (%d partitions)\n", topicName, partitionsCount)
|
||||||
|
|
||||||
// Check if topic exists, auto-create if it doesn't (simulates auto.create.topics.enable=true)
|
// Check if topic exists, auto-create if it doesn't (simulates auto.create.topics.enable=true)
|
||||||
h.topicsMu.Lock()
|
topicExists := h.seaweedMQHandler.TopicExists(topicName)
|
||||||
_, topicExists := h.topics[topicName]
|
|
||||||
|
|
||||||
// Debug: show all existing topics
|
// Debug: show all existing topics
|
||||||
existingTopics := make([]string, 0, len(h.topics))
|
existingTopics := h.seaweedMQHandler.ListTopics()
|
||||||
for tName := range h.topics {
|
|
||||||
existingTopics = append(existingTopics, tName)
|
|
||||||
}
|
|
||||||
fmt.Printf("DEBUG: Topic exists check: '%s' -> %v (existing topics: %v)\n", topicName, topicExists, existingTopics)
|
fmt.Printf("DEBUG: Topic exists check: '%s' -> %v (existing topics: %v)\n", topicName, topicExists, existingTopics)
|
||||||
if !topicExists {
|
if !topicExists {
|
||||||
fmt.Printf("DEBUG: Auto-creating topic during Produce: %s\n", topicName)
|
fmt.Printf("DEBUG: Auto-creating topic during Produce: %s\n", topicName)
|
||||||
h.topics[topicName] = &TopicInfo{
|
if err := h.seaweedMQHandler.CreateTopic(topicName, 1); err != nil {
|
||||||
Name: topicName,
|
fmt.Printf("DEBUG: Failed to auto-create topic '%s': %v\n", topicName, err)
|
||||||
Partitions: 1, // Default to 1 partition
|
} else {
|
||||||
CreatedAt: time.Now().UnixNano(),
|
// Initialize ledger for partition 0
|
||||||
|
h.GetOrCreateLedger(topicName, 0)
|
||||||
|
topicExists = true // CRITICAL FIX: Update the flag after creating the topic
|
||||||
|
fmt.Printf("DEBUG: Topic '%s' auto-created successfully, topicExists = %v\n", topicName, topicExists)
|
||||||
}
|
}
|
||||||
// Initialize ledger for partition 0
|
|
||||||
h.GetOrCreateLedger(topicName, 0)
|
|
||||||
topicExists = true // CRITICAL FIX: Update the flag after creating the topic
|
|
||||||
fmt.Printf("DEBUG: Topic '%s' auto-created successfully, topicExists = %v\n", topicName, topicExists)
|
|
||||||
}
|
}
|
||||||
h.topicsMu.Unlock()
|
|
||||||
|
|
||||||
// Response: topic_name_size(2) + topic_name + partitions_array
|
// Response: topic_name_size(2) + topic_name + partitions_array
|
||||||
response = append(response, byte(topicNameSize>>8), byte(topicNameSize))
|
response = append(response, byte(topicNameSize>>8), byte(topicNameSize))
|
||||||
@@ -441,9 +435,7 @@ func (h *Handler) handleProduceV2Plus(correlationID uint32, apiVersion uint16, r
|
|||||||
currentTime := time.Now().UnixNano()
|
currentTime := time.Now().UnixNano()
|
||||||
|
|
||||||
// Check if topic exists; for v2+ do NOT auto-create
|
// Check if topic exists; for v2+ do NOT auto-create
|
||||||
h.topicsMu.RLock()
|
topicExists := h.seaweedMQHandler.TopicExists(topicName)
|
||||||
_, topicExists := h.topics[topicName]
|
|
||||||
h.topicsMu.RUnlock()
|
|
||||||
|
|
||||||
if !topicExists {
|
if !topicExists {
|
||||||
errorCode = 3 // UNKNOWN_TOPIC_OR_PARTITION
|
errorCode = 3 // UNKNOWN_TOPIC_OR_PARTITION
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package protocol
|
|||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestHandler_handleProduce(t *testing.T) {
|
func TestHandler_handleProduce(t *testing.T) {
|
||||||
@@ -11,11 +10,11 @@ func TestHandler_handleProduce(t *testing.T) {
|
|||||||
correlationID := uint32(333)
|
correlationID := uint32(333)
|
||||||
|
|
||||||
// First create a topic
|
// First create a topic
|
||||||
h.topics["test-topic"] = &TopicInfo{
|
// h.topics["test-topic"] = &TopicInfo{ // Commented out - now handled by SeaweedMQ handler
|
||||||
Name: "test-topic",
|
// Name: "test-topic",
|
||||||
Partitions: 1,
|
// Partitions: 1,
|
||||||
CreatedAt: time.Now().UnixNano(),
|
// CreatedAt: time.Now().UnixNano(),
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Build a simple Produce request with minimal record
|
// Build a simple Produce request with minimal record
|
||||||
clientID := "test-producer"
|
clientID := "test-producer"
|
||||||
@@ -188,11 +187,11 @@ func TestHandler_handleProduce_FireAndForget(t *testing.T) {
|
|||||||
correlationID := uint32(555)
|
correlationID := uint32(555)
|
||||||
|
|
||||||
// Create a topic
|
// Create a topic
|
||||||
h.topics["test-topic"] = &TopicInfo{
|
// h.topics["test-topic"] = &TopicInfo{ // Commented out - now handled by SeaweedMQ handler
|
||||||
Name: "test-topic",
|
// Name: "test-topic",
|
||||||
Partitions: 1,
|
// Partitions: 1,
|
||||||
CreatedAt: time.Now().UnixNano(),
|
// CreatedAt: time.Now().UnixNano(),
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Build Produce request with acks=0 (fire and forget)
|
// Build Produce request with acks=0 (fire and forget)
|
||||||
clientID := "test-producer"
|
clientID := "test-producer"
|
||||||
|
|||||||
Reference in new issue
Block a user