Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion GALERA_VERSION
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
GALERA_VERSION_WSREP_API=26
GALERA_VERSION_MAJOR=4
GALERA_VERSION_MINOR=26
GALERA_VERSION_MINOR=27
GALERA_VERSION_EXTRA=
GALERA_EPOCH=
2 changes: 1 addition & 1 deletion SConstruct
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ static_ssl = ARGUMENTS.get('static_ssl', None)
install = ARGUMENTS.get('install', None)
version_script = int(ARGUMENTS.get('version_script', 1))

GALERA_VER = ARGUMENTS.get('version', '4.26')
GALERA_VER = ARGUMENTS.get('version', '4.27')
GALERA_REV = ARGUMENTS.get('revno', 'XXXX')

# Attempt to read from file if not given
Expand Down
4 changes: 2 additions & 2 deletions debian/changelog
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
galera-4 (26.4.26) UNRELEASED; urgency=medium
galera-4 (26.4.27) UNRELEASED; urgency=medium

* Galera 4 release

-- Codership Oy <[email protected]> Mon, 16 Feb 2026 18:09:55 +0200
-- Codership Oy <[email protected]> Tue, 12 May 2026 12:52:12 +0300
6 changes: 5 additions & 1 deletion gcache/src/GCache_seqno.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,8 +220,9 @@ namespace gcache
old_gap = new_gap;

seqno_t const start (idx - 1);
seqno_t const end (seqno - start >= 2*batch_size ?
seqno_t const s_end (seqno - start >= 2*batch_size ?
start + batch_size : seqno);
seqno_t const end (std::min(s_end, seqno_locked - 1));
#ifndef NDEBUG
if (params.debug())
{
Expand Down Expand Up @@ -274,6 +275,9 @@ namespace gcache

loop = (end < seqno) && loop;

/* Stop if we hit the seqno_locked boundary - no more progress possible */
if (loop && seqno_released + 1 >= seqno_locked) loop = false;

#ifndef NDEBUG
if (params.debug())
{
Expand Down
99 changes: 99 additions & 0 deletions gcache/tests/gcache_top_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,8 @@ START_TEST(top_level_page_caching_locking) // test that caching in pages work
"seqno_min: %" PRId64 " (expected 4)", gc.seqno_min());

gc.seqno_unlock();
/* Re-release them now as all the locks are removed. */
gc.seqno_release(12);
ps.wait_page_discard(); // pages 2 and 3 should be discarded
ck_assert_msg(ps.total_pages() == 2,
"total_pages %zu (expected 2)", ps.total_pages());
Expand All @@ -284,6 +286,102 @@ START_TEST(top_level_page_caching_locking) // test that caching in pages work
}
END_TEST


/*
* Verify that seqno_release() does not free buffers that are locked for IST.
*/
START_TEST(top_level_seqno_lock_protects_ist_buffers)
{
log_info << "\n#\n# top_level_seqno_lock_protects_ist_buffers\n#";
const char* const dir_name = "";
size_t const bh_size = sizeof(gcache::BufferHeader);
size_t const page_size = (8 + bh_size)*3;

gu::Config cfg;
GCache::register_params(cfg);
cfg.set("gcache.dir", dir_name);
cfg.set("gcache.size", 0); // turn off ring buffer
cfg.set("gcache.page_size", page_size);
cfg.set("gcache.keep_pages_size", 10 * page_size);
#ifndef NDEBUG
cfg.set("gcache.debug", DEBUG);
#endif

GCache gc(nullptr, cfg, dir_name);
const PageStore& ps(gc.page_store());
ck_assert_msg(ps.page_size() == page_size,
"ps.page_size: %zu (expected %zu)",
ps.page_size(), page_size);

std::vector<void*> buf;

mark_point();

/*
* 1. Populate 5 pages
*/
for (size_t page_count(1); page_count <= 5; page_count++)
test_caching_fill_page(gc, buf, page_count);
ck_assert_msg(ps.total_pages() == 5,
"total_pages %zu (expected 5)", ps.total_pages());

/*
* 2. Assign seqnos
*/
for (size_t seqno(1); seqno <= buf.size(); seqno++)
gc.seqno_assign(buf[seqno - 1], seqno, 0, false);
ck_assert(gc.seqno_min() == 1);

/*
* 3. Lock seqno 1
*/
gc.seqno_lock(1);

/*
* Release up to seqno 5. This MUST NOT free buffers
* at or above seqno_locked (1).
*/
gc.seqno_release(5);

/* DIAGNOSTIC: check BH_is_released flag after seqno_release */
for (int i = 0; i < 5; ++i)
{
BufferHeader* const bh = ptr2BH(buf[i]);
ck_assert_msg(!BH_is_released(bh),
"buffer %d (seqno %ld) released despite seqno_lock(1)!",
i, (long)bh->seqno_g);
}

/*
* Now simulate IST sender reading: seqno_get_buffers() should
* return all 5 buffers since they are protected by the lock.
* Without the seqno_locked guard in seqno_release(), this returns 0.
*/
std::vector<GCache::Buffer> got(5);
size_t const n = gc.seqno_get_buffers(got, 1);
ck_assert_msg(n == (size_t)5,
"seqno_get_buffers returned %zu, expected 5. "
"seqno_release() freed locked IST buffers!",
n);

for (int i = 0; i < 5; i++)
{
ck_assert_msg(got[i].seqno_g() == (seqno_t)(i + 1),
"buffer %d has wrong seqno, expected %d",
i, i + 1);
}

gc.seqno_unlock();

/* Release all remaining buffers so pages can be discarded */
gc.seqno_release(buf.size());
ps.wait_page_discard();

mark_point();
}
END_TEST


Suite* gcache_top_suite()
{
Suite* s = suite_create("gcache::top-level");
Expand All @@ -292,6 +390,7 @@ Suite* gcache_top_suite()
tc = tcase_create("test");
tcase_add_test(tc, top_level_page_caching);
tcase_add_test(tc, top_level_page_caching_locking);
tcase_add_test(tc, top_level_seqno_lock_protects_ist_buffers);
suite_add_tcase(s, tc);

return s;
Expand Down
2 changes: 1 addition & 1 deletion scripts/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ set -eux
# $Id$

# Galera library version
VERSION="26.4.26"
VERSION="26.4.27"

get_cores()
{
Expand Down