src/hg/utils/refreshNamedSessionCustomTracks/refreshNamedSessionCustomTracks.c 1.10

1.10 2009/05/19 23:54:31 angie
Instead of processing the hgcentral query of session info row by row, slurp the results into memory and then process. Hopefully this will reduce the frequency of losing the connection to hgcentral (currently 4x/day).
Index: src/hg/utils/refreshNamedSessionCustomTracks/refreshNamedSessionCustomTracks.c
===================================================================
RCS file: /projects/compbio/cvsroot/kent/src/hg/utils/refreshNamedSessionCustomTracks/refreshNamedSessionCustomTracks.c,v
retrieving revision 1.9
retrieving revision 1.10
diff -b -B -U 1000000 -r1.9 -r1.10
--- src/hg/utils/refreshNamedSessionCustomTracks/refreshNamedSessionCustomTracks.c	18 May 2009 21:38:07 -0000	1.9
+++ src/hg/utils/refreshNamedSessionCustomTracks/refreshNamedSessionCustomTracks.c	19 May 2009 23:54:31 -0000	1.10
@@ -1,198 +1,217 @@
 /* refreshNamedSessionCustomTracks -- cron robot for keeping alive custom 
  * tracks that are referenced by saved sessions. */
 
 #include "common.h"
 #include "options.h"
 #include "hash.h"
 #include "cheapcgi.h"
 #include "hdb.h"
 #include "customTrack.h"
 #include "customFactory.h"
 #include "hui.h"
 
 static char const rcsid[] = "$Id$";
 
 #define savedSessionTable "namedSessionDb"
 
 void usage()
 /* Explain usage and exit. */
 {
 errAbort(
   "refreshNamedSessionCustomTracks -- scan central database's %s\n"
   "    contents for custom tracks and touch any that are found, to prevent\n"
   "    them from being removed by the custom track cleanup process.\n"
   "usage:\n"
   "    refreshNamedSessionCustomTracks hgcentral[test,beta]\n"
   "This is intended to be run as a nightly cron job for each central db.\n"
   "The ~/.hg.conf file (or $HGDB_CONF) must specify the same central db\n"
   "as the command line.  [The command line arg exists only to suppress this\n"
   "message].\n"
   "\n",
   savedSessionTable
   );
 }
 
 /* Options: */
 static struct optionSpec options[] = {
     {"hardcore", OPTION_BOOLEAN}, /* Intentionally omitted from usage(). */
     {NULL, 0},
 };
 
 char *scanSettingsForCT(char *userName, char *sessionName, char *contents,
 			int *pLiveCount, int *pExpiredCount)
 /* Parse the CGI-encoded session contents into {var,val} pairs and search
  * for custom tracks.  If found, refresh the custom track.  Parsing code 
  * taken from cartParseOverHash. 
  * If any nonexistent custom track files are found, return a SQL update
  * command that will remove those from this session.  We can't just do 
  * the update here because that messes up the caller's query. */
 {
 int contentLength = strlen(contents);
 struct dyString *newContents = dyStringNew(contentLength+1);
 struct dyString *oneSetting = dyStringNew(contentLength / 4);
 char *updateIfAny = NULL;
 char *contentsToChop = cloneString(contents);
 char *namePt = contentsToChop;
 verbose(3, "Scanning %s %s\n", userName, sessionName);
 while (isNotEmpty(namePt))
     {
     char *dataPt = strchr(namePt, '=');
     char *nextNamePt;
     if (dataPt == NULL)
 	errAbort("Mangled session content string %s", namePt);
     *dataPt++ = 0;
     nextNamePt = strchr(dataPt, '&');
     if (nextNamePt != NULL)
 	*nextNamePt++ = 0;
     dyStringClear(oneSetting);
     dyStringPrintf(oneSetting, "%s=%s%s",
 		   namePt, dataPt, (nextNamePt ? "&" : ""));
     if (startsWith(CT_FILE_VAR_PREFIX, namePt))
 	{
 	boolean thisGotLiveCT = FALSE, thisGotExpiredCT = FALSE;
 	cgiDecode(dataPt, dataPt, strlen(dataPt));
 	verbose(3, "Found variable %s = %s\n", namePt, dataPt);
 	/* If the file does not exist, omit this setting from newContents so 
 	 * it doesn't get copied from session to session.  If it does exist,
 	 * leave it up to customFactoryTestExistence to parse the file for 
 	 * possible customTrash table references, some of which may exist 
 	 * and some not. */
 	if (! fileExists(dataPt))
 	    {
 	    verbose(3, "Removing %s from %s %s\n", oneSetting->string,
 		    userName, sessionName);
 	    thisGotExpiredCT = TRUE;
 	    }
 	else
 	    {
 	    char *db = namePt + strlen(CT_FILE_VAR_PREFIX);
 	    dyStringAppend(newContents, oneSetting->string);
 	    customFactoryTestExistence(db, dataPt,
 				       &thisGotLiveCT, &thisGotExpiredCT);
 	    }
 	if (thisGotLiveCT && pLiveCount != NULL)
 	    (*pLiveCount)++;
 	if (thisGotExpiredCT && pExpiredCount != NULL)
 	    (*pExpiredCount)++;
 	if (thisGotExpiredCT)
 	    {
 	    if (verboseLevel() >= 3)
 		verbose(3, "Found expired custom track in %s %s: %s\n",
 			userName, sessionName, dataPt);
 	    else
 		verbose(2, "Found expired custom track: %s\n", dataPt);
 	    }
 	if (thisGotLiveCT)
 	    verbose(4, "Found live custom track: %s\n", dataPt);
 	}
     else
 	dyStringAppend(newContents, oneSetting->string);
     namePt = nextNamePt;
     }
 if (newContents->stringSize != contentLength)
     {
     struct dyString *update = dyStringNew(contentLength*2);
     if (newContents->stringSize > contentLength)
 	errAbort("Uh, why is newContents (%d) longer than original (%d)??",
 		 newContents->stringSize, contentLength);
     dyStringPrintf(update, "UPDATE %s set contents='", savedSessionTable);
     dyStringAppendN(update, newContents->string, newContents->stringSize);
     dyStringPrintf(update, "', lastUse=now(), useCount=useCount+1 "
 		   "where userName=\"%s\" and sessionName=\"%s\";",
 		   userName, sessionName);
     verbose(3, "Removing one or more dead CT file settings from %s %s "
 	    "(original length %d, now %d)\n", 
 	    userName, sessionName,
 	    contentLength, newContents->stringSize);
     updateIfAny = dyStringCannibalize(&update);
     }
 dyStringFree(&oneSetting);
 dyStringFree(&newContents);
 freeMem(contentsToChop);
 return updateIfAny;
 }
 
+struct sessionInfo
+    {
+    struct sessionInfo *next;
+    char userName[256];
+    char sessionName[256];
+    char *contents;
+    };
+
 void refreshNamedSessionCustomTracks(char *centralDbName)
 /* refreshNamedSessionCustomTracks -- cron robot for keeping alive custom 
  * tracks that are referenced by saved sessions. */
 {
 struct sqlConnection *conn = hConnectCentral();
 struct slPair *updateList = NULL, *update;
 char *actualDbName = sqlGetDatabase(conn);
 int liveCount=0, expiredCount=0;
 
 setUdcCacheDir();  /* programs that use udc must call this to initialize cache dir location */
 
 if (!sameString(centralDbName, actualDbName))
     errAbort("Central database specified in hg.conf file is %s but %s "
 	     "was specified on the command line.",
 	     actualDbName, centralDbName);
 else
     verbose(2, "Got connection to %s\n", centralDbName);
 
 if (sqlTableExists(conn, savedSessionTable))
     {
+    struct sessionInfo *sessionList = NULL, *si;
     struct sqlResult *sr = NULL;
     char **row = NULL;
     char query[512];
     safef(query, sizeof(query),
 	  "select userName,sessionName,contents from %s "
 	  "order by userName,sessionName", savedSessionTable);
     sr = sqlGetResult(conn, query);
+    // Slurp results into memory instead of processing row by row,
+    // reducing the chance of lost connection.
     while ((row = sqlNextRow(sr)) != NULL)
 	{
-	char *updateIfAny = scanSettingsForCT(row[0], row[1], row[2],
+	AllocVar(si);
+	safecpy(si->userName, sizeof(si->userName), row[0]);
+	safecpy(si->sessionName, sizeof(si->sessionName), row[1]);
+	si->contents = cloneString(row[2]);
+	slAddHead(&sessionList, si);
+	}
+    sqlFreeResult(&sr);
+    for (si = sessionList;  si != NULL;  si = si->next)
+	{
+	char *updateIfAny = scanSettingsForCT(si->userName, si->sessionName, si->contents,
 					      &liveCount, &expiredCount);
 	if (updateIfAny)
 	    {
 	    AllocVar(update);
 	    update->name = updateIfAny;
 	    slAddHead(&updateList, update);
 	    }
 	}
-    sqlFreeResult(&sr);
     }
 
 /* Now that we're done reading from savedSessionTable, we can modify it: */
 if (optionExists("hardcore"))
     {
     for (update = updateList;  update != NULL;  update = update->next)
 	sqlUpdate(conn, update->name);
     }
 hDisconnectCentral(&conn);
 verbose(1, "Found %d live and %d expired custom tracks in %s.\n",
 	liveCount, expiredCount, centralDbName);
 }
 
 
 int main(int argc, char *argv[])
 /* Process command line. */
 {
 optionInit(&argc, argv, options);
 if (argc != 2)
     usage();
 setCurrentDir(CGI_BIN);
 refreshNamedSessionCustomTracks(argv[1]);
 return 0;
 }