Repository: blakeblackshear/frigate

Issue:
Should not we store the database file in `/data` by default?
1. `/data` is unique per add-on, which persists for as long as the add-on is installed
2. Uninstalling the add-on would cause the database file to get deleted, which I think makes sense
3. I suppose several people maps their `/media/frigate` folder to an external (potentially an much slower HDD) device. It isn't smart to store the database in the HDD in this case.



Discussion:
I'm not sure I see a good way to change this without being a major breaking change, I agree anyone with /media/frigate would want the db on a separate drive, but it is a complicated thing to support existing users
How about:

- If a custom db path is set in frigate.yml, do nothing, use it as is
- Otherwise, check if `/media/frigate/frigate.db` (or whatever is the default filename) exists. If it does, move it to `/data/frigate.db`.
that only works for the addon though which has /data/ mapped to it. Any non addon user doesn't have /data/ mapped which means all the future DB changes would not be saved once frigate restarted and the db would be gone
Docs can be updated to include this additional volume mount. Also, this can be mentioned as part of the release notes. Something like:

- The database is now by default stored at /data. Frigate will automatically move the database file to the new location. Please add this additional volume mount to your docker-compose.yaml to ensure it will persist across restarts.

---

Or we can of course use different database paths by default for add-on vs docker. This can be handled during add-on startup. Something like:

```
if "running as addon"; then
  echo '/data/frigate.db' > /var/run/s6/container_environment/FRIGATE_DEFAULT_DB_PATH
fi
```

And then, Frigate as Python can read the `FRIGATE_DEFAULT_DB_PATH` environment variable. But I still prefer the first option. Chances are that even people running as a container will still map /media to a separate disk.
the problem is not everyone reads the release notes, in general it is what it is, but a breaking change which would result in data lost if the user didn't read the release notes is not acceptable IMO.

I also have a problem with

> Uninstalling the add-on would cause the database file to get deleted, which I think makes sense

many users switch between the normal and full access addon and between the normal and beta addon, we don't want that operation to result in lost data
In this case, even `/config` is a better place than `/media`.
But I'm all in for breaking changes as Frigate is still in 0.x. This is in accordance with SemVer, which expects breaking changes to occur in the development phase.

> many users switch between the normal and full access addon and between the normal and beta addon, we don't want that operation to result in lost data

I can't argue with this.
Another option would be to store the db at `/media/frigate.db` instead of `/media/frigate/frigate.db`, supposing people would map `/media/frigate` to an external disk rather than the whole `/media`.
> But I'm all in for breaking changes as Frigate is still in 0.x. This is in accordance with SemVer, which expects breaking changes to occur in the development phase.

That's fair, but as someone who works to support users in the issues it is not that simple and being in 0.x doesn't change the time it takes to help users solve the breaking changes. Also, we're trying to move away from breaking changes as it just makes things more difficult for everyone involved.
I think this change is probably a good idea. It just needs to be done very carefully. 
Home Assistant stores its db in `/config`:

```console
> ls -lha /config/*.db
-rw-r--r--    1 root     root     1000.8M Jan 23 20:30 /config/home-assistant_v2.db
```

Thus, I'd suggest we do it as well. There's no downside of it, I think.
> Thus, I'd suggest we do it as well. There's no downside of it, I think.

many users right now have their config file mapped directly instead of the entire config direcory, so it would end up being written to nothing for non addon installs and when the container restarts the DB is erased with it.
Oh yeah. I was only thinking about the add-on users. Here's something we can do:

```bash
# db migration

if [[ there is nothing set in frigate.yaml:database.path ]]; then
  if [[ -f /media/frigate/frigate.db ]]; then
    if mountpoint /config; then
      # /config is a mount point, move the db
      echo "[INFO] Moving db to /config..."
      mv -f /media/frigate/frigate.db
    else
      echo "[ERROR] Trying to move the db to /config but it's not a mountpoint, please mount the /config folder"
      exit 1
    fi
  fi
fi
```

![image](https://user-images.githubusercontent.com/29582865/214177045-877a62de-54b9-42a3-980f-1fda48e0683f.png)

yeah, that does seem like a solution that could work 🤔 
Now... should it be done as part of the Frigate process, or should we do as a s6 oneshot/init service?
> Now... should it be done as part of the Frigate process, or should we do as a s6 oneshot/init service?

Feel free to work on it but just FYI I don't believe it will be included in 0.12. We are looking to eventually break different processes into their own S6 processes along with the potential idea for zeromq so I think it would make sense as an S6 service init

Resolving patch:
diff --git a/frigate/app.py b/frigate/app.py
--- a/frigate/app.py
+++ b/frigate/app.py
@@ -18,7 +18,7 @@
 from frigate.comms.mqtt import MqttClient
 from frigate.comms.ws import WebSocketClient
 from frigate.config import FrigateConfig
-from frigate.const import CACHE_DIR, CLIPS_DIR, RECORD_DIR
+from frigate.const import CACHE_DIR, CLIPS_DIR, CONFIG_DIR, DEFAULT_DB_PATH, RECORD_DIR
 from frigate.object_detection import ObjectDetectProcess
 from frigate.events import EventCleanup, EventProcessor
 from frigate.http import create_app
@@ -55,7 +55,7 @@ def set_environment_vars(self) -> None:
             os.environ[key] = value
 
     def ensure_dirs(self) -> None:
-        for d in [RECORD_DIR, CLIPS_DIR, CACHE_DIR]:
+        for d in [CONFIG_DIR, RECORD_DIR, CLIPS_DIR, CACHE_DIR]:
             if not os.path.exists(d) and not os.path.islink(d):
                 logger.info(f"Creating directory: {d}")
                 os.makedirs(d)
@@ -141,7 +141,7 @@ def init_queues(self) -> None:
 
     def init_database(self) -> None:
         # Migrate DB location
-        old_db_path = os.path.join(CLIPS_DIR, "frigate.db")
+        old_db_path = DEFAULT_DB_PATH
         if not os.path.isfile(self.config.database.path) and os.path.isfile(
             old_db_path
         ):
diff --git a/frigate/config.py b/frigate/config.py
--- a/frigate/config.py
+++ b/frigate/config.py
@@ -13,8 +13,8 @@
 from pydantic.fields import PrivateAttr
 
 from frigate.const import (
-    BASE_DIR,
     CACHE_DIR,
+    DEFAULT_DB_PATH,
     REGEX_CAMERA_NAME,
     YAML_EXT,
 )
@@ -731,9 +731,7 @@ def _get_ffmpeg_cmd(self, ffmpeg_input: CameraInput):
 
 
 class DatabaseConfig(FrigateBaseModel):
-    path: str = Field(
-        default=os.path.join(BASE_DIR, "frigate.db"), title="Database path."
-    )
+    path: str = Field(default=DEFAULT_DB_PATH, title="Database path.")
 
 
 class LogLevelEnum(str, Enum):
diff --git a/frigate/const.py b/frigate/const.py
--- a/frigate/const.py
+++ b/frigate/const.py
@@ -1,3 +1,5 @@
+CONFIG_DIR = "/config"
+DEFAULT_DB_PATH = f"{CONFIG_DIR}/frigate.db"
 BASE_DIR = "/media/frigate"
 CLIPS_DIR = f"{BASE_DIR}/clips"
 RECORD_DIR = f"{BASE_DIR}/recordings"


Test patch: