[PATCH] [SQLBOX] Implement batch processing

Damián Viano <[email protected]>
Newsgroups gmane.comp.mobile.kannel.devel
Message-ID <[email protected]>
Hi:

Another one for sqlbox. This is based on
http://www.magicom-bcn.net/kannel/sqlbox-standalone-multi-20080227.patch by
Alejandro Guerrieri, but avoiding the situation where if we die in the middle
of a batch we lose messages. In this implementation worst case scenario we
repeat a message.

I've only implemented the batch processing for msyql, since that's what I use,
but everything is in place to implement the same for the rest of the db backends.

Also I think it would be ok to cleanup the single-message processing from
gw/sqlbox.c once every backend implement the batch processing (which is a lot
more performant and tunable, I could come up with some numbers if needed).

There is a small comment to make about the List *msgids parameter, please note
that this parameter is completely backend controlled to allow backends using
multiple keys or things even weirder, gw/sqlbox.c only cares about calling
gw_sql_batch_msg_done with the corresponding component from the list (which is
assumed is in the exact same order of the List *msgs parameter).

It's no biggie (note that around 50% of mysql_fetch_batch() in sqlbox_mysql.c
is the same msg processing of mysql_fetch_msg() [1]) but helps performance a great
deal.

diffstat sqlbox-0.7.2-batch_processing.patch
 sqlbox-cfg.def   |    1 
 sqlbox.c         |   30 ++++++++++++++++++++-
 sqlbox_mssql.c   |    2 +
 sqlbox_mysql.c   |   76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++  
 sqlbox_mysql.h   |    5 +++
 sqlbox_oracle.c  |    2 +
 sqlbox_pgsql.c   |    2 +
 sqlbox_sdb.c     |    2 +
 sqlbox_sql.h     |    4 ++
 sqlbox_sqlite.c  |    2 +
 sqlbox_sqlite3.c |    2 +
 11 files changed, 127 insertions(+), 1 deletion(-)


Comments, corrections, commits ;) welcome

Again, this diff is against current stable, let me know if it needs updating.

  Damián Viano(Des).

[1] Yeah, I know that phrase calls for a function but that wouldn't make sense
if we drop the single message processing code
sqlbox-0.7.2-batch_processing.patch (text/x-diff, 11 KB)
diff -Nura -x '.*' sqlbox-0.7.2/gw/sqlbox.c sqlbox-0.7.2_des/gw/sqlbox.c
--- sqlbox-0.7.2/gw/sqlbox.c	2009-05-19 12:08:35.000000000 -0300
+++ sqlbox-0.7.2_des/gw/sqlbox.c	2009-06-08 17:47:49.000000000 -0300
@@ -86,6 +86,7 @@
 static Octstr *box_allow_ip;
 static Octstr *box_deny_ip;
 static Octstr *global_sender;
+static long limit_per_cycle;
 
 #ifndef HAVE_MSSQL
 #ifndef HAVE_MYSQL
@@ -105,6 +106,7 @@
 Octstr *sqlbox_id;
 
 #define SLEEP_BETWEEN_SELECTS 1.0
+#define DEFAULT_LIMIT_PER_CYCLE 10
 
 typedef struct _boxc {
     Connection    *smsbox_connection;
@@ -528,6 +530,7 @@
     Boxc *boxc;
     int fd;
     Msg *msg;
+    List *msgs, *msgids;
 
     boxc = gw_malloc(sizeof(Boxc));
     boxc->bearerbox_connection = connect_to_bearerbox_real(bearerbox_host, bearerbox_port, bearerbox_port_ssl, NULL /* bb_our_host */);
@@ -545,8 +548,28 @@
 
     identify_to_bearerbox(boxc);
 
+    if (gw_sql_fetch_batch != NULL) {
+       msgs = gwlist_create();
+       msgids = gwlist_create();
+    }
+
     while (sqlbox_status == SQL_RUNNING) {
-        if ((msg = gw_sql_fetch_msg()) != NULL) {
+        if (gw_sql_fetch_batch != NULL) {
+            debug("sqlbox", 0, "sql_to_bearerbox: using batch processing, limit: %ld", limit_per_cycle);
+            if (gw_sql_fetch_batch(msgs, msgids, limit_per_cycle) == 0)
+                gwthread_sleep(SLEEP_BETWEEN_SELECTS);
+            while ((msg = gwlist_consume(msgs)) != NULL ) {
+                if (global_sender != NULL && (msg->sms.sender == NULL || octstr_len(msg->sms.sender) == 0)) {
+                    msg->sms.sender = octstr_duplicate(global_sender);
+                }
+                send_msg(boxc->bearerbox_connection, boxc, msg);
+                gw_sql_save_msg(msg, octstr_imm("MT"));
+                gw_sql_batch_msg_done(gwlist_consume(msgids));
+            }
+            gw_assert(gwlist_len(msgids) == 0);
+        }
+        else if (gw_sql_fetch_batch == NULL && (msg = gw_sql_fetch_msg()) != NULL) {
+            debug("sqlbox", 0, "sql_to_bearerbox: using single message processing, limit: %ld", limit_per_cycle);
             if (global_sender != NULL && (msg->sms.sender == NULL || octstr_len(msg->sms.sender) == 0)) {
                 msg->sms.sender = octstr_duplicate(global_sender);
             }
@@ -685,6 +708,11 @@
 
     if (cfg_get_integer(&sqlbox_port, grp, octstr_imm("smsbox-port")) == -1)
         sqlbox_port = 13005;
+
+    /* setup limit per cycle */
+    if (cfg_get_integer(&limit_per_cycle, grp, octstr_imm("limit-per-cycle")) == -1)
+        limit_per_cycle = DEFAULT_LIMIT_PER_CYCLE;
+
     /* setup logfile stuff */
     logfile = cfg_get(grp, octstr_imm("log-file"));
 
diff -Nura -x '.*' sqlbox-0.7.2/gw/sqlbox-cfg.def sqlbox-0.7.2_des/gw/sqlbox-cfg.def
--- sqlbox-0.7.2/gw/sqlbox-cfg.def	2008-11-03 17:33:15.000000000 -0200
+++ sqlbox-0.7.2_des/gw/sqlbox-cfg.def	2009-06-08 15:09:07.000000000 -0300
@@ -21,4 +21,5 @@
     OCTSTR(ssl-server-cert-file)
     OCTSTR(ssl-server-key-file)
     OCTSTR(ssl-trusted-ca-file)
+    OCTSTR(limit-per-cycle)
 )
diff -Nura -x '.*' sqlbox-0.7.2/gw/sqlbox_mssql.c sqlbox-0.7.2_des/gw/sqlbox_mssql.c
--- sqlbox-0.7.2/gw/sqlbox_mssql.c	2009-05-19 12:08:35.000000000 -0300
+++ sqlbox-0.7.2_des/gw/sqlbox_mssql.c	2009-06-08 15:05:15.000000000 -0300
@@ -293,6 +293,8 @@
     res->sql_leave = mssql_leave;
     res->sql_fetch_msg = mssql_fetch_msg;
     res->sql_save_msg = mssql_save_msg;
+    res->sql_fetch_batch = NULL;
+    res->sql_batch_msg_done = NULL;
     return res;
 }
 #endif
diff -Nura -x '.*' sqlbox-0.7.2/gw/sqlbox_mysql.c sqlbox-0.7.2_des/gw/sqlbox_mysql.c
--- sqlbox-0.7.2/gw/sqlbox_mysql.c	2009-05-19 12:08:35.000000000 -0300
+++ sqlbox-0.7.2_des/gw/sqlbox_mysql.c	2009-06-08 17:37:39.000000000 -0300
@@ -161,6 +161,80 @@
     return msg;
 }
 
+long mysql_fetch_batch(List *msgs, List *msgids, long limit)
+{
+    Msg *msg = NULL;
+    Octstr *sql, *id;
+    MYSQL_RES *res;
+    MYSQL_ROW row;
+    int amount;
+
+    sql = octstr_format(SQLBOX_MYSQL_SELECT_BATCH_QUERY, sqlbox_insert_table, limit);
+#if defined(SQLBOX_TRACE)
+     debug("SQLBOX", 0, "sql: %s", octstr_get_cstr(sql));
+#endif
+    res = mysql_select(sql);
+    if (res == NULL) {
+        debug("sqlbox", 0, "SQL statement failed: %s", octstr_get_cstr(sql));
+    }
+    else {
+        if (amount = mysql_num_rows(res) >= 1) {
+            while (row = mysql_fetch_row(res)) {
+                id = octstr_null_create(row[0]);
+                /* save fields in this row as msg struct */
+                msg = msg_create(sms);
+                msg->sms.sender     = octstr_null_create(row[2]);
+                msg->sms.receiver   = octstr_null_create(row[3]);
+                msg->sms.udhdata    = octstr_null_create(row[4]);
+                msg->sms.msgdata    = octstr_null_create(row[5]);
+                msg->sms.time       = atol_null(row[6]);
+                msg->sms.smsc_id    = octstr_null_create(row[7]);
+                msg->sms.service    = octstr_null_create(row[8]);
+                msg->sms.account    = octstr_null_create(row[9]);
+                /* msg->sms.id      = atol_null(row[10]); */
+                msg->sms.sms_type   = atol_null(row[11]);
+                msg->sms.mclass     = atol_null(row[12]);
+                msg->sms.mwi        = atol_null(row[13]);
+                msg->sms.coding     = atol_null(row[14]);
+                msg->sms.compress   = atol_null(row[15]);
+                msg->sms.validity   = atol_null(row[16]);
+                msg->sms.deferred   = atol_null(row[17]);
+                msg->sms.dlr_mask   = atol_null(row[18]);
+                msg->sms.dlr_url    = octstr_null_create(row[19]);
+                msg->sms.pid        = atol_null(row[20]);
+                msg->sms.alt_dcs    = atol_null(row[21]);
+                msg->sms.rpi        = atol_null(row[22]);
+                msg->sms.charset    = octstr_null_create(row[23]);
+                msg->sms.binfo      = octstr_null_create(row[25]);
+                if (row[24] == NULL) {
+                    msg->sms.boxc_id= octstr_duplicate(sqlbox_id);
+                }
+                else {
+                    msg->sms.boxc_id= octstr_null_create(row[24]);
+                }
+                gwlist_produce(msgs, msg);
+                gwlist_produce(msgids, id);
+            }
+        }
+        mysql_free_result(res);
+    }
+    octstr_destroy(sql);
+    return amount;
+}
+
+void mysql_batch_msg_done(void *id)
+{
+    Octstr *delet; 
+    /* delete row by row */
+    delet = octstr_format(SQLBOX_MYSQL_DELETE_QUERY, sqlbox_insert_table, (Octstr *)id);
+#if defined(SQLBOX_TRACE)
+    debug("SQLBOX", 0, "sql: %s", octstr_get_cstr(delet));
+#endif
+    mysql_update(delet);
+    octstr_destroy((Octstr *)id);
+    octstr_destroy(delet);
+}
+
 static Octstr *get_numeric_value_or_return_null(long int num)
 {
     if (num == -1) {
@@ -309,6 +383,8 @@
     res->sql_leave = mysql_leave;
     res->sql_fetch_msg = mysql_fetch_msg;
     res->sql_save_msg = mysql_save_msg;
+    res->sql_fetch_batch = mysql_fetch_batch;
+    res->sql_batch_msg_done = mysql_batch_msg_done;
     return res;
 }
 #endif
diff -Nura -x '.*' sqlbox-0.7.2/gw/sqlbox_mysql.h sqlbox-0.7.2_des/gw/sqlbox_mysql.h
--- sqlbox-0.7.2/gw/sqlbox_mysql.h	2009-05-19 12:08:35.000000000 -0300
+++ sqlbox-0.7.2_des/gw/sqlbox_mysql.h	2009-06-08 17:36:38.000000000 -0300
@@ -31,6 +31,11 @@
 compress, validity, deferred, dlr_mask, dlr_url, pid, alt_dcs, rpi, \
 charset, boxc_id, binfo FROM %S LIMIT 0,1"
 
+#define SQLBOX_MYSQL_SELECT_BATCH_QUERY "SELECT sql_id, momt, sender, receiver, udhdata, \
+msgdata, time, smsc_id, service, account, id, sms_type, mclass, mwi, coding, \
+compress, validity, deferred, dlr_mask, dlr_url, pid, alt_dcs, rpi, \
+charset, boxc_id, binfo FROM %S LIMIT 0,%ld"
+
 #define SQLBOX_MYSQL_INSERT_QUERY "INSERT INTO %S (sql_id, momt, sender, \
 receiver, udhdata, msgdata, time, smsc_id, service, account, sms_type, \
 mclass, mwi, coding, compress, validity, deferred, dlr_mask, dlr_url, \
diff -Nura -x '.*' sqlbox-0.7.2/gw/sqlbox_oracle.c sqlbox-0.7.2_des/gw/sqlbox_oracle.c
--- sqlbox-0.7.2/gw/sqlbox_oracle.c	2009-05-19 12:08:35.000000000 -0300
+++ sqlbox-0.7.2_des/gw/sqlbox_oracle.c	2009-06-08 15:05:32.000000000 -0300
@@ -314,6 +314,8 @@
     res->sql_leave = oracle_leave;
     res->sql_fetch_msg = oracle_fetch_msg;
     res->sql_save_msg = oracle_save_msg;
+    res->sql_fetch_batch = NULL;
+    res->sql_batch_msg_done = NULL;
     return res;
 }
 #endif
diff -Nura -x '.*' sqlbox-0.7.2/gw/sqlbox_pgsql.c sqlbox-0.7.2_des/gw/sqlbox_pgsql.c
--- sqlbox-0.7.2/gw/sqlbox_pgsql.c	2009-05-19 12:08:35.000000000 -0300
+++ sqlbox-0.7.2_des/gw/sqlbox_pgsql.c	2009-06-08 15:05:41.000000000 -0300
@@ -376,6 +376,8 @@
     res->sql_leave = pgsql_leave;
     res->sql_fetch_msg = pgsql_fetch_msg;
     res->sql_save_msg = pgsql_save_msg;
+    res->sql_fetch_batch = NULL;
+    res->sql_batch_msg_done = NULL;
     return res;
 }
 
diff -Nura -x '.*' sqlbox-0.7.2/gw/sqlbox_sdb.c sqlbox-0.7.2_des/gw/sqlbox_sdb.c
--- sqlbox-0.7.2/gw/sqlbox_sdb.c	2009-05-19 12:08:35.000000000 -0300
+++ sqlbox-0.7.2_des/gw/sqlbox_sdb.c	2009-06-08 15:05:49.000000000 -0300
@@ -412,6 +412,8 @@
     res->sql_leave = sdb_leave;
     res->sql_fetch_msg = sdb_fetch_msg;
     res->sql_save_msg = sdb_save_msg;
+    res->sql_fetch_batch = NULL;
+    res->sql_batch_msg_done = NULL;
     return res;
 }
 #endif
diff -Nura -x '.*' sqlbox-0.7.2/gw/sqlbox_sql.h sqlbox-0.7.2_des/gw/sqlbox_sql.h
--- sqlbox-0.7.2/gw/sqlbox_sql.h	2009-05-19 12:08:35.000000000 -0300
+++ sqlbox-0.7.2_des/gw/sqlbox_sql.h	2009-06-08 14:26:34.000000000 -0300
@@ -16,6 +16,8 @@
     void (*sql_leave) ();
     Msg *(*sql_fetch_msg) ();
     void (*sql_save_msg) (Msg *, Octstr *);
+    long (*sql_fetch_batch) (List *msgs, List *msgids, long limit);
+    void (*sql_batch_msg_done) (void *id);
 };
 
 struct sqlbox_db_queries {
@@ -41,6 +43,8 @@
 #define gw_sql_save_msg sql_type->sql_save_msg
 #define gw_sql_enter sql_type->sql_enter
 #define gw_sql_leave sql_type->sql_leave
+#define gw_sql_fetch_batch sql_type->sql_fetch_batch
+#define gw_sql_batch_msg_done sql_type->sql_batch_msg_done
 
 /* Macro to run the queries to create tables */
 #define sqlbox_run_query(query, table) \
diff -Nura -x '.*' sqlbox-0.7.2/gw/sqlbox_sqlite3.c sqlbox-0.7.2_des/gw/sqlbox_sqlite3.c
--- sqlbox-0.7.2/gw/sqlbox_sqlite3.c	2009-05-19 12:08:35.000000000 -0300
+++ sqlbox-0.7.2_des/gw/sqlbox_sqlite3.c	2009-06-08 15:05:58.000000000 -0300
@@ -314,6 +314,8 @@
     res->sql_leave = sqlite3_leave;
     res->sql_fetch_msg = sqlite3_fetch_msg;
     res->sql_save_msg = sqlite3_save_msg;
+    res->sql_fetch_batch = NULL;
+    res->sql_batch_msg_done = NULL;
     return res;
 }
 #endif
diff -Nura -x '.*' sqlbox-0.7.2/gw/sqlbox_sqlite.c sqlbox-0.7.2_des/gw/sqlbox_sqlite.c
--- sqlbox-0.7.2/gw/sqlbox_sqlite.c	2009-05-19 12:08:35.000000000 -0300
+++ sqlbox-0.7.2_des/gw/sqlbox_sqlite.c	2009-06-08 15:06:05.000000000 -0300
@@ -322,6 +322,8 @@
     res->sql_leave = sqlite_leave;
     res->sql_fetch_msg = sqlite_fetch_msg;
     res->sql_save_msg = sqlite_save_msg;
+    res->sql_fetch_batch = NULL;
+    res->sql_batch_msg_done = NULL;
     return res;
 }
 #endif
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.