Changeset: f293b967a5f7 for MonetDB
URL: https://dev.monetdb.org/hg/MonetDB?cmd=changeset;node=f293b967a5f7
Modified Files:
        monetdb5/mal/mal.c
        monetdb5/mal/mal_runtime.c
        monetdb5/mal/mal_runtime.h
        monetdb5/modules/mal/sysmon.c
        monetdb5/modules/mal/sysmon.mal
        sql/backends/monet5/sql_upgrades.c
        sql/scripts/26_sysmon.sql
Branch: userstats
Log Message:

A basic working version of the sys.user_statistics() function.

Manually tested with two users.


diffs (truncated from 477 to 300 lines):

diff --git a/monetdb5/mal/mal.c b/monetdb5/mal/mal.c
--- a/monetdb5/mal/mal.c
+++ b/monetdb5/mal/mal.c
@@ -103,6 +103,7 @@ void mal_reset(void)
        GDKprepareExit();
        MCstopClients(0);
        dropQRYqueue();
+       dropUSRstats();
        setHeartbeat(-1);
        stopProfiler(0);
        AUTHreset();
diff --git a/monetdb5/mal/mal_runtime.c b/monetdb5/mal/mal_runtime.c
--- a/monetdb5/mal/mal_runtime.c
+++ b/monetdb5/mal/mal_runtime.c
@@ -27,30 +27,71 @@
 
 
 QueryQueue QRYqueue = NULL;
-UserStats  USRstats;
-lng        usize = 0;
-
 size_t qsize = 0, qhead = 0, qtail = 0;
 static oid qtag= 1;            // A unique query identifier
 
+UserStats  USRstats = NULL;
+size_t usrstatscnt = 0;
+
+static void
+clearUSRstats(size_t idx)
+{
+       USRstats[idx].user= 0;
+       USRstats[idx].username = 0;
+       USRstats[idx].querycount = 0;
+       USRstats[idx].totalticks = 0;
+       USRstats[idx].started = 0;
+       USRstats[idx].finished = 0;
+       USRstats[idx].maxticks = 0;
+       USRstats[idx].maxquery = 0;
+}
+
+/*
+ * Find the index of the given 'user' in USRstats.
+ * For a new 'user' return a new free slot.
+ * If USRstats is full, extend it.
+ */
 static
-str
-updateUserStats(Client cntxt, lng ticks, time_t started, time_t finished, str 
query)
+size_t 
+getUSRstatsIdx(MalBlkPtr mb, oid user)
 {
-       int idx = (int) cntxt->idx;
+       size_t i = 0;
+       UserStats tmp = NULL;
+
+       for (i = 0; i < usrstatscnt; i++)
+               /* The array is dense, so we either find the user or an empty 
slot. */
+               if (USRstats[i].user == user || USRstats[i].username == NULL)
+                       return i;
 
-       if(idx > MAL_MAXCLIENTS){
+       /* expand USRstats */
+       tmp = (UserStats) GDKrealloc(USRstats, sizeof (struct USERSTAT) * 
(size_t) (usrstatscnt += MAL_MAXCLIENTS));
+       if (tmp == NULL) {
+               /* It's not a fatal error if we can't extend USRstats.
+                * We don't want to affect existing USRstats. */
+               addMalException(mb,"getUSRstatsIdx" MAL_MALLOC_FAIL);
+               return (size_t) -1;
+       }
+       USRstats = tmp;
+       for ( ; i < usrstatscnt; i++)
+               clearUSRstats(i);
+       return usrstatscnt - MAL_MAXCLIENTS;
+}
+
+static
+void
+updateUserStats(Client cntxt, MalBlkPtr mb, lng ticks, time_t started, time_t 
finished, str query)
+{
+       size_t idx = getUSRstatsIdx(mb, cntxt->user);
+
+       if (idx == (size_t) -1) {
+               addMalException(mb, "updateUserStats" "Failed to get an entry 
in user statistics");
+               return;
        }
 
-       if(usize == 0){
-               USRstats = (UserStats) GDKzalloc( sizeof (struct USERSTAT) * 
(size_t) (usize= MAL_MAXCLIENTS));
+       if (USRstats[idx].username == NULL) {
+               USRstats[idx].user = cntxt->user;
+               USRstats[idx].username = GDKstrdup(cntxt->username);
        }
-       if(idx > usize){
-               USRstats = (UserStats) GDKrealloc( USRstats, sizeof (struct 
USERSTAT) * (size_t) (usize += MAL_MAXCLIENTS));
-       }
-       if( USRstats == NULL){
-       }
-       USRstats[idx].username= strdup(cntxt->username);
        USRstats[idx].querycount++;
        USRstats[idx].totalticks += ticks;
        if( ticks > USRstats[idx].maxticks && query){
@@ -59,9 +100,26 @@ updateUserStats(Client cntxt, lng ticks,
                USRstats[idx].maxticks = ticks;
                if( USRstats[idx].maxquery)
                        GDKfree(USRstats[idx].maxquery);
-               USRstats[idx].maxquery= strdup(query);
+               USRstats[idx].maxquery= GDKstrdup(query);
        }
-       return MAL_SUCCEED;
+}
+
+void
+dropUSRstats(void)
+{
+       size_t i;
+       MT_lock_set(&mal_delayLock);
+       for(i = 0; i < usrstatscnt; i++){
+               if(USRstats[i].username)
+                       GDKfree(USRstats[i].username);
+               if( USRstats[i].maxquery)
+                       GDKfree(USRstats[i].maxquery);
+               clearUSRstats(i); // FIXME: not needed since it's freed below
+       }
+       GDKfree(USRstats);
+       USRstats = NULL;
+       // FIXME: shouldn't reset usrstatscnt?
+       MT_lock_unset(&mal_delayLock);
 }
 
 void
@@ -73,6 +131,10 @@ mal_runtime_reset(void)
        qtag= 1;
        qhead = 0;
        qtail = 0;
+
+       GDKfree(USRstats); // FIXME: where should we free the contents of 
USRstats?
+       USRstats = NULL;
+       usrstatscnt = 0;
 }
 
 static str
@@ -148,7 +210,7 @@ dropQRYqueue(void)
                        GDKfree(QRYqueue[i].query);
                if(QRYqueue[i].username)
                        GDKfree(QRYqueue[i].username);
-               clearQRYqueue(i);
+               clearQRYqueue(i); // FIXME: not needed since it's freed below?
        }
        GDKfree(QRYqueue);
        QRYqueue = NULL;
@@ -163,6 +225,18 @@ runtimeProfileInit(Client cntxt, MalBlkP
        QueryQueue tmp;
 
        MT_lock_set(&mal_delayLock);
+
+       // FIXME: is this the correct init function to initiate this?
+       if(USRstats == NULL){ // FIXME: isn't USRstats always NULL here?
+               usrstatscnt = MAL_MAXCLIENTS;
+               USRstats = (UserStats) GDKzalloc( sizeof (struct USERSTAT) * 
usrstatscnt);
+       }
+       if(USRstats == NULL) {
+               addMalException(mb,"runtimeProfileInit" MAL_MALLOC_FAIL);
+               MT_lock_unset(&mal_delayLock);
+               return;
+       }
+
        tmp = QRYqueue;
        if ( QRYqueue == NULL)
                QRYqueue = (QueryQueue) GDKzalloc( sizeof (struct QRYQUEUE) * 
(qsize= 8)); /* for testing */
@@ -194,7 +268,7 @@ runtimeProfileInit(Client cntxt, MalBlkP
                QRYqueue = (QueryQueue) GDKrealloc( QRYqueue, sizeof (struct 
QRYQUEUE) * qsize);
                if ( QRYqueue == NULL){
                        addMalException(mb,"runtimeProfileInit" 
MAL_MALLOC_FAIL);
-                       GDKfree(tmp);
+                       GDKfree(tmp); // FIXME: shouldn't we free the contents 
of 'tmp'?
                        MT_lock_unset(&mal_delayLock);
                        return;
                }
@@ -235,9 +309,6 @@ runtimeProfileFinish(Client cntxt, MalBl
 {
        size_t i;
 
-       (void) cntxt;
-       (void) mb;
-
        MT_lock_set(&mal_delayLock);
        i=qtail;
        while (i != qhead){
@@ -248,12 +319,12 @@ runtimeProfileFinish(Client cntxt, MalBl
                                mb->tag = stk->tag;
                                break;
                        }
-                       updateUserStats(cntxt, QRYqueue[i].ticks, 
QRYqueue[i].start, QRYqueue[i].finished, QRYqueue[i].query);
                        QRYqueue[i].status = "finished";
                        QRYqueue[i].finished = time(0);
                        QRYqueue[i].cntxt = 0;
                        QRYqueue[i].stk = 0;
                        QRYqueue[i].mb = 0;
+                       updateUserStats(cntxt, mb, QRYqueue[i].ticks, 
QRYqueue[i].start, QRYqueue[i].finished, QRYqueue[i].query);
                        // assume that the user is now idle
                        cntxt->idle = time(0);
                        break;
diff --git a/monetdb5/mal/mal_runtime.h b/monetdb5/mal/mal_runtime.h
--- a/monetdb5/mal/mal_runtime.h
+++ b/monetdb5/mal/mal_runtime.h
@@ -43,20 +43,22 @@ mal_export size_t qhead, qtail, qsize;
 
 /* We keep a few statistics per user to identify unexpected behavior */
 typedef struct USERSTAT{
+       oid user;       /* user id in the auth administration */
        str username;
-       int querycount;
+       lng querycount;
        lng totalticks;
        time_t started;
        time_t finished;
        lng maxticks;
        str maxquery;
 } *UserStats;
+mal_export size_t usrstatscnt;
 
 typedef struct WORKINGSET{
        Client          cntxt;
-    MalBlkPtr   mb;
-    MalStkPtr   stk;
-    InstrPtr    pci;
+       MalBlkPtr   mb;
+       MalStkPtr   stk;
+       InstrPtr    pci;
 } Workingset;
 
 mal_export Workingset workingset[THREADS];
@@ -66,6 +68,7 @@ mal_export void runtimeProfileFinish(Cli
 mal_export void runtimeProfileBegin(Client cntxt, MalBlkPtr mb, MalStkPtr stk, 
InstrPtr pci, RuntimeProfile prof);
 mal_export void runtimeProfileExit(Client cntxt, MalBlkPtr mb, MalStkPtr stk, 
InstrPtr pci, RuntimeProfile prof);
 mal_export void dropQRYqueue(void);
+mal_export void dropUSRstats(void);
 mal_export lng getVolume(MalStkPtr stk, InstrPtr pci, int rd);
 mal_export lng getBatSpace(BAT *b);
 
diff --git a/monetdb5/modules/mal/sysmon.c b/monetdb5/modules/mal/sysmon.c
--- a/monetdb5/modules/mal/sysmon.c
+++ b/monetdb5/modules/mal/sysmon.c
@@ -20,110 +20,110 @@
 str
 SYSMONstatistics(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
 {
-       BAT *user, *querycount, *totalticks, *started, *finished, *query, 
*maxticks;
+       BAT *user, *querycount, *totalticks, *started, *finished, *maxquery, 
*maxticks;
        bat *u = getArgReference_bat(stk,pci,0);
        bat *c = getArgReference_bat(stk,pci,1);
        bat *t = getArgReference_bat(stk,pci,2);
        bat *s = getArgReference_bat(stk,pci,3);
        bat *f = getArgReference_bat(stk,pci,4);
-       bat *q = getArgReference_bat(stk,pci,5);
-       bat *m = getArgReference_bat(stk,pci,6);
-       lng i;
-       int sz;
+       bat *m = getArgReference_bat(stk,pci,5);
+       bat *q = getArgReference_bat(stk,pci,6);
+       size_t i;
        timestamp tsn;
        str msg = MAL_SUCCEED;
 
-       (void) cntxt;
        (void) mb;
-       sz = MAL_MAXCLIENTS; // reserve space for all possible clients.
-       user = COLnew(0, TYPE_str, sz, TRANSIENT);
-       querycount = COLnew(0, TYPE_lng, sz, TRANSIENT);
-       totalticks = COLnew(0, TYPE_lng, sz, TRANSIENT);
-       started = COLnew(0, TYPE_timestamp, sz, TRANSIENT);
-       finished = COLnew(0, TYPE_timestamp, sz, TRANSIENT);
-       query = COLnew(0, TYPE_str, sz, TRANSIENT);
-       maxticks = COLnew(0, TYPE_lng, sz, TRANSIENT);
-       if ( user == NULL || querycount == NULL || totalticks == NULL || 
started == NULL || finished == NULL || query == NULL || maxticks == NULL){
+       user = COLnew(0, TYPE_str, usrstatscnt, TRANSIENT);
+       querycount = COLnew(0, TYPE_lng, usrstatscnt, TRANSIENT);
+       totalticks = COLnew(0, TYPE_lng, usrstatscnt, TRANSIENT);
+       started = COLnew(0, TYPE_timestamp, usrstatscnt, TRANSIENT);
+       finished = COLnew(0, TYPE_timestamp, usrstatscnt, TRANSIENT);
+       maxticks = COLnew(0, TYPE_lng, usrstatscnt, TRANSIENT);
+       maxquery = COLnew(0, TYPE_str, usrstatscnt, TRANSIENT);
+       if (user == NULL || querycount == NULL || totalticks == NULL || started 
== NULL || finished == NULL || maxquery == NULL || maxticks == NULL){
                BBPreclaim(user);
                BBPreclaim(started);
                BBPreclaim(querycount);
                BBPreclaim(totalticks);
                BBPreclaim(finished);
-               BBPreclaim(query);
                BBPreclaim(maxticks);
-               throw(MAL, "SYSMONqueue", SQLSTATE(HY013) MAL_MALLOC_FAIL);
+               BBPreclaim(maxquery);
+               throw(MAL, "SYSMONstatistics", SQLSTATE(HY013) MAL_MALLOC_FAIL);
        }
 
        MT_lock_set(&mal_delayLock);
-       for ( i = 0; i < MAL_MAXCLIENTS; i++)
-               if( USRstats[i].querycount && cntxt->user == MAL_ADMIN ){
-
-                       if (BUNappend(user, USRstats[i].username, false) != 
GDK_SUCCEED) {
_______________________________________________
checkin-list mailing list
[email protected]
https://www.monetdb.org/mailman/listinfo/checkin-list

Reply via email to