В Fri, 27 Nov 2009 20:49:16 +0300
Dmitry Banshchikov <[email protected]> пишет:
> Hello.
>
> Jabberd2 support virtual domains. But, this support is limited. I
> mean, that we can not support new domains without adding them into
> sm.xml and restarting session manager process(as far as I know).
> More than that, if we need to support many virtuail domains - it is
> become a problem(about 10K. I want to support many domains, because
> we have a lot of clients with a lot of domains(virtual hosting) and we
> want give everyone jabber support for domain.).
>
> The problem is that if we have about 10k domains in our sm.xml,
> then restarting time of jabberd is about 15 minutes. Bigger part of
> this time is consumpted by router process.
> I have made some tests and understand, that router spend its time for
> this code:
> router.c: 307 line
>
> /* tell the new component about everyone else */
> xhash_walk(comp->r->routes, _router_advertise_reverse, (void *)
> comp);
>
> I measure time for this call and number of elements in
> comp->r->routes. There are results:
> 1 elements is 0.000160000000
> 2 elements is 0.000610000000
> 3 elements is 0.000750000000
> 4 elements is 0.000430000000
> 5 elements is 0.000650000000
> 6 elements is 0.000540000000
>
> --- --- ---
> 62731 elements is 0.141400000000
> 62732 elements is 0.141670000000
> 62733 elements is 0.143430000000
> 62734 elements is 0.141510000000
> 62735 elements is 0.141460000000
>
> We can see, that this time consumption is significant.
> More than that, the problem is also that really we don't need to
> support all of this domains, but only those, for which we have
> accounts.
>
> I have writen patch, which solve this problem(I believe).
> The idea is that we set handler for SIGALRM signal and every rldtime
> secs(new directive in sm.xml) get distinct lists of realms from
> authreg table. Than we check every realm in sm->avail list and if we
> have no record for this domain - we dynamically add it. Patch need
> some additional function with storage modules, but I think, this
> function which allow us to generate customize querys to data backend
> is helpful.
>
>
>
Sorry. Here is the patch.
--
Dmitry Banshchikov
--- sm/main.c 2009-07-06 01:54:15.000000000 +0400
+++ sm/main.c 2009-11-27 17:05:09.000000000 +0300
@@ -117,6 +117,8 @@
sm->retry_lost = j_atoi(config_get_one(sm->config, "router.retry.lost", 0), 3);
if((sm->retry_sleep = j_atoi(config_get_one(sm->config, "router.retry.sleep", 0), 2)) < 1)
sm->retry_sleep = 1;
+ if((sm->rldtime = j_atoi(config_get_one(sm->config, "router.rldtime", 0), 60)) < 1)
+ sm->rldtime = 1;
sm->log_type = log_STDOUT;
if(config_get(sm->config, "log") != NULL) {
@@ -167,6 +169,45 @@
}
}
+static void _sm_hosts_new(int signo) {
+ int nsec, ns;
+ os_t os;
+ os_object_t o;
+ char *str;
+ nad_t nad;
+
+ nsec = sm->rldtime;
+
+ if (alarm(nsec) != 0) {
+ log_write(sm->log, LOG_NOTICE, "previous alarm timer was set");
+ }
+ if (storage_get_custom(sm->st, "distinct(realm)", "authreg", "1 = 1", &os) == st_SUCCESS) {
+ if (os_iter_first(os))
+ do {
+ o = os_iter_object(os);
+
+ if (os_object_get_str(os, o, "realm", &str)) {
+ /* Check this domain already configured */
+ if ( (xhash_get(sm->hosts, str) == NULL)) {
+ xhash_put(sm->hosts, pstrdup(xhash_pool(sm->hosts), str), sm);
+ nad = nad_new();
+ ns = nad_add_namespace(nad, uri_COMPONENT, NULL);
+ nad_append_elem(nad, ns, "bind", 0);
+ nad_append_attr(nad, -1, "name", str);
+ nad_append_attr(nad, -1, "multi", "to");
+ sx_nad_write(sm->router, nad);
+ log_write(sm->log, LOG_NOTICE, "[%s] configured", str);
+ }
+
+ }
+
+ } while (os_iter_next(os));
+
+ }
+
+
+}
+
static int _sm_router_connect(sm_t sm) {
log_write(sm->log, LOG_NOTICE, "attempting connection to router at %s, port=%d", sm->router_ip, sm->router_port);
@@ -218,6 +259,7 @@
jabber_signal(SIGINT, _sm_signal);
jabber_signal(SIGTERM, _sm_signal);
+ jabber_signal(SIGALRM, _sm_hosts_new);
#ifdef SIGHUP
jabber_signal(SIGHUP, _sm_signal_hup);
#endif
@@ -359,6 +401,8 @@
sm->retry_left = sm->retry_init;
_sm_router_connect(sm);
+ _sm_hosts_new(SIGALRM);
+
while(!sm_shutdown) {
mio_run(sm->mio, 5);
--- sm/sm.h 2009-07-06 01:54:15.000000000 +0400
+++ sm/sm.h 2009-11-27 15:34:34.000000000 +0300
@@ -203,6 +203,8 @@
int retry_sleep; /**< sleep interval between retries */
int retry_left; /**< number of tries left before failure */
+ int rldtime; /**< number of seconds between searching new hosts */
+
storage_t st; /**< storage subsystem */
mm_t mm; /**< module subsystem */
@@ -606,6 +608,7 @@
st_ret_t (*put)(st_driver_t drv, const char *type, const char *owner, os_t os);
/** get handler */
st_ret_t (*get)(st_driver_t drv, const char *type, const char *owner, const char *filter, os_t *os);
+ st_ret_t (*get_custom)(st_driver_t drv, const char *what, const char *from, const char *cond, os_t *os);
/** count handler */
st_ret_t (*count)(st_driver_t drv, const char *type, const char *owner, const char *filter, int *count);
/** delete handler */
--- sm/storage.c 2009-07-06 01:54:15.000000000 +0400
+++ sm/storage.c 2009-11-27 16:08:41.000000000 +0300
@@ -241,6 +241,30 @@
return (drv->get)(drv, type, owner, filter, os);
}
+st_ret_t storage_get_custom(storage_t st, const char *what, const char *from, const char *cond, os_t *os) {
+ st_driver_t drv;
+ st_ret_t ret;
+
+ log_debug(ZONE, "storage_get_custom: what=%s from=%s cond=%s", what, from, cond);
+
+ /* find the handler for this type */
+ drv = xhash_get(st->types, from);
+ if(drv == NULL) {
+ drv = st->default_drv;
+ if (drv == NULL) {
+ log_debug(ZONE, "no driver associated with type, and no default driver");
+ return st_NOTIMPL;
+ }
+
+ ret = storage_add_type(st, drv->name, from);
+ if (ret != st_SUCCESS)
+ return ret;
+ }
+
+ return (drv->get_custom)(drv, what, from, cond, os);
+
+}
+
st_ret_t storage_count(storage_t st, const char *type, const char *owner, const char *filter, int *count) {
st_driver_t drv;
st_ret_t ret;
--- storage/storage_mysql.c 2009-07-06 01:54:16.000000000 +0400
+++ storage/storage_mysql.c 2009-11-27 16:33:24.000000000 +0300
@@ -425,6 +425,128 @@
return st_SUCCESS;
}
+static st_ret_t _st_mysql_get_custom(st_driver_t drv, const char *what, const char *from, const char *cond, os_t *os) {
+ drvdata_t data = (drvdata_t) drv->private;
+ char *buf = NULL;
+ int buflen = 0;
+ MYSQL_RES *res;
+ int ntuples, nfields, i, j;
+ MYSQL_FIELD *fields;
+ MYSQL_ROW tuple;
+ unsigned long *lengths;
+ os_object_t o;
+ char *val;
+ os_type_t ot;
+ int ival;
+ char tbuf[255];
+
+ if ( (mysql_ping(data->conn)) !=0) {
+ log_write(drv->st->sm->log, LOG_ERR, "mysql: connection to database lost");
+ return st_FAILED;
+ }
+
+ if (data->prefix != NULL) {
+ snprintf(tbuf, sizeof(tbuf), "%s%s", data->prefix, what);
+ }
+
+ MYSQL_SAFE(buf, strlen(what) + strlen(from) + strlen(cond) + 21, buflen)
+ sprintf(buf, "SELECT %s FROM %s WHERE %s", what, from, cond);
+
+ log_debug(ZONE, "prepared sql: %s", buf);
+
+ if (mysql_query(data->conn, buf) != 0) {
+ log_write(drv->st->sm->log, LOG_ERR, "mysql: sql select failed: %s", mysql_error(data->conn));
+ free(buf);
+ return st_FAILED;
+ }
+
+ free(buf);
+
+ res = mysql_store_result(data->conn);
+ if (res == NULL) {
+ log_write(drv->st->sm->log, LOG_ERR, "mysql: sql result retrieval failed: %s", mysql_error(data->conn));
+ return st_FAILED;
+ }
+
+ ntuples = mysql_num_rows(res);
+ if (ntuples == 0) {
+ mysql_free_result(res);
+ return st_NOTFOUND;
+ }
+
+ log_debug(ZONE, "%d tupes returned", ntuples);
+
+ nfields = mysql_num_fields(res);
+
+ if (nfields == 0) {
+ log_debug(ZONE, "weird, tuples were returned but no fields *shrug*");
+ mysql_free_result(res);
+ return st_NOTFOUND;
+ }
+
+ fields = mysql_fetch_fields(res);
+
+ *os = os_new();
+
+
+ for (i = 0; i < ntuples; i++) {
+ o = os_object_new(*os);
+
+ if ((tuple = mysql_fetch_row(res)) == NULL)
+ break;
+
+
+ for (j = 0; j < nfields; j++) {
+ if (tuple[j] == NULL)
+ continue;
+
+ lengths = mysql_fetch_lengths(res);
+ switch(fields[j].type) {
+ case FIELD_TYPE_TINY:
+ ot = os_type_BOOLEAN;
+ break;
+ case FIELD_TYPE_LONG:
+ ot = os_type_INTEGER;
+ break;
+ case FIELD_TYPE_BLOB:
+ case FIELD_TYPE_VAR_STRING:
+ ot = os_type_STRING;
+ break;
+ default:
+ log_debug(ZONE, "unknow filed type %d, ingoring it", fields[j].type);
+ continue;
+ }
+
+ val = tuple[j];
+
+ switch(ot) {
+ case os_type_BOOLEAN:
+ ival = (val[0] == '0') ? 0 : 1;
+ os_object_put(o, fields[j].name, &ival, ot);
+ break;
+
+ case os_type_INTEGER:
+ ival = atoi(val);
+ os_object_put(o, fields[j].name, &ival, ot);
+ break;
+ case os_type_STRING:
+ os_object_put(o, fields[j].name, val, os_type_STRING);
+ break;
+
+ case os_type_NAD:
+ case os_type_UNKNOWN:
+ break;
+
+ }
+ }
+ }
+
+ mysql_free_result(res);
+
+ return st_SUCCESS;
+
+}
+
static st_ret_t _st_mysql_count(st_driver_t drv, const char *type, const char *owner, const char *filter, int *count) {
drvdata_t data = (drvdata_t) drv->private;
char *cond, *buf = NULL;
@@ -630,6 +752,7 @@
drv->put = _st_mysql_put;
drv->count = _st_mysql_count;
drv->get = _st_mysql_get;
+ drv->get_custom = _st_mysql_get_custom;
drv->delete = _st_mysql_delete;
drv->replace = _st_mysql_replace;
drv->free = _st_mysql_free;