Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package memcached for openSUSE:Factory checked in at 2026-04-11 22:22:39 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/memcached (Old) and /work/SRC/openSUSE:Factory/.memcached.new.21863 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "memcached" Sat Apr 11 22:22:39 2026 rev:72 rq:1345699 version:1.6.41 Changes: -------- --- /work/SRC/openSUSE:Factory/memcached/memcached.changes 2026-02-06 19:06:44.975017467 +0100 +++ /work/SRC/openSUSE:Factory/.memcached.new.21863/memcached.changes 2026-04-11 22:22:40.411290714 +0200 @@ -1,0 +2,19 @@ +Thu Apr 9 09:12:47 UTC 2026 - Dirk Müller <[email protected]> + +- update to 1.6.41: + * tests: make slabs-reassign2 test more resilient + * proxy: reduce flakiness in t/proxyunits.t + * proxy: fix off by one in temp string with 250b key + * slabs: fix hang and crash. + * Fix failing proxy*.t tests on some systems like OL8 + * Account for absent 'ssl_proto_errors' in stats during SSL tests + * Fix test compatibility on IPv6-only systems. + * Use SSLv23 method when TLSv1.3 is unsupported (e.g., macOS) + * extstore: more compaction write patience + * parser: fix lru command regression + * Fix: avoid null print for slab busy reason + * extstore: testing around rescued compaction items + * extstore: fix compaction checks wrong refcount + * proto: armor against empty commands + +------------------------------------------------------------------- Old: ---- memcached-1.6.40.tar.gz New: ---- memcached-1.6.41.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ memcached.spec ++++++ --- /var/tmp/diff_new_pack.tXBDol/_old 2026-04-11 22:22:41.171321825 +0200 +++ /var/tmp/diff_new_pack.tXBDol/_new 2026-04-11 22:22:41.187322481 +0200 @@ -42,7 +42,7 @@ %endif %endif Name: memcached -Version: 1.6.40 +Version: 1.6.41 Release: 0 Summary: A high-performance, distributed memory object caching system License: BSD-3-Clause ++++++ memcached-1.6.40.tar.gz -> memcached-1.6.41.tar.gz ++++++ ++++ 2704 lines of diff (skipped) ++++ retrying with extended exclude list diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/memcached-1.6.40/authfile.c new/memcached-1.6.41/authfile.c --- old/memcached-1.6.40/authfile.c 2025-12-17 01:16:18.000000000 +0100 +++ new/memcached-1.6.41/authfile.c 2026-03-06 21:46:28.000000000 +0100 @@ -33,6 +33,10 @@ char *auth_data = NULL; auth_t auth_entries[MAX_ENTRIES]; + if (!file || strlen(file) == 0) { + return AUTHFILE_OPENFAIL; + } + FILE *pwfile = fopen(file, "r"); if (pwfile == NULL) { return AUTHFILE_OPENFAIL; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/memcached-1.6.40/doc/Makefile new/memcached-1.6.41/doc/Makefile --- old/memcached-1.6.40/doc/Makefile 2025-12-17 01:17:57.000000000 +0100 +++ new/memcached-1.6.41/doc/Makefile 2026-03-06 22:03:59.000000000 +0100 @@ -187,17 +187,17 @@ LIBS = -levent LTLIBOBJS = MAKEINFO = ${SHELL} '/home/dormando/p/code/memcached/missing' makeinfo -MKDIR_P = /usr/bin/mkdir -p +MKDIR_P = mkdir -p OBJEXT = o OPENSSL_CFLAGS = OPENSSL_LIBS = PACKAGE = memcached PACKAGE_BUGREPORT = [email protected] PACKAGE_NAME = memcached -PACKAGE_STRING = memcached 1.6.40 +PACKAGE_STRING = memcached 1.6.41 PACKAGE_TARNAME = memcached PACKAGE_URL = -PACKAGE_VERSION = 1.6.40 +PACKAGE_VERSION = 1.6.41 PATH_SEPARATOR = : PKG_CONFIG = /usr/bin/pkg-config PKG_CONFIG_LIBDIR = @@ -208,7 +208,7 @@ SET_MAKE = SHELL = /bin/bash STRIP = -VERSION = 1.6.40 +VERSION = 1.6.41 XML2RFC = no XSLTPROC = no abs_builddir = /home/dormando/p/code/memcached/doc diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/memcached-1.6.40/logger.c new/memcached-1.6.41/logger.c --- old/memcached-1.6.40/logger.c 2025-10-22 06:59:10.000000000 +0200 +++ new/memcached-1.6.41/logger.c 2026-03-06 21:46:28.000000000 +0100 @@ -530,7 +530,7 @@ "type=compact_read_start id=%lu offset=%llu" }, [LOGGER_COMPACT_READ_END] = {512, LOG_SYSEVENTS, _logger_log_text, _logger_parse_text, - "type=compact_read_end id=%lu offset=%llu rescues=%lu lost=%lu skipped=%lu" + "type=compact_read_end id=%lu offset=%llu rescues=%lu rescues_realloc=%lu lost=%lu lost_oom=%lu skipped=%lu" }, [LOGGER_COMPACT_END] = {512, LOG_SYSEVENTS, _logger_log_text, _logger_parse_text, "type=compact_end id=%lu" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/memcached-1.6.40/memcached.c new/memcached-1.6.41/memcached.c --- old/memcached-1.6.40/memcached.c 2025-12-17 01:16:18.000000000 +0100 +++ new/memcached-1.6.41/memcached.c 2026-03-06 21:46:28.000000000 +0100 @@ -1847,13 +1847,18 @@ APPEND_STAT("hash_bytes", "%llu", (unsigned long long)stats_state.hash_bytes); APPEND_STAT("hash_is_expanding", "%u", stats_state.hash_is_expanding); if (settings.slab_reassign) { + const char *busy_status = stats.slab_reassign_last_busy_status; + if (!busy_status) { + // Ensure we can't be NULL, for portability reasons. + busy_status = "none"; + } APPEND_STAT("slab_reassign_rescues", "%llu", stats.slab_reassign_rescues); APPEND_STAT("slab_reassign_chunk_rescues", "%llu", stats.slab_reassign_chunk_rescues); APPEND_STAT("slab_reassign_inline_reclaim", "%llu", stats.slab_reassign_inline_reclaim); APPEND_STAT("slab_reassign_busy_items", "%llu", stats.slab_reassign_busy_items); APPEND_STAT("slab_reassign_busy_deletes", "%llu", stats.slab_reassign_busy_deletes); APPEND_STAT("slab_reassign_busy_nomem", "%llu", stats.slab_reassign_busy_nomem); - APPEND_STAT("slab_reassign_last_busy_status", "%s", stats.slab_reassign_last_busy_status); + APPEND_STAT("slab_reassign_last_busy_status", "%s", busy_status); APPEND_STAT("slab_reassign_running", "%u", stats_state.slab_reassign_running); APPEND_STAT("slabs_moved", "%llu", stats.slabs_moved); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/memcached-1.6.40/memcached.h new/memcached-1.6.41/memcached.h --- old/memcached-1.6.40/memcached.h 2025-12-17 01:16:18.000000000 +0100 +++ new/memcached-1.6.41/memcached.h 2026-03-06 21:46:28.000000000 +0100 @@ -911,6 +911,9 @@ ssize_t (*read)(conn *c, void *buf, size_t count); ssize_t (*sendmsg)(conn *c, struct msghdr *msg, int flags); ssize_t (*write)(conn *c, void *buf, size_t count); +#ifdef MEMCACHED_DEBUG + void *debug_item_reffed; // used for refcount leak testing +#endif }; /* array of conn structures, indexed by file descriptor */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/memcached-1.6.40/memcached.spec new/memcached-1.6.41/memcached.spec --- old/memcached-1.6.40/memcached.spec 2025-12-17 01:17:54.000000000 +0100 +++ new/memcached-1.6.41/memcached.spec 2026-03-06 22:03:56.000000000 +0100 @@ -27,7 +27,7 @@ %endif Name: memcached -Version: 1.6.40 +Version: 1.6.41 Release: 1%{?dist} Summary: High Performance, Distributed Memory Object Cache diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/memcached-1.6.40/proto_text.c new/memcached-1.6.41/proto_text.c --- old/memcached-1.6.40/proto_text.c 2025-12-17 01:16:18.000000000 +0100 +++ new/memcached-1.6.41/proto_text.c 2026-03-06 21:46:28.000000000 +0100 @@ -851,16 +851,25 @@ out_string(c, "MISS"); return; } + c->debug_item_reffed = it; } else if (strncmp(subcmd, "unref", len) == 0) { - // double unlink. debugger must have already ref'ed it or this - // underflows. - item *it = item_get(key, klen, c->thread, DONT_UPDATE); - if (it == NULL) { - out_string(c, "MISS"); - return; + if (klen == 6 && strncmp(key, "reffed", klen) == 0) { + // Item memory held in slot so we can test replacing reffed items + // without leaking. + item *reffed = c->debug_item_reffed; + do_item_remove(reffed); + c->debug_item_reffed = NULL; + } else { + // double unlink. debugger must have already ref'ed it or this + // underflows. + item *it = item_get(key, klen, c->thread, DONT_UPDATE); + if (it == NULL) { + out_string(c, "MISS"); + return; + } + do_item_remove(it); + do_item_remove(it); } - do_item_remove(it); - do_item_remove(it); } else { out_string(c, "ERROR"); return; @@ -1605,13 +1614,15 @@ return; } else if (ret == PROCESS_REQUEST_CMD_NOT_FOUND) { - int len = 0; - const char *cm = mcmc_token_get(pr.request, &pr.tok, 0, &len); - for (int x = 0; text_cmd_entries[x].s; x++) { - const struct text_cmd_entry *e = &text_cmd_entries[x]; - if (strncmp(e->s, cm, len) == 0) { - e->func(c, &pr); - return; + if (pr.tok.ntokens > 0) { + int len = 0; + const char *cm = mcmc_token_get(pr.request, &pr.tok, 0, &len); + for (int x = 0; text_cmd_entries[x].s; x++) { + const struct text_cmd_entry *e = &text_cmd_entries[x]; + if (strlen(e->s) == len && strncmp(e->s, cm, len) == 0) { + e->func(c, &pr); + return; + } } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/memcached-1.6.40/proxy_lua.c new/memcached-1.6.41/proxy_lua.c --- old/memcached-1.6.40/proxy_lua.c 2025-12-17 01:16:18.000000000 +0100 +++ new/memcached-1.6.41/proxy_lua.c 2026-03-06 21:46:28.000000000 +0100 @@ -722,7 +722,7 @@ } memcpy(temp, key, klen); - temp[klen+1] = '\0'; + temp[klen] = '\0'; // TODO (v2): memmem would avoid the temp key and memcpy here, but it's // not technically portable. An easy improvement would be to detect diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/memcached-1.6.40/proxy_network.c new/memcached-1.6.41/proxy_network.c --- old/memcached-1.6.40/proxy_network.c 2025-12-17 01:16:18.000000000 +0100 +++ new/memcached-1.6.41/proxy_network.c 2026-03-06 21:46:28.000000000 +0100 @@ -1368,7 +1368,7 @@ } #ifdef PROXY_DEBUG - if (!STAILQ_EMPTY(&be->iop_head)) { + if (!STAILQ_EMPTY(&be->iop_read)) { P_DEBUG("backend has leftover IOs: %d\n", be->depth); } #endif @@ -1430,7 +1430,7 @@ } #ifdef PROXY_DEBUG - if (!STAILQ_EMPTY(&be->iop_head)) { + if (!STAILQ_EMPTY(&be->iop_read)) { P_DEBUG("backend has leftover IOs: %d\n", be->depth); } #endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/memcached-1.6.40/slab_automove.c new/memcached-1.6.41/slab_automove.c --- old/memcached-1.6.40/slab_automove.c 2024-10-31 21:47:40.000000000 +0100 +++ new/memcached-1.6.41/slab_automove.c 2026-03-06 21:46:28.000000000 +0100 @@ -82,6 +82,8 @@ bool youngest_evicting = false; *src = -1; *dst = -1; + bool mem_limit_reached = false; + global_page_pool_size(&mem_limit_reached); // fill after structs fill_item_stats_automove(a->iam_after); @@ -128,7 +130,8 @@ // if > N free chunks and not dirty, make decision. if (a->sam_after[n].free_chunks > a->sam_after[n].chunks_per_page * MIN_PAGES_FOR_RECLAIM) { - if (w_sum.dirty == 0) { + // Only handle "too much free" scenario if all memory was malloc'ed + if (mem_limit_reached && w_sum.dirty == 0) { *src = n; *dst = 0; youngest = oldest = -1; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/memcached-1.6.40/slabs_mover.c new/memcached-1.6.41/slabs_mover.c --- old/memcached-1.6.40/slabs_mover.c 2025-12-17 01:16:18.000000000 +0100 +++ new/memcached-1.6.41/slabs_mover.c 2026-03-06 21:46:28.000000000 +0100 @@ -108,6 +108,10 @@ } void *page = slabs_peek_page(t->rebal.s_clsid, &size, &perslab); + // We _almost_ never get called in a condition where this can fail. + if (page == NULL) { + return -1; + } // Bit-vector to keep track of completed chunks t->rebal.completed = (uint8_t*)calloc(perslab,sizeof(uint8_t)); @@ -136,13 +140,17 @@ return 0; } -static void *slab_rebalance_alloc(struct slab_rebal_thread *t, unsigned int id) { +#define MC_ALLOW_NEWPAGE 1 +#define MC_NO_NEWPAGE 0 +static void *slab_rebalance_alloc(struct slab_rebal_thread *t, unsigned int id, int flags) { item *new_it = NULL; + // translating our flags to downstream flags... + int sa_flags = flags == MC_ALLOW_NEWPAGE ? 0 : SLABS_ALLOC_NO_NEWPAGE; // We will either wipe the whole page if unused, or run out of memory in // the page and return NULL. while (1) { - new_it = slabs_alloc(id, SLABS_ALLOC_NO_NEWPAGE); + new_it = slabs_alloc(id, sa_flags); if (new_it == NULL) { break; } @@ -174,23 +182,48 @@ return; } - t->new_it = slab_rebalance_alloc(t, s_clsid); + t->new_it = slab_rebalance_alloc(t, s_clsid, MC_NO_NEWPAGE); // we could free the entire page in the above alloc call, but not get any // other memory to work with. // We try to busy-loop the page mover at least a few times in this case, // so it will pick up on all of the memory being freed already. - if (t->new_it == NULL && t->allow_evictions) { - // global is empty and memory limit is reached. we have to evict - // memory to move forward. - for (int x = 0; x < 10; x++) { - if (lru_pull_tail(s_clsid, COLD_LRU, 0, LRU_PULL_EVICT, 0, NULL) <= 0) { - if (settings.lru_segmented) { - lru_pull_tail(s_clsid, HOT_LRU, 0, 0, 0, NULL); + if (t->new_it == NULL) { + // If we've been stuck for a long time and don't have memory to + // allocate with, flip allow_evictions. It starts on if we + // start rebalance with completely full memory. However it's + // possible to start rebalance before memory is full then it fills + // while we rebalance and get stuck forever. + if (!t->allow_evictions && t->rebal.busy_loops > SLAB_MOVE_MAX_LOOPS) { + bool mem_limit_reached = false; + global_page_pool_size(&mem_limit_reached); + + // This is a stupid corner case that should self-resolve as memory + // fills. If we haven't malloc'ed all possible memory but we're + // trying to move memory for some reason, causing an eviction is + // nonsense. So we allocate a page to push a page out. + if (mem_limit_reached) { + t->allow_evictions = true; + } else { + t->new_it = slab_rebalance_alloc(t, s_clsid, MC_ALLOW_NEWPAGE); + if (t->new_it) { + return; } } - t->new_it = slab_rebalance_alloc(t, s_clsid); - if (t->new_it != NULL) { - break; + } + + if (t->allow_evictions) { + // global is empty and memory limit is reached. we have to evict + // memory to move forward. + for (int x = 0; x < 10; x++) { + if (lru_pull_tail(s_clsid, COLD_LRU, 0, LRU_PULL_EVICT, 0, NULL) <= 0) { + if (settings.lru_segmented) { + lru_pull_tail(s_clsid, HOT_LRU, 0, 0, 0, NULL); + } + } + t->new_it = slab_rebalance_alloc(t, s_clsid, MC_NO_NEWPAGE); + if (t->new_it != NULL) { + break; + } } } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/memcached-1.6.40/storage.c new/memcached-1.6.41/storage.c --- old/memcached-1.6.40/storage.c 2025-12-17 01:16:18.000000000 +0100 +++ new/memcached-1.6.41/storage.c 2026-03-06 21:46:28.000000000 +0100 @@ -934,10 +934,17 @@ uint32_t page_id, uint64_t page_version, uint32_t page_offset, uint64_t read_size) { uint64_t offset = 0; unsigned int rescues = 0; + // rescues which required a memory realloc (informational) + unsigned int rescues_realloc = 0; unsigned int lost = 0; + // failed rescues because of OOM vs disk space + unsigned int lost_oom = 0; unsigned int skipped = 0; unsigned int rescue_cold = 0; unsigned int rescue_old = 0; + // Sleep this many times during the readback before becoming impatient. + // Each sleep is 1ms. + int max_waits = 5000; while (offset < read_size) { item *hdr_it = NULL; @@ -983,24 +990,30 @@ if (do_write) { bool do_update = false; - int tries; + int tries = max_waits; obj_io io; io.len = ntotal; io.mode = OBJ_IO_WRITE; - for (tries = 10; tries > 0; tries--) { + // Wait for a write buffer or page to become available. + if (tries < 10) { + // Wait a minimum of 10ms + tries = 10; + } + for (; tries > 0; tries--) { if (extstore_write_request(storage, bucket, bucket, &io) == 0) { memcpy(io.buf, it, io.len); extstore_write(storage, &io); do_update = true; break; } else { + max_waits--; usleep(1000); } } if (do_update) { bool rescued = false; - if (it->refcount == 2) { + if (hdr_it->refcount == 2) { hdr->page_version = io.page_version; hdr->page_id = io.page_id; hdr->offset = io.offset; @@ -1028,8 +1041,10 @@ item_replace(hdr_it, new_it, hv, ITEM_get_cas(hdr_it)); do_item_remove(new_it); // release our reference. rescued = true; + rescues_realloc++; } else { lost++; + lost_oom++; } } @@ -1063,7 +1078,7 @@ stats.extstore_compact_resc_old += rescue_old; STATS_UNLOCK(); LOGGER_LOG(l, LOG_SYSEVENTS, LOGGER_COMPACT_READ_END, - NULL, page_id, offset, rescues, lost, skipped); + NULL, page_id, offset, rescues, rescues_realloc, lost, lost_oom, skipped); } // wrap lock is held while waiting for this callback, preventing caller thread Binary files old/memcached-1.6.40/t/.slabs-reassign2.t.swp and new/memcached-1.6.41/t/.slabs-reassign2.t.swp differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/memcached-1.6.40/t/extstore-start.t new/memcached-1.6.41/t/extstore-start.t --- old/memcached-1.6.40/t/extstore-start.t 1970-01-01 01:00:00.000000000 +0100 +++ new/memcached-1.6.41/t/extstore-start.t 2026-03-06 21:46:28.000000000 +0100 @@ -0,0 +1,45 @@ +#!/usr/bin/env perl + +# Starting the daemon multiple times within one test file makes attaching GDB +# to a test difficult. We isolate those tests into this file. + +use strict; +use warnings; +use Test::More; +use FindBin qw($Bin); +use lib "$Bin/lib"; +use MemcachedTest; +use Data::Dumper qw/Dumper/; + +my $ext_path; + +if (!supports_extstore()) { + plan skip_all => 'extstore not enabled'; + exit 0; +} + +$ext_path = "/tmp/extstore.$$"; + +eval { + my $server = new_memcached("-o ext_path=$ext_path:0m"); +}; +ok($@, "failed to start server with zero pages assigned"); + +eval { + my $server = new_memcached("-o ext_path=$ext_path:1GB"); +}; +ok($@, "failed to start server with invalid path size"); + +my $server = new_memcached("-m 64 -U 0 -o ext_page_size=8,ext_wbuf_size=2,ext_threads=1,ext_io_depth=2,ext_item_size=512,ext_item_age=2,ext_recache_rate=10000,ext_max_frag=0.9,ext_path=$ext_path:64m,slab_automove=0,ext_compact_under=1,ext_max_sleep=100000"); +my $sock = $server->sock; + +eval { + my $server = new_memcached("-o ext_path=$ext_path:64m"); +}; +ok($@, "failed to start a second server with the same file path"); + +done_testing(); + +END { + unlink $ext_path if $ext_path; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/memcached-1.6.40/t/extstore.t new/memcached-1.6.41/t/extstore.t --- old/memcached-1.6.40/t/extstore.t 2025-12-17 01:16:18.000000000 +0100 +++ new/memcached-1.6.41/t/extstore.t 2026-03-06 21:46:28.000000000 +0100 @@ -17,24 +17,9 @@ $ext_path = "/tmp/extstore.$$"; -eval { - my $server = new_memcached("-o ext_path=$ext_path:0m"); -}; -ok($@, "failed to start server with zero pages assigned"); - -eval { - my $server = new_memcached("-o ext_path=$ext_path:1GB"); -}; -ok($@, "failed to start server with invalid path size"); - my $server = new_memcached("-m 64 -U 0 -o ext_page_size=8,ext_wbuf_size=2,ext_threads=1,ext_io_depth=2,ext_item_size=512,ext_item_age=2,ext_recache_rate=10000,ext_max_frag=0.9,ext_path=$ext_path:64m,slab_automove=0,ext_compact_under=1,ext_max_sleep=100000"); my $sock = $server->sock; -eval { - my $server = new_memcached("-o ext_path=$ext_path:64m"); -}; -ok($@, "failed to start a second server with the same file path"); - # Wait until all items have flushed sub wait_for_ext { my $target = shift || 0; @@ -61,6 +46,7 @@ is($res, "OK\r\n", "watcher enabled"); my $fragcount = 20; + my $read_end = ''; while (my $log = <$watcher>) { chomp $log; if ($log =~ m/type=compact_fraginfo/) { @@ -80,61 +66,64 @@ } } -# fill a small object -print $sock "set foo 0 0 2\r\nhi\r\n"; -is(scalar <$sock>, "STORED\r\n", "stored small value"); -# fetch -mem_get_is($sock, "foo", "hi"); -# check extstore counters -{ - my $stats = mem_stats($sock); - is($stats->{extstore_objects_written}, 0); -} -# fill some larger objects -{ - # set one canary value for later - print $sock "set canary 0 0 20000 noreply\r\n$value\r\n"; - my $keycount = 1000; - for (1 .. $keycount) { - print $sock "set nfoo$_ 0 0 20000 noreply\r\n$value\r\n"; - } - # wait for a flush - wait_for_ext(); +subtest 'basics' => sub { + # fill a small object + print $sock "set foo 0 0 2\r\nhi\r\n"; + is(scalar <$sock>, "STORED\r\n", "stored small value"); # fetch - # TODO: Fetch back all values - mem_get_is($sock, "nfoo1", $value); - # check extstore counters - my $stats = mem_stats($sock); - cmp_ok($stats->{extstore_page_allocs}, '>', 0, 'at least one page allocated'); - cmp_ok($stats->{extstore_objects_written}, '>', $keycount / 2, 'some objects written'); - cmp_ok($stats->{extstore_bytes_written}, '>', length($value) * 2, 'some bytes written'); - cmp_ok($stats->{get_extstore}, '>', 0, 'one object was fetched'); - cmp_ok($stats->{extstore_objects_read}, '>', 0, 'one object read'); - cmp_ok($stats->{extstore_bytes_read}, '>', length($value), 'some bytes read'); + mem_get_is($sock, "foo", "hi"); - # Remove half of the keys for the next test. - for (1 .. $keycount) { - next unless $_ % 2 == 0; - print $sock "delete nfoo$_ noreply\r\n"; + # check extstore counters + { + my $stats = mem_stats($sock); + is($stats->{extstore_objects_written}, 0); } - my $stats2 = mem_stats($sock); - cmp_ok($stats->{extstore_bytes_used}, '>', $stats2->{extstore_bytes_used}, - 'bytes used dropped after deletions'); - cmp_ok($stats->{extstore_objects_used}, '>', $stats2->{extstore_objects_used}, - 'objects used dropped after deletions'); - is($stats2->{badcrc_from_extstore}, 0, 'CRC checks successful'); - is($stats2->{miss_from_extstore}, 0, 'no misses'); + # fill some larger objects + { + # set one canary value for later + print $sock "set canary 0 0 20000 noreply\r\n$value\r\n"; + my $keycount = 1000; + for (1 .. $keycount) { + print $sock "set nfoo$_ 0 0 20000 noreply\r\n$value\r\n"; + } + # wait for a flush + wait_for_ext(); + # fetch + # TODO: Fetch back all values + mem_get_is($sock, "nfoo1", $value); + # check extstore counters + my $stats = mem_stats($sock); + cmp_ok($stats->{extstore_page_allocs}, '>', 0, 'at least one page allocated'); + cmp_ok($stats->{extstore_objects_written}, '>', $keycount / 2, 'some objects written'); + cmp_ok($stats->{extstore_bytes_written}, '>', length($value) * 2, 'some bytes written'); + cmp_ok($stats->{get_extstore}, '>', 0, 'one object was fetched'); + cmp_ok($stats->{extstore_objects_read}, '>', 0, 'one object read'); + cmp_ok($stats->{extstore_bytes_read}, '>', length($value), 'some bytes read'); + + # Remove half of the keys for the next test. + for (1 .. $keycount) { + next unless $_ % 2 == 0; + print $sock "delete nfoo$_ noreply\r\n"; + } - # delete the rest - for (1 .. $keycount) { - next unless $_ % 2 == 1; - print $sock "delete nfoo$_ noreply\r\n"; + my $stats2 = mem_stats($sock); + cmp_ok($stats->{extstore_bytes_used}, '>', $stats2->{extstore_bytes_used}, + 'bytes used dropped after deletions'); + cmp_ok($stats->{extstore_objects_used}, '>', $stats2->{extstore_objects_used}, + 'objects used dropped after deletions'); + is($stats2->{badcrc_from_extstore}, 0, 'CRC checks successful'); + is($stats2->{miss_from_extstore}, 0, 'no misses'); + + # delete the rest + for (1 .. $keycount) { + next unless $_ % 2 == 1; + print $sock "delete nfoo$_ noreply\r\n"; + } } -} +}; -# check item flag survival after write to disk -{ +subtest 'check item flag survival after write to disk' => sub { print $sock "ms itflagtest 20000\r\n$value\r\n"; is(scalar <$sock>, "HD\r\n", "prepped flag test value"); print $sock "mg itflagtest\r\n"; @@ -146,11 +135,102 @@ print $sock "mg itflagtest h\r\n"; is(scalar <$sock>, "HD h1 X W\r\n", "flags came back as expected"); -} +}; -# fill to eviction -{ +subtest 'compaction and rescues' => sub { + my $keycount = 2500; + + print $sock "set refcanary 5 0 20000 noreply\r\n$value\r\n"; + + # Ensure our canary goes to disk. + wait_for_ext(); + print $sock "touch refcanary 0 noreply\r\n"; + print $sock "touch refcanary 0 noreply\r\n"; + # Set some extra flags + print $sock "md refcanary I\r\n"; + is(scalar <$sock>, "HD\r\n", "invalidated item to set STALE"); + + # reflock one key to test realloc rescues + # important: reflock _after_ it goes to disk so we lock the header item. + print $sock "debugitem ref refcanary\r\n"; + my $res = <$sock>; + + # Need to ensure we catch all compaction log events. + my $watcher = $server->new_sock; + print $watcher "watch sysevents\n"; + $res = <$watcher>; + is($res, "OK\r\n", "watcher enabled"); + + for (1 .. $keycount) { + print $sock "set cfoo$_ 0 0 20000 noreply\r\n$value\r\n"; + # wait to avoid evictions + wait_for_ext(500) if ($_ % 2000 == 0); + } + # because item_age is set to 2s + wait_for_ext(); + my $stats = mem_stats($sock); + is($stats->{evictions}, 0, 'no evictions'); + + # check counters + cmp_ok($stats->{extstore_page_evictions}, '==', 0, 'no pages evicted'); + cmp_ok($stats->{extstore_objects_evicted}, '==', 0, 'no objects evicted'); + cmp_ok($stats->{extstore_bytes_evicted}, '==', 0, 'no bytes evicted'); + cmp_ok($stats->{extstore_pages_free}, '<', 2, 'few pages are free'); + + for (1 .. $keycount) { + next unless $_ % 2 == 0; + print $sock "delete cfoo$_ noreply\r\n"; + } + + my %h = (); + my $tries = 250; + while (my $log = <$watcher>) { + #diag "WATCHER LOG: $log"; + chomp $log; + if ($log =~ m/type=compact_read_end/) { + # FIXME: I forget the better method for this. + my @p = split(/\s+/, $log); + for (@p) { + my @hp = split(/=/, $_); + $h{$hp[0]} = $hp[1]; + } + if ($h{rescues_realloc} != 0) { + ok("saw a rescue realloc"); + last; + } + } + if ($tries-- == 0) { + fail("never saw a rescue realloc"); + last; + } + } + + $stats = mem_stats($sock); + cmp_ok($stats->{extstore_pages_free}, '>', 0, 'some pages now free'); + cmp_ok($stats->{extstore_compact_rescues}, '>', 0, 'some compaction rescues happened'); + cmp_ok($stats->{extstore_compact_skipped}, '==', 0, 'no compaction skips happened'); + print $sock "extstore drop_unread 0\r\n"; + $res = <$sock>; + + # release our leaked item + print $sock "debugitem unref reffed\r\n"; + $res = <$sock>; + # Ensure various flag bits and header memory survived. + print $sock "mg refcanary f h\r\n"; + is(scalar <$sock>, "HD f5 h1 X W\r\n", "reffed item flags came back as expected"); + + mem_get_is({ sock => $sock, flags => 5 }, "refcanary", $value); + + # remove all data and wait for extstore to settle. + print $sock "flush_all\r\n"; + $res = <$sock>; + + watch_compact(); +}; + +subtest 'eviction and compaction' => sub { my $keycount = 4000; + for (1 .. $keycount) { print $sock "set mfoo$_ 0 0 20000 noreply\r\n$value\r\n"; # wait to avoid evictions @@ -201,10 +281,10 @@ cmp_ok($stats->{extstore_compact_skipped}, '>', 0, 'some compaction skips happened'); print $sock "extstore drop_unread 0\r\n"; $res = <$sock>; -} +}; # attempt to incr/decr/append/prepend or chunk objects that were sent to disk. -{ +subtest 'invalid operations' => sub { my $keycount = 100; for (1 .. $keycount) { print $sock "set bfoo$_ 0 0 20000 noreply\r\n$value\r\n"; @@ -220,7 +300,7 @@ is(scalar <$sock>, "NOT_STORED\r\n", 'append fails'); print $sock "prepend bfoo1 0 0 2\r\nhi\r\n"; is(scalar <$sock>, "NOT_STORED\r\n", 'prepend fails'); -} +}; done_testing(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/memcached-1.6.40/t/issue_67.t new/memcached-1.6.41/t/issue_67.t --- old/memcached-1.6.40/t/issue_67.t 2024-08-01 20:54:00.000000000 +0200 +++ new/memcached-1.6.41/t/issue_67.t 2026-03-06 21:46:28.000000000 +0100 @@ -85,8 +85,8 @@ my $server = run_server($params); my %ports = read_ports(); - validate_port($name, $ports{'TCP INET'}, $expected_tcp); - validate_port($name, $ports{'UDP INET'}, $expected_udp); + validate_port($name, $ports{'TCP INET'} || $ports{'TCP INET6'}, $expected_tcp); + validate_port($name, $ports{'UDP INET'} || $ports{'UDP INET6'}, $expected_udp); } skip_if_default_addr_in_use { when('no arguments', '', 11211, -1) }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/memcached-1.6.40/t/proxyunits.lua new/memcached-1.6.41/t/proxyunits.lua --- old/memcached-1.6.40/t/proxyunits.lua 2025-10-22 06:59:10.000000000 +0200 +++ new/memcached-1.6.41/t/proxyunits.lua 2026-03-06 21:46:28.000000000 +0100 @@ -14,7 +14,8 @@ local b2z = {b2} local b3z = {b3} - local dead = srv('dead', '127.9.9.9', 11011); + local dead = srv({ label = "dead", host = "127.9.9.9", port = 11011, + down = true }) local no_label = srv('', '127.0.0.1', 11414) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/memcached-1.6.40/t/slabs-reassign2.t new/memcached-1.6.41/t/slabs-reassign2.t --- old/memcached-1.6.40/t/slabs-reassign2.t 2025-10-22 06:59:10.000000000 +0200 +++ new/memcached-1.6.41/t/slabs-reassign2.t 2026-03-06 21:46:28.000000000 +0100 @@ -2,7 +2,7 @@ use strict; use warnings; -use Test::More tests => 11; +use Test::More tests => 12; use FindBin qw($Bin); use lib "$Bin/lib"; use MemcachedTest; @@ -12,25 +12,23 @@ my $sock = $server->sock; my $value = "B"x11000; -my $keycount = 5000; +my $keycount = 6000; my $res; for (1 .. $keycount) { print $sock "set nfoo$_ 0 0 11000 noreply\r\n$value\r\n"; } -my $todelete = 0; { my $stats = mem_stats($sock); cmp_ok($stats->{curr_items}, '>', 4000, "stored at least 4000 11k items"); - $todelete = $stats->{curr_items}; -# for ('evictions', 'reclaimed', 'curr_items', 'cmd_set', 'bytes') { -# print STDERR "$_: ", $stats->{$_}, "\n"; -# } + #for ('evictions', 'reclaimed', 'curr_items', 'cmd_set', 'bytes') { + # diag "$_: ", $stats->{$_}, "\n"; + #} } # Make room in old class so rescues can happen when we switch slab classes. -for (1 .. $todelete) { +for (1 .. $keycount) { next unless $_ % 2 == 0; print $sock "delete nfoo$_ noreply\r\n"; } @@ -47,9 +45,13 @@ cmp_ok($tries, '>', 0, 'some pages moved back to global pool'); } -$value = "B"x7000; +my $stats_before = mem_stats($sock); + +# Ensure we can set data into a different slab class. + +$value = "B"x6000; for (1 .. $keycount) { - print $sock "set ifoo$_ 0 0 7000 noreply\r\n$value\r\n"; + print $sock "set ifoo$_ 0 0 6000 noreply\r\n$value\r\n"; } my $missing = 0; @@ -57,7 +59,7 @@ for (1 .. $keycount) { print $sock "get ifoo$_\r\n"; my $body = scalar(<$sock>); - my $expected = "VALUE ifoo$_ 0 7000\r\n$value\r\nEND\r\n"; + my $expected = "VALUE ifoo$_ 0 6000\r\n$value\r\nEND\r\n"; if ($body =~ /^END/) { $missing++; } else { @@ -73,7 +75,8 @@ { my $stats = mem_stats($sock); - cmp_ok($stats->{evictions}, '<', 2000, 'evictions were less than 2000'); + cmp_ok($stats->{evictions} - $stats_before->{evictions}, '>', 0, 'evictions were greater than 0'); + cmp_ok($stats->{evictions} - $stats_before->{evictions}, '<', 2200, 'evictions were less than 2200'); # for ('evictions', 'reclaimed', 'curr_items', 'cmd_set', 'bytes') { # print STDERR "$_: ", $stats->{$_}, "\n"; # } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/memcached-1.6.40/t/ssl_ports.t new/memcached-1.6.41/t/ssl_ports.t --- old/memcached-1.6.40/t/ssl_ports.t 2025-10-22 06:59:10.000000000 +0200 +++ new/memcached-1.6.41/t/ssl_ports.t 2026-03-06 21:46:28.000000000 +0100 @@ -33,8 +33,15 @@ # not trying very hard but: if the above works and this doesn't, it's going to # be the peer cert. If someone wants to tryhard you can try inspecting # $SSL_ERROR and/or checking `watch connevents` stream -my $mtls_sock = $server->new_nocert_tls_sock($mtls_port, 'TLSv1_3'); +my ($mtls_sock, $mtls_version); +for my $tls ('TLSv1_3', 'SSLv23') { + $mtls_sock = $server->new_nocert_tls_sock($mtls_port, $tls); + if ($mtls_sock) { + $mtls_version = $tls; + last; + } +} print $mtls_sock "version\r\n"; -is(scalar <$mtls_sock>, undef, "failed to connect without peer cert"); +is(scalar <$mtls_sock>, undef, "failed to connect without peer cert on $mtls_version"); done_testing() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/memcached-1.6.40/t/ssl_proto_version.t new/memcached-1.6.41/t/ssl_proto_version.t --- old/memcached-1.6.40/t/ssl_proto_version.t 2024-08-01 20:54:00.000000000 +0200 +++ new/memcached-1.6.41/t/ssl_proto_version.t 2026-03-06 21:46:28.000000000 +0100 @@ -37,9 +37,16 @@ SKIP: { skip 'TLS v1.3 not available', 1 if !$is_tls_13_available; # Above minimum supported protocol version - $sock = $server->new_sock(undef, 'TLSv1_3'); - print $sock "version\r\n"; - like(scalar <$sock>, qr/VERSION/, "handshake above minimum proto version"); + my ($mtls_sock, $mtls_version); + for my $tls ('TLSv1_3', 'SSLv23') { + $mtls_sock = $server->new_sock(undef, $tls); + if ($mtls_sock) { + $mtls_version = $tls; + last; + } + } + print $mtls_sock "version\r\n"; + like(scalar <$mtls_sock>, qr/VERSION/, "handshake above minimum proto version on $mtls_version"); } done_testing(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/memcached-1.6.40/t/stats.t new/memcached-1.6.41/t/stats.t --- old/memcached-1.6.40/t/stats.t 2025-12-17 01:16:18.000000000 +0100 +++ new/memcached-1.6.41/t/stats.t 2026-03-06 21:46:28.000000000 +0100 @@ -28,7 +28,8 @@ # when TLS is enabled, stats contains additional keys: # - ssl_handshake_errors # - time_since_server_cert_refresh - is(scalar(keys(%$stats)), 86, "expected count of stats values"); + # - ssl_proto_errors + is(scalar(keys(%$stats)), 87, "expected count of stats values"); } else { is(scalar(keys(%$stats)), 84, "expected count of stats values"); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/memcached-1.6.40/vendor/mcmc/mcmc.c new/memcached-1.6.41/vendor/mcmc/mcmc.c --- old/memcached-1.6.40/vendor/mcmc/mcmc.c 2025-10-22 06:59:10.000000000 +0200 +++ new/memcached-1.6.41/vendor/mcmc/mcmc.c 2026-03-06 21:46:28.000000000 +0100 @@ -45,7 +45,7 @@ // INTERNAL FUNCTIONS -#define TOKENIZER_MAXLEN USHRT_MAX-1 +#define TOKENIZER_MAXLEN (USHRT_MAX-1) // Find the starting offsets of each token; ignoring length. // This creates a fast small (<= cacheline) index into the request, @@ -57,8 +57,9 @@ // Function _assumes_ const char *line ends with \n or \r\n, but will not // break so long as the passed in 'len' is reasonable. MCMC_STATIC int _mcmc_tokenize_meta(mcmc_tokenizer_t *t, const char *line, size_t len, const int mstart, const int max) { - const char *s = line; + const char *s; const char *end; + const char *n = s = line; t->metaflags = 0; // since multigets can be huge, we can't purely judge reqlen against this @@ -75,7 +76,47 @@ int curtoken = 0; - while (s != end) { + // NOTE: To optimize this loop for long tokens: + // Add n++ to the *n == ' ' case next to s++ + // Remove final n++, add else { n = memchr(etc) } + // This is slower with lots of single char tokens, faster with long + // tokens. + // To favor code simplicity I'm leaving it middle of the road: if we + // desire the optimization we need an if with two separate loops based on + // line len > X, where X is determined by benchmarking sets of string + // examples to be a good common tradeoff. + while (n != end) { + if (*n == ' ') { + if (s != n) { + t->tokens[curtoken] = s - line; + if (curtoken >= mstart) { + if (*s > 64 && *s < 123) { + t->metaflags |= (uint64_t)1 << (*s - 65); + } else if (isdigit(*s) == 0) { + return MCMC_NOK; + } + } + if (++curtoken == max) { + s++; + s = memchr(s, ' ', end - s); + if (!s) { + s = end; + } + n = s; // avoid adding extra token + break; + } + s = n; + } + s++; + } + n++; + } + + // reached end of parsing with active token + if (s != n) { + // Deliberate code redundancy; too many args, inline return. + // Bad smell for macro, potential extra branch if inline func. + t->tokens[curtoken] = s - line; if (curtoken >= mstart) { if (*s > 64 && *s < 123) { t->metaflags |= (uint64_t)1 << (*s - 65); @@ -83,34 +124,13 @@ return MCMC_NOK; } } - t->tokens[curtoken] = s - line; - if (++curtoken == max) { - s++; - // hit max tokens before end of the line. - // keep advancing so we can place endcap token. - s = memchr(s, ' ', end - s); - if (!s) { - s = end; - } - break; - } - s++; - // avoid memchr if we were a single byte token. - if (*s == ' ') { - while (*s == ' ' && s != end) { - s++; - } - } else { - // advance over a token - s = memchr(s, ' ', end - s); - if (!s) { - s = end; + curtoken++; + // must advance to space or next token for end cap + while (s != end) { + if (*s == ' ') { break; - } else { - while (*s == ' ' && s != end) { - s++; // skip any spaces until the next token. - } } + s++; } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/memcached-1.6.40/version.m4 new/memcached-1.6.41/version.m4 --- old/memcached-1.6.40/version.m4 2025-12-17 01:17:54.000000000 +0100 +++ new/memcached-1.6.41/version.m4 2026-03-06 22:03:56.000000000 +0100 @@ -1 +1 @@ -m4_define([VERSION_NUMBER], [1.6.40]) +m4_define([VERSION_NUMBER], [1.6.41])
