mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-03 05:51:15 +02:00
feat(api): add optimistic locking to LLM model config updates
- Include `version` field in all LLM model config response schemas - Add `expected_version` parameter to update endpoints for optimistic locking - Handle concurrent modification errors with HTTP 409 Conflict status - Update both user and group config endpoints consistently
This commit is contained in:
1 parent
9315f06e1f
commit
161b7feb01
5 files changed
+92
-8
No files matched your search
@@ -15,7 +15,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from sqlalchemy import Column, Boolean, ForeignKey, CheckConstraint, UniqueConstraint, Index
|
||||
from sqlalchemy import Column, Boolean, ForeignKey, CheckConstraint, UniqueConstraint, Index, Integer
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
@@ -39,6 +39,7 @@ class LLMModelConfig(BaseTable):
|
||||
user_id = Column(GUID, ForeignKey("users.user_id", ondelete="CASCADE"), nullable=True)
|
||||
group_id = Column(GUID, ForeignKey("user_groups.user_group_id", ondelete="CASCADE"), nullable=True)
|
||||
is_default = Column(Boolean, default=False, nullable=False)
|
||||
version = Column(Integer, default=0, nullable=False) # Optimistic locking version
|
||||
|
||||
# Relationships
|
||||
user = relationship("User", backref="llm_model_configs")
|
||||
@@ -69,4 +70,4 @@ class LLMModelConfig(BaseTable):
|
||||
def __repr__(self):
|
||||
config_name = self.config.get("name", "unnamed") if self.config else "unnamed"
|
||||
owner = f"user_{self.user_id}" if self.user_id else f"group_{self.group_id}"
|
||||
return f"<LLMModelConfig {config_name} for {owner} (default={self.is_default})>"
|
||||
return f"<LLMModelConfig {config_name} for {owner} (default={self.is_default}, version={self.version})>"
|
||||
@@ -96,9 +96,19 @@ class LLMModelConfigsRepository(BaseRepository):
|
||||
self,
|
||||
config_id: UUID,
|
||||
user_id: UUID,
|
||||
updates: Dict[str, Any]
|
||||
updates: Dict[str, Any],
|
||||
expected_version: Optional[int] = None
|
||||
) -> Optional[models.LLMModelConfig]:
|
||||
"""Update a user's LLM model configuration."""
|
||||
"""
|
||||
Update a user's LLM model configuration.
|
||||
Uses optimistic locking to prevent concurrent modifications.
|
||||
|
||||
:param config_id: Configuration ID
|
||||
:param user_id: User ID
|
||||
:param updates: Dictionary of fields to update
|
||||
:param expected_version: Expected version for optimistic locking (raises error if mismatch)
|
||||
:raises ValueError: If version mismatch (concurrent modification)
|
||||
"""
|
||||
query = select(models.LLMModelConfig).where(
|
||||
and_(
|
||||
models.LLMModelConfig.config_id == config_id,
|
||||
@@ -111,6 +121,13 @@ class LLMModelConfigsRepository(BaseRepository):
|
||||
if not db_config:
|
||||
return None
|
||||
|
||||
# Check optimistic lock if expected_version is provided
|
||||
if expected_version is not None and db_config.version != expected_version:
|
||||
raise ValueError(
|
||||
f"Concurrent modification detected. Expected version {expected_version}, "
|
||||
f"but current version is {db_config.version}. Please retry."
|
||||
)
|
||||
|
||||
# Encrypt API key if present in updates
|
||||
from gns3server.utils.encryption import encrypt
|
||||
updates_copy = updates.copy()
|
||||
@@ -130,6 +147,8 @@ class LLMModelConfigsRepository(BaseRepository):
|
||||
current_config[key] = value
|
||||
|
||||
db_config.config = current_config
|
||||
# Increment version for optimistic locking
|
||||
db_config.version = db_config.version + 1
|
||||
await self._db_session.commit()
|
||||
await self._db_session.refresh(db_config)
|
||||
return db_config
|
||||
@@ -234,9 +253,19 @@ class LLMModelConfigsRepository(BaseRepository):
|
||||
self,
|
||||
config_id: UUID,
|
||||
group_id: UUID,
|
||||
updates: Dict[str, Any]
|
||||
updates: Dict[str, Any],
|
||||
expected_version: Optional[int] = None
|
||||
) -> Optional[models.LLMModelConfig]:
|
||||
"""Update a group's LLM model configuration."""
|
||||
"""
|
||||
Update a group's LLM model configuration.
|
||||
Uses optimistic locking to prevent concurrent modifications.
|
||||
|
||||
:param config_id: Configuration ID
|
||||
:param group_id: Group ID
|
||||
:param updates: Dictionary of fields to update
|
||||
:param expected_version: Expected version for optimistic locking (raises error if mismatch)
|
||||
:raises ValueError: If version mismatch (concurrent modification)
|
||||
"""
|
||||
query = select(models.LLMModelConfig).where(
|
||||
and_(
|
||||
models.LLMModelConfig.config_id == config_id,
|
||||
@@ -249,6 +278,13 @@ class LLMModelConfigsRepository(BaseRepository):
|
||||
if not db_config:
|
||||
return None
|
||||
|
||||
# Check optimistic lock if expected_version is provided
|
||||
if expected_version is not None and db_config.version != expected_version:
|
||||
raise ValueError(
|
||||
f"Concurrent modification detected. Expected version {expected_version}, "
|
||||
f"but current version is {db_config.version}. Please retry."
|
||||
)
|
||||
|
||||
# Encrypt API key if present in updates
|
||||
from gns3server.utils.encryption import encrypt
|
||||
updates_copy = updates.copy()
|
||||
@@ -268,6 +304,8 @@ class LLMModelConfigsRepository(BaseRepository):
|
||||
current_config[key] = value
|
||||
|
||||
db_config.config = current_config
|
||||
# Increment version for optimistic locking
|
||||
db_config.version = db_config.version + 1
|
||||
await self._db_session.commit()
|
||||
await self._db_session.refresh(db_config)
|
||||
return db_config
|
||||
|
||||
Reference in new issue
Block a user