Btrfs: elfogyott a hely, pedig nem. Miért? Mi a megoldás?

Sziasztok!

Azzal a problémával szembesültem, hogy btrfs filerendszerre nem tudok kiírni adatokat elsőre. Másodjára viszont sikerül. Nézzük az alábbi tesztkörnyezetet:

Hozzunk létre egy image file-t:

truncate -s 200M btrfs.img
Formázzuk meg btrfs-re úgy, hogy a kis méret miatt a metaadat és adat terület kevert legyen, s ne fussunk abba bele, hogy vagy a metaadat számára fenntartott hely fogy el, vagy az adatok számára fenntartott, duplikátum semmiből se legyen:

mkfs.btrfs -L home -d single -m single -M btrfs.img
SMALL VOLUME: forcing mixed metadata/data groups

WARNING! - Btrfs v3.12 IS EXPERIMENTAL
WARNING! - see http://btrfs.wiki.kernel.org before using

Turning ON incompat feature 'mixed-bg': mixed data and metadata block groups
Turning ON incompat feature 'extref': increased hardlink limit per file to 65536
Created a data/metadata chunk of size 8388608
fs created label home on btrfs.img
        nodesize 4096 leafsize 4096 sectorsize 4096 size 200.00MiB
Btrfs v3.12

Hozzuk létre a csatolási pontot, majd csatoljuk fel a filerendszerünket tömörítés opcióval:

mkdir /mnt/home
mount -o loop,compress=lzo btrfs.img /mnt/home


Másoljunk rá adatokat. Ezek mindösszesen 66 MB terjedelműek:

rsync -a /home/locsemege/install/cd/hb/4/rootfs/home/ /mnt/home/
rsync: write failed on "/mnt/home/locsemege/.mozilla/firefox/g4byxabc.default/places.sqlite": No space left on device (28)
rsync error: error in file IO (code 11) at receiver.c(389) [receiver=3.1.0pre1]

Nézzük, mit mond a filerendszer utility-je:

btrfs fi df /mnt/home/
System, single: total=4.00MiB, used=4.00KiB
Data+Metadata, single: total=196.00MiB, used=13.39MiB
btrfs fi show
Label: home  uuid: b2c38ec4-eac5-4ac4-ae7a-a5933541c65e
        Total devices 1 FS bytes used 13.39MiB
        devid    1 size 200.00MiB used 200.00MiB path /dev/loop0

Btrfs v3.12

Most töröljük le, amit csináltunk:

rm -Rf /mnt/home/*
ll /mnt/home/

total 0
btrfs fi show
Label: home  uuid: b2c38ec4-eac5-4ac4-ae7a-a5933541c65e
        Total devices 1 FS bytes used 13.39MiB
        devid    1 size 200.00MiB used 200.00MiB path /dev/loop0

Btrfs v3.12
btrfs fi df /mnt/home/
System, single: total=4.00MiB, used=4.00KiB
Data+Metadata, single: total=196.00MiB, used=28.00KiB

Másoljuk újra ugyanazokat az adatokat:

rsync -a /home/locsemege/install/cd/hb/4/rootfs/home/ /mnt/home/

Hm... most sikerült! Ez hogy lehet?

btrfs fi show
Label: home  uuid: b2c38ec4-eac5-4ac4-ae7a-a5933541c65e
        Total devices 1 FS bytes used 26.30MiB
        devid    1 size 200.00MiB used 200.00MiB path /dev/loop0

Btrfs v3.12
btrfs fi df /mnt/home/
System, single: total=4.00MiB, used=4.00KiB
Data+Metadata, single: total=196.00MiB, used=26.30MiB

A kernel a legfrissebb 3.12.6-os. Várom az ötleteket, javaslatokat!

Update1:

Lekérdeztem a filerendszer állapotát közvetlenül a formázást követő mount után. Körvonalazódni látszik a probléma, épp csak azt nem tudom, hogyan lehet kijavítani:

btrfs fi df /mnt/home/
System, single: total=4.00MiB, used=4.00KiB
Data+Metadata, single: total=8.00MiB, used=28.00KiB
btrfs fi show
Label: home  uuid: 457c854b-275f-4193-a44f-613e9dd70a15
        Total devices 1 FS bytes used 32.00KiB
        devid    1 size 200.00MiB used 12.00MiB path /dev/loop0

Btrfs v3.12

Tehát, ha jól látom, az a baja, hogy a rendelkezésre álló 200 MB-ból csak 12 MB-ot foglal első körben a filerendszer. Ebből 4 MB elmegy rendszer célokra, 8 MB marad adatnak és metaadatnak együttesen. Erre viszont természetesen nem fér rá a 66 MB-nyi adat még tömörítve sem. Valami azt súgja, a btrfs automatikusan csinált online filerendszer átméretezést, ezért lehetett az, hogy másodjára sikerült a másolás.

Tehát az a kérdés, hogyan lehet forszírozni, hogy a létrejövő filerendszer valóban kihasználja azt a helyet, amekkora az a device, amit megformáztunk.

Hozzászólások

Jó kérdés. Az biztos, hogy 200MB elég szűkös egy btrfs-nek, még mixed data+metadata módban is.
Esetleg bekapcsolhatod a skinny extents feature-t is, az is segít csökkenteni a metadata által elfoglalt helyet (btrfstune -x).

Most értem haza. Köszönöm a válaszod. Mindjárt nézem tovább.

Azért ilyen kicsi, mert eredendően egy általam reszelt live linux /home-ja lenne, mégpedig RAM-ban - esetleg részben swap-en - tmpfs-re létrehozott image-ben kialakított btrfs. Azért éppen ez, mert tud röptében tömöríteni. Ha a tömörítést nem igényelném, akkor közvetlenül is írhatnék a tmpfs-re. Persze, ha nagy az overhead, akkor semmire sem megyek vele, s jobban járok a tmpfs direkt használatával. Egyelőre úgy érzem, jó lehet ez a btrfs compress=lzo opcióval, csak meg kell győzzem arról, hogy kegyeskedjék az egész területet kihasználni.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

UbiFS-t meg hasonlókat keresgéltem, aztán láttam ezt a hozzászólásod. Most én is nézegetem zram-ot, nagyon jónak néz ki. Jegyzet magamnak:

http://wiki.gentoo.org/wiki/Zram

modprobe zram
echo $((1024*1024*1024)) > /sys/block/zram0/disksize
mkfs.ext4 /dev/zram0
mkdir /mnt/zram
mount /dev/zram0 /mnt/zram

Igen, hallottam róla, viszont nekem úgy tűnik, a Fedora kernelben nincs.

modinfo zram
modinfo: ERROR: Module zram not found.

Noha volt idő, amikor rendszeresen kernelt fordítottam magamnak forrásból, ezt elkerülném, mert egy live image elkészítése elsősorban a squashfs tömörítés miatt sokáig tart - becslésem szerint 25 perc biztosan van -, így a kernel fordítás miatt idegtépően sok ideig tartana egy-egy új live image elkészítése.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Kicsit utána néztem ennek. Úgy látom, a zram-mal baj volt, hogy a kutya sem fejleszti, de most úgy tűnik, belekerül a 3.14-es vanilla kernelbe, így vélhetően a Fedorában is megjelenik majd. De az 2014 tavaszánál nem lesz előbb.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Érdekes, mindeközben a df jó riportot ad vissza:

df -h /mnt/home
Filesystem      Size  Used Avail Use% Mounted on
/dev/loop0      200M   32K  196M   1% /mnt/home

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Nem. Látod, egy reguláris file-on van. Szerintem ebben az esetben fogalma sincs, az a reguláris file min van. Amúgy, ha tmpfs-ben van az image file, amely lehet részben RAM-ban, részben például HDD-n swap-en, akkor is ugyanaz a jelenség, mint amikor tisztán HDD-n van, bár ha fizikai helyet nézünk, akkor a "tisztán HDD" kifejezés a disk cache miatt részint RAM-ot is takar valameddig.

Való igaz, az mkfs.btrfs parancsot még nem próbáltam ki -K (avagy --nodiscard) opcióval, még ezt az egyet megpróbálom.

Ha érdekel és van szabad 10 perced, könnyen ki tudod próbálni, nem véletlenül írtam le, hogyan teszteltem. Kényelmesen, nem kell hozzá újraindítani a gépet, vagy virtualizálni, szóval semmi macera nincs a kipróbálásával.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Azért kérdeztem, mert ssdn a btrfs olyan módba kapcsol, ami totál faszság ssdnél is, és mindenképp ki kell kapcsolni nossd opcióval. Azt csinálja ugyanis, hogy az ssd kímélése céljából random pakolássza az adatokat, ami azt a fajta töredezést, ami meggátolja az adat írását (ami nálad is van) mert nagy blokkok le vannak foglalva kis metaadat miatt, és az adatnak nincs hely... :) Xen gépben pl. alapból ssdnek látja a xen block eszközt, akkor is, ha nem ssd... :)

Törlés után a szanaszét maradt félig használt blokkokat én ezzel a scripttel szoktam optimalizálni:

#!/usr/bin/php
<?php
$all = false;
if ($argv[1] == 'all') {
ob_start();
passthru("mount -t btrfs");
$list0 = ob_get_clean();
$m = preg_match_all("/(^.*? on (.*?) .*\$)+/m", $list0, $matches);
foreach ($matches[2] as $mount) {
$list[] = $mount;
}
} elseif ($argv[1]) {
$list = array($argv[1]);
}
if (is_array($list)) foreach ($list as $mount) {
echo $mount." defrag, optimalizálás!\n";
$cmd = "/sbin/btrfs fi de -v -c ".escapeshellarg($mount);
passthru($cmd);
$cmd = "/sbin/btrfs fi ba start -dusage=25 -musage=25 ".escapeshellarg($mount);
passthru($cmd);
}

------

a hangsúly ezen van:

btrfs fi ba start -dusage=25 -musage=25 ESZKÖZ

Ilyen kis blokk eszköznél egyébként (200 mega) sokkal nagyobb eséllyel fordul elő ez a probléma, mint valós esetben, több gigás eszköznél (de azért ott is van rá példa nagyon sok file sok törlés/írás esetén)

Nem lett jobb. Időközben eldöntöttem, nem btrfs-t fogok használni. Simán tmpfs-en lesz a /home, aztán leállításkor $USER.tar.gz megy pendrive-ra, induláskor meg vissza onnan. Ez utóbbi scriptjeim már készen vannak, működnek is, így elég könnyedén elkészülök vele.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Köszönöm, próbáltam. Mondjuk úgy, érdekesen viselkedett. Konkrétabban az történt, hogy a tömörített file-okat előállította, azon a helyen, ahova fel volt csatolva, látszottak a file-ok, a tartalmuk viszont csupa 0 byte volt. Egyes file-ok esetében pedig I/O error az eléréskor.

Indítottam debug módban is, abból kiderült, melyik függvénye jött vissza hibával, de valahogy nem éreztem magamban erőt ahhoz, hogy hosszasan nézzem a forráskódját.

Ha van időd, kedved, megnézhetnéd, nálad működik-e. A SELinux szabályokra azért nem gyanakszom, mert arról szerintem kapnék értesítést, ha azon akadt volna fenn.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Köszönöm, kíváncsi vagyok.

Minden esetre most csináltam egy live image-et, a /home tmpfs-en van, a felhasználók induláskor tar.gz-ből bemásolódnak, leállításkor mentésre kerülnek, utolsó 3 mentés van meg, a korábbiakat a script törli, így a hely nem fogy el a pendrive-on.

Működik jól, egyelőre több időt nem akarok elbökni vele ma, de idővel úgyis hozzányúlok, magamat ismerve. :)

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Megértem, most eleged lett a hack-ből gondolom.. Viszont nekem kellene :)

Ezt nézegetem:
http://sourceforge.net/apps/mediawiki/fuse/index.php?title=CompressedFi…

Szerk.: zram egyébként olyan szempontból nem jó, hogy nem adja vissza a RAM-ot, ha törölsz belőle - ez nem igazán jó szerintem, ezért keresek mást.

Jaj, ez komoly? Mert ehhez már most sem kell semmit sem tennem. A live úgy néz ki, hogy van egy iso9660 image, benne egy squashfs image, abban egy ext4 snapshot image, amely tartalmazza a rootfs-t. Viszont ez valójában természetesen read only, ugyanakkor ténylegesen rw. Ezt úgy csinálják, hogy ha az ember módosít valamelyik file-on, akkor arról másolat készül RAM-ba, ott tárolja, a nyilvántartásba meg bejegyzi, hogy a snapshotban lévő file már felejtős, pontosabban a RAM-ban van. Nyilván nem nézi a tartalmat, szóval a RAM-ot nem szabadítja fel törléskor vagy akkor, ha az eredetire módosul vissza a file.

Ebből aztán következik, hogy nehéz jó live disztribúciót készíteni, mert előbb-utóbb felfalja a RAM-ot, és akkor nem tud mit tenni, lényegében egy sérült filerendszerrel találjuk magunkat szemben, s merev fagyás lesz belőle.

Éppen ezért raktam a /home-ot tmpfs-re, hogy ne azt a RAM-ot zabálja, amely csak foglalódik, de sohasem kerül felszabadításra, épp, mint egy memory leak.

Szerk.: Lásd még az elején a sárga hátterű szöveget!

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Most olyan találtam, hogy ntfs-3g tud tömörítés a compression opcióval.

man ntfs-3g:

compression

This option enables creating new transparently compressed files in directories marked for compression. A directory is marked for compression by setting the bit 11 (value 0x00000800) in its Windows attribute. In such a directory, new files are created compressed and new subdirectories are themselves marked for compression. The option and the flag have no effect on existing files.

Ez jónak néz ki:

https://code.google.com/p/fuse-zip/

Ubuntu-n (fuse csoportnak tagja legyen a user):

su -c "apt-get install fuse-zip"
touch data.zip
mkdir dir
fuse-zip data.zip dir
echo ez egy teszt > dir/szoveg.txt
fursermount -u dir

Szerk.: ráadásul ez pont jó lenne neked, mert nem kellene tmpfs-el szórakoznod, mivel a .zip fájlt felcsatolva a fuse-zip mindvégig saját memóriában tárolja a dolgokat, és lecsatolásnál pedig visszaírja - tehát automatikusan elvégződne a vissza mentés.

Még tesztelem sebesség meg jogosultságok tekintetében. Gondolom nincsenek Unix jogok meg extra attributumok.

Még jobb megoldás: archivemount

Minden ugyanaz mint feljebb, a felcsatolás stb, csak több formátumot támogat, és a tgz archívumban megmaradnak a unix jogok. Kipróbáltam 2705 joggal meg egyébbel, és rendben hozza.

További szépsége, hogy megvan csomagban EPEL-ben ;)

Még további jó dolog, hogy csinál alapból backup fájlt visszacsomagolás előtt, illetve rohadt sok kapcsolója van.

Szerk.: további megjegyzés: a memóriában tárolt módosítások és fájlok cache-ként tárolódnak, így nem a használt memóriából vesz el. Lecsatoláskor, kiírás után felszabadul a memória. Symlink és hardlink nem implementált.

Kis érdekesség ;)

$ ls -lh
total 0
-rw-rw-r-- 0 user user 500M Dec 27 19:13 rand
$ du -sh .
0 .
$ df -h .
Filesystem Size Used Avail Use% Mounted on
archivemount 1000G 0 1000G 0% /tmp/test/dir

Szerk.: úgy látom, hogy dev/zero-ból írva az archívumon belül egy fájlt, max. 40 MB/s a sebessége a core2duo-mon tmpfs-en ramban. Gondolom ez főként a fuse réteg oka.

Még kiegészítés: touch-al létrehozott fájlt felcsatolva mindenképpen tömörítetlen tar formátumot hoz létre. Ha tömörítést akarunk, akkor egy eleve tömörített formátumot kell beadnunk neki (lehet tar-rolt cucc, vagy sima gzip, bzip2, zip stb.), pl:

echo test > data
tar cfz data.tgz data
mkdir dir
archivemount data.tgz dir
echo ez egy teszt > dir/szoveg.txt
fursermount -u dir

Most látom, hogy a függő műveleteket nem saját maga által lefoglalt memóriában tárolja, hanem csak simán a létező tmpfs-emen:

/tmp/archivemount_fajlnev_randomresz

Tehát az élő tmpfs lesz a műveletek limitje. Ki is próbáltam, és 550 MB-ot már nem enged egymásra másolni (1 GB a limit).

No, de várj, nem arról beszélsz, hogy csak a végén tömörít, addig, RAM-ban bufferel? Az nekem nem jó, a dolog lényege éppen a memóriával takarékosság némi CPU idő feláldozásával. Azt már jelenleg is tudom sokkal egyszerűbben, hogy menet közben tmpfs, a végén tar.gz pendrive-ra.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Ha rsyncet használnál az sokkal gyorsabb lenne, mivel inkrementális, igaz, hogy nem lesz tömörített, de még így is megéri sebességben szvsz.

Sőt: a leállító scriptet cronból is lehet futtatni "autosave" ként, és nem lenne túl nagy overhead, ha a fileok statjai elférnek a ramban, szinte ingyen van.

USERS[ ] tömbben a felhasználóim vannak, ilyesmi a lényeg, persze jócskán van körítés hozzá:

i=0
while [ $i -lt ${#USERS[@]} ]; do
    if [ x"${USERS[i]}" != x'root' ]; then
        log "tar -czf ${USERS[i]}.tar.gz /home/${USERS[i]}"
        tar -czf "${USERS[i]}.tar.gz" "/home/${USERS[i]}"
    fi
    ((i++))
done

A log() függvény stderrorra logol, ami még a script elején exec-cel file-ba van irányítva. Mindez persze a leállításkor történik, miután egy openbox --exit parancsot követően meggyőződtem, hogy már kilépett a session-ből.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

De, szimpatikus. Ha már itt tartunk, az rsync hogyan működik? Hasht számol, s ha nem változott a file, akkor nem módosítja? Ami változott, azt felülírja, ami létrejött, azt a backup-ba másolja? Ami eltűnt, azt letörli?

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Jó, persze, az nem gond, hogy ha rászánom magam, akkor megfelelően paraméterezzem. Most azon gondolkodom, hogy a dátum és a fileméret elegendő-e, de végül is igen, még akkor is, ha rosszul jár az óra, mert ns pontossággal esélytelen beletalálni az időbe. Úgy tudom, az ext4 képes ns felbontással tárolni az időt, kérdés, hogy valóban kitöltik-e, mert semmit sem ér, ha 1388269124.000000000 van 1388269124.303466460 helyett. A date parancs tudja: date '+%s.%N'

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Persze, csak ez live, s ki tudja, hogyan jár a gép órája. Van chronyd, csak ha nincs net, akkor semmit sem ér. De mondom, kicsi a valószínűsége, hogy ugyanabba az időbe találjon az ember. Erre írtam, hogy ha nanosecundum felbontással tárolják az időt, akkor jó, ha másodperc felbontással, akkor nem annyira jó.

Szerk.: tehát, mivel live, ugyanazt a pendrive-ot más-más fizikai gépen lehet használni.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Nem tudom milyen filerendszer milyen pontossággal tárolja a pontos időt, és ez a valós mtime, stb. statban is szerepel-e (vagy csak valami speciális attribútumként kérdezhető le, mert ugye az mtime az alapból 32 bites, nem?) A valóság az, hogy ha egy user célja az, hogy egy filet megváltoztasson a backupban szereplő dátumra, a file méret megtartásával, pedig a tartalmát megváltoztatta, akkor ezt meg tudja tenni, de vajon mi értelme lenne ennek? Valós működésnél ez NEM fordul elő és kész. Nekem ilyen problémát - ahol -c kapcsolóra volt szükség - virtuális gép fagyás okozott innodb table cache filenál volt eltérő tartalom azonos fileméret/dátum mellett (directio miatt talán??), de ez nálad nem fordulhat elő, vagy ha igen, akkor tmpfst már úgysem mented le :D

Kétféleképpen értelmezhetem, amit írtál:

- nem fordulhat elő, mert az idő szigorúan monoton nő
- nem fordulhat elő, mert nagyon kicsi az előfordulás valószínűsége

Engem a második eset zavar. Az első kiesik, mert nem egy telepített oprendszerről van szó, hanem egy pendrive-on lévő live image-ről, ami kap maga alá egy vasat, amelynek ki tudja, merre kalandozik az RTC-je.

Most man rsync-et kellene olvassak, mert az előbbi ok miatt olyan lehet, hogy a régebbinek tűnő file az újabb, szóval csak azt nézheti nekem, hogy egyezik a dátum, vagy nem. Ha igen, nem kell másolni, ha nem, akkor kell, függetlenül attól, hogy kisebb vagy nagyobb a forrásfile modify time-ja a célfile idejéhez képest.

tr '[:lower:]' '[:upper:]' <<<locsemege
LOCSEMEGE

Az rsync esetén alapban meg kell adni egy forrás majd utána egy cél könyvtárat (vagy file-t). Ekkor ha eltérés van két állomány között, akkor mindenképpen a forrás könyvtárból kerül át a cél könyvtárba az adat. Olyat persze lehet kérni, hogy "kölcsönös" szinkronizáció legyen, ekkor történhet olyan, hogy az eredetileg célkönyvtárként megadott helyről kerül vissza állomány a forrás könyvtárba - de ez külön kapcsoló.
Ráadásként vegyük a következő hipotetikus esetet: egy program fix rekord mérettel dolgozik, majd futása közben frissít pár bejegyzést, de bővítés/törlés nem történik. Ekkor a korábban rsync-kel mentett állomány és az aktuális állomány méret nem változik. Tegyük fel, a vekkerrel gond van, így a módosításkor pont az az időpecsét kerül a file-ra, amellyel a backup is készült. (Tudom, ennek esélye ezrelékek alatt, azért is hipotetikus az eset.) Ekkor az rsync a file paraméterek alapján nem fog különbséget látni a két file között, ellenben -c kapcsolóval hash-t kérve azonnal kiderül a turpisság, hogy a két file tartalma dacára az egyéb egyező paramétereknek, bizony eltérő. Itt jön a nagy kérdés: melyik file az új, melyik a régi? Ez biza az időpecsétekből nem fog kiderülni! Itt jön tehát az, hogy a forrásban lévő az új, az érvényes, a célban lévő pedig a régi, az érvénytelen.