Skip to content

Commit

Permalink
[events-api] Create cfg file in parallel without data truncation (glu…
Browse files Browse the repository at this point in the history
…ster#4079)

* [events-api] Write cfg file with exclusive locks

to prevent race cases that might occur when several nodes
are trying to create the config file at the same time
if it is non existent (first run).

Fixes: gluster#3714
Updates: gluster#3715

Signed-off-by: black-dragon74 <[email protected]>

* [events-api] Use tempfiles for parallel writes without contention

The tempfile created is different everytime, the logic of which is
encapsulated in `NamedTempOpen` class.

Once we are done writing the data to the file it is then persisted to the
disk with `os.rename()` call under the name of `filename` passed as an arg
when instantiating the class.

This approach saves us the need of fcntl locks and still guarantees the sanity
of the file.

Signed-off-by: black-dragon74 <[email protected]>

* [events-api] Close FD before renaming the file

As after rename fop, the already open file descriptor
will be stale and in a bad state which is erroneous.

Signed-off-by: black-dragon74 <[email protected]>

* [events-api] Hidden temp file and use dest dir as wd

The temp file created by `NamedTempOpen` is now hidden (prefixed
with a '.'). Additionally the temp file is now created in the dest
dir of the named file, i.e. in case the filepath is /foo/bar, the
temp file will be created inside /foo.

In case the dest dir cannot be determined (cases where the path is just
a filename eg. 'myfile.json`), cwd of the process is used. However, we
will never encounter this use case as we always provide the full path.

Signed-off-by: black-dragon74 <[email protected]>

* [events-api] Remove zombie file in case of exception

Signed-off-by: black-dragon74 <[email protected]>

* [events-api] Refactor code as per reviews

Signed-off-by: black-dragon74 <[email protected]>

* Fix regression failures due to py3 syntax

Change-Id: I976ded40393c6b84c4c08428f3a025df1e21b614
Signed-off-by: black-dragon74 <[email protected]>

---------

Signed-off-by: black-dragon74 <[email protected]>
  • Loading branch information
black-dragon74 authored Mar 28, 2023
1 parent 5ad0830 commit db96516
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 5 deletions.
10 changes: 5 additions & 5 deletions events/src/peer_eventsapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
sync_file_to_peers, GlusterCmdException,
output_error, execute_in_peers, runcli,
set_common_args_func)
from gfevents.utils import LockedOpen, get_jwt_token, save_https_cert
from gfevents.utils import LockedOpen, get_jwt_token, save_https_cert, NamedTempOpen

from gfevents.eventsapiconf import (WEBHOOKS_FILE_TO_SYNC,
WEBHOOKS_FILE,
Expand Down Expand Up @@ -78,8 +78,8 @@ def create_custom_config_file_if_not_exists(args):
json_output=args.json)

if not os.path.exists(CUSTOM_CONFIG_FILE):
with open(CUSTOM_CONFIG_FILE, "w") as f:
f.write("{}")
with NamedTempOpen(CUSTOM_CONFIG_FILE, "w") as f:
f.write(json.dumps({}))


def create_webhooks_file_if_not_exists(args):
Expand All @@ -91,8 +91,8 @@ def create_webhooks_file_if_not_exists(args):
json_output=args.json)

if not os.path.exists(WEBHOOKS_FILE):
with open(WEBHOOKS_FILE, "w") as f:
f.write("{}")
with NamedTempOpen(WEBHOOKS_FILE, "w") as f:
f.write(json.dumps({}))


def boolify(value):
Expand Down
40 changes: 40 additions & 0 deletions events/src/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import hmac
from hashlib import sha256
from calendar import timegm
from tempfile import NamedTemporaryFile

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

Expand Down Expand Up @@ -313,6 +314,45 @@ def plugin_webhook(message):
webhooks_pool.send(message["event"], message["ts"], message_json)


class NamedTempOpen(object):
"""
This class is used to create a temporary file which is then written to with contents.
The temp file is then persisted with the give name by calling os.rename().
This class is used to avoid the data loss or truncation in case of multiple processes
writing to the same file without the use of fcntl locks.
The temporary file is created in the dest dir of the file.
"""

def __init__(self, filename, open_mode, *args, **kwagrs):
self.filename = filename
self.open_mode = open_mode
self.open_args = args
self.open_kwargs = kwagrs
self.working_dir = "."
self.fileobj = None

self.working_dir = os.path.dirname(os.path.realpath(self.filename))

def __enter__(self):
tfile = NamedTemporaryFile(mode=self.open_mode,
delete=False,
prefix='.',
dir=self.working_dir,
*self.open_args,
**self.open_kwargs)

self.fileobj = tfile
return self.fileobj

def __exit__(self, ex_type, ex_val, ex_tb):
self.fileobj.close()

if ex_type is not None:
os.unlink(self.fileobj.name)
else:
os.rename(self.fileobj.name, self.filename)

class LockedOpen(object):

def __init__(self, filename, *args, **kwargs):
Expand Down

0 comments on commit db96516

Please sign in to comment.