Optimizing RelationFindReplTupleSeq() for CLOBBER_CACHE_ALWAYS

Started by Noah Mischalmost 6 years ago1 messages
#1Noah Misch
noah@leadboat.com
1 attachment(s)

When testing commit c6b9204 with CLOBBER_CACHE_ALWAYS, of the 20 hours for
check-world, 001_rep_changes.pl took 1.8 hours. At commit 5406513, the test
failed at a poll_query_until() timeout[1]This seemed to result from the poll query being 2-3x faster at commit 5406513, not from logical replication being slower. (poll_query_until() times out after 1800 polls separated by 0.1s sleeps, however long that takes.) I had guessed that commit 1c7a0b3 greatly accelerated this test case, but it gave about a 4% improvement under CLOBBER_CACHE_ALWAYS.. The slow part is the logical
replication of "DELETE FROM tab_ins WHERE a > 0", which deletes 100 records
from a table of ~1100 records, using RelationFindReplTupleSeq().
tuples_equal() called lookup_type_cache() for every comparison. Performing
those lookups once per RelationFindReplTupleSeq(), as attached, cut the test's
runtime by an order of magnitude. While performance for CLOBBER_CACHE_ALWAYS
is not important, this is consistent with record_eq() and is easy. I'm
slightly inclined not to back-patch it, though.

[1]: This seemed to result from the poll query being 2-3x faster at commit 5406513, not from logical replication being slower. (poll_query_until() times out after 1800 polls separated by 0.1s sleeps, however long that takes.) I had guessed that commit 1c7a0b3 greatly accelerated this test case, but it gave about a 4% improvement under CLOBBER_CACHE_ALWAYS.
5406513, not from logical replication being slower. (poll_query_until() times
out after 1800 polls separated by 0.1s sleeps, however long that takes.) I
had guessed that commit 1c7a0b3 greatly accelerated this test case, but it
gave about a 4% improvement under CLOBBER_CACHE_ALWAYS.

Attachments:

clobber-speed-tuples_equal-v1.patchtext/plain; charset=us-asciiDownload
Author:     Noah Misch <noah@leadboat.com>
Commit:     Noah Misch <noah@leadboat.com>

    Optimize RelationFindReplTupleSeq() for CLOBBER_CACHE_ALWAYS.
    
    Specifically, remember lookup_type_cache() results instead of retrieving
    them once per comparison.  Under CLOBBER_CACHE_ALWAYS, this reduced
    src/test/subscription/t/001_rep_changes.pl elapsed time by an order of
    magnitude, which reduced check-world elapsed time by 9%.
    
    Reviewed by FIXME.
    
    Discussion: https://postgr.es/m/FIXME

diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c
index 7194bec..58448ba 100644
--- a/src/backend/executor/execReplication.c
+++ b/src/backend/executor/execReplication.c
@@ -225,7 +225,8 @@ retry:
  * Compare the tuples in the slots by checking if they have equal values.
  */
 static bool
-tuples_equal(TupleTableSlot *slot1, TupleTableSlot *slot2)
+tuples_equal(TupleTableSlot *slot1, TupleTableSlot *slot2,
+			 TypeCacheEntry **eq)
 {
 	int			attrnum;
 
@@ -256,12 +257,18 @@ tuples_equal(TupleTableSlot *slot1, TupleTableSlot *slot2)
 
 		att = TupleDescAttr(slot1->tts_tupleDescriptor, attrnum);
 
-		typentry = lookup_type_cache(att->atttypid, TYPECACHE_EQ_OPR_FINFO);
-		if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
-			ereport(ERROR,
-					(errcode(ERRCODE_UNDEFINED_FUNCTION),
-					 errmsg("could not identify an equality operator for type %s",
-							format_type_be(att->atttypid))));
+		typentry = eq[attrnum];
+		if (typentry == NULL)
+		{
+			typentry = lookup_type_cache(att->atttypid,
+										 TYPECACHE_EQ_OPR_FINFO);
+			if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
+				ereport(ERROR,
+						(errcode(ERRCODE_UNDEFINED_FUNCTION),
+						 errmsg("could not identify an equality operator for type %s",
+								format_type_be(att->atttypid))));
+			eq[attrnum] = typentry;
+		}
 
 		if (!DatumGetBool(FunctionCall2Coll(&typentry->eq_opr_finfo,
 											att->attcollation,
@@ -290,12 +297,15 @@ RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
 	TupleTableSlot *scanslot;
 	TableScanDesc scan;
 	SnapshotData snap;
+	TypeCacheEntry **eq;
 	TransactionId xwait;
 	bool		found;
 	TupleDesc	desc PG_USED_FOR_ASSERTS_ONLY = RelationGetDescr(rel);
 
 	Assert(equalTupleDescs(desc, outslot->tts_tupleDescriptor));
 
+	eq = palloc0(sizeof(*eq) * outslot->tts_tupleDescriptor->natts);
+
 	/* Start a heap scan. */
 	InitDirtySnapshot(snap);
 	scan = table_beginscan(rel, &snap, 0, NULL);
@@ -309,7 +319,7 @@ retry:
 	/* Try to find the tuple */
 	while (table_scan_getnextslot(scan, ForwardScanDirection, scanslot))
 	{
-		if (!tuples_equal(scanslot, searchslot))
+		if (!tuples_equal(scanslot, searchslot, eq))
 			continue;
 
 		found = true;