Skip to content

Commit 2320701

Browse files
authored
Do not crash if there are invalid model configs in the DB (#6593)
## Summary This PR changes the handling of invalid model configs in the DB to log a warning rather than crashing the app. This change is being made in preparation for some upcoming new model additions. Previously, if a user rolled back from an app version that added a new model type, the app would not launch until the DB was fixed. This PR changes this behaviour to allow rollbacks of this type (with warnings). **Keep in mind that this change is only helpful to users _rolling back to a version that has this fix_. I.e. it offers no help in the first version that includes it.** ## QA Instructions 1. Run the Spandrel model branch, which adds a new model type #6556. 2. Add a spandrel model via the model manager. 3. Rollback to main. The app will crash on launch due to the invalid spandrel model config. 4. Checkout this branch. The app should now run with warnings about the invalid model config. ## Checklist - [x] _The PR has a short but descriptive title, suitable for a changelog_ - [ ] _Tests added / updated (if applicable)_ - [x] _Documentation added / updated (if applicable)_
2 parents 5795617 + 69af099 commit 2320701

File tree

5 files changed

+24
-7
lines changed

5 files changed

+24
-7
lines changed

docs/contributing/MODEL_MANAGER.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,7 @@ config = get_config()
408408
409409
logger = InvokeAILogger.get_logger(config=config)
410410
db = SqliteDatabase(config.db_path, logger)
411-
record_store = ModelRecordServiceSQL(db)
411+
record_store = ModelRecordServiceSQL(db, logger)
412412
queue = DownloadQueueService()
413413
queue.start()
414414

invokeai/app/api/dependencies.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ def initialize(config: InvokeAIAppConfig, event_handler_id: int, logger: Logger
9999
model_images_service = ModelImageFileStorageDisk(model_images_folder / "model_images")
100100
model_manager = ModelManagerService.build_model_manager(
101101
app_config=configuration,
102-
model_record_service=ModelRecordServiceSQL(db=db),
102+
model_record_service=ModelRecordServiceSQL(db=db, logger=logger),
103103
download_queue=download_queue_service,
104104
events=events,
105105
)

invokeai/app/services/model_records/model_records_sql.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,14 @@
4040
"""
4141

4242
import json
43+
import logging
4344
import sqlite3
4445
from math import ceil
4546
from pathlib import Path
4647
from typing import List, Optional, Union
4748

49+
import pydantic
50+
4851
from invokeai.app.services.model_records.model_records_base import (
4952
DuplicateModelException,
5053
ModelRecordChanges,
@@ -67,7 +70,7 @@
6770
class ModelRecordServiceSQL(ModelRecordServiceBase):
6871
"""Implementation of the ModelConfigStore ABC using a SQL database."""
6972

70-
def __init__(self, db: SqliteDatabase):
73+
def __init__(self, db: SqliteDatabase, logger: logging.Logger):
7174
"""
7275
Initialize a new object from preexisting sqlite3 connection and threading lock objects.
7376
@@ -76,6 +79,7 @@ def __init__(self, db: SqliteDatabase):
7679
super().__init__()
7780
self._db = db
7881
self._cursor = db.conn.cursor()
82+
self._logger = logger
7983

8084
@property
8185
def db(self) -> SqliteDatabase:
@@ -291,7 +295,20 @@ def search_by_attr(
291295
tuple(bindings),
292296
)
293297
result = self._cursor.fetchall()
294-
results = [ModelConfigFactory.make_config(json.loads(x[0]), timestamp=x[1]) for x in result]
298+
299+
# Parse the model configs.
300+
results: list[AnyModelConfig] = []
301+
for row in result:
302+
try:
303+
model_config = ModelConfigFactory.make_config(json.loads(row[0]), timestamp=row[1])
304+
except pydantic.ValidationError:
305+
# We catch this error so that the app can still run if there are invalid model configs in the database.
306+
# One reason that an invalid model config might be in the database is if someone had to rollback from a
307+
# newer version of the app that added a new model type.
308+
self._logger.warning(f"Found an invalid model config in the database. Ignoring this model. ({row[0]})")
309+
else:
310+
results.append(model_config)
311+
295312
return results
296313

297314
def search_by_path(self, path: Union[str, Path]) -> List[AnyModelConfig]:

tests/app/services/model_records/test_model_records_sql.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def store(
4040
config._root = datadir
4141
logger = InvokeAILogger.get_logger(config=config)
4242
db = create_mock_sqlite_database(config, logger)
43-
return ModelRecordServiceSQL(db)
43+
return ModelRecordServiceSQL(db, logger)
4444

4545

4646
def example_ti_config(key: Optional[str] = None) -> TextualInversionFileConfig:

tests/backend/model_manager/model_manager_fixtures.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ def mm2_installer(
110110
logger = InvokeAILogger.get_logger()
111111
db = create_mock_sqlite_database(mm2_app_config, logger)
112112
events = TestEventService()
113-
store = ModelRecordServiceSQL(db)
113+
store = ModelRecordServiceSQL(db, logger)
114114

115115
installer = ModelInstallService(
116116
app_config=mm2_app_config,
@@ -128,7 +128,7 @@ def mm2_installer(
128128
def mm2_record_store(mm2_app_config: InvokeAIAppConfig) -> ModelRecordServiceBase:
129129
logger = InvokeAILogger.get_logger(config=mm2_app_config)
130130
db = create_mock_sqlite_database(mm2_app_config, logger)
131-
store = ModelRecordServiceSQL(db)
131+
store = ModelRecordServiceSQL(db, logger)
132132
# add five simple config records to the database
133133
config1 = VAEDiffusersConfig(
134134
key="test_config_1",

0 commit comments

Comments
 (0)