Ad Widget

Collapse

Zabbix Postgresql 9.? autopartitioning

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • alledm
    Member
    • May 2012
    • 84

    #1

    Zabbix Postgresql 9.? autopartitioning

    I have created the following WIKI page with the latest update for this script
    Please refer to the wiki page rather than this thread.

    Join the friendly and open Zabbix community on our forums and social media platforms.



    Most partitioning solutions for Zabbix require a cron/scheduled job to create the partitions.

    I have created this function to simplify the partitioning under Postgresql so that maintenance if kept to the minimum. The function will automatically create the new partition when it needs it.

    A trigger is attached to each table that you want to partition. The trigger needs to have a parameter as either "day" or "month". The trigger will attempt to insert the record into a table called

    table_p2012_10_08 (for daily partitions) or
    table_p2012_10 (for monthly partitions)

    If the table is not there, it will create it and attempt another insert.

    To make it work, all you need to do is to add the following triggers to the "CLOCK" based tables you want to partition.

    Code:
    CREATE TRIGGER partition_trg BEFORE INSERT ON history           FOR EACH ROW EXECUTE PROCEDURE trg_partition('day');
    CREATE TRIGGER partition_trg BEFORE INSERT ON history_sync      FOR EACH ROW EXECUTE PROCEDURE trg_partition('day');
    CREATE TRIGGER partition_trg BEFORE INSERT ON history_uint      FOR EACH ROW EXECUTE PROCEDURE trg_partition('day');
    CREATE TRIGGER partition_trg BEFORE INSERT ON history_str_sync  FOR EACH ROW EXECUTE PROCEDURE trg_partition('day');
    CREATE TRIGGER partition_trg BEFORE INSERT ON history_log       FOR EACH ROW EXECUTE PROCEDURE trg_partition('day');
    CREATE TRIGGER partition_trg BEFORE INSERT ON trends            FOR EACH ROW EXECUTE PROCEDURE trg_partition('month');
    CREATE TRIGGER partition_trg BEFORE INSERT ON trends_uint       FOR EACH ROW EXECUTE PROCEDURE trg_partition('month');
    -- Do not create a trigger for these tables. See my comment later in this thread
    -- CREATE TRIGGER partition_trg BEFORE INSERT ON acknowledges      FOR EACH ROW EXECUTE PROCEDURE trg_partition('month');
    -- CREATE TRIGGER partition_trg BEFORE INSERT ON alerts            FOR EACH ROW EXECUTE PROCEDURE trg_partition('month');
    -- CREATE TRIGGER partition_trg BEFORE INSERT ON auditlog          FOR EACH ROW EXECUTE PROCEDURE trg_partition('month');
    -- CREATE TRIGGER partition_trg BEFORE INSERT ON events            FOR EACH ROW EXECUTE PROCEDURE trg_partition('month');
    -- CREATE TRIGGER partition_trg BEFORE INSERT ON service_alarms    FOR EACH ROW EXECUTE PROCEDURE trg_partition('month');
    To disable the partitioning, simply remove the trigger and reimport the data in the main table

    The function itself is
    Code:
    -- Function: trg__partition()
    /*
    This function makes it easy to partition Zabbix tables on PostgresSQL 9.x (tested on 9.2 only)
    It will attempt an insert on the partitioned table, and if the table does not exist it will create a new one.
    
    Inspiration and code taken from:
    http://zabbixzone.com/zabbix/partitioning-tables/
    http://stackoverflow.com/questions/7891320/self-managing-postgresql-partition-tables-plpgsql
    
    Alessandro De Maria <[email protected]>
    */
    -- DROP FUNCTION trg__partition();
    
    CREATE OR REPLACE FUNCTION trg_partition()
    
    RETURNS trigger AS
    $BODY$
    DECLARE
    timeformat text;
    selector text;
    _interval interval;
    tablename text;
    startdate text;
    enddate text;
    create_table_part text;
    create_index_part text;
    BEGIN
    
    selector = TG_ARGV[0];
    
    IF selector = 'day' THEN
    timeformat := 'YYYY_MM_DD';
    ELSIF selector = 'month' THEN
    timeformat := 'YYYY_MM';
    END IF;
    
    _interval := '1 ' || selector;
    tablename := TG_TABLE_NAME || '_p' || to_char(to_timestamp(NEW.clock), timeformat);
    
    EXECUTE 'INSERT INTO ' || quote_ident(tablename) || ' SELECT ($1).*' USING NEW;
    RETURN NULL;
    
    EXCEPTION
    WHEN undefined_table THEN
    
    startdate := extract(epoch FROM date_trunc(selector, to_timestamp(NEW.clock)));
    enddate := extract(epoch FROM date_trunc(selector, to_timestamp(NEW.clock) + _interval ));
    
    create_table_part:= 'CREATE TABLE IF NOT EXISTS '|| quote_ident(tablename) || ' (CHECK ((clock >= ' || quote_literal(startdate) || ' AND clock < ' || quote_literal(enddate) || '))) INHERITS ('|| TG_TABLE_NAME || ')';
    create_index_part:= 'CREATE INDEX '|| quote_ident(tablename) || '_1 on ' || quote_ident(tablename) || '(itemid,clock)';
    
    EXECUTE create_table_part;
    EXECUTE create_index_part;
    
    --insert it again
    EXECUTE 'INSERT INTO ' || quote_ident(tablename) || ' SELECT ($1).*' USING NEW;
    RETURN NULL;
    
    END;
    $BODY$
    LANGUAGE plpgsql
    Please report any problem to me.

    Last edited by alledm; 22-10-2012, 18:44.
  • Colttt
    Senior Member
    Zabbix Certified Specialist
    • Mar 2009
    • 878

    #2
    what is the difference between yours and THIS??
    Debian-User

    Sorry for my bad english

    Comment

    • alledm
      Member
      • May 2012
      • 84

      #3
      They are both achieving the same goal but in 2 different way

      The other script requires a cron job to run daily and requires you to "initialise" it the first time to pre-create the tables.
      If a table is not found, it adds the record to an "emergency" table that needs to be create during the initialization procedure

      My script:
      • creates the table automatically if, at insertion time, it does not exist already.
      • It does not require you to initialise or do anything to the database before using it.
      • It will self maintain without the need of a cron script or other scheduled job.


      Also, IMHO, it is a bit simpler.

      Comment

      • alledm
        Member
        • May 2012
        • 84

        #4
        Has anybody got any feedback? Have you tested it?

        Shall I add it to the wiki?

        Comment

        • rsvancara
          Member
          • Jul 2012
          • 42

          #5
          I Just use Cron

          I like your approach of building the logic into the function for the trigger to automatically create the table. I had considered a similar idea, but I like where you put yours into the exception block so it is only created if there is a problem, otherwise the script runs as it should.

          Comment

          • alledm
            Member
            • May 2012
            • 84

            #6
            Thank you for your feedback.

            BTW I have found that the function will not work on tables that are not base on clock and itemid (as it creates an index on this), so it should not be used on non history or trends table for now.

            Will fix it soon.

            If you have done it already, please just drop the triggers on the tables you don't need.

            Code:
            DROP TRIGGER partition_trg ON acknowledges;
            DROP TRIGGER partition_trg ON alerts;
            DROP TRIGGER partition_trg ON auditlog;
            DROP TRIGGER partition_trg ON events;
            DROP TRIGGER partition_trg ON service_alarms;
            Regards
            Alessandro

            Comment

            • Jason
              Senior Member
              • Nov 2007
              • 430

              #7
              Have you an automated procedure/ cron job to remove old tables setup this way?

              Comment

              • alledm
                Member
                • May 2012
                • 84

                #8
                I am working on it

                I might link it to the table creation so that when a new table gets created the old one gets dropped.

                Comment

                • Jason
                  Senior Member
                  • Nov 2007
                  • 430

                  #9
                  I was thinking a cronjob that can specify the number of days/months to keep tables for and it just does a date sub to drop tables older than that?

                  Comment

                  • alledm
                    Member
                    • May 2012
                    • 84

                    #10
                    This is what I have now

                    you would run it with

                    delete_partitions('7 days', 'day')
                    delete_partitions('12 months', 'month')

                    but might split it in 2 functions as it seems the cleanest way to do it.

                    Code:
                    CREATE OR REPLACE FUNCTION delete_partitions(intervaltodelete interval, tabletype text)
                      RETURNS text AS
                    $BODY$
                    DECLARE
                    message text;
                    result record ;
                    table_timestamp timestamp;
                    delete_before_date date;
                    
                    BEGIN
                    message := '';
                    FOR result IN SELECT * FROM pg_tables WHERE schemaname = 'partitions' LOOP
                    
                    table_timestamp := to_timestamp(substring(result.tablename from '[0-9_]*$'), 'YYYY_MM_DD');
                    delete_before_date := date_trunc('day', NOW() - intervalToDelete);
                    
                    IF length(substring(result.tablename from '[0-9_]*$')) = 10 AND tabletype = 'month' THEN
                    --This is a daily partition YYYY_MM_DD
                    CONTINUE;
                    ELSIF length(substring(result.tablename from '[0-9_]*$')) = 7 AND tabletype = 'day' THEN
                    --this is a monthly partition
                    CONTINUE;
                    END IF;
                    
                    
                    IF table_timestamp <= delete_before_date THEN
                    message := message || substring(result.tablename from '[0-9_]*$') || ' ' ; 
                    
                    --drop table
                    END IF;
                    END LOOP;
                    RETURN message;
                    
                    END;
                    
                    $BODY$
                      LANGUAGE plpgsql
                    Feedback is very welcome.

                    Comment

                    • Colttt
                      Senior Member
                      Zabbix Certified Specialist
                      • Mar 2009
                      • 878

                      #11
                      Originally posted by alledm
                      They are both achieving the same goal but in 2 different way

                      The other script requires a cron job to run daily and requires you to "initialise" it the first time to pre-create the tables.
                      If a table is not found, it adds the record to an "emergency" table that needs to be create during the initialization procedure

                      My script:
                      • creates the table automatically if, at insertion time, it does not exist already.
                      • It does not require you to initialise or do anything to the database before using it.
                      • It will self maintain without the need of a cron script or other scheduled job.


                      Also, IMHO, it is a bit simpler.
                      ahh ok.. THANKS.. also is you way a little bit better?!

                      can i use it in my install-script?!
                      Debian-User

                      Sorry for my bad english

                      Comment

                      • alledm
                        Member
                        • May 2012
                        • 84

                        #12
                        You can use it in your install script, of course.

                        Personally, I would wait a little longer to see if anybody sees any problem with it. Make sure you test it

                        Also, it is not including a function to remove the tables.. so best if you wait for the complete solution to be available.


                        Better.. well.. it's not for me to say They both work and achieve the same results.

                        Comment

                        • parcival
                          Member
                          • Sep 2010
                          • 44

                          #13
                          Hi alledm,
                          Thank you for this scripts to postgresql.
                          A question about the delete script. How is here your drop rule to delete the tables?

                          for example -> EXECUTE 'DROP TABLE ' || .........

                          Sorry, but am not a sql expert.
                          thanks for your time.
                          parcival


                          Originally posted by alledm
                          This is what I have now

                          you would run it with

                          delete_partitions('7 days', 'day')
                          delete_partitions('12 months', 'month')

                          but might split it in 2 functions as it seems the cleanest way to do it.

                          Feedback is very welcome.

                          Comment

                          • alledm
                            Member
                            • May 2012
                            • 84

                            #14
                            Hi parcival,

                            Will try to give you an answer soon. Hang on in there!

                            Comment

                            • Jason
                              Senior Member
                              • Nov 2007
                              • 430

                              #15
                              It looks like the routines just identify the partitioned tables currently and I suspect enable you to get the name... just a bit of logic needed to dump the names into a variable and then run the drop on those names....

                              Comment

                              Working...