MVC: add locking in JsonKeyValueStoreField type.

Although in theory the current stat() should at least make sure the same thread wouldn't execute the same action twice, it seems in reality actions are being executed
for every record in a set. Calling stat() after write+flush seems to return the previous status in stead of the one just written to disk (ufs issue on fbsd?). To prevent this from happening, use fstat() and lock the target
file while processing.

Found this with the firewall api plugin enabled and a set of rules in it, which seemed to trigger "list gateways" for every record.
This commit is contained in:
Ad Schellevis 2021-01-11 19:48:32 +01:00
parent cc67f7625e
commit 8b7d1a5b1b

View File

@ -124,23 +124,30 @@ class JsonKeyValueStoreField extends BaseListField
$sourcefile = $this->internalSourceFile;
}
if (!empty($this->internalConfigdPopulateAct)) {
// execute configd action when provided
if (!is_file($sourcefile)) {
$muttime = 0;
if (is_file($sourcefile)) {
$sourcehandle = fopen($sourcefile, "r+");
} else {
$stat = stat($sourcefile);
// ignore empty files
$muttime = $stat['size'] == 0 ? 0 : $stat['mtime'];
$sourcehandle = fopen($sourcefile, "w");
}
if (time() - $muttime > $this->internalConfigdPopulateTTL) {
$act = $this->internalConfigdPopulateAct;
$backend = new Backend();
$response = $backend->configdRun($act, false, 20);
if (!empty($response) && json_decode($response) !== null) {
// only store parsable results
file_put_contents($sourcefile, $response);
if (flock($sourcehandle, LOCK_EX)) {
// execute configd action when provided
$stat = fstat($sourcehandle);
$muttime = $stat['size'] == 0 ? 0 : $stat['mtime'];
if (time() - $muttime > $this->internalConfigdPopulateTTL) {
$act = $this->internalConfigdPopulateAct;
$backend = new Backend();
$response = $backend->configdRun($act, false, 20);
if (!empty($response) && json_decode($response) !== null) {
// only store parsable results
fseek($sourcehandle, 0);
ftruncate($sourcehandle, 0);
fwrite($sourcehandle, $response);
fflush($sourcehandle);
}
}
}
flock($sourcehandle, LOCK_UN);
fclose($sourcehandle);
}
if (is_file($sourcefile)) {
$data = json_decode(file_get_contents($sourcefile), true);