How to reference hypertables properly using foreign key constraints in PostgreSQL?

579 Views Asked by At

#Error description: It's possible to create a table that has a foreign key into a hypertable provided the foreign key is defined when the table is created

#To Reproduce, there are next tables:

CREATE TABLE ids ( 
    measurement_id int DEFAULT 0,        
    description text DEFAULT 0,       
    m_id bigserial NOT NULL,
    service_id int  DEFAULT NULL,     
    time bigint NOT NULL DEFAULT cast((EXTRACT(EPOCH FROM now() AT TIME ZONE 'UTC') * 1000) as bigint),
    user_id int  DEFAULT NULL,
    end_time DOUBLE PRECISION DEFAULT 0,     
    start_time int NOT NULL DEFAULT 0
);

CREATE INDEX ON ids (time DESC, user_id);
CREATE INDEX ON ids (time DESC, service_id);

SELECT create_hypertable('ids', 'start_time', chunk_time_interval => 604800016);

---------

CREATE TABLE IF NOT EXISTS metrics (
  id bigserial NOT NULL, 
  duration real DEFAULT NULL, 
  metric integer DEFAULT 0,   
  m_id bigint NOT NULL,
  time bigint NOT NULL DEFAULT 0   
);

ALTER TABLE metrics ADD PRIMARY KEY (time, m_id);

CREATE INDEX ON metrics (time DESC);
CREATE INDEX ON metrics (time DESC, measurement );
CREATE INDEX ON metrics (time DESC, m_id );

grant all privileges on ids, metrics to your_db_user;

SELECT create_hypertable('metrics', 'time' , chunk_time_interval => 604800016);

SELECT table_catalog, table_schema, table_name, privilege_type FROM  information_schema.table_privileges WHERE  grantee = 'your_db_user';

---------

DROP TABLE IF EXISTS resource;
CREATE TABLE resource(
id          int NOT NULL,
cpu         text DEFAULT 0,
storing     text DEFAULT 0,
memory      text DEFAULT 0
); 

ALTER TABLE resource ADD PRIMARY KEY (id);

CREATE SEQUENCE resource_id_seq
  INCREMENT 1
  MINVALUE 1
  MAXVALUE 2147483647
  START 1
  CACHE 1;
ALTER TABLE resource_id_seq
  OWNER TO your_db_user;

ALTER TABLE resource ALTER COLUMN id SET DEFAULT nextval('resource_id_seq'::regclass);

---------

CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

DROP TABLE IF EXISTS ns;

CREATE TABLE ns(
id                    bigint NOT NULL,
uuid                  uuid NOT NULL DEFAULT uuid_generate_v4 (),
availability          double precision,
faultTolerance        boolean,
activated             boolean,     
UNIQUE (id, uuid),
PRIMARY KEY(id),
  CONSTRAINT fk_resource
     FOREIGN KEY(id)
         REFERENCES resource(id)
         ON DELETE CASCADE
);

CREATE SEQUENCE ns_id_seq
  INCREMENT 1
  MINVALUE 1
  MAXVALUE 9223372036854775807
  START 1
  CACHE 1;
ALTER TABLE ns_id_seq
  OWNER TO your_db_user;

ALTER TABLE ns ALTER COLUMN id SET DEFAULT nextval('ns_id_seq'::regclass);

---------

DROP TABLE IF EXISTS authentication;

CREATE TABLE authentication(
id                    integer NOT NULL,
username              character varying(255) NOT NULL,
password              character varying(255) NOT NULL,
host                  character varying(255) NOT NULL,
port                  character varying(10) NOT NULL,    
PRIMARY KEY(id)
);

CREATE SEQUENCE auth_id_seq
  INCREMENT 1
  MINVALUE 1
  MAXVALUE 2147483647
  START 1
  CACHE 1;
ALTER TABLE auth_id_seq
  OWNER TO your_db_user;

ALTER TABLE authentication ALTER COLUMN id SET DEFAULT nextval('auth_id_seq'::regclass);

---------

DROP TABLE IF EXISTS job;

CREATE TABLE job(
id                    int NOT NULL,
interval              integer NOT NULL,
auth_id               integer REFERENCES authentication (id),
ns_id                 integer REFERENCES ns (id),  
UNIQUE (auth_id, ns_id),
PRIMARY KEY(id)
);

ALTER TABLE job
ADD CONSTRAINT fk_auth_id
FOREIGN KEY (id) REFERENCES authentication (id)
ON DELETE CASCADE
DEFERRABLE INITIALLY DEFERRED;

ALTER TABLE job
ADD CONSTRAINT fk_ns_id
FOREIGN KEY (id) REFERENCES ns (id)
ON DELETE CASCADE
DEFERRABLE INITIALLY DEFERRED;

CREATE SEQUENCE job_id_seq
  INCREMENT 1
  MINVALUE 1
  MAXVALUE 2147483647
  START 1
  CACHE 1;
ALTER TABLE job_id_seq
  OWNER TO your_db_user;

ALTER TABLE job ALTER COLUMN id SET DEFAULT nextval('job_id_seq'::regclass);

---------

DROP TABLE IF EXISTS job_metric;

CREATE TABLE job_metric (
  id                       int NOT NULL,       
  j_id                     int NOT NULL REFERENCES job (id), 
  mj_id                   bigint  NOT NULL,
  jm_time                 bigint NOT NULL 
);
CREATE INDEX ON job_metric (jm_time DESC);
CREATE INDEX ON job_metric (jm_time DESC, id);
CREATE INDEX ON job_metric (jm_time DESC, mj_id);

ALTER TABLE job_metric ADD PRIMARY KEY (jm_time, id);

grant all privileges on job_metric to your_db_user;

SELECT create_hypertable('job_metric', 'jm_time' , chunk_time_interval => 604800016);

CREATE SEQUENCE mjob_metric_id_seq
  INCREMENT 1
  MINVALUE 1
  MAXVALUE 2147483647
  START 1
  CACHE 1;
ALTER TABLE mjob_metric_id_seq
  OWNER TO your_db_user;

ALTER TABLE job_metric ALTER COLUMN id SET DEFAULT nextval('mjob_metric_id_seq'::regclass);
---------

After creating the tables, I have used the solution proposed by @Laurenz in a database with PostgreSQL 12.6 using the extension of timescaledb 1.7.5 as follows:

#To fill the table with the appropriate values:

UPDATE job_metric AS jm_point
SET jm_time = qm.time
FROM metrics AS qm
WHERE qm.m_id = jm_point.mj_id;

#Then set it NOT NULL:

ALTER TABLE job_metric ALTER jm_time SET NOT NULL;

#To define your foreign key:

ALTER TABLE job_metric
   ADD FOREIGN KEY (mj_id, jm_time)
   REFERENCES metrics (time, m_id) MATCH FULL;

#Response of the last reference table to enable foreign key: Query returned successfully in 40 msec.

Expected behavior: The idea is to use the table job_metric in an even many-to-many relationship to access the information of job and metrics tables.

Actual behavior and error: Tables are created and FKs were created but cannot be used when data is inserted at job_metric as is detailed in the following:

INSERT INTO job_metric (j_id, mj_id, jm_time) 
VALUES(13, 185063, 1621957192266);

ERROR: foreign keys to hypertables are not supported CONTEXT: SQL statement " ALTER TABLE _timescaledb_internal._hyper_5_5_chunk ADD CONSTRAINT "5_13_job_metric_j_id_mj_id_jm_time_fkey" FOREIGN KEY (j_id, mj_id, jm_time) REFERENCES qmetrics("time", m_id) MATCH FULL " PL/pgSQL function _timescaledb_internal.chunk_constraint_add_table_constraint(_timescaledb_catalog.chunk_constraint) line 42 at EXECUTE SQL state: 0A000

***According to https://docs.timescale.com/timescaledb/latest/overview/limitations/##distributed-hypertable-limitations, it looks like the above error is part of the hypertable limitations:

Foreign key constraints referencing a hypertable are not supported.

#Request: Given the above information and errors, does anyone know any solution at the DB level to establish the relationships (many-to-many or one-to-many) using timescaledb extension and mainly hypertables?


Actually, I have obtained similar of above error when I had attempted to create many-to-many relation among the tables metrics and job_metric using the Django Rest Framework:

class Job_Metrics(models.Model):
job = models.OneToOneField(Job, on_delete=models.CASCADE)
metrics = models.ManyToManyField(Metrics)
time = models.IntegerField(default=0)

Running the application metrics pointing out directly metrics_db: $ python3 manage.py migrate metrics --database=metrics_db

Operations to perform: Apply all migrations: metrics Running migrations: Applying metrics.0002_job...Traceback (most recent call last): File "/var/myproject/myprojectenv/lib/python3.8/site-packages/django/db/backends/utils.py", line 84, in _execute return self.cursor.execute(sql, params) psycopg2.errors.FeatureNotSupported: foreign keys to hypertables are not supported

If someone knows a solution or has an idea to deal with the above error at the REST API level, please could you share your idea with the aim to access data associated tables (metrics and jobs) and modify them together when is required to delete e.g., a job_metric. So far, using hypertables amendments of timescaledb extension seems to be not a viable solution.

0

There are 0 best solutions below