Добавил:
ИВТ (советую зайти в "Несортированное")rnПИН МАГА Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Database 2024 / Books / Искусство PostgreSQL.pdf
Скачиваний:
14
Добавлен:
20.11.2024
Размер:
1.62 Mб
Скачать

Chapter 38 Triggers j 328

That’s the happy scenario where no problem occurs. Now, in the real life, here’s what will sometimes happen. It’s not always, mind you, but not never either. Concurrency bugs — they like to hide in plain sight.

1.

The

rst transaction of the day attempts to update the daily counters table

 

for this day but

nds no records because it’s the

rst one.

2.

The second transaction of the day attempts to update the daily counters

 

table for this day, but nds no records, because the rst one isn’t there yet.

3.

The second transaction of the day now proceeds to insert the rst value

 

for the day, because the job wasn’t done yet.

 

4.

The

rst transaction of the day then inserts the

rst value… and fails with a

 

primary key con

ict error because that insert has already been done. Sorry

 

about that!

 

 

There are several ways to address this issue, and the classic one is documented at A PL/pgSQL Trigger Procedure For Maintaining A Summary Table example in the PostgreSQL documentation.

The solution there is to loop over attempts at update then insert until one of those works, ignoring the UNIQUE_VIOLATION exceptions in the process. That allows implementing a fall back when another transaction did insert a value concurrently, i.e. in the middle of the NOT FOUND test and the consequent insert.

Starting in PostgreSQL 9.5 with support for the on conflict clause of the insert into command, there’s a much better way to address this problem.

Fixing the Behavior

While it’s easy to maintain a cache in an event driven fashion thanks to PostgreSQL and its trigger support, turning an insert into an update with contention on a single row is never a good idea. It’s even a classic anti-pattern.

Here’s a modern way to x the problem with the previous trigger implementation, this time applied to a per-message counter of retweet and favorite actions:

1begin;

2

3 create table twcache.counters

4(

 

 

Chapter 38 Triggers j 329

5

messageid

bigint not null references tweet.message(messageid),

6

rts

bigint,

7

favs

bigint,

8

 

 

9unique(messageid)

10

);

11

 

12create or replace function twcache.tg_update_counters ()

13returns trigger

14language plpgsql

15as $$

16declare

17begin

18insert into twcache.counters(messageid, rts, favs)

19select NEW.messageid,

20

case

when

NEW.action

=

'rt' then 1 else 0 end,

21

case

when

NEW.action

=

'fav' then 1 else 0 end

22on conflict (messageid)

23do update

24

set rts = case when NEW.action = 'rt'

25

then counters.rts + 1

26

 

27

when NEW.action = 'de-rt'

28

then counters.rts - 1

29

 

30

else counters.rts

31

end,

32

 

33

favs = case when NEW.action = 'fav'

34

then counters.favs + 1

35

 

36

when NEW.action = 'de-fav'

37

then counters.favs - 1

38

 

39

else counters.favs

40

end

41

where counters.messageid = NEW.messageid;

42

 

43RETURN NULL;

44end;

45$$;

46

47CREATE TRIGGER update_counters

48AFTER INSERT

49ON tweet.activity

50FOR EACH ROW

51EXECUTE PROCEDURE twcache.tg_update_counters();

52

53insert into tweet.activity(messageid, action)

54values (7, 'rt'),

55

(7,

'fav'),

56

(7,

'de-fav'),

Chapter 38 Triggers j 330

57

(8, 'rt'),

58

(8, 'rt'),

59

(8, 'rt'),

60

(8, 'de-rt'),

61

(8, 'rt');

62

 

63select messageid, rts, favs

64from twcache.counters;

65

66 rollback;

And here’s the result of running that le in psql, either from the command line with psql -f or with the interactive \i <path/to/file.sql command:

BEGIN

CREATE TABLE CREATE FUNCTION CREATE TRIGGER INSERT 0 8

messageid │ rts │ favs

═══════════╪═════╪══════

7

1

0

8

3

0

(2 rows) ROLLBACK

You might have noticed that the le ends with a ROLLBACK statement. That’s because we don’t really want to install such a trigger, it’s meant as an example only.

The reason why we don’t actually want to install it is that it would cancel all our previous e forts to model for tweet activity scalability by transforming every insert into tweet.activity into an update twcache.counters on the same messageid. We looked into that exact thing in the previous section and we saw that it would never scale to our requirements.

Event Triggers

Event triggers are another kind of triggers that only PostgreSQL supports, and theyallowone to implement triggers on anyevent that the source code integrates. Currently event triggers are mainly provided for DDL commands.

Have a look at “A Table Rewrite Event Trigger Example” in the PostgreSQL documentation for more information about event triggers, as they are not covered

Chapter 38 Triggers j 331

in this book.

Соседние файлы в папке Books