Thread
Commits
GET /api/v1/messages/:b64id/commits
the thread's linked commits as JSON, with link sources.
API reference →
-
Avoid orphaned objects dependencies
- 2fbb21170e90 19 (unreleased) landed
- 5100bdbd3ba2 14 (unreleased) landed
- 5fa137727db0 15 (unreleased) landed
- d9bc0d96c247 16 (unreleased) landed
- 3a9909eda207 17 (unreleased) landed
- c8cd3d6976f7 18 (unreleased) landed
-
Don't try to record dependency on a dropped column's datatype
- fd93ee100830 19 (unreleased) landed
- 36b6ed2606e1 14 (unreleased) landed
- ef3d7b15e4cd 15 (unreleased) landed
- d44cd467471c 16 (unreleased) landed
- c1588f92a915 17 (unreleased) landed
- f9d5a52da4ca 18 (unreleased) landed
-
Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2024-04-22T08:45:19Z
Hi, This new thread is a follow-up of [1] and [2]. Problem description: We have occasionally observed objects having an orphaned dependency, the most common case we have seen is functions not linked to any namespaces. Examples to produce such orphaned dependencies: Scenario 1: session 1: begin; drop schema schem; session 2: create a function in the schema schem session 1: commit; With the above, the function created in session 2 would be linked to a non existing schema. Scenario 2: session 1: begin; create a function in the schema schem session 2: drop schema schem; session 1: commit; With the above, the function created in session 1 would be linked to a non existing schema. A patch has been initially proposed to fix this particular (function-to-namespace) dependency (see [1]), but there could be much more scenarios (like the function-to-datatype one highlighted by Gilles in [1] that could lead to a function having an invalid parameter datatype). As Tom said there are dozens more cases that would need to be considered, and a global approach to avoid those race conditions should be considered instead. A first global approach attempt has been proposed in [2] making use of a dirty snapshot when recording the dependency. But this approach appeared to be "scary" and it was still failing to close some race conditions (see [2] for details). Then, Tom proposed another approach in [2] which is that "creation DDL will have to take a lock on each referenced object that'd conflict with a lock taken by DROP". This is what the attached patch is trying to achieve. It does the following: 1) A new lock (that conflicts with a lock taken by DROP) has been put in place when the dependencies are being recorded. Thanks to it, the drop schema in scenario 2 would be locked (resulting in an error should session 1 committs). 2) After locking the object while recording the dependency, the patch checks that the object still exists. Thanks to it, session 2 in scenario 1 would be locked and would report an error once session 1 committs (that would not be the case should session 1 abort the transaction). The patch also adds a few tests for some dependency cases (that would currently produce orphaned objects): - schema and function (as the above scenarios) - function and type - table and type (which is I think problematic enough, as involving a table into the game, to fix this stuff as a whole). [1]: https://www.postgresql.org/message-id/flat/a4f55089-7cbd-fe5d-a9bb-19adc6418ae9@darold.net#9af5cdaa9e80879beb1def3604c976e8 [2]: https://www.postgresql.org/message-id/flat/8369ff70-0e31-f194-2954-787f4d9e21dd%40amazon.com Please note that I'm not used to with this area of the code so that the patch might not take the option proposed by Tom the "right" way. Adding the patch to the July CF. Looking forward to your feedback, Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Alexander Lakhin <exclusion@gmail.com> — 2024-04-22T10:00:00Z
Hi Bertrand, 22.04.2024 11:45, Bertrand Drouvot wrote: > Hi, > > This new thread is a follow-up of [1] and [2]. > > Problem description: > > We have occasionally observed objects having an orphaned dependency, the > most common case we have seen is functions not linked to any namespaces. > > ... > > Looking forward to your feedback, This have reminded me of bug #17182 [1]. Unfortunately, with the patch applied, the following script: for ((i=1;i<=100;i++)); do ( { for ((n=1;n<=20;n++)); do echo "DROP SCHEMA s;"; done } | psql ) >psql1.log 2>&1 & echo " CREATE SCHEMA s; CREATE FUNCTION s.func1() RETURNS int LANGUAGE SQL AS 'SELECT 1;'; CREATE FUNCTION s.func2() RETURNS int LANGUAGE SQL AS 'SELECT 2;'; CREATE FUNCTION s.func3() RETURNS int LANGUAGE SQL AS 'SELECT 3;'; CREATE FUNCTION s.func4() RETURNS int LANGUAGE SQL AS 'SELECT 4;'; CREATE FUNCTION s.func5() RETURNS int LANGUAGE SQL AS 'SELECT 5;'; " | psql >psql2.log 2>&1 & wait psql -c "DROP SCHEMA s CASCADE" >psql3.log done echo " SELECT pg_identify_object('pg_proc'::regclass, pp.oid, 0), pp.oid FROM pg_proc pp LEFT JOIN pg_namespace pn ON pp.pronamespace = pn.oid WHERE pn.oid IS NULL" | psql still ends with: server closed the connection unexpectedly This probably means the server terminated abnormally before or while processing the request. 2024-04-22 09:54:39.171 UTC|||662633dc.152bbc|LOG: server process (PID 1388378) was terminated by signal 11: Segmentation fault 2024-04-22 09:54:39.171 UTC|||662633dc.152bbc|DETAIL: Failed process was running: SELECT pg_identify_object('pg_proc'::regclass, pp.oid, 0), pp.oid FROM pg_proc pp LEFT JOIN pg_namespace pn ON pp.pronamespace = pn.oid WHERE pn.oid IS NULL [1] https://www.postgresql.org/message-id/flat/17182-a6baa001dd1784be%40postgresql.org Best regards, Alexander -
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2024-04-22T10:52:21Z
Hi, On Mon, Apr 22, 2024 at 01:00:00PM +0300, Alexander Lakhin wrote: > Hi Bertrand, > > 22.04.2024 11:45, Bertrand Drouvot wrote: > > Hi, > > > > This new thread is a follow-up of [1] and [2]. > > > > Problem description: > > > > We have occasionally observed objects having an orphaned dependency, the > > most common case we have seen is functions not linked to any namespaces. > > > > ... > > > > Looking forward to your feedback, > > This have reminded me of bug #17182 [1]. Thanks for the link to the bug! > Unfortunately, with the patch applied, the following script: > > for ((i=1;i<=100;i++)); do > ( { for ((n=1;n<=20;n++)); do echo "DROP SCHEMA s;"; done } | psql ) >psql1.log 2>&1 & > echo " > CREATE SCHEMA s; > CREATE FUNCTION s.func1() RETURNS int LANGUAGE SQL AS 'SELECT 1;'; > CREATE FUNCTION s.func2() RETURNS int LANGUAGE SQL AS 'SELECT 2;'; > CREATE FUNCTION s.func3() RETURNS int LANGUAGE SQL AS 'SELECT 3;'; > CREATE FUNCTION s.func4() RETURNS int LANGUAGE SQL AS 'SELECT 4;'; > CREATE FUNCTION s.func5() RETURNS int LANGUAGE SQL AS 'SELECT 5;'; > " | psql >psql2.log 2>&1 & > wait > psql -c "DROP SCHEMA s CASCADE" >psql3.log > done > echo " > SELECT pg_identify_object('pg_proc'::regclass, pp.oid, 0), pp.oid FROM pg_proc pp > LEFT JOIN pg_namespace pn ON pp.pronamespace = pn.oid WHERE pn.oid IS NULL" | psql > > still ends with: > server closed the connection unexpectedly > This probably means the server terminated abnormally > before or while processing the request. > > 2024-04-22 09:54:39.171 UTC|||662633dc.152bbc|LOG: server process (PID > 1388378) was terminated by signal 11: Segmentation fault > 2024-04-22 09:54:39.171 UTC|||662633dc.152bbc|DETAIL: Failed process was > running: SELECT pg_identify_object('pg_proc'::regclass, pp.oid, 0), pp.oid > FROM pg_proc pp > LEFT JOIN pg_namespace pn ON pp.pronamespace = pn.oid WHERE pn.oid IS NULL > Thanks for sharing the script. That's weird, I just launched it several times with the patch applied and I'm not able to see the seg fault (while I can see it constently failing on the master branch). Are you 100% sure you tested it against a binary with the patch applied? Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com -
Re: Avoid orphaned objects dependencies, take 3
Alexander Lakhin <exclusion@gmail.com> — 2024-04-22T12:00:00Z
22.04.2024 13:52, Bertrand Drouvot wrote: > > That's weird, I just launched it several times with the patch applied and I'm not > able to see the seg fault (while I can see it constently failing on the master > branch). > > Are you 100% sure you tested it against a binary with the patch applied? > Yes, at least I can't see what I'm doing wrong. Please try my self-contained script attached. Best regards, Alexander
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2024-04-23T04:59:09Z
Hi, On Mon, Apr 22, 2024 at 03:00:00PM +0300, Alexander Lakhin wrote: > 22.04.2024 13:52, Bertrand Drouvot wrote: > > > > That's weird, I just launched it several times with the patch applied and I'm not > > able to see the seg fault (while I can see it constently failing on the master > > branch). > > > > Are you 100% sure you tested it against a binary with the patch applied? > > > > Yes, at least I can't see what I'm doing wrong. Please try my > self-contained script attached. Thanks for sharing your script! Yeah your script ensures the patch is applied before the repro is executed. I do confirm that I can also see the issue with the patch applied (but I had to launch multiple attempts, while on master one attempt is enough). I'll have a look. Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2024-04-23T16:20:46Z
Hi, On Tue, Apr 23, 2024 at 04:59:09AM +0000, Bertrand Drouvot wrote: > Hi, > > On Mon, Apr 22, 2024 at 03:00:00PM +0300, Alexander Lakhin wrote: > > 22.04.2024 13:52, Bertrand Drouvot wrote: > > > > > > That's weird, I just launched it several times with the patch applied and I'm not > > > able to see the seg fault (while I can see it constently failing on the master > > > branch). > > > > > > Are you 100% sure you tested it against a binary with the patch applied? > > > > > > > Yes, at least I can't see what I'm doing wrong. Please try my > > self-contained script attached. > > Thanks for sharing your script! > > Yeah your script ensures the patch is applied before the repro is executed. > > I do confirm that I can also see the issue with the patch applied (but I had to > launch multiple attempts, while on master one attempt is enough). > > I'll have a look. Please find attached v2 that should not produce the issue anymore (I launched a lot of attempts without any issues). v1 was not strong enough as it was not always checking for the dependent object existence. v2 now always checks if the object still exists after the additional lock acquisition attempt while recording the dependency. I still need to think about v2 but in the meantime could you please also give v2 a try on you side? Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2024-04-24T08:38:01Z
Hi, On Tue, Apr 23, 2024 at 04:20:46PM +0000, Bertrand Drouvot wrote: > Please find attached v2 that should not produce the issue anymore (I launched a > lot of attempts without any issues). v1 was not strong enough as it was not > always checking for the dependent object existence. v2 now always checks if the > object still exists after the additional lock acquisition attempt while recording > the dependency. > > I still need to think about v2 but in the meantime could you please also give > v2 a try on you side? I gave more thought to v2 and the approach seems reasonable to me. Basically what it does is that in case the object is already dropped before we take the new lock (while recording the dependency) then the error message is a "generic" one (means it does not provide the description of the "already" dropped object). I think it makes sense to write the patch that way by abandoning the patch's ambition to tell the description of the dropped object in all the cases. Of course, I would be happy to hear others thought about it. Please find v3 attached (which is v2 with more comments). Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Alexander Lakhin <exclusion@gmail.com> — 2024-04-24T12:00:00Z
Hi Bertrand, 24.04.2024 11:38, Bertrand Drouvot wrote: >> Please find attached v2 that should not produce the issue anymore (I launched a >> lot of attempts without any issues). v1 was not strong enough as it was not >> always checking for the dependent object existence. v2 now always checks if the >> object still exists after the additional lock acquisition attempt while recording >> the dependency. >> >> I still need to think about v2 but in the meantime could you please also give >> v2 a try on you side? > I gave more thought to v2 and the approach seems reasonable to me. Basically what > it does is that in case the object is already dropped before we take the new lock > (while recording the dependency) then the error message is a "generic" one (means > it does not provide the description of the "already" dropped object). I think it > makes sense to write the patch that way by abandoning the patch's ambition to > tell the description of the dropped object in all the cases. > > Of course, I would be happy to hear others thought about it. > > Please find v3 attached (which is v2 with more comments). Thank you for the improved version! I can confirm that it fixes that case. I've also tested other cases that fail on master (most of them also fail with v1), please try/look at the attached script. (There could be other broken-dependency cases, of course, but I think I've covered all the representative ones.) All tested cases work correctly with v3 applied — I couldn't get broken dependencies, though concurrent create/drop operations can still produce the "cache lookup failed" error, which is probably okay, except that it is an INTERNAL_ERROR, which assumed to be not easily triggered by users. Best regards, Alexander
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2024-04-25T05:00:22Z
Hi, On Wed, Apr 24, 2024 at 03:00:00PM +0300, Alexander Lakhin wrote: > 24.04.2024 11:38, Bertrand Drouvot wrote: > > I gave more thought to v2 and the approach seems reasonable to me. Basically what > > it does is that in case the object is already dropped before we take the new lock > > (while recording the dependency) then the error message is a "generic" one (means > > it does not provide the description of the "already" dropped object). I think it > > makes sense to write the patch that way by abandoning the patch's ambition to > > tell the description of the dropped object in all the cases. > > > > Of course, I would be happy to hear others thought about it. > > > > Please find v3 attached (which is v2 with more comments). > > Thank you for the improved version! > > I can confirm that it fixes that case. Great, thanks for the test! > I've also tested other cases that fail on master (most of them also fail > with v1), please try/look at the attached script. Thanks for all those tests! > (There could be other broken-dependency cases, of course, but I think I've > covered all the representative ones.) Agree. Furthermore the way the patch is written should be agnostic to the object's kind that are part of the dependency. Having said that, that does not hurt to add more tests in this patch, so v4 attached adds some of your tests ( that would fail on the master branch without the patch applied). The way the tests are written in the patch are less "racy" that when triggered with your script. Indeed, I think that in the patch the dependent object can not be removed before the new lock is taken when recording the dependency. We may want to add injection points in the game if we feel the need. > All tested cases work correctly with v3 applied — Yeah, same on my side, I did run them too and did not find any issues. > I couldn't get broken > dependencies, Same here. > though concurrent create/drop operations can still produce > the "cache lookup failed" error, which is probably okay, except that it is > an INTERNAL_ERROR, which assumed to be not easily triggered by users. I did not see any of those "cache lookup failed" during my testing with/without your script. During which test(s) did you see those with v3 applied? Attached v4, simply adding more tests to v3. Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Alexander Lakhin <exclusion@gmail.com> — 2024-04-25T06:00:00Z
Hi, 25.04.2024 08:00, Bertrand Drouvot wrote: > >> though concurrent create/drop operations can still produce >> the "cache lookup failed" error, which is probably okay, except that it is >> an INTERNAL_ERROR, which assumed to be not easily triggered by users. > I did not see any of those "cache lookup failed" during my testing with/without > your script. During which test(s) did you see those with v3 applied? You can try, for example, table-trigger, or other tests that check for "cache lookup failed" psql output only (maybe you'll need to increase the iteration count). For instance, I've got (with v4 applied): 2024-04-25 05:48:08.102 UTC [3638763] ERROR: cache lookup failed for function 20893 2024-04-25 05:48:08.102 UTC [3638763] STATEMENT: CREATE TRIGGER modified_c1 BEFORE UPDATE OF c ON t FOR EACH ROW WHEN (OLD.c <> NEW.c) EXECUTE PROCEDURE trigger_func('modified_c'); Or with function-function: 2024-04-25 05:52:31.390 UTC [3711897] ERROR: cache lookup failed for function 32190 at character 54 2024-04-25 05:52:31.390 UTC [3711897] STATEMENT: CREATE FUNCTION f1() RETURNS int LANGUAGE SQL RETURN f() + 1; -- 2024-04-25 05:52:37.639 UTC [3720011] ERROR: cache lookup failed for function 34465 at character 54 2024-04-25 05:52:37.639 UTC [3720011] STATEMENT: CREATE FUNCTION f1() RETURNS int LANGUAGE SQL RETURN f() + 1; > Attached v4, simply adding more tests to v3. Thank you for working on this! Best regards, Alexander -
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2024-04-25T07:20:05Z
Hi, On Thu, Apr 25, 2024 at 09:00:00AM +0300, Alexander Lakhin wrote: > Hi, > > 25.04.2024 08:00, Bertrand Drouvot wrote: > > > > > though concurrent create/drop operations can still produce > > > the "cache lookup failed" error, which is probably okay, except that it is > > > an INTERNAL_ERROR, which assumed to be not easily triggered by users. > > I did not see any of those "cache lookup failed" during my testing with/without > > your script. During which test(s) did you see those with v3 applied? > > You can try, for example, table-trigger, or other tests that check for > "cache lookup failed" psql output only (maybe you'll need to increase the > iteration count). For instance, I've got (with v4 applied): > 2024-04-25 05:48:08.102 UTC [3638763] ERROR: cache lookup failed for function 20893 > 2024-04-25 05:48:08.102 UTC [3638763] STATEMENT: CREATE TRIGGER modified_c1 BEFORE UPDATE OF c ON t > FOR EACH ROW WHEN (OLD.c <> NEW.c) EXECUTE PROCEDURE trigger_func('modified_c'); > > Or with function-function: > 2024-04-25 05:52:31.390 UTC [3711897] ERROR: cache lookup failed for function 32190 at character 54 > 2024-04-25 05:52:31.390 UTC [3711897] STATEMENT: CREATE FUNCTION f1() RETURNS int LANGUAGE SQL RETURN f() + 1; > -- > 2024-04-25 05:52:37.639 UTC [3720011] ERROR: cache lookup failed for function 34465 at character 54 > 2024-04-25 05:52:37.639 UTC [3720011] STATEMENT: CREATE FUNCTION f1() RETURNS int LANGUAGE SQL RETURN f() + 1; I see, so this is during object creation. It's easy to reproduce this kind of issue with gdb. For example set a breakpoint on SearchSysCache1() and during the create function f1() once it breaks on: #0 SearchSysCache1 (cacheId=45, key1=16400) at syscache.c:221 #1 0x00005ad305beacd6 in func_get_detail (funcname=0x5ad308204d50, fargs=0x0, fargnames=0x0, nargs=0, argtypes=0x7ffff2ff9cc0, expand_variadic=true, expand_defaults=true, include_out_arguments=false, funcid=0x7ffff2ff9ba0, rettype=0x7ffff2ff9b9c, retset=0x7ffff2ff9b94, nvargs=0x7ffff2ff9ba4, vatype=0x7ffff2ff9ba8, true_typeids=0x7ffff2ff9bd8, argdefaults=0x7ffff2ff9be0) at parse_func.c:1622 #2 0x00005ad305be7dd0 in ParseFuncOrColumn (pstate=0x5ad30823be98, funcname=0x5ad308204d50, fargs=0x0, last_srf=0x0, fn=0x5ad308204da0, proc_call=false, location=55) at parse_func.c:266 #3 0x00005ad305bdffb0 in transformFuncCall (pstate=0x5ad30823be98, fn=0x5ad308204da0) at parse_expr.c:1474 #4 0x00005ad305bdd2ee in transformExprRecurse (pstate=0x5ad30823be98, expr=0x5ad308204da0) at parse_expr.c:230 #5 0x00005ad305bdec34 in transformAExprOp (pstate=0x5ad30823be98, a=0x5ad308204e20) at parse_expr.c:990 #6 0x00005ad305bdd1a0 in transformExprRecurse (pstate=0x5ad30823be98, expr=0x5ad308204e20) at parse_expr.c:187 #7 0x00005ad305bdd00b in transformExpr (pstate=0x5ad30823be98, expr=0x5ad308204e20, exprKind=EXPR_KIND_SELECT_TARGET) at parse_expr.c:131 #8 0x00005ad305b96b7e in transformReturnStmt (pstate=0x5ad30823be98, stmt=0x5ad308204ee0) at analyze.c:2395 #9 0x00005ad305b92213 in transformStmt (pstate=0x5ad30823be98, parseTree=0x5ad308204ee0) at analyze.c:375 #10 0x00005ad305c6321a in interpret_AS_clause (languageOid=14, languageName=0x5ad308204c40 "sql", funcname=0x5ad308204ad8 "f100", as=0x0, sql_body_in=0x5ad308204ee0, parameterTypes=0x0, inParameterNames=0x0, prosrc_str_p=0x7ffff2ffa208, probin_str_p=0x7ffff2ffa200, sql_body_out=0x7ffff2ffa210, queryString=0x5ad3082040b0 "CREATE FUNCTION f100() RETURNS int LANGUAGE SQL RETURN f() + 1;") at functioncmds.c:953 #11 0x00005ad305c63c93 in CreateFunction (pstate=0x5ad308186310, stmt=0x5ad308204f00) at functioncmds.c:1221 then drop function f() in another session. Then the create function f1() would: postgres=# CREATE FUNCTION f() RETURNS int LANGUAGE SQL RETURN f() + 1; ERROR: cache lookup failed for function 16400 This stuff does appear before we get a chance to call the new depLockAndCheckObject() function. I think this is what Tom was referring to in [1]: " So the only real fix for this would be to make every object lookup in the entire system do the sort of dance that's done in RangeVarGetRelidExtended. " The fact that those kind of errors appear also somehow ensure that no orphaned dependencies can be created. The patch does ensure that no orphaned depencies can occur after those "initial" look up are done (should the dependent object be dropped). I'm tempted to not add extra complexity to avoid those kind of errors and keep the patch as it is. All of those servicing the same goal: no orphaned depencies are created. [1]: https://www.postgresql.org/message-id/2872252.1630851337%40sss.pgh.pa.us Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com -
Re: Avoid orphaned objects dependencies, take 3
Alexander Lakhin <exclusion@gmail.com> — 2024-04-30T17:00:00Z
Hi Bertrand, 25.04.2024 10:20, Bertrand Drouvot wrote: > postgres=# CREATE FUNCTION f() RETURNS int LANGUAGE SQL RETURN f() + 1; > ERROR: cache lookup failed for function 16400 > > This stuff does appear before we get a chance to call the new depLockAndCheckObject() > function. > > I think this is what Tom was referring to in [1]: > > " > So the only real fix for this would be to make every object lookup in the entire > system do the sort of dance that's done in RangeVarGetRelidExtended. > " > > The fact that those kind of errors appear also somehow ensure that no orphaned > dependencies can be created. I agree; the only thing that I'd change here, is the error code. But I've discovered yet another possibility to get a broken dependency. Please try this script: res=0 numclients=20 for ((i=1;i<=100;i++)); do for ((c=1;c<=numclients;c++)); do echo " CREATE SCHEMA s_$c; CREATE CONVERSION myconv_$c FOR 'LATIN1' TO 'UTF8' FROM iso8859_1_to_utf8; ALTER CONVERSION myconv_$c SET SCHEMA s_$c; " | psql >psql1-$c.log 2>&1 & echo "DROP SCHEMA s_$c RESTRICT;" | psql >psql2-$c.log 2>&1 & done wait pg_dump -f db.dump || { echo "on iteration $i"; res=1; break; } for ((c=1;c<=numclients;c++)); do echo "DROP SCHEMA s_$c CASCADE;" | psql >psql3-$c.log 2>&1 done done psql -c "SELECT * FROM pg_conversion WHERE connamespace NOT IN (SELECT oid FROM pg_namespace);" It fails for me (with the v4 patch applied) as follows: pg_dump: error: schema with OID 16392 does not exist on iteration 1 oid | conname | connamespace | conowner | conforencoding | contoencoding | conproc | condefault -------+----------+--------------+----------+----------------+---------------+-------------------+------------ 16396 | myconv_6 | 16392 | 10 | 8 | 6 | iso8859_1_to_utf8 | f Best regards, Alexander -
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2024-05-09T12:20:51Z
Hi, On Tue, Apr 30, 2024 at 08:00:00PM +0300, Alexander Lakhin wrote: > Hi Bertrand, > > But I've discovered yet another possibility to get a broken dependency. Thanks for the testing and the report! > Please try this script: > res=0 > numclients=20 > for ((i=1;i<=100;i++)); do > for ((c=1;c<=numclients;c++)); do > echo " > CREATE SCHEMA s_$c; > CREATE CONVERSION myconv_$c FOR 'LATIN1' TO 'UTF8' FROM iso8859_1_to_utf8; > ALTER CONVERSION myconv_$c SET SCHEMA s_$c; > " | psql >psql1-$c.log 2>&1 & > echo "DROP SCHEMA s_$c RESTRICT;" | psql >psql2-$c.log 2>&1 & > done > wait > pg_dump -f db.dump || { echo "on iteration $i"; res=1; break; } > for ((c=1;c<=numclients;c++)); do > echo "DROP SCHEMA s_$c CASCADE;" | psql >psql3-$c.log 2>&1 > done > done > psql -c "SELECT * FROM pg_conversion WHERE connamespace NOT IN (SELECT oid FROM pg_namespace);" > > It fails for me (with the v4 patch applied) as follows: > pg_dump: error: schema with OID 16392 does not exist > on iteration 1 > oid | conname | connamespace | conowner | conforencoding | contoencoding | conproc | condefault > -------+----------+--------------+----------+----------------+---------------+-------------------+------------ > 16396 | myconv_6 | 16392 | 10 | 8 | 6 | iso8859_1_to_utf8 | f > Thanks for sharing the test, I'm able to reproduce the issue with v4. Oh I see, your test updates an existing dependency. v4 took care about brand new dependencies creation (recordMultipleDependencies()) but forgot to take care about changing an existing dependency (which is done in another code path: changeDependencyFor()). Please find attached v5 that adds: - a call to the new depLockAndCheckObject() function in changeDependencyFor(). - a test when altering an existing dependency. With v5 applied, I don't see the issue anymore. Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com -
Re: Avoid orphaned objects dependencies, take 3
Alexander Lakhin <exclusion@gmail.com> — 2024-05-14T12:00:00Z
Hi Bertrand, 09.05.2024 15:20, Bertrand Drouvot wrote: > Oh I see, your test updates an existing dependency. v4 took care about brand new > dependencies creation (recordMultipleDependencies()) but forgot to take care > about changing an existing dependency (which is done in another code path: > changeDependencyFor()). > > Please find attached v5 that adds: > > - a call to the new depLockAndCheckObject() function in changeDependencyFor(). > - a test when altering an existing dependency. > > With v5 applied, I don't see the issue anymore. Me too. Thank you for the improved version! I will test the patch in the background, but for now I see no other issues with it. Best regards, Alexander
-
Re: Avoid orphaned objects dependencies, take 3
Michael Paquier <michael@paquier.xyz> — 2024-05-15T01:14:09Z
On Thu, May 09, 2024 at 12:20:51PM +0000, Bertrand Drouvot wrote: > Oh I see, your test updates an existing dependency. v4 took care about brand new > dependencies creation (recordMultipleDependencies()) but forgot to take care > about changing an existing dependency (which is done in another code path: > changeDependencyFor()). > > Please find attached v5 that adds: > > - a call to the new depLockAndCheckObject() function in changeDependencyFor(). > - a test when altering an existing dependency. > > With v5 applied, I don't see the issue anymore. + if (object_description) + ereport(ERROR, errmsg("%s does not exist", object_description)); + else + ereport(ERROR, errmsg("a dependent object does not ex This generates an internal error code. Is that intended? --- /dev/null +++ b/src/test/modules/test_dependencies_locks/specs/test_dependencies_locks.spec This introduces a module with only one single spec. I could get behind an extra module if splitting the tests into more specs makes sense or if there is a restriction on installcheck. However, for one spec file filed with a bunch of objects, and note that I'm OK to let this single spec grow more for this range of tests, it seems to me that this would be better under src/test/isolation/. + if (use_dirty_snapshot) + { + InitDirtySnapshot(DirtySnapshot); + snapshot = &DirtySnapshot; + } + else + snapshot = NULL; I'm wondering if Robert has a comment about that. It looks backwards in a world where we try to encourage MVCC snapshots for catalog scans (aka 568d4138c646), especially for the part related to dependency.c and ObjectAddresses. -- Michael -
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2024-05-15T08:31:43Z
Hi, On Wed, May 15, 2024 at 10:14:09AM +0900, Michael Paquier wrote: > On Thu, May 09, 2024 at 12:20:51PM +0000, Bertrand Drouvot wrote: > > Oh I see, your test updates an existing dependency. v4 took care about brand new > > dependencies creation (recordMultipleDependencies()) but forgot to take care > > about changing an existing dependency (which is done in another code path: > > changeDependencyFor()). > > > > Please find attached v5 that adds: > > > > - a call to the new depLockAndCheckObject() function in changeDependencyFor(). > > - a test when altering an existing dependency. > > > > With v5 applied, I don't see the issue anymore. > > + if (object_description) > + ereport(ERROR, errmsg("%s does not exist", object_description)); > + else > + ereport(ERROR, errmsg("a dependent object does not ex > > This generates an internal error code. Is that intended? Thanks for looking at it! Yes, it's like when say you want to create an object in a schema that does not exist (see get_namespace_oid()). > --- /dev/null > +++ b/src/test/modules/test_dependencies_locks/specs/test_dependencies_locks.spec > > This introduces a module with only one single spec. I could get > behind an extra module if splitting the tests into more specs makes > sense or if there is a restriction on installcheck. However, for > one spec file filed with a bunch of objects, and note that I'm OK to > let this single spec grow more for this range of tests, it seems to me > that this would be better under src/test/isolation/. Yeah, I was not sure about this one (the location is from take 2 mentioned up-thread). I'll look at moving the tests to src/test/isolation/. Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com -
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2024-05-15T08:33:05Z
Hi, On Tue, May 14, 2024 at 03:00:00PM +0300, Alexander Lakhin wrote: > Hi Bertrand, > > 09.05.2024 15:20, Bertrand Drouvot wrote: > > Oh I see, your test updates an existing dependency. v4 took care about brand new > > dependencies creation (recordMultipleDependencies()) but forgot to take care > > about changing an existing dependency (which is done in another code path: > > changeDependencyFor()). > > > > Please find attached v5 that adds: > > > > - a call to the new depLockAndCheckObject() function in changeDependencyFor(). > > - a test when altering an existing dependency. > > > > With v5 applied, I don't see the issue anymore. > > Me too. Thank you for the improved version! > I will test the patch in the background, but for now I see no other > issues with it. Thanks for confirming! Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2024-05-15T10:31:02Z
Hi, On Wed, May 15, 2024 at 08:31:43AM +0000, Bertrand Drouvot wrote: > Hi, > > On Wed, May 15, 2024 at 10:14:09AM +0900, Michael Paquier wrote: > > +++ b/src/test/modules/test_dependencies_locks/specs/test_dependencies_locks.spec > > > > This introduces a module with only one single spec. I could get > > behind an extra module if splitting the tests into more specs makes > > sense or if there is a restriction on installcheck. However, for > > one spec file filed with a bunch of objects, and note that I'm OK to > > let this single spec grow more for this range of tests, it seems to me > > that this would be better under src/test/isolation/. > > Yeah, I was not sure about this one (the location is from take 2 mentioned > up-thread). I'll look at moving the tests to src/test/isolation/. Please find attached v6 (only diff with v5 is moving the tests as suggested above). Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Alexander Lakhin <exclusion@gmail.com> — 2024-05-19T08:00:00Z
Hello Bertrand, 15.05.2024 11:31, Bertrand Drouvot wrote: > On Wed, May 15, 2024 at 10:14:09AM +0900, Michael Paquier wrote: > >> + if (object_description) >> + ereport(ERROR, errmsg("%s does not exist", object_description)); >> + else >> + ereport(ERROR, errmsg("a dependent object does not ex >> >> This generates an internal error code. Is that intended? > Yes, it's like when say you want to create an object in a schema that does not > exist (see get_namespace_oid()). AFAICS, get_namespace_oid() throws not ERRCODE_INTERNAL_ERROR, but ERRCODE_UNDEFINED_SCHEMA: # SELECT regtype('unknown_schema.type'); ERROR: schema "unknown_schema" does not exist LINE 1: SELECT regtype('unknown_schema.type'); ^ # \echo :LAST_ERROR_SQLSTATE 3F000 Probably, it's worth to avoid ERRCODE_INTERNAL_ERROR here in light of [1] and [2], as these errors are not that abnormal (not Assert-like). [1] https://www.postgresql.org/message-id/Zic_GNgos5sMxKoa%40paquier.xyz [2] https://commitfest.postgresql.org/48/4735/ Best regards, Alexander -
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2024-05-21T07:22:57Z
Hi, On Sun, May 19, 2024 at 11:00:00AM +0300, Alexander Lakhin wrote: > Hello Bertrand, > > Probably, it's worth to avoid ERRCODE_INTERNAL_ERROR here in light of [1] > and [2], as these errors are not that abnormal (not Assert-like). > > [1] https://www.postgresql.org/message-id/Zic_GNgos5sMxKoa%40paquier.xyz > [2] https://commitfest.postgresql.org/48/4735/ > Thanks for mentioning the above examples, I agree that it's worth to avoid ERRCODE_INTERNAL_ERROR here: please find attached v7 that makes use of a new ERRCODE: ERRCODE_DEPENDENT_OBJECTS_DOES_NOT_EXIST I thought about this name as it is close enough to the already existing "ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST" but I'm open to suggestion too. Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Robert Haas <robertmhaas@gmail.com> — 2024-05-21T12:53:06Z
On Wed, May 15, 2024 at 6:31 AM Bertrand Drouvot <bertranddrouvot.pg@gmail.com> wrote: > Please find attached v6 (only diff with v5 is moving the tests as suggested > above). I don't immediately know what to think about this patch. I've known about this issue for a long time, but I didn't think this was how we would fix it. I think the problem here is basically that we don't lock namespaces (schemas) when we're adding and removing things from the schema. So I assumed that if we ever did something about this, what we would do would be add a bunch of calls to lock schemas to the appropriate parts of the code. What you've done here instead is add locking at a much lower level - whenever we are adding a dependency on an object, we lock the object. The advantage of that approach is that we definitely won't miss anything. The disadvantage of that approach is that it means we have some very low-level code taking locks, which means it's not obvious which operations are taking what locks. Maybe it could even result in some redundancy, like the higher-level code taking a lock also (possibly in a different mode) and then this code taking another one. I haven't gone through the previous threads; it sounds like there's already been some discussion of this, but I'm just telling you how it strikes me on first look. -- Robert Haas EDB: http://www.enterprisedb.com
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2024-05-22T10:21:34Z
Hi, On Tue, May 21, 2024 at 08:53:06AM -0400, Robert Haas wrote: > On Wed, May 15, 2024 at 6:31 AM Bertrand Drouvot > <bertranddrouvot.pg@gmail.com> wrote: > > Please find attached v6 (only diff with v5 is moving the tests as suggested > > above). > > I don't immediately know what to think about this patch. Thanks for looking at it! > I've known about this issue for a long time, but I didn't think this was how we > would fix it. I started initially with [1] but it was focusing on function-schema only. Then I proposed [2] making use of a dirty snapshot when recording the dependency. But this approach appeared to be "scary" and it was still failing to close some race conditions. Then, Tom proposed another approach in [2] which is that "creation DDL will have to take a lock on each referenced object that'd conflict with a lock taken by DROP". This is the one the current patch is trying to implement. > What you've done here instead is add locking at a much > lower level - whenever we are adding a dependency on an object, we > lock the object. The advantage of that approach is that we definitely > won't miss anything. Right, as there is much more than the ones related to schemas, for example: - function and arg type - function and return type - function and function - domain and domain - table and type - server and foreign data wrapper to name a few. > The disadvantage of that approach is that it > means we have some very low-level code taking locks, which means it's > not obvious which operations are taking what locks. Right, but the new operations are "only" the ones leading to recording or altering a dependency. > Maybe it could > even result in some redundancy, like the higher-level code taking a > lock also (possibly in a different mode) and then this code taking > another one. The one that is added here is in AccessShareLock mode. It could conflict with the ones in AccessExclusiveLock means (If I'm not missing any): - AcquireDeletionLock(): which is exactly what we want - get_object_address() - get_object_address_rv() - ExecAlterObjectDependsStmt() - ExecRenameStmt() - ExecAlterObjectDependsStmt() - ExecAlterOwnerStmt() - RemoveObjects() - AlterPublication() I think there is 2 cases here: First case: the "conflicting" lock mode is for one of our own lock then LockCheckConflicts() would report this as a NON conflict. Second case: the "conflicting" lock mode is NOT for one of our own lock then LockCheckConflicts() would report a conflict. But I've the feeling that the existing code would already lock those sessions. One example where it would be the case: Session 1: doing "BEGIN; ALTER FUNCTION noschemas.foo2() SET SCHEMA alterschema" would acquire the lock in AccessExclusiveLock during ExecAlterObjectSchemaStmt()->get_object_address()->LockDatabaseObject() (in the existing code and before the new call that would occur through changeDependencyFor()->depLockAndCheckObject() with the patch in place). Then, session 2: doing "alter function noschemas.foo2() owner to newrole;" would be locked in the existing code while doing ExecAlterOwnerStmt()->get_object_address()->LockDatabaseObject()). Means that in this example, the new lock this patch is introducing would not be responsible of session 2 beging locked. Was your concern about "First case" or "Second case" or both? [1]: https://www.postgresql.org/message-id/flat/5a9daaae-5538-b209-6279-e903c3ea2157%40amazon.com [2]: https://www.postgresql.org/message-id/flat/8369ff70-0e31-f194-2954-787f4d9e21dd%40amazon.com Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com -
Re: Avoid orphaned objects dependencies, take 3
Robert Haas <robertmhaas@gmail.com> — 2024-05-22T14:48:12Z
On Wed, May 22, 2024 at 6:21 AM Bertrand Drouvot <bertranddrouvot.pg@gmail.com> wrote: > I started initially with [1] but it was focusing on function-schema only. Yeah, that's what I thought we would want to do. And then just extend that to the other cases. > Then I proposed [2] making use of a dirty snapshot when recording the dependency. > But this approach appeared to be "scary" and it was still failing to close > some race conditions. The current patch still seems to be using dirty snapshots for some reason, which struck me as a bit odd. My intuition is that if we're relying on dirty snapshots to solve problems, we likely haven't solved the problems correctly, which seems consistent with your statement about "failing to close some race conditions". But I don't think I understand the situation well enough to be sure just yet. > Then, Tom proposed another approach in [2] which is that "creation DDL will have > to take a lock on each referenced object that'd conflict with a lock taken by DROP". > This is the one the current patch is trying to implement. It's a clever idea, but I'm not sure that I like it. > I think there is 2 cases here: > > First case: the "conflicting" lock mode is for one of our own lock then LockCheckConflicts() > would report this as a NON conflict. > > Second case: the "conflicting" lock mode is NOT for one of our own lock then LockCheckConflicts() > would report a conflict. But I've the feeling that the existing code would > already lock those sessions. > > Was your concern about "First case" or "Second case" or both? The second case, I guess. It's bad to take a weaker lock first and then a stronger lock on the same object later, because it can lead to deadlocks that would have been avoided if the stronger lock had been taken at the outset. Here, it seems like things would happen in the other order: if we took two locks, we'd probably take the stronger lock in the higher-level code and then the weaker lock in the dependency code. That shouldn't break anything; it's just a bit inefficient. My concern was really more about the maintainability of the code. I fear that if we add code that takes heavyweight locks in surprising places, we might later find the behavior difficult to reason about. Tom, what is your thought about that concern? -- Robert Haas EDB: http://www.enterprisedb.com
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2024-05-23T04:19:36Z
Hi, On Wed, May 22, 2024 at 10:48:12AM -0400, Robert Haas wrote: > On Wed, May 22, 2024 at 6:21 AM Bertrand Drouvot > <bertranddrouvot.pg@gmail.com> wrote: > > I started initially with [1] but it was focusing on function-schema only. > > Yeah, that's what I thought we would want to do. And then just extend > that to the other cases. > > > Then I proposed [2] making use of a dirty snapshot when recording the dependency. > > But this approach appeared to be "scary" and it was still failing to close > > some race conditions. > > The current patch still seems to be using dirty snapshots for some > reason, which struck me as a bit odd. My intuition is that if we're > relying on dirty snapshots to solve problems, we likely haven't solved > the problems correctly, which seems consistent with your statement > about "failing to close some race conditions". But I don't think I > understand the situation well enough to be sure just yet. The reason why we are using a dirty snapshot here is for the cases where we are recording a dependency on a referenced object that we are creating at the same time behind the scene (for example, creating a composite type while creating a relation). Without the dirty snapshot, then the object we are creating behind the scene (the composite type) would not be visible and we would wrongly assume that it has been dropped. Note that the usage of the dirty snapshot is only when the object is first reported as "non existing" by the new ObjectByIdExist() function. > > I think there is 2 cases here: > > > > First case: the "conflicting" lock mode is for one of our own lock then LockCheckConflicts() > > would report this as a NON conflict. > > > > Second case: the "conflicting" lock mode is NOT for one of our own lock then LockCheckConflicts() > > would report a conflict. But I've the feeling that the existing code would > > already lock those sessions. > > > > Was your concern about "First case" or "Second case" or both? > > The second case, I guess. It's bad to take a weaker lock first and > then a stronger lock on the same object later, because it can lead to > deadlocks that would have been avoided if the stronger lock had been > taken at the outset. In the example I shared up-thread that would be the opposite: the Session 1 would take an AccessExclusiveLock lock on the object before taking an AccessShareLock during changeDependencyFor(). > Here, it seems like things would happen in the > other order: if we took two locks, we'd probably take the stronger > lock in the higher-level code and then the weaker lock in the > dependency code. Yeah, I agree. > That shouldn't break anything; it's just a bit > inefficient. Yeah, the second lock is useless in that case (like in the example up-thread). > My concern was really more about the maintainability of > the code. I fear that if we add code that takes heavyweight locks in > surprising places, we might later find the behavior difficult to > reason about. > I think I understand your concern about code maintainability but I'm not sure that adding locks while recording a dependency is that surprising. > Tom, what is your thought about that concern? +1 Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Robert Haas <robertmhaas@gmail.com> — 2024-05-23T18:10:54Z
On Thu, May 23, 2024 at 12:19 AM Bertrand Drouvot <bertranddrouvot.pg@gmail.com> wrote: > The reason why we are using a dirty snapshot here is for the cases where we are > recording a dependency on a referenced object that we are creating at the same > time behind the scene (for example, creating a composite type while creating > a relation). Without the dirty snapshot, then the object we are creating behind > the scene (the composite type) would not be visible and we would wrongly assume > that it has been dropped. The usual reason for using a dirty snapshot is that you want to see uncommitted work by other transactions. It sounds like you're saying you just need to see uncommitted work by the same transaction. If that's true, I think using HeapTupleSatisfiesSelf would be clearer. Or maybe we just need to put CommandCounterIncrement() calls in the right places to avoid having the problem in the first place. Or maybe this is another sign that we're doing the work at the wrong level. -- Robert Haas EDB: http://www.enterprisedb.com
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2024-05-23T21:12:28Z
Hi, On Thu, May 23, 2024 at 02:10:54PM -0400, Robert Haas wrote: > On Thu, May 23, 2024 at 12:19 AM Bertrand Drouvot > <bertranddrouvot.pg@gmail.com> wrote: > > The reason why we are using a dirty snapshot here is for the cases where we are > > recording a dependency on a referenced object that we are creating at the same > > time behind the scene (for example, creating a composite type while creating > > a relation). Without the dirty snapshot, then the object we are creating behind > > the scene (the composite type) would not be visible and we would wrongly assume > > that it has been dropped. > > The usual reason for using a dirty snapshot is that you want to see > uncommitted work by other transactions. It sounds like you're saying > you just need to see uncommitted work by the same transaction. Right. > If that's true, I think using HeapTupleSatisfiesSelf would be clearer. Oh thanks! I did not know about the SNAPSHOT_SELF snapshot type (I should have check all the snapshot types first though) and that's exactly what is needed here. Please find attached v8 making use of SnapshotSelf instead of a dirty snapshot. Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2024-06-06T05:56:14Z
Hi, On Thu, May 23, 2024 at 02:10:54PM -0400, Robert Haas wrote: > On Thu, May 23, 2024 at 12:19 AM Bertrand Drouvot > <bertranddrouvot.pg@gmail.com> wrote: > > The reason why we are using a dirty snapshot here is for the cases where we are > > recording a dependency on a referenced object that we are creating at the same > > time behind the scene (for example, creating a composite type while creating > > a relation). Without the dirty snapshot, then the object we are creating behind > > the scene (the composite type) would not be visible and we would wrongly assume > > that it has been dropped. > > The usual reason for using a dirty snapshot is that you want to see > uncommitted work by other transactions. It sounds like you're saying > you just need to see uncommitted work by the same transaction. If > that's true, I think using HeapTupleSatisfiesSelf would be clearer. Or > maybe we just need to put CommandCounterIncrement() calls in the right > places to avoid having the problem in the first place. Or maybe this > is another sign that we're doing the work at the wrong level. Thanks for having discussed your concern with Tom last week during pgconf.dev and shared the outcome to me. I understand your concern regarding code maintainability with the current approach. Please find attached v9 that: - Acquire the lock and check for object existence at an upper level, means before calling recordDependencyOn() and recordMultipleDependencies(). - Get rid of the SNAPSHOT_SELF snapshot usage and relies on CommandCounterIncrement() instead to ensure new entries are visible when we check for object existence (for the cases where we create additional object behind the scene: like composite type while creating a relation). - Add an assertion in recordMultipleDependencies() to ensure that we locked the object before recording the dependency (to ensure we don't miss any cases now that the lock is acquired at an upper level). A few remarks: My first attempt has been to move eliminate_duplicate_dependencies() out of record_object_address_dependencies() so that we get the calls in this order: eliminate_duplicate_dependencies() depLockAndCheckObjects() record_object_address_dependencies() What I'm doing instead in v9 is to rename record_object_address_dependencies() to lock_record_object_address_dependencies() and add depLockAndCheckObjects() in it at the right place. That way the caller of [lock_]record_object_address_dependencies() is not responsible of calling eliminate_duplicate_dependencies() (which would have been the case with my first attempt). We need to setup the LOCKTAG before calling the new Assert in recordMultipleDependencies(). So, using "#ifdef USE_ASSERT_CHECKING" here to not setup the LOCKTAG on a non Assert enabled build. v9 is more invasive (as it changes code in much more places) than v8 but it is easier to follow (as it is now clear where the new lock is acquired). Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Robert Haas <robertmhaas@gmail.com> — 2024-06-06T20:00:23Z
On Thu, Jun 6, 2024 at 1:56 AM Bertrand Drouvot <bertranddrouvot.pg@gmail.com> wrote: > v9 is more invasive (as it changes code in much more places) than v8 but it is > easier to follow (as it is now clear where the new lock is acquired). Hmm, this definitely isn't what I had in mind. Possibly that's a sign that what I had in mind was dumb, but for sure it's not what I imagined. What I thought you were going to do was add calls like LockDatabaseObject(NamespaceRelationId, schemaid, 0, AccessShareLock) in various places, or perhaps LockRelationOid(reloid, AccessShareLock), or whatever the case may be. Here you've got stuff like this: - record_object_address_dependencies(&conobject, addrs_auto, - DEPENDENCY_AUTO); + lock_record_object_address_dependencies(&conobject, addrs_auto, + DEPENDENCY_AUTO); ...which to me looks like the locking is still pushed down inside the dependency code. And you also have stuff like this: ObjectAddressSet(referenced, RelationRelationId, childTableId); + depLockAndCheckObject(&referenced); recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_SEC); But in depLockAndCheckObject you have: + if (object->classId == RelationRelationId || object->classId == AuthMemRelationId) + return; That doesn't seem right, because then it seems like the call isn't doing anything, but there isn't really any reason for it to not be doing anything. If we're dropping a dependency on a table, then it seems like we need to have a lock on that table. Presumably the reason why we don't end up with dangling dependencies in such cases now is because we're careful about doing LockRelation() in the right places, but we're not similarly careful about other operations e.g. ConstraintSetParentConstraint is called by DefineIndex which calls table_open(childRelId, ...) first, but there's no logic in DefineIndex to lock the constraint. Thoughts? -- Robert Haas EDB: http://www.enterprisedb.com
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2024-06-07T08:41:36Z
Hi, On Thu, Jun 06, 2024 at 04:00:23PM -0400, Robert Haas wrote: > On Thu, Jun 6, 2024 at 1:56 AM Bertrand Drouvot > <bertranddrouvot.pg@gmail.com> wrote: > > v9 is more invasive (as it changes code in much more places) than v8 but it is > > easier to follow (as it is now clear where the new lock is acquired). > > Hmm, this definitely isn't what I had in mind. Possibly that's a sign > that what I had in mind was dumb, but for sure it's not what I > imagined. What I thought you were going to do was add calls like > LockDatabaseObject(NamespaceRelationId, schemaid, 0, AccessShareLock) > in various places, or perhaps LockRelationOid(reloid, > AccessShareLock), or whatever the case may be. I see what you’re saying, doing things like: LockDatabaseObject(TypeRelationId, returnType, 0, AccessShareLock); in ProcedureCreate() for example. > Here you've got stuff > like this: > > - record_object_address_dependencies(&conobject, addrs_auto, > - DEPENDENCY_AUTO); > + lock_record_object_address_dependencies(&conobject, addrs_auto, > + DEPENDENCY_AUTO); > > ...which to me looks like the locking is still pushed down inside the > dependency code. Yes but it’s now located in places where, I think, it’s easier to understand what’s going on (as compare to v8), except maybe for: recordDependencyOnExpr() makeOperatorDependencies() GenerateTypeDependencies() makeParserDependencies() makeDictionaryDependencies() makeTSTemplateDependencies() makeConfigurationDependencies() but probably for: heap_create_with_catalog() StorePartitionKey() index_create() AggregateCreate() CastCreate() CreateConstraintEntry() ProcedureCreate() RangeCreate() InsertExtensionTuple() CreateTransform() CreateProceduralLanguage() The reasons I keep it linked to the dependency code are: - To ensure we don’t miss anything (well, with the new Assert in place that’s probably a tangential argument) - It’s not only about locking the object: it’s also about 1) verifying the object is pinned, 2) checking it still exists and 3) provide a description in the error message if we can (in case the object does not exist anymore). Relying on an already build object (in the dependency code) avoid to 1) define the object(s) one more time or 2) create new functions that would do the same as isObjectPinned() and getObjectDescription() with a different set of arguments. That may sounds like weak arguments but it has been my reasoning. Do you still find the code hard to maintain with v9? > > And you also have stuff like this: > > ObjectAddressSet(referenced, RelationRelationId, childTableId); > + depLockAndCheckObject(&referenced); > recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_SEC); > > But in depLockAndCheckObject you have: > > + if (object->classId == RelationRelationId || object->classId == > AuthMemRelationId) > + return; > > That doesn't seem right, because then it seems like the call isn't > doing anything, but there isn't really any reason for it to not be > doing anything. If we're dropping a dependency on a table, then it > seems like we need to have a lock on that table. Presumably the reason > why we don't end up with dangling dependencies in such cases now is > because we're careful about doing LockRelation() in the right places, Yeah, that's what I think: we're already careful when we deal with relations. > but we're not similarly careful about other operations e.g. > ConstraintSetParentConstraint is called by DefineIndex which calls > table_open(childRelId, ...) first, but there's no logic in DefineIndex > to lock the constraint. table_open(childRelId, ...) would lock any "ALTER TABLE <childRelId> DROP CONSTRAINT" already. Not sure I understand your concern here. Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Robert Haas <robertmhaas@gmail.com> — 2024-06-13T14:49:34Z
On Fri, Jun 7, 2024 at 4:41 AM Bertrand Drouvot <bertranddrouvot.pg@gmail.com> wrote: > Do you still find the code hard to maintain with v9? I don't think it substantially changes my concerns as compared with the earlier version. > > but we're not similarly careful about other operations e.g. > > ConstraintSetParentConstraint is called by DefineIndex which calls > > table_open(childRelId, ...) first, but there's no logic in DefineIndex > > to lock the constraint. > > table_open(childRelId, ...) would lock any "ALTER TABLE <childRelId> DROP CONSTRAINT" > already. Not sure I understand your concern here. I believe this is not true. This would take a lock on the table, not the constraint itself. -- Robert Haas EDB: http://www.enterprisedb.com
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2024-06-13T16:52:09Z
Hi, On Thu, Jun 13, 2024 at 10:49:34AM -0400, Robert Haas wrote: > On Fri, Jun 7, 2024 at 4:41 AM Bertrand Drouvot > <bertranddrouvot.pg@gmail.com> wrote: > > Do you still find the code hard to maintain with v9? > > I don't think it substantially changes my concerns as compared with > the earlier version. Thanks for the feedback, I'll give it more thoughts. > > > > but we're not similarly careful about other operations e.g. > > > ConstraintSetParentConstraint is called by DefineIndex which calls > > > table_open(childRelId, ...) first, but there's no logic in DefineIndex > > > to lock the constraint. > > > > table_open(childRelId, ...) would lock any "ALTER TABLE <childRelId> DROP CONSTRAINT" > > already. Not sure I understand your concern here. > > I believe this is not true. This would take a lock on the table, not > the constraint itself. I agree that it would not lock the constraint itself. What I meant to say is that , nevertheless, the constraint can not be dropped. Indeed, the "ALTER TABLE" necessary to drop the constraint (ALTER TABLE <childRelId> DROP CONSTRAINT) would be locked by the table_open(childRelId, ...). That's why I don't understand your concern with this particular example. But anyway, I'll double check your related concern: + if (object->classId == RelationRelationId || object->classId == AuthMemRelationId) + return; in depLockAndCheckObject(). Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Robert Haas <robertmhaas@gmail.com> — 2024-06-13T18:27:45Z
On Thu, Jun 13, 2024 at 12:52 PM Bertrand Drouvot <bertranddrouvot.pg@gmail.com> wrote: > > > table_open(childRelId, ...) would lock any "ALTER TABLE <childRelId> DROP CONSTRAINT" > > > already. Not sure I understand your concern here. > > > > I believe this is not true. This would take a lock on the table, not > > the constraint itself. > > I agree that it would not lock the constraint itself. What I meant to say is that > , nevertheless, the constraint can not be dropped. Indeed, the "ALTER TABLE" > necessary to drop the constraint (ALTER TABLE <childRelId> DROP CONSTRAINT) would > be locked by the table_open(childRelId, ...). Ah, right. So, I was assuming that, with either this version of your patch or the earlier version, we'd end up locking the constraint itself. Was I wrong about that? -- Robert Haas EDB: http://www.enterprisedb.com
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2024-06-14T07:54:52Z
Hi, On Thu, Jun 13, 2024 at 02:27:45PM -0400, Robert Haas wrote: > On Thu, Jun 13, 2024 at 12:52 PM Bertrand Drouvot > <bertranddrouvot.pg@gmail.com> wrote: > > > > table_open(childRelId, ...) would lock any "ALTER TABLE <childRelId> DROP CONSTRAINT" > > > > already. Not sure I understand your concern here. > > > > > > I believe this is not true. This would take a lock on the table, not > > > the constraint itself. > > > > I agree that it would not lock the constraint itself. What I meant to say is that > > , nevertheless, the constraint can not be dropped. Indeed, the "ALTER TABLE" > > necessary to drop the constraint (ALTER TABLE <childRelId> DROP CONSTRAINT) would > > be locked by the table_open(childRelId, ...). > > Ah, right. So, I was assuming that, with either this version of your > patch or the earlier version, we'd end up locking the constraint > itself. Was I wrong about that? The child contraint itself is not locked when going through ConstraintSetParentConstraint(). While at it, let's look at a full example and focus on your concern. Let's do that with this gdb file: " $ cat gdb.txt b dependency.c:1542 command 1 printf "Will return for: classId %d and objectId %d\n", object->classId, object->objectId c end b dependency.c:1547 if object->classId == 2606 command 2 printf "Will lock constraint: classId %d and objectId %d\n", object->classId, object->objectId c end " knowing that: " Line 1542 is the return here in depLockAndCheckObject() (your concern): if (object->classId == RelationRelationId || object->classId == AuthMemRelationId) return; Line 1547 is the lock here in depLockAndCheckObject(): /* assume we should lock the whole object not a sub-object */ LockDatabaseObject(object->classId, object->objectId, 0, AccessShareLock); " So, with gdb attached to a session let's: 1. Create the parent relation CREATE TABLE upsert_test ( a INT, b TEXT ) PARTITION BY LIST (a); gdb produces: --- Will return for: classId 1259 and objectId 16384 Will return for: classId 1259 and objectId 16384 --- Oid 16384 is upsert_test, so I think the return (dependency.c:1542) is fine as we are creating the object (it can't be dropped as not visible to anyone else). 2. Create another relation (will be the child) CREATE TABLE upsert_test_2 (b TEXT, a int); gdb produces: --- Will return for: classId 1259 and objectId 16391 Will return for: classId 1259 and objectId 16394 Will return for: classId 1259 and objectId 16394 Will return for: classId 1259 and objectId 16391 --- Oid 16391 is upsert_test_2 Oid 16394 is pg_toast_16391 so I think the return (dependency.c:1542) is fine as we are creating those objects (can't be dropped as not visible to anyone else). 3. Attach the partition ALTER TABLE upsert_test ATTACH PARTITION upsert_test_2 FOR VALUES IN (2); gdb produces: --- Will return for: classId 1259 and objectId 16384 --- That's fine because we'd already had locked the relation 16384 through AlterTableLookupRelation()->RangeVarGetRelidExtended()->LockRelationOid(). 4. Add a constraint on the child relation ALTER TABLE upsert_test_2 add constraint bdtc2 UNIQUE (a); gdb produces: --- Will return for: classId 1259 and objectId 16391 Will lock constraint: classId 2606 and objectId 16397 --- That's fine because we'd already had locked the relation 16391 through AlterTableLookupRelation()->RangeVarGetRelidExtended()->LockRelationOid(). Oid 16397 is the constraint we're creating (bdtc2). 5. Add a constraint on the parent relation (this goes through ConstraintSetParentConstraint()) ALTER TABLE upsert_test add constraint bdtc1 UNIQUE (a); gdb produces: --- Will return for: classId 1259 and objectId 16384 Will lock constraint: classId 2606 and objectId 16399 Will return for: classId 1259 and objectId 16398 Will return for: classId 1259 and objectId 16391 Will lock constraint: classId 2606 and objectId 16399 Will return for: classId 1259 and objectId 16391 --- Regarding "Will return for: classId 1259 and objectId 16384": That's fine because we'd already had locked the relation 16384 through AlterTableLookupRelation()->RangeVarGetRelidExtended()->LockRelationOid(). Regarding "Will lock constraint: classId 2606 and objectId 16399": Oid 16399 is the constraint that we're creating. Regarding "Will return for: classId 1259 and objectId 16398": That's fine because Oid 16398 is an index that we're creating while creating the constraint (so the index can't be dropped as not visible to anyone else). Regarding "Will return for: classId 1259 and objectId 16391": That's fine because 16384 we'd be already locked (as mentioned above). And I think that's fine because trying to drop "upsert_test_2" (aka 16391) would produce RemoveRelations()->RangeVarGetRelidExtended()->RangeVarCallbackForDropRelation() ->LockRelationOid(relid=16384, lockmode=8) and so would be locked. Regarding this example, I don't think that the return in depLockAndCheckObject(): " if (object->classId == RelationRelationId || object->classId == AuthMemRelationId) return; " is an issue. Indeed, the example above shows it would return for an object that we'd be creating (so not visible to anyone else) or for an object that we'd already have locked. Is it an issue outside of this example?: I've the feeling it's not as we're already careful when we deal with relations. That said, to be on the safe side we could get rid of this return and make use of LockRelationOid() instead. Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com -
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2024-06-17T10:50:56Z
Hi, On Thu, Jun 13, 2024 at 04:52:09PM +0000, Bertrand Drouvot wrote: > Hi, > > On Thu, Jun 13, 2024 at 10:49:34AM -0400, Robert Haas wrote: > > On Fri, Jun 7, 2024 at 4:41 AM Bertrand Drouvot > > <bertranddrouvot.pg@gmail.com> wrote: > > > Do you still find the code hard to maintain with v9? > > > > I don't think it substantially changes my concerns as compared with > > the earlier version. > > Thanks for the feedback, I'll give it more thoughts. Please find attached v10 that puts the object locking outside of the dependency code. It's done that way except for: recordDependencyOnExpr() recordDependencyOnSingleRelExpr() makeConfigurationDependencies() The reason is that I think that it would need part of the logic that his inside the above functions to be duplicated and I'm not sure that's worth it. For example, we would probably need to: - make additional calls to find_expr_references_walker() - make additional scan on the config map It's also not done outside of recordDependencyOnCurrentExtension() as: 1. I think it is clear enough that way (as it is clear that the lock is taken on a ExtensionRelationId object class). 2. why to include "commands/extension.h" in more places (locking would depend of "creating_extension" and "CurrentExtensionObject"), while 1.? Remarks: 1. depLockAndCheckObject() and friends in v9 have been renamed to LockNotPinnedObject() and friends (as the vast majority of their calls are now done outside of the dependency code). 2. regarding the concern around RelationRelationId (discussed in [1]), v10 adds a comment "XXX Do we need a lock for RelationRelationId?" at the places we may want to lock this object class. I did not think about it yet (but will do), I only added this comment at multiple places. I think that v10 is easier to follow (as compare to v9) as we can easily see for which object class we'll put a lock on. Thoughts? [1]: https://www.postgresql.org/message-id/Zmv3TPfJAyQXhIdu%40ip-10-97-1-34.eu-west-3.compute.internal Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Robert Haas <robertmhaas@gmail.com> — 2024-06-17T16:24:46Z
On Fri, Jun 14, 2024 at 3:54 AM Bertrand Drouvot <bertranddrouvot.pg@gmail.com> wrote: > > Ah, right. So, I was assuming that, with either this version of your > > patch or the earlier version, we'd end up locking the constraint > > itself. Was I wrong about that? > > The child contraint itself is not locked when going through > ConstraintSetParentConstraint(). > > While at it, let's look at a full example and focus on your concern. I'm not at the point of having a concern yet, honestly. I'm trying to understand the design ideas. The commit message just says that we take a conflicting lock, but it doesn't mention which object types that principle does or doesn't apply to. I think the idea of skipping it for cases where it's redundant with the relation lock could be the right idea, but if that's what we're doing, don't we need to explain the principle somewhere? And shouldn't we also apply it across all object types that have the same property? Along the same lines: + /* + * Those don't rely on LockDatabaseObject() when being dropped (see + * AcquireDeletionLock()). Also it looks like they can not produce + * orphaned dependent objects when being dropped. + */ + if (object->classId == RelationRelationId || object->classId == AuthMemRelationId) + return; "It looks like X cannot happen" is not confidence-inspiring. At the very least, a better comment is needed here. But also, that relation has no exception for AuthMemRelationId, only for RelationRelationId. And also, the exception for RelationRelationId doesn't imply that we don't need a conflicting lock here: the special case for RelationRelationId in AcquireDeletionLock() is necessary because the lock tag format is different for relations than for other database objects, not because we don't need a lock at all. If the handling here were really symmetric with what AcquireDeletionLock(), the coding would be to call either LockRelationOid() or LockDatabaseObject() depending on whether classid == RelationRelationId. Now, that isn't actually necessary, because we already have relation-locking calls elsewhere, but my point is that the rationale this commit gives for WHY it isn't necessary seems to me to be wrong in multiple ways. So to try to sum up here: I'm not sure I agree with this design. But I also feel like the design is not as clear and consistently implemented as it could be. So even if I just ignored the question of whether it's the right design, it feels like we're a ways from having something potentially committable here, because of issues like the ones I mentioned in the last paragraph. -- Robert Haas EDB: http://www.enterprisedb.com
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2024-06-17T17:57:05Z
Hi, On Mon, Jun 17, 2024 at 12:24:46PM -0400, Robert Haas wrote: > On Fri, Jun 14, 2024 at 3:54 AM Bertrand Drouvot > <bertranddrouvot.pg@gmail.com> wrote: > > > Ah, right. So, I was assuming that, with either this version of your > > > patch or the earlier version, we'd end up locking the constraint > > > itself. Was I wrong about that? > > > > The child contraint itself is not locked when going through > > ConstraintSetParentConstraint(). > > > > While at it, let's look at a full example and focus on your concern. > > I'm not at the point of having a concern yet, honestly. I'm trying to > understand the design ideas. The commit message just says that we take > a conflicting lock, but it doesn't mention which object types that > principle does or doesn't apply to. I think the idea of skipping it > for cases where it's redundant with the relation lock could be the > right idea, but if that's what we're doing, don't we need to explain > the principle somewhere? And shouldn't we also apply it across all > object types that have the same property? Yeah, I still need to deeply study this area and document it. > Along the same lines: > > + /* > + * Those don't rely on LockDatabaseObject() when being dropped (see > + * AcquireDeletionLock()). Also it looks like they can not produce > + * orphaned dependent objects when being dropped. > + */ > + if (object->classId == RelationRelationId || object->classId == > AuthMemRelationId) > + return; > > "It looks like X cannot happen" is not confidence-inspiring. Yeah, it is not. It is just a "feeling" that I need to work on to remove any ambiguity and/or adjust the code as needed. > At the > very least, a better comment is needed here. But also, that relation > has no exception for AuthMemRelationId, only for RelationRelationId. > And also, the exception for RelationRelationId doesn't imply that we > don't need a conflicting lock here: the special case for > RelationRelationId in AcquireDeletionLock() is necessary because the > lock tag format is different for relations than for other database > objects, not because we don't need a lock at all. If the handling here > were really symmetric with what AcquireDeletionLock(), the coding > would be to call either LockRelationOid() or LockDatabaseObject() > depending on whether classid == RelationRelationId. Agree. > Now, that isn't > actually necessary, because we already have relation-locking calls > elsewhere, but my point is that the rationale this commit gives for > WHY it isn't necessary seems to me to be wrong in multiple ways. Agree. I'm not done with that part yet (should have made it more clear). > So to try to sum up here: I'm not sure I agree with this design. But I > also feel like the design is not as clear and consistently implemented > as it could be. So even if I just ignored the question of whether it's > the right design, it feels like we're a ways from having something > potentially committable here, because of issues like the ones I > mentioned in the last paragraph. > Agree. I'll now move on with the "XXX Do we need a lock for RelationRelationId?" comments that I put in v10 (see [1]) and study all the cases around them. Once done, I think that it will easier to 1.remove ambiguity, 2.document and 3.do the "right" thing regarding the RelationRelationId object class. [1]: https://www.postgresql.org/message-id/ZnAVEBhlGvpDDVOD%40ip-10-97-1-34.eu-west-3.compute.internal Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Ashutosh Sharma <ashu.coek88@gmail.com> — 2024-06-19T11:49:28Z
Hi, If the dependency is more, this can hit max_locks_per_transaction limit very fast. Won't it? I just tried this little experiment with and without patch. 1) created some UDTs (I have just chosen some random number, 15) do $$ declare i int := 1; type_name text; begin while i <= 15 loop type_name := format('ct_%s', i); -- check if the type already exists if not exists ( select 1 from pg_type where typname = type_name ) then execute format('create type %I as (f1 INT, f2 TEXT);', type_name); end if; i := i + 1; end loop; end $$; 2) started a transaction and tried creating a table that uses all udts created above: begin; create table dep_tab(a ct_1, b ct_2, c ct_3, d ct_4, e ct_5, f ct_6, g ct_7, h ct_8, i ct_9, j ct_10, k ct_11, l ct_12, m ct_13, n ct_14, o ct_15); 3) checked the pg_locks entries inside the transaction both with and without patch: -- with patch: select count(*) from pg_locks; count ------- 23 (1 row) -- without patch: select count(*) from pg_locks; count ------- 7 (1 row) With patch, it increased by 3 times. Won't that create a problem if many concurrent sessions engage in similar activity? -- With Regards, Ashutosh Sharma. -
Re: Avoid orphaned objects dependencies, take 3
Robert Haas <robertmhaas@gmail.com> — 2024-06-19T12:20:08Z
On Wed, Jun 19, 2024 at 7:49 AM Ashutosh Sharma <ashu.coek88@gmail.com> wrote: > If the dependency is more, this can hit max_locks_per_transaction > limit very fast. Your experiment doesn't support this conclusion. Very few users would have 15 separate user-defined types in the same table, and even if they did, and dropped the table, using 23 locks is no big deal. By default, max_locks_per_transaction is 64, so the user would need to have more like 45 separate user-defined types in the same table in order to use more than 64 locks. So, yes, it is possible that if every backend in the system were simultaneously trying to drop a table and all of those tables had an average of at least 45 or so user-defined types, all different from each other, you might run out of lock table space. But probably nobody will ever do that in real life, and if they did, they could just raise max_locks_per_transaction. When posting about potential problems like this, it is a good idea to first do a careful thought experiment to assess how realistic the problem is. I would consider an issue like this serious if there were a realistic scenario under which a small number of backends could exhaust the lock table for the whole system, but I think you can see that this isn't the case here. Even your original scenario is more extreme than what most people are likely to hit in real life, and it only uses 23 locks. -- Robert Haas EDB: http://www.enterprisedb.com
-
Re: Avoid orphaned objects dependencies, take 3
Ashutosh Sharma <ashu.coek88@gmail.com> — 2024-06-19T13:35:36Z
Hi Robert, On Wed, Jun 19, 2024 at 5:50 PM Robert Haas <robertmhaas@gmail.com> wrote: > > On Wed, Jun 19, 2024 at 7:49 AM Ashutosh Sharma <ashu.coek88@gmail.com> wrote: > > If the dependency is more, this can hit max_locks_per_transaction > > limit very fast. > > Your experiment doesn't support this conclusion. Very few users would > have 15 separate user-defined types in the same table, and even if > they did, and dropped the table, using 23 locks is no big deal. By > default, max_locks_per_transaction is 64, so the user would need to > have more like 45 separate user-defined types in the same table in > order to use more than 64 locks. So, yes, it is possible that if every > backend in the system were simultaneously trying to drop a table and > all of those tables had an average of at least 45 or so user-defined > types, all different from each other, you might run out of lock table > space. > > But probably nobody will ever do that in real life, and if they did, > they could just raise max_locks_per_transaction. > > When posting about potential problems like this, it is a good idea to > first do a careful thought experiment to assess how realistic the > problem is. I would consider an issue like this serious if there were > a realistic scenario under which a small number of backends could > exhaust the lock table for the whole system, but I think you can see > that this isn't the case here. Even your original scenario is more > extreme than what most people are likely to hit in real life, and it > only uses 23 locks. > I agree that based on the experiment I shared (which is somewhat unrealistic), this doesn't seem to have any significant implications. However, I was concerned that it could potentially increase the usage of max_locks_per_transaction, which is why I wanted to mention it here. Nonetheless, my experiment did not reveal any serious issues related to this. Sorry for the noise. -- With Regards, Ashutosh Sharma.
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2024-06-19T14:11:50Z
Hi, On Mon, Jun 17, 2024 at 05:57:05PM +0000, Bertrand Drouvot wrote: > Hi, > > On Mon, Jun 17, 2024 at 12:24:46PM -0400, Robert Haas wrote: > > So to try to sum up here: I'm not sure I agree with this design. But I > > also feel like the design is not as clear and consistently implemented > > as it could be. So even if I just ignored the question of whether it's > > the right design, it feels like we're a ways from having something > > potentially committable here, because of issues like the ones I > > mentioned in the last paragraph. > > > > Agree. I'll now move on with the "XXX Do we need a lock for RelationRelationId?" > comments that I put in v10 (see [1]) and study all the cases around them. A. I went through all of them, did some testing for all, and reached the conclusion that we must be in one of the two following cases that would already prevent the relation to be dropped: 1. The relation is already locked (could be an existing relation or a relation that we are creating). 2. The relation is protected indirectly (i.e an index protected by a lock on its table, a table protected by a lock on a function that depends of the table...). So we don't need to add a lock if this is a RelationRelationId object class for the cases above. As a consequence, I replaced the "XXX" related comments that were in v10 by another set of comments in v11 (attached) like "No need to call LockRelationOid() (through LockNotPinnedObject())....". Reason is to make it clear in the code and also to ease the review. B. I explained in [1] (while sharing v10) that the object locking is now outside of the dependency code except for (and I explained why): recordDependencyOnExpr() recordDependencyOnSingleRelExpr() makeConfigurationDependencies() So I also did some testing, on the RelationRelationId case, for those and I reached the same conclusion as the one shared above. For A. and B. the testing has been done by adding a "ereport(WARNING.." at those places when a RelationRelationId is involved. Then I run "make check" and went to the failed tests (output were not the expected ones due to the extra "WARNING"), reproduced them with gdb and checked for the lock on the relation producing the "WARNING". All of those were linked to 1. or 2. Note that adding an assertion on an existing lock would not work for the cases described in 2. So, I'm now confident that we must be in 1. or 2. but it's also possible that I've missed some cases (though I think the way the testing has been done is not that weak). To sum up, I did not see any cases that did not lead to 1. or 2., so I think it's safe to not add an extra lock for the RelationRelationId case. If, for any reason, there is still cases that are outside 1. or 2. then they may lead to orphaned dependencies linked to the RelationRelationId class. I think that's fine to take that "risk" given that a. that would not be worst than currently and b. I did not see any of those in our fleet currently (while I have seen a non negligible amount outside of the RelationRelationId case). > Once done, I think that it will easier to 1.remove ambiguity, 2.document and > 3.do the "right" thing regarding the RelationRelationId object class. > Please find attached v11, where I added more detailed comments in the commit message and also in the code (I also removed the useless check on AuthMemRelationId). [1]: https://www.postgresql.org/message-id/ZnAVEBhlGvpDDVOD%40ip-10-97-1-34.eu-west-3.compute.internal Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2024-06-21T13:22:43Z
Hi, On Wed, Jun 19, 2024 at 02:11:50PM +0000, Bertrand Drouvot wrote: > To sum up, I did not see any cases that did not lead to 1. or 2., so I think > it's safe to not add an extra lock for the RelationRelationId case. If, for any > reason, there is still cases that are outside 1. or 2. then they may lead to > orphaned dependencies linked to the RelationRelationId class. I think that's > fine to take that "risk" given that a. that would not be worst than currently > and b. I did not see any of those in our fleet currently (while I have seen a non > negligible amount outside of the RelationRelationId case). Another thought for the RelationRelationId class case: we could check if there is a lock first and if there is no lock then acquire one. That way that would ensure the relation is always locked (so no "risk" anymore), but OTOH it may add "unecessary" locking (see 2. mentioned previously). I think I do prefer this approach to be on the safe side of thing, what do you think? Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2024-06-26T10:24:41Z
Hi, On Fri, Jun 21, 2024 at 01:22:43PM +0000, Bertrand Drouvot wrote: > Another thought for the RelationRelationId class case: we could check if there > is a lock first and if there is no lock then acquire one. That way that would > ensure the relation is always locked (so no "risk" anymore), but OTOH it may > add "unecessary" locking (see 2. mentioned previously). Please find attached v12 implementing this idea for the RelationRelationId class case. As mentioned, it may add unnecessary locking for 2. but I think that's worth it to ensure that we are always on the safe side of thing. This idea is implemented in LockNotPinnedObjectById(). A few remarks: - there is one place where the relation is not visible (even if CommandCounterIncrement() is used). That's in TypeCreate(), because the new relation Oid is _not_ added to pg_class yet. Indeed, in heap_create_with_catalog(), AddNewRelationType() is called before AddNewRelationTuple()). I put a comment in this part of the code explaining why it's not necessary to call LockRelationOid() here. - some namespace related stuff is removed from "test_oat_hooks/expected/alter_table.out". That's due to the logic in cachedNamespacePath() and the fact that the same namespace related stuff is added prior in alter_table.out. - the patch touches 37 .c files, but that's mainly due to the fact that LockNotPinnedObjectById() has to be called in a lot of places. Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2024-07-01T09:39:17Z
Hi, On Wed, Jun 26, 2024 at 10:24:41AM +0000, Bertrand Drouvot wrote: > Hi, > > On Fri, Jun 21, 2024 at 01:22:43PM +0000, Bertrand Drouvot wrote: > > Another thought for the RelationRelationId class case: we could check if there > > is a lock first and if there is no lock then acquire one. That way that would > > ensure the relation is always locked (so no "risk" anymore), but OTOH it may > > add "unecessary" locking (see 2. mentioned previously). > > Please find attached v12 implementing this idea for the RelationRelationId class > case. As mentioned, it may add unnecessary locking for 2. but I think that's > worth it to ensure that we are always on the safe side of thing. This idea is > implemented in LockNotPinnedObjectById(). Please find attached v13, mandatory rebase due to 0cecc908e97. In passing, make use of CheckRelationOidLockedByMe() added in 0cecc908e97. Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2024-07-02T05:56:23Z
Hi, On Mon, Jul 01, 2024 at 09:39:17AM +0000, Bertrand Drouvot wrote: > Hi, > > On Wed, Jun 26, 2024 at 10:24:41AM +0000, Bertrand Drouvot wrote: > > Hi, > > > > On Fri, Jun 21, 2024 at 01:22:43PM +0000, Bertrand Drouvot wrote: > > > Another thought for the RelationRelationId class case: we could check if there > > > is a lock first and if there is no lock then acquire one. That way that would > > > ensure the relation is always locked (so no "risk" anymore), but OTOH it may > > > add "unecessary" locking (see 2. mentioned previously). > > > > Please find attached v12 implementing this idea for the RelationRelationId class > > case. As mentioned, it may add unnecessary locking for 2. but I think that's > > worth it to ensure that we are always on the safe side of thing. This idea is > > implemented in LockNotPinnedObjectById(). > > Please find attached v13, mandatory rebase due to 0cecc908e97. In passing, make > use of CheckRelationOidLockedByMe() added in 0cecc908e97. Please find attached v14, mandatory rebase due to 65b71dec2d5. Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2024-07-10T07:31:06Z
Hi, On Tue, Jul 02, 2024 at 05:56:23AM +0000, Bertrand Drouvot wrote: > Hi, > > On Mon, Jul 01, 2024 at 09:39:17AM +0000, Bertrand Drouvot wrote: > > Hi, > > > > On Wed, Jun 26, 2024 at 10:24:41AM +0000, Bertrand Drouvot wrote: > > > Hi, > > > > > > On Fri, Jun 21, 2024 at 01:22:43PM +0000, Bertrand Drouvot wrote: > > > > Another thought for the RelationRelationId class case: we could check if there > > > > is a lock first and if there is no lock then acquire one. That way that would > > > > ensure the relation is always locked (so no "risk" anymore), but OTOH it may > > > > add "unecessary" locking (see 2. mentioned previously). > > > > > > Please find attached v12 implementing this idea for the RelationRelationId class > > > case. As mentioned, it may add unnecessary locking for 2. but I think that's > > > worth it to ensure that we are always on the safe side of thing. This idea is > > > implemented in LockNotPinnedObjectById(). > > > > Please find attached v13, mandatory rebase due to 0cecc908e97. In passing, make > > use of CheckRelationOidLockedByMe() added in 0cecc908e97. > > Please find attached v14, mandatory rebase due to 65b71dec2d5. In [1] I mentioned that the object locking logic has been put outside of the dependency code except for: recordDependencyOnExpr() recordDependencyOnSingleRelExpr() makeConfigurationDependencies() Please find attached v15 that also removes the logic outside of the 3 above functions. Well, for recordDependencyOnExpr() and recordDependencyOnSingleRelExpr() that's now done in find_expr_references_walker(): It's somehow still in the dependency code but at least it is now clear which objects we are locking (and I'm not sure how we could do better than that for those 2 functions). There is still one locking call in recordDependencyOnCurrentExtension() but I think this one is clear enough and does not need to be put outside (for the same reason mentioned in [1]). So, to sum up: A. Locking is now done exclusively with LockNotPinnedObject(Oid classid, Oid objid) so that it's now always clear what object we want to acquire a lock for. It means we are not manipulating directly an object address or a list of objects address as it was the case when the locking was done "directly" within the dependency code. B. A special case is done for objects that belong to the RelationRelationId class. For those, we should be in one of the two following cases that would already prevent the relation to be dropped: 1. The relation is already locked (could be an existing relation or a relation that we are creating). 2. The relation is protected indirectly (i.e an index protected by a lock on its table, a table protected by a lock on a function that depends the table...) To avoid any risks for the RelationRelationId class case, we acquire a lock if there is none. That may add unnecessary lock for 2. but that seems worth it. [1]: https://www.postgresql.org/message-id/ZnAVEBhlGvpDDVOD%40ip-10-97-1-34.eu-west-3.compute.internal Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2024-08-19T15:35:14Z
Hi, On Wed, Jul 10, 2024 at 07:31:06AM +0000, Bertrand Drouvot wrote: > So, to sum up: > > A. Locking is now done exclusively with LockNotPinnedObject(Oid classid, Oid objid) > so that it's now always clear what object we want to acquire a lock for. It means > we are not manipulating directly an object address or a list of objects address > as it was the case when the locking was done "directly" within the dependency code. > > B. A special case is done for objects that belong to the RelationRelationId class. > For those, we should be in one of the two following cases that would already > prevent the relation to be dropped: > > 1. The relation is already locked (could be an existing relation or a relation > that we are creating). > > 2. The relation is protected indirectly (i.e an index protected by a lock on > its table, a table protected by a lock on a function that depends the table...) > > To avoid any risks for the RelationRelationId class case, we acquire a lock if > there is none. That may add unnecessary lock for 2. but that seems worth it. > Please find attached v16, mandatory rebase due to 80ffcb8427. Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2024-10-28T09:30:19Z
Hi, On Mon, Aug 19, 2024 at 03:35:14PM +0000, Bertrand Drouvot wrote: > Hi, > > On Wed, Jul 10, 2024 at 07:31:06AM +0000, Bertrand Drouvot wrote: > > So, to sum up: > > > > A. Locking is now done exclusively with LockNotPinnedObject(Oid classid, Oid objid) > > so that it's now always clear what object we want to acquire a lock for. It means > > we are not manipulating directly an object address or a list of objects address > > as it was the case when the locking was done "directly" within the dependency code. > > > > B. A special case is done for objects that belong to the RelationRelationId class. > > For those, we should be in one of the two following cases that would already > > prevent the relation to be dropped: > > > > 1. The relation is already locked (could be an existing relation or a relation > > that we are creating). > > > > 2. The relation is protected indirectly (i.e an index protected by a lock on > > its table, a table protected by a lock on a function that depends the table...) > > > > To avoid any risks for the RelationRelationId class case, we acquire a lock if > > there is none. That may add unnecessary lock for 2. but that seems worth it. > > > > Please find attached v16, mandatory rebase due to 80ffcb8427. rebased (v17 attached). Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2025-01-02T08:15:13Z
Hi, On Mon, Oct 28, 2024 at 09:30:19AM +0000, Bertrand Drouvot wrote: > Hi, > > On Mon, Aug 19, 2024 at 03:35:14PM +0000, Bertrand Drouvot wrote: > > Hi, > > > > On Wed, Jul 10, 2024 at 07:31:06AM +0000, Bertrand Drouvot wrote: > > > So, to sum up: > > > > > > A. Locking is now done exclusively with LockNotPinnedObject(Oid classid, Oid objid) > > > so that it's now always clear what object we want to acquire a lock for. It means > > > we are not manipulating directly an object address or a list of objects address > > > as it was the case when the locking was done "directly" within the dependency code. > > > > > > B. A special case is done for objects that belong to the RelationRelationId class. > > > For those, we should be in one of the two following cases that would already > > > prevent the relation to be dropped: > > > > > > 1. The relation is already locked (could be an existing relation or a relation > > > that we are creating). > > > > > > 2. The relation is protected indirectly (i.e an index protected by a lock on > > > its table, a table protected by a lock on a function that depends the table...) > > > > > > To avoid any risks for the RelationRelationId class case, we acquire a lock if > > > there is none. That may add unnecessary lock for 2. but that seems worth it. > > > > > > > Please find attached v16, mandatory rebase due to 80ffcb8427. > > rebased (v17 attached). rebased (v18 attached). Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2025-02-04T13:24:03Z
Hi, On Thu, Jan 02, 2025 at 08:15:13AM +0000, Bertrand Drouvot wrote: > rebased (v18 attached). Thanks to all of you that have discussed this patch during the developer meeting at FOSDEM PGDay last week [1]. I'm attaching a new version to address Álvaro's concern about calling getObjectDescription() in the new LockNotPinnedObjectById() function. This call was being used to provide a "meaningful" error message but we agreed to provide the object OID instead (as anyway the object is dropped). Note that the OIDs are reported as "errdetail" to ensure the isolation tests added in this patch remain stable (output does not depend of the actual OIDs values). A quick sum up about this patch: A. Locking is done exclusively with LockNotPinnedObject(Oid classid, Oid objid) so that it's now always clear what object we want to acquire a lock for. It means that we are not manipulating directly an object address or a list of objects address as it was the case when the locking was done "directly" within the dependency code. B. A special case is done for objects that belong to the RelationRelationId class. For those, we should be in one of the two following cases that would already prevent the relation to be dropped: B1. The relation is already locked (could be an existing relation or a relation that we are creating). B2. The relation is protected indirectly (i.e an index protected by a lock on its table, a table protected by a lock on a function that depends the table...) To avoid any risks for the RelationRelationId class case, we acquire a lock if there is none. That may add unnecessary lock for B2. but that seems worth it. That's a lot of mechanical changes so that's easy to miss one or to do it wrong but: 1. I did my best to avoid that 2. assertions are added in recordMultipleDependencies() to "ensure" the object is locked 3. even if one case is missing (that is not catched by the assertions because the dependency is not covered in the tests, not sure that exists though), then it just means that we could be in the current state (orphaned dependency), not worst than that During the meeting a question has been raised regarding the number of locks increase. This has already been discussed in [2] and I think that the outcome is that the default max_locks_per_transaction value (64) is probably still enough in real life (and even if it is not then it can be increased to satisfy the requirements). [1]: https://2025.fosdempgday.org/devmeeting [2]: https://www.postgresql.org/message-id/CA%2BTgmoaFPUubBBk52Qp2wkoL7JX7OjhewiK%2B7LSot7%3DrecbzzQ%40mail.gmail.com Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Jeff Davis <pgsql@j-davis.com> — 2025-05-19T17:02:52Z
On Wed, 2024-05-22 at 10:48 -0400, Robert Haas wrote: > > Then, Tom proposed another approach in [2] which is that "creation > > DDL will have > > to take a lock on each referenced object that'd conflict with a > > lock taken by DROP". > > This is the one the current patch is trying to implement. > > It's a clever idea, but I'm not sure that I like it. I'm not sure if you intended it this way, but using "clever" here suggests that it's an unusual trick. But isn't that the kind of thing heavyweight locks are for? > > My concern was really more about the maintainability of > the code. I fear that if we add code that takes heavyweight locks in > surprising places, we might later find the behavior difficult to > reason about. FWIW, a lock while recording a dependency would not be surprising to me. Assuming that heavyweight locks are the right approach, the locks need to be taken somewhere. And expecting all the callers to get it right seems error-prone. This is a long thread so I must be missing some problem or complication here. Regards, Jeff Davis
-
Re: Avoid orphaned objects dependencies, take 3
Robert Haas <robertmhaas@gmail.com> — 2025-05-19T18:07:34Z
On Mon, May 19, 2025 at 1:02 PM Jeff Davis <pgsql@j-davis.com> wrote: > I'm not sure if you intended it this way, but using "clever" here > suggests that it's an unusual trick. But isn't that the kind of thing > heavyweight locks are for? > > FWIW, a lock while recording a dependency would not be surprising to > me. Assuming that heavyweight locks are the right approach, the locks > need to be taken somewhere. And expecting all the callers to get it > right seems error-prone. I agree with that, but I think that it may also be error-prone to assume that it's OK to acquire heavyweight locks on other catalog objects at any place in the code where we record a dependency. I will not be surprised at all if that turns out to have some negative consequences. For example, I think it might result in acquiring the locks on those other objects at a subtly wrong time (leading to race conditions) or acquiring two locks on the same object with different lock modes where we should really only acquire one. I'm all in favor of solving this problem using heavyweight locks, but I think that implicitly acquiring them as a side effect of recording dependencies feels too surprising. -- Robert Haas EDB: http://www.enterprisedb.com
-
Re: Avoid orphaned objects dependencies, take 3
Jeff Davis <pgsql@j-davis.com> — 2025-05-20T21:12:41Z
On Mon, 2025-05-19 at 14:07 -0400, Robert Haas wrote: > I agree with that, but I think that it may also be error-prone to > assume that it's OK to acquire heavyweight locks on other catalog > objects at any place in the code where we record a dependency. I will > not be surprised at all if that turns out to have some negative > consequences. For example, I think it might result in acquiring the > locks on those other objects at a subtly wrong time (leading to race > conditions) or acquiring two locks on the same object with different > lock modes where we should really only acquire one. I'm all in favor > of solving this problem using heavyweight locks, but I think that > implicitly acquiring them as a side effect of recording dependencies > feels too surprising. I see what you mean now, in the sense that other code that calls LockDatabaseObject (and other variants of LockAcquire) are mostly higher-level operations like AlterPublication(), and not side-effects of something else. But relation_open() is sort of an exception. There are lots of places where that takes a lock because we happen to want something out of the relcache, like generate_partition_qual() taking a lock on the parent or CheckAttributeType() taking a lock on the typrelid. You could say those are fairly obvious, but that's because we already know, and we could make it more widely known that recording a dependency takes a lock. One compromise might be to have recordDependencyOn() take a LOCKMODE parameter, which would both inform the caller that a lock will be taken, and allow the caller to do it their own way and specify NoLock if necessary. That still results in a huge diff, but the end result would not be any more complex than the current code. Regards, Jeff Davis
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2025-05-21T06:37:10Z
Hi, On Tue, May 20, 2025 at 02:12:41PM -0700, Jeff Davis wrote: > On Mon, 2025-05-19 at 14:07 -0400, Robert Haas wrote: > > I agree with that, but I think that it may also be error-prone to > > assume that it's OK to acquire heavyweight locks on other catalog > > objects at any place in the code where we record a dependency. I will > > not be surprised at all if that turns out to have some negative > > consequences. For example, I think it might result in acquiring the > > locks on those other objects at a subtly wrong time (leading to race > > conditions) or acquiring two locks on the same object with different > > lock modes where we should really only acquire one. I'm all in favor > > of solving this problem using heavyweight locks, but I think that > > implicitly acquiring them as a side effect of recording dependencies > > feels too surprising. > > I see what you mean now, in the sense that other code that calls > LockDatabaseObject (and other variants of LockAcquire) are mostly > higher-level operations like AlterPublication(), and not side-effects > of something else. > > But relation_open() is sort of an exception. There are lots of places > where that takes a lock because we happen to want something out of the > relcache, like generate_partition_qual() taking a lock on the parent or > CheckAttributeType() taking a lock on the typrelid. You could say those > are fairly obvious, but that's because we already know, and we could > make it more widely known that recording a dependency takes a lock. > > One compromise might be to have recordDependencyOn() take a LOCKMODE > parameter, which would both inform the caller that a lock will be > taken, and allow the caller to do it their own way and specify NoLock > if necessary. That still results in a huge diff, but the end result > would not be any more complex than the current code. Thanks for sharing your thoughts! I had in mind to "just" check if there is an existing lock (and if so, skip acquiring a new one) but your proposal sounds better. Indeed it would make the locking behavior explicit and also be flexible (allowing the callers to choose the LOCKMODE). I'll prepare a new version implementing your proposal. Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Robert Haas <robertmhaas@gmail.com> — 2025-05-21T16:55:22Z
On Tue, May 20, 2025 at 5:12 PM Jeff Davis <pgsql@j-davis.com> wrote: > But relation_open() is sort of an exception. There are lots of places > where that takes a lock because we happen to want something out of the > relcache, like generate_partition_qual() taking a lock on the parent or > CheckAttributeType() taking a lock on the typrelid. You could say those > are fairly obvious, but that's because we already know, and we could > make it more widely known that recording a dependency takes a lock. To me, relation_open() feels like the kind of operation that I would expect to take a lock. If I open something, I must have acquired some resource on it that I will then use for a while before closing the object. > One compromise might be to have recordDependencyOn() take a LOCKMODE > parameter, which would both inform the caller that a lock will be > taken, and allow the caller to do it their own way and specify NoLock > if necessary. That still results in a huge diff, but the end result > would not be any more complex than the current code. Yeah, that's not a terrible idea. I still like the idea I thought Bertrand was pursuing, namely, to take no lock in recordDependencyOn() but assert that the caller has previously acquired one. However, we could still do the Assert() check with this design when NoLock is passed, so I think this is a reasonable alternative to that design. -- Robert Haas EDB: http://www.enterprisedb.com
-
Re: Avoid orphaned objects dependencies, take 3
Jeff Davis <pgsql@j-davis.com> — 2025-05-21T17:17:58Z
On Wed, 2025-05-21 at 12:55 -0400, Robert Haas wrote: > > ...like generate_partition_qual() taking a lock on the parent or > > CheckAttributeType() taking a lock on the typrelid... > > To me, relation_open() feels like the kind of operation that I would > expect to take a lock. If I open something, I must have acquired some > resource on it that I will then use for a while before closing the > object. Of course relation_open() takes a lock, but sometimes relation_open() is hidden in the call stack below other functions where it's not so obvious. > > Yeah, that's not a terrible idea. I still like the idea I thought > Bertrand was pursuing, namely, to take no lock in > recordDependencyOn() > but assert that the caller has previously acquired one. However, we > could still do the Assert() check with this design when NoLock is > passed, so I think this is a reasonable alternative to that design. I'd have to see the patch to see whether I liked the end result. But I'm guessing that involves a lot of non-mechanical changes in the call sites, and also relies on test coverage for all of them. Regards, Jeff Davis
-
Re: Avoid orphaned objects dependencies, take 3
Robert Haas <robertmhaas@gmail.com> — 2025-05-21T17:47:17Z
On Wed, May 21, 2025 at 1:18 PM Jeff Davis <pgsql@j-davis.com> wrote: > Of course relation_open() takes a lock, but sometimes relation_open() > is hidden in the call stack below other functions where it's not so > obvious. Probably true, although some of those are probably code that could stand to be improved. > > Yeah, that's not a terrible idea. I still like the idea I thought > > Bertrand was pursuing, namely, to take no lock in > > recordDependencyOn() > > but assert that the caller has previously acquired one. However, we > > could still do the Assert() check with this design when NoLock is > > passed, so I think this is a reasonable alternative to that design. > > I'd have to see the patch to see whether I liked the end result. But > I'm guessing that involves a lot of non-mechanical changes in the call > sites, and also relies on test coverage for all of them. Sure, fair enough. -- Robert Haas EDB: http://www.enterprisedb.com
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2025-05-22T12:15:11Z
Hi, On Wed, May 21, 2025 at 10:17:58AM -0700, Jeff Davis wrote: > On Wed, 2025-05-21 at 12:55 -0400, Robert Haas wrote: > > Yeah, that's not a terrible idea. I still like the idea I thought > > Bertrand was pursuing, namely, to take no lock in > > recordDependencyOn() > > but assert that the caller has previously acquired one. However, we > > could still do the Assert() check with this design when NoLock is > > passed, so I think this is a reasonable alternative to that design. > > I'd have to see the patch to see whether I liked the end result. But > I'm guessing that involves a lot of non-mechanical changes in the call > sites, and also relies on test coverage for all of them. Thinking more about it, I'm not sure the NoLock/AccessShareLock will be easy to make it right and maintain. The thing is that in recordMultipleDependencies() we may iterate over multiple referenced objects and those are the ones we want a lock on. So if we enter recordMultipleDependencies() with "NoLock" we would need to be 100% sure that ALL the referenced objects are already locked (or that we don't want to take a lock on ALL the referenced objects). But that's not so easy, for example with something like: create schema my_schema; CREATE TABLE my_schema.test_maint(i INT); INSERT INTO my_schema.test_maint VALUES (1), (2); CREATE FUNCTION my_schema.fn(INT) RETURNS INT IMMUTABLE LANGUAGE plpgsql AS $$ BEGIN RAISE NOTICE 'BDT'; RETURN $1; END; $$; Then during: CREATE MATERIALIZED VIEW my_schema.test_maint_mv AS SELECT my_schema.fn(i) FROM my_schema.test_maint; 1. The schema is locked 2. The relation is locked 3. The function is not locked So we should pass "AccessShareLock" to recordMultipleDependencies() but that could be easy to miss, resulting in passing "NoLock". Furthermore, it means that even if we pass "AccessShareLock" correctly here, we'd need to check that there is no existing lock on each referenced object (with LockHeldByMe()/CheckRelationOidLockedByMe()) with a level > AccessShareLock (if not, we'd add an extra lock without any good reason to do so). With Robert's idea we could avoid to call LockDatabaseObject()/LockRelationOid() if we know that the object/relation is already locked (or that we don't want a lock at this place). But it would mean being 100% sure that if there are multiple code paths leading to the same referenced object insertion location then each of them have the same locking behavior. As that seems hard, a better approach would probably be to also always call LockHeldByMe()/CheckRelationOidLockedByMe() before trying to acquire the lock. So I think: - Jeff's idea could be hard to reason about, as "NoLock" could mean: we are sure that ALL the existing referenced objects are already locked and "AccessShareLock" would mean: we know that at least one referenced object needs an AccessShareLock. Then that would mean that "by design" we'd need to check if there is no existing lock before trying to acquire the AccessShareLock on the referenced objects. - Robert's idea would still require that we check whether there is any existing lock before acquiring the AccessShareLock (to be on the safe side of things). So I wonder if, after all, it makes sense to simply try to acquire the AccessShareLock on a referenced object in recordMultipleDependencies() IIF this referenced object is not already locked (basically what was initially proposed, but with this extra check added and without the "NoLock"/"lock" addition to recordMultipleDependencies())). That would probably address Robert's concern [1] "acquiring two locks on the same object with different lock modes where we should really only acquire one" but probably not this one "I think it might result in acquiring the locks on those other objects at a subtly wrong time (leading to race conditions)". For the latter I'm not sure how that could be a subtly wrong time or how could we determine what a subtly wrong time is (to avoid taking the lock). Thoughts? [1]: https://www.postgresql.org/message-id/CA%2BTgmoaCJ5-Zx5R0uN%2Bah4EZU1SamY1PneaW5O617FsNKavmfw%40mail.gmail.com Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com -
Re: Avoid orphaned objects dependencies, take 3
Robert Haas <robertmhaas@gmail.com> — 2025-05-22T13:40:47Z
On Thu, May 22, 2025 at 8:15 AM Bertrand Drouvot <bertranddrouvot.pg@gmail.com> wrote: > That would probably address Robert's concern [1] "acquiring two locks on the same > object with different lock modes where we should really only acquire one" but > probably not this one "I think it might result in acquiring the > locks on those other objects at a subtly wrong time (leading to race > conditions)". > > For the latter I'm not sure how that could be a subtly wrong time or how could > we determine what a subtly wrong time is (to avoid taking the lock). Well, I admit I'm not quite sure there is any timing problem here. But I think it's possible. For example, consider RangeVarGetRelidExtended, and in particular the callback argument. If we do permissions checking before locking the relation, the results can change before we actually lock the relation; if we do it afterward, users can contrive to hold locks on relations for which they don't have permissions. That rather ornate callback system allows us to have the best of both worlds. That system is also concerned with the possibility that we do a name -> OID translation before holding a lock and by the time we acquire the lock, concurrent DDL has happened and the answer we got is no longer the right answer. We've had security holes due to such things. Now I'm not entirely sure that either of those things are issues here. My first guess is that name lookup races are not an issue here, because we're following OID links, but permissions checks seem like they might be an issue. We might not decide to do something as elaborate as we did with RangeVarGetRelidExtended and ... maybe that's OK? But in general I am skeptical of the conclusion that we can just shove all the locking down into some subordinate layer and nothing will go wrong, because locking doesn't exist in a vacuum -- it relates to other things that we also need to do, and whether we do the locking before or after other steps can affect semantics and even security. Pushing the locking down into recordDependencyOn amounts to hoping that we don't need to study each code path in detail and decide on the exactly right place to acquire the lock. It amounts to hoping that wherever the recordDependencyOn call is located, it's before things that we want the locking to happen before and after the things that we want the locking to happen after. And maybe that's true. Or maybe it's close enough to true that it's still better than the status quo where we're not taking locks at all. But on the other hand, since I can't think of any compelling reason why it HAS to be true, maybe it isn't. -- Robert Haas EDB: http://www.enterprisedb.com
-
Re: Avoid orphaned objects dependencies, take 3
Jeff Davis <pgsql@j-davis.com> — 2025-05-22T19:38:50Z
On Thu, 2025-05-22 at 09:40 -0400, Robert Haas wrote: > Pushing the locking down into recordDependencyOn amounts to hoping > that we don't need to study each code path in detail and decide on > the > exactly right place to acquire the lock. There are (by my rough count) over 250 call sites modified by the v19 patch. I fear that, if each of those call sites needs to be studied for the kinds of subtle issues you are describing, then we are likely to make a mistake. If not now, then in the future as new features change those call sites. Bertrand, what pattern is safe to follow for most call sites? Which call sites are the most interesting ones that need special attention? Regards, Jeff Davis
-
Re: Avoid orphaned objects dependencies, take 3
jian he <jian.universality@gmail.com> — 2025-05-23T04:20:01Z
On Tue, Feb 4, 2025 at 9:24 PM Bertrand Drouvot <bertranddrouvot.pg@gmail.com> wrote: > > Hi, > > On Thu, Jan 02, 2025 at 08:15:13AM +0000, Bertrand Drouvot wrote: > > rebased (v18 attached). > > Thanks to all of you that have discussed this patch during the developer meeting > at FOSDEM PGDay last week [1]. I'm attaching a new version to address Álvaro's > concern about calling getObjectDescription() in the new LockNotPinnedObjectById() > function. This call was being used to provide a "meaningful" error message but > we agreed to provide the object OID instead (as anyway the object is dropped). > > Note that the OIDs are reported as "errdetail" to ensure the isolation tests > added in this patch remain stable (output does not depend of the actual OIDs > values). > > A quick sum up about this patch: > > A. Locking is done exclusively with LockNotPinnedObject(Oid classid, Oid objid) > so that it's now always clear what object we want to acquire a lock for. It means > that we are not manipulating directly an object address or a list of objects > address as it was the case when the locking was done "directly" within the > dependency code. > > B. A special case is done for objects that belong to the RelationRelationId class. > For those, we should be in one of the two following cases that would already > prevent the relation to be dropped: > > B1. The relation is already locked (could be an existing relation or a relation > that we are creating). > > B2. The relation is protected indirectly (i.e an index protected by a lock on > its table, a table protected by a lock on a function that depends the table...) > > To avoid any risks for the RelationRelationId class case, we acquire a lock if > there is none. That may add unnecessary lock for B2. but that seems worth it. > > That's a lot of mechanical changes so that's easy to miss one or to do it > wrong but: > > 1. I did my best to avoid that > 2. assertions are added in recordMultipleDependencies() to "ensure" the object > is locked > 3. even if one case is missing (that is not catched by the assertions because > the dependency is not covered in the tests, not sure that exists though), then it > just means that we could be in the current state (orphaned dependency), not worst > than that > > During the meeting a question has been raised regarding the number of locks > increase. This has already been discussed in [2] and I think that the outcome > is that the default max_locks_per_transaction value (64) is probably still > enough in real life (and even if it is not then it can be increased to satisfy > the requirements). > > [1]: https://2025.fosdempgday.org/devmeeting > [2]: https://www.postgresql.org/message-id/CA%2BTgmoaFPUubBBk52Qp2wkoL7JX7OjhewiK%2B7LSot7%3DrecbzzQ%40mail.gmail.com > hi. I plan to play around with v19. but i can not build based on it. related error message: running bootstrap script ... ok performing post-bootstrap initialization ... TRAP: failed Assert("LockHeldByMe(&tag, AccessShareLock, false)"), File: "../../Desktop/pg_src/src1/postgres/src/backend/catalog/pg_depend.c", Line: 116, PID: 1118343 /home/jian/postgres/regression1/bin/postgres(ExceptionalCondition+0xf3)[0xe66090] /home/jian/postgres/regression1/bin/postgres(recordMultipleDependencies+0x17f)[0x6ae34d] /home/jian/postgres/regression1/bin/postgres(record_object_address_dependencies+0x4e)[0x66b333] /home/jian/postgres/regression1/bin/postgres(ProcedureCreate+0x2443)[0x6b9ad7] /home/jian/postgres/regression1/bin/postgres(CreateFunction+0xf91)[0x73a7ef] /home/jian/postgres/regression1/bin/postgres[0xbcba68] /home/jian/postgres/regression1/bin/postgres(standard_ProcessUtility+0x170c)[0xbc9fb7] /home/jian/postgres/regression1/bin/postgres(ProcessUtility+0x184)[0xbc889b] /home/jian/postgres/regression1/bin/postgres[0xbc6821] /home/jian/postgres/regression1/bin/postgres[0xbc6bbd] /home/jian/postgres/regression1/bin/postgres(PortalRun+0x3f4)[0xbc5bda] /home/jian/postgres/regression1/bin/postgres[0xbbae37] /home/jian/postgres/regression1/bin/postgres(PostgresMain+0x1110)[0xbc2feb] /home/jian/postgres/regression1/bin/postgres(PostgresMain+0x0)[0xbc1edb] /home/jian/postgres/regression1/bin/postgres(main+0x4cf)[0x8e98b1] /lib/x86_64-linux-gnu/libc.so.6(+0x29d90)[0x71d95a829d90] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80)[0x71d95a829e40] /home/jian/postgres/regression1/bin/postgres(_start+0x25)[0x4af1c5] Aborted (core dumped) child process exited with exit code 134 -
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2025-06-02T14:02:36Z
Hi, On Thu, May 22, 2025 at 09:40:47AM -0400, Robert Haas wrote: > On Thu, May 22, 2025 at 8:15 AM Bertrand Drouvot > <bertranddrouvot.pg@gmail.com> wrote: > > That would probably address Robert's concern [1] "acquiring two locks on the same > > object with different lock modes where we should really only acquire one" but > > probably not this one "I think it might result in acquiring the > > locks on those other objects at a subtly wrong time (leading to race > > conditions)". > > > > For the latter I'm not sure how that could be a subtly wrong time or how could > > we determine what a subtly wrong time is (to avoid taking the lock). > > Well, I admit I'm not quite sure there is any timing problem here. But > I think it's possible. For example, consider RangeVarGetRelidExtended, > and in particular the callback argument. If we do permissions checking > before locking the relation, the results can change before we actually > lock the relation; if we do it afterward, users can contrive to hold > locks on relations for which they don't have permissions. That rather > ornate callback system allows us to have the best of both worlds. That > system is also concerned with the possibility that we do a name -> OID > translation before holding a lock and by the time we acquire the lock, > concurrent DDL has happened and the answer we got is no longer the > right answer. We've had security holes due to such things. Yeah I just looked at 2ad36c4e44c and see what issues it solved. > > Now I'm not entirely sure that either of those things are issues here. > My first guess is that name lookup races are not an issue here, > because we're following OID links, Yeah, I think the same and think that checking that the object(s) still exist once we tried to get the lock is probably enough (as done in the patch versions that have been shared). > but permissions checks seem like > they might be an issue. I agree that we might end up locking an object we don't have the permission on. I just looked at 2 examples. 1. CREATE MATERIALIZED VIEW test_maint_mv2 AS SELECT fn(i) FROM test_maint; Would call recordMultipleDependencies() with the function as a referenced object: Breakpoint 3, recordMultipleDependencies 123 (gdb) p *depender $3 = {classId = 2618, objectId = 16468, objectSubId = 0} (gdb) p *referenced $4 = {classId = 1255, objectId = 16388, objectSubId = 0} postgres=# select * from pg_identify_object(1255,16388,0); type | schema | name | identity ----------+--------+------+-------------------- function | public | | public.fn(integer) (1 row) But would check the permissions after: Breakpoint 1, object_aclcheck (classid=1255, objectid=16388, roleid=16384, mode=128) 3823 return object_aclcheck_ext(classid, objectid, roleid, mode, NULL); Means that in that case we'd put a lock on the function *before* checking for the permissions. 2. OTOH it's also possible the other way around: CREATE FUNCTION myschema1.foo() RETURNS int AS 'select 1' LANGUAGE sql; Would call object_aclcheck() on the schema: Breakpoint 1, object_aclcheck (classid=2615, objectid=16435, roleid=10, mode=512) 3823 return object_aclcheck_ext(classid, objectid, roleid, mode, NULL); postgres=# select * from pg_identify_object(2615,16435,0); type | schema | name | identity --------+--------+-----------+----------- schema | | myschema1 | myschema1 (1 row) before adding the dependency: Breakpoint 3, recordMultipleDependencies 123 (gdb) p *referenced $1 = {classId = 2615, objectId = 16435, objectSubId = 0} > We might not decide to do something as > elaborate as we did with RangeVarGetRelidExtended So, we currently have 2 patterns: P1: permission checks on a referenced object is done before we call recordMultipleDependencies() P2: permission checks on a referenced object is done after we call recordMultipleDependencies() So, if we add locking in recordMultipleDependencies() then I think that P2 is worst than P1 (there is still the risk that the permissions changed by the time we reach recordMultipleDependencies() though). As also stated in RangeVarGetRelidExtended()'s comment: " and it's really best to check permissions * before locking anything! " We could imagine doing the permissions check in recordMultipleDependencies() (in more or less the same way as RangeVarGetRelidExtended() is doing with callback) because only the recordMultipleDependencies()'s "caller" could know what kind of check is needed. But that would not work for the same reasons as Jeff's proposal does not fly (we may manipulate multiple referenced objects that would need distinct callbacks...). So, it looks like that we may need some refactoring of the existing code to ensure that the permissions checks on the referenced objects are done before the lock is acquired (before recordMultipleDependencies() is called?). I propose to first try to list the cases where P2 is in action (and I think those will be the ones that need special attention that Jeff is asking for in [1]). And then discuss once we have the cases in hand. Thoughts? [1]: https://www.postgresql.org/message-id/d721011cd3ec3aedd57b193ef10cf541f50df325.camel%40j-davis.com Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com -
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2025-06-02T14:06:43Z
Hi, On Thu, May 22, 2025 at 12:38:50PM -0700, Jeff Davis wrote: > Which > call sites are the most interesting ones that need special attention? I think the ones that need special attention are the ones that check for the permissions on the referenced objects *after* recordMultipleDependencies is called (see [1]). Does that make sense to you? [1]: https://www.postgresql.org/message-id/aD2u/GR/yaRkBJdJ%40ip-10-97-1-34.eu-west-3.compute.internal Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Robert Haas <robertmhaas@gmail.com> — 2025-06-03T17:27:29Z
On Mon, Jun 2, 2025 at 10:02 AM Bertrand Drouvot <bertranddrouvot.pg@gmail.com> wrote: > So, we currently have 2 patterns: > > P1: permission checks on a referenced object is done before we call recordMultipleDependencies() > P2: permission checks on a referenced object is done after we call recordMultipleDependencies() > > So, if we add locking in recordMultipleDependencies() then I think that P2 is worst > than P1 (there is still the risk that the permissions changed by the time > we reach recordMultipleDependencies() though). Hmm. I don't think I agree. If I understand correctly, P2 only permits users to take a lock on an object they shouldn't be able to touch, permitting them to temporarily interfere with access to it. P1 permits users to potentially perform a permanent catalog modification that should have been blocked by the permissions system. To my knowledge, we've never formally classified the former type of problem as a security vulnerability, although maybe there's an argument that it is one. We've filed CVEs for problems of the latter sort. -- Robert Haas EDB: http://www.enterprisedb.com
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2025-06-06T05:37:36Z
Hi, On Tue, Jun 03, 2025 at 01:27:29PM -0400, Robert Haas wrote: > On Mon, Jun 2, 2025 at 10:02 AM Bertrand Drouvot > <bertranddrouvot.pg@gmail.com> wrote: > > So, we currently have 2 patterns: > > > > P1: permission checks on a referenced object is done before we call recordMultipleDependencies() > > P2: permission checks on a referenced object is done after we call recordMultipleDependencies() > > > > So, if we add locking in recordMultipleDependencies() then I think that P2 is worst > > than P1 (there is still the risk that the permissions changed by the time > > we reach recordMultipleDependencies() though). > > Hmm. I don't think I agree. Thanks for sharing your thoughts! > If I understand correctly, P2 only permits > users to take a lock on an object they shouldn't be able to touch, > permitting them to temporarily interfere with access to it. P1 permits > users to potentially perform a permanent catalog modification that > should have been blocked by the permissions system. To my knowledge, > we've never formally classified the former type of problem as a > security vulnerability, although maybe there's an argument that it is > one. We've filed CVEs for problems of the latter sort. What I meant to say is that P2 is worse "by design" because it's "always" wrong to lock an object we don't have a permission on: so the permission should be checked first. So we have: P1: * check_permissions() * permissions could change here * Lock in recordMultipleDependencies() P2: * Lock in recordMultipleDependencies() * FWIW, permissions could change here too (for example, one could still " revoke usage on schema myschema1 from user1" while there is an AccessShareLock on schema myschema1) * check_permissions() But P2 sequence of events is "wrong" by design (to lock an object we may not have permissions on) that's what I meant. Now if we look at it from a "pure" security angle (as you did) I agree that P1 is the worse because it could allow catalog change that should have been blocked by the permission check. P2 would prevent that. I agree that we should focus on P1 then. Let me try to list the P1 cases (instead of the P2 ones) so that we can focus on /discuss those. Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Roman Eskin <r.eskin@arenadata.io> — 2025-11-09T08:33:39Z
Hi everybody, Apologies for jumping into this conversation. Our customers have also encountered a similar issue with the concurrent drop of a dependent object. In our code (in the Greengage DB), we implemented a fix analogous to one of the first versions of Bertrand's approach, using locking within the pg_depend code. In the long term, however, we are interested in aligning with the upstream Postgres code as much as possible. Since there haven't been any recent updates in this thread, we were wondering if there are any plans for the next steps regarding this issue. Thank you in advance for your insights. Best regards, Roman Eskin 06.06.2025 15:37, Bertrand Drouvot пишет: > Hi, > > On Tue, Jun 03, 2025 at 01:27:29PM -0400, Robert Haas wrote: >> On Mon, Jun 2, 2025 at 10:02 AM Bertrand Drouvot >> <bertranddrouvot.pg@gmail.com> wrote: >>> So, we currently have 2 patterns: >>> >>> P1: permission checks on a referenced object is done before we call recordMultipleDependencies() >>> P2: permission checks on a referenced object is done after we call recordMultipleDependencies() >>> >>> So, if we add locking in recordMultipleDependencies() then I think that P2 is worst >>> than P1 (there is still the risk that the permissions changed by the time >>> we reach recordMultipleDependencies() though). >> >> Hmm. I don't think I agree. > > Thanks for sharing your thoughts! > >> If I understand correctly, P2 only permits >> users to take a lock on an object they shouldn't be able to touch, >> permitting them to temporarily interfere with access to it. P1 permits >> users to potentially perform a permanent catalog modification that >> should have been blocked by the permissions system. To my knowledge, >> we've never formally classified the former type of problem as a >> security vulnerability, although maybe there's an argument that it is >> one. We've filed CVEs for problems of the latter sort. > > What I meant to say is that P2 is worse "by design" because it's "always" wrong > to lock an object we don't have a permission on: so the permission should be > checked first. > > So we have: > > P1: > > * check_permissions() > * permissions could change here > * Lock in recordMultipleDependencies() > > P2: > > * Lock in recordMultipleDependencies() > * FWIW, permissions could change here too (for example, one could still " > revoke usage on schema myschema1 from user1" while there is an AccessShareLock > on schema myschema1) > * check_permissions() > > But P2 sequence of events is "wrong" by design (to lock an object we may not > have permissions on) that's what I meant. > > Now if we look at it from a "pure" security angle (as you did) I agree that P1 is > the worse because it could allow catalog change that should have been blocked by > the permission check. P2 would prevent that. I agree that we should focus on > P1 then. > > Let me try to list the P1 cases (instead of the P2 ones) so that we can focus on > /discuss those. > > Regards, > -- С уважением, Роман Ескин | C Developer +7-964-449-81-15 | _r.eskin@arenadata.io_ <mailto:r.eskin@arenadata.io> _Arenadata_ <https://arenadata.tech/> | Подписывайтесь на нас в Telegram <https://t.me/arenadata> и VK <https://vk.com/arenadata>! <https://arenadata.tech/events/> УВЕДОМЛЕНИЕ О КОНФИДЕНЦИАЛЬНОСТИ: Это электронное сообщение и любые документы, приложенные к нему, могут содержать конфиденциальную информацию и предназначаются только адресату/адресатам. Настоящим уведомляем Вас о том, что Вы не можете воспроизводить, распространять и разглашать содержимое данного электронного сообщения кому-либо без письменного согласия ООО «Аренадата Софтвер». Разглашение конфиденциальной информации строго запрещено. Если Вы получили это сообщение по ошибке, пожалуйста, сообщите об этом отправителю по электронной почте и немедленно удалите это сообщение.
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-04-15T18:36:49Z
Hi, On Sun, Nov 09, 2025 at 06:33:39PM +1000, Roman Eskin wrote: > Hi everybody, > > Apologies for jumping into this conversation. No problem at all, you're more than welcome to join it! > Our customers have also encountered a similar issue with the > concurrent drop of a dependent object. Yeah, it's not something so rare that one could thought (we also see a non negligible of them in our fleet). > In our code (in the Greengage > DB), we implemented a fix analogous to one of the first versions of > Bertrand's approach, using locking within the pg_depend code. > > In the long term, however, we are interested in aligning with the > upstream Postgres code as much as possible. > > Since there haven't been any recent updates in this thread, we were > wondering if there are any plans for the next steps regarding this > issue. Apologies for this late reply. I plan to work on this again in the coming weeks. The next step I've in mind is to build the list of the P1 cases described above. Once we've that list I think we could start discussing the next steps. Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-04-28T11:17:17Z
Hi, On Wed, Apr 15, 2026 at 06:36:49PM +0000, Bertrand Drouvot wrote: > Hi, > > On Sun, Nov 09, 2025 at 06:33:39PM +1000, Roman Eskin wrote: > > Hi everybody, > > > > Apologies for jumping into this conversation. > > No problem at all, you're more than welcome to join it! > > > Our customers have also encountered a similar issue with the > > concurrent drop of a dependent object. > > Yeah, it's not something so rare that one could thought (we also see a non > negligible of them in our fleet). > > > In our code (in the Greengage > > DB), we implemented a fix analogous to one of the first versions of > > Bertrand's approach, using locking within the pg_depend code. > > > > In the long term, however, we are interested in aligning with the > > upstream Postgres code as much as possible. > > > > Since there haven't been any recent updates in this thread, we were > > wondering if there are any plans for the next steps regarding this > > issue. > > Apologies for this late reply. I plan to work on this again in the coming weeks. > The next step I've in mind is to build the list of the P1 cases described above. > > Once we've that list I think we could start discussing the next steps. I've been able to identify the P1 cases (lock after acl check) thanks to 0003 attached (more on that later). With the list at hands, v20 attached is made of 3 sub patches. 0001: Avoid orphaned objects dependencies Fix by acquiring AccessShareLock on referenced objects when recording dependencies. This conflicts with AccessExclusiveLock taken by DROP, preventing the race. After acquiring the lock, verify the object still exists, if it was dropped concurrently, report an error. The lock and check is done in both recordMultipleDependencies() and changeDependencyFor(). Remarks: 1/ If the caller already holds a lock that conflicts with DROP (AccessShareLock or stronger), skip the lock acquisition entirely. 2/ That does not address Robert's concern about lock after acl check cases and that's what 0002 and 0003 are for. 0002: Lock referenced objects before permission checks in DDL commands Add LockNotPinnedObjectById() calls before object_aclcheck() at all caller sites where a permission check on a referenced object occurs before the dependency on that object is recorded. This converts permission check before lock to lock before permission check. It closes the TOCTOU window that could allow a REVOKE to succeed between the permission check and the dependency recording. Note that the permission check before lock cases fixed in 0002 are already present. 0001 just makes the TOCTOU window wider in case there is a concurrent drop that would block the dependency recording. So, I think that 0002 has merit on its own. 0003: Add Assert guard to detect permission check before lock regressions Add instrumentation under USE_ASSERT_CHECKING to detect cases where object_aclcheck() is called on a referenced object before a lock is held on it, which would widen the TOCTOU window between the permission check and the dependency recording. In recordMultipleDependencies() and changeDependencyFor(), for each referenced object that had a permission check earlier in the same statement, assert that a lock is already held. This catches any future code that adds a permission check on a referenced object without first acquiring a lock. This is enabled only for Assert enabled builds so that there is no overhead for production builds. I think that v20 is taking care of what has been discussed in this thread, mainly: - It adds the new lock when the dependency is being recorded and thus avoids having to modify about 250 call sites (as mentioned by Jeff in [1]). - It addresses Robert's concern about the permission check before lock TOCTOU window (thanks to 0002 that modifies about 70 call sites and 0003 that ensures that we should trap new ones if any). Thoughts? [1]: https://postgr.es/m/d721011cd3ec3aedd57b193ef10cf541f50df325.camel%40j-davis.com Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Robert Haas <robertmhaas@gmail.com> — 2026-05-13T20:20:21Z
On Tue, Apr 28, 2026 at 7:17 AM Bertrand Drouvot <bertranddrouvot.pg@gmail.com> wrote: > 0003: Add Assert guard to detect permission check before lock regressions > > Add instrumentation under USE_ASSERT_CHECKING to detect cases where object_aclcheck() > is called on a referenced object before a lock is held on it, which would widen > the TOCTOU window between the permission check and the dependency recording. I really like the idea of having some kind of cross-check system that can detect future (or current) coding mistakes. But what I wonder about this mechanism is: should we instead be insisting that we take a lock and check permissions on every dependency? Is it an error to record a dependency on an object without any sort of permissions check? Also, I think the mechanism might not be entirely safe. ProcessUtility can result in executing user-defined functions which could theoretically run other DDL and then it seems like this code would get confused. -- Robert Haas EDB: http://www.enterprisedb.com
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-05-18T12:14:34Z
Hi, On Wed, May 13, 2026 at 04:20:21PM -0400, Robert Haas wrote: > On Tue, Apr 28, 2026 at 7:17 AM Bertrand Drouvot > <bertranddrouvot.pg@gmail.com> wrote: > > 0003: Add Assert guard to detect permission check before lock regressions > > > > Add instrumentation under USE_ASSERT_CHECKING to detect cases where object_aclcheck() > > is called on a referenced object before a lock is held on it, which would widen > > the TOCTOU window between the permission check and the dependency recording. > > I really like the idea of having some kind of cross-check system that > can detect future (or current) coding mistakes. Thanks for the feedback! BTW, it detected a new one due to 4793fc41f82, so v21 attached does fix it to make the CI green. > But what I wonder > about this mechanism is: should we instead be insisting that we take a > lock and check permissions on every dependency? Is it an error to > record a dependency on an object without any sort of permissions > check? I'm not sure. For example, currently, without execute privilege on myfunc(), one could create a view like: CREATE VIEW v1 AS SELECT myfunc(a) FROM t1; so that the dependency is recorded. The execution permission is checked when the view is queried. I don't think this example is a bug, so that I'm doubtful about insisting that we take a lock and check permissions on every dependency. > Also, I think the mechanism might not be entirely safe. ProcessUtility > can result in executing user-defined functions which could > theoretically run other DDL and then it seems like this code would get > confused. The assert fires when an aclcheck was tracked and the lock is not held. Since the locks are transaction-scoped, any lock acquired during the statement persists, so the assert passes regardless of nesting. So that looks not possible to get false positives (assert fires) due to user-defined functions running other DDL. The concern would be false negatives (missed detection) due to the reset wiping the outer DDL's entries. I believe, that's theoretically possible only if: - The outer DDL has a P1 bug (aclcheck without lock on object X) - A user-defined function happens to run DDL that also aclchecks and locks the same object X - The user-defined function DDL's lock satisfies the assert for the outer DDL But then, the bug would be caught in normal testing without the user-defined function being present. So I'm not sure how this code could get confused. Do you have an example of what you have in mind? Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Heikki Linnakangas <hlinnaka@iki.fi> — 2026-05-26T18:00:11Z
On 18/05/2026 15:14, Bertrand Drouvot wrote: > On Wed, May 13, 2026 at 04:20:21PM -0400, Robert Haas wrote: >> On Tue, Apr 28, 2026 at 7:17 AM Bertrand Drouvot >> <bertranddrouvot.pg@gmail.com> wrote: >>> 0003: Add Assert guard to detect permission check before lock regressions >>> >>> Add instrumentation under USE_ASSERT_CHECKING to detect cases where object_aclcheck() >>> is called on a referenced object before a lock is held on it, which would widen >>> the TOCTOU window between the permission check and the dependency recording. >> >> I really like the idea of having some kind of cross-check system that >> can detect future (or current) coding mistakes. > > Thanks for the feedback! BTW, it detected a new one due to 4793fc41f82, so v21 > attached does fix it to make the CI green. I had a closer look at patch 0001. It doesn't fix all the problems, we definitely need patch 0002/0003 too, but it's a good step forward and I think it makes sense to commit it independently. So I'm focusing on that now. I noticed we are already doing essentially the same thing for shared dependencies, in shdepLockAndCheckObject(). I see that you even copied the function comment from shdepLockAndCheckObject() to LockNotPinnedObject(), but I didn't see it being otherwise mentioned in this thread. In any case, that's a good argument for doing the same for non-shared dependencies that we already do for shared dependencies. > + if (object->classId == RelationRelationId) > + { > + /* skip shared relations as they are pinned */ > + if (IsSharedRelation(object->objectId)) > + return; > + > + /* > + * We must be in one of the two following cases that would already > + * prevent the relation to be dropped: 1. The relation is already > + * locked (could be an existing relation or a relation that we are > + * creating). 2. The relation is protected indirectly (i.e an index > + * protected by a lock on its table, a table protected by a lock on a > + * function that depends of the table...). To avoid any risks, acquire > + * a lock if there is none. That may add unnecessary lock for 2. but > + * that's worth it. > + */ > + if (!CheckRelationOidLockedByMe(object->objectId, AccessShareLock, true)) > + LockRelationOid(object->objectId, AccessShareLock); > + return; > + } Hmm, shouldn't that re-check that the relation exists, after acquiring the lock, like the non-relation codepath does? Otherwise it's a little pointless to acquire the lock. I did a bunch of little refactorings and ended up with the attached. Notable changes: - I renamed and moved LockNotPinnedObject() into dependencyLockAndCheckObject(), for consistency with shdepLockAndCheckObject(). - Removed ObjectByIdExist(), inlined it into the caller. The argument to use SnapshotSelf or not seemed a bit too special to be generally useful - Changed the error message and code to rhyme with the existing "role <OID> was concurrently dropped" errors. I didn't add the OID to the error message however, because that makes the testing hard. It'd be nice to have a general masking mechanism for OIDs and XIDs in error messages in tests, but now is not the time for that. - Added a test for a dependency on a role too, to cover the existing shdepLockAndCheckObject() function. It's currently disabled because it prints the OID, though. - Heikki -
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-05-26T20:18:50Z
Hi, On Tue, May 26, 2026 at 09:00:11PM +0300, Heikki Linnakangas wrote: > On 18/05/2026 15:14, Bertrand Drouvot wrote: > > On Wed, May 13, 2026 at 04:20:21PM -0400, Robert Haas wrote: > > > On Tue, Apr 28, 2026 at 7:17 AM Bertrand Drouvot > > > <bertranddrouvot.pg@gmail.com> wrote: > > > > 0003: Add Assert guard to detect permission check before lock regressions > > > > > > > > Add instrumentation under USE_ASSERT_CHECKING to detect cases where object_aclcheck() > > > > is called on a referenced object before a lock is held on it, which would widen > > > > the TOCTOU window between the permission check and the dependency recording. > > > > > > I really like the idea of having some kind of cross-check system that > > > can detect future (or current) coding mistakes. > > > > Thanks for the feedback! BTW, it detected a new one due to 4793fc41f82, so v21 > > attached does fix it to make the CI green. > > I had a closer look at patch 0001. Thanks! > It doesn't fix all the problems, we > definitely need patch 0002/0003 too, but it's a good step forward and I > think it makes sense to commit it independently. Yeah, this will prevent orphaned objects which is a good step forward. > So I'm focusing on that > now. Thanks! I'll resume working on something similar to 0002 and 0003 once 0001 gets in. > I noticed we are already doing essentially the same thing for shared > dependencies, in shdepLockAndCheckObject(). I see that you even copied the > function comment from shdepLockAndCheckObject() to LockNotPinnedObject(), > but I didn't see it being otherwise mentioned in this thread. In any case, > that's a good argument for doing the same for non-shared dependencies that > we already do for shared dependencies. Right. > > + if (object->classId == RelationRelationId) > > + { > > + /* skip shared relations as they are pinned */ > > + if (IsSharedRelation(object->objectId)) > > + return; > > + > > + /* > > + * We must be in one of the two following cases that would already > > + * prevent the relation to be dropped: 1. The relation is already > > + * locked (could be an existing relation or a relation that we are > > + * creating). 2. The relation is protected indirectly (i.e an index > > + * protected by a lock on its table, a table protected by a lock on a > > + * function that depends of the table...). To avoid any risks, acquire > > + * a lock if there is none. That may add unnecessary lock for 2. but > > + * that's worth it. > > + */ > > + if (!CheckRelationOidLockedByMe(object->objectId, AccessShareLock, true)) > > + LockRelationOid(object->objectId, AccessShareLock); > > + return; > > + } > > Hmm, shouldn't that re-check that the relation exists, after acquiring the > lock, like the non-relation codepath does? Otherwise it's a little pointless > to acquire the lock. Good catch! Your change: + if (CheckRelationOidLockedByMe(objectId, AccessShareLock, true)) + return; + LockRelationOid(objectId, AccessShareLock); + + if (SearchSysCacheExists1(RELOID, ObjectIdGetDatum(objectId))) + return; + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("dependent relation was concurrently dropped"))); Looks good to me. > I did a bunch of little refactorings and ended up with the attached. Notable > changes: > > - I renamed and moved LockNotPinnedObject() into > dependencyLockAndCheckObject(), for consistency with > shdepLockAndCheckObject(). Makes sense to me. > - Removed ObjectByIdExist(), inlined it into the caller. The argument to use > SnapshotSelf or not seemed a bit too special to be generally useful Yeah, better to have this logic inlined as it will probably not be useful outside of it. > - Changed the error message and code to rhyme with the existing "role <OID> > was concurrently dropped" errors. I didn't add the OID to the error message > however, because that makes the testing hard. Agreed that's better. > - Added a test for a dependency on a role too, to cover the existing > shdepLockAndCheckObject() function. It's currently disabled because it > prints the OID, though. Nit: It also adds: +# function - role +permutation "s1_begin" "s1_alter_function_owner" "s2_drop_role" "s1_commit" That is not disabled and would already pass without the changes added in 0001. So I wonder if we could just start by adding this new test (and the XXX one) in a dedicated patch and then add 0001 that would focus on orphaned stuff only. A few comments: 1/ +#include "catalog/index.h" That doesn't look needed. 2/ It looks like pgindent "complains" on pg_depend.c for dependencyLockAndCheckObject(). Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com -
Re: Avoid orphaned objects dependencies, take 3
Heikki Linnakangas <hlinnaka@iki.fi> — 2026-05-27T15:53:35Z
On 26/05/2026 23:18, Bertrand Drouvot wrote: > On Tue, May 26, 2026 at 09:00:11PM +0300, Heikki Linnakangas wrote: >> - Added a test for a dependency on a role too, to cover the existing >> shdepLockAndCheckObject() function. It's currently disabled because it >> prints the OID, though. > > Nit: It also adds: > > +# function - role > +permutation "s1_begin" "s1_alter_function_owner" "s2_drop_role" "s1_commit" > > That is not disabled and would already pass without the changes added in 0001. > > So I wonder if we could just start by adding this new test (and the XXX one) in > a dedicated patch and then add 0001 that would focus on orphaned stuff only. True, we could do that. It doesn't really seem to me any better or worse than just including it in the same commit though. > A few comments: > > 1/ > > +#include "catalog/index.h" > > That doesn't look needed. > > 2/ > > It looks like pgindent "complains" on pg_depend.c for dependencyLockAndCheckObject(). Fixed these, and did some more copy-editing on the comments. With that, committed and backpatched. Version 14 needed a little more work to backport because it didn't have IsPinnedObject(). And I noticed that recordDependencyFor() was being called with InvalidOid on ALTER TABLE commands on tables with dropped columns. That was bogus but harmless before this patch; I fixed that too. Thanks! - Heikki
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-06-01T09:21:39Z
Hi, On Wed, May 27, 2026 at 06:53:35PM +0300, Heikki Linnakangas wrote: > Fixed these, and did some more copy-editing on the comments. With that, > committed and backpatched. Thanks! Now that we avoid orphaned objects dependencies, I resumed working on Robert's concern about the TOCTOU window where a REVOKE could land between the original permission check and the dependency recording. Based on our discussion during PGConf.dev, PFA a new patch that uses the same approach as RangeVarGetRelidExtended(): record SharedInvalidMessageCounter at the time of the original aclcheck, then before locking compare the current counter to the saved value. If it changed, recheck permission before acquiring the lock. After the lock wait, if more invalidations arrived, release and retry. Remarks: The tracking array lives in a dedicated AclCheckTrackContext memory context (child of TopMemoryContext). The context is reset at the start of each top-level utility statement, which frees all prior allocations and provides clean lifetime management. Recording is gated by aclcheck_tracking_active, which is set to true only during top-level utility statement execution. This ensures DML and queries pay no cost. The flag is cleared both at normal completion of ProcessUtility and in AbortTransaction to handle the error path. The patch adds a test that would fail without the TOCTOU protection in place. Alternatives considered: - To avoid allocating memory for each statement, keep the array in TopMemoryContext and never free it (only resetting the count). But that left the high-water mark allocated for the lifetime of the backend. - Passing privilege info (roleId, mode) as extra arguments through the dependency recording APIs (recordDependencyOn, recordMultipleDependencies, etc.). It was discarded because expression-based dependencies (recordDependencyOnExpr, find_expr_references_walker) discover objects by walking expression trees: the caller never sees individual objects and cannot attach privilege info to them. That's why I believe the tracking approach that is in the attached sounds like a right approach. Bonus point, it would also help if we want to "ensure" that we always do the acl check prior the dependency recording: that would avoid cases like the one mentioned in [1] (for example, when we can create a view and then record a dependency based on a function we don't have exec privilege on). [1]: https://postgr.es/m/agsCqlLTytZCudMv%40bdtpg Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Jeff Davis <pgsql@j-davis.com> — 2026-06-03T18:08:13Z
On Mon, 2026-06-01 at 09:21 +0000, Bertrand Drouvot wrote: > Now that we avoid orphaned objects dependencies, I resumed working on > Robert's > concern about the TOCTOU window where a REVOKE could land between the > original > permission check and the dependency recording. > > Based on our discussion during PGConf.dev, PFA a new patch that uses > the same > approach as RangeVarGetRelidExtended(): record > SharedInvalidMessageCounter at the > time of the original aclcheck, then before locking compare the > current counter to > the saved value. If it changed, recheck permission before acquiring > the lock. > After the lock wait, if more invalidations arrived, release and > retry. RangeVarGetRelidExtended() coordinates three things: - name lookup - lock - ACL check whereas recheckAclAndLock() only coordinates the latter two. That means there can still be some strange failures, like: -- Session 1 BEGIN; DROP SCHEMA s2; -- Session 2 SET search_path=s2, s1; CREATE FUNCTION f() RETURNS INT LANGUAGE plpgsql AS $$ BEGIN RETURN 42; END; $$; -- Session 1 COMMIT; -- Session 2 ERROR: referenced schema was concurrently dropped even though no schema was actually referenced in the query, and a retry of the transaction successfully creates the function in s1. Is that expected? Regards, Jeff Davis -
Re: Avoid orphaned objects dependencies, take 3
Jeff Davis <pgsql@j-davis.com> — 2026-06-04T17:03:22Z
On Mon, 2026-06-01 at 09:21 +0000, Bertrand Drouvot wrote: > The tracking array lives in a dedicated AclCheckTrackContext memory > context > (child of TopMemoryContext). The context is reset at the start of > each > top-level utility statement, which frees all prior allocations and > provides > clean lifetime management. > > Recording is gated by aclcheck_tracking_active, which is set to true > only > during top-level utility statement execution. This ensures DML and > queries pay > no cost. The flag is cleared both at normal completion of > ProcessUtility and in > AbortTransaction to handle the error path. This could use some better high-level comments in the code. Something like: "DDL performs ACL checks on referenced objects before acquiring a lock on them. The lock is acquired much later, when recording dependencies. Track the ACL checks, so that we can re-check them after acquiring the lock. XXX: consider refactoring so that we perform the name lookup, acquire the lock, and check ACLs all in unison, like RangeVarGetRelidExtended()." Assuming I understand correctly. Regards, Jeff Davis
-
Re: Avoid orphaned objects dependencies, take 3
Jeff Davis <pgsql@j-davis.com> — 2026-06-04T17:17:57Z
On Wed, 2026-06-03 at 11:08 -0700, Jeff Davis wrote: > RangeVarGetRelidExtended() coordinates three things: > > - name lookup > - lock > - ACL check > > whereas recheckAclAndLock() only coordinates the latter two. Given that we don't do another name lookup, the object Oid doesn't change, and it's not obvious why we need a loop in this path. A sequence like: Earlier during DDL processing: 0. Name lookup and ACL check (and track ACLs) When recording dependencies: 1. Lock object 2. Check that it still exists, error if not 3. recheck tracked ACLs, error if failure could work too, right? I see why you might want to do the checks while not holding the lock, but it doesn't seem like a requirement (if the user doesn't have permissions it should fail quickly and release). In any case, it's worth a comment explaining why the loop is there. Regards, Jeff Davis
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-06-05T13:09:37Z
Hi, On Thu, Jun 04, 2026 at 10:03:22AM -0700, Jeff Davis wrote: > On Mon, 2026-06-01 at 09:21 +0000, Bertrand Drouvot wrote: > > The tracking array lives in a dedicated AclCheckTrackContext memory > > context > > (child of TopMemoryContext). The context is reset at the start of > > each > > top-level utility statement, which frees all prior allocations and > > provides > > clean lifetime management. > > > > Recording is gated by aclcheck_tracking_active, which is set to true > > only > > during top-level utility statement execution. This ensures DML and > > queries pay > > no cost. The flag is cleared both at normal completion of > > ProcessUtility and in > > AbortTransaction to handle the error path. > > This could use some better high-level comments in the code. Something > like: > > "DDL performs ACL checks on referenced objects before acquiring a lock > on them. The lock is acquired much later, when recording dependencies. > Track the ACL checks, so that we can re-check them after acquiring the > lock. Agreed, I just re-worded a bit to add some nuance in v24 attached, as: " may perform ACL checks on referenced objects without first holding a lock on them. In that case, the lock is acquired much later, when recording the dependencies. Track the ACL checks, so that we can re-check them after acquiring the lock while recording dependencies." > XXX: consider refactoring so that we perform the name lookup, > acquire the lock, and check ACLs all in unison, like > RangeVarGetRelidExtended()." I like the XXX, as I agree that what you mentioned in [1] is an area of improvement but not something directly linked to the TOCTOU issue that this patch is addressing. FWIW, with the scenario you described in [1]: - before 2fbb21170e9, the function would be linked to a non existing schema - with 2fbb21170e9, it produces "ERROR: referenced schema was concurrently dropped" the current patch just "keep" the post 2fbb21170e9 behavior. [1]: https://postgr.es/m/5315d15a42109297259d1a3264ad09e363eb98df.camel%40j-davis.com Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-06-05T13:12:34Z
Hi, On Thu, Jun 04, 2026 at 10:17:57AM -0700, Jeff Davis wrote: > Given that we don't do another name lookup, the object Oid doesn't > change, and it's not obvious why we need a loop in this path. > > A sequence like: > > Earlier during DDL processing: > 0. Name lookup and ACL check (and track ACLs) > > When recording dependencies: > 1. Lock object > 2. Check that it still exists, error if not > 3. recheck tracked ACLs, error if failure > > could work too, right? Yeah, that's a good point, thanks! > I see why you might want to do the checks while not holding the lock, > but it doesn't seem like a requirement (if the user doesn't have > permissions it should fail quickly and release). I agree that we can get rid of the loop and that's what v24 shared up-thread [1] is now implementing. The downside is that one could take a lock on an object he has no privileges on but then, as you said, it will be released shortly after. [1]: https://postgr.es/m/aiLKkTC6QBt8i35P%40bdtpg Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Jeff Davis <pgsql@j-davis.com> — 2026-06-08T21:55:19Z
On Fri, 2026-06-05 at 13:09 +0000, Bertrand Drouvot wrote: > the current patch /* * Look up a tracked aclcheck entry for the given object. * Returns true if found, filling in roleId, mode, and inval_count. * Searches from the end to find the most recent check (the one with the * freshest inval_count). What if two tracked ACL checks had different modes? Don't you need to do both checks or somehow combine the modes in the tracking entry? (I'm not sure if this is a practical problem or not.) Regards, Jeff Davis
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-06-09T06:47:06Z
Hi, On Mon, Jun 08, 2026 at 02:55:19PM -0700, Jeff Davis wrote: > On Fri, 2026-06-05 at 13:09 +0000, Bertrand Drouvot wrote: > > the current patch > > /* > * Look up a tracked aclcheck entry for the given object. > * Returns true if found, filling in roleId, mode, and inval_count. > * Searches from the end to find the most recent check (the one with > the > * freshest inval_count). > > What if two tracked ACL checks had different modes? Don't you need to > do both checks or somehow combine the modes in the tracking entry? > > (I'm not sure if this is a practical problem or not.) Humm, good point. I was focusing on a "full" role revoke but maybe what you describe could be a practical problem (in case of privilege revoke from the role). In order to be on the safe side of things, the attached now iterates through all matching entries (and not only the last one). It's still a flat array as its linear scan is O(n) in the number of tracked entries, but that's fine since a typical DDL statement tracks only a few objects. Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-06-09T18:04:20Z
Hi, On Tue, Jun 09, 2026 at 06:47:06AM +0000, Bertrand Drouvot wrote: > Hi, > > On Mon, Jun 08, 2026 at 02:55:19PM -0700, Jeff Davis wrote: > > On Fri, 2026-06-05 at 13:09 +0000, Bertrand Drouvot wrote: > > > the current patch > > > > /* > > * Look up a tracked aclcheck entry for the given object. > > * Returns true if found, filling in roleId, mode, and inval_count. > > * Searches from the end to find the most recent check (the one with > > the > > * freshest inval_count). > > > > What if two tracked ACL checks had different modes? Don't you need to > > do both checks or somehow combine the modes in the tracking entry? > > > > (I'm not sure if this is a practical problem or not.) > > Humm, good point. I was focusing on a "full" role revoke but maybe what you > describe could be a practical problem (in case of privilege revoke from the > role). > > In order to be on the safe side of things, the attached now iterates through all > matching entries (and not only the last one). It's still a flat array as its > linear scan is O(n) in the number of tracked entries, but that's fine since a > typical DDL statement tracks only a few objects. I just realized that the cfbot is complaining, let me fix it and come back with an updated patch. Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Jeff Davis <pgsql@j-davis.com> — 2026-06-09T21:44:10Z
Hi, On Tue, 2026-06-09 at 06:47 +0000, Bertrand Drouvot wrote: > In order to be on the safe side of things, the attached now iterates > through all > matching entries (and not only the last one). I don't think: if (context == PROCESS_UTILITY_TOPLEVEL) is quite right. With SPI, I bet you can end up in a situation where the branch fires twice (causing an early reset) or not at all (because the only call is PROCESS_UTLITY_QUERY). In any case, I don't think mixing the tracking entries between entirely different DDL commands is a good idea. If you execute DDL inside of SPI in its own subtransaction, and it inserts a tracking entry to recheck something, and then you roll back the subxact, you don't want the tracking entry to then cause the outer transaction to fail. I didn't test this, so if there's something preventing this kind of problem, let me know. We probably need to track where we are in the stack of ProcessUtility() calls and keep the tracking entries separate, and always remove entries from that level on return (or rollback). Regards, Jeff Davis
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-06-10T12:31:04Z
Hi, On Tue, Jun 09, 2026 at 02:44:10PM -0700, Jeff Davis wrote: > Hi, > > On Tue, 2026-06-09 at 06:47 +0000, Bertrand Drouvot wrote: > > In order to be on the safe side of things, the attached now iterates > > through all > > matching entries (and not only the last one). > > I don't think: > > if (context == PROCESS_UTILITY_TOPLEVEL) > > is quite right. > > In any case, I don't think mixing the tracking entries between entirely > different DDL commands is a good idea. If you execute DDL inside of SPI > in its own subtransaction, and it inserts a tracking entry to recheck > something, and then you roll back the subxact, you don't want the > tracking entry to then cause the outer transaction to fail. I didn't > test this, so if there's something preventing this kind of problem, let > me know. > > We probably need to track where we are in the stack of ProcessUtility() > calls and keep the tracking entries separate, and always remove entries > from that level on return (or rollback). I think you are right. I changed this in the attached so that aclcheck_tracked_count is saved on entry and restored on return of each ProcessUtility call, so that each nesting level's entries are kept separate. Also, the reset is now done whenever tracking is initialized (means at the outermost ProcessUtility call that starts tracking). I think that addresses your concerns and it also fixes the issues reported by the cfbot (that I mentioned up-thread). I also added more tests related to nested calls. Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-06-10T15:16:56Z
Hi, On Wed, Jun 10, 2026 at 12:31:04PM +0000, Bertrand Drouvot wrote: > Hi, > > On Tue, Jun 09, 2026 at 02:44:10PM -0700, Jeff Davis wrote: > > Hi, > > > > On Tue, 2026-06-09 at 06:47 +0000, Bertrand Drouvot wrote: > > > In order to be on the safe side of things, the attached now iterates > > > through all > > > matching entries (and not only the last one). > > > > I don't think: > > > > if (context == PROCESS_UTILITY_TOPLEVEL) > > > > is quite right. > > > > In any case, I don't think mixing the tracking entries between entirely > > different DDL commands is a good idea. If you execute DDL inside of SPI > > in its own subtransaction, and it inserts a tracking entry to recheck > > something, and then you roll back the subxact, you don't want the > > tracking entry to then cause the outer transaction to fail. I didn't > > test this, so if there's something preventing this kind of problem, let > > me know. > > > > We probably need to track where we are in the stack of ProcessUtility() > > calls and keep the tracking entries separate, and always remove entries > > from that level on return (or rollback). > > I think you are right. I changed this in the attached so that aclcheck_tracked_count > is saved on entry and restored on return of each ProcessUtility call, so that each > nesting level's entries are kept separate. > > Also, the reset is now done whenever tracking is initialized (means at the > outermost ProcessUtility call that starts tracking). > > I think that addresses your concerns and it also fixes the issues reported > by the cfbot (that I mentioned up-thread). > > I also added more tests related to nested calls. PFA a new version of v26, it adds a new test as compared to the v26 previously shared. Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Jeff Davis <pgsql@j-davis.com> — 2026-06-16T01:19:19Z
On Wed, 2026-06-10 at 15:16 +0000, Bertrand Drouvot wrote: > PFA a new version of v26, it adds a new test as compared to the v26 > previously > shared. I'd like to avoid adding lines to AbortTransaction(). Also, I think it might miss subtransaction aborts, which could be relevant in complex cases with SPI. Can you use a structure like: ProcessUtility() { TrackAclTable *prevTrackAclTable = CurrentTrackAclTable; /* allocates in CurrentMemoryContext */ CurrentTrackAclTable = NewTrackAclTable(); PG_TRY(); { ... rest of ProcessUtility ... } PG_FINALLY(); { FreeTrackAclTable(CurrentTrackAclTable); CurrentTrackAclTable = prevTrackAclTable; } PG_END_TRY(); } That would avoid the need to create a special memory context; you could just repalloc() the chunk allocated for the table. It would also mean you don't have to track the stack frames manually with a counter, just use a local variable. Also, are you sure that the two call sites for aclcheck_track_record() are enough? Or do we need checks in e.g. pg_attribute_aclcheck() as well? Regards, Jeff Davis -
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-06-16T10:09:48Z
Hi, On Mon, Jun 15, 2026 at 06:19:19PM -0700, Jeff Davis wrote: > On Wed, 2026-06-10 at 15:16 +0000, Bertrand Drouvot wrote: > > PFA a new version of v26, it adds a new test as compared to the v26 > > previously > > shared. > > I'd like to avoid adding lines to AbortTransaction(). Also, I think it > might miss subtransaction aborts, which could be relevant in complex > cases with SPI. > > Can you use a structure like: > > ProcessUtility() > { > TrackAclTable *prevTrackAclTable = CurrentTrackAclTable; > /* allocates in CurrentMemoryContext */ > CurrentTrackAclTable = NewTrackAclTable(); > > PG_TRY(); > { > ... rest of ProcessUtility ... > } > PG_FINALLY(); > { > FreeTrackAclTable(CurrentTrackAclTable); > CurrentTrackAclTable = prevTrackAclTable; > } > PG_END_TRY(); > } > > That would avoid the need to create a special memory context; you could > just repalloc() the chunk allocated for the table. It would also mean > you don't have to track the stack frames manually with a counter, just > use a local variable. The advantage of v26 is that it also checked for the outer while checking the nested (that way a revoke that impacted the outer would fail sooner). That said, I like your idea: it's more readable and probably less error-prone (don't use global variable in multiple places and a dedicated counter for nested checks). So, the attached implements it that way. It's in 0001. The reason for multiple sub-patches: > Also, are you sure that the two call sites for aclcheck_track_record() > are enough? Or do we need checks in e.g. pg_attribute_aclcheck() as > well? Currently, the only caller of pg_attribute_aclcheck_ext() that also records dependencies on the checked object is checkFkeyPermissions(), which already holds ShareRowExclusiveLock on the referenced table. While ShareRowExclusiveLock prevents direct REVOKE on the table, it does not prevent role membership revokes. So 0003 adds pg_attribute_aclcheck_ext to ACL tracking for dependency recording, which also protects against new DDL callers. The remaining aclcheck functions (pg_parameter_aclcheck and pg_largeobject_aclcheck_snapshot) do not need tracking: pg_parameter_aclcheck checks GUC parameters which are not dependency targets, and pg_largeobject_aclcheck_snapshot is only called from inv_open() and has_largeobject_privilege(), neither of which records dependencies. That said, while looking at checkFkeyPermissions(), I realized that we have a corner case in 0001. Indeed, checkFkeyPermissions() first tries pg_class_aclcheck() (which fails for column-level grants), then falls through to pg_attribute_aclcheck(). But as aclcheck_track_record() is called for both successful and failed checks, then the tracked failed entry could trigger a 'permission denied' in recheckAcl() if catalog invalidations arrived between the check and dependency recording. 0002: fixes it by moving aclcheck_track_record() to after the permission check succeeds in object_aclcheck_ext() and pg_class_aclcheck_ext(). Indeed, there is no need to track failed permission checks. It also adds a test that reproduces the issue by injecting invalidations while the FK creation is paused after the failed table-level check. The test would fail without the fix in 0002 applied. 0003: adds pg_attribute_aclcheck_ext to ACL tracking for dependency recording. It adds an attnum field to AclCheckEntry so that recheckAcl() can distinguish column-level checks from table-level checks and call the appropriate recheck function. InvalidAttrNumber means whole-object check. The sub-patches are to ease the review. They should probably be merged before being pushed. Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com -
Re: Avoid orphaned objects dependencies, take 3
Jeff Davis <pgsql@j-davis.com> — 2026-06-16T19:14:12Z
On Tue, 2026-06-16 at 10:09 +0000, Bertrand Drouvot wrote: > 0002: fixes it by moving aclcheck_track_record() to after the > permission check > succeeds in object_aclcheck_ext() and pg_class_aclcheck_ext(). > Indeed, there is > no need to track failed permission checks. IIUC, this is necessary for correctness. If an ACL failure doesn't cause a transaction abort, then there's a danger that we cause the transaction to fail that should have succeeded. So the ACL tracking needs to be precise: we can't track an ACL check unless a failure always causes transaction abort; and we must track an ACL check if it would cause a transaction abort. Right? Regards, Jeff Davis
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-06-17T05:44:47Z
Hi, On Tue, Jun 16, 2026 at 12:14:12PM -0700, Jeff Davis wrote: > On Tue, 2026-06-16 at 10:09 +0000, Bertrand Drouvot wrote: > > 0002: fixes it by moving aclcheck_track_record() to after the > > permission check > > succeeds in object_aclcheck_ext() and pg_class_aclcheck_ext(). > > Indeed, there is > > no need to track failed permission checks. > > IIUC, this is necessary for correctness. If an ACL failure doesn't > cause a transaction abort, then there's a danger that we cause the > transaction to fail that should have succeeded. Exactly, because we'd recheck an "harmless" failed ACL check and then produce an error. > So the ACL tracking needs to be precise: we can't track an ACL check > unless a failure always causes transaction abort; and we must track an > ACL check if it would cause a transaction abort. Right? I would say: we just need to track (and recheck) ACL checks that succeeded. I think that there is no reason to recheck (and so to record) a failed ACL as what we are dealing with here is the TOCTOU window. Re-checking a failed ACL check would handle cases when a GRANT has been given during the TOCTOU window which is not useful (for our protection goal) compared to re-checking a REVOKE during the TOCTOU window (as the latter would record a dependency on an object we don't have permission on). Doing so, as proposed in 0002, allows us to fix the "re-check a harmless failed ACL bug" (demonstrated by the added test) and still protect us for REVOKE during the TOCTOU window. Thoughts? Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Jeff Davis <pgsql@j-davis.com> — 2026-06-18T23:21:33Z
On Wed, 2026-06-17 at 05:44 +0000, Bertrand Drouvot wrote: > > IIUC, this is necessary for correctness. If an ACL failure doesn't > > cause a transaction abort, then there's a danger that we cause the > > transaction to fail that should have succeeded. > > Exactly, because we'd recheck an "harmless" failed ACL check and then > produce > an error. > > > So the ACL tracking needs to be precise: we can't track an ACL > > check > > unless a failure always causes transaction abort; and we must track > > an > > ACL check if it would cause a transaction abort. Right? > > I would say: we just need to track (and recheck) ACL checks that > succeeded. IIUC, we cannot have false positives (tracking ACL checks that wouldn't have caused an abort) nor can we have false negatives (missing an ACL check that could cause an abort). It's hard for me to convince myself that we got all the cases right; and if we have, that they won't be broken in the future. For instance, I just realized that something else I'm working on is related: https://www.postgresql.org/message-id/5c629d2455946ad2fde3c184f64ea2c323ef2133.camel@j-davis.com It does an ACL check inside a subtransaction, where the parent transaction is a DDL statement. It happens to be a DROP statement, so it's not recording new dependencies, so I don't think it breaks your tracking mechanism, but it's too close for comfort. We could keep the transaction ID in the tracking record, and ignore entries from an aborted subxact. But it's getting fairly complex and delicate. Thoughts? Regards, Jeff Davis
-
Re: Avoid orphaned objects dependencies, take 3
Jeff Davis <pgsql@j-davis.com> — 2026-06-19T02:13:38Z
On Thu, 2026-06-18 at 16:21 -0700, Jeff Davis wrote: > IIUC, we cannot have false positives (tracking ACL checks that > wouldn't > have caused an abort) nor can we have false negatives (missing an ACL > check that could cause an abort). Idea: what if we check for changes in ACLs on the object, rather than whether it passes the check or not? Then, if track an ACL check that wouldn't actually cause a failure, then it still might be acceptable to throw an error if the ACL changes. Still some details to sort out, so this is just an idea. Regards, Jeff Davis
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-06-19T13:28:21Z
Hi, On Thu, Jun 18, 2026 at 04:21:33PM -0700, Jeff Davis wrote: > On Wed, 2026-06-17 at 05:44 +0000, Bertrand Drouvot wrote: > > > IIUC, this is necessary for correctness. If an ACL failure doesn't > > > cause a transaction abort, then there's a danger that we cause the > > > transaction to fail that should have succeeded. > > > > Exactly, because we'd recheck an "harmless" failed ACL check and then > > produce > > an error. > > > > > So the ACL tracking needs to be precise: we can't track an ACL > > > check > > > unless a failure always causes transaction abort; and we must track > > > an > > > ACL check if it would cause a transaction abort. Right? > > > > I would say: we just need to track (and recheck) ACL checks that > > succeeded. > > IIUC, we cannot have false positives (tracking ACL checks that wouldn't > have caused an abort) nor can we have false negatives (missing an ACL > check that could cause an abort). Right. > It's hard for me to convince myself that we got all the cases right; > and if we have, that they won't be broken in the future. > > For instance, I just realized that something else I'm working on is > related: > > https://www.postgresql.org/message-id/5c629d2455946ad2fde3c184f64ea2c323ef2133.camel@j-davis.com > > It does an ACL check inside a subtransaction, where the parent > transaction is a DDL statement. It happens to be a DROP statement, so > it's not recording new dependencies, so I don't think it breaks your > tracking mechanism, but it's too close for comfort. I see, I don't think we have this pattern currently but yeah we may have it in the future (and our current tracking mechanism would probably fail in such a case). > We could keep the transaction ID in the tracking record, and ignore > entries from an aborted subxact. But it's getting fairly complex and > delicate. Agreed. We could use RegisterSubXactCallback to save/restore the entry count on subtransaction abort, but as you say it adds complexity. Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-06-19T13:36:45Z
Hi, On Thu, Jun 18, 2026 at 07:13:38PM -0700, Jeff Davis wrote: > On Thu, 2026-06-18 at 16:21 -0700, Jeff Davis wrote: > > IIUC, we cannot have false positives (tracking ACL checks that > > wouldn't > > have caused an abort) nor can we have false negatives (missing an ACL > > check that could cause an abort). > > Idea: what if we check for changes in ACLs on the object, rather than > whether it passes the check or not? > > Then, if track an ACL check that wouldn't actually cause a failure, > then it still might be acceptable to throw an error if the ACL changes. > Still some details to sort out, so this is just an idea. Yeah, I think I do prefer this idea. As you say, that could cause an error even if the ACL change does not REVOKE anything on this object (say the ACL change is a GRANT), but that should be rare in practice and probably much simpler to reason about that way. But I don't think tracking ACL changes would be enough though. I think we would also need to track ROLE changes. So what about? - Save a copy of the object's ACL and compare at recheck time: If not the same, then error out. - Save the ROLE membership and compare at recheck time. If not the same, then error out. That way we cover both parts: the object's ACL and the ROLE membership. That's just a high level idea, I can move forward and try to implement it. Thoughts? Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com
-
Re: Avoid orphaned objects dependencies, take 3
Bertrand Drouvot <bertranddrouvot.pg@gmail.com> — 2026-06-25T11:10:58Z
Hi, On Fri, Jun 19, 2026 at 01:28:21PM +0000, Bertrand Drouvot wrote: > Hi, > > On Thu, Jun 18, 2026 at 04:21:33PM -0700, Jeff Davis wrote: > > > We could keep the transaction ID in the tracking record, and ignore > > entries from an aborted subxact. But it's getting fairly complex and > > delicate. As discussed off-list, let's move forward with this proposal (as the save a copy of the object's ACL + ROLE membership mentioned in [1] would be more complex and fragile). So PFA the new version doing so. It's the same patch series as the previous one except that it adds: 0004: Discard ACL tracking entries from aborted subtransactions It stores SubTransactionId in each AclCheckEntry at recording time. On subtransaction abort, AtEOSubXact_AclTrack() discards entries whose subxid is >= the aborting subtransaction's ID. The code is pretty simple but I suspect that adding a test would probably need to create a brand new module (say test_acltrack) with C code forcing recordDependencyOn() and BeginInternalSubTransaction()) and I'm not sure it's worth it. Indeed, I think the test would need to do things similar to what you shared in [2]. The patch is split to ease the review but should probably be merged as a single patch. [1]: https://postgr.es/m/ajVF7YWf%2Bpxs4cOf%40bdtpg [2]: https://postgr.es/m/5c629d2455946ad2fde3c184f64ea2c323ef2133.camel@j-davis.com Regards, -- Bertrand Drouvot PostgreSQL Contributors Team RDS Open Source Databases Amazon Web Services: https://aws.amazon.com