1. Understands locking mechanism functionality
Key Points
- LOCK command: Controls concurrent access to shared data resources
- Lock types: Exclusive (default), Shared (#"S"), Escalating (#"E")
- Lock granularity: Locks apply to nodes and implicitly affect parent/child relationships
- Lock modes: Incremental locks, timeout options, automatic unlock
- Lock table: System-wide tracking of all locks and waiting requests
Detailed Notes
InterSystems IRIS provides sophisticated locking mechanisms through the LOCK command to control concurrent access to data resources. The fundamental concept is that a lock is a named resource (typically matching a global variable name) that processes must acquire before modifying the corresponding data. Locks follow the same naming conventions as variables and can be local (process-specific) or global (cross-process). When you lock a specific node like ^student(1,2), you have an explicit lock only on that node, but other processes are implicitly barred from locking parent nodes (^student(1)) or child nodes (^student(1,2,3)). This hierarchical lock behavior prevents conflicts across the subscript tree.
The system supports three primary lock types: exclusive (default), shared (#"S"), and escalating (#"E"). Exclusive locks limit a resource to one process, while shared locks allow multiple processes to hold non-conflicting locks simultaneously. An existing shared lock prevents other processes from applying an exclusive lock, and vice versa. However, a process can upgrade its own shared lock to exclusive. Escalating locks provide automatic lock escalation when the number of locks at a subscript level reaches the threshold (default 1000), automatically locking the parent node to prevent lock table overflow.
Lock operations use three indicators: no character (unlock all prior locks then lock), + (incremental lock without unlocking), and - (unlock/decrement). The timeout parameter (specified as :seconds) is critical for preventing process hangs; without a timeout, lock requests wait indefinitely. When successful, $TEST is set to 1; on timeout, $TEST becomes 0. The lock table maintains system-wide information about all locks including exclusive/shared counts, escalating status, delock state, and waiting processes. Locks are automatically released when a process terminates, and within transactions, unlocks may be deferred until commit/rollback.
Documentation References
2. Applies best practices for lock usage
Key Points
- Always use timeouts: Prevents indefinite process hangs (LOCK ^data:10)
- Minimize lock duration: Acquire locks late, release early to reduce contention
- Avoid deadlocks: Never upgrade shared to exclusive with +; use consistent lock ordering
- Use subscripted locks: LOCK ^student(1) performs better than LOCK ^student
- Monitor lock table: Use Management Portal or ^LOCKTAB to diagnose contention
Detailed Notes
Best practices for lock usage center on preventing deadlocks, minimizing contention, and maintaining system responsiveness. The most critical practice is always specifying timeout values when acquiring locks. Without timeouts, a LOCK command waits indefinitely if another process holds a conflicting lock, potentially hanging the process. A timeout of 0 makes one attempt and immediately returns, while positive values wait the specified number of seconds. After issuing a lock with timeout, always check $TEST to verify success before proceeding with critical operations.
Deadlock prevention requires understanding the dangerous pattern: requesting an exclusive lock when holding a shared lock. If Process A holds a shared lock on ^data(1) and Process B also holds a shared lock on ^data(1), and both then request exclusive locks with LOCK +^data(1), neither can proceed - each waits for the other to release its shared lock. The solution is to use LOCK ^data(1) (without the plus sign) which first releases the shared lock before requesting the exclusive lock. Consistent lock ordering across all processes also prevents deadlocks: if all processes lock resources in the same sequence (e.g., always lock ^indexA before ^indexB), circular wait conditions cannot occur.
Performance best practices include using subscripted lock names whenever possible (^student(1) rather than ^student) and minimizing lock duration by acquiring locks as late as possible and releasing them as soon as safe. Lock granularity matters: locking at the appropriate level balances concurrency (fine-grained locks allow more parallel operations) against overhead (too many locks consume lock table space). Use escalating locks (#"E") when applying many locks at the same subscript level to prevent lock table overflow. Within transactions, understand that unlocks of non-incremented locks are deferred until transaction commit unless you specify immediate unlock (#"I"). Regular monitoring of the lock table through Management Portal (System Operation > Locks > View Locks) or the ^LOCKTAB utility helps identify contention bottlenecks and deadlock situations before they impact production systems.
Documentation References
3. Selects between row-level and table-level locks appropriately
Key Points
- Row-level locks: SQL default; lock individual records for high concurrency
- Table-level locks: Lock escalation at threshold (default 1000 locks)
- Lock threshold: Configurable via $SYSTEM.SQL.Util.SetOption("LockThreshold")
- Transaction locks: Automatic locking for INSERT, UPDATE, DELETE operations
- Lock escalation: Prevents lock table overflow by escalating to parent node
Detailed Notes
InterSystems IRIS automatically manages the choice between row-level and table-level locks during SQL operations, with the ability to configure the escalation threshold based on application requirements. Row-level locking is the default for SQL DML operations (INSERT, UPDATE, DELETE) within transactions. Each row modification acquires a lock on the unique data value, allowing high concurrency as different processes can simultaneously modify different rows in the same table. These locks are held for the duration of the transaction to prevent conflicts and ensure data integrity. For example, if a process deletes a unique data value, this value remains locked until the transaction completes, preventing another process from inserting a record with the same value (which could cause a duplicate after rollback).
Lock escalation occurs automatically when the number of row-level locks for a single table exceeds the lock threshold. The default threshold is 1000 locks per table. When the 1001st lock is requested, the system escalates to a table-level lock, releasing all individual row locks and acquiring a single lock on the parent node. This table lock persists for the duration of the transaction, and subsequent row operations increment the table lock count rather than creating new row locks. Lock escalation prevents lock table overflow but reduces concurrency since the entire table is locked rather than individual rows. The lock threshold is configurable using $SYSTEM.SQL.Util.SetOption("LockThreshold",value) which changes both the current system-wide value and the configuration file setting. You can query the current threshold with $SYSTEM.SQL.Util.GetOption("LockThreshold").
Choosing the appropriate lock granularity depends on transaction patterns. Applications with short transactions modifying few rows benefit from row-level locks maximizing concurrency. Batch operations modifying large numbers of rows might benefit from a lower lock threshold or even explicit table-level locks to reduce lock management overhead. However, lowering the threshold too much increases the chance of lock escalation, which blocks other processes from accessing any rows in the table. In ObjectScript, you can use escalating locks explicitly with the #"E" lock type: LOCK +^data(1,1)#"E",+^data(1,2)#"E",... When the threshold is reached, these automatically escalate to ^data(1). Understanding the escalation mechanism allows developers to design applications that balance concurrency needs against lock table size constraints. The %NOLOCK restriction argument in SQL statements bypasses automatic locking when you have external lock management or read-only operations.
Documentation References
4. Uses Work Queue Manager for parallel processing
Key Points
- Purpose: Distributes independent work units across a pool of worker jobs for parallel execution
- Main class: `%SYSTEM.WorkMgr` — accessed via `$SYSTEM.WorkMgr`
- Core workflow: `Initialize()` → repeated `Queue()` → `Sync()` or `WaitForComplete()`
- Categories: DEFAULT, SQL, and custom categories control which worker pool handles the work
- Callbacks: Setup/Teardown callbacks run once per worker (not per work item) for connection setup, etc.
- Not for interactive requests: Designed for CPU-bound or long I/O-bound batch work where parallelism outweighs job-creation overhead
Detailed Notes
The Work Queue Manager is InterSystems IRIS's built-in API for parallelizing independent work across multiple background worker jobs. It is fundamentally different from the LOCK-based concurrency control covered earlier in this topic: locks coordinate access to shared data among competing processes, while the Work Queue Manager actively distributes work to achieve parallelism. The primary use cases are CPU-bound batch processing (complex calculations, bulk imports, data transformations), parallelizable I/O (report generation, external API fan-out), and any workload that can be decomposed into independent units with no shared mutable state. Because each work unit runs in a separate worker job, the work must be thread-safe and independent — shared variables from the caller are NOT available inside the worker unless explicitly passed as arguments. The API is accessed through $SYSTEM.WorkMgr, which returns an instance of %SYSTEM.WorkMgr.
The basic workflow has four steps. First, call Set queue = $SYSTEM.WorkMgr.Initialize(,.sc) to obtain a work queue handle — the returned status sc indicates success. Second, repeatedly call queue.Queue("classname.methodname", arg1, arg2, ...) to submit work items. The first argument names an entry point (a ClassMethod or a routine tag like TAG^ROUTINE), followed by up to eight positional arguments. Each Queue() call adds one unit of work; worker jobs pick up items as they become available. Third, call queue.Sync() to block until all queued work is complete, or queue.WaitForComplete(timeout) for the same behavior with a timeout. The WaitOne(timeout) method returns as soon as any single item finishes, which is useful for streaming-result patterns. Finally, errors from individual work items are gathered into queue.%OnGetResultInfo() or can be checked via the return status of Sync(). If any work item returns an error, Sync() returns the first error status.
Categories control which pool of workers processes a given queue. The DEFAULT category is used for general-purpose work; the SQL category is reserved for the parallel query executor; and administrators can define custom categories via $SYSTEM.WorkMgr.Setup() or the Management Portal to isolate workloads (for example, a category with a small worker pool for low-priority batch jobs). When initializing a queue you can pass the category as the first argument: $SYSTEM.WorkMgr.Initialize("CATEGORY_NAME", .sc). The number of workers per category is configurable and defaults to one per CPU core. Setup and Teardown callbacks give each worker a chance to initialize state once before processing work items and to clean up after the last item — they do NOT run per work item, which is essential for performance when each unit shares expensive initialization like opening a database connection. Register them on the queue with queue.Setup(...) and queue.Teardown(...) before queuing work. Finally, long-running queues can be paused and resumed via queue.Pause()/queue.Resume(), and a queue can be detached from the current process (queue.Detach()) and reattached later from another process ($SYSTEM.WorkMgr.Attach(token)) — useful for survivability across process restarts. Best practices: make each work unit fully self-contained with no shared mutable state; pass all needed data as arguments to Queue(); always check the status returned by Sync(); keep unit work size large enough to amortize the per-job overhead (worker job startup is non-trivial); avoid Work Queue Manager for sub-second interactive requests.
Exam Preparation Summary
Critical Concepts to Master:
- Lock Fundamentals: LOCK command syntax, lock types (exclusive/shared/escalating), timeout usage
- Hierarchical Locking: Understand parent/child node lock relationships and implicit locking
- Deadlock Prevention: Never upgrade shared to exclusive with +; use consistent lock ordering
- Lock Table Management: View locks via Management Portal and ^LOCKTAB utility
- Row vs Table Locks: Understand automatic escalation at threshold and when to use each
- Transaction Behavior: Locks deferred in transactions, incremental locking, automatic release
Common Exam Scenarios:
- Identifying deadlock situations and how to prevent them
- Choosing appropriate lock granularity for different use cases
- Understanding when locks escalate from row-level to table-level
- Debugging lock contention using lock table monitoring tools
- Determining proper timeout values and error handling strategies
- Recognizing lock type indicators (#"S", #"E", #"I", #"D") and their effects
Hands-On Practice Recommendations:
- Practice LOCK command with various timeout scenarios and check $TEST
- Create intentional deadlock situations and resolve them
- Use Management Portal to monitor locks during concurrent operations
- Experiment with lock escalation by setting low LockThreshold values
- Test shared vs exclusive locks with multiple Terminal sessions
- Practice incremental locking and observe lock counts in lock table
- Implement transaction-based locking with deferred unlock behavior
- Use ^$LOCK to programmatically query lock information
Key Syntax to Memorize:
Important Lock Table Monitoring:
- Management Portal: System Operation > Locks > View Locks
- ^LOCKTAB utility: DO ^LOCKTAB (from %SYS namespace)
- Lock states: Exclusive, Shared, WaitExclusiveExact, WaitExclusiveParent, WaitExclusiveChild
- ModeCount values: Exclusive/n, Shared/n, Exclusive_e, Shared_e, ->Delock
Transaction and Lock Interaction:
- Locks within transactions are held until COMMIT or ROLLBACK
- Default unlock behavior in transaction: defers release until transaction end
- Use #"I" for immediate unlock within transaction
- Lock escalation threshold applies to locks per table per transaction
- Automatic locks for INSERT, UPDATE, DELETE unless %NOLOCK specified