Postgresql kérdések

Megőrülök… már egy jó ideje nem tudok rájönni, hogy mi a baj.

Php-ban szeretnék csinálni adatbázist. Teljesen nulláról felépíteni.
A gond az, hogy szeretnék két user-t létrehozni. Egy aki az adatbázist írni is tudja (puser), egy másikat pedig ami csak olvasni tudja (pread).
A két usert közös funkcióból hozom létre, tehát ugyanaz a kód fut mindkét esetében. Ami különbség az a nevük (, jelszavuk) és a jogosultság. A pread user pesrze csak SELECT jogosultságot kap.

Az első puser tökéletesen működik, míg a második pread userrel nem tudok sem SELECT sem semmi mást futtatni az adatbázisban.

Aztán kirpóbáltam, hogy a SELECT helyett egy az egyben ugyan azokat a jogokat kapja a pread user, mint a sima puser.

Szóval az alábbi sorok futnak le a userek létrehozásánál:

CREATE USER puser WITH PASSWORD 'u' NOSUPERUSER INHERIT NOCREATEDB NOCREATEROLE;
SET SEARCH_PATH TO public;
GRANT USAGE ON SCHEMA public TO puser;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO puser;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES ON TABLES TO puser;

CREATE USER pread WITH PASSWORD 'r' NOSUPERUSER INHERIT NOCREATEDB NOCREATEROLE;
SET SEARCH_PATH TO public;
GRANT USAGE ON SCHEMA public TO pread;
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO pread;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES ON TABLES TO pread;

És a táblát ezután hozom létre. És lehet, hogy fontos, hogy létrehozok egy funkciót és egy triggert is.

CREATE TABLE IF NOT EXISTS public.users (id SERIAL,title varchar(20),name varchar(20),created TIMESTAMP DEFAULT NOW(),modified TIMESTAMP DEFAULT NOW());
CREATE FUNCTION users_update_modified_column() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN NEW.modified = NOW(); RETURN NEW; END; $$;
CREATE TRIGGER users_modified_time BEFORE UPDATE ON public.users FOR EACH ROW EXECUTE PROCEDURE users_update_modified_column();

Ha ugyanezeket a sorokat parancssorból futtatom, akkor tudom a pread userrel olvasni a dolgokat, de ha a php-ból futtatom, akkor nem.
A php-ból futtatás során nem dob hibát. És természetesen a php_connect-nél az adatbázishoz csatlakozom.

Próbáltam úgy is, hogy ALTER DEFAULT… SEQUENCES-hez is jogot adni, de semmi.
Szóval azt nem értem, hogy az első miért sikerül és a második miért nem, ha ugyanaz a funkció hozza létre??? Ráadásul a két funkció egymás után van, nincs semmi sem köztük.

Ja igen, ha megnézem a \ddp paranncsal, akkor (ha jól értem) helyesen létrejön az default érték:

teszt=> \ddp

             Default access privileges
  Owner   | Schema | Type  |   Access privileges    
----------+--------+-------+------------------------
 postgres | public | table | puser=arwdDxt/postgres+
          |        |       | pread=arwdDxt/postgres

bemásolom ide a php kódot, hogy az is hátha segít:

public function createUserIfNeeded($user, $password, $privileges = NULL, $grants = NULL) {
	$dbName = $this->dbName;
	$schema = $this->schema;

	error_log("database name: $dbName");

	if ($this->userIsExistInDatabase($user)) {
		return true;
	}

	$dbHandle = $this->getCreateHandle($dbName);

	$query = "CREATE USER ".$user." WITH PASSWORD '".$password."'";
	if (!empty($privileges)) {
		$query .= " $privileges";
	}
	$query .= ";";
	$this->query($query, array('transaction' => $this->writeTransactionKey()), $dbHandle);

	$query = "SET SEARCH_PATH TO $schema;";
	$this->query($query, array('transaction' => $this->writeTransactionKey()), $dbHandle);

	$query = "GRANT USAGE ON SCHEMA $schema TO $user;";
	$this->query($query, array('transaction' => $this->writeTransactionKey()), $dbHandle);

	if (!empty($grants)) {
		$query = "GRANT $grants ON ALL TABLES IN SCHEMA $schema TO $user;";
		$this->query($query, array('transaction' => $this->writeTransactionKey()), $dbHandle);
		$query = "ALTER DEFAULT PRIVILEGES IN SCHEMA $schema GRANT $grants ON TABLES TO $user;";
		$this->query($query, array('transaction' => $this->writeTransactionKey()), $dbHandle);
//			$query = "ALTER DEFAULT PRIVILEGES IN SCHEMA $schema GRANT $grants ON SEQUENCES TO $user;";
//			$this->query($query, array('transaction' => $this->writeTransactionKey()), $dbHandle);
//			$query = "ALTER DEFAULT PRIVILEGES IN SCHEMA $schema GRANT USAGE, SELECT ON SEQUENCES TO $user;";
//			$this->query($query, array('transaction' => $this->writeTransactionKey()), $dbHandle);
//			$query = "ALTER DEFAULT PRIVILEGES IN SCHEMA $schema GRANT EXECUTE ON FUNCTIONS TO $user;";
//			$this->query($query, array('transaction' => $this->writeTransactionKey()), $dbHandle);
	}

	$this->closeDBHandle($dbHandle);

	return true;
}

Csak egy tipp: probald meg elobb letrehozni a tablat, es utana jogot adni hozza pread-nek.

Az működik. És bár ez lesz a végső megoldás, de az ALTER DEFAULT PRIVILEGES pont ezt hivatott kikerülni, hogy ne kelljen minden egyes tábla létrehozása után megadni a jogokat is.
Bár mondjuk a végső megoldás valószínű az lesz, hogy csoportot is létre kell majd hozni, a csoport lesz a tulajdonosa a táblának és ahhoz fognak tartozni a userek.
És az a rohadt idegesítő, hogy ha egyesével beütöm ugyanezeket a parancsokat, akkor meg szépen működik.
Az egyetlen ami még eszembe jut, hogy nem jön létre a tábla összes tulajdonsága addigra, mire meghívom az ALTER DEFAULT PRIVILEGES parancsot és emiatt nem kerül be helyesen a jogosultság.

Szerintem itt a tranzakcio hatarokkal lesz a gond. Ha kezzel irod be kliensbe, ott altalaban minden sor sajat tranzakcioban megy.
Amikor PHP-ben kuldod be, ott lehet, hogy csoportositja. Mivel ALTER DEFAULT PRIVILEGES csak a kiadasat kovetoen letrehozott objektumokra vonatkozik, igy a vele egy menetben letrejottek szurke zonaba esnek. Ha akarom neki van igaza, ha akarom nem…

Ismét lenne egy érdekes kérdésem, amivel kapcsolatban nem találtam segítséget. Természetesen megint postgresql-ről van szó.

Van egy adatbázis dump sql fájlom. Ha törlöm az adtbázist, létrehozom újra, és a pg_restore segítsével visszaállítom, akkor tökéletesen működik, nem ír semmi hibát sem. Viszont teszt miatt szeretném csak az adatokat visszaállítani. A táblákat létrehozom előtte. A visszaállítás során a foreignt key-jel van a baja, mint ha nem jó sorrendben akarná visszatölteni az adatbázist.

Ha kiexportálom a backup sql adatokat -l (kis L) kapcsolóval, abban látom is, hogy ábc sorrendben vannak benne a visszatöltendő adatok és ez így nyilván nem jó. Ugyanakkor, ha “táblástól” akarom visszaállítani, akkor meg működik. (Egyébként próbáltam a -t kapcsolóval a helyes sorrendben megadni a táblákat, de ugyanúgy elhasalt).

Szóval, ha a -l (kis L) kapcsolóval kiexportálom a listát, és sorba rakom a sorokat, ahogy megköveteli a logika (plusz kitörlök szándékosan egy táblát, amit nem szeretnék visszaállítani), akkor pedig ezt a hibát dobja:

pg_restore: [tar archiver] restoring data out of order is not supported in this archive format: “3087.dat” is required, but comes before “3085.dat” in the archive file.

A vicces ebben az, hogy a fájlban a 3087 később van, mint a 3085, mégis azt írja, mint ha a 3085 előtt lenne a 3087.

Hogyan tudnám csak a tábla adatait visszamásolni? Lehet, hogy tényleg táblánként kellene mennem egyesével és nem egy parancsban?

Úgy látszik tényleg külön parancsonként -t opcióval megoldható a helyes sorrend. Annyi különbséggel, hogy először importáltam magát az adott táblát és ugyanabban a parancsban importáltam a hozzá tartozó sequence-t is. Így már nem dobott foreign hibát és látszólag beimportált mindent.

Tehát készítettem egy bash-t, amely szépen sorban táblánként importálja az adatokat. Van 10 tábla és egyesével készítettem el a helyes sorrend alapján így (gépemről futtatva dockerben) ez működik:

docker exec -ti psql bash -c 'pg_restore \
    --format=t \
    -U root_user_name \
    -d dbname \
    -a \
    -t tabla \
    -t tabla_id_seq \
    < "/var/lib/postgresql/data/backup.sql"\
'

Amikor en meglevo srukturaba toltok vissza dumpot, akkor a pg_restore ‘–data-only’ kapcsolojat szoktam hasznalni. Elvileg triggerkent futnak a constraint/foreign key checkek, ezert veletlenul pont van a pg_restore-nak ‘–disable-triggers’ kapcsoloja, hogy ha dumpot tolt vissza az ember, akkor semmilyen trigger se fusson le, mert az egyeb mas problemat is szulhet.

1 kedvelés

Igen, a --data-only rövidítése a -a kapcsoló, amit én is használtam.
A disable triggers nem jutott eszembe, bár csak a létrehozás a módosítás dátuma oszlopra van triggerrem azt hiszem. :) De igazad van, ezt ki illene kikapcsolnom. :)
Kíváncsiságból kirpróbáltam a --disable-trigger kapcsolót, és úgy is ilyen hibákat dobott:

pg_restore: [archiver (db)] COPY failed for table "pages": ERROR:  duplicate key value violates unique constraint "pages_pkey"
DETAIL:  Key (id)=(59) already exists.

De ami érdekes, hogy 10 táblám van és mindegyikre dob egyet-egyet, de csak ennyit.
Így próbáltam most meg visszaállítani:

docker exec -ti psql bash -c ' \
        pg_restore \
        --format=t \
        -U root_user_name \
        -d dbname \
        -a --disable-triggers \
        < "/var/lib/postgresql/data/backup.sql"\
    '

Bár hozzátenném mindenesetre, hogy amúgy is ki kell hagynom egy táblát, mert az máshonnan jön. Viszont akkor sem értem, hogy miért nem jó, ha a tábla sorrendet megpróbálom meghatározni a -t kapcsolókkal egy parancsban? Ha külön parancsokban egy-egy tábla és a hozzá tartozó sequence van csak, akkor működik szépen és egy darab hibát sem dob. (Lehet ilyenkor a foreign értékeket újra előállítja és azért nem dob hibát?)
Próbáltam listába kiexportálni a backup tartalmát és megváltoztatni a sorrendet, de úgy sem volt jó. Pedig a hivatalos leírás és példa szerint lehetséges az amit csináltam. :)

De amit itt most irsz, az duplicate key, az nem az amit korabban jeleztel, hogy baj van a constraintekkel. A constraintek vizsgalata a triggereknel van, a duplicate key pedig akkor van, ha egy oszlopra beallitottad hogy unique a tartalma, es be szeretnel szurni meg 1 olyan sort, amiben ismetlodes van. Biztos hogy rendben van az a dump?

Csak remélem, hogy rendben van a dump, minden nap új készül. És ami érdekes, ha droppolom az adatbázist és újra létrehozom, és utána restore-olom, és abban “nem válogatok”, akkor tökéletesen visszaállítja az adatbázist. Pontosabban nyilván nem ellenőriztem le minden egyes táblát, hogy megvan-e minden sor, de maga a restore egyáltalán nem dob semmi hibát sem.

nade, amikor elszall azzal, hogy duplicate, akkor tenyleg azt csinalod, hogy be akarsz szurni ket olyan sort, ahol a beszurando adatokban egy unique flaggal rendelkezo mezoben ugyonazok az adatok vannak. Ha tenyleg van ilyened a dumpban, akkor ott a dump nem jo. Ha a jelenlegi adatbazisodban is van ilyen es megis be van kapcsolva arra az oszlopra az unique, akkor ott valami para van.

Ezek szerint én rontottam el valamit (szerencsére). Ugyanis Vapor projektről van szó. Vapor migrate paranccsal létrehozza a db tábláit, valamint egy sajátot, amelyben menti, hogy mely migrációs lépéseket hajtotta már végre. Kipróbáltam mégegyszer de most úgy, hogy migrálás után töröltem a vapor migrációs tábla adatait is. Így lefuttatva a --data-only és a --disable-triggers kapcsolókkal tényleg nem dobott duplicate hibát, míg ugyanezt végigjátszva a --disable-triggers nélkül viszont ugyanazokat a duplikátum hibákat írta, mint korábban írtam. Szóval valójában jó volt a dump, csak egyrészt most próbáltam először csak adatokat visszaállítani és nem egész adatbázist, így nem tudtam a --disable-triggers kapcsolóról, másrészt valamit nagyon benéztem menet közben, mert most egyértelműen ok minden. :) Köszi a türelmet! :)

1 kedvelés

Mi pg_basebackup-ot használunk ami minden éjszaka készít egy új full backupot. Ebben a konfig fileok is benne vannak. Ezt lehet ötvözni azzal, ha a WAL logok folyamatosan streamelve vannak, hogy egy tetszőleges időpontra lehessen vissza állni.

1 kedvelés

Csak kíváncsiságból:
ezzel kezdődik a pg_basebackup leírása:

pg_basebackup — take a base backup of a PostgreSQL cluster

Tehát csak clusterezett adatbázisnál lehet ezt használni (bár kétlem) vagy csak azért írják így, mert ott van értelme? :slight_smile:
Ahogy hirtelen utána olvastam a WAL log többek között performanica hatékonyságból jó, bár úgy sejtem, hogy ez kell a clusterezett adatbázisok közötti kommunikációhoz?
Ugyanakkor nekem az adatbázisom nincs és nem is lesz clusterben, mert az alkalmazásomat nagyon max 4 ember használja egyszerre, és az elmúlt nem is tudom 2-3 hónapban még csak egyetlen egy írás sem volt. :slight_smile: Szóval úgy látom ez számomra jelenleg nem releváns. Vagy rosszul látom?
Cluster nélkül is ki tudom ezt próbálni? Már csak teszt miatt is kérdezem.

Elnezest a kesei valaszert, ritkan jutok rendesen gephez (sajathoz). :-)
Szoval a pg_basebackup egy adott futo postgres instance-rol tud egy full backupot csinalni, ami tartalmazza a konfig fileokat, beallitasok, pg_hba-t, mindent. Ehhez nem kell cluster, csak egy postgresql server ahol a pg_hba helyesen be van allitva (most nem vagyok benne biztos, de mintha ehhez a replication role kell).
A pg_basebackupot egy masik szerverrol kell inditani. Ez csatlakozik a postgres szerverhez a postgres porton (5432 alabol, ehhez kell a helyes pg_hba) es az adatbazis mellett letolt minden mas beallitast is. Ebbol a backupbol teljesen nullarol ujra lehet epiteni az adatbazist es mivel a beallitasok is le vannak mentve, azzal se kell bajlodni.
Ehhez jon hozza WAL, ami tartalmazza az osszes tortenest az databazisban, legyen az adat hozzaadasa, torles, vagy csak egy sima query. A postgres (talan defaultbol be van kapcsolva) ki tudja irni ezeket az operaciokat lemezre, amit aztan archivalni tudsz. Erre van beepitett megoldas, az archive_command, amivel azt tudod megszabni, hogy amennyiben egy ilyen WAL file elkeszul (vagy eler egy file meret limitet, vagy max idot), akkor ezt a filet valahogy lemetse mielott torlesre kerul. Ez a parancs lehet egy scp is, vagy egy full script, a lenyege, hogy a filet valahogy a backup szerverre juttassa.
Ezek utan, ha beut a baj es vissza kell szerezni az adatokat, csak a basebackup altal letrehozott foldert kell visszahuzni, illetve azt a foldert, ahova a WAL fileok le vannak mentve. Recovery kozben a postgres elkezdi beolvasni a WAL fileokat es visszatudja allitani az adatbazist.
Ennek az elonye, hogy gyakorlatilag folyamatosan van backup, a legtobb, ami elveszhet, azok azok a muveletek, amik az utolso wal logfile mentes ota tortentek (ennek ideje ugye allithato). A masik elonye, hogy meg tudod adni, pontosan mikorra legyen az adatbazis visszaallitva, szoval ha veletlenul pl. egy table droppolva lett delutan, akkor vissza tudod allitvani masodpercekkel az esemeny elotti allapotot, nem csak az utolso dumpot.
Pl. minden ejszaka keszulhet egy uj basebackup es a wal fileok par napig vannak megtartva.
Meg egy dolog, hogy basebackup elotti idore nem tudsz visszamenni meg akkor sem, ha megvannak a wal fileok!
Erdemes ezt az oldalt elolvasni: https://www.postgresql.org/docs/12/continuous-archiving.html

2 kedvelés

Köszi az összefoglalót, akkor érdemes mindenképpen kipróbálnom.
Egyébként tényleg fontos másik szerverről indítani? Bár valószínű, hogy úgy is egy másik docker containert készítenék erre.

Mert a pg_basebackup ugy mukodik, hogy egy masik szerverrol huzza le az adatbazist :-)
Mondjuk igy utanaolvasva, valoszinuleg menne lokalisan is, ha egy masik mappat adsz meg neki celnak.

1 kedvelés

Ezért kérdeztem rá, mert hirtelen nem találtam rá magyarázatot. :)
De amúgy nem is annyira egyszerű megoldani így első átgondolásra Docker környezetben. Ugyanis a WAL archive_commandnak ha jól sejtem akkor az adott szerveren vagyis containerben kell futtatni, és onnan “valahogy” ki kell törnöm kívülre. Majd ezt kívül észlelnem és elindítani a feltöltés mechanikát. A postgresql containerem most eredeti image-ből van, nem szívesen raknék bele semmit sem. :). Nyilván kívül lehet rá írni egy cron jobot, ami időnként figyeli az archiválás mappáját, de akkor így már nem lesz azonnali mentés.

Lehet en vagyok regimodi, de nekem egy kicsit furcsa adatbazist kontenerben futtatni. Nem ismerem jol a dockert, de mintha nem garantalna, hogy a szerviz ami a kontenerben fut, szepen le tud allni, siman ki tudja loni. Ilyen esetben meg nincs garantalva, hogy minden ki van irva a lemezre.
Igazabol innen mar csak egy lepes clustert csinalni es akkor a visszaallas is gyorsan megvan. :-)

1 kedvelés

Nyilván, ha hibázik a Docker, akkor igen, ilyen esetek előfordulhatnak, de ugyanúgy előfordulhat ez fizikai szerver vagy virtuális szervernél is (akaratlanul is tudok nyomni a VM-wareben Power Off gombot). Persze nyilván ehhez a megfelelő együttállásnak is meg kell történnie. Hozzátenném azt is hogy megvan írva szépen postresql esetén a grace shutdown, szóval amikor stop-ot nyomok a docker servicenek, akkor szépen megáll a postgresql és nem csak meghal…
A Docker nyilván nem fogja biztonságosabban futtatni az alkalmazást. Az előnye ott van, hogy a Dockerfile vagy megfelelő bash scripttel át tudom költöztetni szolgáltatók és/vagy linux disztribúciók között az alkalmazásomat. Tehát ha nagyon beüt a crach az Amazonnál (ami majdnem, hogy kizárt), max fél óra alatt újra fel tudom húzni máshol az egész alkalmazás szerveremet. (Na már persze, ha az Amazon DNS-hez is hozzáférek :D)

Egyébként persze a következő lépés a cluster lenne. Clusterhez pedig Kubernetes. Ahol szintén megvannak a hátrányok. Mondjuk ott pont az egyik nagyon nagy hátrány, hogy a Storage miatt valószínűleg Amazonhoz kötném magam és nem tudnám a Kubernetes rendszert olyan gyorsan feléleszteni máshol. :)

Mindenesetre az alkalmazásom csak “belsőleg” van használva, kb. 10-15 embernek van hozzáférése, ráadásul nagyon max egyszerre eddig talán 3 ember használta. Így a basebackup is kicsit túlzásnak történik. Ugyanakkor időszakonként amikor beindul a használat, akkor jól jöhet a pontos időpontra való visszaállás. De tervezem, hogy az alkalmazásban oldok meg eleve backup dolgokat és csak a legvégsőbb esetben állnánk vissza adatbázis backupból. Akkor meg vélhetően elég a napi dump. :)