Обсуждение: Triggers, again.. ;-)
Hello,
I have asked about this before, but I just haven't been able to get
anywhere with it yet.. I'm hoping someone can help me?
Here is my original function and trigger:
CREATE OR REPLACE VIEW items_category AS select count(*) AS count
,b.category,nlevel(b.category) AS level,
subpath(b.category,0,nlevel(b.category)-1) as parent,
b.head_title,b.cat_title,b.subcat_title FROM items a,category b
WHERE b.category @> a.category
AND a.status = 'open'
GROUP BY b.category,b.head_title, b.cat_title, b.subcat_title
ORDER BY b.category;
-----------
CREATE OR REPLACE FUNCTION category_mv_refresh_row(ltree[]) RETURNS VOID
SECURITY DEFINER
LANGUAGE 'plpgsql' AS '
BEGIN
DELETE FROM category_mv WHERE category @> $1;
INSERT INTO category_mv SELECT * FROM items_category WHERE category @> $1;
RETURN;
END
';
-----------
CREATE OR REPLACE FUNCTION update_ut() RETURNS TRIGGER
SECURITY DEFINER LANGUAGE 'plpgsql' AS '
BEGIN
IF OLD.category = NEW.category THEN
PERFORM category_mv_refresh_row(NEW.category);
ELSE
PERFORM category_mv_refresh_row(OLD.category);
PERFORM category_mv_refresh_row(NEW.category);
END IF;
RETURN NULL;
END
';
-----------
CREATE TRIGGER category_mv_ut AFTER UPDATE ON items
FOR EACH ROW EXECUTE PROCEDURE update_ut();
Now what I need is a way for category_mv_refresh_row to be made optional
during the execution of update_ut, or somewhere. I thought about
changing update_ut to something like:
CREATE OR REPLACE FUNCTION update_ut() RETURNS TRIGGER
SECURITY DEFINER LANGUAGE 'plpgsql' AS '
BEGIN
IF skip_update IS NOT NULL THEN
IF OLD.category = NEW.category THEN
PERFORM category_mv_refresh_row(NEW.category);
ELSE
PERFORM category_mv_refresh_row(OLD.category);
PERFORM category_mv_refresh_row(NEW.category);
END IF;
END IF;
RETURN NULL;
END
';
.. then somehow setting "skip_update" during my transaction.. Something like:
BEGIN
skip_update boolean := 't';
... insert rows
... update materialized view table
COMMIT;
But, I can't set skip_update like that I guess. Does anyone have any
idea how I might go about doing this?.. The reason is, I have a function
that updates the *entire* materialized view that takes about 15 seconds
to execute, but calling category_mv_refresh_row takes about 2 seconds.
When I am inserting thousands of rows, this results in an enormous
additional load on the database server (and takes far longer to execute).
Out of curiosity, is "DROP TRIGGER" transaction safe?... I mean, could I do:
BEGIN
DROP TRIGGER category_mv_ut;
... insert rows
... update materialized view table
CREATE TRIGGER category_mv_ut AFTER UPDATE ON items
FOR EACH ROW EXECUTE PROCEDURE update_ut();
COMMIT;
.. without other sessions being affected?
I hope I've explained this well enough!
Thanks as always!
- Greg
Greg wrote: > is "DROP TRIGGER" transaction safe?... I mean, could I do: > > BEGIN > DROP TRIGGER category_mv_ut; > ... insert rows > ... update materialized view table > CREATE TRIGGER category_mv_ut AFTER UPDATE ON items > FOR EACH ROW EXECUTE PROCEDURE update_ut(); > COMMIT; > > .. without other sessions being affected? This is exactly what I do to avoid matview bulk-update performance issues. --Phil.
>Greg wrote: > > is "DROP TRIGGER" transaction safe?... I mean, could I do: > > > > BEGIN > > DROP TRIGGER category_mv_ut; > > ... insert rows > > ... update materialized view table > > CREATE TRIGGER category_mv_ut AFTER UPDATE ON items > > FOR EACH ROW EXECUTE PROCEDURE update_ut(); > > COMMIT; > > > > .. without other sessions being affected? > >This is exactly what I do to avoid matview bulk-update performance issues. > >--Phil. Hello, Are there any issues I should know about with this method?.... Will other transactions be able to take place during this?... Any blocking issues?..... I've never attempted anything like this and it seems sort-of scary to me (at the very least, applying an awfully big hammer to the problem). Thanks for your help! - Greg
Greg asked: > > is "DROP TRIGGER" transaction safe?... I mean, could I do: > > > > BEGIN > > DROP TRIGGER category_mv_ut; > > ... insert rows > > ... update materialized view table > > CREATE TRIGGER category_mv_ut AFTER UPDATE ON items > > FOR EACH ROW EXECUTE PROCEDURE update_ut(); > > COMMIT; > > > > .. without other sessions being affected? I replied: > This is exactly what I do to avoid matview bulk-update > performance issues. Greg then asked: > Are there any issues I should know about with this method?.... Will > other transactions be able to take place during this?... Any blocking > issues?..... I've never attempted anything like this and it seems > sort-of scary to me (at the very least, applying an awfully big hammer > to the problem). I am not an expert, but I use this technique. Maybe other users will have some observations. But as I perceive it, the triggers currently in force are recorded in a (system) table somewhere and that table has the same well-behaved transactional semantics as other tables. So, as far as other transactions are concerned, the triggers are unchanged and this is entirely safe. My experience suggests that it is not inefficient. As for locking, my guess is that another transaction that was also trying to create or drop triggers could block especially if it was trying to change the same triggers, but other operations will be fine. It seems less scary when you think of metadata as just being the content of more tables, rather than something special. Hopefully someone will correct me if it is worse than this! --Phil.
Phil Endecott <spam_from_postgresql_general@chezphil.org> writes:
> It seems less scary when you think of metadata as just being the content
> of more tables, rather than something special.
PG does just fine with handling metadata changes transactionally.
However, most operations that affect a table's schema at all will take
an exclusive lock on the table, thereby blocking out other operations
on the table until the schema-altering operation commits. This could be
pretty annoying if you have lots of concurrent activity that needs to
keep going --- in particular the proposed approach would lock out access
to the underlying table for as long as it takes to update the
materialized view, since the DROP TRIGGER would take that exclusive lock
and it'd be held till end of transaction. If that's OK then there's
nothing wrong with doing it that way.
regards, tom lane
Tom Lane wrote: > Phil Endecott <spam_from_postgresql_general@chezphil.org> writes: > >>It seems less scary when you think of metadata as just being the content >>of more tables, rather than something special. > > > PG does just fine with handling metadata changes transactionally. > However, most operations that affect a table's schema at all will take > an exclusive lock on the table, thereby blocking out other operations > on the table until the schema-altering operation commits. This could be > pretty annoying if you have lots of concurrent activity that needs to > keep going --- in particular the proposed approach would lock out access > to the underlying table for as long as it takes to update the > materialized view, since the DROP TRIGGER would take that exclusive lock > and it'd be held till end of transaction. If that's OK then there's > nothing wrong with doing it that way. Hi Tom, I was hoping that my positive-sounding message would flush out any problems... I would understand this if I were doing an "ALTER TABLE", for example. But does adding or removing a trigger really count as "schema-altering"? --Phil.
Phil Endecott <spam_from_postgresql_general@chezphil.org> writes:
> I would understand this if I were doing an "ALTER TABLE", for example.
> But does adding or removing a trigger really count as "schema-altering"?
[ shrug... ] Hard to say. Probably depends a lot on what the trigger
does. I suppose we could at least reduce the lock from AccessExclusive
to Exclusive, which would allow concurrent readers (since SELECT by
definition doesn't fire any triggers).
No one's really gone through and taken a hard look at whether every
single DDL operation needs the maximum lock ...
regards, tom lane
> Phil Endecott <spam_from_postgresql_general@chezphil.org> writes: >> I would understand this if I were doing an "ALTER TABLE", for example. >> But does adding or removing a trigger really count as "schema-altering"? > > [ shrug... ] Hard to say. Probably depends a lot on what the trigger > does. I suppose we could at least reduce the lock from AccessExclusive > to Exclusive, which would allow concurrent readers (since SELECT by > definition doesn't fire any triggers). > > No one's really gone through and taken a hard look at whether every > single DDL operation needs the maximum lock ... > > regards, tom lane > FYI, I did experience locking issues (my attempt to drop the trigger resulted in other sessions blocking)... In any event, I found a solution which at first seemed stupid, but works so great I thought I'd share it... All I did was added an extra column to my table "batch_process". Then in the trigger do something like: IF NEW.batch_process THEN NEW.batch_process := NULL; RETURN NULL; END IF; .. whatever the rest of transaction is Then when doing an insert, just: INSERT INTO table (..., batch_process) VALUES (..., 't') when you want the trigger not to fire... What do you guys think?.. Is this dumb?.... - Greg