DBA: ni pipe ṣeto awọn amuṣiṣẹpọ ati awọn agbewọle lati ilu okeere

Fun sisẹ eka ti awọn eto data nla (orisirisi Awọn ilana ETL: agbewọle lati ilu okeere, awọn iyipada ati mimuuṣiṣẹpọ pẹlu orisun ita) nigbagbogbo iwulo wa fun igba diẹ "ranti" ati lẹsẹkẹsẹ ni kiakia ilana nkankan voluminous.

Iṣẹ-ṣiṣe aṣoju ti iru yii nigbagbogbo dun nkankan bi eleyi: "Nibi gangan iṣiro Eka unloaded lati onibara bank awọn sisanwo ti o gba kẹhin, o nilo lati gbe wọn yarayara si oju opo wẹẹbu ki o sopọ wọn si awọn akọọlẹ rẹ.

Ṣugbọn nigbati iwọn didun ti "nkankan" yii bẹrẹ lati wiwọn ni awọn ọgọọgọrun ti megabyte, ati pe iṣẹ naa gbọdọ tẹsiwaju lati ṣiṣẹ pẹlu data 24x7, ọpọlọpọ awọn ipa ẹgbẹ ti yoo ba aye rẹ jẹ.
DBA: ni pipe ṣeto awọn amuṣiṣẹpọ ati awọn agbewọle lati ilu okeere
Lati koju wọn ni PostgreSQL (ati kii ṣe ninu rẹ nikan), o le lo diẹ ninu awọn iṣapeye ti yoo gba ọ laaye lati ṣe ilana ohun gbogbo ni iyara ati pẹlu lilo awọn orisun ti o dinku.

1. Nibo ni lati firanṣẹ?

Ni akọkọ, jẹ ki a pinnu ibiti a ti le gbejade data ti a fẹ lati “ṣe.”

1.1. Àwọn tábìlì ìgbà díẹ̀

Ni opo, fun PostgreSQL awọn tabili igba diẹ jẹ kanna bi eyikeyi miiran. Nitorina, superstitions bi "Ohun gbogbo ti o wa ni ipamọ nikan ni iranti, ati pe o le pari". Ṣugbọn awọn iyatọ pataki pupọ tun wa.

“Aaye orukọ” tirẹ fun asopọ kọọkan si ibi ipamọ data

Ti awọn asopọ meji ba gbiyanju lati sopọ ni akoko kanna CREATE TABLE x, lẹhinna ẹnikan yoo dajudaju gba aṣiṣe ti kii ṣe alailẹgbẹ database ohun.

Ṣugbọn ti awọn mejeeji ba gbiyanju lati ṣiṣẹ CREATE TEMPORARY TABLE x, lẹhinna awọn mejeeji yoo ṣe deede, ati pe gbogbo eniyan yoo gba ẹda rẹ awọn tabili. Ati pe kii yoo si nkankan ni wọpọ laarin wọn.

"Pa ara ẹni run" nigbati o ba ge asopọ

Nigbati asopọ ba wa ni pipade, gbogbo awọn tabili igba diẹ ti paarẹ laifọwọyi, nitorinaa pẹlu ọwọ DROP TABLE x ko si aaye ayafi...

Ti o ba n ṣiṣẹ nipasẹ pgbouncer ni ipo idunadura, lẹhinna aaye data tẹsiwaju lati gbagbọ pe asopọ yii ṣi ṣiṣẹ, ati ninu rẹ tabili igba diẹ tun wa.

Nitorinaa, igbiyanju lati ṣẹda rẹ lẹẹkansi, lati asopọ ti o yatọ si pgbouncer, yoo ja si aṣiṣe kan. Ṣugbọn eyi le ṣee yika nipasẹ lilo CREATE TEMPORARY TABLE IF NOT EXISTS x.

Lootọ, o dara ki a ma ṣe eyi lonakona, nitori lẹhinna o le “lojiji” wa nibẹ data ti o ku lati “eni ti tẹlẹ”. Dipo, o dara julọ lati ka iwe afọwọkọ naa ki o rii pe nigba ṣiṣẹda tabili o ṣee ṣe lati ṣafikun ON COMMIT DROP - iyẹn ni, nigbati idunadura ba pari, tabili yoo paarẹ laifọwọyi.

Ti kii ṣe atunṣe

Nitoripe wọn jẹ nikan si asopọ kan pato, awọn tabili igba diẹ ko tun ṣe. Sugbon eyi yọkuro iwulo fun gbigbasilẹ ilọpo meji ti data ni òkìtì + WAL, nitorinaa FI sii/Imudojuiwọn/Parẹ sinu rẹ yiyara ni pataki.

Ṣugbọn niwọn igba ti tabili igba diẹ tun jẹ tabili “fere lasan”, ko le ṣẹda lori ẹda kan boya. O kere ju fun bayi, botilẹjẹpe alemo ti o baamu ti n kaakiri fun igba pipẹ.

1.2. TABI ti a ko wọle

Ṣugbọn kini o yẹ ki o ṣe, fun apẹẹrẹ, ti o ba ni iru ilana ETL ti o lewu ti ko le ṣe imuse laarin idunadura kan, ṣugbọn o tun ni. pgbouncer ni ipo idunadura? ..

Tabi sisan data ti tobi to Ko si bandiwidi to lori asopọ kan lati ibi ipamọ data (ka, ilana kan fun Sipiyu)? ..

Tabi diẹ ninu awọn iṣẹ ṣiṣe n lọ asynchronously ni orisirisi awọn asopọ? ..

Aṣayan kan nikan wa nibi - igba die ṣẹda a ti kii-ibùgbé tabili. Pun, bẹẹni. Ti o jẹ:

  • ṣẹda awọn tabili “ti ara mi” pẹlu awọn orukọ lairotẹlẹ maximally ki o má ba ṣe ajọṣepọ pẹlu ẹnikẹni
  • jade: kún wọn pẹlu data lati orisun ita
  • Yipada: iyipada, kun ni awọn aaye asopọ bọtini
  • fifuye: tú data ti o ṣetan sinu awọn tabili ibi-afẹde
  • paarẹ awọn tabili "mi".

Ati nisisiyi - a fly ni ikunra. Ni pato, gbogbo awọn kikọ ni PostgreSQL ṣẹlẹ lemeji - akọkọ ni WAL, lẹhinna sinu tabili / awọn ara atọka. Gbogbo eyi ni a ṣe lati ṣe atilẹyin ACID ati ṣatunṣe hihan data laarin COMMIT'nutty ati ROLLBACK'asan lẹkọ.

Ṣugbọn a ko nilo eyi! A ni gbogbo ilana Boya o jẹ aṣeyọri patapata tabi kii ṣe.. Ko ṣe pataki iye awọn iṣowo agbedemeji yoo jẹ - a ko nifẹ si “tẹsiwaju ilana naa lati aarin,” ni pataki nigbati ko ṣe alaye ibiti o wa.

Lati ṣe eyi, awọn Difelopa PostgreSQL, pada ni ẹya 9.1, ṣafihan iru nkan bii UNLOGGED tabili:

Pẹlu itọkasi yii, tabili ti ṣẹda bi ṣiṣi silẹ. Awọn data ti a kọ si awọn tabili ti a ko wọle ko lọ nipasẹ iwe kikọ-iwaju (wo Abala 29), nfa iru awọn tabili si ṣiṣẹ Elo yiyara ju ibùgbé. Sibẹsibẹ, wọn ko ni aabo si ikuna; ni ọran ikuna olupin tabi tiipa pajawiri, tabili ti ko wọle laifọwọyi truncated. Ni afikun, awọn akoonu ti tabili unlogged ko tun ṣe to ẹrú apèsè. Eyikeyi atọka ti a ṣẹda lori tabili ṣiṣi silẹ di ṣiṣi silẹ laifọwọyi.

Ni soki, yoo yiyara pupọ, ṣugbọn ti olupin data ba "ṣubu", yoo jẹ aibanujẹ. Ṣugbọn igba melo ni eyi n ṣẹlẹ, ati pe ilana ETL rẹ mọ bi o ṣe le ṣe atunṣe eyi ni deede “lati aarin” lẹhin “isọji” data data?...

Ti kii ba ṣe bẹ, ati pe ọran loke jẹ iru tirẹ, lo UNLOGGED, sugbon ko ma ṣe mu abuda yii ṣiṣẹ lori awọn tabili gidi, data lati eyi ti o jẹ ọwọn si ọ.

1.3. ON IFỌRỌWỌRỌ { PA awọn ori ila | DOP}

Itumọ yii gba ọ laaye lati pato ihuwasi adaṣe nigbati idunadura kan ba pari nigbati o ṣẹda tabili kan.

on ON COMMIT DROP Mo ti kọ tẹlẹ loke, o ṣe ipilẹṣẹ DROP TABLE, ṣugbọn pẹlu ON COMMIT DELETE ROWS awọn ipo jẹ diẹ awon - o ti wa ni ti ipilẹṣẹ nibi TRUNCATE TABLE.

Niwọn igba ti gbogbo awọn amayederun fun titoju meta-apejuwe ti tabili igba diẹ jẹ deede kanna bi ti tabili deede, lẹhinna Ṣiṣẹda igbagbogbo ati piparẹ awọn tabili igba diẹ yori si “wiwu” ti o lagbara ti awọn tabili eto pg_class, pg_tribute, pg_attrdef, pg_depend,…

Bayi fojuinu pe o ni oṣiṣẹ kan lori asopọ taara si ibi ipamọ data, eyiti o ṣii idunadura tuntun ni gbogbo iṣẹju-aaya, ṣẹda, kun, awọn ilana ati paarẹ tabili igba diẹ… eyi yoo fa afikun idaduro fun iṣẹ kọọkan.

Ni gbogbogbo, maṣe ṣe eyi! Ninu apere yi o jẹ Elo siwaju sii munadoko CREATE TEMPORARY TABLE x ... ON COMMIT DELETE ROWS mu jade kuro ninu ilana iṣowo - lẹhinna nipasẹ ibẹrẹ ti iṣowo tuntun kọọkan awọn tabili ti wa tẹlẹ yoo wa (fi ipe pamọ CREATE), ṣugbọn yoo ṣofo, ọpẹ si TRUNCATE (a tun fipamọ ipe rẹ) nigbati o ba pari idunadura iṣaaju.

1.4. BI...PẸLU...

Mo mẹnuba ni ibẹrẹ pe ọkan ninu awọn ọran lilo aṣoju fun awọn tabili igba diẹ jẹ ọpọlọpọ iru awọn agbewọle lati ilu okeere - ati pe olupilẹṣẹ ti rẹwẹsi daakọ-lẹẹmọ atokọ ti awọn aaye ti tabili ibi-afẹde sinu ikede ti igba diẹ rẹ…

Ṣugbọn ọlẹ jẹ ẹrọ ilọsiwaju! Iyẹn ni idi ṣẹda tabili tuntun “da lori apẹẹrẹ” o le jẹ rọrun pupọ:

CREATE TEMPORARY TABLE import_table(
  LIKE target_table
);

Niwọn igba ti o le ṣe ipilẹṣẹ data pupọ sinu tabili yii, wiwa nipasẹ rẹ kii yoo yara rara. Ṣugbọn ojutu ibile kan wa si eyi - awọn atọka! Ati, bẹẹni, tabili igba diẹ le tun ni awọn atọka.

Niwọn igba ti, nigbagbogbo, awọn atọka ti a beere ṣe deede pẹlu awọn atọka ti tabili ibi-afẹde, o le nirọrun kọ LIKE target_table INCLUDING INDEXES.

Ti o ba tun nilo DEFAULT-awọn iye (fun apẹẹrẹ, lati kun awọn iye bọtini akọkọ), o le lo LIKE target_table INCLUDING DEFAULTS. Tabi nìkan - LIKE target_table INCLUDING ALL - awọn adakọ aiyipada, awọn atọka, awọn ihamọ,...

Ṣugbọn nibi o nilo lati ni oye pe ti o ba ṣẹda gbe wọle tabili lẹsẹkẹsẹ pẹlu awọn atọka, ki o si awọn data yoo gba to gun lati fifuyeju ti o ba kọkọ kun ohun gbogbo, ati lẹhinna yi awọn atọka soke - wo bi o ṣe ṣe eyi bi apẹẹrẹ pg_idasonu.

Ni gbogbogbo, RTFM!

2. Bawo ni lati kọ?

Jẹ ki n kan sọ - lo COPY-ṣiṣan dipo “pack” INSERT, isare ni igba. O le paapaa taara lati faili ti ipilẹṣẹ tẹlẹ.

3. Bawo ni lati ṣe ilana?

Nítorí náà, jẹ ki ká intro wa nkankan bi yi:

  • o ni tabili pẹlu data alabara ti o fipamọ sinu aaye data rẹ 1M igbasilẹ
  • lojoojumọ onibara n ran ọ lọwọ tuntun kan kikun "aworan"
  • lati iriri ti o mọ pe lati akoko si akoko ko si siwaju sii ju 10K igbasilẹ ti wa ni yi pada

A Ayebaye apẹẹrẹ ti iru ipo kan ni KLADR ipilẹ - ọpọlọpọ awọn adirẹsi ni apapọ, ṣugbọn ni ikojọpọ osẹ kọọkan awọn iyipada pupọ wa (yiyipada awọn ibugbe, apapọ awọn opopona, irisi awọn ile titun) paapaa ni iwọn orilẹ-ede.

3.1. Alugoridimu amuṣiṣẹpọ ni kikun

Fun ayedero, jẹ ki a sọ pe iwọ ko paapaa nilo lati tunto data naa - kan mu tabili wa sinu fọọmu ti o fẹ, iyẹn ni:

  • yọ kuro ohun gbogbo ti ko si ohun to wa
  • imudojuiwọn ohun gbogbo ti o wa tẹlẹ ati pe o nilo imudojuiwọn
  • fi sii ohun gbogbo ti ko sele sibẹsibẹ

Kini idi ti awọn iṣẹ ṣiṣe ni aṣẹ yii? Nitori eyi ni bi iwọn tabili yoo ṣe dagba ni iwonba (ranti MVCC!).

PAARA LATI Dst

Rara, dajudaju o le gba nipasẹ awọn iṣẹ meji:

  • yọ kuro (DELETE) ohun gbogbo ni apapọ
  • fi sii gbogbo lati aworan titun

Ṣugbọn ni akoko kanna, o ṣeun si MVCC, Awọn iwọn ti awọn tabili yoo mu gangan lemeji! Ngba awọn aworan +1M ti awọn igbasilẹ ninu tabili nitori imudojuiwọn 10K jẹ bẹ-apọju…

TRUNCATE dst

Olùgbéejáde ti o ni iriri diẹ sii mọ pe gbogbo tabulẹti le di mimọ ni iye owo:

  • ko o (TRUNCATE) gbogbo tabili
  • fi sii gbogbo lati aworan titun

Ọna naa munadoko, ma oyimbo wulo, ṣugbọn iṣoro kan wa ... A yoo fi awọn igbasilẹ 1M kun fun igba pipẹ, nitorina a ko le ni anfani lati lọ kuro ni tabili ni ofo fun gbogbo akoko yii (gẹgẹbi yoo ṣẹlẹ lai fi ipari si ni iṣowo kan).

Eyi ti o tumọ si:

  • a bere idunadura igba pipẹ
  • TRUNCATE faṣẹ Wiwọle Iyasoto-ìdènà
  • a ṣe ifibọ fun igba pipẹ, ati gbogbo eniyan ni akoko yii ko le paapaa SELECT

Nkankan ko lọ daradara...

TABLE ALTER… fun lorukọ mii… / Jọ tabili silẹ…

Yiyan ni lati kun ohun gbogbo sinu tabili tuntun ti o lọtọ, ati lẹhinna fun lorukọ mii ni aaye ti atijọ. Diẹ ninu awọn nkan kekere ti o buruju:

  • tun tun Wiwọle Iyasoto, biotilejepe significantly kere akoko
  • gbogbo awọn ero ibeere / awọn iṣiro fun tabili yii jẹ atunto, nilo lati ṣiṣe ANALYZE
  • gbogbo awọn bọtini ajeji ti fọ (FK) si tabili

Patch WIP kan wa lati ọdọ Simon Riggs ti o daba ṣiṣe ALTER-iṣẹ kan lati rọpo ara tabili ni ipele faili, laisi awọn iṣiro fọwọkan ati FK, ṣugbọn ko gba iye-iye.

Parẹ, imudojuiwọn, FI sii

Nitorinaa, a yanju lori aṣayan ti kii ṣe ìdènà ti awọn iṣẹ mẹta. Fere mẹta... Bawo ni lati ṣe eyi ni imunadoko?

-- все делаем в рамках транзакции, чтобы никто не видел "промежуточных" состояний
BEGIN;

-- создаем временную таблицу с импортируемыми данными
CREATE TEMPORARY TABLE tmp(
  LIKE dst INCLUDING INDEXES -- по образу и подобию, вместе с индексами
) ON COMMIT DROP; -- за рамками транзакции она нам не нужна

-- быстро-быстро вливаем новый образ через COPY
COPY tmp FROM STDIN;
-- ...
-- .

-- удаляем отсутствующие
DELETE FROM
  dst D
USING
  dst X
LEFT JOIN
  tmp Y
    USING(pk1, pk2) -- поля первичного ключа
WHERE
  (D.pk1, D.pk2) = (X.pk1, X.pk2) AND
  Y IS NOT DISTINCT FROM NULL; -- "антиджойн"

-- обновляем оставшиеся
UPDATE
  dst D
SET
  (f1, f2, f3) = (T.f1, T.f2, T.f3)
FROM
  tmp T
WHERE
  (D.pk1, D.pk2) = (T.pk1, T.pk2) AND
  (D.f1, D.f2, D.f3) IS DISTINCT FROM (T.f1, T.f2, T.f3); -- незачем обновлять совпадающие

-- вставляем отсутствующие
INSERT INTO
  dst
SELECT
  T.*
FROM
  tmp T
LEFT JOIN
  dst D
    USING(pk1, pk2)
WHERE
  D IS NOT DISTINCT FROM NULL;

COMMIT;

3.2. Gbe wọle ranse si-processing

Ni KLADR kanna, gbogbo awọn igbasilẹ ti o yipada gbọdọ wa ni afikun ṣiṣe nipasẹ sisẹ-ifiweranṣẹ - deede, afihan awọn koko-ọrọ, ati dinku si awọn ẹya ti o nilo. Ṣugbọn bawo ni o ṣe mọ - ohun gangan yi padalaisi idiju koodu amuṣiṣẹpọ, ni pipe laisi fọwọkan rara?

Ti ilana rẹ nikan ba ni iraye si kikọ ni akoko imuṣiṣẹpọ, lẹhinna o le lo okunfa kan ti yoo gba gbogbo awọn ayipada fun wa:

-- целевые таблицы
CREATE TABLE kladr(...);
CREATE TABLE kladr_house(...);

-- таблицы с историей изменений
CREATE TABLE kladr$log(
  ro kladr, -- тут лежат целые образы записей старой/новой
  rn kladr
);

CREATE TABLE kladr_house$log(
  ro kladr_house,
  rn kladr_house
);

-- общая функция логирования изменений
CREATE OR REPLACE FUNCTION diff$log() RETURNS trigger AS $$
DECLARE
  dst varchar = TG_TABLE_NAME || '$log';
  stmt text = '';
BEGIN
  -- проверяем необходимость логгирования при обновлении записи
  IF TG_OP = 'UPDATE' THEN
    IF NEW IS NOT DISTINCT FROM OLD THEN
      RETURN NEW;
    END IF;
  END IF;
  -- создаем запись лога
  stmt = 'INSERT INTO ' || dst::text || '(ro,rn)VALUES(';
  CASE TG_OP
    WHEN 'INSERT' THEN
      EXECUTE stmt || 'NULL,$1)' USING NEW;
    WHEN 'UPDATE' THEN
      EXECUTE stmt || '$1,$2)' USING OLD, NEW;
    WHEN 'DELETE' THEN
      EXECUTE stmt || '$1,NULL)' USING OLD;
  END CASE;
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

Bayi a le lo awọn okunfa ṣaaju ki o to bẹrẹ amuṣiṣẹpọ (tabi mu wọn ṣiṣẹ nipasẹ ALTER TABLE ... ENABLE TRIGGER ...):

CREATE TRIGGER log
  AFTER INSERT OR UPDATE OR DELETE
  ON kladr
    FOR EACH ROW
      EXECUTE PROCEDURE diff$log();

CREATE TRIGGER log
  AFTER INSERT OR UPDATE OR DELETE
  ON kladr_house
    FOR EACH ROW
      EXECUTE PROCEDURE diff$log();

Ati lẹhinna a ni idakẹjẹ yọ gbogbo awọn ayipada ti a nilo lati awọn tabili log ati ṣiṣe wọn nipasẹ awọn olutọju afikun.

3.3. Ṣe agbewọle Awọn eto ti o sopọ mọ

Loke a gbero awọn ọran nigbati awọn ẹya data ti orisun ati opin irin ajo jẹ kanna. Ṣugbọn kini ti o ba jẹ pe ikojọpọ lati eto ita ni ọna kika ti o yatọ si eto ibi ipamọ ninu data data wa?

Jẹ ki a mu bi apẹẹrẹ ibi ipamọ ti awọn alabara ati awọn akọọlẹ wọn, aṣayan “ọpọlọpọ-si-ọkan” Ayebaye:

CREATE TABLE client(
  client_id
    serial
      PRIMARY KEY
, inn
    varchar
      UNIQUE
, name
    varchar
);

CREATE TABLE invoice(
  invoice_id
    serial
      PRIMARY KEY
, client_id
    integer
      REFERENCES client(client_id)
, number
    varchar
, dt
    date
, sum
    numeric(32,2)
);

Ṣugbọn igbasilẹ lati orisun ita wa si wa ni irisi “gbogbo ni ọkan”:

CREATE TEMPORARY TABLE invoice_import(
  client_inn
    varchar
, client_name
    varchar
, invoice_number
    varchar
, invoice_dt
    date
, invoice_sum
    numeric(32,2)
);

O han ni, data alabara le ṣe pidánpidán ninu ẹya yii, ati pe igbasilẹ akọkọ jẹ “iroyin”:

0123456789;Вася;A-01;2020-03-16;1000.00
9876543210;Петя;A-02;2020-03-16;666.00
0123456789;Вася;B-03;2020-03-16;9999.00

Fun awoṣe, a yoo kan fi data idanwo wa sii, ṣugbọn ranti - COPY siwaju sii daradara!

INSERT INTO invoice_import
VALUES
  ('0123456789', 'Вася', 'A-01', '2020-03-16', 1000.00)
, ('9876543210', 'Петя', 'A-02', '2020-03-16', 666.00)
, ('0123456789', 'Вася', 'B-03', '2020-03-16', 9999.00);

Lákọ̀ọ́kọ́, ẹ jẹ́ ká sọ̀rọ̀ nípa “àwọn èèṣì” tí “àwọn òkodoro òtítọ́” wa ń tọ́ka sí. Ninu ọran wa, awọn risiti tọka si awọn alabara:

CREATE TEMPORARY TABLE client_import AS
SELECT DISTINCT ON(client_inn)
-- можно просто SELECT DISTINCT, если данные заведомо непротиворечивы
  client_inn inn
, client_name "name"
FROM
  invoice_import;

Lati le ṣe idapọ awọn akọọlẹ ni deede pẹlu awọn ID alabara, a nilo akọkọ lati wa tabi ṣe ipilẹṣẹ awọn idamọ wọnyi. Jẹ ki a ṣafikun awọn aaye labẹ wọn:

ALTER TABLE invoice_import ADD COLUMN client_id integer;
ALTER TABLE client_import ADD COLUMN client_id integer;

Jẹ ki a lo ọna imuṣiṣẹpọ tabili ti a ṣalaye loke pẹlu atunṣe kekere - a kii yoo ṣe imudojuiwọn tabi paarẹ ohunkohun ninu tabili ibi-afẹde, nitori a gbe wọle awọn alabara “afikun-nikan”:

-- проставляем в таблице импорта ID уже существующих записей
UPDATE
  client_import T
SET
  client_id = D.client_id
FROM
  client D
WHERE
  T.inn = D.inn; -- unique key

-- вставляем отсутствовавшие записи и проставляем их ID
WITH ins AS (
  INSERT INTO client(
    inn
  , name
  )
  SELECT
    inn
  , name
  FROM
    client_import
  WHERE
    client_id IS NULL -- если ID не проставился
  RETURNING *
)
UPDATE
  client_import T
SET
  client_id = D.client_id
FROM
  ins D
WHERE
  T.inn = D.inn; -- unique key

-- проставляем ID клиентов у записей счетов
UPDATE
  invoice_import T
SET
  client_id = D.client_id
FROM
  client_import D
WHERE
  T.client_inn = D.inn; -- прикладной ключ

Lootọ, ohun gbogbo wa ninu invoice_import Bayi a ti kun aaye olubasọrọ client_id, pẹlu eyiti a yoo fi risiti sii.

orisun: www.habr.com

Fi ọrọìwòye kun