@markwylde/eventbase
Version:
A distributed, event-sourced, key-value database built on top of **NATS JetStream**. Eventbase provides a simple yet powerful API for storing, retrieving, and subscribing to data changes, with automatic state synchronization across distributed instances a
94 lines • 3.57 kB
JavaScript
import { EventEmitter } from 'events';
import { createEventbase } from './index.js';
export class EventbaseManager extends EventEmitter {
config;
instances = {};
dbPath;
nats;
keepAliveSeconds;
onMessage;
cleanupIntervalMs;
cleanupInterval = null;
getStatsStreamName;
constructor(config) {
super();
this.config = config;
const { dbPath, nats, keepAliveSeconds = 3600, // Default to 1 hour
onMessage, cleanupIntervalMs = 60000, // Default to 60 seconds
getStatsStreamName, } = config;
this.dbPath = dbPath;
this.nats = nats;
this.keepAliveSeconds = keepAliveSeconds;
this.onMessage = onMessage;
this.cleanupIntervalMs = cleanupIntervalMs;
this.getStatsStreamName = getStatsStreamName;
}
startCleanupInterval() {
if (this.cleanupInterval)
return;
this.cleanupInterval = setInterval(async () => {
const now = Date.now();
for (const [streamName, instanceOrPromise] of Object.entries(this.instances)) {
const instance = await instanceOrPromise;
const lastAccessed = instance.getLastAccessed();
const idleTime = now - lastAccessed;
const noActiveSubscriptions = instance.getActiveSubscriptions() === 0;
if (idleTime > this.keepAliveSeconds * 1000 && noActiveSubscriptions) {
try {
await instance.close();
delete this.instances[streamName];
// Emit 'stream:closed' event
this.emit('stream:closed', streamName);
}
catch (error) {
console.error(`Error closing stale instance ${streamName}:`, error);
}
}
}
}, this.cleanupIntervalMs);
}
stopCleanupInterval() {
if (this.cleanupInterval) {
clearInterval(this.cleanupInterval);
this.cleanupInterval = null;
}
}
async getStream(streamName) {
if (!this.instances[streamName]) {
this.instances[streamName] = createEventbase({
streamName,
statsStreamName: this.getStatsStreamName
? this.getStatsStreamName(streamName)
: undefined,
nats: this.nats,
dbPath: this.dbPath ? `${this.dbPath}/${streamName}` : undefined,
onMessage: this.onMessage,
});
this.startCleanupInterval();
// Emit 'stream:opened' event
this.emit('stream:opened', streamName);
}
const instance = await this.instances[streamName];
return instance;
}
async closeAll() {
this.stopCleanupInterval();
const closePromises = Object.entries(this.instances).map(async ([streamName, instanceOrPromise]) => {
try {
const instance = await instanceOrPromise;
await instance.close();
// Emit 'stream:closed' event
this.emit('stream:closed', streamName);
}
catch (error) {
console.error(`Error closing instance ${streamName}:`, error);
}
});
await Promise.all(closePromises);
this.instances = {};
}
}
export function createEventbaseManager(config) {
return new EventbaseManager(config);
}
//# sourceMappingURL=manager.js.map