| 1799 | */ |
| 1800 | #define CLIENTS_CRON_MIN_ITERATIONS 5 |
| 1801 | void clientsCron(void) { |
| 1802 | /* Try to process at least numclients/server.hz of clients |
| 1803 | * per call. Since normally (if there are no big latency events) this |
| 1804 | * function is called server.hz times per second, in the average case we |
| 1805 | * process all the clients in 1 second. */ |
| 1806 | int numclients = listLength(server.clients); |
| 1807 | int iterations = numclients/server.hz; |
| 1808 | mstime_t now = mstime(); |
| 1809 | |
| 1810 | /* Process at least a few clients while we are at it, even if we need |
| 1811 | * to process less than CLIENTS_CRON_MIN_ITERATIONS to meet our contract |
| 1812 | * of processing each client once per second. */ |
| 1813 | if (iterations < CLIENTS_CRON_MIN_ITERATIONS) |
| 1814 | iterations = (numclients < CLIENTS_CRON_MIN_ITERATIONS) ? |
| 1815 | numclients : CLIENTS_CRON_MIN_ITERATIONS; |
| 1816 | |
| 1817 | |
| 1818 | int curr_peak_mem_usage_slot = server.unixtime % CLIENTS_PEAK_MEM_USAGE_SLOTS; |
| 1819 | /* Always zero the next sample, so that when we switch to that second, we'll |
| 1820 | * only register samples that are greater in that second without considering |
| 1821 | * the history of such slot. |
| 1822 | * |
| 1823 | * Note: our index may jump to any random position if serverCron() is not |
| 1824 | * called for some reason with the normal frequency, for instance because |
| 1825 | * some slow command is called taking multiple seconds to execute. In that |
| 1826 | * case our array may end containing data which is potentially older |
| 1827 | * than CLIENTS_PEAK_MEM_USAGE_SLOTS seconds: however this is not a problem |
| 1828 | * since here we want just to track if "recently" there were very expansive |
| 1829 | * clients from the POV of memory usage. */ |
| 1830 | int zeroidx = (curr_peak_mem_usage_slot+1) % CLIENTS_PEAK_MEM_USAGE_SLOTS; |
| 1831 | ClientsPeakMemInput[zeroidx] = 0; |
| 1832 | ClientsPeakMemOutput[zeroidx] = 0; |
| 1833 | |
| 1834 | |
| 1835 | while(listLength(server.clients) && iterations--) { |
| 1836 | client *c; |
| 1837 | listNode *head; |
| 1838 | |
| 1839 | /* Rotate the list, take the current head, process. |
| 1840 | * This way if the client must be removed from the list it's the |
| 1841 | * first element and we don't incur into O(N) computation. */ |
| 1842 | listRotateTailToHead(server.clients); |
| 1843 | head = listFirst(server.clients); |
| 1844 | c = listNodeValue(head); |
| 1845 | /* The following functions do different service checks on the client. |
| 1846 | * The protocol is that they return non-zero if the client was |
| 1847 | * terminated. */ |
| 1848 | if (clientsCronHandleTimeout(c,now)) continue; |
| 1849 | if (clientsCronResizeQueryBuffer(c)) continue; |
| 1850 | if (clientsCronTrackExpansiveClients(c, curr_peak_mem_usage_slot)) continue; |
| 1851 | if (clientsCronTrackClientsMemUsage(c)) continue; |
| 1852 | if (closeClientOnOutputBufferLimitReached(c, 0)) continue; |
| 1853 | } |
| 1854 | } |
| 1855 | |
| 1856 | /* This function handles 'background' operations we are required to do |
| 1857 | * incrementally in Redis databases, such as active key expiring, resizing, |
no test coverage detected