9.2 KiB
source_files, generated_at, model, schema_version, sha256
| source_files | generated_at | model | schema_version | sha256 | |||
|---|---|---|---|---|---|---|---|
|
2026-04-16T02:06:50.106613+00:00 | Qwen/Qwen3-Coder-Next-FP8 | 1 | 95ed39e21f5416a3 |
Documentation: Database Locking for Import Operations
1. Purpose
This module provides database-level locking for import operations on three distinct entity types—Groups, Test Setups, and Sensors—to prevent concurrent modifications during data import workflows. Each class (LockImportGroups, LockImportTestSetups, LockImportSensors) implements the ILockImport interface and encapsulates logic to acquire, release, and (if authorized) steal locks on the respective entities. Locks are managed via the shared LockManager utility, with support for detecting and reclaiming stranded locks (i.e., locks held longer than a configurable timeout) and handling contention by requiring explicit admin consent. These classes exist to ensure data integrity and consistency during import processes that modify critical system resources.
2. Public Interface
All three classes implement the same interface ILockImport, with identical method signatures and semantics. Below are the concrete implementations per class.
LockImportGroups
-
Constructor:
LockImportGroups(User currentUser, double strandedLockTimeoutMinutes)
Initializes the instance with the current user and timeout (in minutes) after which a lock is considered stranded. -
bool Contended { get; }
Returnstrueif any group lock acquisition failed due to contention (i.e., another user holds the lock and it is not expired or owned by the current user). -
void FreeLock(ref ImportObject importObject)
Releases all locks currently held by this instance (stored in_lockedGroups). Iterates over_lockedGroupsand callsLockManager.FreeLock(...)for each. No-op if_lockedGroupsis empty. -
void SetLock(ref ImportObject importObject, ref StringBuilder message)
Attempts to acquire locks for all groups returned byimportObject.StaticGroups().- Clears
_lockedGroupsand_contendedGroupsfirst. - For each group:
- Calls
LockManager.LockItem(...)with categoryLockManager.ItemCategories.Group. - On failure:
- Skips if
lockError.ErrorCode == LockError.ITEM_NOT_FOUND. - Otherwise, checks if lock is expired (
LastUpdated + timeout < Now) or owned by current user/machine. If so, frees and re-acquires the lock, adding to_lockedGroups. - Else, adds the existing lock record to
_contendedGroups.
- Skips if
- On success, adds lock record to
_lockedGroups.
- Calls
- If any locks are contended, appends a user-facing message listing each contended group (key, owner, machine) using
StringResources.ImportTestSetup_GroupsLocked.
- Clears
-
bool StealLock(bool proceed)
Attempts to steal contended locks (only ifproceed == trueand_currentUser.IsAdmin == true).- Returns
trueif no contention or if stealing succeeded. - Returns
falseif user is not admin orproceedisfalse. - For each contended group:
- Calls
LockManager.FreeLock(...)to release the existing lock. - Calls
LockManager.LockItem(...)again (note: category incorrectly set toLockManager.ItemCategories.Sensor—see Gotchas). - Adds the new lock record to
_lockedGroups.
- Calls
- Returns
LockImportTestSetups
-
Constructor:
LockImportTestSetups(User currentUser, double strandedLockTimeoutMinutes)
Same semantics asLockImportGroups. -
bool Contended { get; }
Same semantics. -
void FreeLock(ref ImportObject importObject)
Releases locks in_lockedTestsviaLockManager.FreeLock(...). -
void SetLock(ref ImportObject importObject, ref StringBuilder message)
Attempts to lock all test setups returned byimportObject.TestSetups(), using categoryLockManager.ItemCategories.TestSetup.- On contention, appends message using
StringResources.ImportTestSetup_TestsLocked.
- On contention, appends message using
-
bool StealLock(bool proceed)
Same logic asLockImportGroups.StealLock(...), but operates on_contendedTestsand_lockedTests.
LockImportSensors
-
Constructor:
LockImportSensors(User currentUser, double strandedLockTimeoutMinutes)
Same semantics. -
bool Contended { get; }
Same semantics. -
void FreeLock(ref ImportObject importObject)
Releases locks in_lockedSensorsviaLockManager.FreeLock(...). -
void SetLock(ref ImportObject importObject, ref StringBuilder message)
Attempts to lock all sensors referenced inimportObject.Sensors().- Pre-builds a
sensorLookupfromSensorsCollection.SensorsList.GetAllSensors(true)keyed bySerialNumber. - Skips sensors not in the lookup.
- Uses category
LockManager.ItemCategories.Sensor. - On contention, appends message using
StringResources.ImportTestSetup_TestsLocked(note: same string as test setups—see Gotchas).
- Pre-builds a
-
bool StealLock(bool proceed)
Same logic as others, but operates on_contendedSensorsand_lockedSensors.
3. Invariants
-
Lock ownership and expiration:
A lock is considered reclaimable if either:existingLock.LastUpdated.AddMinutes(_strandedLockTimeoutMinutes) < DateTime.Now, orexistingLock.LockingUserName == _currentUser.UserName && existingLock.LockingMachineName == Environment.MachineName.
-
Lock state separation:
AfterSetLock(...)completes,_lockedGroups/_lockedTests/_lockedSensorscontains only locks successfully acquired (including re-claimed ones), and_contendedGroups/_contendedTests/_contendedSensorscontains only locks that could not be acquired due to active contention. -
Contendedproperty:
Always reflects whether_contended*list is non-empty after the lastSetLock(...)call. -
Admin requirement for stealing:
StealLock(...)returnsfalseunless_currentUser.IsAdmin == trueandproceed == true. -
No double-locking:
SetLock(...)clears the internal lists before processing, ensuring no stale lock records persist across calls.
4. Dependencies
External Dependencies
DTS.Common.Classes.Locking.LockManager: Central lock manager used forLockItem,FreeLock. DefinesLockError,LockRecord, andItemCategories.DTS.Slice.Users.User: Represents the current user; accessed forUserName,Id, andIsAdmin.DTS.Common.Storage.LockManager: Used inLockImportSensorsto accessSensorsCollection.SensorsList.GetAllSensors(...).DTS.Common.SharedResource.Strings.StringResources: Provides localized messages (e.g.,ImportTestSetup_GroupsLocked,ImportTestSetup_TestsLocked).DTS.Common.Utilities.Logging.APILogger: Used to log exceptions during lock operations.
Interface Dependencies
ILockImport: All three classes implement this interface (not shown in source, but implied by usage and naming). Signature must includeSetLock,FreeLock,StealLock, andContended.
Depended Upon
- These classes are likely used by higher-level import orchestrators (e.g.,
ImportService,ImportWizard) that coordinate import workflows and handle user prompts for lock stealing.
5. Gotchas
-
Incorrect category in
StealLockfor Groups:
InLockImportGroups.StealLock(...), the re-lock call usesLockManager.ItemCategories.Sensorinstead ofLockManager.ItemCategories.Group. This is inconsistent with the initial lock and may cause lock metadata corruption or misattribution. -
Incorrect resource string for sensor contention messages:
InLockImportSensors.SetLock(...), the message appended on contention usesStringResources.ImportTestSetup_TestsLocked(same as test setups), not a sensor-specific string. This may mislead users. -
No validation of
ImportObjectcontents:
The classes assumeimportObject.StaticGroups(),TestSetups(), andSensors()return valid collections. No null/empty checks are performed on the collections themselves. -
Exception swallowing in
SetLock:
All exceptions during lock acquisition are caught and logged viaAPILogger.Log(ex), but the method continues silently. This may hide critical failures (e.g., database connectivity issues) and leave the system in an inconsistent state (e.g., partial locking). -
Race condition in lock stealing:
BetweenFreeLockandLockIteminStealLock(...), another user could acquire the lock. The code does not re-check lock ownership before re-locking. -
Ambiguity in
ImportObjectinterface:
The source does not defineImportObjector its methods (StaticGroups,TestSetups,Sensors). Their behavior (e.g., ordering, nullability) is unknown. -
No rollback on partial failure:
IfSetLock(...)fails partway through (e.g., due to an exception), already-locked items are not released. OnlyFreeLock(...)(called later) cleans up. -
Machine name dependency:
Lock ownership check usesEnvironment.MachineName, which may be unreliable in containerized/distributed environments or if machine names change.
None identified beyond the above.