Thread: example of really weird caching (or whatever) problem

example of really weird caching (or whatever) problem

From
"Brandon Metcalf"
Date:
Here is an example of the caching problem I described yesterday in a
post.  I have the following tables:

  db=> \d bmetcalf.foo1;
      Table "bmetcalf.foo1"
    Column   | Type | Modifiers
  -----------+------+-----------
   country   | text |
   replicaID | text |
   host      | text |
   replica   | text |
  Triggers:
      foo_trig BEFORE DELETE ON foo1 FOR EACH ROW EXECUTE PROCEDURE foo_func()

  db=> \d bmetcalf.foo2;
      Table "bmetcalf.foo2"
    Column   | Type | Modifiers
  -----------+------+-----------
   vobID     | text |
   replicaID | text |
  Triggers:
      foo_trig BEFORE DELETE ON foo2 FOR EACH ROW EXECUTE PROCEDURE foo_func()

And here is the SQL for the function and trigger definitions:

  CREATE OR REPLACE FUNCTION bmetcalf.foo_func()
  RETURNS TRIGGER
  LANGUAGE plperlu
  AS $$
  require 5.8.0;

  my $table = $_TD->{relname};
  warn "table name is $table";
  warn "BWM before call: table name is $table";

  do_delete();

  return 'SKIP';

  sub do_delete {
      warn "BWM in call: table name is $table";
  }

  $$;

  DROP TRIGGER foo_trig ON bmetcalf.foo1;

  DROP TRIGGER foo_trig ON bmetcalf.foo2;

  CREATE TRIGGER foo_trig BEFORE DELETE ON bmetcalf.foo1 FOR EACH ROW
    EXECUTE PROCEDURE bmetcalf.foo_func();

  CREATE TRIGGER foo_trig BEFORE DELETE ON bmetcalf.foo2 FOR EACH ROW
    EXECUTE PROCEDURE bmetcalf.foo_func();

Now, watch what happens when I execute a DELETE on bmetcalf.foo1 after
a DELETE on bmetcalf.foo2 .  The table name when this trigger fires
for bmetcalf.foo1 changes:

  db=# delete from bmetcalf.foo1 where "replicaID" = '11';
  ...
  db=# delete from bmetcalf.foo1 where "replicaID" = '11';
  NOTICE:  table name is foo1 at line 6.

  NOTICE:  BWM before call: table name is foo1 at line 8.

  NOTICE:  BWM in call: table name is foo2 at line 15.

  DELETE 0

If I close my session and reconnect and only run the DELETE against
bmetcalf.foo1, the problem doesn't appear:

  db=# delete from bmetcalf.foo1 where "replicaID" = '11';
  NOTICE:  table name is foo1 at line 6.

  NOTICE:  BWM before call: table name is foo1 at line 8.

  NOTICE:  BWM in call: table name is foo1 at line 15.

  DELETE 0

Any ideas?

--
Brandon

Re: example of really weird caching (or whatever) problem

From
Richard Huxton
Date:
Brandon Metcalf wrote:
> Here is an example of the caching problem I described yesterday in a
> post.  I have the following tables:

> And here is the SQL for the function and trigger definitions:
>
>   CREATE OR REPLACE FUNCTION bmetcalf.foo_func()
>   RETURNS TRIGGER
>   LANGUAGE plperlu
>   AS $$
>   require 5.8.0;
>
>   my $table = $_TD->{relname};
>   warn "table name is $table";
>   warn "BWM before call: table name is $table";
>
>   do_delete();
>
>   return 'SKIP';
>
>   sub do_delete {
>       warn "BWM in call: table name is $table";
>   }
>
>   $$;

Umm - you've got a named closure inside your funciton here - "sub
do_delete".

It's warning isn't using the "my $table" variable, it's using a
localised copy of that variable. That gets defined when the sub is
defined, which will be on the first call (my $table=foo2) and still
exists, unchanged on the second call (my $table=foo1).

Warning - can't remember if I'm using the right terminology on the
above, although I think it's the right diagnosis.

--
  Richard Huxton
  Archonet Ltd

Re: example of really weird caching (or whatever) problem

From
"Brandon Metcalf"
Date:
d == dev@archonet.com writes:

 d> Brandon Metcalf wrote:
 d> > Here is an example of the caching problem I described yesterday in a
 d> > post.  I have the following tables:

 d> > And here is the SQL for the function and trigger definitions:
 d> >
 d> >   CREATE OR REPLACE FUNCTION bmetcalf.foo_func()
 d> >   RETURNS TRIGGER
 d> >   LANGUAGE plperlu
 d> >   AS $$
 d> >   require 5.8.0;
 d> >
 d> >   my $table = $_TD->{relname};
 d> >   warn "table name is $table";
 d> >   warn "BWM before call: table name is $table";
 d> >
 d> >   do_delete();
 d> >
 d> >   return 'SKIP';
 d> >
 d> >   sub do_delete {
 d> >       warn "BWM in call: table name is $table";
 d> >   }
 d> >
 d> >   $$;

 d> Umm - you've got a named closure inside your funciton here - "sub
 d> do_delete".

 d> It's warning isn't using the "my $table" variable, it's using a
 d> localised copy of that variable. That gets defined when the sub is
 d> defined, which will be on the first call (my $table=foo2) and still
 d> exists, unchanged on the second call (my $table=foo1).


Yep, it seems that's the problem.  If I pass in $table and use a
lexical variable defined inside do_delete(), the problem goes away.
So, this is where my understanding of how triggers work lacks.  For a
given session, each execution of a trigger isn't completely
independent?

--
Brandon

Re: example of really weird caching (or whatever) problem

From
Richard Huxton
Date:
Brandon Metcalf wrote:
> Yep, it seems that's the problem.  If I pass in $table and use a
> lexical variable defined inside do_delete(), the problem goes away.
> So, this is where my understanding of how triggers work lacks.  For a
> given session, each execution of a trigger isn't completely
> independent?

Nothing to do with triggers - it's all to do with your Perl code.

#!/usr/bin/perl

sub foo {
    my $x = shift;
    print "foo x = $x\n";
    bar();
    return;

    sub bar {
        print "bar x = $x\n";
    }
}

foo(1);
foo(2);
exit;

$ ./perl_example.pl
foo x = 1
bar x = 1
foo x = 2
bar x = 1

If you use warnings it'll tell you about it too with this example.

--
  Richard Huxton
  Archonet Ltd

Re: example of really weird caching (or whatever) problem

From
"Brandon Metcalf"
Date:
d == dev@archonet.com writes:

 d> Brandon Metcalf wrote:
 d> > Yep, it seems that's the problem.  If I pass in $table and use a
 d> > lexical variable defined inside do_delete(), the problem goes away.
 d> > So, this is where my understanding of how triggers work lacks.  For a
 d> > given session, each execution of a trigger isn't completely
 d> > independent?

 d> Nothing to do with triggers - it's all to do with your Perl code.


I respectfully disagree because if I don't execute a DELETE on foo2 as
shown in my original email, the problem doesn't occur.  Somewhere in
the trigger execution it's remembering the first table on which the
trigger fired.  So, the information about foo2 is coming from
somewhere and it's in the Perl code.  In other words, I performing two
different DELETEs which cause two different invocations of the same
trigger.


 d> #!/usr/bin/perl

 d> sub foo {
 d>     my $x = shift;
 d>     print "foo x = $x\n";
 d>     bar();
 d>     return;

 d>     sub bar {
 d>         print "bar x = $x\n";
 d>     }
 d> }

 d> foo(1);
 d> foo(2);
 d> exit;

 d> $ ./perl_example.pl
 d> foo x = 1
 d> bar x = 1
 d> foo x = 2
 d> bar x = 1

 d> If you use warnings it'll tell you about it too with this example.



--
Brandon

Re: example of really weird caching (or whatever) problem

From
"Brandon Metcalf"
Date:
b == bmetcalf@cash.us.nortel.com writes:

 b> d == dev@archonet.com writes:

 b>  d> Brandon Metcalf wrote:
 b>  d> > Yep, it seems that's the problem.  If I pass in $table and use a
 b>  d> > lexical variable defined inside do_delete(), the problem goes away.
 b>  d> > So, this is where my understanding of how triggers work lacks.  For a
 b>  d> > given session, each execution of a trigger isn't completely
 b>  d> > independent?

 b>  d> Nothing to do with triggers - it's all to do with your Perl code.


 b> I respectfully disagree because if I don't execute a DELETE on foo2 as
 b> shown in my original email, the problem doesn't occur.  Somewhere in
 b> the trigger execution it's remembering the first table on which the
 b> trigger fired.  So, the information about foo2 is coming from
 b> somewhere and it's in the Perl code.  In other words, I performing two

That should read it's _not_ in the Perl code.


 b> different DELETEs which cause two different invocations of the same
 b> trigger.


 b>  d> #!/usr/bin/perl

 b>  d> sub foo {
 b>  d>     my $x = shift;
 b>  d>     print "foo x = $x\n";
 b>  d>     bar();
 b>  d>     return;

 b>  d>     sub bar {
 b>  d>         print "bar x = $x\n";
 b>  d>     }
 b>  d> }

 b>  d> foo(1);
 b>  d> foo(2);
 b>  d> exit;

 b>  d> $ ./perl_example.pl
 b>  d> foo x = 1
 b>  d> bar x = 1
 b>  d> foo x = 2
 b>  d> bar x = 1

 b>  d> If you use warnings it'll tell you about it too with this example.





--
Brandon

Re: example of really weird caching (or whatever) problem

From
Richard Huxton
Date:
Brandon Metcalf wrote:
> d == dev@archonet.com writes:
>
>  d> Brandon Metcalf wrote:
>  d> > Yep, it seems that's the problem.  If I pass in $table and use a
>  d> > lexical variable defined inside do_delete(), the problem goes away.
>  d> > So, this is where my understanding of how triggers work lacks.  For a
>  d> > given session, each execution of a trigger isn't completely
>  d> > independent?
>
>  d> Nothing to do with triggers - it's all to do with your Perl code.
>
>
> I respectfully disagree because if I don't execute a DELETE on foo2 as
> shown in my original email, the problem doesn't occur.

Of course not.

>  Somewhere in
> the trigger execution it's remembering the first table on which the
> trigger fired.

Yes. in your "sub do_delete" with it's local variable.

> So, the information about foo2 is coming from
> somewhere and it's in the Perl code.

Yes, your local copy of $table in do_delete.

> In other words, I performing two
> different DELETEs which cause two different invocations of the same
> trigger.

You've written your code such that do_delete has a local copy of $table.
  In fact, the way it actually works iirc is that when you exit the
trigger function "my $table" goes out of scope and vanishes, but the
"$table" in do_delete doesn't vanish and persists from call to call. You
might call this a static variable in C terms.

>  d> #!/usr/bin/perl
>
>  d> sub foo {
>  d>     my $x = shift;
>  d>     print "foo x = $x\n";
>  d>     bar();
>  d>     return;
>
>  d>     sub bar {
>  d>         print "bar x = $x\n";
>  d>     }
>  d> }
>
>  d> foo(1);
>  d> foo(2);
>  d> exit;

This code mirrors _exactly_ what is happening with your trigger. On the
first call to foo $x is set to 1, on the second it's set to 2. That
doesn't affect "sub bar" though because its copy of $x is still the one
from the first call.

Maybe the following makes it clearer:
#!/usr/bin/perl

sub foo {
        my $x = shift;
        print "foo x = $x, ";
        bar();
        return;

        sub bar {
                print "bar x = $x\n";
                $x--;
        }
}

for my $i (1..5) { foo($i); }
exit;

$ ./perl_example.pl
foo x = 1, bar x = 1
foo x = 2, bar x = 0
foo x = 3, bar x = -1
foo x = 4, bar x = -2
foo x = 5, bar x = -3

The two $x variables go their separate ways and the one in "bar" doesn't
go out of scope at the end of the function.

--
  Richard Huxton
  Archonet Ltd

Re: example of really weird caching (or whatever) problem

From
"Brandon Metcalf"
Date:
d == dev@archonet.com writes:

 d> Brandon Metcalf wrote:
 d> > d == dev@archonet.com writes:
 d> >
 d> >  d> Brandon Metcalf wrote:
 d> >  d> > Yep, it seems that's the problem.  If I pass in $table and use a
 d> >  d> > lexical variable defined inside do_delete(), the problem goes away.
 d> >  d> > So, this is where my understanding of how triggers work lacks.  For a
 d> >  d> > given session, each execution of a trigger isn't completely
 d> >  d> > independent?
 d> >
 d> >  d> Nothing to do with triggers - it's all to do with your Perl code.
 d> >
 d> >
 d> > I respectfully disagree because if I don't execute a DELETE on foo2 as
 d> > shown in my original email, the problem doesn't occur.

 d> Of course not.

 d> >  Somewhere in
 d> > the trigger execution it's remembering the first table on which the
 d> > trigger fired.

 d> Yes. in your "sub do_delete" with it's local variable.

 d> > So, the information about foo2 is coming from
 d> > somewhere and it's in the Perl code.

 d> Yes, your local copy of $table in do_delete.

 d> > In other words, I performing two
 d> > different DELETEs which cause two different invocations of the same
 d> > trigger.

 d> You've written your code such that do_delete has a local copy of $table.
 d>   In fact, the way it actually works iirc is that when you exit the
 d> trigger function "my $table" goes out of scope and vanishes, but the
 d> "$table" in do_delete doesn't vanish and persists from call to call. You
 d> might call this a static variable in C terms.


OK.  I understand the Perl part of what is going on.  What I don't
understand is why $table in do_delete() hangs around.  It seems this
is more a characteristic of how triggers work in pgsql.  At any rate,
I appreciate the input since it provides me with a fix.


 d> >  d> #!/usr/bin/perl
 d> >
 d> >  d> sub foo {
 d> >  d>     my $x = shift;
 d> >  d>     print "foo x = $x\n";
 d> >  d>     bar();
 d> >  d>     return;
 d> >
 d> >  d>     sub bar {
 d> >  d>         print "bar x = $x\n";
 d> >  d>     }
 d> >  d> }
 d> >
 d> >  d> foo(1);
 d> >  d> foo(2);
 d> >  d> exit;

 d> This code mirrors _exactly_ what is happening with your trigger. On the
 d> first call to foo $x is set to 1, on the second it's set to 2. That
 d> doesn't affect "sub bar" though because its copy of $x is still the one
 d> from the first call.

 d> Maybe the following makes it clearer:
 d> #!/usr/bin/perl

 d> sub foo {
 d>         my $x = shift;
 d>         print "foo x = $x, ";
 d>         bar();
 d>         return;

 d>         sub bar {
 d>                 print "bar x = $x\n";
 d>                 $x--;
 d>         }
 d> }

 d> for my $i (1..5) { foo($i); }
 d> exit;

 d> $ ./perl_example.pl
 d> foo x = 1, bar x = 1
 d> foo x = 2, bar x = 0
 d> foo x = 3, bar x = -1
 d> foo x = 4, bar x = -2
 d> foo x = 5, bar x = -3

 d> The two $x variables go their separate ways and the one in "bar" doesn't
 d> go out of scope at the end of the function.



--
Brandon

Re: example of really weird caching (or whatever) problem

From
Richard Huxton
Date:
Brandon Metcalf wrote:
>
> OK.  I understand the Perl part of what is going on.  What I don't
> understand is why $table in do_delete() hangs around.  It seems this
> is more a characteristic of how triggers work in pgsql.  At any rate,
> I appreciate the input since it provides me with a fix.

Because it will persist until either:
1. "sub do_delete" expires
2. the whole perl interpreter expires (which then implies #1)

Your trigger function will be "compiled"* just before it is first called
and will then exist until #2 above happens (when you disconnect). Since
your trigger function exists, that implies do_delete continues to exist
which means its version of $table continues to exist. It really is
exactly like that example script I attached, where the "exit" equals
database end-of-session.


* ok, perl is technically interpreted, except of course it isn't really,
and I don't know what a better term than compiled would be.

--
  Richard Huxton
  Archonet Ltd

Re: example of really weird caching (or whatever) problem

From
"Brandon Metcalf"
Date:
d == dev@archonet.com writes:

 d> Brandon Metcalf wrote:
 d> >
 d> > OK.  I understand the Perl part of what is going on.  What I don't
 d> > understand is why $table in do_delete() hangs around.  It seems this
 d> > is more a characteristic of how triggers work in pgsql.  At any rate,
 d> > I appreciate the input since it provides me with a fix.

 d> Because it will persist until either:
 d> 1. "sub do_delete" expires
 d> 2. the whole perl interpreter expires (which then implies #1)

 d> Your trigger function will be "compiled"* just before it is first called
 d> and will then exist until #2 above happens (when you disconnect). Since
 d> your trigger function exists, that implies do_delete continues to exist
 d> which means its version of $table continues to exist. It really is
 d> exactly like that example script I attached, where the "exit" equals
 d> database end-of-session.

OK, that makes sense.

 d> * ok, perl is technically interpreted, except of course it isn't really,
 d> and I don't know what a better term than compiled would be.

No, you're right.  It is compiled before being executed, but not in
the sense that C code is compiled.


--
Brandon