Skip to content

Commit 55f447e

Browse files
committed
fix: trigger memory leak by replacing shared vars with local vars #15
1 parent e30c28f commit 55f447e

2 files changed

Lines changed: 124 additions & 100 deletions

File tree

db/trigger.go

Lines changed: 123 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -44,40 +44,51 @@ func CreateTrigger(ctx context.Context, dbPool *pgxpool.Pool, tableName string,
4444
url := quoteSQLString(*opts.UpdateEntityURL)
4545
caseStatements += fmt.Sprintf(`
4646
WHEN 'entity.update.version' THEN
47-
-- Deduplicate: only fire for the first audit row for this entity UUID
48-
IF EXISTS (
49-
SELECT 1
50-
FROM %s a
51-
WHERE a.action = 'entity.update.version'
52-
AND a.details->'entity'->>'uuid' = NEW.details->'entity'->>'uuid'
53-
AND a.details ? 'webhook_sent'
54-
) THEN
55-
-- Already processed this entity
47+
DECLARE
48+
entity_data jsonb;
49+
entity_payload jsonb;
50+
BEGIN
51+
-- Deduplicate: only fire for the first audit row for this entity UUID
52+
IF EXISTS (
53+
SELECT 1
54+
FROM %s a
55+
WHERE a.action = 'entity.update.version'
56+
AND a.details->'entity'->>'uuid' = NEW.details->'entity'->>'uuid'
57+
AND a.details ? 'webhook_sent'
58+
) THEN
59+
-- Already processed this entity
60+
NEW.details := NEW.details || '{"webhook_sent": true}'::jsonb;
61+
RETURN NEW;
62+
END IF;
63+
64+
SELECT entity_defs."data"
65+
INTO entity_data
66+
FROM entity_defs
67+
WHERE entity_defs.id = (NEW.details->>'entityDefId')::int;
68+
69+
IF entity_data IS NULL THEN
70+
RAISE WARNING 'Entity def not found for entityDefId: %%', NEW.details->>'entityDefId';
71+
NEW.details := NEW.details || '{"webhook_sent": true}'::jsonb;
72+
RETURN NEW;
73+
END IF;
74+
75+
entity_payload := jsonb_build_object(
76+
'type', 'entity.update.version',
77+
'id', (NEW.details->'entity'->>'uuid'),
78+
'data', entity_data
79+
);
80+
81+
PERFORM http((
82+
'POST',
83+
%s,
84+
http_headers(%s),
85+
'application/json',
86+
entity_payload::text
87+
)::http_request);
88+
89+
-- Mark as processed
5690
NEW.details := NEW.details || '{"webhook_sent": true}'::jsonb;
57-
RETURN NEW;
58-
END IF;
59-
60-
SELECT entity_defs."data"
61-
INTO result_data
62-
FROM entity_defs
63-
WHERE entity_defs.id = (NEW.details->>'entityDefId')::int;
64-
65-
webhook_payload := jsonb_build_object(
66-
'type', 'entity.update.version',
67-
'id', (NEW.details->'entity'->>'uuid'),
68-
'data', result_data
69-
);
70-
71-
PERFORM http((
72-
'POST',
73-
%s,
74-
http_headers(%s),
75-
'application/json',
76-
webhook_payload::text
77-
)::http_request);
78-
79-
-- Mark as processed
80-
NEW.details := NEW.details || '{"webhook_sent": true}'::jsonb;
91+
END;
8192
`, tableName, url, headersSQL)
8293
}
8394

@@ -88,40 +99,51 @@ func CreateTrigger(ctx context.Context, dbPool *pgxpool.Pool, tableName string,
8899
url := quoteSQLString(*opts.NewSubmissionURL)
89100
caseStatements += fmt.Sprintf(`
90101
WHEN 'submission.create' THEN
91-
-- Deduplicate by instanceId
92-
IF EXISTS (
93-
SELECT 1
94-
FROM %s a
95-
WHERE a.action = 'submission.create'
96-
AND a.details->>'instanceId' = NEW.details->>'instanceId'
97-
AND a.details ? 'webhook_sent'
98-
) THEN
99-
-- Already processed
102+
DECLARE
103+
submission_data jsonb;
104+
submission_payload jsonb;
105+
BEGIN
106+
-- Deduplicate by instanceId
107+
IF EXISTS (
108+
SELECT 1
109+
FROM %s a
110+
WHERE a.action = 'submission.create'
111+
AND a.details->>'instanceId' = NEW.details->>'instanceId'
112+
AND a.details ? 'webhook_sent'
113+
) THEN
114+
-- Already processed
115+
NEW.details := NEW.details || '{"webhook_sent": true}'::jsonb;
116+
RETURN NEW;
117+
END IF;
118+
119+
SELECT jsonb_build_object('xml', submission_defs.xml)
120+
INTO submission_data
121+
FROM submission_defs
122+
WHERE submission_defs.id = (NEW.details->>'submissionDefId')::int;
123+
124+
IF submission_data IS NULL THEN
125+
RAISE WARNING 'Submission def not found for submissionDefId: %%', NEW.details->>'submissionDefId';
126+
NEW.details := NEW.details || '{"webhook_sent": true}'::jsonb;
127+
RETURN NEW;
128+
END IF;
129+
130+
submission_payload := jsonb_build_object(
131+
'type', 'submission.create',
132+
'id', (NEW.details->>'instanceId'),
133+
'data', submission_data
134+
);
135+
136+
PERFORM http((
137+
'POST',
138+
%s,
139+
http_headers(%s),
140+
'application/json',
141+
submission_payload::text
142+
)::http_request);
143+
144+
-- Mark as processed
100145
NEW.details := NEW.details || '{"webhook_sent": true}'::jsonb;
101-
RETURN NEW;
102-
END IF;
103-
104-
SELECT jsonb_build_object('xml', submission_defs.xml)
105-
INTO result_data
106-
FROM submission_defs
107-
WHERE submission_defs.id = (NEW.details->>'submissionDefId')::int;
108-
109-
webhook_payload := jsonb_build_object(
110-
'type', 'submission.create',
111-
'id', (NEW.details->>'instanceId'),
112-
'data', result_data
113-
);
114-
115-
PERFORM http((
116-
'POST',
117-
%s,
118-
http_headers(%s),
119-
'application/json',
120-
webhook_payload::text
121-
)::http_request);
122-
123-
-- Mark as processed
124-
NEW.details := NEW.details || '{"webhook_sent": true}'::jsonb;
146+
END;
125147
`, tableName, url, headersSQL)
126148
}
127149

@@ -132,38 +154,42 @@ func CreateTrigger(ctx context.Context, dbPool *pgxpool.Pool, tableName string,
132154
url := quoteSQLString(*opts.ReviewSubmissionURL)
133155
caseStatements += fmt.Sprintf(`
134156
WHEN 'submission.update' THEN
135-
-- Deduplicate by instanceId + reviewState
136-
IF EXISTS (
137-
SELECT 1
138-
FROM %s a
139-
WHERE a.action = 'submission.update'
140-
AND a.details->>'instanceId' = NEW.details->>'instanceId'
141-
AND a.details->>'reviewState' = NEW.details->>'reviewState'
142-
AND a.details ? 'webhook_sent'
143-
) THEN
144-
-- Already processed
157+
DECLARE
158+
review_payload jsonb;
159+
BEGIN
160+
-- Deduplicate by instanceId + reviewState
161+
IF EXISTS (
162+
SELECT 1
163+
FROM %s a
164+
WHERE a.action = 'submission.update'
165+
AND a.details->>'instanceId' = NEW.details->>'instanceId'
166+
AND a.details->>'reviewState' = NEW.details->>'reviewState'
167+
AND a.details ? 'webhook_sent'
168+
) THEN
169+
-- Already processed
170+
NEW.details := NEW.details || '{"webhook_sent": true}'::jsonb;
171+
RETURN NEW;
172+
END IF;
173+
174+
review_payload := jsonb_build_object(
175+
'type', 'submission.update',
176+
'id', (NEW.details->>'instanceId'),
177+
'data', jsonb_build_object(
178+
'reviewState', NEW.details->>'reviewState'
179+
)
180+
);
181+
182+
PERFORM http((
183+
'POST',
184+
%s,
185+
http_headers(%s),
186+
'application/json',
187+
review_payload::text
188+
)::http_request);
189+
190+
-- Mark as processed
145191
NEW.details := NEW.details || '{"webhook_sent": true}'::jsonb;
146-
RETURN NEW;
147-
END IF;
148-
149-
webhook_payload := jsonb_build_object(
150-
'type', 'submission.update',
151-
'id', (NEW.details->>'instanceId'),
152-
'data', jsonb_build_object(
153-
'reviewState', NEW.details->>'reviewState'
154-
)
155-
);
156-
157-
PERFORM http((
158-
'POST',
159-
%s,
160-
http_headers(%s),
161-
'application/json',
162-
webhook_payload::text
163-
)::http_request);
164-
165-
-- Mark as processed
166-
NEW.details := NEW.details || '{"webhook_sent": true}'::jsonb;
192+
END;
167193
`, tableName, url, headersSQL)
168194
}
169195

@@ -178,8 +204,6 @@ func CreateTrigger(ctx context.Context, dbPool *pgxpool.Pool, tableName string,
178204
$$
179205
DECLARE
180206
action_type text;
181-
result_data jsonb;
182-
webhook_payload jsonb;
183207
BEGIN
184208
action_type := NEW.action;
185209

db/trigger_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ func TestEntityTrigger(t *testing.T) {
283283
tdb.Is.True(strings.Contains(functionSQL, "WHEN 'entity.update.version'"))
284284
tdb.Is.True(strings.Contains(functionSQL, "'type', 'entity.update.version'"))
285285
tdb.Is.True(strings.Contains(functionSQL, "'id', (NEW.details->'entity'->>'uuid')"))
286-
tdb.Is.True(strings.Contains(functionSQL, "'data', result_data"))
286+
tdb.Is.True(strings.Contains(functionSQL, "'data', entity_data"))
287287

288288
err = RemoveTrigger(tdb.Ctx, tdb.Pool, "audits_test")
289289
tdb.Is.NoErr(err)

0 commit comments

Comments
 (0)