Eh bien, la première chose que je ferais serait de laisser tomber l'analyse de chaîne icky partout et de la remplacer par des types natifs PostgreSQL. Pour stocker le statut de réplication sur chaque enregistrement similaire à votre solution actuelle :
CREATE TYPE replication_status AS ENUM (
'no_action',
'replicate_record',
'record_replicated',
'error_1',
'error_2',
'error_3'
);
ALTER TABLE t ADD COLUMN rep_status_array replication_status[];
Cela vous coûte un peu plus d'espace de stockage - les valeurs enum sont de 4 octets au lieu de 1 et les tableaux ont une surcharge. Cependant, en enseignant à la base de données vos concepts au lieu de les cacher, vous pouvez écrire des choses comme :
-- find all records that need to be replicated to host 4
SELECT * FROM t WHERE rep_status_array[4] = 'replicate_record';
-- find all records that contain any error status
SELECT * FROM t WHERE rep_status_array &&
ARRAY['error_1', 'error_2', 'error_3']::replication_status[];
Vous pouvez mettre un index GIN directement sur rep_status_array
si cela vous aide dans votre cas d'utilisation, mais il est préférable d'examiner vos requêtes et de créer des index spécifiquement pour ce que vous utilisez :
CREATE INDEX t_replication_host_4_key ON t ((rep_status_array[4]));
CREATE INDEX t_replication_error_key ON t (id)
WHERE rep_status_array && ARRAY['error_1', 'error_2', 'error_3']::replication_status[];
Cela dit, étant donné 200 tables, je serais tenté de diviser cela en une seule table d'état de réplication - soit une ligne avec un tableau d'états, soit une ligne par hôte, selon le fonctionnement du reste de la logique de réplication. J'utiliserais toujours cette énumération :
CREATE TABLE adhoc_replication (
record_id bigint not null,
table_oid oid not null,
host_id integer not null,
replication_status status not null default 'no_action',
primary key (record_id,table_oid,host_id)
);
PostgreSQL attribue en interne à chaque table un OID (essayez SELECT *, tableoid FROM t LIMIT 1
), qui est un identifiant numérique stable pratique dans un système de base de données unique. Autrement dit, cela change si la table est supprimée et recréée (ce qui peut arriver si, par exemple, vous videz et restaurez la base de données), et pour cette même raison, il est très probablement différent entre le développement et la production. Si vous préférez que ces situations fonctionnent en échange d'une rupture lorsque vous ajoutez ou renommez une table, utilisez une énumération au lieu d'un OID.
L'utilisation d'une table unique pour toutes les réplications vous permettrait de réutiliser facilement les déclencheurs et les requêtes, etc., en dissociant la plupart de la logique de réplication des données qu'elle réplique. Il vous permet également d'interroger en fonction du statut d'un hôte donné sur toutes vos tables d'origine en faisant référence à un seul index, ce qui peut être important.
En ce qui concerne la taille de la table, PostgreSQL peut certainement gérer 10 millions de lignes dans la même table. Si vous optez pour une table dédiée à la réplication, vous pouvez toujours partition par hôte. (Le partitionnement par table n'a guère de sens pour moi; cela semble pire que de stocker l'état de la réplication sur chaque ligne en amont.) La manière de partitionner ou si elle est appropriée ou non dépend entièrement du type de questions que vous avez l'intention de poser à votre base de données, et quel type d'activité se produit sur les tables de base. (Partitionner signifie conserver de nombreux petits blobs au lieu de quelques grands, et potentiellement accéder à de nombreux petits blobs pour effectuer une seule opération.) C'est vraiment une question de choisir quand vous voulez que votre disque cherche à se produire.