Skip to content

Commit e3fad66

Browse files
committed
enhance: simplify API & optimize logger suppport
Signed-off-by: Cloorc <wittcnezh@foxmail.com>
1 parent ebba5b7 commit e3fad66

File tree

14 files changed

+315
-213
lines changed

14 files changed

+315
-213
lines changed

api.go

Lines changed: 86 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,48 @@
11
package matcher
22

33
import (
4+
"context"
45
"crypto/rand"
56
"fmt"
67
"log/slog"
78
"os"
89
"time"
910
)
1011

12+
// logger is a package-level alias to the default slog logger. Use the
13+
// contextual logging helpers (InfoContext, WarnContext, ErrorContext,
14+
// DebugContext) when a context is available from the matcher instance.
15+
var logger = slog.Default()
16+
17+
func SetLogger(log *slog.Logger) {
18+
if log != nil {
19+
logger = log
20+
}
21+
}
22+
1123
// MatcherEngine provides a simple, high-level API for the rule matching system
1224
type MatcherEngine struct {
13-
matcher *InMemoryMatcher
25+
matcher *MemoryMatcherEngine
1426
persistence PersistenceInterface
1527
eventBroker Broker // Changed from eventSub to eventBroker
1628
nodeID string // Create forest index
1729
autoSaveStop chan bool
1830
}
1931

2032
// NewMatcherEngine creates a new matcher engine with the specified persistence and event broker
21-
func NewMatcherEngine(persistence PersistenceInterface, eventBroker Broker, nodeID string) (*MatcherEngine, error) {
22-
matcher, err := NewInMemoryMatcher(persistence, eventBroker, nodeID)
33+
// Note: ctx should not be cancelled in normal status
34+
func NewMatcherEngine(ctx context.Context, persistence PersistenceInterface, broker Broker, nodeID string, dcs *DimensionConfigs, initialTimeout time.Duration) (*MatcherEngine, error) {
35+
matcher, err := newInMemoryMatcherEngine(ctx, persistence, broker, nodeID, dcs, initialTimeout)
2336
if err != nil {
2437
return nil, fmt.Errorf("failed to create matcher: %w", err)
2538
}
2639

40+
logger.InfoContext(matcher.ctx, "created matcher engine", "node_id", nodeID)
41+
2742
return &MatcherEngine{
2843
matcher: matcher,
2944
persistence: persistence,
30-
eventBroker: eventBroker,
45+
eventBroker: broker,
3146
nodeID: nodeID,
3247
}, nil
3348
}
@@ -37,7 +52,8 @@ func NewMatcherEngineWithDefaults(dataDir string) (*MatcherEngine, error) {
3752
persistence := NewJSONPersistence(dataDir)
3853
// Generate a default node ID based on hostname or use a UUID
3954
nodeID := GenerateDefaultNodeID()
40-
return NewMatcherEngine(persistence, nil, nodeID)
55+
logger.InfoContext(context.Background(), "creating matcher engine with defaults", "data_dir", dataDir)
56+
return NewMatcherEngine(context.Background(), persistence, nil, nodeID, nil, 0)
4157
}
4258

4359
// RuleBuilder provides a fluent API for building rules
@@ -132,11 +148,13 @@ func (rb *RuleBuilder) Build() *Rule {
132148

133149
// AddRule adds a rule to the engine
134150
func (me *MatcherEngine) AddRule(rule *Rule) error {
151+
logger.InfoContext(me.matcher.ctx, "engine.AddRule", "rule_id", rule.ID)
135152
return me.matcher.AddRule(rule)
136153
}
137154

138155
// UpdateRule updates an existing rule
139156
func (me *MatcherEngine) UpdateRule(rule *Rule) error {
157+
logger.InfoContext(me.matcher.ctx, "engine.UpdateRule", "rule_id", rule.ID)
140158
return me.matcher.updateRule(rule)
141159
}
142160

@@ -202,9 +220,15 @@ func (me *MatcherEngine) GetRule(ruleID string) (*Rule, error) {
202220

203221
// DeleteRule removes a rule by ID
204222
func (me *MatcherEngine) DeleteRule(ruleID string) error {
223+
logger.InfoContext(me.matcher.ctx, "engine.DeleteRule", "rule_id", ruleID)
205224
return me.matcher.DeleteRule(ruleID)
206225
}
207226

227+
// GetDimensionConfigs returns deep cloned DimensionConfigs instance
228+
func (me *MatcherEngine) GetDimensionConfigs() *DimensionConfigs {
229+
return me.matcher.GetDimensionConfigs()
230+
}
231+
208232
// AddDimension adds a new dimension configuration
209233
func (me *MatcherEngine) AddDimension(config *DimensionConfig) error {
210234
return me.matcher.AddDimension(config)
@@ -218,6 +242,7 @@ func (me *MatcherEngine) SetAllowDuplicateWeights(allow bool) {
218242

219243
// FindBestMatch finds the best matching rule for a query
220244
func (me *MatcherEngine) FindBestMatch(query *QueryRule) (*MatchResult, error) {
245+
logger.DebugContext(me.matcher.ctx, "engine.FindBestMatch", "query", query.Values)
221246
return me.matcher.FindBestMatch(query)
222247
}
223248

@@ -226,21 +251,30 @@ func (me *MatcherEngine) FindBestMatch(query *QueryRule) (*MatchResult, error) {
226251
// atomic snapshot view for a group of queries with respect to concurrent
227252
// updates.
228253
func (me *MatcherEngine) FindBestMatchInBatch(queries ...*QueryRule) ([]*MatchResult, error) {
254+
logger.DebugContext(me.matcher.ctx, "engine.FindBestMatchInBatch", "num_queries", len(queries))
229255
return me.matcher.FindBestMatchInBatch(queries)
230256
}
231257

232258
// FindAllMatches finds all matching rules for a query
233259
func (me *MatcherEngine) FindAllMatches(query *QueryRule) ([]*MatchResult, error) {
260+
logger.DebugContext(me.matcher.ctx, "engine.FindAllMatches", "query", query.Values)
234261
return me.matcher.FindAllMatches(query)
235262
}
236263

237264
// FindAllMatches finds all matching rules for a query
238265
func (me *MatcherEngine) FindAllMatchesInBatch(query ...*QueryRule) ([][]*MatchResult, error) {
266+
logger.DebugContext(me.matcher.ctx, "engine.FindAllMatchesInBatch", "num_queries", len(query))
239267
return me.matcher.FindAllMatchesInBatch(query)
240268
}
241269

270+
// LoadDimensions loads all dimensions in bulk
271+
func (me *MatcherEngine) LoadDimensions(configs []*DimensionConfig) {
272+
me.matcher.LoadDimensions(configs)
273+
}
274+
242275
// ListRules returns all rules with pagination
243276
func (me *MatcherEngine) ListRules(offset, limit int) ([]*Rule, error) {
277+
logger.DebugContext(me.matcher.ctx, "engine.ListRules", "offset", offset, "limit", limit)
244278
return me.matcher.ListRules(offset, limit)
245279
}
246280

@@ -256,6 +290,7 @@ func (me *MatcherEngine) GetStats() *MatcherStats {
256290

257291
// Save saves the current state to persistence
258292
func (me *MatcherEngine) Save() error {
293+
logger.InfoContext(me.matcher.ctx, "engine.Save requested")
259294
return me.matcher.SaveToPersistence()
260295
}
261296

@@ -269,6 +304,7 @@ func (me *MatcherEngine) Close() error {
269304
if me.autoSaveStop != nil {
270305
close(me.autoSaveStop)
271306
}
307+
logger.InfoContext(me.matcher.ctx, "engine closing")
272308
return me.matcher.Close()
273309
}
274310

@@ -286,9 +322,10 @@ func (me *MatcherEngine) AutoSave(interval time.Duration) chan<- bool {
286322
case <-ticker.C:
287323
if err := me.Save(); err != nil {
288324
// Log error but continue
289-
slog.Error("Auto-save error", "error", err)
325+
logger.ErrorContext(me.matcher.ctx, "Auto-save error", "error", err)
290326
}
291327
case <-stopChan:
328+
logger.InfoContext(me.matcher.ctx, "autosave stopped")
292329
return
293330
}
294331
}
@@ -307,8 +344,6 @@ func (me *MatcherEngine) BatchAddRules(rules []*Rule) error {
307344
return nil
308345
}
309346

310-
// Convenience methods for quick rule creation
311-
312347
// AddSimpleRule creates a rule with all exact matches
313348
func (me *MatcherEngine) AddSimpleRule(id string, dimensions map[string]string, manualWeight *float64) error {
314349
builder := NewRule(id)
@@ -340,6 +375,49 @@ func (me *MatcherEngine) AddAnyRule(id string, dimensionNames []string, manualWe
340375
return me.AddRule(rule)
341376
}
342377

378+
// GetForestStats returns detailed forest index statistics
379+
func (me *MatcherEngine) GetForestStats() map[string]interface{} {
380+
me.matcher.mu.RLock()
381+
defer me.matcher.mu.RUnlock()
382+
383+
stats := make(map[string]interface{})
384+
385+
for key, forestIndex := range me.matcher.forestIndexes {
386+
stats[key] = forestIndex.GetStats()
387+
}
388+
389+
// Add summary stats
390+
stats["total_forests"] = len(me.matcher.forestIndexes)
391+
stats["total_rules"] = len(me.matcher.rules)
392+
393+
return stats
394+
}
395+
396+
// ClearCache clears the query cache
397+
func (me *MatcherEngine) ClearCache() {
398+
me.matcher.cache.Clear()
399+
}
400+
401+
// GetCacheStats returns cache statistics
402+
func (me *MatcherEngine) GetCacheStats() map[string]interface{} {
403+
return me.matcher.cache.Stats()
404+
}
405+
406+
// ValidateRule validates a rule before adding it
407+
func (me *MatcherEngine) ValidateRule(rule *Rule) error {
408+
return me.matcher.validateRule(rule)
409+
}
410+
411+
// Rebuild rebuilds the forest index (useful after bulk operations)
412+
func (me *MatcherEngine) Rebuild() error {
413+
// Use the existing Rebuild method which is already tenant-aware
414+
return me.matcher.Rebuild()
415+
}
416+
417+
func (me *MatcherEngine) SaveToPersistence() error {
418+
return me.matcher.SaveToPersistence()
419+
}
420+
343421
// CreateQuery creates a query from a map of dimension values
344422
func CreateQuery(values map[string]string) *QueryRule {
345423
return &QueryRule{
@@ -473,45 +551,6 @@ func CreateQueryWithAllRulesTenantAndExcluded(tenantID, applicationID string, va
473551
}
474552
}
475553

476-
// GetForestStats returns detailed forest index statistics
477-
func (me *MatcherEngine) GetForestStats() map[string]interface{} {
478-
me.matcher.mu.RLock()
479-
defer me.matcher.mu.RUnlock()
480-
481-
stats := make(map[string]interface{})
482-
483-
for key, forestIndex := range me.matcher.forestIndexes {
484-
stats[key] = forestIndex.GetStats()
485-
}
486-
487-
// Add summary stats
488-
stats["total_forests"] = len(me.matcher.forestIndexes)
489-
stats["total_rules"] = len(me.matcher.rules)
490-
491-
return stats
492-
}
493-
494-
// ClearCache clears the query cache
495-
func (me *MatcherEngine) ClearCache() {
496-
me.matcher.cache.Clear()
497-
}
498-
499-
// GetCacheStats returns cache statistics
500-
func (me *MatcherEngine) GetCacheStats() map[string]interface{} {
501-
return me.matcher.cache.Stats()
502-
}
503-
504-
// ValidateRule validates a rule before adding it
505-
func (me *MatcherEngine) ValidateRule(rule *Rule) error {
506-
return me.matcher.validateRule(rule)
507-
}
508-
509-
// RebuildIndex rebuilds the forest index (useful after bulk operations)
510-
func (me *MatcherEngine) RebuildIndex() error {
511-
// Use the existing Rebuild method which is already tenant-aware
512-
return me.matcher.Rebuild()
513-
}
514-
515554
// GenerateDefaultNodeID generates a default node ID based on hostname and random suffix
516555
func GenerateDefaultNodeID() string {
517556
hostname, err := os.Hostname()

api_test.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package matcher
22

33
import (
4+
"context"
45
"os"
56
"regexp"
67
"testing"
@@ -1407,7 +1408,7 @@ func TestDynamicConfigsIntegration(t *testing.T) {
14071408

14081409
func TestDeleteDimensionCoverage(t *testing.T) {
14091410
persistence := NewJSONPersistence("./test_data")
1410-
engine, err := NewInMemoryMatcher(persistence, nil, "delete-dim-test")
1411+
engine, err := NewMatcherEngine(context.Background(), persistence, nil, "delete-dim-test", nil, 0)
14111412
if err != nil {
14121413
t.Fatalf("Failed to create engine: %v", err)
14131414
}
@@ -1515,16 +1516,16 @@ func TestMatcherEngineFullAPIIntegration(t *testing.T) {
15151516
t.Errorf("Failed to add dimension: %v", err)
15161517
}
15171518

1518-
// Add a rule and test RebuildIndex
1519+
// Add a rule and test Rebuild
15191520
rule := NewRule("rebuild-test").
15201521
Dimension("region", "us-west", MatchTypeEqual).
15211522
Build()
15221523
if err := engine.AddRule(rule); err != nil {
15231524
t.Errorf("Failed to add rule: %v", err)
15241525
}
15251526

1526-
if err := engine.RebuildIndex(); err != nil {
1527-
t.Errorf("RebuildIndex failed: %v", err)
1527+
if err := engine.Rebuild(); err != nil {
1528+
t.Errorf("Rebuild failed: %v", err)
15281529
}
15291530
}
15301531

@@ -1655,7 +1656,7 @@ func TestTypesAPIIntegration(t *testing.T) {
16551656
func TestPublicUpdateAndGetRule(t *testing.T) {
16561657
// Create matcher with mock persistence
16571658
persistence := NewJSONPersistence("./test_data")
1658-
matcher, err := NewInMemoryMatcher(persistence, nil, "test-node-1")
1659+
matcher, err := NewMatcherEngine(context.Background(), persistence, nil, "test-node-1", nil, 0)
16591660
if err != nil {
16601661
t.Fatalf("Failed to create matcher: %v", err)
16611662
}
@@ -1743,7 +1744,7 @@ func TestPublicUpdateAndGetRule(t *testing.T) {
17431744
func TestPublicAPIImmutability(t *testing.T) {
17441745
// Create matcher with mock persistence
17451746
persistence := NewJSONPersistence("./test_data")
1746-
matcher, err := NewInMemoryMatcher(persistence, nil, "test-node-1")
1747+
matcher, err := NewMatcherEngine(context.Background(), persistence, nil, "test-node-1", nil, 0)
17471748
if err != nil {
17481749
t.Fatalf("Failed to create matcher: %v", err)
17491750
}

cmd/debug_matching/main.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package main
22

33
import (
4+
"context"
45
"fmt"
56
"log/slog"
67
"os"
@@ -12,11 +13,7 @@ func main() {
1213
fmt.Println("=== Debug Matching Test ===")
1314

1415
// Create engine
15-
engine, err := matcher.NewInMemoryMatcher(
16-
matcher.NewJSONPersistence("./debug_data"),
17-
nil,
18-
"debug-test",
19-
)
16+
engine, err := matcher.NewMatcherEngine(context.Background(), matcher.NewJSONPersistence("./debug_data"), nil, "debug-test", nil, 0)
2017
if err != nil {
2118
slog.Error("Failed to create matcher", "error", err)
2219
os.Exit(1)

cmd/performance_benchmark/main.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package main
22

33
import (
4+
"context"
45
"fmt"
56
"log/slog"
67
"math/rand"
@@ -113,11 +114,7 @@ func runPerformanceBenchmark(config BenchmarkConfig) (PerformanceResult, error)
113114
var setupStart = time.Now()
114115

115116
// Create matcher engine
116-
engine, err := matcher.NewInMemoryMatcher(
117-
matcher.NewJSONPersistence("./perf_test_data"),
118-
nil,
119-
"perf-benchmark",
120-
)
117+
engine, err := matcher.NewMatcherEngine(context.Background(), matcher.NewJSONPersistence("./perf_test_data"), nil, "perf-benchmark", nil, 0)
121118
if err != nil {
122119
return PerformanceResult{}, fmt.Errorf("failed to create matcher: %w", err)
123120
}
@@ -213,7 +210,7 @@ func runPerformanceBenchmark(config BenchmarkConfig) (PerformanceResult, error)
213210
}, nil
214211
}
215212

216-
func runConcurrentQueries(engine *matcher.InMemoryMatcher, queries []*matcher.QueryRule, config BenchmarkConfig) (int64, int64) {
213+
func runConcurrentQueries(engine *matcher.MatcherEngine, queries []*matcher.QueryRule, config BenchmarkConfig) (int64, int64) {
217214
var wg sync.WaitGroup
218215
var successCount, totalCount int64
219216
var mu sync.Mutex

0 commit comments

Comments
 (0)