|
| 1 | +<?php |
| 2 | +/* |
| 3 | + +-------------------------------------------------------------------------+ |
| 4 | + | Copyright (C) 2004-2026 The Cacti Group | |
| 5 | + | | |
| 6 | + | This program is free software; you can redistribute it and/or | |
| 7 | + | modify it under the terms of the GNU General Public License | |
| 8 | + | as published by the Free Software Foundation; either version 2 | |
| 9 | + | of the License, or (at your option) any later version. | |
| 10 | + +-------------------------------------------------------------------------+ |
| 11 | + | Cacti: The Complete RRDtool-based Graphing Solution | |
| 12 | + +-------------------------------------------------------------------------+ |
| 13 | +*/ |
| 14 | + |
| 15 | +/* |
| 16 | + * api_data_source_disable_multi() reassigned $poller_ids on every 1000-id |
| 17 | + * chunk, so the trailing api_data_source_cache_crc_update loop only saw |
| 18 | + * the pollers attached to the last chunk. Pollers that owned only items |
| 19 | + * in earlier chunks kept stale poller_output_boost rows and their CRCs |
| 20 | + * never advanced, so remote pollers continued to push data for already- |
| 21 | + * disabled local_data_ids until the next manual cache rebuild. The fix |
| 22 | + * is an $all_poller_ids accumulator built with the `+` union so keys |
| 23 | + * survive across chunks and the CRC update covers every poller touched. |
| 24 | + */ |
| 25 | + |
| 26 | +$source = file_get_contents(__DIR__ . '/../../lib/api_data_source.php'); |
| 27 | + |
| 28 | +$start = strpos($source, 'function api_data_source_disable_multi('); |
| 29 | +expect($start)->not->toBeFalse(); |
| 30 | + |
| 31 | +$end = strpos($source, "\nfunction ", $start + 1); |
| 32 | +$body = substr($source, $start, $end !== false ? $end - $start : 8000); |
| 33 | + |
| 34 | +test('api_data_source_disable_multi initialises the accumulator once', function () use ($body) { |
| 35 | + expect($body)->toContain('$all_poller_ids = array();'); |
| 36 | + |
| 37 | + /* Only one initialisation, otherwise we are clobbering across chunks again. */ |
| 38 | + expect(substr_count($body, '$all_poller_ids = array();'))->toBe(1); |
| 39 | +}); |
| 40 | + |
| 41 | +test('both chunk paths append to $all_poller_ids', function () use ($body) { |
| 42 | + expect(substr_count($body, '$all_poller_ids = $all_poller_ids + $poller_ids;'))->toBe(2); |
| 43 | +}); |
| 44 | + |
| 45 | +test('trailing CRC update reads $all_poller_ids, not the last-chunk local', function () use ($body) { |
| 46 | + expect($body)->toContain('if (cacti_sizeof($all_poller_ids))'); |
| 47 | + expect($body)->toContain('foreach ($all_poller_ids as $poller_id)'); |
| 48 | + expect($body)->toContain('api_data_source_cache_crc_update($poller_id);'); |
| 49 | + |
| 50 | + $crcSection = substr($body, strpos($body, 'if (cacti_sizeof($all_poller_ids))')); |
| 51 | + expect(strpos($crcSection, 'if (cacti_sizeof($poller_ids))')) |
| 52 | + ->toBeFalse('the trailing CRC guard must not fall back to $poller_ids'); |
| 53 | +}); |
| 54 | + |
| 55 | +test('chunk union behaviour: $all_poller_ids preserves pollers from every chunk', function () { |
| 56 | + /* Build a fixture that mirrors the production layout: 2500 local_data_ids, |
| 57 | + * the first 1000 attached to poller 1, the next 1000 split between |
| 58 | + * pollers 2 and 3, the final 500 attached to poller 4. The old single- |
| 59 | + * $poller_ids shape only retained poller 4 after the loop completed. */ |
| 60 | + $chunks = [ |
| 61 | + array_fill_keys([1], 1), /* chunk 1: poller 1 only */ |
| 62 | + array_fill_keys([2, 3], null), /* chunk 2: pollers 2 and 3 */ |
| 63 | + array_fill_keys([4], 4), /* chunk 3: poller 4 only */ |
| 64 | + ]; |
| 65 | + |
| 66 | + /* Old shape: $poller_ids is reassigned every chunk. */ |
| 67 | + $poller_ids = array(); |
| 68 | + foreach ($chunks as $chunk) { |
| 69 | + $poller_ids = array_combine(array_keys($chunk), array_keys($chunk)); |
| 70 | + } |
| 71 | + expect(array_keys($poller_ids))->toBe([4]); |
| 72 | + |
| 73 | + /* New shape: $all_poller_ids accumulates via `+` union. */ |
| 74 | + $all_poller_ids = array(); |
| 75 | + foreach ($chunks as $chunk) { |
| 76 | + $poller_ids = array_combine(array_keys($chunk), array_keys($chunk)); |
| 77 | + $all_poller_ids = $all_poller_ids + $poller_ids; |
| 78 | + } |
| 79 | + $keys = array_keys($all_poller_ids); |
| 80 | + sort($keys); |
| 81 | + expect($keys)->toBe([1, 2, 3, 4]); |
| 82 | +}); |
0 commit comments