Koliko plačujemo za subvencije sončnim elektrarnam
pod kategorijami: slovenija energija sonce

Pogosto se kot argument proti sončnim elektrarnam pojavljajo subvencije. Subvencije, ki jih opisujem v tej objavi, se pojavijo na položnici vsakega plačilnega mesta pod postavko "Prispevek za SPTE in OVE". Ta izplačila nimajo nobene povezave z subvencijo, ki jo prejmejo domače sončne elektrarne. Te subvencije predstavljajo tudi največje finančno breme in so bile večinoma podeljene med leti 2009 in 2013.

https://zejn.net/b/2021/12/26/koliko-subvencij-placujemo-za-soncne-elektrarne/prispevek_spte_ove2.png

Subvencijska shema je med 2009 in 2013 delovala večinoma po pogodbi o odkupu električne energije po zagotovljeni ceni, pogodba pa je bila sklenjena za 15 let. Na začetku je ta shema imela smisel, saj so si investitorji s tem zagotovili povračilo investicije, ki sicer ne bi bila finančno smiselna. Slovenija je tako dobila znatno količino sončnih elektrarn, vendar pa se je ta shema končala relativno kmalu za tem, ko je nekdo naredil preračun koliko to stane.

Subvencioniran odkup električne energije po tej shemi izvaja Borzen oz. njegov organizacijski del Center za podpore proizvodnji zelene energije. Ta objavlja tudi poročila o izplačilih subvencij. Spodnji graf prikazuje izplačila.

https://zejn.net/b/2021/12/26/koliko-subvencij-placujemo-za-soncne-elektrarne/subvencije_pv2.png

Na grafu so subvencije različnim tipom elektrarn, največji znesek pa poberejo sončne elektrarne, ki so leta 2020 prejele 51% izplačil. Izplačila sončnim elektrarnam so dosegla plato okrog 70 milijonov evrov in to po tem, ko je nekdo ugotovil, da bo znesek skozi 15 letno dobo odplačevanja relativno visok. Ocena končne vrednosti subvencij za sončne elektrarne prek centra podpor bo okrog 1.050 milijonov evrov, če predvidimo poenostavljen izračun 70 milijonov krat 15 let. Do sedaj smo do vključno 3. kvartala 2021 izplačali 663 milijonov evrov, torej smo malo čez polovico. Malo čez polovico smo tudi po tem, da so subvencije dosegle plato leta 2013, danes pa smo 8 let kasneje. Te subvencije bomo v večjem znesku odplačevali še približno do 2028, potem pa bodo upadle, vsaj te iz naslova sončnih elektrarn.

Kako so te subvencije narasle na tak znesek?

Leta 2009 smo po vzoru drugih držav uvedli subvencije na obnovljive vire energije. Zagotovljena cena odkupa megavatne ure električne energije iz sončnih celic je bila leta 2009 sprva 415 EUR/MWh, potem pa je bilo predvideno, da se bo vsako leto znižala za dodatnih 7% glede na prvo leto. Med letom 2009 in 2013 se je cena celic znatno zniževala in tako se je vsako leto sprejemalo nove uredbe, ki so odkupno ceno za leto 2013 zniževale iz prvotno predvidenih 300 EUR/MWh najprej na 249 EUR/MWh, potem 216 EUR/MWh, potem na 164 EUR/MWh in končno na 150 EUR/MWh. Verjetno "najdražja" napaka je bila, da je bil samodejno priznan fiksen, v uredbi vnaprej določen strošek na enoto proizvedene električne energije, ne glede na to koliko je investicija dejansko stala.

Od takrat se je marsikaj spremenilo glede podpor, ki jih izplačuje Center. Investitorji morajo sedaj ponuditi svojo ceno, pod katero so pripravljeni prodajati električno energijo, to pa lahko center sprejme ali zavrne, tako da ne prihaja več do pomembne razlike med priznano ceno in stroški, ki jih ima investitor.

V Sloveniji smo v dobi med 2009 in 2013 dobili okrog 250MW nameščenih sončnih elektrarn. Takrat je bila cena postavitve sončnih celic visoka, zato je večji del Evrope podprl subvencijske sheme, ki niso bile neposredno odvisne od stroškov postavitve. Morda so k znižanju stroškov postavitve sončnih celic pomagale tudi te subvencije, koliko je to res, bo težko dokazati. Dejstvo pa ostaja, da so sončne celice desetletje pozneje ena ekonomsko cenejših oblik pridobivanja električne energije.

Takratno znižanje subvencij ob koncu 2013 je precej oklestilo investicije v sončne elektrarne in potem dolga leta nismo dodajali večjih količin sončnih elektrarn. To se dogaja šele zadnja leta, delno zaradi tega, ker so domače sončne elektrarne postale cenovno dostopnejše in z net-metering shemo same pokrijejo svojo investicijo, delno podjetja iščejo načine, da si znižajo izdatke za energijo oz. da zmanjšajo svoj ogljični odtis, pojavljajo pa se tudi že večje sončne elektrarne, ki jih postavljajo elektro energetska podjetja. K temu pa pomaga, da so tudi Slovenijo dosegle "evropske" višje cene električne energije.


Viri:
Kako spodbujamo rabo nizkoemisijskih tovornih vozil
pod kategorijami: slovenija emisije zrak

V Londonu je že pred časom stopila v veljavo nova ureditev, kjer se je precej razširil obseg ultra-nizko-emisijske cone, kamor smejo brez plačila dodatne okoljske globe vstopiti zgolj vozila, ki delajo manj izpustov.

Slovenija take ureditve niti ne omogoča v zakonodaji, tako da v Ljubljano lahko mirno zapelje vsako vozilo, ne glede na to koliko emisij dela. Posebej problematični so starejši težki tovornjaki, ki ne dosegajo niti emisijskega standarda EURO 3.

Ljubljana, ki ji župan pravi "najlepše mesto na svetu", se je po kvaliteti zraka na lestvici 323 evropskih mest uvrstila na 260. mesto. Precej slabo!

Ljubljana ima Odlok o načrtu za kakovost zraka na območju Mestne občine Ljubljana, v tem si lahko v prilogi 2 preberemo podrobnejši seznam ukrepov. Med ukrepi na prometnem področju so tudi omejevanje hitrosti na avtocestah in hitrih cestah na območjih s slabo kakovostjo zraka, kadar agencija razglasi njegovo čezmerno onesnaženost (točka 4.2.3) in prepoved vožnje tovornih vozil na severni ljubljanski obvoznici (točka 4.2.4). Naveden je tudi nadzor nad izpusti iz vozil s čezmernimi emisijami (točka 4.2.5).

Emisijski standard Euro I je bil sprejet leta 1992, pred 29 leti, in za tovorna vozila ne predvideva nobenih omejitev glede količine izpustov prašnih delcev. Prve znatne izboljšave je prinesel šele standard Euro III, v veljavi od 1999, kar je kar 22 let nazaj. Kljub temu lahko ta 20, 30 ali več let stara tovorna vozila prosto vozijo po mestnih ulicah in onesnažujejo zrak, ob taki starosti pa je vprašljivo tudi njihovo vzdrževanje oz. obrabljenost motorja, kar izpuste le še povečuje.

Za primerjavo si lahko na spletni strani DARS prek interaktivnega kalkulatorja izračunamo koliko informativno stane cestnina za tovorna vozila. Za lažji preračun vzamemo odsek med priključkom Logatec in Celje zahod, razdalja med njima je 100,29km.

Emisijski razred Število osi Cestninski razred Cestnina Logatec – Celje Z.
(100,29km)
Pribitek
E0 2 R2 24,00 EUR 9,07 EUR
E6 2 R2 14,93 EUR
E0 3 R3 26,66 EUR 10,07 EUR
E6 3 R3 16,59 EUR
E0 4+ R4 55,09 EUR 20,96 EUR
E6 4+ R4 34,13 EUR

Pozorno oko lahko opazi, da je cestnina na avtocestah za vozila emisijskega razreda E0 znatno dražja od vozil emisijskega razreda E6. Kar pomeni, da DARS s cestnino na avtocestah na nek način bolj spodbuja rabo okolju in človeku prijaznejših tovornih vozil kot to počne MOL ali katera koli druga občina.

Viri:
Koliko stane plinska elektrarna?
pod kategorijami: slovenija energetika

Energetika Ljubljana je predzadnji vikend letošnjega oktobra opozorila svoje odjemalce, da lahko pride do izpada dobave toplote v večjem delu severne Ljubljane. Razlog? Zahtevni poseg pri gradnji nove plinsko-parne enote. Energetika Ljubljana namreč menja stara premogovna kotla, blok 1 in blok 2, ki dovajata toploto po Ljubljani, s plinsko parno enoto, ki bo poleg toplote proizvajala tudi električno energijo.

Plinske elektrarne so lahko večje, kot so te v Termoelektrarni-toplarni Ljubljana ali blok 6 in 7 v Brestanici, lahko pa so tudi manjše, kot so npr. enote za kogeneracijo, ki se nameščajo v večje zgradbe, ki potrebujejo večje količine toplote, npr. šole, domove starejših občanov ali hotele.

Večje elektrarne so praviloma turbinske in lahko kot nadomestno gorivo uporabljajo tudi dizel oz. kurilno olje. Manjše so pogosto batni motor na notranje izgorevanje in ponavadi delujejo samo na plin.

Koliko stane postavitev plinske elektrarne?

Elektrarna Vrsta Električna moč (kW el) Toplotna moč (kW th) Cena izgradnje (EUR) Cena EUR/kW el moči Zgrajena
TEB Blok 6 Turbina 53.000 - 35.000.000 € 660 €/kW 2018
TEB Blok 7 Turbina 56.000 - 26.450.000 € 472 €/kW 2021
TEB6 in TEB7 Turbina 109.000 - 129.548.221 € 563 €/kW 2021
TETOL Plinsko parna enota 1 Turbina 139.000 118.000 129.548.221 € 832 €/kW 2021
Toplarna Šiška (1998) Turbina 6.000 ? 1,2 milijarde SIT (1998) ? €/kW 1998
Toplarna Šiška (2022) Turbina 7.520 ? 9.680.743 € 1287 €/kW 2022
Dijaški dom Novo Mesto Batni motor 50 80 110.000 € 2200 €/kW 2010
Gostišče Julči Batni motor 5,5 12,5 23.000 € 4182 €/kW 2010

V tej tabeli je sicer kar nekaj neprimerljivih kategorij, saj sta Termoelektrarna Brestanica blok 6 in 7 enostaven cikel, plinsko parna enota TETOL je kombiniran cikel, zraven pa sta še dva primera kogeneracije na plin, ki sta precej manjša.

Termoelektrarna Brestanica je postavila dva nova bloka, ki ju je potrebno upoštevati skupaj, ker je bilo v okviru bloka 6 izvedenih tudi nekaj del za blok 7. Elektrarni sta enostavnega cikla, kar pomeni da je njun izkoristek tam v tridesetih odstotkih, ker pa delujeta na plin, sta med dražjimi elektrarnami po strošku na kWh električne energije. Cena za postavitev večje elektrarne na plin z enostavnim ciklom je približno 563 EUR na kW električne moči.

Plinsko-parna enota termoelektrarne in toplarne Ljubljana izkorišča, kot že ime pove, plin in paro. Ima dve turbini Siemens SGT-800, ki delujeta na plin, njun izpuh pa bo grel vodo za parno turbino, tako da bo izkoristek precej boljši. Proizvajalec navaja, da lahko te turbine dosegajo izkoristke do 58%, kar pomeni precej manjši, skoraj polovični strošek za proizvodnjo energije. Cena postavitve večje elektrarne na plin s kombiniranim ciklom je približno 832 EUR na kW električne moči, se pa je potrebno zavedati, da toplarna koristno izkorišča tudi toploto.

V letu 2022 bo zamenjana tudi plinska turbina v toplarni Šiška, ki je bila postavljena leta 1998, kar daje neko oceno 23 let življenjske dobe takega stroja. Vedeti je tudi treba, da toplarna v Ljubljani deluje večino leta.

Tretja kategorija so manjše naprave za kogeneracijo, ki so običajno večji ali manjši batni motorji na notranje izgorevanje, ki za gorivo uporabljajo plin, iz izpušnih plinov pa se pridobiva toplota za ogrevanje ali sanitarno toplo vodo. Te naprave so manjše in zato precej dražje in so bile bolj odvisne od subvencij, tako je njihova vgradnja po letu 2015, ko se je spremenil sistem subvencij, precej zamrla, in nisem uspel najti javno objavljenih podatkov o ceni sveže izvedenih projektov. Poglaviten kriterij pri vgradnji tovrstnih naprav pa je vselej potreba po toploti - če je večji del leta dovolj velik odjem toplote, potem je smiselno pogledati tudi kogeneracijo. 50kWel SPTE napravo je leta 2010 Dijaški dom Novo mesto postavil za ceno 2200 EUR na kW električne moči.

Vse te naprave pa so seveda šele začetek 20-letne naročnine na energent - plin, ki je zaenkrat še vedno fosilno gorivo.

Viri:
Koliko stane sončna elektrarna?
pod kategorijami: slovenija energetika

Slovenija ima dobro geografsko lego za sončne elektrarne in dobro osončenost. Nekateri pravijo, da so sončne celice drage, ampak koliko zares stane postavitev sončne elektrarne? Se to izplača?

Idealna lega sončnih celic, da posamezna celica pridobi kar največ energije, je proti jugu, celica pa mora biti montirana pod naklonom približno 35 stopinj. Ponoči sonce ne sije, zato takrat sončne celice ne dajejo energije od sebe, tako da v povprečju skozi celo leto daje sončna elektrarna v Sloveniji približno 13% nazivne moči - ta delež imenujemo faktor izkoriščenosti.

Tako 1kW sončna elektrarna skozi celo leto pridela približno 24 ur x 365 dni x 13% = 1138,8 kWh električne energije. Za enostavnejše pretvarjanje med letno proizvodnjo in zahtevano močjo sončne elektrarne sam ponavadi vzamem kar faktor 1,14: če letno porabo v MWh delimo z 1,14, dobimo številko zahtevano moč sončne elektrarne v kW, ki bi pridelala toliko električne energije.

To je seveda sončna elektrarna, ki ima idealno postavitev fotovoltaičnih panelov. Če ima streha, kamor bi postavili elektrarno, senčno lego, npr. da je višji hrib na vzhodu, jugu ali zahodu, se lahko izkoristek precej zmanjša in to je potrebno upoštevati.

Kaj pa cena?

Zadnja leta postajajo fotovoltaične elektrarne cenovno zelo dostopne in posledično raste tudi njihova popularnost. Za okvirno oceno stroška postavitve elektrarne bom vzel ceno ene male elektrarne, ki jo je postavil posameznik v letu 2021, ter največje sončne elektrarne, ki bo letos postavljena pri nas, fotovoltaične elektrarne Prapretno, ki bo ležala na odlagališču pepela iz Trboveljske termoelektrarne.

Elektrarna Inštalirana moč (kW) Letna proizvodnja (MWh) Cena izgradnje EUR na kW moči
Domača 11,06 kW 12,6 MWh 13.500 EUR 1220 EUR/kW
Domača
(+subvencija)
11,06 kW 12,6 MWh 11.928 EUR
(142 EUR, 7 let)
1078 EUR/kW
PV Prapretno 3.036 kW 3.360 MWh 2.500.000 EUR 823 EUR/kW

Leta 2021 pride domača sončna elektrarna približno 1200 EUR na kW, ob upoštevanju subvencije Eko sklada je strošek 1078 EUR na kW, večja postavitev pa lahko pride tudi do 823 EUR na kW.

Velja omeniti, da je v ceni, ki jo je za domačo elektrarno ponudil GEN-I, izdelava projekta na ključ, ki vključuje pripravo dokumentov za pridobivanje soglasij, pridobivanje subvencije Eko sklada in tako dalje, ter da je bilo v tem primeru izbrano 7 letno brezobrestno financiranje s strani ponudnika. Če razmišljate o postavitvi sončne elektrarne, potem vsekakor velja vprašati za ponudbo več podjetij, ki postavljajo sončne elektrarne in se odločati na podlagi več ponudb, ker se lahko med sabo znatno razlikujejo, pozanimati pa se je potrebno tudi kaj vse ponudba vsebuje.

Domača sončna elektrarna se danes izplača predvsem kadar je potrošnja električne energije tako velika, da lahko potrošnjo energije zamenja obrok kredita za odplačilo sončnih celic, kar v praksi pomeni, da je npr. ogrevanje na toplotno črpalko ali če je pri hiši električni avto. Obstoječa ureditev "net meteringa" na letnem nivoju namreč omogoča, da se viški pridelani poleti upoštevajo pri porabi pozimi, s tem pa se poenostavi poplačilo investicije in spodbuja namestitev sončnih elektrarn.

Viri:
Koliko stane hidroelektrarna?
pod kategorijami: slovenija energetika

Slovenija ima relativno veliko hidro elektrarn, nekaj večjih pa smo na spodnji Savi zgradili tudi v zadnjih letih. Koliko stane taka elektrarna?

Na spodnji Savi smo zgradili pet elektrarn, šesta je še v gradnji. Prva je bila zgrajena že leta 1993, in sicer HE Anhovo. Ostale so precej novejše, in sicer so bile zgrajene po letu 2000. To pomeni, da je dostopnih precej javnih virov, da se lahko ovrednoti stroške gradnje na enoto električne moči. HE Anhovo v tej tabeli ne primerjam, ker je bila zgrajena že tako dolgo nazaj, da za njo nisem našel vrednosti projekta, pa tudi sicer spada pod Savske elektrarne Ljubljana (SEL), medtem ko preostale, ki so spodaj v tabeli, sodijo pod podjetje Hidroelektrarne na spodnji Savi (HE-SS).

Izračun je zelo poenostavljen in sicer samo gledam koliko stane začetna investicija v hidroelektrarno. V energetski del spadajo jez, turbina, generator in generatorska stavba. Za dane elektrarne na kilovat inštalirane moči ta znaša približno 2.400 EUR/kW instalirane moči pri nazadnje zgrajenih hidroelektrarnah pri nas. Seveda v to ni vključeno še vzdrževanje in drugi obratovalni stroški.

Ta velikostni razred je neka približna ocena koliko stane elektrarna take velikosti. Vse te elektrarne imajo po 3 agregate, imajo skupno moč med 30MW in 40MW, večina pa ima projektiran faktor izkoriščenosti okrog 40%. Tako lahko večino časa varno obratuje tudi kadar je en agregat oz. turbina v remontu, kar pa se praviloma izvaja v času nižjih pretokov reke.

Če so stroški za izgradnjo energetskega dela dokaj podobni, so stroški za izgradnjo infrastrukturnega dela precej različni med posamičnimi elektrarnami. Pod infrastrukturni del se šteje ureditev vodotoka, izgradnja nadomestnih cest, izgradnja nasipov in podobno, vse kar je potrebno, da se lahko gorvodno od jezu pojavi zajezitveno jezero, brez da bi to bistveno vplivalo na življenje lokalnih prebivalcev - no, vsaj ljudi. Višina teh stroškov je zelo odvisna od lokacije hidroelektrarne in koliko ter kako obsežni posegi so potrebni.

Infrastrukturni del običajno financira država in lokalna skupnost, energetski del pa energetska družba, ki ima koncesijo za izkoriščanje energije vodnega padca, v tem primeru Hidroelektrarne na spodnji Savi, HESS d.o.o.

HE Letna proizvodnja (GWh) Inštalirana moč (MW) Začetek obratovanja Faktor izkoriščenosti Cena izgradnje (infrastrukturni del) Cena izgradnje (energetski del) EUR na kW moči
Boštanj 109 32,5 2006 38.3% 26,9 mio 66.976.651 EUR 2061 EUR/kW
Blanca 148 39,12 2009 43.2% 45 mio 92.635.530 EUR 2368 EUR/kW
Krško 146 39,12 2012 42.6% 70,6 mio 95.262.114 EUR 2435 EUR/kW
Brežice 161 47,4 2015 38.8% 141 mio 113 mio EUR 2384 EUR/kW
Mokrice (projektirana) 135 30,5 ? 50.5%? 70 mio 100 mio EUR? 3279 EUR/kW?

Viri:

Doseganje cilja 25 odstotkov iz obnovljivih virov energije do 2020
pod kategorijami: slovenija energetika

Slovenija si je za leto 2020 zadala cilj, da bo pri pridobila četrtino oz. 25 odstotkov končne rabe energije iz obnovljivih virov. Slovenija ima relativno velik delež hidroelektrarn, ki spadajo v obnovljive vire energije, ampak končno rabo obsega tudi neposredna raba energentov npr. za prevoz, ne samo električna energija. Kljub visokemu deležu hidroelektrarn je zadnja leta kazalo, da nam ne bo uspelo, saj so bili skupni deleži obnovljivih virov leta 2017 21,66%, leta 2018 21,38% in leta 2019 21,97%. Premalo.

Lani je sicer k zmanjšani rabi fosilnih goriv pomagal kovidni zaklep družbe, ko je bilo osebnega prometa zelo malo, tako da prve ocene kažejo, da bo delež obnovljivih virov višji, in sicer okrog 23,5%.

Za izpolnitev obljube 25 odstotkov bo morala Slovenija opraviti t.i. čezmejni statistični prenos obnovljivih virov energije. To pomeni, da Slovenija eni drugi državi članici EU plača nekaj denarja, da lahko k svoji kvoti prišteje nekaj njenih megavatnih ur porabljene energije iz obnovljivih virov in si tako lahko čestita, da je dosegla cilje za rabo energije iz obnovljivih virov.

Koliko energije nam umanjka?

Za leto 2020 še ni končnega statističnega poročila koliko energije smo porabili, a če sklepamo po preteklih letih, je končna poraba približno 73% oskrbe s primarnimi energenti. Leta 2020 smo se oskrbeli z 74.647 gigavatnimi urami energije, 73% te vrednosti pa znese 54.489 GWh. Odstotek in pol ocene porabljene energije je 817 GWh.

Cena za statistično preneseno megavatno uro naj bi bila nekje med 12 in 13 euri, kar znese med 9,8 in 10,6 milijona, torej približno 10 milijonov.

Če bi v letu 2020 ne bilo koronskega zaklepa družbe in bi bila oskrba in poraba energije podobna preteklim letom, bi za dokup razlike potrebovali več kot 20 milijonov evrov.


Dopolnitev, 29. november 2021.

Žurnal24 poroča, da smo dosegli 24,16% delež OVE v bruto končni rabi energije. Manjkajoči del do 25%, 455GWh, smo odkupili od Češke za približno 5 milijonov, kar pomeni ceno okrog 11 eurov za megavatno uro.


Opombe, viri in reference:

Cheating at the Countdown show with Postgres
pod kategorijami: postgres

This friday, a member of the local programmers group on FB put up a question of how the others would approach the problem of finding all valid words from a word list, that can be composed from a list of characters, where letters can repeat. So if you have letters "ABOTOG", you can assemble words TAG, TAB, BOOT, BOAT, etc. This is essentially the same as seen in the Countdown TV game show.

The original poster had already resolved that problem for an online game project, but was curious how others would approach the problem. It sparked a bit of interest in the office and after some debate I set off to see how can this be done in Postgres.

Setup

I found a word list of 370.000 english words to test how the queries perform (words_alpha.txt from a github repo). This is how I created the table and imported the data:

 create table wordlist (word text);
 \copy wordlist from 'words_alpha.txt';
 select count(*) from wordlist ;
  count
---------
  370099
 (1 row)

Original author's SQL query

The author's original query was a MySQL version of this query:

SELECT word FROM wordlist WHERE
char_length(word)-char_length(replace(upper(word),'A','')) <= 0
AND char_length(word)-char_length(replace(upper(word),'B','')) <= 0
AND char_length(word)-char_length(replace(upper(word),'C','')) <= 0
AND char_length(word)-char_length(replace(upper(word),'D','')) <= 0
AND char_length(word)-char_length(replace(upper(word),'E','')) <= 2
AND char_length(word)-char_length(replace(upper(word),'F','')) <= 0
AND char_length(word)-char_length(replace(upper(word),'G','')) <= 1
AND char_length(word)-char_length(replace(upper(word),'H','')) <= 0
AND char_length(word)-char_length(replace(upper(word),'I','')) <= 1
AND char_length(word)-char_length(replace(upper(word),'J','')) <= 1
AND char_length(word)-char_length(replace(upper(word),'K','')) <= 0
AND char_length(word)-char_length(replace(upper(word),'L','')) <= 0
AND char_length(word)-char_length(replace(upper(word),'M','')) <= 1
AND char_length(word)-char_length(replace(upper(word),'N','')) <= 1
AND char_length(word)-char_length(replace(upper(word),'O','')) <= 0
AND char_length(word)-char_length(replace(upper(word),'P','')) <= 0
AND char_length(word)-char_length(replace(upper(word),'R','')) <= 0
AND char_length(word)-char_length(replace(upper(word),'S','')) <= 0
AND char_length(word)-char_length(replace(upper(word),'T','')) <= 0
AND char_length(word)-char_length(replace(upper(word),'U','')) <= 1
AND char_length(word)-char_length(replace(upper(word),'V','')) <= 0
AND char_length(word)-char_length(replace(upper(word),'Z','')) <= 0
AND char_length(word)-char_length(replace(upper(word),'X','')) <= 0
AND char_length(word)-char_length(replace(upper(word),'Y','')) <= 0
AND char_length(word)-char_length(replace(upper(word),'Q','')) <= 0
AND char_length(word)-char_length(replace(upper(word),'W','')) <= 0;

This compares the letter count in the words and is relatively easy to understand, but is somewhat resource intensive. This was the query plan Postgres took:

                                                QUERY PLAN
--------------------------------------------------------------------------------------------------------------------
 Seq Scan on public.wordlist  (cost=0.00..149953.60 rows=1 width=10) (actual time=113.228..755.601 rows=120 loops=1)
   Output: word
   Filter: (((char_length(wordlist.word) - char_length(replace(upper(wordlist.word), 'A'::text, ''::text))) <= 0) AND ((char_length(wordlist.word) - char_length(replace(upper(wordlist.word)
   Rows Removed by Filter: 369979
   Buffers: shared hit=1914
 Planning time: 0.273 ms
 Execution time: 755.696 ms
(7 rows)

Regex queries

The easiest win is using regex in a query. With it you can limit which letters may be used in a word, and it works surprisingly well, essentially doing a sequential scan, reading full table and seeing if words match. This is the query for the letters "EJIGUNEM":

explain (analyze, verbose, buffers) select word from wordlist where word ~* '^[EJIGUNEM]+$';
                                                QUERY PLAN
--------------------------------------------------------------------------------------------------------------------
 Seq Scan on public.wordlist  (cost=0.00..6540.24 rows=37 width=10) (actual time=40.506..142.450 rows=249 loops=1)
   Output: word
   Filter: (wordlist.word ~* '^[EJIGUNEM]+$'::text)
   Rows Removed by Filter: 369850
   Buffers: shared hit=1914
 Planning time: 0.220 ms
 Execution time: 142.491 ms
(7 rows)

A trained eye will spot that a regex does not actually enforce letter repetition limit and therefore returns 249 rows instead of correct 120. Since I was doing this before the original author posted his solution, I created a python function which checks if a word can be composed from given chars, but you could equally well use his query to filter the words:

create or replace function can_word(word text, chars text) returns boolean
language plpython3u immutable strict as $f$
valid_chars = [c for c in chars.lower()]
for c in word:
    try:
        valid_chars.remove(c)
    except:
        return False
return True
$f$;

With this extra check the query then becomes:

 explain (analyze, verbose, buffers)
 with words_maybe as (select word from wordlist where word ~* '^[EJIGUNEM]+$')
 select word from words_maybe where can_word(word, 'EJIGUNEM');
                                                         QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------
  CTE Scan on words_maybe  (cost=6540.24..6550.23 rows=12 width=32) (actual time=42.183..150.407 rows=120 loops=1)
    Output: words_maybe.word
    Filter: can_word(words_maybe.word, 'EJIGUNEM'::text)
    Rows Removed by Filter: 129
    Buffers: shared hit=1914
    CTE words_maybe
      ->  Seq Scan on public.wordlist  (cost=0.00..6540.24 rows=37 width=10) (actual time=42.141..148.823 rows=249 loops=1)
            Output: wordlist.word
            Filter: (wordlist.word ~* '^[EJIGUNEM]+$'::text)
            Rows Removed by Filter: 369850
            Buffers: shared hit=1914
  Planning time: 0.190 ms
  Execution time: 150.443 ms
 (13 rows)

This query returns the correct 120 rows and it works in a decent enough time for most developers to stop here.

Cube extension

If we can represent letters as a vector, we could try the cube extension. Let's create a function that evolves the original author's query and creates a full vector from the letter count (we're assuming the alphabet is ASCII letters only here):

create or replace function word2vec(word text) returns cube
language plpython3u immutable strict as $f$
cu = [0] * 26

for c in word.upper():
    intc = ord(c)
    if 65 <= intc <= 90:
        cu[(intc - 65)] += 1
    else:
        return None
return str(tuple(cu))
$f$;

We can now create an index over the letter vectors, but we also include word in the index, in order to enable postgres to return word while only accessing index and not touching the heap:

create index wordlist_word_vec on wordlist using gist (word2vec(word), word);

And query is now magically fast:

 explain (analyze, verbose, buffers) select word from wordlist where word2vec(word) <@ cube_union(word2vec('EJIGUNEM'), '0');
                                                                                                        QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  Bitmap Heap Scan on public.wordlist  (cost=95.28..1118.29 rows=370 width=10) (actual time=7.518..7.650 rows=120 loops=1)
    Output: word
    Recheck Cond: (word2vec(wordlist.word) <@ '(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),(0, 0, 0, 0, 2, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0)'::cube)
    Heap Blocks: exact=67
    Buffers: shared hit=1737
    ->  Bitmap Index Scan on wordlist_word_vec  (cost=0.00..95.18 rows=370 width=0) (actual time=7.500..7.500 rows=120 loops=1)
          Index Cond: (word2vec(wordlist.word) <@ '(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),(0, 0, 0, 0, 2, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0)'::cube)
          Buffers: shared hit=1670
  Planning time: 0.370 ms
  Execution time: 7.726 ms
 (10 rows)

Queries are now fast, using index and returning 120 matching rows in sub 10ms. While the original table is only 15MB, the index is much bigger at 176MB, so it's a common memory vs. CPU time tradeoff.

Online resizing BTRFS
pod kategorijami: btrfs filesystems linux

Every now and then a sysadmin or a developer comes into a situation where he wishes he had not created one big partition or deletes a partition and wishes to grow one file system over the next partition. Besides growing file system, btrfs also supports shrinking in a seemingly simple command. Because the filesystem and partition meddling is always a scary operation, I was wondering if the resize operation actually moves data on btrfs.

This simple test uses blktrace and seekwatcher to trace and visualize how has the file system actually been accessing the underlying block device.

For the test, I'm using kernel 4.9.20 and btrfs-progs 4.7.3.

Preparing test file system

To test this I set up a 2GB disk image and used blktrace on it, to record block device access:

/btrfs-test# dd if=/dev/zero of=btrfs.img bs=1M count=2048
2048+0 records in
2048+0 records out
2147483648 bytes (2.1 GB, 2.0 GiB) copied, 4.6457 s, 462 MB/s

Since blktrace can only operate on a block device (and not a file), we need to setup the image as a loop device:

/btrfs-test# losetup -f btrfs.img
/btrfs-test# losetup -a
/dev/loop0: [2051]:538617527 (/btrfs-test/btrfs.img)

Let's create a btrfs file system on it:

/btrfs-test# mkfs.btrfs --mixed /dev/loop0
btrfs-progs v4.7.3
See http://btrfs.wiki.kernel.org for more information.

Performing full device TRIM (2.00GiB) ...
Label:              (null)
UUID:
Node size:          4096
Sector size:        4096
Filesystem size:    2.00GiB
Block group profiles:
  Data+Metadata:    single            8.00MiB
  System:           single            4.00MiB
SSD detected:       no
Incompat features:  mixed-bg, extref, skinny-metadata
Number of devices:  1
Devices:
   ID        SIZE  PATH
    1     2.00GiB  /dev/loop0

Test scenario

Assuming the block allocation strategy is to allocate the blocks at the start of the device first, I put up a script that forces the filesystem to write in the second part of the file system, then removing padding files and issuing a resize operation to shrink the file system into half its previous size. This should move the data in latter blocks to first half of device.

This script makes two copies of Ubuntu server image at 829MB each, thus using roughly about 1660MB of (assuming) mostly the first part of disk. These two files serve as padding in order to force the file system to use the second half of disk, where it will (still assuming) place the mini.iso file. After this, the two bigger files are deleted in order to make space on device so the resize operation can actually complete. The script:

#!/bin/bash

cp -v ubuntu-16.04.2-server-amd64.iso testmnt/server-iso1.iso
cp -v ubuntu-16.04.2-server-amd64.iso testmnt/server-iso2.iso

sync
sleep 3

cp -v mini.iso testmnt/mini.iso

sync
sleep 6

rm testmnt/server-iso*
sync
sleep 3

btrfs filesystem resize 1G testmnt/
sync

In another shell, I'm running blktrace:

/btrfs-test# blktrace -d /dev/loop0 -o trace1
^C=== loop0 ===
  CPU  0:                19029 events,      892 KiB data
  CPU  1:                27727 events,     1300 KiB data
  CPU  2:                28491 events,     1336 KiB data
  CPU  3:                28879 events,     1354 KiB data
  Total:                104126 events (dropped 0),     4881 KiB data

The ^C shows I've interrupted it after the test script completed. Blktrace outputs per-cpu data about which blocks were accessed. Seekwatcher takes these files and visualizes them:

/btrfs-test$ seekwatcher -t trace1.blktrace
using tracefile ././trace1
saving graph to trace.png

Test results

The following graph shows how the at the start of script (first 5 seconds) most of the disk is filled, when the two copies of ubuntu are placed on the drive. Then at 16 seconds, the mini.iso is placed almost at the end of drive. At about 26 seconds the resize operation is done, where the filesystem does actually move the mini.iso to another place on device. Yay!

Full seekwatcher graph

Besides disk offset, seekwatcher also displays throughput, seek count and IOPs, all nicely aligned by time so you can quickly see what is happening with disks when you need to, as seen in the whole graph below.

Single device data redundancy with BTRFS
pod kategorijami: btrfs filesystems linux

I've been playing a bit with BTRFS lately. The file system has checksums of both metadata and data so it can offer some guarantees about data integrity and I decided to take a closer look at it hoping I could see if it can keep any of the data that we people try keep on our unstable hardware.

Preparing test data

For data I'll be using a snapshot of raspbian lite root. I mounted the image and packed the root into a tar to reproduce test easily and also compute sha1 sums of all the files to be able to verify them:

# losetup -f 2017-03-02-raspbian-jessie-lite.img
# losetup -a
/dev/loop0: [2051]:203934014 (/home/user/rpi-image-build/2017-03-02-raspbian-jessie-lite.img)
# kpartx -av /dev/loop0
add map loop0p1 (254:0): 0 129024 linear 7:0 8192
add map loop0p2 (254:1): 0 2584576 linear 7:0 137216
# mkdir rpi_root
# mount /dev/mapper/loop0p2 rpi_root
# (cd rpi_root && tar zcf ../root.tar.gz . && find . -type f -exec sha1sum {} \; > ../sha1sums)
# umount rpi_root
# kpartx -dv /dev/loop0
del devmap : loop0p2
del devmap : loop0p1
# losetup -d /dev/loop0

This leaves us with root.tar.gz and sha1sums files.

Test challenge

The challenge is to find and repair (if possible) file system corruption. I created a python script that finds a random non-empty 4K block on device and overwrites it with random data and repeats this 100 times. Here's the corrupt.py:

#!/usr/bin/python
import os
import random

f = 'rpi-btrfs-test.img'
fd = open(f, 'rb+')
filesize = os.path.getsize(f)

print('filesize=%d' % filesize)
random_data = str(bytearray([random.randint(0, 255) for i in range(4096)]))

count = 0
loopcount = 0
while count < 100:
    n = random.randint(0, filesize/4096)
    fd.seek(n*4096)
    block = fd.read(4096)

    if any((i != '\x00' for i in block)):
        print('corrupting block %d' % n)

        fd.seek(n*4096)
        fd.write(random_data)

        assert fd.tell() == (n+1)*4096
        count += 1

    loopcount += 1
    if loopcount > 10000:
        break
fd.close()

Baseline (ext4)

To imitate a SD card I'll use a 4GB raw disk image full of zeroes:

dd if=/dev/zero of=rpi-btrfs-test.img bs=1M count=4096

Create a ext4 fs:

# mkfs.ext4 -O metadata_csum,64bit rpi-btrfs-test.img
mke2fs 1.43.4 (31-Jan-2017)
Discarding device blocks: done
Creating filesystem with 1048576 4k blocks and 262144 inodes
Filesystem UUID: c8c7b1cc-aebc-44a3-bb6d-790fb3b70577
Superblock backups stored on blocks:
        32768, 98304, 163840, 229376, 294912, 819200, 884736

Allocating group tables: done
Writing inode tables: done
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done

Now we create a mount point, mount the file system and put some data on it:

mkdir mnt
mount -o loop rpi-btrfs-test.img mnt
(cd mnt && tar zxf ../root.tar.gz)

Lets look at the file system:

# df -h
/dev/loop0      3.9G  757M  3.0G  21% /home/user/rpi-image-build/mnt

Let the corruption begin:

umount mnt
python corrupt.py

Lets check if the file system has errors:

 # fsck.ext4 -y -v -f rpi-btrfs-test.img
 e2fsck 1.43.4 (31-Jan-2017)
 Pass 1: Checking inodes, blocks, and sizes
 Pass 2: Checking directory structure
 Directory inode 673, block #0, offset 0: directory has no checksum.
 Fix? yes

 ... lots of prinouts of error and repair info

 Pass 5: Checking group summary information

 rpi-btrfs-test.img: ***** FILE SYSTEM WAS MODIFIED *****

        35610 inodes used (13.58%, out of 262144)
           65 non-contiguous files (0.2%)
            7 non-contiguous directories (0.0%)
              # of inodes with ind/dind/tind blocks: 0/0/0
              Extent depth histogram: 30289/1
       226530 blocks used (21.60%, out of 1048576)
            0 bad blocks
            1 large file

        27104 regular files
         3038 directories
           54 character device files
           25 block device files
            0 fifos
          346 links
         5380 symbolic links (5233 fast symbolic links)
            0 sockets
-------------
        35946 files

OK, this filesystem was definitely borked! We should mount it:

mount -o loop rpi-btrfs-test.img mnt

What does sha1sum think:

# (cd mnt && sha1sum -c ../sha1sums | grep -v 'OK' )

... lots of files with FAILED

./usr/lib/arm-linux-gnueabihf/libicudata.so.52.1: FAILED
sha1sum: WARNING: 1 listed file could not be read
sha1sum: WARNING: 90 computed checksums did NOT match

Whoops, sha1sum as a userspace program was actually allowed to read invalid data. Uncool.

Btrfs Single test

Lets create a btrfs file system on it:

# mkfs.btrfs --mixed rpi-btrfs-test.img
btrfs-progs v4.7.3
See http://btrfs.wiki.kernel.org for more information.

Label:              (null)
UUID:
Node size:          4096
Sector size:        4096
Filesystem size:    4.00GiB
Block group profiles:
  Data+Metadata:    single            8.00MiB
  System:           single            4.00MiB
SSD detected:       no
Incompat features:  mixed-bg, extref, skinny-metadata
Number of devices:  1
Devices:
   ID        SIZE  PATH
    1     4.00GiB  rpi-btrfs-test.img

The --mixed switch is needed because the file system is smaller than 16GB and BTRFS is known to have some problems with small devices if it tries to have separate block groups (chunks) for data and metadata, which by default it does. This flag sets up the filesystem so it intermingles data and metadata on same chunks. Supposedly this helps avoid the notorious out of space errors.

Now we mount the file system and put some data on it:

mount -o loop rpi-btrfs-test.img mnt
(cd mnt && tar zxf ../root.tar.gz)

Lets look at the file system:

# df -h
/dev/loop0      4.0G  737M  3.3G  18% /home/user/rpi-image-build/mnt

Let the corruption begin:

umount mnt
python corrupt.py

Lets check the file system:

# mount -o loop rpi-btrfs-test.img mnt
# btrfs scrub start mnt/
scrub started on mnt/, fsid a6301050-0d88-4d86-948d-03380b9434d4 (pid=27478)
# btrfs scrub status mnt/
scrub status for a6301050-0d88-4d86-948d-03380b9434d4
    scrub started at Sun Apr 30 10:17:12 2017 and was aborted after 00:00:00
    total bytes scrubbed: 272.49MiB with 37 errors
    error details: csum=37
    corrected errors: 0, uncorrectable errors: 37, unverified errors: 0

Whoops, there goes data: 37 uncorrectable errors.

What does sha1sum say:

# (cd mnt && sha1sum -c ../sha1sums | grep -v 'OK' )
sha1sum: ./usr/share/perl5/Algorithm/Diff.pm: Input/output error
./usr/share/perl5/Algorithm/Diff.pm: FAILED open or read

... lots more of terminal output similar to above

sha1sum: WARNING: 99 listed files could not be read

Data was lost, but at least the OS decided it will not allow garbage to propagate.

Btrfs Dup test

BTRFS has a way to tell it to make redundant copies of data by using DUP profile. DUP makes a duplicate copy on same device in case the first copy goes bad.

Defining the profile is most easily done at the time of creating the file system:

# mkfs.btrfs --data dup --metadata dup --mixed rpi-btrfs-test.img
btrfs-progs v4.7.3
See http://btrfs.wiki.kernel.org for more information.

Label:              (null)
UUID:
Node size:          4096
Sector size:        4096
Filesystem size:    4.00GiB
Block group profiles:
  Data+Metadata:    DUP             204.75MiB
  System:           DUP               8.00MiB
SSD detected:       no
Incompat features:  mixed-bg, extref, skinny-metadata
Number of devices:  1
Devices:
   ID        SIZE  PATH
    1     4.00GiB  rpi-btrfs-test.img

The DUP denotes that file system will store two copies of data on same device.

Again we put some data on it:

mount -o loop rpi-btrfs-test.img mnt
(cd mnt && tar zxf ../root.tar.gz)

Lets look at the file system:

# df -h | grep mnt
/dev/loop0      2.0G  737M  1.3G  36% /home/user/rpi-image-build/mnt

The file system appears only 2GB of size, because there are two copies to be stored on underlying device.

Again we corrupt the file system:

umount mnt
python corrupt.py

Lets check the file system:

# mount -o loop rpi-btrfs-test.img mnt
# btrfs scrub start mnt/
scrub started on mnt/, fsid 12ffd7a2-b9c4-46ca-b16a-cd07d94d6854 (pid=27863)
# btrfs scrub status mnt/
scrub status for 12ffd7a2-b9c4-46ca-b16a-cd07d94d6854
    scrub started at Sun Apr 30 10:27:31 2017 and finished after 00:00:00
    total bytes scrubbed: 1.41GiB with 99 errors
    error details: csum=99
    corrected errors: 99, uncorrectable errors: 0, unverified errors: 0

Hey, corrected errors, looks like this could work ... checking the sha1sums:

# (cd mnt && sha1sum -c ../sha1sums | grep -v 'OK' )

Looks fine. Need to check how this works on real hardware.

KVM tricks
pod kategorijami: qemu kvm linux

Did you know KVM has a way to enable discard pass thru? I did, but then I forgot and had to google again.

This is how you do it:

kvm -drive discard=unmap,file=debian.qcow2,id=ff,if=none \
    -device virtio-scsi-pci -device scsi-disk,drive=ff

When booted, you have to make sure your fs is mounted with discard option, and can then do:

fstrim /

And the image on the host should be significantly smaller. Use du or ls -alsh, not just ls, because ls does not display actual disk usage if you don't pass -s:

1,4G -rw-r--r--  1 root root 4,0G apr  7 17:22 debian.img

Now you know.

Did you know you can start kvm with vnc disabled and then attach it in monitor? And change CD or save vm snapshot?