polyv-live-cli
Version:
CLI tool for managing PolyV live streaming services.
355 lines • 12.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BatchRequestManager = void 0;
const events_1 = require("events");
class BatchRequestManager extends events_1.EventEmitter {
constructor(config = {}) {
super();
this.pendingRequests = new Map();
this.activeBatches = new Map();
this.batchTimers = new Map();
this.requestIdCounter = 0;
this.batchIdCounter = 0;
this.isRunning = false;
this.config = {
maxBatchSize: 20,
maxWaitTime: 1000,
minBatchInterval: 100,
enableIntelligentBatching: true,
maxConcurrentBatches: 5,
retryAttempts: 3,
retryDelay: 1000,
...config,
};
this.stats = {
totalBatches: 0,
totalRequests: 0,
averageBatchSize: 0,
averageProcessingTime: 0,
successRate: 0,
requestsPerSecond: 0,
pendingRequests: 0,
activeBatches: 0,
};
this.setupPeriodicBatchProcessing();
}
start() {
if (this.isRunning) {
return;
}
this.isRunning = true;
this.emit('started', {
timestamp: Date.now(),
config: this.config,
});
}
stop() {
if (!this.isRunning) {
return;
}
this.isRunning = false;
for (const timer of this.batchTimers.values()) {
clearTimeout(timer);
}
this.batchTimers.clear();
if (this.periodicProcessTimer) {
clearInterval(this.periodicProcessTimer);
this.periodicProcessTimer = undefined;
}
for (const request of this.pendingRequests.values()) {
request.reject(new Error('Batch request manager stopped'));
}
this.pendingRequests.clear();
this.emit('stopped', {
timestamp: Date.now(),
stats: this.getStats(),
});
}
addRequest(method, url, params = {}, body, options = {}) {
if (!this.isRunning) {
return Promise.reject(new Error('Batch request manager is not running'));
}
const requestId = this.generateRequestId();
const timestamp = Date.now();
return new Promise((resolve, reject) => {
const request = {
id: requestId,
method: method.toUpperCase(),
url,
params,
body,
priority: options.priority || 'medium',
timestamp,
resolve,
reject,
...(options.headers && { headers: options.headers }),
...(options.timeout && { timeout: options.timeout }),
};
this.pendingRequests.set(requestId, request);
this.stats.pendingRequests = this.pendingRequests.size;
this.emit('requestAdded', {
requestId,
method,
url,
priority: request.priority,
timestamp,
});
this.checkBatchProcessingConditions();
});
}
getStats() {
return { ...this.stats };
}
getPendingRequests() {
return Array.from(this.pendingRequests.values()).map(request => ({
id: request.id,
method: request.method,
url: request.url,
priority: request.priority,
timestamp: request.timestamp,
}));
}
async flushPendingRequests() {
if (this.pendingRequests.size === 0) {
return;
}
const requests = Array.from(this.pendingRequests.values());
this.pendingRequests.clear();
this.stats.pendingRequests = 0;
await this.processBatch(requests);
}
updateConfig(newConfig) {
this.config = { ...this.config, ...newConfig };
this.emit('configUpdated', {
config: this.config,
timestamp: Date.now(),
});
}
resetStats() {
this.stats = {
totalBatches: 0,
totalRequests: 0,
averageBatchSize: 0,
averageProcessingTime: 0,
successRate: 0,
requestsPerSecond: 0,
pendingRequests: this.pendingRequests.size,
activeBatches: this.activeBatches.size,
};
this.emit('statsReset', {
timestamp: Date.now(),
});
}
generateRequestId() {
return `req_${++this.requestIdCounter}_${Date.now()}`;
}
generateBatchId() {
return `batch_${++this.batchIdCounter}_${Date.now()}`;
}
checkBatchProcessingConditions() {
const pendingCount = this.pendingRequests.size;
const activeCount = this.activeBatches.size;
if (pendingCount >= this.config.maxBatchSize ||
activeCount < this.config.maxConcurrentBatches) {
this.scheduleImmediateBatchProcessing();
}
}
scheduleImmediateBatchProcessing() {
setTimeout(() => {
if (this.pendingRequests.size > 0) {
this.processNextBatch();
}
}, 0);
}
setupPeriodicBatchProcessing() {
const processInterval = Math.max(this.config.maxWaitTime, this.config.minBatchInterval);
this.periodicProcessTimer = setInterval(() => {
if (this.isRunning && this.pendingRequests.size > 0) {
this.processNextBatch();
}
}, processInterval);
}
async processNextBatch() {
if (this.activeBatches.size >= this.config.maxConcurrentBatches) {
return;
}
const batchRequests = this.selectRequestsForBatch();
if (batchRequests.length === 0) {
return;
}
for (const request of batchRequests) {
this.pendingRequests.delete(request.id);
}
this.stats.pendingRequests = this.pendingRequests.size;
await this.processBatch(batchRequests);
}
selectRequestsForBatch() {
const allRequests = Array.from(this.pendingRequests.values());
if (allRequests.length === 0) {
return [];
}
allRequests.sort((a, b) => {
const priorityOrder = { high: 0, medium: 1, low: 2 };
const priorityDiff = priorityOrder[a.priority] - priorityOrder[b.priority];
if (priorityDiff !== 0) {
return priorityDiff;
}
return a.timestamp - b.timestamp;
});
let batchRequests = [];
if (this.config.enableIntelligentBatching) {
batchRequests = this.selectIntelligentBatch(allRequests);
}
else {
batchRequests = allRequests.slice(0, this.config.maxBatchSize);
}
return batchRequests;
}
selectIntelligentBatch(requests) {
const batch = [];
const usedUrls = new Set();
const maxSize = this.config.maxBatchSize;
for (const request of requests) {
if (request.priority === 'high' && batch.length < maxSize) {
batch.push(request);
usedUrls.add(request.url);
}
}
for (const request of requests) {
if (batch.length >= maxSize) {
break;
}
if (batch.includes(request)) {
continue;
}
const hasSimilarRequest = batch.some(batchedRequest => batchedRequest.url === request.url ||
batchedRequest.method === request.method);
if (hasSimilarRequest || batch.length < maxSize / 2) {
batch.push(request);
usedUrls.add(request.url);
}
}
for (const request of requests) {
if (batch.length >= maxSize) {
break;
}
if (!batch.includes(request)) {
batch.push(request);
}
}
return batch;
}
async processBatch(requests) {
if (requests.length === 0) {
return;
}
const batchId = this.generateBatchId();
const startTime = Date.now();
this.activeBatches.set(batchId, requests);
this.stats.activeBatches = this.activeBatches.size;
this.emit('batchStarted', {
batchId,
requestCount: requests.length,
timestamp: startTime,
});
try {
const results = await this.executeBatch(batchId, requests);
const endTime = Date.now();
const processingTime = endTime - startTime;
this.updateBatchStats(requests.length, results, processingTime);
const batchResult = {
batchId,
requestCount: requests.length,
successCount: results.filter(r => r.success).length,
failureCount: results.filter(r => !r.success).length,
processingTime,
results,
};
this.emit('batchCompleted', {
batchId,
result: batchResult,
timestamp: endTime,
});
for (const result of results) {
const request = requests.find(r => r.id === result.requestId);
if (request) {
if (result.success) {
request.resolve(result.data);
}
else {
request.reject(result.error);
}
}
}
}
catch (error) {
this.emit('batchError', {
batchId,
error,
timestamp: Date.now(),
});
for (const request of requests) {
request.reject(error);
}
}
finally {
this.activeBatches.delete(batchId);
this.stats.activeBatches = this.activeBatches.size;
}
}
async executeBatch(_batchId, requests) {
const results = [];
const executeRequest = async (request) => {
const requestStartTime = Date.now();
try {
const response = await this.executeIndividualRequest(request);
const responseTime = Date.now() - requestStartTime;
results.push({
requestId: request.id,
success: true,
data: response,
responseTime,
});
}
catch (error) {
const responseTime = Date.now() - requestStartTime;
results.push({
requestId: request.id,
success: false,
error,
responseTime,
});
}
};
await Promise.all(requests.map(executeRequest));
return results;
}
async executeIndividualRequest(request) {
const delay = 50;
await new Promise(resolve => setTimeout(resolve, delay));
if (request.url.includes('simulate-failure')) {
throw new Error(`Request failed: ${request.method} ${request.url}`);
}
return {
id: request.id,
method: request.method,
url: request.url,
params: request.params,
body: request.body,
timestamp: Date.now(),
data: `Response for ${request.method} ${request.url}`,
};
}
updateBatchStats(requestCount, results, processingTime) {
this.stats.totalBatches++;
this.stats.totalRequests += requestCount;
this.stats.averageBatchSize = this.stats.totalRequests / this.stats.totalBatches;
this.stats.averageProcessingTime = ((this.stats.averageProcessingTime * (this.stats.totalBatches - 1)) + processingTime) / this.stats.totalBatches;
const successCount = results.filter(r => r.success).length;
const totalSuccessful = (this.stats.successRate * (this.stats.totalRequests - requestCount)) + successCount;
this.stats.successRate = totalSuccessful / this.stats.totalRequests;
this.stats.requestsPerSecond = requestCount / (processingTime / 1000);
}
}
exports.BatchRequestManager = BatchRequestManager;
//# sourceMappingURL=batch-request-manager.js.map