Files
go-jdenticon/examples/concurrent-usage.go
Kevin McIntyre d9e84812ff Initial release: Go Jdenticon library v0.1.0
- Core library with SVG and PNG generation
- CLI tool with generate and batch commands
- Cross-platform path handling for Windows compatibility
- Comprehensive test suite with integration tests
2026-01-03 23:41:48 -05:00

252 lines
6.5 KiB
Go

// Package main demonstrates concurrent usage patterns for the go-jdenticon library.
// This example shows safe and efficient ways to generate identicons from multiple goroutines.
package main
import (
"context"
"fmt"
"log"
"sync"
"time"
"github.com/ungluedlabs/go-jdenticon/jdenticon"
)
func main() {
fmt.Println("Go Jdenticon - Concurrent Usage Examples")
fmt.Println("========================================")
// Example 1: Using package-level functions (simplest)
fmt.Println("\n1. Package-level functions (uses singleton generator)")
demonstratePackageLevelConcurrency()
// Example 2: Shared generator instance (recommended for performance)
fmt.Println("\n2. Shared generator instance (optimal performance)")
demonstrateSharedGenerator()
// Example 3: Cache performance monitoring
fmt.Println("\n3. Cache performance monitoring")
demonstrateCacheMonitoring()
// Example 4: High-throughput concurrent generation
fmt.Println("\n4. High-throughput concurrent generation")
demonstrateHighThroughput()
}
// demonstratePackageLevelConcurrency shows the simplest concurrent usage pattern
func demonstratePackageLevelConcurrency() {
userEmails := []string{
"alice@example.com",
"bob@example.com",
"charlie@example.com",
"diana@example.com",
"eve@example.com",
}
var wg sync.WaitGroup
for i, email := range userEmails {
wg.Add(1)
go func(id int, userEmail string) {
defer wg.Done()
// Safe: Package-level functions use internal singleton
icon, err := jdenticon.Generate(context.Background(), userEmail, 64)
if err != nil {
log.Printf("Worker %d failed to generate icon: %v", id, err)
return
}
// Icons are immutable and safe to use concurrently
svg, err := icon.ToSVG()
if err != nil {
log.Printf("Worker %d failed to generate SVG: %v", id, err)
return
}
fmt.Printf(" Worker %d: Generated %d-byte SVG for %s\n",
id, len(svg), userEmail)
}(i, email)
}
wg.Wait()
fmt.Println(" ✓ All workers completed successfully")
}
// demonstrateSharedGenerator shows optimal performance pattern with shared generator
func demonstrateSharedGenerator() {
// Create custom configuration
config, err := jdenticon.Configure(
jdenticon.WithColorSaturation(0.8),
jdenticon.WithPadding(0.1),
jdenticon.WithHueRestrictions([]float64{120, 180, 240}), // Blue/green theme
)
if err != nil {
log.Fatalf("Failed to create config: %v", err)
}
// Create generator with larger cache for concurrent workload
generator, err := jdenticon.NewGeneratorWithConfig(config, 1000)
if err != nil {
log.Fatalf("Failed to create generator: %v", err)
}
const numWorkers = 10
const iconsPerWorker = 5
var wg sync.WaitGroup
start := time.Now()
for workerID := 0; workerID < numWorkers; workerID++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
for i := 0; i < iconsPerWorker; i++ {
userID := fmt.Sprintf("user-%d-%d@company.com", id, i)
// Safe: Multiple goroutines can use the same generator
icon, err := generator.Generate(context.Background(), userID, 96)
if err != nil {
log.Printf("Worker %d failed to generate icon %d: %v", id, i, err)
continue
}
// Generate both formats concurrently on the same icon
var pngData []byte
var svgData string
var formatWg sync.WaitGroup
formatWg.Add(2)
go func() {
defer formatWg.Done()
pngData, _ = icon.ToPNG()
}()
go func() {
defer formatWg.Done()
svgData, _ = icon.ToSVG()
}()
formatWg.Wait()
fmt.Printf(" Worker %d: Generated PNG (%d bytes) and SVG (%d bytes) for %s\n",
id, len(pngData), len(svgData), userID)
}
}(workerID)
}
wg.Wait()
duration := time.Since(start)
totalIcons := numWorkers * iconsPerWorker
fmt.Printf(" ✓ Generated %d icons in %v (%.0f icons/sec)\n",
totalIcons, duration, float64(totalIcons)/duration.Seconds())
}
// demonstrateCacheMonitoring shows how to monitor cache performance
func demonstrateCacheMonitoring() {
generator, err := jdenticon.NewGeneratorWithConfig(jdenticon.DefaultConfig(), 100)
if err != nil {
log.Fatalf("Failed to create generator: %v", err)
}
// Generate some icons to populate cache
testUsers := []string{
"user1@test.com", "user2@test.com", "user3@test.com",
"user4@test.com", "user5@test.com",
}
var wg sync.WaitGroup
// First pass: populate cache
fmt.Println(" Populating cache...")
for _, user := range testUsers {
wg.Add(1)
go func(u string) {
defer wg.Done()
_, _ = generator.Generate(context.Background(), u, 64)
}(user)
}
wg.Wait()
hits1, misses1 := generator.GetCacheMetrics()
fmt.Printf(" After first pass - Hits: %d, Misses: %d\n", hits1, misses1)
// Second pass: should hit cache
fmt.Println(" Requesting same icons (should hit cache)...")
for _, user := range testUsers {
wg.Add(1)
go func(u string) {
defer wg.Done()
_, _ = generator.Generate(context.Background(), u, 64)
}(user)
}
wg.Wait()
hits2, misses2 := generator.GetCacheMetrics()
fmt.Printf(" After second pass - Hits: %d, Misses: %d\n", hits2, misses2)
if hits2 > hits1 {
ratio := float64(hits2) / float64(hits2+misses2) * 100
fmt.Printf(" ✓ Cache hit ratio: %.1f%%\n", ratio)
}
}
// demonstrateHighThroughput shows high-performance concurrent generation
func demonstrateHighThroughput() {
generator, err := jdenticon.NewGeneratorWithConfig(jdenticon.DefaultConfig(), 2000)
if err != nil {
log.Fatalf("Failed to create generator: %v", err)
}
const numWorkers = 20
const duration = 2 * time.Second
var wg sync.WaitGroup
stopChan := make(chan struct{})
// Start timer
go func() {
time.Sleep(duration)
close(stopChan)
}()
start := time.Now()
// Launch workers
for i := 0; i < numWorkers; i++ {
wg.Add(1)
go func(workerID int) {
defer wg.Done()
count := 0
for {
select {
case <-stopChan:
fmt.Printf(" Worker %d generated %d icons\n", workerID, count)
return
default:
userID := fmt.Sprintf("load-test-user-%d-%d", workerID, count)
_, err := generator.Generate(context.Background(), userID, 32)
if err != nil {
log.Printf("Generation failed: %v", err)
continue
}
count++
}
}
}(i)
}
wg.Wait()
actualDuration := time.Since(start)
hits, misses := generator.GetCacheMetrics()
total := hits + misses
throughput := float64(total) / actualDuration.Seconds()
fmt.Printf(" ✓ Generated %d icons in %v (%.0f icons/sec)\n",
total, actualDuration, throughput)
fmt.Printf(" ✓ Cache performance - Hits: %d, Misses: %d (%.1f%% hit rate)\n",
hits, misses, float64(hits)/float64(total)*100)
}