Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1tGzc5-002Le7-63 for pgsql-hackers@arkaria.postgresql.org; Fri, 29 Nov 2024 11:56:45 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.94.2) (envelope-from ) id 1tGzc1-0076bx-5o for pgsql-hackers@arkaria.postgresql.org; Fri, 29 Nov 2024 11:56:42 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1tGzc0-0076bo-N9 for pgsql-hackers@lists.postgresql.org; Fri, 29 Nov 2024 11:56:42 +0000 Received: from mail-ed1-x52e.google.com ([2a00:1450:4864:20::52e]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.94.2) (envelope-from ) id 1tGzby-0008q8-2W; Fri, 29 Nov 2024 11:56:41 +0000 Received: by mail-ed1-x52e.google.com with SMTP id 4fb4d7f45d1cf-5d071f70b51so2161374a12.3; Fri, 29 Nov 2024 03:56:38 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1732881397; x=1733486197; darn=postgresql.org; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=T4tFxf0ScASHW7taXMEGsH2nKIUi0OO1EOAxEocr59o=; b=VbK87mpxRAPdXtkb6gsrr7JCTFRySXwb4DT8IupjQuO6Foz+GMpf2TvCaC+41qY0aI 4LpiAaPtvvxbh1GEcl78Y115VozhNfDXcizFDGRmzX20TbP3dqMs2gs3OyT0UoKw5sGj exfSQ/EinZVoR3aaRFqnP31jlnOpmAdAKVBlKjXZK4klwQTxfZxa9Fws5+SkuVA4wR/l RBhqp9Mrj0F6C5kWeSwiNWQKxXvRMVedeTdDI6iyIvgcROsYxjt1e6uvrjavu4iePta7 AbPFk1AQtEhssUyTwWPZPNIeZt90PnVnpGfb1JuGV340sgwvo8xqDGxY03TJ6nMBbimZ OVhg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1732881397; x=1733486197; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=T4tFxf0ScASHW7taXMEGsH2nKIUi0OO1EOAxEocr59o=; b=pzRpjLldiTW6Rt8WsCR9TehKPEM9MPr39endglA6dnRHMykYtCOKI9NJlMk9kAtJ6V UKQgQmpO3tUCuxJuQWQ1HF5NPNTJuL8JuSURg3aP8sVcp4vBLEbTBxFLnHdLzfEwlmwh gbeSH0BSbf+k7p2CugmBvTYLDdLUCNKLyO4E1mdLs1kTuaf4dNYcNCJxftNZEntrNkbS CbN7exrldivqxzG1RssFGdvnP29GcIaQclqvY0kHDzKqziP42RUN9tFSGOCtHJ1RK+iq nrgja1nKkHgVA0+19Cyv0UxOpG3hhwlUb6UIOCr/8525gNu9Udm8dGWKLB8wikS4oN0e IOvQ== X-Forwarded-Encrypted: i=1; AJvYcCUAjRzGX4iWr+JEKyXgU0yBjjldXrxIx/7uMN7DNyFVnPoPcGhssntyXUGoVOFZ0skFIEzXhcQ=@postgresql.org X-Gm-Message-State: AOJu0YwN6NT77mwFsGlTPe9eKLD/d44UMkfoDeBb1FMVoOSnkh8I9ReQ sTsOJBWOs0MAHW9GYM/DumoTxU6jPjRXau/BihAKiLLK4dBicZQTyel1gG5LWrdqVlVG+8krD4y JvX4ZActQXfB/F16W7inhC7ozhvD7KjSI X-Gm-Gg: ASbGnctkE0V1NHhdNM0T09VBR8JOjvvWhCFMQNV/CRCpQz/NCzgJKVTZY64s48xSOyj r+oj3jlQ9KxtagAj1QzlMhkouJ3SsSQVnQH6dsk6I/nKDSGfCJvITjgO8tIn64P0= X-Google-Smtp-Source: AGHT+IHepoUkRsefx1RridmoyhyV6PSSdYV1PSZsOrZRMITzRWbTbmb3OqG34nCO5FcMMPHpMOXWq2TKXazLcWCS9ak= X-Received: by 2002:a17:906:3cb2:b0:aa5:2041:6a6f with SMTP id a640c23a62f3a-aa580f4c532mr757660466b.27.1732881396790; Fri, 29 Nov 2024 03:56:36 -0800 (PST) MIME-Version: 1.0 References: In-Reply-To: From: Michail Nikolaev Date: Fri, 29 Nov 2024 12:55:00 +0100 Message-ID: Subject: Re: Strange assertion in procarray.c To: Michael Paquier Cc: PostgreSQL Hackers , nathan@postgresql.org, Heikki Linnakangas Content-Type: multipart/alternative; boundary="00000000000017fe7106280be4b4" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --00000000000017fe7106280be4b4 Content-Type: text/plain; charset="UTF-8" Hello, Michael! > I fail to see how this is related? The original issue was that this > was impossible to run safely concurrently, but now we have the > facilities able to do so. There are a few cases where using a wait > point has limits, for example outside a transaction context for some > of the processes, but that has not really been an issue up to now. I encountered this issue while trying to stabilize tests for [0]. Tests were crashing during the installcheck with assertion of that thread. I have spent time trying to identify the root cause - injection points and the way it may affect another tests even if them set to be executed locally. In the spec, the backend performs the following: SELECT injection_points_set_local(); SELECT injection_points_attach('invalidate_catalog_snapshot_end', 'wait'); That's all - we don't even execute any command that would trigger the wait condition. Meanwhile, three different backends are attempting to test SERIALIZABLE isolation without any injection points. Initially, this was a separate 'two-ids' test executed in parallel during installcheck, but I incorporated it into the reproducer spec for simplicity. > Isn't that pointing to an actual bug with serializable transactions? No, let me explain. > What you are telling here is that there is a race condition where it > is possible to trigger an assertion failure when finishing a session > while another one is waiting on an invalidation, if there's in the mix > a read/write dependency error. Actually, no backend is waiting for invalidation in this case. Here's the sequence of events: * The s4 backend creates a local injection point but performs no further actions. The injection point is marked as local for that pid. * Three other backends proceed with their serializable snapshot operations. * s2 determines it cannot commit and correctly decides to abort the transaction. * s2 begins releasing resources: ResourceOwnerReleaseInternal resowner.c:694 <--- NOTE: After starting the release process, by calling this function, no new ResourceOwnerRelease resowner.c:654 resources can be remembered in the resource owner. AbortTransaction xact.c:2960 AbortCurrentTransactionInternal xact.c:3531 AbortCurrentTransaction xact.c:3449 PostgresMain postgres.c:4513 BackendMain backend_startup.c:107 postmaster_child_launch launch_backend.c:274 BackendStartup postmaster.c:3377 ServerLoop postmaster.c:1663 PostmasterMain postmaster.c:1361 main main.c:196 * During transaction abort, s2 invalidates its catalog snapshot with this stack trace: InvalidateCatalogSnapshot snapmgr.c:430 AtEOXact_Snapshot snapmgr.c:1050 CleanupTransaction xact.c:3016 AbortCurrentTransactionInternal xact.c:3532 AbortCurrentTransaction xact.c:3449 PostgresMain postgres.c:4513 BackendMain backend_startup.c:107 postmaster_child_launch launch_backend.c:274 BackendStartup postmaster.c:3377 ServerLoop postmaster.c:1663 PostmasterMain postmaster.c:1361 main main.c:196 * Consequently, s2 encounters INJECTION_POINT("invalidate_catalog_snapshot_end"); * Although invalidate_catalog_snapshot_end is set to 'wait' only for s4, s2 enters the injection_wait handler * Since this is s2's first interaction with injection points (as injection_points isn't in shared_preload_libraries), it calls injection_init_shmem * Here, GetNamedDSMSegment is called - this is new infrastructure for initializing shared memory for extensions without shared_preload_libraries, committed by Nathan [1] * GetNamedDSMSegment attempts to attach to memory and triggers the assertion: if (owner->releasing) elog(ERROR, "ResourceOwnerEnlarge called after release started"); ResourceOwnerEnlarge resowner.c:449 dsm_create_descriptor dsm.c:1206 dsm_attach dsm.c:696 dsa_attach dsa.c:519 init_dsm_registry dsm_registry.c:115 GetNamedDSMSegment dsm_registry.c:156 injection_init_shmem injection_points.c:185 injection_wait injection_points.c:277 InjectionPointRun injection_point.c:551 InvalidateCatalogSnapshot snapmgr.c:430 AtEOXact_Snapshot snapmgr.c:1050 CleanupTransaction xact.c:3016 AbortCurrentTransactionInternal xact.c:3532 AbortCurrentTransaction xact.c:3449 PostgresMain postgres.c:4513 BackendMain backend_startup.c:107 postmaster_child_launch launch_backend.c:274 BackendStartup postmaster.c:3377 ServerLoop postmaster.c:1663 PostmasterMain postmaster.c:1361 main main.c:196 * This assertion during transaction abort triggers another abort call (this could be improved): ProcArrayEndTransaction procarray.c:677 AbortTransaction xact.c:2946 AbortCurrentTransactionInternal xact.c:3531 AbortCurrentTransaction xact.c:3449 PostgresMain postgres.c:4513 <--------------- exception handler here BackendMain backend_startup.c:107 postmaster_child_launch launch_backend.c:274 BackendStartup postmaster.c:3377 ServerLoop postmaster.c:1663 PostmasterMain postmaster.c:1361 main main.c:196 This isn't the same abort attempt - it's the second one, which triggers Assert(TransactionIdIsValid(proc->xid)); In summary: Each code component functions correctly in isolation However, when an injection point registered as local by one backend causes another backend to register resources (and potentially other operations), it can lead to difficult-to-diagnose issues I see several potential solutions: * Add injection_points to shared_preload_libraries for all tests * Implement a mechanism to call prev_shmem_startup_hook for libraries outside shared_preload_libraries * Modify GetNamedDSMSegment's behavior * Run all injection_points tests in an isolated environment In my opinion, the last option seems most appropriate. Best regards, Mikhail. [0]: https://commitfest.postgresql.org/50/5160/ [1]: https://github.com/postgres/postgres/commit/8b2bcf3f287c79eaebf724cba57e5ff664b01e06 --00000000000017fe7106280be4b4 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hello, Michael!

> I fail to see how this is rela= ted?=C2=A0 The original issue was that this
> was impossible to run s= afely concurrently, but now we have the
> facilities able to do so.= =C2=A0 There are a few cases where using a wait
> point has limits, f= or example outside a transaction context for some
> of the processes,= but that has not really been an issue up to now.

I encountered this= issue while trying to stabilize tests for [0].

Tests were crashing = during the installcheck with assertion of that thread.
I have spent time= trying to identify the root cause - injection points and
the way it may= affect another tests even if them set to be executed locally.

In th= e spec, the backend performs the following:

=C2=A0 =C2=A0 SELECT inj= ection_points_set_local();
=C2=A0 =C2=A0 SELECT injection_points_attach(= 'invalidate_catalog_snapshot_end', 'wait');

That'= ;s all - we don't even execute any command that would trigger the wait = condition.

Meanwhile, three different backends are attempting to tes= t SERIALIZABLE isolation without any injection points.
Initially, this w= as a separate 'two-ids' test executed in parallel during installche= ck,
but I incorporated it into the reproducer spec for simplicity.
> Isn't that pointing to an actual bug with serializable transacti= ons?

No, let me explain.

> What you are telling here is th= at there is a race condition where it
> is possible to trigger an ass= ertion failure when finishing a session
> while another one is waitin= g on an invalidation, if there's in the mix
> a read/write depend= ency error.

Actually, no backend is waiting for invalidation in this= case.

Here's the sequence of events:

* The s4 backend cr= eates a local injection point but performs no further actions. The injectio= n point is marked as local for that pid.
* Three other backends proceed = with their serializable snapshot operations.
* s2 determines it cannot c= ommit and correctly decides to abort the transaction.
* s2 begins releas= ing resources:

=C2=A0 =C2=A0 =C2=A0 =C2=A0 ResourceOwnerReleaseInter= nal resowner.c:694 <--- NOTE: After starting the release process, by cal= ling this function, no new
=C2=A0 =C2=A0 =C2=A0 =C2=A0 ResourceOwnerRele= ase resowner.c:654 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0resources can be remembered in the resource owner.
=C2=A0 = =C2=A0 =C2=A0 =C2=A0 AbortTransaction xact.c:2960
=C2=A0 =C2=A0 =C2=A0 = =C2=A0 AbortCurrentTransactionInternal xact.c:3531
=C2=A0 =C2=A0 =C2=A0 = =C2=A0 AbortCurrentTransaction xact.c:3449
=C2=A0 =C2=A0 =C2=A0 =C2=A0 P= ostgresMain postgres.c:4513
=C2=A0 =C2=A0 =C2=A0 =C2=A0 BackendMain back= end_startup.c:107
=C2=A0 =C2=A0 =C2=A0 =C2=A0 postmaster_child_launch la= unch_backend.c:274
=C2=A0 =C2=A0 =C2=A0 =C2=A0 BackendStartup postmaster= .c:3377
=C2=A0 =C2=A0 =C2=A0 =C2=A0 ServerLoop postmaster.c:1663
=C2= =A0 =C2=A0 =C2=A0 =C2=A0 PostmasterMain postmaster.c:1361
=C2=A0 =C2=A0 = =C2=A0 =C2=A0 main main.c:196

* During transaction abort, s2 invalid= ates its catalog snapshot with this stack trace:

=C2=A0 =C2=A0 =C2= =A0 =C2=A0 InvalidateCatalogSnapshot snapmgr.c:430
=C2=A0 =C2=A0 =C2=A0 = =C2=A0 AtEOXact_Snapshot snapmgr.c:1050
=C2=A0 =C2=A0 =C2=A0 =C2=A0 Clea= nupTransaction xact.c:3016
=C2=A0 =C2=A0 =C2=A0 =C2=A0 AbortCurrentTrans= actionInternal xact.c:3532
=C2=A0 =C2=A0 =C2=A0 =C2=A0 AbortCurrentTrans= action xact.c:3449
=C2=A0 =C2=A0 =C2=A0 =C2=A0 PostgresMain postgres.c:4= 513
=C2=A0 =C2=A0 =C2=A0 =C2=A0 BackendMain backend_startup.c:107
=C2= =A0 =C2=A0 =C2=A0 =C2=A0 postmaster_child_launch launch_backend.c:274
= =C2=A0 =C2=A0 =C2=A0 =C2=A0 BackendStartup postmaster.c:3377
=C2=A0 =C2= =A0 =C2=A0 =C2=A0 ServerLoop postmaster.c:1663
=C2=A0 =C2=A0 =C2=A0 =C2= =A0 PostmasterMain postmaster.c:1361
=C2=A0 =C2=A0 =C2=A0 =C2=A0 main ma= in.c:196

* Consequently, s2 encounters INJECTION_POINT("invalid= ate_catalog_snapshot_end");
* Although invalidate_catalog_snapshot_= end is set to 'wait' only for s4, s2 enters the injection_wait hand= ler
* Since this is s2's first interaction with injection points (as= injection_points isn't in shared_preload_libraries), it calls injectio= n_init_shmem
* Here, GetNamedDSMSegment is called - this is new infrastr= ucture for initializing shared memory for extensions without shared_preload= _libraries, committed by Nathan [1]
* GetNamedDSMSegment attempts to att= ach to memory and triggers the assertion:

=C2=A0 =C2=A0 =C2=A0 =C2= =A0 if (owner->releasing)
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 e= log(ERROR, "ResourceOwnerEnlarge called after release started");<= br>
=C2=A0 =C2=A0 =C2=A0 =C2=A0 ResourceOwnerEnlarge resowner.c:449
= =C2=A0 =C2=A0 =C2=A0 =C2=A0 dsm_create_descriptor dsm.c:1206
=C2=A0 =C2= =A0 =C2=A0 =C2=A0 dsm_attach dsm.c:696
=C2=A0 =C2=A0 =C2=A0 =C2=A0 dsa_a= ttach dsa.c:519
=C2=A0 =C2=A0 =C2=A0 =C2=A0 init_dsm_registry dsm_regist= ry.c:115
=C2=A0 =C2=A0 =C2=A0 =C2=A0 GetNamedDSMSegment dsm_registry.c:1= 56
=C2=A0 =C2=A0 =C2=A0 =C2=A0 injection_init_shmem injection_points.c:1= 85
=C2=A0 =C2=A0 =C2=A0 =C2=A0 injection_wait injection_points.c:277
= =C2=A0 =C2=A0 =C2=A0 =C2=A0 InjectionPointRun injection_point.c:551
=C2= =A0 =C2=A0 =C2=A0 =C2=A0 InvalidateCatalogSnapshot snapmgr.c:430
=C2=A0 = =C2=A0 =C2=A0 =C2=A0 AtEOXact_Snapshot snapmgr.c:1050
=C2=A0 =C2=A0 =C2= =A0 =C2=A0 CleanupTransaction xact.c:3016
=C2=A0 =C2=A0 =C2=A0 =C2=A0 Ab= ortCurrentTransactionInternal xact.c:3532
=C2=A0 =C2=A0 =C2=A0 =C2=A0 Ab= ortCurrentTransaction xact.c:3449
=C2=A0 =C2=A0 =C2=A0 =C2=A0 PostgresMa= in postgres.c:4513
=C2=A0 =C2=A0 =C2=A0 =C2=A0 BackendMain backend_start= up.c:107
=C2=A0 =C2=A0 =C2=A0 =C2=A0 postmaster_child_launch launch_back= end.c:274
=C2=A0 =C2=A0 =C2=A0 =C2=A0 BackendStartup postmaster.c:3377=C2=A0 =C2=A0 =C2=A0 =C2=A0 ServerLoop postmaster.c:1663
=C2=A0 =C2=A0= =C2=A0 =C2=A0 PostmasterMain postmaster.c:1361
=C2=A0 =C2=A0 =C2=A0 =C2= =A0 main main.c:196

* This assertion during transaction abort trigge= rs another abort call (this could be improved):

=C2=A0 =C2=A0 =C2=A0= =C2=A0 ProcArrayEndTransaction procarray.c:677
=C2=A0 =C2=A0 =C2=A0 =C2= =A0 AbortTransaction xact.c:2946
=C2=A0 =C2=A0 =C2=A0 =C2=A0 AbortCurren= tTransactionInternal xact.c:3531
=C2=A0 =C2=A0 =C2=A0 =C2=A0 AbortCurren= tTransaction xact.c:3449
=C2=A0 =C2=A0 =C2=A0 =C2=A0 PostgresMain postgr= es.c:4513 <--------------- exception handler here
=C2=A0 =C2=A0 =C2= =A0 =C2=A0 BackendMain backend_startup.c:107
=C2=A0 =C2=A0 =C2=A0 =C2=A0= postmaster_child_launch launch_backend.c:274
=C2=A0 =C2=A0 =C2=A0 =C2= =A0 BackendStartup postmaster.c:3377
=C2=A0 =C2=A0 =C2=A0 =C2=A0 ServerL= oop postmaster.c:1663
=C2=A0 =C2=A0 =C2=A0 =C2=A0 PostmasterMain postmas= ter.c:1361
=C2=A0 =C2=A0 =C2=A0 =C2=A0 main main.c:196

This isn&#= 39;t the same abort attempt - it's the second one, which triggers Asser= t(TransactionIdIsValid(proc->xid));

In summary:

Each code = component functions correctly in isolation
However, when an injection po= int registered as local by one backend causes another backend to register r= esources (and potentially other operations),
it can lead to difficult-to= -diagnose issues

I see several potential solutions:

* Add inj= ection_points to shared_preload_libraries for all tests
* Implement a me= chanism to call prev_shmem_startup_hook for libraries outside shared_preloa= d_libraries
* Modify GetNamedDSMSegment's behavior
* Run all inje= ction_points tests in an isolated environment

In my opinion, the las= t option seems most appropriate.

Best regards,
Mikhail.

[0= ]: https://commitfes= t.postgresql.org/50/5160/
[1]: https://github.= com/postgres/postgres/commit/8b2bcf3f287c79eaebf724cba57e5ff664b01e06 --00000000000017fe7106280be4b4--