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 GC4. 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.