| 1988 | */ |
| 1989 | #define CLIENTS_CRON_MIN_ITERATIONS 5 |
| 1990 | void clientsCron(int iel) { |
| 1991 | /* Try to process at least numclients/g_pserver->hz of clients |
| 1992 | * per call. Since normally (if there are no big latency events) this |
| 1993 | * function is called g_pserver->hz times per second, in the average case we |
| 1994 | * process all the clients in 1 second. */ |
| 1995 | int numclients = listLength(g_pserver->clients); |
| 1996 | int iterations = numclients/g_pserver->hz; |
| 1997 | mstime_t now = mstime(); |
| 1998 | |
| 1999 | /* Process at least a few clients while we are at it, even if we need |
| 2000 | * to process less than CLIENTS_CRON_MIN_ITERATIONS to meet our contract |
| 2001 | * of processing each client once per second. */ |
| 2002 | if (iterations < CLIENTS_CRON_MIN_ITERATIONS) |
| 2003 | iterations = (numclients < CLIENTS_CRON_MIN_ITERATIONS) ? |
| 2004 | numclients : CLIENTS_CRON_MIN_ITERATIONS; |
| 2005 | |
| 2006 | |
| 2007 | int curr_peak_mem_usage_slot = g_pserver->unixtime % CLIENTS_PEAK_MEM_USAGE_SLOTS; |
| 2008 | /* Always zero the next sample, so that when we switch to that second, we'll |
| 2009 | * only register samples that are greater in that second without considering |
| 2010 | * the history of such slot. |
| 2011 | * |
| 2012 | * Note: our index may jump to any random position if serverCron() is not |
| 2013 | * called for some reason with the normal frequency, for instance because |
| 2014 | * some slow command is called taking multiple seconds to execute. In that |
| 2015 | * case our array may end containing data which is potentially older |
| 2016 | * than CLIENTS_PEAK_MEM_USAGE_SLOTS seconds: however this is not a problem |
| 2017 | * since here we want just to track if "recently" there were very expansive |
| 2018 | * clients from the POV of memory usage. */ |
| 2019 | int zeroidx = (curr_peak_mem_usage_slot+1) % CLIENTS_PEAK_MEM_USAGE_SLOTS; |
| 2020 | ClientsPeakMemInput[zeroidx] = 0; |
| 2021 | ClientsPeakMemOutput[zeroidx] = 0; |
| 2022 | |
| 2023 | |
| 2024 | while(listLength(g_pserver->clients) && iterations--) { |
| 2025 | client *c; |
| 2026 | listNode *head; |
| 2027 | /* Rotate the list, take the current head, process. |
| 2028 | * This way if the client must be removed from the list it's the |
| 2029 | * first element and we don't incur into O(N) computation. */ |
| 2030 | listRotateTailToHead(g_pserver->clients); |
| 2031 | head = (listNode*)listFirst(g_pserver->clients); |
| 2032 | c = (client*)listNodeValue(head); |
| 2033 | if (c->iel == iel) |
| 2034 | { |
| 2035 | fastlock_lock(&c->lock); |
| 2036 | /* The following functions do different service checks on the client. |
| 2037 | * The protocol is that they return non-zero if the client was |
| 2038 | * terminated. */ |
| 2039 | if (clientsCronHandleTimeout(c,now)) continue; // Client free'd so don't release the lock |
| 2040 | if (clientsCronResizeQueryBuffer(c)) goto LContinue; |
| 2041 | if (clientsCronTrackExpansiveClients(c, curr_peak_mem_usage_slot)) goto LContinue; |
| 2042 | if (clientsCronTrackClientsMemUsage(c)) goto LContinue; |
| 2043 | if (closeClientOnOutputBufferLimitReached(c, 0)) continue; // Client also free'd |
| 2044 | if (closeClientOnOverload(c)) continue; |
| 2045 | LContinue: |
| 2046 | fastlock_unlock(&c->lock); |
| 2047 | } |
no test coverage detected