Changeset: 7e0b81925e3f for MonetDB URL: https://dev.monetdb.org/hg/MonetDB?cmd=changeset;node=7e0b81925e3f Modified Files: clients/Tests/exports.stable.out Branch: makelibstreamgreatagain Log Message:
Merge branch 'default' into 'makelibstreamgreatagain' diffs (truncated from 90317 to 300 lines): diff --git a/ChangeLog.userstats b/ChangeLog.userstats new file mode 100644 --- /dev/null +++ b/ChangeLog.userstats @@ -0,0 +1,19 @@ +# ChangeLog file for userstats +# This file is updated with Maddlog + +* Mon Aug 10 2020 Ying Zhang <[email protected]> +- Finished a first version of the new monitoring function + user_statistics(), which is only intended for the DBAs. + For each database user who has logged in during the current mserver5 + session, it returns + "username": login name of the database user, + "querycount": the number of queries this user has executed since his/her + first login, + "totalticks": the total execution time (in microsecond) of the queries ran + by this user, + "maxquery": the query with the longest execution time (if two queries have + the same execution time, the newer overwrites the older), + "maxticks": the execution time of the 'maxquery' (in microsecond), + "started": the start timestamp of the 'maxquery', + "finished": the finish timestamp of the 'maxquery'. + diff --git a/clients/Tests/MAL-signatures.stable.out b/clients/Tests/MAL-signatures.stable.out --- a/clients/Tests/MAL-signatures.stable.out +++ b/clients/Tests/MAL-signatures.stable.out @@ -3788,15 +3788,22 @@ stdout of test 'MAL-signatures` in direc [ "batcalc", "cmp", "pattern batcalc.cmp(X_1:str, X_2:bat[:str]):bat[:bte] ", "CMDbatCMP;", "" ] [ "batcalc", "cmp", "pattern batcalc.cmp(X_1:str, X_2:bat[:str], X_3:bat[:oid]):bat[:bte] ", "CMDbatCMP;", "" ] [ "batcalc", "date", "command batcalc.date(X_1:bat[:date]):bat[:date] ", "MTIMEdate_date_bulk;", "" ] +[ "batcalc", "date", "command batcalc.date(X_1:bat[:date], X_2:bat[:oid]):bat[:date] ", "MTIMEdate_date_bulk_cand;", "" ] [ "batcalc", "date", "command batcalc.date(X_1:bat[:str]):bat[:date] ", "MTIMEdate_fromstr_bulk;", "" ] +[ "batcalc", "date", "command batcalc.date(X_1:bat[:str], X_2:bat[:oid]):bat[:date] ", "MTIMEdate_fromstr_bulk_cand;", "" ] [ "batcalc", "date", "command batcalc.date(X_1:bat[:timestamp]):bat[:date] ", "MTIMEtimestamp_extract_date_bulk;", "" ] +[ "batcalc", "date", "command batcalc.date(X_1:bat[:timestamp], X_2:bat[:oid]):bat[:date] ", "MTIMEtimestamp_extract_date_bulk_cand;", "" ] [ "batcalc", "date", "command batcalc.date(X_1:bat[:str]):bat[:date] ", "batstr_2_date;", "" ] [ "batcalc", "date", "command batcalc.date(X_1:bat[:str], X_2:bat[:oid]):bat[:date] ", "batstr_2_date_cand;", "" ] [ "batcalc", "date", "pattern batcalc.date(X_1:bat[:oid]):bat[:date] ", "nil_2_date;", "" ] [ "batcalc", "daytime", "command batcalc.daytime(X_1:bat[:daytime]):bat[:daytime] ", "MTIMEdaytime_daytime_bulk;", "" ] +[ "batcalc", "daytime", "command batcalc.daytime(X_1:bat[:daytime], X_2:bat[:oid]):bat[:daytime] ", "MTIMEdaytime_daytime_bulk_cand;", "" ] [ "batcalc", "daytime", "command batcalc.daytime(X_1:bat[:lng]):bat[:daytime] ", "MTIMEdaytime_fromseconds_bulk;", "" ] +[ "batcalc", "daytime", "command batcalc.daytime(X_1:bat[:lng], X_2:bat[:oid]):bat[:daytime] ", "MTIMEdaytime_fromseconds_bulk_cand;", "" ] [ "batcalc", "daytime", "command batcalc.daytime(X_1:bat[:str]):bat[:daytime] ", "MTIMEdaytime_fromstr_bulk;", "" ] +[ "batcalc", "daytime", "command batcalc.daytime(X_1:bat[:str], X_2:bat[:oid]):bat[:daytime] ", "MTIMEdaytime_fromstr_bulk_cand;", "" ] [ "batcalc", "daytime", "command batcalc.daytime(X_1:bat[:timestamp]):bat[:daytime] ", "MTIMEtimestamp_extract_daytime_bulk;", "" ] +[ "batcalc", "daytime", "command batcalc.daytime(X_1:bat[:timestamp], X_2:bat[:oid]):bat[:daytime] ", "MTIMEtimestamp_extract_daytime_bulk_cand;", "" ] [ "batcalc", "daytime", "command batcalc.daytime(X_1:bat[:str], X_2:int):bat[:daytime] ", "batstr_2time_daytime;", "" ] [ "batcalc", "daytime", "command batcalc.daytime(X_1:bat[:str], X_2:bat[:oid], X_3:int):bat[:daytime] ", "batstr_2time_daytime_cand;", "" ] [ "batcalc", "daytime", "pattern batcalc.daytime(X_1:bat[:daytime], X_2:bat[:oid], X_3:int):bat[:daytime] ", "daytime_2time_daytime;", "" ] @@ -5275,10 +5282,15 @@ stdout of test 'MAL-signatures` in direc [ "batcalc", "sub_noerror", "pattern batcalc.sub_noerror(X_1:sht, X_2:bat[:lng], X_3:bat[:oid]):bat[:lng] ", "CMDbatSUB;", "" ] [ "batcalc", "sub_noerror", "pattern batcalc.sub_noerror(X_1:sht, X_2:bat[:sht], X_3:bat[:oid]):bat[:sht] ", "CMDbatSUB;", "" ] [ "batcalc", "timestamp", "command batcalc.timestamp(X_1:bat[:date]):bat[:timestamp] ", "MTIMEtimestamp_fromdate_bulk;", "" ] +[ "batcalc", "timestamp", "command batcalc.timestamp(X_1:bat[:date], X_2:bat[:oid]):bat[:timestamp] ", "MTIMEtimestamp_fromdate_bulk_cand;", "" ] [ "batcalc", "timestamp", "command batcalc.timestamp(X_1:bat[:lng]):bat[:timestamp] ", "MTIMEtimestamp_frommsec_bulk;", "" ] +[ "batcalc", "timestamp", "command batcalc.timestamp(X_1:bat[:lng], X_2:bat[:oid]):bat[:timestamp] ", "MTIMEtimestamp_frommsec_bulk_cand;", "" ] [ "batcalc", "timestamp", "command batcalc.timestamp(X_1:bat[:int]):bat[:timestamp] ", "MTIMEtimestamp_fromsecond_bulk;", "" ] +[ "batcalc", "timestamp", "command batcalc.timestamp(X_1:bat[:int], X_2:bat[:oid]):bat[:timestamp] ", "MTIMEtimestamp_fromsecond_bulk_cand;", "" ] [ "batcalc", "timestamp", "command batcalc.timestamp(X_1:bat[:str]):bat[:timestamp] ", "MTIMEtimestamp_fromstr_bulk;", "" ] +[ "batcalc", "timestamp", "command batcalc.timestamp(X_1:bat[:str], X_2:bat[:oid]):bat[:timestamp] ", "MTIMEtimestamp_fromstr_bulk_cand;", "" ] [ "batcalc", "timestamp", "command batcalc.timestamp(X_1:bat[:timestamp]):bat[:timestamp] ", "MTIMEtimestamp_timestamp_bulk;", "" ] +[ "batcalc", "timestamp", "command batcalc.timestamp(X_1:bat[:timestamp], X_2:bat[:oid]):bat[:timestamp] ", "MTIMEtimestamp_timestamp_bulk_cand;", "" ] [ "batcalc", "timestamp", "command batcalc.timestamp(X_1:bat[:str], X_2:int):bat[:timestamp] ", "batstr_2time_timestamp;", "" ] [ "batcalc", "timestamp", "command batcalc.timestamp(X_1:bat[:str], X_2:bat[:oid], X_3:int):bat[:timestamp] ", "batstr_2time_timestamp_cand;", "" ] [ "batcalc", "timestamp", "command batcalc.timestamp(X_1:bat[:str], X_2:int, X_3:int):bat[:timestamp] ", "batstr_2time_timestamptz;", "" ] @@ -8754,6 +8766,7 @@ stdout of test 'MAL-signatures` in direc [ "sysmon", "stop", "pattern sysmon.stop(X_1:int):void ", "SYSMONstop;", "" ] [ "sysmon", "stop", "pattern sysmon.stop(X_1:lng):void ", "SYSMONstop;", "" ] [ "sysmon", "stop", "pattern sysmon.stop(X_1:sht):void ", "SYSMONstop;", "" ] +[ "sysmon", "user_statistics", "pattern sysmon.user_statistics() (X_0:bat[:str], X_1:bat[:lng], X_2:bat[:lng], X_3:bat[:timestamp], X_4:bat[:timestamp], X_5:bat[:lng], X_6:bat[:str]) ", "SYSMONstatistics;", "" ] [ "tokenizer", "append", "command tokenizer.append(X_1:str):oid ", "TKNZRappend;", "" ] [ "tokenizer", "close", "command tokenizer.close():void ", "TKNZRclose;", "" ] [ "tokenizer", "depositFile", "command tokenizer.depositFile(X_1:str):void ", "TKNZRdepositFile;", "" ] diff --git a/clients/Tests/MAL-signatures.stable.out.int128 b/clients/Tests/MAL-signatures.stable.out.int128 --- a/clients/Tests/MAL-signatures.stable.out.int128 +++ b/clients/Tests/MAL-signatures.stable.out.int128 @@ -5336,15 +5336,22 @@ stdout of test 'MAL-signatures` in direc [ "batcalc", "cmp", "pattern batcalc.cmp(X_1:str, X_2:bat[:str]):bat[:bte] ", "CMDbatCMP;", "" ] [ "batcalc", "cmp", "pattern batcalc.cmp(X_1:str, X_2:bat[:str], X_3:bat[:oid]):bat[:bte] ", "CMDbatCMP;", "" ] [ "batcalc", "date", "command batcalc.date(X_1:bat[:date]):bat[:date] ", "MTIMEdate_date_bulk;", "" ] +[ "batcalc", "date", "command batcalc.date(X_1:bat[:date], X_2:bat[:oid]):bat[:date] ", "MTIMEdate_date_bulk_cand;", "" ] [ "batcalc", "date", "command batcalc.date(X_1:bat[:str]):bat[:date] ", "MTIMEdate_fromstr_bulk;", "" ] +[ "batcalc", "date", "command batcalc.date(X_1:bat[:str], X_2:bat[:oid]):bat[:date] ", "MTIMEdate_fromstr_bulk_cand;", "" ] [ "batcalc", "date", "command batcalc.date(X_1:bat[:timestamp]):bat[:date] ", "MTIMEtimestamp_extract_date_bulk;", "" ] +[ "batcalc", "date", "command batcalc.date(X_1:bat[:timestamp], X_2:bat[:oid]):bat[:date] ", "MTIMEtimestamp_extract_date_bulk_cand;", "" ] [ "batcalc", "date", "command batcalc.date(X_1:bat[:str]):bat[:date] ", "batstr_2_date;", "" ] [ "batcalc", "date", "command batcalc.date(X_1:bat[:str], X_2:bat[:oid]):bat[:date] ", "batstr_2_date_cand;", "" ] [ "batcalc", "date", "pattern batcalc.date(X_1:bat[:oid]):bat[:date] ", "nil_2_date;", "" ] [ "batcalc", "daytime", "command batcalc.daytime(X_1:bat[:daytime]):bat[:daytime] ", "MTIMEdaytime_daytime_bulk;", "" ] +[ "batcalc", "daytime", "command batcalc.daytime(X_1:bat[:daytime], X_2:bat[:oid]):bat[:daytime] ", "MTIMEdaytime_daytime_bulk_cand;", "" ] [ "batcalc", "daytime", "command batcalc.daytime(X_1:bat[:lng]):bat[:daytime] ", "MTIMEdaytime_fromseconds_bulk;", "" ] +[ "batcalc", "daytime", "command batcalc.daytime(X_1:bat[:lng], X_2:bat[:oid]):bat[:daytime] ", "MTIMEdaytime_fromseconds_bulk_cand;", "" ] [ "batcalc", "daytime", "command batcalc.daytime(X_1:bat[:str]):bat[:daytime] ", "MTIMEdaytime_fromstr_bulk;", "" ] +[ "batcalc", "daytime", "command batcalc.daytime(X_1:bat[:str], X_2:bat[:oid]):bat[:daytime] ", "MTIMEdaytime_fromstr_bulk_cand;", "" ] [ "batcalc", "daytime", "command batcalc.daytime(X_1:bat[:timestamp]):bat[:daytime] ", "MTIMEtimestamp_extract_daytime_bulk;", "" ] +[ "batcalc", "daytime", "command batcalc.daytime(X_1:bat[:timestamp], X_2:bat[:oid]):bat[:daytime] ", "MTIMEtimestamp_extract_daytime_bulk_cand;", "" ] [ "batcalc", "daytime", "command batcalc.daytime(X_1:bat[:str], X_2:int):bat[:daytime] ", "batstr_2time_daytime;", "" ] [ "batcalc", "daytime", "command batcalc.daytime(X_1:bat[:str], X_2:bat[:oid], X_3:int):bat[:daytime] ", "batstr_2time_daytime_cand;", "" ] [ "batcalc", "daytime", "pattern batcalc.daytime(X_1:bat[:daytime], X_2:bat[:oid], X_3:int):bat[:daytime] ", "daytime_2time_daytime;", "" ] @@ -7514,10 +7521,15 @@ stdout of test 'MAL-signatures` in direc [ "batcalc", "sub_noerror", "pattern batcalc.sub_noerror(X_1:sht, X_2:bat[:lng], X_3:bat[:oid]):bat[:lng] ", "CMDbatSUB;", "" ] [ "batcalc", "sub_noerror", "pattern batcalc.sub_noerror(X_1:sht, X_2:bat[:sht], X_3:bat[:oid]):bat[:sht] ", "CMDbatSUB;", "" ] [ "batcalc", "timestamp", "command batcalc.timestamp(X_1:bat[:date]):bat[:timestamp] ", "MTIMEtimestamp_fromdate_bulk;", "" ] +[ "batcalc", "timestamp", "command batcalc.timestamp(X_1:bat[:date], X_2:bat[:oid]):bat[:timestamp] ", "MTIMEtimestamp_fromdate_bulk_cand;", "" ] [ "batcalc", "timestamp", "command batcalc.timestamp(X_1:bat[:lng]):bat[:timestamp] ", "MTIMEtimestamp_frommsec_bulk;", "" ] +[ "batcalc", "timestamp", "command batcalc.timestamp(X_1:bat[:lng], X_2:bat[:oid]):bat[:timestamp] ", "MTIMEtimestamp_frommsec_bulk_cand;", "" ] [ "batcalc", "timestamp", "command batcalc.timestamp(X_1:bat[:int]):bat[:timestamp] ", "MTIMEtimestamp_fromsecond_bulk;", "" ] +[ "batcalc", "timestamp", "command batcalc.timestamp(X_1:bat[:int], X_2:bat[:oid]):bat[:timestamp] ", "MTIMEtimestamp_fromsecond_bulk_cand;", "" ] [ "batcalc", "timestamp", "command batcalc.timestamp(X_1:bat[:str]):bat[:timestamp] ", "MTIMEtimestamp_fromstr_bulk;", "" ] +[ "batcalc", "timestamp", "command batcalc.timestamp(X_1:bat[:str], X_2:bat[:oid]):bat[:timestamp] ", "MTIMEtimestamp_fromstr_bulk_cand;", "" ] [ "batcalc", "timestamp", "command batcalc.timestamp(X_1:bat[:timestamp]):bat[:timestamp] ", "MTIMEtimestamp_timestamp_bulk;", "" ] +[ "batcalc", "timestamp", "command batcalc.timestamp(X_1:bat[:timestamp], X_2:bat[:oid]):bat[:timestamp] ", "MTIMEtimestamp_timestamp_bulk_cand;", "" ] [ "batcalc", "timestamp", "command batcalc.timestamp(X_1:bat[:str], X_2:int):bat[:timestamp] ", "batstr_2time_timestamp;", "" ] [ "batcalc", "timestamp", "command batcalc.timestamp(X_1:bat[:str], X_2:bat[:oid], X_3:int):bat[:timestamp] ", "batstr_2time_timestamp_cand;", "" ] [ "batcalc", "timestamp", "command batcalc.timestamp(X_1:bat[:str], X_2:int, X_3:int):bat[:timestamp] ", "batstr_2time_timestamptz;", "" ] @@ -11718,6 +11730,7 @@ stdout of test 'MAL-signatures` in direc [ "sysmon", "stop", "pattern sysmon.stop(X_1:int):void ", "SYSMONstop;", "" ] [ "sysmon", "stop", "pattern sysmon.stop(X_1:lng):void ", "SYSMONstop;", "" ] [ "sysmon", "stop", "pattern sysmon.stop(X_1:sht):void ", "SYSMONstop;", "" ] +[ "sysmon", "user_statistics", "pattern sysmon.user_statistics() (X_0:bat[:str], X_1:bat[:lng], X_2:bat[:lng], X_3:bat[:timestamp], X_4:bat[:timestamp], X_5:bat[:lng], X_6:bat[:str]) ", "SYSMONstatistics;", "" ] [ "tokenizer", "append", "command tokenizer.append(X_1:str):oid ", "TKNZRappend;", "" ] [ "tokenizer", "close", "command tokenizer.close():void ", "TKNZRclose;", "" ] [ "tokenizer", "depositFile", "command tokenizer.depositFile(X_1:str):void ", "TKNZRdepositFile;", "" ] diff --git a/clients/Tests/exports.stable.out b/clients/Tests/exports.stable.out --- a/clients/Tests/exports.stable.out +++ b/clients/Tests/exports.stable.out @@ -1126,6 +1126,7 @@ str STRtostr(str *res, const str *src); str SYSMONpause(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci); str SYSMONqueue(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci); str SYSMONresume(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci); +str SYSMONstatistics(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci); str SYSMONstop(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci); str SYScpuStatistics(bat *ret, bat *ret2); str SYSgdkEnv(bat *ret, bat *ret2); @@ -1176,6 +1177,7 @@ str TRNtrans_commit(Client cntxt, MalBlk int TYPE_blob; int TYPE_uuid; int TYPE_xml; +UserStats USRstats; str WLCaction(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci); str WLCappend(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci); str WLCcatalog(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci); @@ -1837,6 +1839,7 @@ str updateRef; void updateScenario(str scen, str nme, MALfcn fcn); Module userModule(void); str userRef; +size_t usrstatscnt; str vectorRef; str window_boundRef; str wlcRef; diff --git a/documentation/source/developers_handbook.rst b/documentation/source/developers_handbook.rst --- a/documentation/source/developers_handbook.rst +++ b/documentation/source/developers_handbook.rst @@ -25,13 +25,45 @@ are stored in ``.stable.out`` and ``.sta the same directory. For example in the directory ``sql/test/json/Tests`` we find the files: -* ``jsonkeyarray.sql`` -* ``jsonkeyarray.stable.out`` -* ``jsonkeyarray.stable.err`` +* ``jsonkeyarray.Bug-6858.sql`` +* ``jsonkeyarray.Bug-6858.stable.out`` +* ``jsonkeyarray.Bug-6858.stable.err`` + +These three files define the test ``jsonkeyarray.Bug-6858``. + +In any directory with tests you will also find a file named ``All``. This is an +index of all the tests in the directory. If a test is not mentioned in the +``All`` file, it will not run. Running tests ------------- +The `canonical` name of a test is its path relative to the root of the source +tree (``$src_root``), with the ``Tests`` component and the suffix removed. For +instance the above test's canonical name is +``sql/test/json/jsonkeyarray.Bug-6858``. + +In order to run a single test from ``$src_root`` or any of its descendants, use +its canonical name as the argument of ``Mtest.py``:: + + [$src_root]$ Mtest.py sql/test/json/jsonkeyarray.Bug-6858 + +Alternatively, from within the directory where the test is defined you can run +the test by giving the last component of its canonical name as the argument to +``Mtest.py``:: + + [$src_root/sql/test/json/Tests/]$ Mtest.py jsonkeyarray.Bug-6858 + +Another way to run tests is running a number of them together using the +canonical name of a group. For example to run all the tests in the ``json`` +group of the ``sql`` component use:: + + [$src_root]$ Mtest.py sql/test/json/ + +This will run all the tests defined in the file +``$src_root/sql/test/json/Tests/All``. + + Adding a new test ----------------- 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 @@ -30,6 +30,97 @@ QueryQueue QRYqueue = NULL; 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 +size_t +getUSRstatsIdx(MalBlkPtr mb, oid user) +{ + 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; + + /* 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 (USRstats[idx].username == NULL) { + USRstats[idx].user = cntxt->user; + USRstats[idx].username = GDKstrdup(cntxt->username); + } + USRstats[idx].querycount++; + USRstats[idx].totalticks += ticks; + if( ticks >= USRstats[idx].maxticks && query){ + USRstats[idx].started = started; + USRstats[idx].finished = finished; + USRstats[idx].maxticks = ticks; + GDKfree(USRstats[idx].maxquery); + USRstats[idx].maxquery= GDKstrdup(query); + } +} + +/* + * Free up the whole USRstats before mserver5 exits. + */ +static void +dropUSRstats(void) +{ + size_t i; + MT_lock_set(&mal_delayLock); + for(i = 0; i < usrstatscnt; i++){ + GDKfree(USRstats[i].username); + GDKfree(USRstats[i].maxquery); + clearUSRstats(i); + } + GDKfree(USRstats); + USRstats = NULL; + MT_lock_unset(&mal_delayLock); +} _______________________________________________ checkin-list mailing list [email protected] https://www.monetdb.org/mailman/listinfo/checkin-list
