Skip to content

Performance Tips ​

This guide provides comprehensive performance optimization strategies for applications built with the ziegel library ecosystem.

Overview ​

Performance is a critical aspect of any application. The ziegel library is designed with performance in mind, but proper usage patterns and optimization techniques can significantly improve your application's performance.

Core Performance Principles ​

1. Memory Management ​

typescript
import { Disposable, using } from '@breadstone/ziegel-core';

// Use 'using' statement for automatic resource disposal
async function processLargeDataset(data: any[]): Promise<void> {
    using processor = new DataProcessor();

    for (const item of data) {
        await processor.process(item);
        // processor is automatically disposed when out of scope
    }
}

// Implement IDisposable for custom resources
export class DatabaseConnection implements Disposable {
    private connection: any;

    dispose(): void {
        if (this.connection) {
            this.connection.close();
            this.connection = null;
        }
    }
}

2. Object Pooling ​

typescript
import { ObjectPool } from '@breadstone/ziegel-core';

// Create pools for expensive objects
const bufferPool = new ObjectPool<Buffer>(() => Buffer.alloc(1024));

function processData(data: Uint8Array): ProcessedData {
    const buffer = bufferPool.get();

    try {
        // Use the buffer for processing
        buffer.write(data.toString());
        return processBuffer(buffer);
    } finally {
        bufferPool.return(buffer);
    }
}

3. Lazy Initialization ​

typescript
import { Lazy } from '@breadstone/ziegel-core';

export class ExpensiveService {
    private readonly _heavyResource = new Lazy<HeavyResource>(() => {
        console.log('Initializing heavy resource...');
        return new HeavyResource();
    });

    get resource(): HeavyResource {
        return this._heavyResource.value; // Only initialized when first accessed
    }
}

HTTP Performance Optimization ​

1. Connection Pooling ​

typescript
import { HttpClient } from '@breadstone/ziegel-platform-http';

// Configure HTTP client with connection pooling
const httpClient = new HttpClient({
    baseUrl: 'https://api.example.com',
    connectionPool: {
        maxConnections: 50,
        maxConnectionsPerHost: 10,
        keepAlive: true,
        keepAliveTimeout: 30000
    }
});

2. Request Batching ​

typescript
export class BatchingService {
    private readonly pendingRequests = new Map<string, any[]>();
    private readonly batchTimeout = 50; // milliseconds

    async getUsersBatch(userIds: string[]): Promise<User[]> {
        return new Promise((resolve, reject) => {
            const batchKey = 'users';

            if (!this.pendingRequests.has(batchKey)) {
                this.pendingRequests.set(batchKey, []);

                // Schedule batch execution
                setTimeout(() => this.executeBatch(batchKey), this.batchTimeout);
            }

            this.pendingRequests.get(batchKey)!.push({
                userIds,
                resolve,
                reject
            });
        });
    }

    private async executeBatch(batchKey: string): Promise<void> {
        const requests = this.pendingRequests.get(batchKey) || [];
        this.pendingRequests.delete(batchKey);

        if (requests.length === 0) return;

        try {
            // Combine all user IDs from all requests
            const allUserIds = requests.flatMap(req => req.userIds);
            const uniqueUserIds = [...new Set(allUserIds)];

            // Make single batch request
            const users = await this.httpClient.post('/api/users/batch', {
                userIds: uniqueUserIds
            });

            // Resolve all pending requests
            requests.forEach(request => {
                const userMap = new Map(users.data.map(u => [u.id, u]));
                const requestUsers = request.userIds.map(id => userMap.get(id));
                request.resolve(requestUsers);
            });
        } catch (error) {
            // Reject all pending requests
            requests.forEach(request => request.reject(error));
        }
    }
}

3. Response Compression ​

typescript
import { CompressionInterceptor } from '@breadstone/ziegel-platform-http';

const httpClient = new HttpClient({
    baseUrl: 'https://api.example.com'
});

// Add compression support
httpClient.addInterceptor(new CompressionInterceptor({
    threshold: 1024, // Compress responses larger than 1KB
    algorithms: ['gzip', 'deflate', 'br']
}));

Caching Strategies ​

1. Multi-Level Caching ​

typescript
import {
    MemoryCache,
    DistributedCache,
    CacheManager
} from '@breadstone/ziegel-platform-caching';

export class UserService {
    private readonly cacheManager: CacheManager;

    constructor() {
        this.cacheManager = new CacheManager()
            .addLevel(new MemoryCache({ maxSize: 1000, ttl: 300 })) // L1: Memory
            .addLevel(new DistributedCache({ ttl: 3600 })); // L2: Distributed
    }

    async getUser(id: string): Promise<User> {
        const cacheKey = `user:${id}`;

        // Try cache first
        let user = await this.cacheManager.get<User>(cacheKey);
        if (user) {
            return user;
        }

        // Load from database
        user = await this.userRepository.getById(id);

        // Cache the result
        await this.cacheManager.set(cacheKey, user);

        return user;
    }
}

2. Cache Warming ​

typescript
export class CacheWarmupService {
    constructor(
        private readonly userService: UserService,
        private readonly productService: ProductService,
        private readonly scheduler: IScheduler
    ) {}

    async warmupCaches(): Promise<void> {
        await Promise.all([
            this.warmupPopularUsers(),
            this.warmupFeaturedProducts(),
            this.warmupStaticContent()
        ]);
    }

    private async warmupPopularUsers(): Promise<void> {
        const popularUserIds = await this.analyticsService.getPopularUsers();

        await Promise.all(
            popularUserIds.map(id => this.userService.getUser(id))
        );
    }

    scheduleWarmup(): void {
        // Warm cache every hour
        this.scheduler.schedule('0 0 * * * *', () => this.warmupCaches());
    }
}

3. Cache Invalidation Patterns ​

typescript
export class SmartCacheService {
    private readonly cache: ICache;
    private readonly dependencyGraph = new Map<string, Set<string>>();

    async set(key: string, value: any, dependencies?: string[]): Promise<void> {
        await this.cache.set(key, value);

        if (dependencies) {
            dependencies.forEach(dep => {
                if (!this.dependencyGraph.has(dep)) {
                    this.dependencyGraph.set(dep, new Set());
                }
                this.dependencyGraph.get(dep)!.add(key);
            });
        }
    }

    async invalidate(key: string): Promise<void> {
        // Invalidate the key itself
        await this.cache.delete(key);

        // Invalidate all dependent keys
        const dependents = this.dependencyGraph.get(key);
        if (dependents) {
            await Promise.all(
                Array.from(dependents).map(dependent =>
                    this.invalidate(dependent)
                )
            );
            this.dependencyGraph.delete(key);
        }
    }
}

// Usage
const cacheService = new SmartCacheService();

// Cache user with dependencies
await cacheService.set('user:123', user, ['users', 'active-users']);

// Cache user's orders with dependencies
await cacheService.set('user:123:orders', orders, ['user:123', 'orders']);

// Invalidating 'user:123' will also invalidate 'user:123:orders'
await cacheService.invalidate('user:123');

Database Performance ​

1. Query Optimization ​

typescript
import { QueryBuilder, QueryOperator } from '@breadstone/ziegel-data';

export class OptimizedUserRepository {
    // Use projection to load only needed fields
    async getUserSummaries(): Promise<UserSummary[]> {
        return this.query()
            .select(['id', 'name', 'email', 'lastLoginAt'])
            .where('active', QueryOperator.Equals, true)
            .orderBy('lastLoginAt', 'desc')
            .take(100)
            .toArray();
    }

    // Use indexes for filtering
    async getActiveUsersByRole(role: string): Promise<User[]> {
        return this.query()
            .where('active', QueryOperator.Equals, true) // Indexed field first
            .and('role', QueryOperator.Equals, role)
            .useIndex('idx_active_role') // Hint for composite index
            .toArray();
    }

    // Batch operations for bulk updates
    async updateLastLoginBatch(userIds: string[]): Promise<void> {
        const now = DateTime.now();

        await this.batchUpdate(
            userIds.map(id => ({
                filter: { id },
                update: { lastLoginAt: now }
            }))
        );
    }
}

2. Connection Management ​

typescript
export class DatabaseConnectionManager {
    private readonly readPool: ConnectionPool;
    private readonly writePool: ConnectionPool;

    constructor() {
        this.readPool = new ConnectionPool({
            size: 20,
            timeout: 5000,
            readonly: true
        });

        this.writePool = new ConnectionPool({
            size: 5,
            timeout: 10000,
            readonly: false
        });
    }

    async executeQuery<T>(query: string, params?: any[]): Promise<T[]> {
        using connection = await this.readPool.acquire();
        return connection.query<T>(query, params);
    }

    async executeCommand(command: string, params?: any[]): Promise<void> {
        using connection = await this.writePool.acquire();
        return connection.execute(command, params);
    }
}

Reactive Patterns Performance ​

1. Operator Optimization ​

typescript
import { Observable, Subject } from '@breadstone/ziegel-rx';

export class OptimizedEventProcessor {
    private readonly eventSubject = new Subject<Event>();

    constructor() {
        this.setupOptimizedPipeline();
    }

    private setupOptimizedPipeline(): void {
        this.eventSubject
            .pipe(
                // Buffer events to reduce processing overhead
                bufferTime(100),
                filter(events => events.length > 0),

                // Use concatMap to maintain order while allowing concurrency
                concatMap(events => this.processBatch(events)),

                // Error handling to prevent stream termination
                catchError(error => {
                    console.error('Event processing error:', error);
                    return of([]); // Continue with empty result
                }),

                // Share the stream to avoid duplicate processing
                share()
            )
            .subscribe(results => {
                this.handleResults(results);
            });
    }

    private async processBatch(events: Event[]): Promise<ProcessedEvent[]> {
        // Group events by type for batch processing
        const eventGroups = groupBy(events, 'type');

        const processors = Object.entries(eventGroups).map(([type, groupEvents]) =>
            this.getProcessor(type).processBatch(groupEvents)
        );

        const results = await Promise.all(processors);
        return results.flat();
    }
}

2. Backpressure Management ​

typescript
export class BackpressureManager {
    private readonly maxBufferSize = 10000;
    private readonly currentBuffer: any[] = [];

    processStream<T>(source: Observable<T>): Observable<T> {
        return new Observable<T>(subscriber => {
            let isProcessing = false;

            const subscription = source.subscribe({
                next: async (value) => {
                    if (this.currentBuffer.length >= this.maxBufferSize) {
                        // Drop oldest items to prevent memory overflow
                        this.currentBuffer.splice(0, this.maxBufferSize * 0.1);
                    }

                    this.currentBuffer.push(value);

                    if (!isProcessing) {
                        isProcessing = true;
                        await this.drainBuffer(subscriber);
                        isProcessing = false;
                    }
                },
                error: (error) => subscriber.error(error),
                complete: () => subscriber.complete()
            });

            return () => subscription.unsubscribe();
        });
    }

    private async drainBuffer<T>(subscriber: any): Promise<void> {
        while (this.currentBuffer.length > 0) {
            const batch = this.currentBuffer.splice(0, 100);

            for (const item of batch) {
                subscriber.next(item);

                // Yield control to prevent blocking
                await new Promise(resolve => setImmediate(resolve));
            }
        }
    }
}

Bundle Optimization ​

1. Tree Shaking ​

typescript
// Import only what you need
import { Guid } from '@breadstone/ziegel-core/Guid';
import { DateTime } from '@breadstone/ziegel-core/DateTime';

// Instead of
// import { Guid, DateTime } from '@breadstone/ziegel-core';

2. Code Splitting ​

typescript
// Lazy load heavy modules
export class AnalyticsService {
    private analyticsModule: Promise<any> | null = null;

    private async getAnalyticsModule(): Promise<any> {
        if (!this.analyticsModule) {
            this.analyticsModule = import('@breadstone/ziegel-platform-analytics');
        }
        return this.analyticsModule;
    }

    async trackEvent(event: AnalyticsEvent): Promise<void> {
        const analytics = await this.getAnalyticsModule();
        return analytics.track(event);
    }
}

3. Asset Optimization ​

typescript
// webpack.config.js
module.exports = {
    optimization: {
        splitChunks: {
            chunks: 'all',
            cacheGroups: {
                ziegel: {
                    test: /[\\/]node_modules[\\/]@breadstone[\\/]ziegel/,
                    name: 'ziegel',
                    chunks: 'all',
                    priority: 20
                },
                vendor: {
                    test: /[\\/]node_modules[\\/]/,
                    name: 'vendors',
                    chunks: 'all',
                    priority: 10
                }
            }
        }
    }
};

Monitoring and Profiling ​

1. Performance Metrics ​

typescript
import { PerformanceMonitor } from '@breadstone/ziegel-platform-analytics';

export class UserService {
    private readonly monitor = new PerformanceMonitor();

    @monitor.measure('user-service.get-user')
    async getUser(id: string): Promise<User> {
        const timer = this.monitor.startTimer('database.query');

        try {
            const user = await this.userRepository.getById(id);
            timer.end({ success: true });
            return user;
        } catch (error) {
            timer.end({ success: false, error: error.message });
            throw error;
        }
    }

    @monitor.measure('user-service.search-users')
    async searchUsers(query: UserSearchQuery): Promise<PagedResult<User>> {
        const startTime = Date.now();

        try {
            const result = await this.userRepository.search(query);

            this.monitor.histogram('search.result.count', result.items.length);
            this.monitor.histogram('search.duration', Date.now() - startTime);

            return result;
        } catch (error) {
            this.monitor.counter('search.errors').increment();
            throw error;
        }
    }
}

2. Memory Profiling ​

typescript
export class MemoryProfiler {
    private snapshots: any[] = [];

    takeSnapshot(label: string): void {
        if (typeof process !== 'undefined' && process.memoryUsage) {
            const usage = process.memoryUsage();
            this.snapshots.push({
                label,
                timestamp: Date.now(),
                heapUsed: usage.heapUsed,
                heapTotal: usage.heapTotal,
                external: usage.external,
                rss: usage.rss
            });
        }
    }

    analyzeTrend(): MemoryTrend {
        if (this.snapshots.length < 2) {
            return { trend: 'stable', growth: 0 };
        }

        const recent = this.snapshots.slice(-5);
        const heapGrowth = recent[recent.length - 1].heapUsed - recent[0].heapUsed;
        const timeSpan = recent[recent.length - 1].timestamp - recent[0].timestamp;

        const growthRate = heapGrowth / timeSpan; // bytes per ms

        return {
            trend: growthRate > 0.1 ? 'growing' : growthRate < -0.1 ? 'shrinking' : 'stable',
            growth: growthRate
        };
    }

    getRecommendations(): string[] {
        const trend = this.analyzeTrend();
        const recommendations: string[] = [];

        if (trend.trend === 'growing') {
            recommendations.push('Consider implementing object pooling');
            recommendations.push('Review cache expiration policies');
            recommendations.push('Check for memory leaks in event listeners');
        }

        const latest = this.snapshots[this.snapshots.length - 1];
        if (latest && latest.heapUsed > 100 * 1024 * 1024) { // > 100MB
            recommendations.push('Heap usage is high, consider reducing memory footprint');
        }

        return recommendations;
    }
}

Best Practices Summary ​

1. General Performance Rules ​

  • Measure First: Always profile before optimizing
  • Optimize Bottlenecks: Focus on the most impactful improvements
  • Cache Wisely: Cache expensive operations, but avoid over-caching
  • Batch Operations: Combine multiple operations when possible
  • Use Appropriate Data Structures: Choose the right tool for the job

2. Async Operations ​

typescript
// Good: Parallel execution
const [users, orders, products] = await Promise.all([
    userService.getUsers(),
    orderService.getOrders(),
    productService.getProducts()
]);

// Bad: Sequential execution
const users = await userService.getUsers();
const orders = await orderService.getOrders();
const products = await productService.getProducts();

3. Memory Management ​

typescript
// Good: Dispose resources
using connection = await database.connect();
const results = await connection.query('SELECT * FROM users');
// connection automatically disposed

// Good: Clear references
let largeArray = generateLargeDataset();
processData(largeArray);
largeArray = null; // Allow GC

4. Event Handling ​

typescript
// Good: Remove event listeners
class ComponentManager {
    private subscription?: Subscription;

    onInit(): void {
        this.subscription = eventService.subscribe(this.handleEvent);
    }

    onDestroy(): void {
        this.subscription?.unsubscribe();
    }
}

5. HTTP Optimization ​

typescript
// Good: Reuse HTTP client instances
const httpClient = new HttpClient({ baseUrl: 'https://api.example.com' });

// Good: Use appropriate timeouts
const response = await httpClient.get('/data', {
    timeout: 5000,
    retries: 3
});

By following these performance optimization strategies, you can ensure your ziegel-based applications run efficiently and scale well under load. Remember to continuously monitor and profile your applications to identify new optimization opportunities.