mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-09-16 03:20:50 +02:00
TopicExists can return a transient false-negative for a topic that is in fact present (a broker/filer blip under load, or a just-created topic whose existence cache is still stale). The metadata and produce handlers then tried to auto-create, hit "topic already exists", treated that as a failure, and dropped the topic from the Metadata response - so the client saw a spurious UNKNOWN_TOPIC_OR_PARTITION right after the topic was created. Return a sentinel ErrTopicAlreadyExists from the create paths and add ensureTopicExists, which treats it as confirmation the topic exists. It also folds in the repeated TopicExists -> invalidate -> recheck -> create logic shared by every metadata handler (v0-v8) and both produce paths. v2+ produce now auto-creates too, matching the auto.create.topics.enable=true behavior the rest of the gateway already simulates.
75 lines
2.6 KiB
Go
75 lines
2.6 KiB
Go
package protocol
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/mq/kafka/integration"
|
|
)
|
|
|
|
// autoCreateMockHandler models the flaky-existence scenario: TopicExists reports
|
|
// the topic missing (a transient broker/filer false-negative), while a create
|
|
// attempt reports the topic already exists. It reuses FastMockHandler for the
|
|
// rest of the interface and overrides only the two methods under test.
|
|
type autoCreateMockHandler struct {
|
|
*FastMockHandler
|
|
exists bool
|
|
createErr error
|
|
}
|
|
|
|
func (h *autoCreateMockHandler) TopicExists(name string) bool { return h.exists }
|
|
|
|
func (h *autoCreateMockHandler) CreateTopic(name string, partitions int32) error {
|
|
return h.createErr
|
|
}
|
|
|
|
// TestEnsureTopicExistsTreatsAlreadyExistsAsSuccess reproduces the offset-resumption
|
|
// flake: TopicExists returns a false-negative for a topic that is in fact present,
|
|
// so the auto-create attempt fails with ErrTopicAlreadyExists. The topic must still
|
|
// be reported as existing, otherwise the client sees UNKNOWN_TOPIC_OR_PARTITION.
|
|
func TestEnsureTopicExistsTreatsAlreadyExistsAsSuccess(t *testing.T) {
|
|
handler := &Handler{
|
|
seaweedMQHandler: &autoCreateMockHandler{
|
|
FastMockHandler: &FastMockHandler{},
|
|
exists: false,
|
|
createErr: fmt.Errorf("%s: %w", "offset-resumption", integration.ErrTopicAlreadyExists),
|
|
},
|
|
}
|
|
|
|
if !handler.ensureTopicExists("offset-resumption", 1) {
|
|
t.Fatal("ensureTopicExists must report true when the create attempt finds the topic already exists")
|
|
}
|
|
}
|
|
|
|
// TestEnsureTopicExistsReportsGenuineFailure confirms a real create failure (not
|
|
// "already exists") still results in the topic being treated as absent.
|
|
func TestEnsureTopicExistsReportsGenuineFailure(t *testing.T) {
|
|
handler := &Handler{
|
|
seaweedMQHandler: &autoCreateMockHandler{
|
|
FastMockHandler: &FastMockHandler{},
|
|
exists: false,
|
|
createErr: fmt.Errorf("broker unavailable"),
|
|
},
|
|
}
|
|
|
|
if handler.ensureTopicExists("missing-topic", 1) {
|
|
t.Fatal("ensureTopicExists must report false when the topic is absent and creation genuinely fails")
|
|
}
|
|
}
|
|
|
|
// TestEnsureTopicExistsWhenPresent covers the common path where the topic is
|
|
// already known to exist and no create attempt is needed.
|
|
func TestEnsureTopicExistsWhenPresent(t *testing.T) {
|
|
handler := &Handler{
|
|
seaweedMQHandler: &autoCreateMockHandler{
|
|
FastMockHandler: &FastMockHandler{},
|
|
exists: true,
|
|
createErr: fmt.Errorf("CreateTopic should not be called when the topic exists"),
|
|
},
|
|
}
|
|
|
|
if !handler.ensureTopicExists("present-topic", 1) {
|
|
t.Fatal("ensureTopicExists must report true for an existing topic")
|
|
}
|
|
}
|