dlr-retry-queue patch
"Stevenard, Kevin (Kevin)" <[email protected]>
| Newsgroups | gmane.comp.mobile.kannel.devel |
|---|---|
| Message-ID | <B2655CC798AF8C438576129FF8B75DE16F3ED1F7C7@FRMRSSXCHMBSD3.dc-m.alcatel-lucent.com> |
Hello Kannel users, I have written a patch on the smsc_smpp connector to implement a DLR retry queue. In our services we are highly dependent of delivery reports. We were first using a patch (http://www.blogalex.com/wp-content/uploads/2009/05/kannel-dlr-retry.patch) but we were not happy with the following: - blocking io thread due to the sleep implemented at dlr search level - preventing instant deliver_sm_resp (due also to the sleep blocking io thread) - system wide retry, can not be configured per account As our setup is composed of several servers in a 'cluster' each server is running Kannel with several smpp/emi binds and each server is writing delivery reports in a local MySQL backend. We then use master-master replication to spread dlr entries on all servers. As we have multiple servers and multiple binds its possible to get a dlr through a deliver_sm before we received the submit_sm_resp containing the message id or before the dlr entry is written and replicated in other db servers (even with a lag < 1sec). So the patch is adding a queue per smscconn and creates for each of them a dedicated thread to re-process dlr that have not been found in the dlr store, if not configured no queue and no thread are created. To configure it: dlr-retry-count: Number of attempts to process a delivery report if not found in the delivery reports store. Defaults to 0 times (disabled). dlr-retry-interval: This timer specifies the interval time between delivery reports retries. Defaults to 60 seconds. #sample in a group = smsc && smsc = smpp: dlr-retry-interval = 60 dlr-retry-count = 3
dlr-retry-queue.patch
(application/octet-stream, 11.3 KB)
Index: gwlib/cfg.def
===================================================================
--- gwlib/cfg.def (revision 4981)
+++ gwlib/cfg.def (working copy)
@@ -422,6 +422,8 @@
OCTSTR(generic-status-sent)
OCTSTR(generic-status-error)
OCTSTR(generic-foreign-id-regex)
+ OCTSTR(dlr-retry-count)
+ OCTSTR(dlr-retry-interval)
)
Index: doc/userguide/userguide.xml
===================================================================
--- doc/userguide/userguide.xml (revision 4981)
+++ doc/userguide/userguide.xml (working copy)
@@ -3576,6 +3576,20 @@
Defaults to 3.
</entry></row>
+ <row><entry><literal>dlr-retry-count</literal></entry>
+ <entry><literal>number</literal></entry>
+ <entry valign="bottom">
+ Number of attempts to process a delivery report if not found in the delivery
+ reports store. Defaults to 0 times (disabled).
+ </entry></row>
+
+ <row><entry><literal>dlr-retry-interval</literal></entry>
+ <entry><literal>number (seconds)</literal></entry>
+ <entry valign="bottom">
+ This timer specifies the interval time between delivery reports retries.
+ Defaults to 60 seconds.
+ </entry></row>
+
</tbody></tgroup></informaltable>
</sect2>
Index: gw/smsc/smsc_smpp.c
===================================================================
--- gw/smsc/smsc_smpp.c (revision 4981)
+++ gw/smsc/smsc_smpp.c (working copy)
@@ -172,6 +172,10 @@
int esm_class;
Load *load;
SMSCConn *conn;
+ long dlr_retry; /* id of the thread responsible for dlr retries */
+ List *dlr_to_retry; /* list containing dlr waiting for a retry */
+ long dlr_retry_count;
+ long dlr_retry_interval;
} SMPP;
@@ -180,6 +184,14 @@
Msg *msg;
};
+struct dlr_retry {
+ time_t retry_time;
+ long dlr_retry_count;
+ int dlrstat;
+ Octstr *msgid;
+ Octstr *destination_address;
+ Octstr *msgdata;
+};
/*
* create smpp_msg struct
@@ -211,6 +223,17 @@
gw_free(msg);
}
+static inline void dlr_retry_destroy(void *arg)
+{
+ struct dlr_retry *dlr;
+ dlr = arg;
+ if (dlr == NULL)
+ return;
+ octstr_destroy(dlr->destination_address);
+ octstr_destroy(dlr->msgdata);
+ octstr_destroy(dlr->msgid);
+ gw_free(dlr);
+}
static SMPP *smpp_create(SMSCConn *conn, Octstr *host, int transmit_port,
int receive_port, Octstr *system_type,
@@ -223,7 +246,8 @@
Octstr *my_number, int smpp_msg_id_type,
int autodetect_addr, Octstr *alt_charset, Octstr *alt_addr_charset,
Octstr *service_type, long connection_timeout,
- long wait_ack, int wait_ack_action, int esm_class)
+ long wait_ack, int wait_ack_action, int esm_class,
+ long dlr_retry_count, long dlr_retry_interval)
{
SMPP *smpp;
@@ -271,6 +295,10 @@
smpp->load = load_create_real(0);
load_add_interval(smpp->load, 1);
smpp->esm_class = esm_class;
+ smpp->dlr_retry = -1;
+ smpp->dlr_to_retry = gwlist_create();
+ smpp->dlr_retry_count = dlr_retry_count;
+ smpp->dlr_retry_interval = dlr_retry_interval;
return smpp;
}
@@ -282,6 +310,7 @@
gw_prioqueue_destroy(smpp->msgs_to_send, msg_destroy_item);
dict_destroy(smpp->sent_msgs);
gwlist_destroy(smpp->received_msgs, msg_destroy_item);
+ gwlist_destroy(smpp->dlr_to_retry, dlr_retry_destroy);
counter_destroy(smpp->message_id_counter);
octstr_destroy(smpp->host);
octstr_destroy(smpp->username);
@@ -1475,10 +1504,28 @@
meta_data_set_value(dlrmsg->sms.meta_data, "smpp", octstr_imm("dlr_err"), network_err, 1);
}
} else {
- error(0,"SMPP[%s]: got DLR but could not find message or was not interested "
- "in it id<%s> dst<%s>, type<%d>",
- octstr_get_cstr(smpp->conn->id), octstr_get_cstr(tmp),
- octstr_get_cstr(destination_addr), dlrstat);
+ if ( smpp->dlr_retry_count > 0 ) {
+ // add the DLR into the retry queue
+ struct dlr_retry *retry = gw_malloc(sizeof(struct dlr_retry));
+ retry->destination_address = octstr_duplicate(destination_addr);
+ retry->dlr_retry_count = 0;
+ retry->dlrstat = dlrstat;
+ retry->msgid = octstr_duplicate(tmp);
+ retry->retry_time = time(NULL) + smpp->dlr_retry_interval;
+
+ retry->msgdata = octstr_duplicate(respstr);
+ gwlist_append(smpp->dlr_to_retry, retry);
+
+ debug("bb.sms.smpp", 0,"SMPP[%s]: Add DLR into the retry queue because it was not found in the DLR storage "
+ "id<%s> dst<%s>, type<%d>",
+ octstr_get_cstr(smpp->conn->id), octstr_get_cstr(tmp),
+ octstr_get_cstr(destination_addr), dlrstat);
+ } else {
+ error(0,"SMPP[%s]: got DLR but could not find message or was not interested "
+ "in it id<%s> dst<%s>, type<%d>",
+ octstr_get_cstr(smpp->conn->id), octstr_get_cstr(tmp),
+ octstr_get_cstr(destination_addr), dlrstat);
+ }
}
octstr_destroy(tmp);
octstr_destroy(network_err);
@@ -1868,8 +1915,76 @@
return io_arg;
}
+/*
+ * go over all DLRs stored in the dlr_retry list. If some of them
+ * have reached their retry time they are processed again
+ * when a DLR has reached its maximum number of retries it is removed
+ * from the list
+ */
+static void do_dlr_retry_cleanup (SMPP *smpp)
+{
+ long i;
+ Msg *dlrmsg = NULL;
+ for (i = 0; i < gwlist_len(smpp->dlr_to_retry); i++) {
+ struct dlr_retry *dlr;
+ dlr = gwlist_get(smpp->dlr_to_retry, i);
+ if (dlr != NULL ) {
+ if ( difftime(time(NULL),dlr->retry_time) >= 0 ) {
+ debug("bb.sms.smpp", 0,"SMPP[%s]: Attempt <%ld> of <%ld> to process DLR id<%s> dst<%s>, type<%d>",
+ octstr_get_cstr(smpp->conn->id),
+ (dlr->dlr_retry_count+1), smpp->dlr_retry_count,
+ octstr_get_cstr(dlr->msgid),
+ octstr_get_cstr(dlr->destination_address), dlr->dlrstat);
+ dlrmsg = dlr_find(smpp->conn->id,
+ dlr->msgid, /* smsc message id */
+ dlr->destination_address, /* destination */
+ dlr->dlrstat, 1);
+ if (dlrmsg != NULL) {
+ debug("bb.sms.smpp", 0,"SMPP[%s]: Found matching DLR id<%s> dst<%s>, type<%d>",
+ octstr_get_cstr(smpp->conn->id), octstr_get_cstr(dlr->msgid),
+ octstr_get_cstr(dlr->destination_address), dlr->dlrstat);
+ dlrmsg->sms.msgdata = octstr_duplicate(dlr->msgdata);
+ dlrmsg->sms.sms_type = report_mo;
+ dlrmsg->sms.account = octstr_duplicate(smpp->username);
+ bb_smscconn_receive(smpp->conn, dlrmsg);
+ gwlist_delete(smpp->dlr_to_retry,i,1);
+ } else {
+ dlr->dlr_retry_count++;
+ if (dlr->dlr_retry_count >= smpp->dlr_retry_count) {
+ gwlist_delete(smpp->dlr_to_retry,i,1);
+ error(0,"SMPP[%s]: Number of retry excedeed for DLR "
+ "id<%s> dst<%s>, type<%d> discarding it",
+ octstr_get_cstr(smpp->conn->id), octstr_get_cstr(dlr->msgid),
+ octstr_get_cstr(dlr->destination_address), dlr->dlrstat);
+ } else {
+ dlr->retry_time = time(NULL) + smpp->dlr_retry_interval;
+ debug("bb.sms.smpp", 0,"SMPP[%s]: DLR not yet found re-queueing it id<%s> dst<%s>, type<%d>",
+ octstr_get_cstr(smpp->conn->id), octstr_get_cstr(dlr->msgid),
+ octstr_get_cstr(dlr->destination_address), dlr->dlrstat);
+ }
+ }
+ }
+ }
+ }
+}
/*
+ * execute in a loop do_dlr_retry_cleanup and wait 1sec till the smpp conn is
+ * not quitting
+ */
+static void dlr_retry_thread(void *arg)
+{
+ SMPP *smpp;
+ smpp = arg;
+ /* Make sure we log into our own log-file if defined */
+ log_thread_to(smpp->conn->log_idx);
+ while (!smpp->quitting) {
+ do_dlr_retry_cleanup(smpp);
+ gwthread_sleep(1.0);
+ }
+}
+
+/*
* sent queue cleanup.
* @return 1 if io_thread should reconnect; 0 if not
*/
@@ -2060,7 +2175,7 @@
break; /* reconnect */
time(&last_cleanup);
}
-
+
/* make sure we send */
if (transmitter && difftime(time(NULL), smpp->throttling_err_time) > SMPP_THROTTLING_SLEEP_TIME) {
smpp->throttling_err_time = 0;
@@ -2257,6 +2372,8 @@
Octstr *alt_addr_charset;
long connection_timeout, wait_ack, wait_ack_action;
long esm_class;
+ long dlr_retry_count;
+ long dlr_retry_interval;
my_number = alt_addr_charset = alt_charset = NULL;
transceiver_mode = 0;
@@ -2401,13 +2518,26 @@
esm_class = ESM_CLASS_SUBMIT_STORE_AND_FORWARD_MODE;
}
+ /* check for dlr retry count */
+ if (cfg_get_integer(&dlr_retry_count, grp, octstr_imm("dlr-retry-count")) == -1)
+ dlr_retry_count = 0;
+ else if (dlr_retry_count < 0)
+ panic(0, "SMPP: Invalid value for dlr retry count (allowed value >= 0).");
+
+ /* check for dlr retry delay */
+ if (cfg_get_integer(&dlr_retry_interval, grp, octstr_imm("dlr-retry-interval")) == -1)
+ dlr_retry_interval = 60;
+ else if (dlr_retry_interval < 1)
+ panic(0, "SMPP: Invalid value for dlr retry interval (allowed value >= 1).");
+
smpp = smpp_create(conn, host, port, receive_port, system_type,
username, password, address_range,
source_addr_ton, source_addr_npi, dest_addr_ton,
dest_addr_npi, enquire_link_interval,
max_pending_submits, version, priority, validity, my_number,
smpp_msg_id_type, autodetect_addr, alt_charset, alt_addr_charset,
- service_type, connection_timeout, wait_ack, wait_ack_action, esm_class);
+ service_type, connection_timeout, wait_ack, wait_ack_action, esm_class,
+ dlr_retry_count, dlr_retry_interval);
cfg_get_integer(&smpp->bind_addr_ton, grp, octstr_imm("bind-addr-ton"));
cfg_get_integer(&smpp->bind_addr_npi, grp, octstr_imm("bind-addr-npi"));
@@ -2455,6 +2585,9 @@
if (receive_port != 0)
smpp->receiver = gwthread_create(io_thread, io_arg_create(smpp, 0));
+ if (smpp->dlr_retry_count > 0)
+ smpp->dlr_retry = gwthread_create(dlr_retry_thread,smpp);
+
if ((port != 0 && smpp->transmitter == -1) ||
(receive_port != 0 && smpp->receiver == -1)) {
error(0, "SMPP[%s]: Couldn't start I/O threads.",
@@ -2468,6 +2601,10 @@
gwthread_wakeup(smpp->receiver);
gwthread_join(smpp->receiver);
}
+ if (smpp->dlr_retry != -1) {
+ gwthread_wakeup(smpp->dlr_retry);
+ gwthread_join(smpp->dlr_retry);
+ }
smpp_destroy(conn->data);
conn->data = NULL;
return -1;