ARPA-SIMC / dballe

Fast on-disk database for meteorological observed and forecast data.
Other
19 stars 6 forks source link

import bufr in posgres by python freeze #282

Closed pat1 closed 9 months ago

pat1 commented 11 months ago

test bufr are attached bufr.zip with sqlite:

dbadb wipe --url=sqlite:troppograndi.sqlite
time dbadb import  --url=sqlite:troppograndi.sqlite troppograndi

real    0m12,845s
user    0m12,506s
sys 0m0,217s
dbadb wipe --url=sqlite:troppograndi.sqlite
time python3 test.py 

real    0m12,912s
user    0m12,815s
sys 0m0,037s

with posgres:

time dbadb import  --dsn="postgresql://user:password@localhost/report_fixed" troppograndi

real    0m13,277s
user    0m11,150s
sys 0m0,481s
 time python3 test.py
DO NOT TEMINATE
KeyboardInterrupt

real    13m39,732s
user    0m9,840s
sys 0m3,671s

test.py

import os,io
import dballe

#dsn="postgresql://user:password@localhost/report_fixed"
#dsn="sqlite:troppograndi.sqlite"

with open("troppograndi","rb") as myfile:
    totalbody=myfile.read()

totalbodyfile = io.BytesIO(totalbody) 
# Connect to the database
db = dballe.DB.connect(dsn)
importer = dballe.Importer("BUFR")
# Start a transaction
with db.transaction() as tr:
    with importer.from_file(totalbodyfile) as f:
        for dballemessage in f:
            try:
                tr.import_messages(dballemessage,overwrite=True, update_station=True,import_attributes=True)
            except KeyError:
                print("Message was rejected importing in DB: not all metadata defined! ")
edigiacomo commented 11 months ago

La issue non è assegnata a me, ma ho provato a riprodurlo su un'immagine Rocky Linux 8 con moncic-ci e non riesco, perché arriva in fondo (ci mette il suo tempo ma ci arriva):

$ monci shell rocky8
[root@mc-folusa ~]# dnf install -qy wget dballe postgresql-server
[root@mc-folusa ~]# /usr/bin/postgresql-setup --initdb
[root@mc-folusa ~]# su - postgres
[postgres@mc-folusa ~]$ createdb report_fixed
[postgres@mc-folusa ~]$ dbadb wipe --dsn=postgresql:///report_fixed
[postgres@mc-folusa ~]$ wget https://github.com/ARPA-SIMC/dballe/files/13588478/bufr.zip && unzip bufr.zip
[postgres@mc-folusa ~]$ time python3 test.py 

real    8m32.439s
user    0m32.450s
sys 0m3.310s

La butto lì: hai provato ad aggiornare postgres e dballe?

[root@mc-folusa ~]# rpm -qi postgresql-server | head -n3
Name        : postgresql-server
Version     : 10.23
Release     : 2.module+el8.9.0+1500+e42a8b96

[root@mc-folusa ~]# rpm -qi dballe | head -n3
Name        : dballe
Version     : 9.5
Release     : 1.el8

@spanezz spero che possa essere utile

pat1 commented 11 months ago

unica differenza: rpm -qi postgresql-server | head -n3 Name : postgresql-server Version : 12.15 Release : 3.module+el8.9.0+1331+6f33d496

brancomat commented 10 months ago

In attesa di replica del problema e di condivisione di accesso al server problematico

pat1 commented 10 months ago

replicato sulla macchina di test fare riferimento alla cartella dballe_issue_282 nella home di root

spanezz commented 9 months ago

Ho guardato al problema nella macchina di test.

Una differenza fondamentale nei due comandi è che python ha overwrite=True, update_station=True, import_attributes=True, e quindi andrebbe confrontato con dbadb import .. --full-pseudoana --overwrite invece che con dbadb import.

Ho aggiunto un po' di progress reporting a test.py (lo trovi come test1.py nella macchina di test):

import os,io
import dballe
import time

dsn="postgresql://rmap:rmap@localhost/test"
#dsn="sqlite:troppograndi.sqlite"

with open("troppograndi","rb") as myfile:
    totalbody=myfile.read()

totalbodyfile = io.BytesIO(totalbody) 
# Connect to the database
db = dballe.DB.connect(dsn)
importer = dballe.Importer("BUFR")
last_time = time.time()
# Start a transaction
with db.transaction() as tr:
    with importer.from_file(totalbodyfile) as f:
        for idx, dballemessage in enumerate(f):
            if idx % 100 == 0:
                current_time = time.time()
                mps = 100 / (current_time - last_time)
                print(f"Import message #{idx}/142026 ({mps:.2f} mps)")
                last_time = current_time
            try:
                tr.import_messages(dballemessage,overwrite=True, update_station=True,import_attributes=True)
            except KeyError:
                self._logging.error("Message was rejected importing in DB: not all metadata defined! ")

Facendolo girare si vede che il conto dei messaggi al secondo cala man mano che procede lo script. Similmente, se lanci:

pv troppograndi | time dbadb import  --dsn="postgresql://rmap:rmap@localhost/test"  --full-pseudoana --overwrite

vedi che i kb/s di messaggi processati man mano cala, e l'ETA aumenta.

Del resto, usando update_station stai chiedendo a dballe, per ogni singolo messaggio importato, di riscrivere tutti i dati stazione: l'idea per la gestione dei dati stazione era che il primo messaggio importato per una stazione li scrive, e al limite si possono importare dei messaggi coi dati piú aggiornati alla bisogna per aggiornarli. Ci sta che usare --full-pseudoana o update_station=True per importare un pacchetto da 140mila messaggi dia delle performance inaccettabili.

Però sí, aggiungendo un report sul progress non è tanto piantato, quanto è un caso d'uso che non scala

pat1 commented 9 months ago

Come riportato in questa issue sopra queste sono le performance con sqlite:

dbadb wipe --url=sqlite:troppograndi.sqlite
time python3 test.py 

real    0m12,912s
user    0m12,815s
sys 0m0,037s

dovendo usare overwrite=True, update_station=True, import_attributes=True l'unica soluzione è utilizzare sqlite o è possibile avere in qualche modo performance simili in postgresql?

spanezz commented 9 months ago

Non ho idea di come poter avere performance simili con postgresql: la struttura dei DB è la stessa.

Puoi provare a dividere il file di input in transazioni di un migliaio di BUFR l'una per vedere se migliora qualcosa?

pat1 commented 9 months ago

i dati non li produco io, il python che uso per fare l'importazione è quello sopra a parte la lettura da file in quanto i dati non risiedono in un file. C'è un modo per farlo in python ?

spanezz commented 9 months ago

Con qualcosa tipo https://docs.python.org/3.12/library/itertools.html#itertools.batched (da python 3.12) o un'equivalente implementazione manuale

pat1 commented 9 months ago

ma scusa con itertools come faccio a non troncare a mezzo un bufr ? Se lo spezzo in 10 parti rompo 9 bufr. Oltre al fatto che ogni importazione mi darebbe errore ...

spanezz commented 9 months ago

Puoi dividere in gruppi l'iterazione di for dballemessage in f: che itera messaggio per messaggio, e aprire una transazione per gruppo invece della transazione all'esterno

pat1 commented 9 months ago

una transazione per messaggio:

time python3 test.py

real    6m47,500s
user    0m22,844s
sys 0m29,945s

una transazione per 10 messaggi:

time python3 test.py

real    2m13,508s
user    0m8,995s
sys 0m10,856s

una transazione per 100 messaggi:

time python3 test.py

real    1m29,868s
user    0m6,518s
sys 0m6,011s

una transazione per 1000 messaggi:

time python3 test.py

real    1m59,135s
user    0m6,362s
sys 0m7,020s
pat1 commented 9 months ago

sqlite continua ad avere performance molto ma molto migliori. una transazione per messaggio:

time python3 single.py

real    0m35,258s
user    0m32,130s
sys 0m2,932s

una transazione ogni 100 messaggi:

time python3 test.py

real    0m12,980s
user    0m4,433s
sys 0m0,220s

Questa cosa mi lascia qualche dubbio, ma se il mio dubbio è considerato effimero si può chiudere la issue.

pat1 commented 9 months ago

la risposta per postgres credo sia qui: https://stackoverflow.com/questions/60166976/why-is-postgres-transaction-block-so-much-slower-and-how-to-solve

rimane poco chiaro come mai sqlite invece non ha questo problema.

spanezz commented 9 months ago

Perché sqlite è una bestia completamente diversa da postgresql, fatta per gestire situazioni diverse.

In particolare, sqlite non deve gestire scritture concorrenti come invece deve fare postgresql, e quindi può prendere scorciatoie che postgres non si può permettere