Toon posts:

[PHP] file write gaat soms fout

Pagina: 1
Acties:

Vraag


Acties:
  • +1 Henk 'm!

Verwijderd

Topicstarter
Ik heb een file::write methode gemaakt en gebruik ik al enige tijd in mijn code. echter bij mijn parser kwam die soms in de problemen. Hij voegde dan karakters toe aan het eind van het bestand (in het geval van mijn parser is dat .php). Hierdoor gaat het parsen fout omdat dan het bestand niet ingeladen kan worden (parse error).Nu gaat ie single threaded redelijk tot goed, maar als ik de parser multithreaded wil aansturen gaat het vaak fout en niet altijd, met dezelfde code.Nu heb ik ook een file::put methode gemaakt die file_put_contents uitvoerd, daarbij gaat het wel altijd goed.Nu is mijn vraag, wat zou ik kunnen verbeteren aan mijn code waardoor die wel multithreaded goed gaat.
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
     * @throws FileWriteException
     * @bug may write wrong files in Parse:Build:write in a multithreading situation . solution use file::put
     */
    public static function write($url='', $data=''){
        $url = (string) $url;
        $data = (string) $data;
        $resource = @fopen($url, 'w');
        if($resource === false){
            return $resource;
        }        
        flock($resource, LOCK_EX);
        for ($written = 0; $written < strlen($data); $written += $fwrite) {
            $fwrite = fwrite($resource, substr($data, $written));
            if ($fwrite === false) {
                break;
            }
        }
        flock($resource, LOCK_UN);
        fclose($resource);
        if($written != strlen($data)){
            throw new FileWriteException('File.write failed, written != strlen data....');
        } else {
            return $written;
        }
    }

Wat ik al gevonden of geprobeerd heb
file:write file::put laten uitvoeren.

Alle reacties


Acties:
  • +1 Henk 'm!

  • Josk79
  • Registratie: September 2013
  • Laatst online: 01-10 00:07
Je controleert niet of flock succesvol is. Tevens, vanwaar de for-loop? Schrijft hij het niet in 1x weg?

Acties:
  • 0 Henk 'm!

  • Josk79
  • Registratie: September 2013
  • Laatst online: 01-10 00:07
Op wat voor systeem schrijf je de files eigenlijk? En is dit lokaal?

Acties:
  • +1 Henk 'm!

  • grasmanek94
  • Registratie: Juli 2015
  • Laatst online: 16:09
https://www.php.net/manual/en/function.fwrite.php
Writing to a network stream may end before the whole string is written. Return value of fwrite() may be checked.
Ik zou verder try { } finally { } constructie gebruiken voor lock, zo weet je dat je _altijd_ een unlock doet (ook als ze in de toekomst excepties aan fwrite toevoegen):

code:
1
2
3
4
5
6
7
8
9
10
11
12
try {
        flock($resource, LOCK_EX);
        for ($written = 0; $written < strlen($data); $written += $fwrite) {
            $fwrite = fwrite($resource, substr($data, $written));
            if ($fwrite === false) {
                break;
            }
        }
}
finally {
        flock($resource, LOCK_UN);
}


En heb je ook het scenario afgedekt dat het dezelfde url is maar een symlink, of verschilt alleen in http/https/wel www/geen www?

Verder als je zorgt (als het mogelijk is) dat je niet in meerdere threads dezelfde 'url' schrijft dan moet het toch goed komen? (bv hash de 'url' string en op basis van [hash modulo% aantal threads] naar een bepaalde thread sturen, heeft zo zijn issues in mogelijk werk oneerlijk verdelen maar beter dan foutief..).

Verder check je niet de return status van flock, die kan false terug geven.
Je doet ook geen flush voordat je de lock released. Het systeem kan je data dan nog in buffer hebben terwijl de lock al is verlopen.

En inderdaad zoals Josk79 al zegt:
Note:

May only be used on file pointers returned by fopen() for local files, or file pointers pointing to userspace streams that implement the streamWrapper::stream_lock() method.
code:
1
flock(sys_get_temp_dir() . '/' . sha256(url), ...) // moet je die file wel maken
?

[ Voor 38% gewijzigd door grasmanek94 op 26-07-2022 08:49 ]


Acties:
  • +1 Henk 'm!

  • Josk79
  • Registratie: September 2013
  • Laatst online: 01-10 00:07
Vandaar de vraag of lokaal wordt weggeschreven. Ik vraag me ook af of locks op een netwerklocatie gegarandeerd werken.

Tevens lijkt file_put_contents me iets eenvoudiger in gebruik

[ Voor 19% gewijzigd door Josk79 op 26-07-2022 08:43 ]


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Het is lokaal en ik gebruik het linux '&' command om multithreaded te gaan. De filenaam is gebaseerd op een sha1 van de originele content en het kan voorkomen dat hij hetzelfde bestand meerdere keren wegschrijft. De for loop heb ik uit het 2e voorbeeld op php.net bij function fwrite. Ik kan de filename niet aanpassen, aangezien de autolader de gegenereerde class moet inlezen nadat dit is weggeschreven.

Acties:
  • +1 Henk 'm!

  • jammo
  • Registratie: November 2020
  • Laatst online: 10:03
Verwijderd schreef op dinsdag 26 juli 2022 @ 11:55:
Het is lokaal en ik gebruik het linux '&' command om multithreaded te gaan.
Doel je op een & op je commandline?
Dat heeft namelijk niks met threads te maken, hiermee start je een process op de achtergrond.
Zit je probleem niet meer in de manier van het aanroepen van je script / deze functie?


Verder zou ik de file openen met iets als 'r+' en niet met 'w' als flag, als jou filesystem niks native met filelocks afdwingt dan betekend 'w' namelijk dat je je file leeg gooit, voordat je kijkt of je een lock kunt krijgen.

Het resultaat van je lock verkrijgen moet je uiteraard ook nog wat mee doen..

Acties:
  • 0 Henk 'm!

  • Josk79
  • Registratie: September 2013
  • Laatst online: 01-10 00:07
Het klinkt wat spannend, wat je wilt doen. Schrijf je php files? Met wat voor doel? Zou het in je 'parser' nog mis kunnen gaan? Weet je zeker dat de onterecht geschreven '.php' niet in $data terecht komt?

[ Voor 43% gewijzigd door Josk79 op 26-07-2022 17:25 ]


Acties:
  • +2 Henk 'm!

  • Voutloos
  • Registratie: Januari 2002
  • Niet online
file_put_contents(…, …, LOCK_EX) zorgt native voor een complete write én locking. Je eigen implementatie is leuk om gemaakt te hebben, maar heeft enkel nadelen t.o.v. deze ingebouwde functie. :)

Afhankelijk van de use case: Als je met echt grote bestanden en/of doorlooptijd zit, kan het soms lonen om naar een tijdelijk random bestand te schrijven en vervolgens een goedkope rename() te doen. Er is dan een flink korter of geen periode waarin op de officiele plek een half bestand te zien is. Uiteraard specifiek voor de situatie en het gebruikte bestandssysteem. Overdenk en test beide opties goed voordat je hiervoor gaat.

Dit gezegd hebbende: file_put_contents() is bijna altijd the way to go. Fopen(), fwrite() en co zijn low level, je moet eigenlijk al een best goede argumentatie hebben om dat te willen.

{signature}


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Josk79 schreef op dinsdag 26 juli 2022 @ 17:16:
Het klinkt wat spannend, wat je wilt doen. Schrijf je php files? Met wat voor doel? Zou het in je 'parser' nog mis kunnen gaan? Weet je zeker dat de onterecht geschreven '.php' niet in $data terecht komt?
Ja ik heb het gedebugged tot de file::write. En $data is juist. Ik schrijf php classes waarmee mijn eigen syntax er naartoe wordt geschreven . Als ik file::write vervang in file::put komt het probleem niet voor. Maar ik wil wel weten wat ik fout doe. Er komen soms enkele karakters dubbel voor in de output terwijl de input die niet heeft.

Acties:
  • 0 Henk 'm!

  • Josk79
  • Registratie: September 2013
  • Laatst online: 01-10 00:07
En wat staat er in file::put? Is $url een filename of zit er een protocol in? En controleer je ondertussen of flock daadwerkelijk lockt?

[ Voor 27% gewijzigd door Josk79 op 26-07-2022 20:15 ]


Acties:
  • 0 Henk 'm!

  • jammo
  • Registratie: November 2020
  • Laatst online: 10:03
$data kan best kloppen, maar als jouw locks niet werken heb je er niks aan om in 1 thread/process naar de content te kijken.
Overigens, als 1 thread / process altijd de hele file aanpast, wat is dan de meerwaarde van meerdere threads / processen die tegeijk dezelfde file aanpassen? Het lijkt er nu op dat je door je huidige process nooit weet wat er uiteindelijk in de file terecht komt (uitkomst van process 1, 2 of een combinatie van beide omdat de locks niet op deze manier werken...)

Begin eens nat te denken over wat je wil bereiken en of de manier die je nu hebt de beste is.

Acties:
  • 0 Henk 'm!

  • eheijnen
  • Registratie: Juli 2008
  • Niet online
Volgens de flock docs geeft flock(...) een boolean terug en die kan dus ook false zijn.

Betekent dat het niet persé zo is dat flock(...) ook altijd de file locked.
Voorbeeld in de documentatie laat dit ook zien.

Ongeacht de uitkomst van flock(...) voer je dan toch die loop uit...

Edit:
Je opent de file met ''w' en die werkt als volgt (docs):
'w' Open for writing only; place the file pointer at the beginning of the file and truncate the file to zero length. If the file does not exist, attempt to create it.
Volgens jouw beschrijving wil je aan het bestand toevoegen...?

[ Voor 35% gewijzigd door eheijnen op 27-07-2022 10:50 ]

Wie du mir, so ich dir.


Acties:
  • 0 Henk 'm!

  • Voutloos
  • Registratie: Januari 2002
  • Niet online
Van élke system call moet je de return value checken.
Dus als je dan per se de fout wil debuggen (nogmaals beste fix is native file_put_contents), moét je ons vertellen wat gebeurt als je deze system call checkt. Als je dat punt negeert weet je niet wat je code uitspookt.

Het nadeel van deze low level api is ook dat je code in een grappige volgorde staat: Fopen(.., 'w') gooit bestanden leeg, maar je kan pas flock() aanroepen met de resource die je uit fopen() krijgt.

{signature}

Pagina: 1