Thread: Triggers, again.. ;-)

Triggers, again.. ;-)

From
"Net Virtual Mailing Lists"
Date:
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


Re: Triggers, again.. ;-)

From
Phil Endecott
Date:
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.







Re: Triggers, again.. ;-)

From
"Net Virtual Mailing Lists"
Date:
>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


Re: Triggers, again.. ;-)

From
Phil Endecott
Date:
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.



Re: Triggers, again.. ;-)

From
Tom Lane
Date:
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

Re: Triggers, again.. ;-)

From
Phil Endecott
Date:
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.


Re: Triggers, again.. ;-)

From
Tom Lane
Date:
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

Re: Triggers, again.. ;-)

From
mailinglists@net-virtual.com
Date:
> 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