We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
JournalStorage not working on Windows10
### Expected behavior

I was using the minimal JournalStorage example from the optuna documentation on my Windows10 PC. 
However the script is not able to run and throws every time json decoding errors.

According to the latest documentation I didn´t see any limitations regarding the OS on which JournalStorage can be used. But maybe I was missing something.


### Environment

- Optuna version:3.1.0.dev
- Python version:3.10.6
- OS:Windows-10-10.0.19044-SP0
- (Optional) Other libraries and their versions:


### Error messages, stack traces, or logs

```shell
C:\1_Files\M\optuna_trial.py:9: ExperimentalWarning: JournalStorage is experimental (supported from v3.1.0). The interface can change in the future.
  storage = optuna.storages.JournalStorage(
Traceback (most recent call last):
  File "C:\1_Files\M\optuna_trial.py", line 12, in <module>
    study = optuna.create_study(storage=storage)
  File "C:\Users\Prometheus\miniconda3\envs\py310\lib\site-packages\optuna\_convert_positional_args.py", line 63, in converter_wrapper
    return func(**kwargs)
  File "C:\Users\Prometheus\miniconda3\envs\py310\lib\site-packages\optuna\study\study.py", line 1155, in create_study
    study_id = storage.create_new_study(study_name)
  File "C:\Users\Prometheus\miniconda3\envs\py310\lib\site-packages\optuna\storages\_journal\storage.py", line 111, in create_new_study
    self._sync_with_backend()
  File "C:\Users\Prometheus\miniconda3\envs\py310\lib\site-packages\optuna\storages\_journal\storage.py", line 103, in _sync_with_backend
    logs = self._backend.read_logs(self._replay_result.log_number_read)
  File "C:\Users\Prometheus\miniconda3\envs\py310\lib\site-packages\optuna\storages\_journal\file.py", line 174, in read_logs
    raise last_decode_error
  File "C:\Users\Prometheus\miniconda3\envs\py310\lib\site-packages\optuna\storages\_journal\file.py", line 184, in read_logs
    logs.append(json.loads(line))
  File "C:\Users\Prometheus\miniconda3\envs\py310\lib\json\__init__.py", line 346, in loads
    return _default_decoder.decode(s)
  File "C:\Users\Prometheus\miniconda3\envs\py310\lib\json\decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "C:\Users\Prometheus\miniconda3\envs\py310\lib\json\decoder.py", line 353, in raw_decode
    obj, end = self.scan_once(s, idx)
json.decoder.JSONDecodeError: Invalid control character at: line 1 column 3 (char 2)
```


### Steps to reproduce

1. Run any JournalStorage example on a Windows10 PC
2. Python finishes with error
3.
```python
import optuna


def objective(trial):
    x = trial.suggest_float("x", -10, 10)
    return (x - 2) ** 2


storage = optuna.storages.JournalStorage(
    optuna.storages.JournalFileStorage("./log_file"))

study = optuna.create_study(storage=storage)
study.optimize(objective)
```


### Additional context (optional)

running the script as an admin yields a slightly different error

```
  File "C:\Users\Prometheus\miniconda3\envs\py310\lib\json\decoder.py", line 355, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 2 column 1 (char 1)
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `optuna/storages/_journal/file.py`
Content:
```
1 import abc
2 from contextlib import contextmanager
3 import errno
4 import json
5 import os
6 import time
7 from typing import Any
8 from typing import Dict
9 from typing import Iterator
10 from typing import List
11 from typing import Optional
12 import uuid
13 
14 from optuna.storages._journal.base import BaseJournalLogStorage
15 
16 
17 LOCK_FILE_SUFFIX = ".lock"
18 RENAME_FILE_SUFFIX = ".rename"
19 
20 
21 class JournalFileBaseLock(abc.ABC):
22     @abc.abstractmethod
23     def acquire(self) -> bool:
24         raise NotImplementedError
25 
26     @abc.abstractmethod
27     def release(self) -> None:
28         raise NotImplementedError
29 
30 
31 class JournalFileSymlinkLock(JournalFileBaseLock):
32     """Lock class for synchronizing processes for NFSv2 or later.
33 
34     On acquiring the lock, link system call is called to create an exclusive file. The file is
35     deleted when the lock is released. In NFS environments prior to NFSv3, use this instead of
36     :class:`~optuna.storages.JournalFileOpenLock`
37 
38     Args:
39         filepath:
40             The path of the file whose race condition must be protected.
41     """
42 
43     def __init__(self, filepath: str) -> None:
44         self._lock_target_file = filepath
45         self._lock_file = filepath + LOCK_FILE_SUFFIX
46         self._lock_rename_file = self._lock_file + str(uuid.uuid4()) + RENAME_FILE_SUFFIX
47 
48     def acquire(self) -> bool:
49         """Acquire a lock in a blocking way by creating a symbolic link of a file.
50 
51         Returns:
52             :obj:`True` if it succeeded in creating a symbolic link of `self._lock_target_file`.
53 
54         """
55         sleep_secs = 0.001
56         while True:
57             try:
58                 os.symlink(self._lock_target_file, self._lock_file)
59                 return True
60             except OSError as err:
61                 if err.errno == errno.EEXIST:
62                     time.sleep(sleep_secs)
63                     sleep_secs = min(sleep_secs * 2, 1)
64                     continue
65                 raise err
66             except BaseException:
67                 self.release()
68                 raise
69 
70     def release(self) -> None:
71         """Release a lock by removing the symbolic link."""
72 
73         try:
74             os.rename(self._lock_file, self._lock_rename_file)
75             os.unlink(self._lock_rename_file)
76         except OSError:
77             raise RuntimeError("Error: did not possess lock")
78         except BaseException:
79             os.unlink(self._lock_rename_file)
80             raise
81 
82 
83 class JournalFileOpenLock(JournalFileBaseLock):
84     """Lock class for synchronizing processes for NFSv3 or later.
85 
86     On acquiring the lock, open system call is called with the O_EXCL option to create an exclusive
87     file. The file is deleted when the lock is released. This class is only supported when using
88     NFSv3 or later on kernel 2.6 or later. In prior NFS environments, use
89     :class:`~optuna.storages.JournalFileSymlinkLock`.
90 
91     Args:
92         filepath:
93             The path of the file whose race condition must be protected.
94     """
95 
96     def __init__(self, filepath: str) -> None:
97         self._lock_file = filepath + LOCK_FILE_SUFFIX
98         self._lock_rename_file = self._lock_file + str(uuid.uuid4()) + RENAME_FILE_SUFFIX
99 
100     def acquire(self) -> bool:
101         """Acquire a lock in a blocking way by creating a lock file.
102 
103         Returns:
104             :obj:`True` if it succeeded in creating a `self._lock_file`
105 
106         """
107         sleep_secs = 0.001
108         while True:
109             try:
110                 open_flags = os.O_CREAT | os.O_EXCL | os.O_WRONLY
111                 os.close(os.open(self._lock_file, open_flags))
112                 return True
113             except OSError as err:
114                 if err.errno == errno.EEXIST:
115                     time.sleep(sleep_secs)
116                     sleep_secs = min(sleep_secs * 2, 1)
117                     continue
118                 raise err
119             except BaseException:
120                 self.release()
121                 raise
122 
123     def release(self) -> None:
124         """Release a lock by removing the created file."""
125 
126         try:
127             os.rename(self._lock_file, self._lock_rename_file)
128             os.unlink(self._lock_rename_file)
129         except OSError:
130             raise RuntimeError("Error: did not possess lock")
131         except BaseException:
132             os.unlink(self._lock_rename_file)
133             raise
134 
135 
136 @contextmanager
137 def get_lock_file(lock_obj: JournalFileBaseLock) -> Iterator[None]:
138     lock_obj.acquire()
139     try:
140         yield
141     finally:
142         lock_obj.release()
143 
144 
145 class JournalFileStorage(BaseJournalLogStorage):
146     """File storage class for Journal log backend.
147 
148     Args:
149         file_path:
150             Path of file to persist the log to.
151 
152         lock_obj:
153             Lock object for process exclusivity.
154 
155     """
156 
157     def __init__(self, file_path: str, lock_obj: Optional[JournalFileBaseLock] = None) -> None:
158         self._file_path: str = file_path
159         self._lock = lock_obj or JournalFileSymlinkLock(self._file_path)
160         open(self._file_path, "a").close()  # Create a file if it does not exist
161         self._log_number_offset: Dict[int, int] = {0: 0}
162 
163     def read_logs(self, log_number_from: int) -> List[Dict[str, Any]]:
164         logs = []
165         with open(self._file_path, "r") as f:
166             log_number_start = 0
167             if log_number_from in self._log_number_offset:
168                 f.seek(self._log_number_offset[log_number_from])
169                 log_number_start = log_number_from
170 
171             last_decode_error = None
172             for log_number, line in enumerate(f, start=log_number_start):
173                 if last_decode_error is not None:
174                     raise last_decode_error
175                 if log_number + 1 not in self._log_number_offset:
176                     byte_len = len(line.encode("utf-8"))
177                     self._log_number_offset[log_number + 1] = (
178                         self._log_number_offset[log_number] + byte_len
179                     )
180                 if log_number < log_number_from:
181                     continue
182                 try:
183                     logs.append(json.loads(line))
184                 except json.JSONDecodeError as err:
185                     last_decode_error = err
186                     del self._log_number_offset[log_number + 1]
187 
188             return logs
189 
190     def append_logs(self, logs: List[Dict[str, Any]]) -> None:
191         with get_lock_file(self._lock):
192             what_to_write = "\n".join([json.dumps(log) for log in logs]) + "\n"
193             with open(self._file_path, "a", encoding="utf-8") as f:
194                 f.write(what_to_write)
195                 f.flush()
196                 os.fsync(f.fileno())
197
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/optuna/storages/_journal/file.py b/optuna/storages/_journal/file.py
--- a/optuna/storages/_journal/file.py
+++ b/optuna/storages/_journal/file.py
@@ -157,12 +157,12 @@
     def __init__(self, file_path: str, lock_obj: Optional[JournalFileBaseLock] = None) -> None:
         self._file_path: str = file_path
         self._lock = lock_obj or JournalFileSymlinkLock(self._file_path)
-        open(self._file_path, "a").close()  # Create a file if it does not exist
+        open(self._file_path, "ab").close()  # Create a file if it does not exist
         self._log_number_offset: Dict[int, int] = {0: 0}
 
     def read_logs(self, log_number_from: int) -> List[Dict[str, Any]]:
         logs = []
-        with open(self._file_path, "r") as f:
+        with open(self._file_path, "rb") as f:
             log_number_start = 0
             if log_number_from in self._log_number_offset:
                 f.seek(self._log_number_offset[log_number_from])
@@ -173,7 +173,7 @@
                 if last_decode_error is not None:
                     raise last_decode_error
                 if log_number + 1 not in self._log_number_offset:
-                    byte_len = len(line.encode("utf-8"))
+                    byte_len = len(line)
                     self._log_number_offset[log_number + 1] = (
                         self._log_number_offset[log_number] + byte_len
                     )
@@ -190,7 +190,7 @@
     def append_logs(self, logs: List[Dict[str, Any]]) -> None:
         with get_lock_file(self._lock):
             what_to_write = "\n".join([json.dumps(log) for log in logs]) + "\n"
-            with open(self._file_path, "a", encoding="utf-8") as f:
-                f.write(what_to_write)
+            with open(self._file_path, "ab") as f:
+                f.write(what_to_write.encode("utf-8"))
                 f.flush()
                 os.fsync(f.fileno())

{"golden_diff": "diff --git a/optuna/storages/_journal/file.py b/optuna/storages/_journal/file.py\n--- a/optuna/storages/_journal/file.py\n+++ b/optuna/storages/_journal/file.py\n@@ -157,12 +157,12 @@\n     def __init__(self, file_path: str, lock_obj: Optional[JournalFileBaseLock] = None) -> None:\n         self._file_path: str = file_path\n         self._lock = lock_obj or JournalFileSymlinkLock(self._file_path)\n-        open(self._file_path, \"a\").close()  # Create a file if it does not exist\n+        open(self._file_path, \"ab\").close()  # Create a file if it does not exist\n         self._log_number_offset: Dict[int, int] = {0: 0}\n \n     def read_logs(self, log_number_from: int) -> List[Dict[str, Any]]:\n         logs = []\n-        with open(self._file_path, \"r\") as f:\n+        with open(self._file_path, \"rb\") as f:\n             log_number_start = 0\n             if log_number_from in self._log_number_offset:\n                 f.seek(self._log_number_offset[log_number_from])\n@@ -173,7 +173,7 @@\n                 if last_decode_error is not None:\n                     raise last_decode_error\n                 if log_number + 1 not in self._log_number_offset:\n-                    byte_len = len(line.encode(\"utf-8\"))\n+                    byte_len = len(line)\n                     self._log_number_offset[log_number + 1] = (\n                         self._log_number_offset[log_number] + byte_len\n                     )\n@@ -190,7 +190,7 @@\n     def append_logs(self, logs: List[Dict[str, Any]]) -> None:\n         with get_lock_file(self._lock):\n             what_to_write = \"\\n\".join([json.dumps(log) for log in logs]) + \"\\n\"\n-            with open(self._file_path, \"a\", encoding=\"utf-8\") as f:\n-                f.write(what_to_write)\n+            with open(self._file_path, \"ab\") as f:\n+                f.write(what_to_write.encode(\"utf-8\"))\n                 f.flush()\n                 os.fsync(f.fileno())\n", "issue": "JournalStorage not working on Windows10\n### Expected behavior\r\n\r\nI was using the minimal JournalStorage example from the optuna documentation on my Windows10 PC. \r\nHowever the script is not able to run and throws every time json decoding errors.\r\n\r\nAccording to the latest documentation I didn\u00b4t see any limitations regarding the OS on which JournalStorage can be used. But maybe I was missing something.\r\n\r\n\r\n### Environment\r\n\r\n- Optuna version:3.1.0.dev\r\n- Python version:3.10.6\r\n- OS:Windows-10-10.0.19044-SP0\r\n- (Optional) Other libraries and their versions:\r\n\r\n\r\n### Error messages, stack traces, or logs\r\n\r\n```shell\r\nC:\\1_Files\\M\\optuna_trial.py:9: ExperimentalWarning: JournalStorage is experimental (supported from v3.1.0). The interface can change in the future.\r\n  storage = optuna.storages.JournalStorage(\r\nTraceback (most recent call last):\r\n  File \"C:\\1_Files\\M\\optuna_trial.py\", line 12, in <module>\r\n    study = optuna.create_study(storage=storage)\r\n  File \"C:\\Users\\Prometheus\\miniconda3\\envs\\py310\\lib\\site-packages\\optuna\\_convert_positional_args.py\", line 63, in converter_wrapper\r\n    return func(**kwargs)\r\n  File \"C:\\Users\\Prometheus\\miniconda3\\envs\\py310\\lib\\site-packages\\optuna\\study\\study.py\", line 1155, in create_study\r\n    study_id = storage.create_new_study(study_name)\r\n  File \"C:\\Users\\Prometheus\\miniconda3\\envs\\py310\\lib\\site-packages\\optuna\\storages\\_journal\\storage.py\", line 111, in create_new_study\r\n    self._sync_with_backend()\r\n  File \"C:\\Users\\Prometheus\\miniconda3\\envs\\py310\\lib\\site-packages\\optuna\\storages\\_journal\\storage.py\", line 103, in _sync_with_backend\r\n    logs = self._backend.read_logs(self._replay_result.log_number_read)\r\n  File \"C:\\Users\\Prometheus\\miniconda3\\envs\\py310\\lib\\site-packages\\optuna\\storages\\_journal\\file.py\", line 174, in read_logs\r\n    raise last_decode_error\r\n  File \"C:\\Users\\Prometheus\\miniconda3\\envs\\py310\\lib\\site-packages\\optuna\\storages\\_journal\\file.py\", line 184, in read_logs\r\n    logs.append(json.loads(line))\r\n  File \"C:\\Users\\Prometheus\\miniconda3\\envs\\py310\\lib\\json\\__init__.py\", line 346, in loads\r\n    return _default_decoder.decode(s)\r\n  File \"C:\\Users\\Prometheus\\miniconda3\\envs\\py310\\lib\\json\\decoder.py\", line 337, in decode\r\n    obj, end = self.raw_decode(s, idx=_w(s, 0).end())\r\n  File \"C:\\Users\\Prometheus\\miniconda3\\envs\\py310\\lib\\json\\decoder.py\", line 353, in raw_decode\r\n    obj, end = self.scan_once(s, idx)\r\njson.decoder.JSONDecodeError: Invalid control character at: line 1 column 3 (char 2)\r\n```\r\n\r\n\r\n### Steps to reproduce\r\n\r\n1. Run any JournalStorage example on a Windows10 PC\r\n2. Python finishes with error\r\n3.\r\n```python\r\nimport optuna\r\n\r\n\r\ndef objective(trial):\r\n    x = trial.suggest_float(\"x\", -10, 10)\r\n    return (x - 2) ** 2\r\n\r\n\r\nstorage = optuna.storages.JournalStorage(\r\n    optuna.storages.JournalFileStorage(\"./log_file\"))\r\n\r\nstudy = optuna.create_study(storage=storage)\r\nstudy.optimize(objective)\r\n```\r\n\r\n\r\n### Additional context (optional)\r\n\r\nrunning the script as an admin yields a slightly different error\r\n\r\n```\r\n  File \"C:\\Users\\Prometheus\\miniconda3\\envs\\py310\\lib\\json\\decoder.py\", line 355, in raw_decode\r\n    raise JSONDecodeError(\"Expecting value\", s, err.value) from None\r\njson.decoder.JSONDecodeError: Expecting value: line 2 column 1 (char 1)\r\n```\n", "before_files": [{"content": "import abc\nfrom contextlib import contextmanager\nimport errno\nimport json\nimport os\nimport time\nfrom typing import Any\nfrom typing import Dict\nfrom typing import Iterator\nfrom typing import List\nfrom typing import Optional\nimport uuid\n\nfrom optuna.storages._journal.base import BaseJournalLogStorage\n\n\nLOCK_FILE_SUFFIX = \".lock\"\nRENAME_FILE_SUFFIX = \".rename\"\n\n\nclass JournalFileBaseLock(abc.ABC):\n    @abc.abstractmethod\n    def acquire(self) -> bool:\n        raise NotImplementedError\n\n    @abc.abstractmethod\n    def release(self) -> None:\n        raise NotImplementedError\n\n\nclass JournalFileSymlinkLock(JournalFileBaseLock):\n    \"\"\"Lock class for synchronizing processes for NFSv2 or later.\n\n    On acquiring the lock, link system call is called to create an exclusive file. The file is\n    deleted when the lock is released. In NFS environments prior to NFSv3, use this instead of\n    :class:`~optuna.storages.JournalFileOpenLock`\n\n    Args:\n        filepath:\n            The path of the file whose race condition must be protected.\n    \"\"\"\n\n    def __init__(self, filepath: str) -> None:\n        self._lock_target_file = filepath\n        self._lock_file = filepath + LOCK_FILE_SUFFIX\n        self._lock_rename_file = self._lock_file + str(uuid.uuid4()) + RENAME_FILE_SUFFIX\n\n    def acquire(self) -> bool:\n        \"\"\"Acquire a lock in a blocking way by creating a symbolic link of a file.\n\n        Returns:\n            :obj:`True` if it succeeded in creating a symbolic link of `self._lock_target_file`.\n\n        \"\"\"\n        sleep_secs = 0.001\n        while True:\n            try:\n                os.symlink(self._lock_target_file, self._lock_file)\n                return True\n            except OSError as err:\n                if err.errno == errno.EEXIST:\n                    time.sleep(sleep_secs)\n                    sleep_secs = min(sleep_secs * 2, 1)\n                    continue\n                raise err\n            except BaseException:\n                self.release()\n                raise\n\n    def release(self) -> None:\n        \"\"\"Release a lock by removing the symbolic link.\"\"\"\n\n        try:\n            os.rename(self._lock_file, self._lock_rename_file)\n            os.unlink(self._lock_rename_file)\n        except OSError:\n            raise RuntimeError(\"Error: did not possess lock\")\n        except BaseException:\n            os.unlink(self._lock_rename_file)\n            raise\n\n\nclass JournalFileOpenLock(JournalFileBaseLock):\n    \"\"\"Lock class for synchronizing processes for NFSv3 or later.\n\n    On acquiring the lock, open system call is called with the O_EXCL option to create an exclusive\n    file. The file is deleted when the lock is released. This class is only supported when using\n    NFSv3 or later on kernel 2.6 or later. In prior NFS environments, use\n    :class:`~optuna.storages.JournalFileSymlinkLock`.\n\n    Args:\n        filepath:\n            The path of the file whose race condition must be protected.\n    \"\"\"\n\n    def __init__(self, filepath: str) -> None:\n        self._lock_file = filepath + LOCK_FILE_SUFFIX\n        self._lock_rename_file = self._lock_file + str(uuid.uuid4()) + RENAME_FILE_SUFFIX\n\n    def acquire(self) -> bool:\n        \"\"\"Acquire a lock in a blocking way by creating a lock file.\n\n        Returns:\n            :obj:`True` if it succeeded in creating a `self._lock_file`\n\n        \"\"\"\n        sleep_secs = 0.001\n        while True:\n            try:\n                open_flags = os.O_CREAT | os.O_EXCL | os.O_WRONLY\n                os.close(os.open(self._lock_file, open_flags))\n                return True\n            except OSError as err:\n                if err.errno == errno.EEXIST:\n                    time.sleep(sleep_secs)\n                    sleep_secs = min(sleep_secs * 2, 1)\n                    continue\n                raise err\n            except BaseException:\n                self.release()\n                raise\n\n    def release(self) -> None:\n        \"\"\"Release a lock by removing the created file.\"\"\"\n\n        try:\n            os.rename(self._lock_file, self._lock_rename_file)\n            os.unlink(self._lock_rename_file)\n        except OSError:\n            raise RuntimeError(\"Error: did not possess lock\")\n        except BaseException:\n            os.unlink(self._lock_rename_file)\n            raise\n\n\n@contextmanager\ndef get_lock_file(lock_obj: JournalFileBaseLock) -> Iterator[None]:\n    lock_obj.acquire()\n    try:\n        yield\n    finally:\n        lock_obj.release()\n\n\nclass JournalFileStorage(BaseJournalLogStorage):\n    \"\"\"File storage class for Journal log backend.\n\n    Args:\n        file_path:\n            Path of file to persist the log to.\n\n        lock_obj:\n            Lock object for process exclusivity.\n\n    \"\"\"\n\n    def __init__(self, file_path: str, lock_obj: Optional[JournalFileBaseLock] = None) -> None:\n        self._file_path: str = file_path\n        self._lock = lock_obj or JournalFileSymlinkLock(self._file_path)\n        open(self._file_path, \"a\").close()  # Create a file if it does not exist\n        self._log_number_offset: Dict[int, int] = {0: 0}\n\n    def read_logs(self, log_number_from: int) -> List[Dict[str, Any]]:\n        logs = []\n        with open(self._file_path, \"r\") as f:\n            log_number_start = 0\n            if log_number_from in self._log_number_offset:\n                f.seek(self._log_number_offset[log_number_from])\n                log_number_start = log_number_from\n\n            last_decode_error = None\n            for log_number, line in enumerate(f, start=log_number_start):\n                if last_decode_error is not None:\n                    raise last_decode_error\n                if log_number + 1 not in self._log_number_offset:\n                    byte_len = len(line.encode(\"utf-8\"))\n                    self._log_number_offset[log_number + 1] = (\n                        self._log_number_offset[log_number] + byte_len\n                    )\n                if log_number < log_number_from:\n                    continue\n                try:\n                    logs.append(json.loads(line))\n                except json.JSONDecodeError as err:\n                    last_decode_error = err\n                    del self._log_number_offset[log_number + 1]\n\n            return logs\n\n    def append_logs(self, logs: List[Dict[str, Any]]) -> None:\n        with get_lock_file(self._lock):\n            what_to_write = \"\\n\".join([json.dumps(log) for log in logs]) + \"\\n\"\n            with open(self._file_path, \"a\", encoding=\"utf-8\") as f:\n                f.write(what_to_write)\n                f.flush()\n                os.fsync(f.fileno())\n", "path": "optuna/storages/_journal/file.py"}], "after_files": [{"content": "import abc\nfrom contextlib import contextmanager\nimport errno\nimport json\nimport os\nimport time\nfrom typing import Any\nfrom typing import Dict\nfrom typing import Iterator\nfrom typing import List\nfrom typing import Optional\nimport uuid\n\nfrom optuna.storages._journal.base import BaseJournalLogStorage\n\n\nLOCK_FILE_SUFFIX = \".lock\"\nRENAME_FILE_SUFFIX = \".rename\"\n\n\nclass JournalFileBaseLock(abc.ABC):\n    @abc.abstractmethod\n    def acquire(self) -> bool:\n        raise NotImplementedError\n\n    @abc.abstractmethod\n    def release(self) -> None:\n        raise NotImplementedError\n\n\nclass JournalFileSymlinkLock(JournalFileBaseLock):\n    \"\"\"Lock class for synchronizing processes for NFSv2 or later.\n\n    On acquiring the lock, link system call is called to create an exclusive file. The file is\n    deleted when the lock is released. In NFS environments prior to NFSv3, use this instead of\n    :class:`~optuna.storages.JournalFileOpenLock`\n\n    Args:\n        filepath:\n            The path of the file whose race condition must be protected.\n    \"\"\"\n\n    def __init__(self, filepath: str) -> None:\n        self._lock_target_file = filepath\n        self._lock_file = filepath + LOCK_FILE_SUFFIX\n        self._lock_rename_file = self._lock_file + str(uuid.uuid4()) + RENAME_FILE_SUFFIX\n\n    def acquire(self) -> bool:\n        \"\"\"Acquire a lock in a blocking way by creating a symbolic link of a file.\n\n        Returns:\n            :obj:`True` if it succeeded in creating a symbolic link of `self._lock_target_file`.\n\n        \"\"\"\n        sleep_secs = 0.001\n        while True:\n            try:\n                os.symlink(self._lock_target_file, self._lock_file)\n                return True\n            except OSError as err:\n                if err.errno == errno.EEXIST:\n                    time.sleep(sleep_secs)\n                    sleep_secs = min(sleep_secs * 2, 1)\n                    continue\n                raise err\n            except BaseException:\n                self.release()\n                raise\n\n    def release(self) -> None:\n        \"\"\"Release a lock by removing the symbolic link.\"\"\"\n\n        try:\n            os.rename(self._lock_file, self._lock_rename_file)\n            os.unlink(self._lock_rename_file)\n        except OSError:\n            raise RuntimeError(\"Error: did not possess lock\")\n        except BaseException:\n            os.unlink(self._lock_rename_file)\n            raise\n\n\nclass JournalFileOpenLock(JournalFileBaseLock):\n    \"\"\"Lock class for synchronizing processes for NFSv3 or later.\n\n    On acquiring the lock, open system call is called with the O_EXCL option to create an exclusive\n    file. The file is deleted when the lock is released. This class is only supported when using\n    NFSv3 or later on kernel 2.6 or later. In prior NFS environments, use\n    :class:`~optuna.storages.JournalFileSymlinkLock`.\n\n    Args:\n        filepath:\n            The path of the file whose race condition must be protected.\n    \"\"\"\n\n    def __init__(self, filepath: str) -> None:\n        self._lock_file = filepath + LOCK_FILE_SUFFIX\n        self._lock_rename_file = self._lock_file + str(uuid.uuid4()) + RENAME_FILE_SUFFIX\n\n    def acquire(self) -> bool:\n        \"\"\"Acquire a lock in a blocking way by creating a lock file.\n\n        Returns:\n            :obj:`True` if it succeeded in creating a `self._lock_file`\n\n        \"\"\"\n        sleep_secs = 0.001\n        while True:\n            try:\n                open_flags = os.O_CREAT | os.O_EXCL | os.O_WRONLY\n                os.close(os.open(self._lock_file, open_flags))\n                return True\n            except OSError as err:\n                if err.errno == errno.EEXIST:\n                    time.sleep(sleep_secs)\n                    sleep_secs = min(sleep_secs * 2, 1)\n                    continue\n                raise err\n            except BaseException:\n                self.release()\n                raise\n\n    def release(self) -> None:\n        \"\"\"Release a lock by removing the created file.\"\"\"\n\n        try:\n            os.rename(self._lock_file, self._lock_rename_file)\n            os.unlink(self._lock_rename_file)\n        except OSError:\n            raise RuntimeError(\"Error: did not possess lock\")\n        except BaseException:\n            os.unlink(self._lock_rename_file)\n            raise\n\n\n@contextmanager\ndef get_lock_file(lock_obj: JournalFileBaseLock) -> Iterator[None]:\n    lock_obj.acquire()\n    try:\n        yield\n    finally:\n        lock_obj.release()\n\n\nclass JournalFileStorage(BaseJournalLogStorage):\n    \"\"\"File storage class for Journal log backend.\n\n    Args:\n        file_path:\n            Path of file to persist the log to.\n\n        lock_obj:\n            Lock object for process exclusivity.\n\n    \"\"\"\n\n    def __init__(self, file_path: str, lock_obj: Optional[JournalFileBaseLock] = None) -> None:\n        self._file_path: str = file_path\n        self._lock = lock_obj or JournalFileSymlinkLock(self._file_path)\n        open(self._file_path, \"ab\").close()  # Create a file if it does not exist\n        self._log_number_offset: Dict[int, int] = {0: 0}\n\n    def read_logs(self, log_number_from: int) -> List[Dict[str, Any]]:\n        logs = []\n        with open(self._file_path, \"rb\") as f:\n            log_number_start = 0\n            if log_number_from in self._log_number_offset:\n                f.seek(self._log_number_offset[log_number_from])\n                log_number_start = log_number_from\n\n            last_decode_error = None\n            for log_number, line in enumerate(f, start=log_number_start):\n                if last_decode_error is not None:\n                    raise last_decode_error\n                if log_number + 1 not in self._log_number_offset:\n                    byte_len = len(line)\n                    self._log_number_offset[log_number + 1] = (\n                        self._log_number_offset[log_number] + byte_len\n                    )\n                if log_number < log_number_from:\n                    continue\n                try:\n                    logs.append(json.loads(line))\n                except json.JSONDecodeError as err:\n                    last_decode_error = err\n                    del self._log_number_offset[log_number + 1]\n\n            return logs\n\n    def append_logs(self, logs: List[Dict[str, Any]]) -> None:\n        with get_lock_file(self._lock):\n            what_to_write = \"\\n\".join([json.dumps(log) for log in logs]) + \"\\n\"\n            with open(self._file_path, \"ab\") as f:\n                f.write(what_to_write.encode(\"utf-8\"))\n                f.flush()\n                os.fsync(f.fileno())\n", "path": "optuna/storages/_journal/file.py"}]}