>From 4851ece2126454a7c1a1b3f92ac491a58505d5d7 Mon Sep 17 00:00:00 2001
From: lucian balanceanu <lucian.balanceanu@1and1.ro>
Date: Wed, 20 Aug 2014 13:49:20 +0300
Subject: [PATCH] sipcapture: retry insert in DB on initial failure

- added insert_retries and insert_retry_timeout parameters, which
  control how many times and for how long Kamailio should try to
  rewrite to the Homer database in case the first attempt fails;
  trying to rewrite proves useful especially in heavy load scenarios.
---
 modules/sipcapture/README                   |  177 ++++++++++++++++++---------
 modules/sipcapture/doc/sipcapture_admin.xml |   42 +++++++
 modules/sipcapture/sipcapture.c             |   58 +++++++--
 3 files changed, 203 insertions(+), 74 deletions(-)

diff --git a/modules/sipcapture/README b/modules/sipcapture/README
index edf544d..172afb5 100644
--- a/modules/sipcapture/README
+++ b/modules/sipcapture/README
@@ -10,9 +10,9 @@ Alexandr Dubovikov
 
    <alexandr.dubovikov@gmail.com>
 
-   Copyright � 2011 QSC AG
+   Copyright �� 2011 QSC AG
 
-   Copyright � 2011 http://www.qsc.de
+   Copyright �� 2011 http://www.qsc.de
      __________________________________________________________________
 
    Table of Contents
@@ -33,15 +33,18 @@ Alexandr Dubovikov
               3.4. hash_source (str)
               3.5. db_insert_mode (integer)
               3.6. capture_on (integer)
-              3.7. hep_capture_on (integer)
-              3.8. raw_ipip_capture_on (integer)
-              3.9. raw_moni_capture_on (integer)
-              3.10. raw_socket_listen (string)
-              3.11. raw_interface (string)
-              3.12. raw_sock_children (integer)
-              3.13. promiscuous_on (integer)
-              3.14. raw_moni_bpf_on (integer)
-              3.15. capture_node (str)
+              3.7. capture_mode (integer)
+              3.8. hep_capture_on (integer)
+              3.9. raw_ipip_capture_on (integer)
+              3.10. raw_moni_capture_on (integer)
+              3.11. raw_socket_listen (string)
+              3.12. raw_interface (string)
+              3.13. raw_sock_children (integer)
+              3.14. promiscuous_on (integer)
+              3.15. raw_moni_bpf_on (integer)
+              3.16. capture_node (str)
+              3.17. insert_retries (integer)
+              3.18. insert_retry_timeout (integer)
 
         4. MI Commands
 
@@ -62,15 +65,18 @@ Alexandr Dubovikov
    1.4. Set mt_mode parameter
    1.5. db_insert_mode example
    1.6. Set capture_on parameter
-   1.7. Set hep_capture_on parameter
-   1.8. Set raw_ipip_capture_on parameter
-   1.9. Set raw_moni_capture_on parameter
-   1.10. Set raw_socket_listen parameter
-   1.11. Set raw_interface parameter
-   1.12. Set raw_sock_children parameter
-   1.13. Set promiscous_on parameter
-   1.14. Set raw_moni_bpf_on parameter
-   1.15. Set capture_node parameter
+   1.7. capture_mode example
+   1.8. Set hep_capture_on parameter
+   1.9. Set raw_ipip_capture_on parameter
+   1.10. Set raw_moni_capture_on parameter
+   1.11. Set raw_socket_listen parameter
+   1.12. Set raw_interface parameter
+   1.13. Set raw_sock_children parameter
+   1.14. Set promiscous_on parameter
+   1.15. Set raw_moni_bpf_on parameter
+   1.16. Set capture_node parameter
+   1.17. Set insert_retries parameter
+   1.18. Set insert_retry_timeout parameter
 
 Chapter 1. Admin Guide
 
@@ -90,15 +96,18 @@ Chapter 1. Admin Guide
         3.4. hash_source (str)
         3.5. db_insert_mode (integer)
         3.6. capture_on (integer)
-        3.7. hep_capture_on (integer)
-        3.8. raw_ipip_capture_on (integer)
-        3.9. raw_moni_capture_on (integer)
-        3.10. raw_socket_listen (string)
-        3.11. raw_interface (string)
-        3.12. raw_sock_children (integer)
-        3.13. promiscuous_on (integer)
-        3.14. raw_moni_bpf_on (integer)
-        3.15. capture_node (str)
+        3.7. capture_mode (integer)
+        3.8. hep_capture_on (integer)
+        3.9. raw_ipip_capture_on (integer)
+        3.10. raw_moni_capture_on (integer)
+        3.11. raw_socket_listen (string)
+        3.12. raw_interface (string)
+        3.13. raw_sock_children (integer)
+        3.14. promiscuous_on (integer)
+        3.15. raw_moni_bpf_on (integer)
+        3.16. capture_node (str)
+        3.17. insert_retries (integer)
+        3.18. insert_retry_timeout (integer)
 
    4. MI Commands
 
@@ -151,15 +160,18 @@ Chapter 1. Admin Guide
    3.4. hash_source (str)
    3.5. db_insert_mode (integer)
    3.6. capture_on (integer)
-   3.7. hep_capture_on (integer)
-   3.8. raw_ipip_capture_on (integer)
-   3.9. raw_moni_capture_on (integer)
-   3.10. raw_socket_listen (string)
-   3.11. raw_interface (string)
-   3.12. raw_sock_children (integer)
-   3.13. promiscuous_on (integer)
-   3.14. raw_moni_bpf_on (integer)
-   3.15. capture_node (str)
+   3.7. capture_mode (integer)
+   3.8. hep_capture_on (integer)
+   3.9. raw_ipip_capture_on (integer)
+   3.10. raw_moni_capture_on (integer)
+   3.11. raw_socket_listen (string)
+   3.12. raw_interface (string)
+   3.13. raw_sock_children (integer)
+   3.14. promiscuous_on (integer)
+   3.15. raw_moni_bpf_on (integer)
+   3.16. capture_node (str)
+   3.17. insert_retries (integer)
+   3.18. insert_retry_timeout (integer)
 
 3.1. db_url (str)
 
@@ -235,29 +247,46 @@ modparam("sipcapture", "db_insert_mode", 1)
 modparam("sipcapture", "capture_on", 1)
 ...
 
-3.7. hep_capture_on (integer)
+3.7. capture_mode (integer)
+
+   This parameter can be used for defining a capture mode which can be
+   used in the sip_capture calls as a parameter. A capture mode has a name
+   and some parameters. It must be defined in the format:
+   name=>param1=val1;param2=val2;... The parameters are db_url,
+   table_name, mt_mode and hash_source (optional). Multiple capture modes
+   can be defined by using this parameter multiple times. After this, the
+   capture modes can be used like: sip_capture ("", "CAPTURE_MODE");
+
+   Example 1.7. capture_mode example
+modparam("sipcapture", "capture_mode", "mode1=>db_url=mysql://user:passwd@host/d
+bname1;table_name=homer_capture1|homer_capture2;mt_mode=hash;hash_source=call_id
+;")
+modparam("sipcapture", "capture_mode", "mode2=>db_url=mysql://user:passwd@host/d
+bname2;table_name=homer_capture3|homer_capture4;mt_mode=rand;")
+
+3.8. hep_capture_on (integer)
 
    Parameter to enable/disable capture of HEP (on(1)/off(0))
 
    Default value is "0".
 
-   Example 1.7. Set hep_capture_on parameter
+   Example 1.8. Set hep_capture_on parameter
 ...
 modparam("sipcapture", "hep_capture_on", 1)
 ...
 
-3.8. raw_ipip_capture_on (integer)
+3.9. raw_ipip_capture_on (integer)
 
    Parameter to enable/disable IPIP capturing (on(1)/off(0))
 
    Default value is "0".
 
-   Example 1.8. Set raw_ipip_capture_on parameter
+   Example 1.9. Set raw_ipip_capture_on parameter
 ...
 modparam("sipcapture", "raw_ipip_capture_on", 1)
 ...
 
-3.9. raw_moni_capture_on (integer)
+3.10. raw_moni_capture_on (integer)
 
    Parameter to enable/disable monitoring/mirroring port capturing
    (on(1)/off(0)) Only one mode on raw socket can be enabled! Monitoring
@@ -265,12 +294,12 @@ modparam("sipcapture", "raw_ipip_capture_on", 1)
 
    Default value is "0".
 
-   Example 1.9. Set raw_moni_capture_on parameter
+   Example 1.10. Set raw_moni_capture_on parameter
 ...
 modparam("sipcapture", "raw_moni_capture_on", 1)
 ...
 
-3.10. raw_socket_listen (string)
+3.11. raw_socket_listen (string)
 
    Parameter indicate an listen IP address of RAW socket for IPIP
    capturing. You can also define a port/portrange for IPIP/Mirroring
@@ -288,49 +317,49 @@ modparam("sipcapture", "raw_moni_capture_on", 1)
 
    Default value is "".
 
-   Example 1.10. Set raw_socket_listen parameter
+   Example 1.11. Set raw_socket_listen parameter
 ...
 modparam("sipcapture", "raw_socket_listen", "10.0.0.1:5060-5090")
 ...
 modparam("sipcapture", "raw_socket_listen", "10.0.0.1:5060")
 ...
 
-3.11. raw_interface (string)
+3.12. raw_interface (string)
 
    Name of the interface to bind on the raw socket.
 
    Default value is "".
 
-   Example 1.11. Set raw_interface parameter
+   Example 1.12. Set raw_interface parameter
 ...
 modparam("sipcapture", "raw_interface", "eth0")
 ...
 
-3.12. raw_sock_children (integer)
+3.13. raw_sock_children (integer)
 
    Parameter define how many children that must be created to listen the
    raw socket.
 
    Default value is "1".
 
-   Example 1.12. Set raw_sock_children parameter
+   Example 1.13. Set raw_sock_children parameter
 ...
 modparam("sipcapture", "raw_sock_children", 6)
 ...
 
-3.13. promiscuous_on (integer)
+3.14. promiscuous_on (integer)
 
    Parameter to enable/disable promiscuous mode on the raw socket. Linux
    only.
 
    Default value is "0".
 
-   Example 1.13. Set promiscous_on parameter
+   Example 1.14. Set promiscous_on parameter
 ...
 modparam("sipcapture", "promiscuous_on", 1)
 ...
 
-3.14. raw_moni_bpf_on (integer)
+3.15. raw_moni_bpf_on (integer)
 
    Activate Linux Socket Filter (LSF based on BPF) on the mirroring
    interface. The structure is defined in linux/filter.h. The default LSF
@@ -339,27 +368,55 @@ modparam("sipcapture", "promiscuous_on", 1)
 
    Default value is "0".
 
-   Example 1.14. Set raw_moni_bpf_on parameter
+   Example 1.15. Set raw_moni_bpf_on parameter
 ...
 modparam("sipcapture", "raw_moni_bpf_on", 1)
 ...
 
-3.15. capture_node (str)
+3.16. capture_node (str)
 
    Name of the capture node.
 
    Default value is "homer01".
 
-   Example 1.15. Set capture_node parameter
+   Example 1.16. Set capture_node parameter
 ...
 modparam("sipcapture", "capture_node", "homer03")
 ...
 
+3.17. insert_retries (integer)
+
+   The number of times Kamailio should retry to write to the Homer
+   database in case the first attempt failed. The retry is also limited
+   timewise by the insert_retry_timeout parameter. Values allowed range
+   from 0 to 500.
+
+   Default value is 0 (no retries).
+
+   Example 1.17. Set insert_retries parameter
+...
+modparam("sipcapture", "insert_retries", 5)
+...
+
+3.18. insert_retry_timeout (integer)
+
+   The time limit in seconds Kamailio retries to write to the Homer
+   database in case the first attempt failed. This parameter is only used
+   together with the insert_retries parameter. Values allowed range from 0
+   to 300.
+
+   Default value is 60 seconds.
+
+   Example 1.18. Set insert_retry_timeout parameter
+...
+modparam("sipcapture", "insert_retry_timeout", 10)
+...
+
 4. MI Commands
 
    4.1. sip_capture
 
-4.1. sip_capture
+4.1.  sip_capture
 
    Name: sip_capture
 
@@ -369,7 +426,7 @@ modparam("sipcapture", "capture_node", "homer03")
           + on
           + off
        The parameter is optional - if missing, the command will return the
-       status of the SIP message capturing (as string "on" or "off" )
+       status of the SIP message capturing (as string ���on��� or ���off��� )
        without changing anything.
 
    MI FIFO Command Format:
@@ -381,7 +438,7 @@ modparam("sipcapture", "capture_node", "homer03")
 
    5.1. sipcapture.status param
 
-5.1. sipcapture.status param
+5.1.  sipcapture.status param
 
    Name: sipcapture.status
 
@@ -389,7 +446,7 @@ modparam("sipcapture", "capture_node", "homer03")
      * on or off: turns on/off SIP message capturing. Possible values are:
           + on
           + off
-     * "check" does not change sipcapture status, just reports the current
+     * ���check��� does not change sipcapture status, just reports the current
        status.
 
 6. Database setup
diff --git a/modules/sipcapture/doc/sipcapture_admin.xml b/modules/sipcapture/doc/sipcapture_admin.xml
index c172c5a..8923149 100644
--- a/modules/sipcapture/doc/sipcapture_admin.xml
+++ b/modules/sipcapture/doc/sipcapture_admin.xml
@@ -407,6 +407,48 @@ modparam("sipcapture", "capture_node", "homer03")
 ...
 </programlisting>
 		</example>
+		</section>
+	<section id="sipcapture.p.insert_retries">
+		<title><varname>insert_retries</varname> (integer)</title>
+		<para>
+		The number of times Kamailio should retry to write to the Homer database in case
+		the first attempt failed. The retry is also limited timewise by the
+		insert_retry_timeout parameter. Values allowed range from 0 to 500.
+		</para>
+		<para>
+		<emphasis>
+			Default value is 0 (no retries).
+		</emphasis>
+		</para>
+		<example>
+			<title>Set <varname>insert_retries</varname> parameter</title>
+			<programlisting format="linespecific">
+...
+modparam("sipcapture", "insert_retries", 5)
+...
+			</programlisting>
+		</example>
+	</section>
+	<section id="sipcapture.p.insert_retry_timeout">
+		<title><varname>insert_retry_timeout</varname> (integer)</title>
+		<para>
+		The time limit in seconds Kamailio retries to write to the Homer database in case
+		the first attempt failed. This parameter is only used together with the insert_retries
+		parameter. Values allowed range from 0 to 300.
+		</para>
+		<para>
+		<emphasis>
+			Default value is 60 seconds.
+		</emphasis>
+		</para>
+		<example>
+			<title>Set <varname>insert_retry_timeout</varname> parameter</title>
+			<programlisting format="linespecific">
+...
+modparam("sipcapture", "insert_retry_timeout", 10)
+...
+			</programlisting>
+		</example>
 	</section>
 </section>	
     <section>
diff --git a/modules/sipcapture/sipcapture.c b/modules/sipcapture/sipcapture.c
index 43f072c..5471f54 100644
--- a/modules/sipcapture/sipcapture.c
+++ b/modules/sipcapture/sipcapture.c
@@ -198,6 +198,8 @@ int db_insert_mode = 0;
 int promisc_on = 0;
 int bpf_on = 0;
 int hep_capture_on   = 0;
+int insert_retries = 0;
+int insert_retry_timeout = 60;
 int hep_offset = 0;
 str raw_socket_listen = { 0, 0 };
 str raw_interface = { 0, 0 };
@@ -225,8 +227,6 @@ static struct sock_filter BPF_code[] = { { 0x28, 0, 0, 0x0000000c }, { 0x15, 0,
 
 unsigned int no_tables = 0;
 
-
-
 enum e_mt_mode mtmode = mode_random ;
 enum hash_source source = hs_error;
 
@@ -234,7 +234,6 @@ enum hash_source source = hs_error;
 
 struct hep_timehdr* heptime;
 
-
 /*! \brief
  * Exported functions
  */
@@ -306,6 +305,8 @@ static param_export_t params[] = {
         {"raw_moni_bpf_on",  		INT_PARAM, &bpf_on   },		
         {"callid_aleg_header",          PARAM_STR, &callid_aleg_header},
         {"capture_mode",		PARAM_STRING|USE_FUNC_PARAM, (void *)capture_mode_param},
+    {"insert_retries",   	INT_PARAM, &insert_retries },
+    {"insert_retry_timeout",INT_PARAM, &insert_retry_timeout },
 		{0, 0, 0}
 };
 
@@ -355,7 +356,6 @@ struct module_exports exports = {
 };
 
 
-
 /* returns number of tables if successful
  * <0 if failed
  */
@@ -788,7 +788,19 @@ static int mod_init(void) {
 		return -1;		                		
 	}
 	
+	if ((insert_retries <0) || ( insert_retries > 500)) {
+		LM_ERR("insert_retries should be a value between 0 and 500");
+		return -1;
+	}
 
+	if  (( 0 == insert_retries) && (insert_retry_timeout != 0)){
+		LM_ERR("insert_retry_timeout has no meaning when insert_retries is not set");
+	}
+
+	if ((insert_retry_timeout <0) || ( insert_retry_timeout > 300)) {
+		LM_ERR("insert_retry_timeout should be a value between 0 and 300");
+		return -1;
+	}
 
 	/* raw processes for IPIP encapsulation */
 	if (ipip_capture_on || moni_capture_on) {
@@ -1072,6 +1084,11 @@ static int sip_capture_store(struct _sipcapture_object *sco, str *dtable, _captu
 
 	str tmp;
 	int ii = 0;
+	int ret = 0;
+	int counter = 0;
+	db_insert_f insert;
+	time_t retry_failed_time = 0;
+
 	str *table = NULL;
 	_capture_mode_data_t *c = NULL;
 
@@ -1320,24 +1337,37 @@ static int sip_capture_store(struct _sipcapture_object *sco, str *dtable, _captu
 
 	LM_DBG("storing info...\n");
 	
-	if(db_insert_mode==1 && c->db_funcs.insert_delayed!=NULL) {
-                if (c->db_funcs.insert_delayed(c->db_con, db_keys, db_vals, NR_KEYS) < 0) {
-                	LM_ERR("failed to insert delayed into database\n");
-                        goto error;
+	if (db_insert_mode == 1 && c->db_funcs.insert_delayed != NULL)
+		insert = c->db_funcs.insert_delayed;
+	else
+		insert = c->db_funcs.insert;
+	counter = 0;
+
+	ret = insert(c->db_con, db_keys, db_vals, NR_KEYS);
+	if (ret < 0)
+		LM_DBG("failed to insert into database(first attempt)\n");
+
+	while ((insert_retries != 0) && ((ret = insert(c->db_con, db_keys, db_vals,
+			NR_KEYS)) < 0)) {
+		counter++;
+		if (1 == counter) //first failed retry
+			retry_failed_time = time(NULL);
+
+		if ((counter > insert_retries) || (time(NULL) - retry_failed_time
+				> insert_retry_timeout)) {
+			LM_ERR("failed to insert into database(second attempt)\n");
+			break;
                 }
-        } else if (c->db_funcs.insert(c->db_con, db_keys, db_vals, NR_KEYS) < 0) {
-		LM_ERR("failed to insert into database\n");
-                goto error;               
 	}
 	
-	
+	if (ret < 0)
+		goto error;
 #ifdef STATISTICS
 	update_stat(sco->stat, 1);
 #endif	
 
 	return 1;
-error:
-	return -1;
+	error: return -1;
 }
 
 static int sip_capture(struct sip_msg *msg, str *_table, _capture_mode_data_t * cm_data)
-- 
1.7.4.1

