roach-storm
Version:
Apache Kafka to Google Pub/Sub Gateway, API controlled
149 lines (148 loc) • 5.91 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const Debug = require("debug");
const debug = Debug("roach:roach");
const MongoWrapper_1 = require("./db/MongoWrapper");
const MongoPoller_1 = require("./db/MongoPoller");
const Discovery_1 = require("./kafka/Discovery");
const HttpServer_1 = require("./api/HttpServer");
const MessageHandler_1 = require("./MessageHandler");
const Consumer_1 = require("./kafka/Consumer");
const Producer_1 = require("./kafka/Producer");
const Metrics_1 = require("./Metrics");
const PubSubHandler_1 = require("./PubSubHandler");
const PubSubToKafka_1 = require("./PubSubToKafka");
const PubSubToMetrics_1 = require("./PubSubToMetrics");
const GRACE_EXIT_MS = 1250;
class RoachStorm {
constructor(config) {
this.alive = true;
this.ready = false;
if (!config || typeof config !== "object") {
throw new Error("Config must be an object: {kafka,discovery,mongo,http}");
}
this.config = config;
this.metrics = new Metrics_1.Metrics("roach", ["topic"], ["topic"]);
if (this.config.gcf && this.config.gcf.metrics && this.config.gcf.metrics.pubSubMetricTopic) {
this.gcfMetrics = new Metrics_1.Metrics(this.config.gcf.metrics.prefix || "gcf_roach", this.config.gcf.metrics.counterLabels, this.config.gcf.metrics.gaugeLabels);
this.pubSubToMetrics = new PubSubToMetrics_1.default(this);
}
else {
this.gcfMetrics = null;
this.pubSubToMetrics = null;
}
this.discovery = new Discovery_1.default(this.config.discovery, this.metrics);
this.mongoWrapper = new MongoWrapper_1.default(this.config.mongo, this);
this.mongoPoller = new MongoPoller_1.default(this.mongoWrapper, this.metrics);
this.httpServer = new HttpServer_1.default(this.config.http, this);
this.consumer = new Consumer_1.default(this.config.kafka, this);
this.producer = new Producer_1.default(this.config.kafka, this);
this.pubSubHandler = new PubSubHandler_1.default(this.config, this);
this.messageHandler = new MessageHandler_1.default(this);
this.pubSubToKafka = new PubSubToKafka_1.default(this);
}
shutdownOnErrorIfNotProduction() {
if (!RoachStorm.isProduction()) {
debug("Shutting down (because of error) in", GRACE_EXIT_MS, "ms");
this.close();
setTimeout(() => {
process.exit(1);
}, GRACE_EXIT_MS);
}
}
shutdownGracefully() {
debug("\nShutting down gracefully in", GRACE_EXIT_MS, "ms");
this.close();
debug("Bye..");
setTimeout(() => {
process.exit(0);
}, GRACE_EXIT_MS);
}
init() {
process.on("SIGINT", this.shutdownGracefully.bind(this));
process.on("SIGUSR1", this.shutdownGracefully.bind(this));
process.on("SIGUSR2", this.shutdownGracefully.bind(this));
process.on("uncaughtException", (error) => {
debug("Unhandled Exception: ", error.message, error.stack);
this.shutdownOnErrorIfNotProduction();
});
process.on("unhandledRejection", (reason, promise) => {
debug("Unhandled Rejection: ", reason);
this.shutdownOnErrorIfNotProduction();
});
if (RoachStorm.isProduction()) {
debug("Running production.");
}
else {
debug("Running NOT in production.");
}
}
async run() {
this.init();
debug("Starting..");
this.metrics.registerDefault();
// its okay to start these first, as consumer not subscribe to anything until the
// poller told him about the configured topics
// NOTE: this is necessary, because consumer requires connection before adjusting subscriptions
await this.mongoWrapper.start();
await this.consumer.start();
if (this.config.pubSubToKafkaTopicName) {
await this.producer.start();
}
this.mongoPoller.on("error", (error) => {
debug("MongoDB polling error: " + error.message, error.stack);
});
this.mongoPoller.on("topic-config-changed", (topics) => {
debug("Topic Configuration changed, adjusting subscription of consumer accordingly..", topics.length);
this.consumer.adjustSubscriptions(topics);
});
await this.discovery.start(this.consumer.getKafkaClient());
await this.mongoPoller.start(30000);
await this.httpServer.start();
if (this.config.pubSubToKafkaTopicName) {
await this.pubSubToKafka.start();
}
if (this.pubSubToMetrics) {
await this.pubSubToMetrics.start();
}
this.setReadyState(true);
debug("Running..");
}
async close() {
debug("Closing..");
this.setAliveState(false);
this.setReadyState(false);
await this.pubSubToKafka.close();
if (this.pubSubToMetrics) {
await this.pubSubToMetrics.close();
}
this.mongoPoller.close();
this.discovery.close();
this.httpServer.close();
await this.consumer.close();
await this.producer.close();
this.mongoWrapper.close();
this.metrics.close();
if (this.gcfMetrics) {
this.gcfMetrics.close();
}
}
static isProduction() {
return process.env.NODE_ENV === "production";
}
setAliveState(state) {
debug("Setting alive state from", this.alive, "to", state);
this.alive = state;
}
isAlive() {
return this.alive;
}
setReadyState(state) {
debug("Setting ready state from", this.ready, "to", state);
this.ready = state;
}
isReady() {
return this.ready;
}
}
exports.default = RoachStorm;