Attributi del singolo file
Il nuovo node richiede una tupla di metadati al backend, che usa una sola info(). Il percorso precedente chiedeva separatamente esistenza, tipo, dimensione e modifica: quattro HEAD sullo stesso file.
Benchmark comparativo S3
Genropy legacy, Genro Storage e client diretti alla prova. Tempi, richieste HTTP e controlli di correttezza per capire dove nasce la differenza.
01 / Cosa emerge
Intervenendo soltanto su Genro Storage, gli attributi del file passano da 6,70 a 1,52 ms, contro 3,27 ms del legacy nella raccolta finale. Copia e spostamento restano più lenti e non sono stati ottimizzati.
Per gli attributi del file: da 4 a 1 richiesta nel nuovo, 2 nel legacy. Per un albero di otto file: 1 richiesta nel nuovo, 18 nel legacy. Sono due comportamenti diversi dello stesso sistema, misurati a un worker con cache dei metadati svuotata.
Le prove riguardano S3 su MinIO locale. Nessuna misura su Hetzner, SFTP o WebDAV è inclusa. La suite è già configurabile per un successivo confronto sull’object store Hetzner.
02 / Le raccolte
Le tre raccolte iniziali hanno tre ripetizioni; le due raccolte prima/dopo ne hanno nove. Ogni caso ha un round di warmup, mentre l’avvio è osservato una sola volta per client. Ordine dei casi rimescolato con seed registrato; dati sintetici deterministici e verifica del risultato fuori dal tratto cronometrato.
Un campione è un batch con una chiamata per worker: @@BATCH_COUNT@@ batch corrispondono a @@CALL_COUNT@@ operazioni. Sono conservati a parte @@WARMUP_COUNT@@ batch di warmup e @@STARTUP_COUNT@@ osservazioni di inizializzazione. La terza raccolta confronta quattro client, le prime due tutti e sette. Le ultime due confrontano legacy e Genro Storage, prima e dopo la modifica.
| Percorso | Implementazione |
|---|---|
| Genropy legacy | StorageNode e servizio aws_s3 reali, con un contesto minimo al posto dell’intero sito. |
| Genro Storage | API pubblica StorageManager / StorageNode del checkout misurato. |
| Genro · versioni disattivate | Stesso percorso, con version_aware=False sul client s3fs creato, solo per diagnosi. |
| FsspecBackend | Backend del repository chiamato direttamente, senza manager e node. |
| s3fs diretto | S3FileSystem con version awareness attiva, come nel nuovo storage. |
| boto3 diretto | API per oggetti S3; download gestito per il file temporaneo locale. |
| smart_open diretto | smart_open per lettura e scrittura; boto3 per le altre operazioni. |
03 / Risultati selezionati
Dati dopo l’ottimizzazione: nove ripetizioni, un worker e cache dei metadati fredda. Nei grafici dei tempi, una barra più corta indica una durata minore.
Ogni grafico usa una propria scala lineare che parte da zero. File da 1 KiB; l’albero contiene otto file da 12 byte.
@@SMALL_TABLE@@Le tabelle ampie scorrono orizzontalmente.
Il nuovo node richiede una tupla di metadati al backend, che usa una sola info(). Il percorso precedente chiedeva separatamente esistenza, tipo, dimensione e modifica: quattro HEAD sullo stesso file.
Il listing riempie la cache dei metadati di s3fs. Le successive letture degli attributi possono riusarla anche se la cache era vuota all’inizio del batch.
Medie di richieste per operazione; setup e verifiche esclusi. HEAD = HeadObject, LIST corrente = ListObjectsV2, LIST versioni = ListObjectVersions.
Qui intervengono anche buffering e modalità di trasferimento. Una singola lettura completa non rappresenta tutti i possibili carichi S3.
@@TRANSFER_TABLE@@Con cinque versioni per chiave il listing risulta più rapido nella variante disattivata, mentre gli attributi del singolo file non migliorano. Questo piccolo carico non misura la crescita del costo su un bucket con molta storia.
@@VERSION_TABLE@@04 / Tutta la matrice
Seleziona una combinazione realmente eseguita e confronta tutti i client disponibili. La selezione iniziale mostra il codice ottimizzato. Le raccolte iniziali mantengono i dati storici dei client diretti. I controlli aggiornano sia il grafico sia la tabella.
| Client | Campioni | Mediana ms | p95 ms | Ops/s | HTTP/op | MiB/s logici |
|---|
Scorri la tabella per vedere tutte le metriche.
Include anche warmup e avvio nel JSON; il CSV contiene le sintesi. Nessuna credenziale o percorso privato della macchina.
05 / Correttezza prima della velocità
Un risultato incompleto non deve sembrare più veloce. La suite verifica dati, spostamenti e listing prima di accettare i campioni.
| Verifica | Passati | Falliti | Saltati |
|---|---|---|---|
| Contratti differenziali · sorgente originale | 10 | 4 attesi | 0 |
| Stessi contratti · sorgente modificato | 14 | 0 | 0 |
| Suite completa finale, inclusi i contratti | 584 | 0 | 14 |
I quattro fallimenti sul sorgente originale dimostrano le discrepanze: data inventata per directory S3 e quattro HEAD per gli attributi, entrambi con e senza versioning. I 14 casi differenziali sono inclusi nei 584, non aggiuntivi. Coprono file vuoti, binari, nomi Unicode, letture, file temporanei, assenza, directory implicite, sovrascritture, copia, spostamento e cancellazione. Non certificano tutta l’API legacy.
I 14 salti comprendono servizi opzionali o configurazioni assenti, casi già esclusi e il bug preesistente S3 set_metadata. I salti non sono successi.
Il legacy restituisce un elenco incompleto oltre la capacità della sua singola pagina. Il test passa perché il controllo rifiuta quel risultato e accetta il listing completo del nuovo storage. Non significa che il limite del legacy sia stato corretto.
La prima raccolta lunga di sviluppo è stata interrotta: il contatore perdeva il collegamento al rinnovo del client legacy. La strumentazione è stata corretta e verificata con un rinnovo reale. Quella raccolta interrotta è esclusa da questo documento. È esclusa anche la baseline preliminare 33e21eba, eseguita in parte insieme ai test; il confronto usa la successiva baseline senza test concorrenti.
Questi esiti appartengono alla verifica già eseguita durante la preparazione della suite. La generazione del documento non riesegue benchmark o test di integrazione.
06 / Metodo e riproducibilità
Letture consumate interamente; scritture completate; copie verificate tramite rilettura e SHA-256; spostamenti verificati anche sull’assenza della sorgente. I listing devono contenere tutti i nomi attesi.
La costruzione del node è inclusa nelle operazioni legacy e nuove. Preparazione dei dati, hash e controlli successivi sono esclusi. L’inizializzazione del client è registrata a parte, una volta per client.
“Fredda” svuota la cache dei metadati del client prima del batch. Connessioni e cache di server e sistema operativo non vengono azzerate. “Calda” conserva la cache, senza garantire che ogni richiesta trovi un dato già disponibile.
Le API sincrone vengono chiamate da pool di thread. La latenza parte dentro il worker; il throughput include l’attesa del batch. Non è un confronto di API asyncio native. Per copie e spostamenti i MiB/s sono byte logici degli oggetti, non traffico di rete.
Gli eventi HTTP botocore/aiobotocore contano anche i retry. Le richieste del client di controllo, usato per setup e verifica, sono escluse. Non vengono registrati URL firmati, header o credenziali.
Il legacy usa il proprio servizio S3 e StorageNode con un contesto minimo, senza avviare il sito o il database. Gli adapter s3fs possiedono client distinti. Ogni raccolta usa un prefisso casuale dedicato nello stesso bucket.
attrs restituisce modifica, dimensione e tipo. tree_attrs elenca la directory e raccoglie gli attributi di ogni file. local_path ottiene un file locale temporaneo, lo legge interamente e ne include la pulizia.
La mediana e il p95 usano le latenze delle singole chiamate nei batch validi. Il p95 segue il metodo nearest-rank. Ops/s e throughput dividono il lavoro totale per la somma dei tempi di batch: non derivano dall’inverso della mediana. Un batch errato è escluso per intero dalle statistiche.
Il numero di byte selezionato non descrive il payload del listing: per le prove di directory i file contengono 12 byte ciascuno. L’avvio client ha una sola osservazione; il suo p95 non è statisticamente informativo.
Sorgente e metadati di distribuzione differiscono. Il codice misurato è il checkout Genro Storage 0.8.0, mentre i metadati della distribuzione installata riportano @@PACKAGE_META_VERSION@@. Il percorso del sorgente registrato punta a src/genro_storage del checkout; commit e hash identificano il codice effettivamente misurato.
I client s3fs registrano blocchi da 50 MiB, cache readahead e cache dei listing attiva. La version awareness è attiva salvo nella variante diagnostica. Le prime due raccolte e le due raccolte prima/dopo usano un bucket non versionato; la terza un bucket versionato con cinque versioni per chiave. Gli hash distinguono il sorgente originale da quello ottimizzato, anche a parità di versione nominale.
Dalla radice del repository, dopo aver installato le dipendenze del benchmark e del legacy:
python -m pip install -e '.[benchmark,dev]'
export BENCH_LEGACY_ROOT=/percorso/al/checkout/genropy
python -m pip install -e "$BENCH_LEGACY_ROOT/gnrpy"
docker compose -p genro-storage-bench \
-f benchmarks/compose.yaml up -d --wait
# 1. File piccoli
python -m benchmarks --smoke --create-bucket
# 2. Trasferimenti da 1 e 32 MiB
python -m benchmarks --sizes 1048576 33554432 \
--operations read write copy move local_path \
--workers 1 4 --list-count 8 --repeats 3 --caches cold
# 3. Cinque versioni per chiave
python -m benchmarks --smoke --enable-versioning --history 5 \
--adapters legacy genro genro-unversioned s3fs \
--operations attrs listing tree_attrs --workers 1 --sizes 1024
# 4–5. Matrice prima/dopo, su bucket non versionato
# Eseguire sul sorgente originale e su quello ottimizzato, senza test concorrenti.
BENCH_S3_BUCKET=genro-storage-before-after python -m benchmarks --create-bucket \
--adapters legacy genro --sizes 1024 33554432 \
--workers 1 --repeats 9 --list-count 8 --caches cold
# Test della suite sul servizio reale
MINIO_ENDPOINT=http://127.0.0.1:29000 BENCH_TEST_ENDPOINT=http://127.0.0.1:29000 \
python -m pytest tests benchmarks/tests -q -o addopts='' -p no:cacheproviderPer Hetzner: impostare BENCH_S3_ENDPOINT, BENCH_S3_BUCKET, BENCH_S3_REGION, BENCH_S3_ACCESS_KEY e BENCH_S3_SECRET_KEY nell’ambiente, poi usare python -m benchmarks --target hetzner --smoke. Le credenziali non devono essere inserite in questo documento.