README
¶
Gossip Examples - Comprehensive Guide
This guide provides extensive documentation for using Gossip in real-world applications.
Table of Contents
- Getting Started
- Basic Concepts
- Setting Up Your Project
- Real-World Examples
- Advanced Patterns
- Best Practices
- Common Pitfalls
Getting Started
Installation
go get github.com/seyedali-dev/gossip
Import in Your Code
import gossip "github.com/seyedali-dev/gossip/event"
Basic Concepts
1. Event Types
Event types are strongly-typed string constants that identify events. Never use raw strings.
// ✅ Good - Strongly typed
const (
UserCreated gossip.EventType = "user.created"
OrderPaid gossip.EventType = "order.paid"
)
// ❌ Bad - Raw strings prone to typos
bus.Publish(gossip.NewEvent("user.created", data))
Convention: Use hierarchical naming with dots: domain.entity.action
Examples:
auth.user.createdauth.login.successorder.payment.completedinventory.stock.depleted
2. Event Structure
Events contain:
- Type: Strongly-typed identifier
- Timestamp: Automatic creation time
- Data: Event-specific payload (any type)
- Metadata: Additional context (map)
event := gossip.NewEvent(UserCreated, &UserData{
UserID: "123",
Email: "user@example.com",
})
// Add metadata for context
event.WithMetadata("request_id", "req-abc-123")
event.WithMetadata("source", "api")
event.WithMetadata("priority", "high")
3. Handlers
Handlers are functions that process events:
func myHandler(ctx context.Context, event *gossip.Event) error {
// Type assert the data
data := event.Data.(*UserData)
// Process the event
log.Printf("Processing user: %s", data.UserID)
// Return error if processing fails
return nil
}
Key points:
- Always type-assert
event.Datato expected type - Return
errorif processing fails - Use
ctxfor cancellation/timeout - Handlers should be idempotent when possible
Setting Up Your Project
Step 1: Define Event Types
Create a dedicated file for event types:
// events/types.go
package events
import gossip "github.com/seyedali-dev/gossip/event"
const (
// User events
UserCreated gossip.EventType = "user.created"
UserUpdated gossip.EventType = "user.updated"
UserDeleted gossip.EventType = "user.deleted"
// Auth events
LoginSuccess gossip.EventType = "auth.login.success"
LoginFailed gossip.EventType = "auth.login.failed"
PasswordChanged gossip.EventType = "auth.password.changed"
// Order events
OrderCreated gossip.EventType = "order.created"
OrderPaid gossip.EventType = "order.paid"
OrderShipped gossip.EventType = "order.shipped"
)
Step 2: Define Event Data Structures
Create structs for event payloads:
// events/data.go
package events
type UserCreatedData struct {
UserID string
Email string
Username string
}
type LoginSuccessData struct {
UserID string
IPAddress string
UserAgent string
}
type OrderCreatedData struct {
OrderID string
CustomerID string
Amount float64
Items []string
}
Step 3: Initialize Event Bus
In your main application:
// main.go
package main
import (
"log"
gossip "github.com/seyedali-dev/gossip/event"
)
var eventBus *gossip.EventBus
func initEventBus() {
config := &gossip.Config{
Workers: 10, // Adjust based on load
BufferSize: 1000, // Adjust based on event volume
}
eventBus = gossip.NewEventBus(config)
log.Println("Event bus initialized")
}
func main() {
initEventBus()
defer eventBus.Shutdown()
// Register handlers
registerHandlers()
// Start your application
// ...
}
Step 4: Register Handlers
Create handler registration function:
// handlers/registration.go
package handlers
import (
"github.com/seyedali-dev/gossip"
"yourapp/events"
)
func RegisterHandlers(bus *gossip.EventBus) {
// User handlers
bus.Subscribe(events.UserCreated, EmailNotificationHandler)
bus.Subscribe(events.UserCreated, AuditLogHandler)
bus.Subscribe(events.UserCreated, MetricsHandler)
// Auth handlers
bus.Subscribe(events.LoginSuccess, SecurityHandler)
bus.Subscribe(events.LoginSuccess, AnalyticsHandler)
// Order handlers
bus.Subscribe(events.OrderCreated, InventoryHandler)
bus.Subscribe(events.OrderPaid, PaymentProcessorHandler)
}
Step 5: Publish Events in Business Logic
// services/user_service.go
package services
import (
"github.com/seyedali-dev/gossip"
"yourapp/events"
)
type UserService struct {
bus *gossip.EventBus
}
func (s *UserService) CreateUser(email, username string) error {
// Core business logic
userID := generateUserID()
// ... save to database ...
// Publish event
eventData := &events.UserCreatedData{
UserID: userID,
Email: email,
Username: username,
}
event := gossip.NewEvent(events.UserCreated, eventData).
WithMetadata("source", "api").
WithMetadata("request_id", getRequestID())
s.bus.Publish(event)
return nil
}
Real-World Examples
1. Authentication Service
File: examples/auth_service/main.go
Demonstrates:
- User registration with welcome emails
- Login tracking and notifications
- Password change alerts
- Audit logging for all auth events
- Security monitoring
Run:
cd examples/auth_service
go run main.go
Key Patterns:
- Multiple handlers for single event (fan-out)
- Middleware composition (retry, timeout, recovery)
- Metadata usage for request tracking
2. E-commerce Platform
File: examples/ecommerce/main.go
Demonstrates:
- Order creation and processing
- Batch email notifications
- Inventory management
- Conditional analytics (high-value orders)
- Payment processing pipeline
Run:
cd examples/ecommerce
go run main.go
Key Patterns:
- Batch processing for efficiency
- Event filtering based on conditions
- Priority-based processing
- Async inventory updates
3. Microservices Communication
File: examples/microservices/main.go
Demonstrates:
- Cross-service event communication
- Service-to-service decoupling
- Multiple services reacting to same event
- Event chaining (service publishes events other services consume)
Run:
cd examples/microservices
go run main.go
Key Patterns:
- Self-registering handlers in service constructors
- Shared event bus across services
- Event-driven service orchestration
Advanced Patterns
1. Middleware Composition
Chain multiple middleware for robust handling:
handler := gossip.Chain(
gossip.WithRecovery(), // Catch panics
gossip.WithRetry(3, 100*time.Millisecond), // Retry on failure
gossip.WithTimeout(5*time.Second), // Prevent hanging
gossip.WithLogging(), // Log execution
)(myHandler)
bus.Subscribe(UserCreated, handler)
Order matters: Place recovery first to catch all panics.
2. Conditional Processing with Filters
Only execute handlers when conditions are met:
// Only process high-priority events
highPriorityFilter := func(event *gossip.Event) bool {
priority, exists := event.Metadata["priority"]
return exists && priority == "high"
}
bus.Subscribe(OrderCreated, gossip.NewFilteredHandler(
highPriorityFilter,
urgentOrderHandler,
))
// Only process events from specific source
apiFilter := gossip.FilterByMetadata("source", "api")
bus.Subscribe(UserCreated, gossip.NewFilteredHandler(apiFilter, apiHandler))
// Combine filters with AND/OR logic
complexFilter := gossip.And(
gossip.FilterByMetadataExists("user_id"),
gossip.Or(
gossip.FilterByMetadata("source", "api"),
gossip.FilterByMetadata("source", "web"),
),
)
3. Batch Processing
Efficient processing of high-volume events:
batchConfig := gossip.BatchConfig{
BatchSize: 100, // Process 100 events at once
FlushPeriod: 5 * time.Second, // Or every 5 seconds
}
batchHandler := func(ctx context.Context, events []*gossip.Event) error {
// Process all events together
log.Printf("Processing batch of %d events", len(events))
// Bulk database insert, bulk email send, etc.
return bulkInsertToDatabase(events)
}
processor := gossip.NewBatchProcessor(OrderCreated, batchConfig, batchHandler)
defer processor.Shutdown()
bus.Subscribe(OrderCreated, processor.AsEventHandler())
When to use:
- Email notifications (batch send)
- Database inserts (bulk insert)
- API calls (batch requests)
- Metric aggregation
4. Synchronous Processing
When you need immediate results:
event := gossip.NewEvent(CriticalTransaction, data)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
errors := bus.PublishSync(ctx, event)
if len(errors) > 0 {
// Some handlers failed
log.Printf("Handler failures: %v", errors)
return handleFailure(errors)
}
log.Println("All handlers succeeded")
Use cases:
- Critical transactions requiring validation
- Rollback scenarios
- Immediate feedback needed
- Testing
5. Priority-Based Processing
Handle critical events first:
priorityQueue := gossip.NewPriorityQueue()
// Enqueue with priority
priorityQueue.Enqueue(criticalEvent, gossip.PriorityHigh)
priorityQueue.Enqueue(normalEvent, gossip.PriorityNormal)
priorityQueue.Enqueue(backgroundTask, gossip.PriorityLow)
// Dequeue processes highest priority first
event, ok := priorityQueue.Dequeue()
if ok {
bus.Publish(event)
}
Best Practices
1. Event Naming Conventions
✅ Do:
- Use hierarchical naming:
domain.entity.action - Be specific:
order.payment.completednotorder.updated - Use past tense:
user.creatednotuser.create - Group related events:
auth.login.*,order.payment.*
❌ Don't:
- Use generic names:
updated,changed - Mix tenses:
user.creating,order.created - Use abbreviations:
usr.crtd
2. Handler Design
✅ Do:
- Keep handlers small and focused
- Make handlers idempotent when possible
- Return errors for failures
- Use context for cancellation
- Log handler actions
❌ Don't:
- Block for long periods
- Panic in handlers (use middleware recovery)
- Modify shared state without locking
- Ignore errors
3. Error Handling
func myHandler(ctx context.Context, event *gossip.Event) error {
// Type assertion with check
data, ok := event.Data.(*UserData)
if !ok {
return fmt.Errorf("invalid event data type")
}
// Business logic with error handling
if err := processUser(data); err != nil {
return fmt.Errorf("failed to process user: %w", err)
}
return nil
}
4. Testing Handlers
func TestUserCreatedHandler(t *testing.T) {
// Create test event
event := gossip.NewEvent(UserCreated, &UserData{
UserID: "test-123",
Email: "test@example.com",
})
// Execute handler
err := myHandler(context.Background(), event)
// Assert results
assert.NoError(t, err)
assert.True(t, emailWasSent("test@example.com"))
}
5. Configuration Tuning
// Low volume (< 100 events/sec)
config := &gossip.Config{
Workers: 5,
BufferSize: 500,
}
// Medium volume (100-1000 events/sec)
config := &gossip.Config{
Workers: 10,
BufferSize: 1000,
}
// High volume (> 1000 events/sec)
config := &gossip.Config{
Workers: 20,
BufferSize: 5000,
}
Common Pitfalls
1. Type Assertion Panics
❌ Wrong:
data := event.Data.(*UserData) // Panics if wrong type
✅ Correct:
data, ok := event.Data.(*UserData)
if !ok {
return fmt.Errorf("invalid data type")
}
2. Blocking Handlers
❌ Wrong:
func slowHandler(ctx context.Context, event *gossip.Event) error {
time.Sleep(30 * time.Second) // Blocks worker
return nil
}
✅ Correct:
func fastHandler(ctx context.Context, event *gossip.Event) error {
// Offload heavy work
go processInBackground(event.Data)
return nil
}
3. Forgetting Shutdown
❌ Wrong:
func main() {
bus := gossip.NewEventBus(config)
// ... application code ...
// No cleanup!
}
✅ Correct:
func main() {
bus := gossip.NewEventBus(config)
defer bus.Shutdown() // Graceful cleanup
// ... application code ...
}
4. Circular Event Dependencies
❌ Wrong:
// Handler A publishes Event B
func handlerA(ctx context.Context, event *gossip.Event) error {
bus.Publish(gossip.NewEvent(EventB, nil))
return nil
}
// Handler B publishes Event A (circular!)
func handlerB(ctx context.Context, event *gossip.Event) error {
bus.Publish(gossip.NewEvent(EventA, nil))
return nil
}
✅ Correct:
- Avoid circular dependencies
- Use event metadata to track origin and prevent loops
- Design event flow as a DAG (Directed Acyclic Graph)
5. Overusing Synchronous Publishing
❌ Wrong:
// Using sync for everything defeats the purpose
for _, item := range items {
bus.PublishSync(ctx, event) // Slow!
}
✅ Correct:
// Use async for most cases
for _, item := range items {
bus.Publish(event) // Fast!
}
// Use sync only when necessary
criticalEvent := gossip.NewEvent(CriticalOp, data)
errors := bus.PublishSync(ctx, criticalEvent)
Performance Tips
- Tune worker count based on handler latency
- Use batch processing for high-volume events
- Implement filtering to avoid unnecessary work
- Monitor buffer fullness - increase if events are dropped
- Profile handler performance - optimize slow handlers
- Use middleware wisely - each layer adds overhead
Troubleshooting
Events Not Being Processed
- Check if handlers are registered before publishing
- Verify event type matches subscription
- Ensure bus hasn't been shutdown
- Check logs for handler errors
Slow Event Processing
- Profile handler execution time
- Check for blocking operations
- Increase worker count
- Use batch processing
Memory Issues
- Reduce buffer size
- Implement event filtering
- Fix handler memory leaks
- Monitor goroutine count
Additional Resources
- Main README - Library overview
- API Documentation
- GitHub Issues
Questions? Open an issue on GitHub or reach out to the community!
Directories
¶
| Path | Synopsis |
|---|---|
|
Package main.
|
Package main. |
|
Package main.
|
Package main. |
|
Package main.
|
Package main. |