v23-0016-Add-read-write-lock-to-radix-tree-in-RT_SHMEM-ca.patch

application/octet-stream

Filename: v23-0016-Add-read-write-lock-to-radix-tree-in-RT_SHMEM-ca.patch
Type: application/octet-stream
Part: 0
Message: Re: [PoC] Improve dead tuple storage for lazy vacuum

Patch

Format: format-patch
Series: patch v23-0016
Subject: Add read-write lock to radix tree in RT_SHMEM case.
File+
src/include/lib/radixtree.h 95 5
src/test/modules/test_radixtree/test_radixtree.c 4 4
From 730cdcba6c89954806ac40e2ed63720a93d3fe56 Mon Sep 17 00:00:00 2001
From: Masahiko Sawada <sawada.mshk@gmail.com>
Date: Wed, 25 Jan 2023 17:43:29 +0900
Subject: [PATCH v23 16/18] Add read-write lock to radix tree in RT_SHMEM case.

---
 src/include/lib/radixtree.h                   | 100 +++++++++++++++++-
 .../modules/test_radixtree/test_radixtree.c   |   8 +-
 2 files changed, 99 insertions(+), 9 deletions(-)

diff --git a/src/include/lib/radixtree.h b/src/include/lib/radixtree.h
index 11716fbfca..542daae6d0 100644
--- a/src/include/lib/radixtree.h
+++ b/src/include/lib/radixtree.h
@@ -40,6 +40,8 @@
  * There are some optimizations not yet implemented, particularly path
  * compression and lazy path expansion.
  *
+ * WIP: describe about how locking works.
+ *
  * WIP: the radix tree nodes don't shrink.
  *
  * To generate a radix tree and associated functions for a use case several
@@ -224,7 +226,7 @@ typedef dsa_pointer RT_HANDLE;
 #endif
 
 #ifdef RT_SHMEM
-RT_SCOPE RT_RADIX_TREE * RT_CREATE(MemoryContext ctx, dsa_area *dsa);
+RT_SCOPE RT_RADIX_TREE * RT_CREATE(MemoryContext ctx, dsa_area *dsa, int tranche_id);
 RT_SCOPE RT_RADIX_TREE * RT_ATTACH(dsa_area *dsa, dsa_pointer dp);
 RT_SCOPE void RT_DETACH(RT_RADIX_TREE *tree);
 RT_SCOPE RT_HANDLE RT_GET_HANDLE(RT_RADIX_TREE *tree);
@@ -371,6 +373,16 @@ typedef struct RT_NODE
 #define RT_INVALID_PTR_ALLOC NULL
 #endif
 
+#ifdef RT_SHMEM
+#define RT_LOCK_EXCLUSIVE(tree)	LWLockAcquire(&tree->ctl->lock, LW_EXCLUSIVE)
+#define RT_LOCK_SHARED(tree)	LWLockAcquire(&tree->ctl->lock, LW_SHARED)
+#define RT_UNLOCK(tree)			LWLockRelease(&tree->ctl->lock);
+#else
+#define RT_LOCK_EXCLUSIVE(tree)	((void) 0)
+#define RT_LOCK_SHARED(tree)	((void) 0)
+#define RT_UNLOCK(tree)			((void) 0)
+#endif
+
 /*
  * Inner nodes and leaf nodes have analogous structure. To distinguish
  * them at runtime, we take advantage of the fact that the key chunk
@@ -596,6 +608,7 @@ typedef struct RT_RADIX_TREE_CONTROL
 #ifdef RT_SHMEM
 	RT_HANDLE	handle;
 	uint32		magic;
+	LWLock		lock;
 #endif
 
 	RT_PTR_ALLOC root;
@@ -1376,7 +1389,7 @@ RT_NODE_INSERT_LEAF(RT_RADIX_TREE *tree, RT_PTR_LOCAL parent, RT_PTR_ALLOC store
  */
 RT_SCOPE RT_RADIX_TREE *
 #ifdef RT_SHMEM
-RT_CREATE(MemoryContext ctx, dsa_area *dsa)
+RT_CREATE(MemoryContext ctx, dsa_area *dsa, int tranche_id)
 #else
 RT_CREATE(MemoryContext ctx)
 #endif
@@ -1398,6 +1411,7 @@ RT_CREATE(MemoryContext ctx)
 	tree->ctl = (RT_RADIX_TREE_CONTROL *) dsa_get_address(dsa, dp);
 	tree->ctl->handle = dp;
 	tree->ctl->magic = RT_RADIX_TREE_MAGIC;
+	LWLockInitialize(&tree->ctl->lock, tranche_id);
 #else
 	tree->ctl = (RT_RADIX_TREE_CONTROL *) palloc0(sizeof(RT_RADIX_TREE_CONTROL));
 
@@ -1581,8 +1595,13 @@ RT_SET(RT_RADIX_TREE *tree, uint64 key, RT_VALUE_TYPE value)
 	Assert(tree->ctl->magic == RT_RADIX_TREE_MAGIC);
 #endif
 
+	RT_LOCK_EXCLUSIVE(tree);
+
 	if (unlikely(tree->ctl->iter_active))
+	{
+		RT_UNLOCK(tree);
 		elog(ERROR, "cannot add new key-value to radix tree while iteration is in progress");
+	}
 
 	/* Empty tree, create the root */
 	if (!RT_PTR_ALLOC_IS_VALID(tree->ctl->root))
@@ -1609,6 +1628,7 @@ RT_SET(RT_RADIX_TREE *tree, uint64 key, RT_VALUE_TYPE value)
 		if (!RT_NODE_SEARCH_INNER(child, key, &new_child))
 		{
 			RT_SET_EXTEND(tree, key, value, parent, stored_child, child);
+			RT_UNLOCK(tree);
 			return false;
 		}
 
@@ -1623,12 +1643,13 @@ RT_SET(RT_RADIX_TREE *tree, uint64 key, RT_VALUE_TYPE value)
 	if (!updated)
 		tree->ctl->num_keys++;
 
+	RT_UNLOCK(tree);
 	return updated;
 }
 
 /*
  * Search the given key in the radix tree. Return true if there is the key,
- * otherwise return false.  On success, we set the value to *val_p so it must
+ * otherwise return false.	On success, we set the value to *val_p so it must
  * not be NULL.
  */
 RT_SCOPE bool
@@ -1636,14 +1657,20 @@ RT_SEARCH(RT_RADIX_TREE *tree, uint64 key, RT_VALUE_TYPE *value_p)
 {
 	RT_PTR_LOCAL node;
 	int			shift;
+	bool		found;
 
 #ifdef RT_SHMEM
 	Assert(tree->ctl->magic == RT_RADIX_TREE_MAGIC);
 #endif
 	Assert(value_p != NULL);
 
+	RT_LOCK_SHARED(tree);
+
 	if (!RT_PTR_ALLOC_IS_VALID(tree->ctl->root) || key > tree->ctl->max_val)
+	{
+		RT_UNLOCK(tree);
 		return false;
+	}
 
 	node = RT_PTR_GET_LOCAL(tree, tree->ctl->root);
 	shift = node->shift;
@@ -1657,13 +1684,19 @@ RT_SEARCH(RT_RADIX_TREE *tree, uint64 key, RT_VALUE_TYPE *value_p)
 			break;
 
 		if (!RT_NODE_SEARCH_INNER(node, key, &child))
+		{
+			RT_UNLOCK(tree);
 			return false;
+		}
 
 		node = RT_PTR_GET_LOCAL(tree, child);
 		shift -= RT_NODE_SPAN;
 	}
 
-	return RT_NODE_SEARCH_LEAF(node, key, value_p);
+	found = RT_NODE_SEARCH_LEAF(node, key, value_p);
+
+	RT_UNLOCK(tree);
+	return found;
 }
 
 #ifdef RT_USE_DELETE
@@ -1685,11 +1718,19 @@ RT_DELETE(RT_RADIX_TREE *tree, uint64 key)
 	Assert(tree->ctl->magic == RT_RADIX_TREE_MAGIC);
 #endif
 
+	RT_LOCK_EXCLUSIVE(tree);
+
 	if (unlikely(tree->ctl->iter_active))
+	{
+		RT_UNLOCK(tree);
 		elog(ERROR, "cannot delete key to radix tree while iteration is in progress");
+	}
 
 	if (!RT_PTR_ALLOC_IS_VALID(tree->ctl->root) || key > tree->ctl->max_val)
+	{
+		RT_UNLOCK(tree);
 		return false;
+	}
 
 	/*
 	 * Descend the tree to search the key while building a stack of nodes we
@@ -1708,7 +1749,10 @@ RT_DELETE(RT_RADIX_TREE *tree, uint64 key)
 		node = RT_PTR_GET_LOCAL(tree, allocnode);
 
 		if (!RT_NODE_SEARCH_INNER(node, key, &child))
+		{
+			RT_UNLOCK(tree);
 			return false;
+		}
 
 		allocnode = child;
 		shift -= RT_NODE_SPAN;
@@ -1721,6 +1765,7 @@ RT_DELETE(RT_RADIX_TREE *tree, uint64 key)
 	if (!deleted)
 	{
 		/* no key is found in the leaf node */
+		RT_UNLOCK(tree);
 		return false;
 	}
 
@@ -1732,7 +1777,10 @@ RT_DELETE(RT_RADIX_TREE *tree, uint64 key)
 	 * node.
 	 */
 	if (node->count > 0)
+	{
+		RT_UNLOCK(tree);
 		return true;
+	}
 
 	/* Free the empty leaf node */
 	RT_FREE_NODE(tree, allocnode);
@@ -1754,6 +1802,7 @@ RT_DELETE(RT_RADIX_TREE *tree, uint64 key)
 		RT_FREE_NODE(tree, allocnode);
 	}
 
+	RT_UNLOCK(tree);
 	return true;
 }
 #endif
@@ -1827,8 +1876,13 @@ RT_BEGIN_ITERATE(RT_RADIX_TREE *tree)
 	RT_PTR_LOCAL root;
 	int			top_level;
 
+	RT_LOCK_EXCLUSIVE(tree);
+
 	if (unlikely(tree->ctl->iter_active))
+	{
+		RT_UNLOCK(tree);
 		elog(ERROR, "cannot begin iteration while another iteration is in progress");
+	}
 
 	old_ctx = MemoryContextSwitchTo(tree->context);
 
@@ -1838,7 +1892,10 @@ RT_BEGIN_ITERATE(RT_RADIX_TREE *tree)
 
 	/* empty tree */
 	if (!iter->tree->ctl->root)
+	{
+		RT_UNLOCK(tree);
 		return iter;
+	}
 
 	root = RT_PTR_GET_LOCAL(tree, iter->tree->ctl->root);
 	top_level = root->shift / RT_NODE_SPAN;
@@ -1852,11 +1909,12 @@ RT_BEGIN_ITERATE(RT_RADIX_TREE *tree)
 
 	MemoryContextSwitchTo(old_ctx);
 
+	RT_UNLOCK(tree);
 	return iter;
 }
 
 /*
- * Return true with setting key_p and value_p if there is next key.  Otherwise,
+ * Return true with setting key_p and value_p if there is next key.	 Otherwise,
  * return false.
  */
 RT_SCOPE bool
@@ -1864,9 +1922,14 @@ RT_ITERATE_NEXT(RT_ITER *iter, uint64 *key_p, RT_VALUE_TYPE *value_p)
 {
 	Assert(iter->tree->ctl->iter_active);
 
+	RT_LOCK_SHARED(iter->tree);
+
 	/* Empty tree */
 	if (!iter->tree->ctl->root)
+	{
+		RT_UNLOCK(iter->tree);
 		return false;
+	}
 
 	for (;;)
 	{
@@ -1882,6 +1945,7 @@ RT_ITERATE_NEXT(RT_ITER *iter, uint64 *key_p, RT_VALUE_TYPE *value_p)
 		{
 			*key_p = iter->key;
 			*value_p = value;
+			RT_UNLOCK(iter->tree);
 			return true;
 		}
 
@@ -1899,7 +1963,10 @@ RT_ITERATE_NEXT(RT_ITER *iter, uint64 *key_p, RT_VALUE_TYPE *value_p)
 
 		/* the iteration finished */
 		if (!child)
+		{
+			RT_UNLOCK(iter->tree);
 			return false;
+		}
 
 		/*
 		 * Set the node to the node iterator and update the iterator stack
@@ -1910,13 +1977,17 @@ RT_ITERATE_NEXT(RT_ITER *iter, uint64 *key_p, RT_VALUE_TYPE *value_p)
 		/* Node iterators are updated, so try again from the leaf */
 	}
 
+	RT_UNLOCK(iter->tree);
 	return false;
 }
 
 RT_SCOPE void
 RT_END_ITERATE(RT_ITER *iter)
 {
+	RT_LOCK_EXCLUSIVE(iter->tree);
 	iter->tree->ctl->iter_active = false;
+	RT_UNLOCK(iter->tree);
+
 	pfree(iter);
 }
 
@@ -1928,6 +1999,8 @@ RT_MEMORY_USAGE(RT_RADIX_TREE *tree)
 {
 	Size		total = 0;
 
+	RT_LOCK_SHARED(tree);
+
 #ifdef RT_SHMEM
 	Assert(tree->ctl->magic == RT_RADIX_TREE_MAGIC);
 	total = dsa_get_total_size(tree->dsa);
@@ -1939,6 +2012,7 @@ RT_MEMORY_USAGE(RT_RADIX_TREE *tree)
 	}
 #endif
 
+	RT_UNLOCK(tree);
 	return total;
 }
 
@@ -2023,6 +2097,8 @@ RT_VERIFY_NODE(RT_PTR_LOCAL node)
 RT_SCOPE void
 RT_STATS(RT_RADIX_TREE *tree)
 {
+	RT_LOCK_SHARED(tree);
+
 	fprintf(stderr, "max_val = " UINT64_FORMAT "\n", tree->ctl->max_val);
 	fprintf(stderr, "num_keys = " UINT64_FORMAT "\n", tree->ctl->num_keys);
 
@@ -2042,6 +2118,8 @@ RT_STATS(RT_RADIX_TREE *tree)
 				tree->ctl->cnt[RT_CLASS_125],
 				tree->ctl->cnt[RT_CLASS_256]);
 	}
+
+	RT_UNLOCK(tree);
 }
 
 static void
@@ -2235,14 +2313,18 @@ RT_DUMP_SEARCH(RT_RADIX_TREE *tree, uint64 key)
 
 	RT_STATS(tree);
 
+	RT_LOCK_SHARED(tree);
+
 	if (!RT_PTR_ALLOC_IS_VALID(tree->ctl->root))
 	{
+		RT_UNLOCK(tree);
 		fprintf(stderr, "empty tree\n");
 		return;
 	}
 
 	if (key > tree->ctl->max_val)
 	{
+		RT_UNLOCK(tree);
 		fprintf(stderr, "key " UINT64_FORMAT "(0x" RT_UINT64_FORMAT_HEX ") is larger than max val\n",
 				key, key);
 		return;
@@ -2276,6 +2358,7 @@ RT_DUMP_SEARCH(RT_RADIX_TREE *tree, uint64 key)
 		shift -= RT_NODE_SPAN;
 		level++;
 	}
+	RT_UNLOCK(tree);
 
 	fprintf(stderr, "%s", buf.data);
 }
@@ -2287,8 +2370,11 @@ RT_DUMP(RT_RADIX_TREE *tree)
 
 	RT_STATS(tree);
 
+	RT_LOCK_SHARED(tree);
+
 	if (!RT_PTR_ALLOC_IS_VALID(tree->ctl->root))
 	{
+		RT_UNLOCK(tree);
 		fprintf(stderr, "empty tree\n");
 		return;
 	}
@@ -2296,6 +2382,7 @@ RT_DUMP(RT_RADIX_TREE *tree)
 	initStringInfo(&buf);
 
 	RT_DUMP_NODE(tree, tree->ctl->root, 0, true, &buf);
+	RT_UNLOCK(tree);
 
 	fprintf(stderr, "%s",buf.data);
 }
@@ -2323,6 +2410,9 @@ RT_DUMP(RT_RADIX_TREE *tree)
 #undef RT_GET_KEY_CHUNK
 #undef BM_IDX
 #undef BM_BIT
+#undef RT_LOCK_EXCLUSIVE
+#undef RT_LOCK_SHARED
+#undef RT_UNLOCK
 #undef RT_NODE_IS_LEAF
 #undef RT_NODE_MUST_GROW
 #undef RT_NODE_KIND_COUNT
diff --git a/src/test/modules/test_radixtree/test_radixtree.c b/src/test/modules/test_radixtree/test_radixtree.c
index 2a93e731ae..bbe1a619b6 100644
--- a/src/test/modules/test_radixtree/test_radixtree.c
+++ b/src/test/modules/test_radixtree/test_radixtree.c
@@ -144,7 +144,7 @@ test_empty(void)
 	dsa_area   *dsa;
 	dsa = dsa_create(tranche_id);
 
-	radixtree = rt_create(CurrentMemoryContext, dsa);
+	radixtree = rt_create(CurrentMemoryContext, dsa, tranche_id);
 #else
 	radixtree = rt_create(CurrentMemoryContext);
 #endif
@@ -195,7 +195,7 @@ test_basic(int children, bool test_inner)
 		 test_inner ? "inner" : "leaf", children);
 
 #ifdef RT_SHMEM
-	radixtree = rt_create(CurrentMemoryContext, dsa);
+	radixtree = rt_create(CurrentMemoryContext, dsa, tranche_id);
 #else
 	radixtree = rt_create(CurrentMemoryContext);
 #endif
@@ -363,7 +363,7 @@ test_node_types(uint8 shift)
 	elog(NOTICE, "testing radix tree node types with shift \"%d\"", shift);
 
 #ifdef RT_SHMEM
-	radixtree = rt_create(CurrentMemoryContext, dsa);
+	radixtree = rt_create(CurrentMemoryContext, dsa, tranche_id);
 #else
 	radixtree = rt_create(CurrentMemoryContext);
 #endif
@@ -434,7 +434,7 @@ test_pattern(const test_spec * spec)
 	MemoryContextSetIdentifier(radixtree_ctx, spec->test_name);
 
 #ifdef RT_SHMEM
-	radixtree = rt_create(radixtree_ctx, dsa);
+	radixtree = rt_create(radixtree_ctx, dsa, tranche_id);
 #else
 	radixtree = rt_create(radixtree_ctx);
 #endif
-- 
2.31.1