Fix packet counter accuracy after TRUNCATE operations

The packet counter uses a PostgreSQL sequence that doesn't get reset
when the packets table is truncated, causing inaccurate counts.

This migration:
- Creates an event trigger that automatically resets the packet counter
  sequence when the packets table is truncated
- Adds a manual reset function reset_packet_counter() that can be called
  if needed
- Improves the get_packet_count() function to properly handle when the
  sequence is at 0

After applying this migration, the counter will automatically reset to 0
whenever TRUNCATE is used on the packets table.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Graham McIntire 2025-07-19 15:25:56 -05:00
parent 2bdad557e9
commit 2629589b7f
No known key found for this signature in database

View file

@ -0,0 +1,113 @@
defmodule Aprsme.Repo.Migrations.AddTruncateResetForPacketCounter do
use Ecto.Migration
def up do
# Create a function to reset the packet counter sequence
execute """
CREATE OR REPLACE FUNCTION reset_packet_counter_on_truncate()
RETURNS event_trigger AS $$
DECLARE
cmd RECORD;
BEGIN
FOR cmd IN SELECT * FROM pg_event_trigger_ddl_commands()
LOOP
IF cmd.command_tag = 'TRUNCATE TABLE' AND cmd.object_identity = 'public.packets' THEN
-- Reset the sequence to 0
PERFORM setval('packet_count_seq', 0, false);
-- Also update the deprecated packet_counters table for backward compatibility
UPDATE packet_counters
SET count = 0, updated_at = NOW()
WHERE counter_type = 'total_packets';
END IF;
END LOOP;
END;
$$ LANGUAGE plpgsql;
"""
# Create an event trigger for TRUNCATE operations
execute """
CREATE EVENT TRIGGER reset_packet_counter_trigger
ON ddl_command_end
WHEN TAG IN ('TRUNCATE TABLE')
EXECUTE FUNCTION reset_packet_counter_on_truncate();
"""
# Also create a manual reset function that can be called if needed
execute """
CREATE OR REPLACE FUNCTION reset_packet_counter()
RETURNS void AS $$
BEGIN
-- Reset the sequence
PERFORM setval('packet_count_seq', 0, false);
-- Update the deprecated table
UPDATE packet_counters
SET count = 0, updated_at = NOW()
WHERE counter_type = 'total_packets';
END;
$$ LANGUAGE plpgsql;
"""
# Fix the get_packet_count function to handle the case when sequence is at 0
execute """
CREATE OR REPLACE FUNCTION get_packet_count()
RETURNS BIGINT AS $$
DECLARE
seq_exists BOOLEAN;
current_val BIGINT;
BEGIN
-- Check if sequence exists
SELECT EXISTS (
SELECT 1 FROM pg_sequences
WHERE schemaname = 'public' AND sequencename = 'packet_count_seq'
) INTO seq_exists;
IF NOT seq_exists THEN
-- Fallback to counting if sequence doesn't exist
RETURN (SELECT COUNT(*) FROM packets);
END IF;
-- Try to get current value
BEGIN
current_val := currval('packet_count_seq');
EXCEPTION
WHEN object_not_in_prerequisite_state THEN
-- If currval hasn't been called yet, get last_value
SELECT last_value INTO current_val FROM packet_count_seq;
END;
-- If sequence is at 0 and hasn't been used (is_called = false), return 0
IF current_val = 0 THEN
RETURN 0;
ELSE
RETURN current_val;
END IF;
END;
$$ LANGUAGE plpgsql STABLE;
"""
end
def down do
# Drop the event trigger
execute "DROP EVENT TRIGGER IF EXISTS reset_packet_counter_trigger;"
# Drop the reset functions
execute "DROP FUNCTION IF EXISTS reset_packet_counter_on_truncate();"
execute "DROP FUNCTION IF EXISTS reset_packet_counter();"
# Restore the original get_packet_count function
execute """
CREATE OR REPLACE FUNCTION get_packet_count()
RETURNS BIGINT AS $$
BEGIN
RETURN currval('packet_count_seq');
EXCEPTION
WHEN object_not_in_prerequisite_state THEN
-- If currval hasn't been called yet in this session, get the last value
RETURN last_value FROM packet_count_seq;
END;
$$ LANGUAGE plpgsql STABLE;
"""
end
end