Handling concurrency issues

By default, the database server runs UDRs concurrently. A UDR that uses data it shares with other UDRs or with multiple instances of the same routine must implement concurrency control on its data.

When the named-memory functions mi_named_alloc(), mi_named_zalloc(), and mi_named_get() return the address of a named-memory block, they do not request a lock on this named memory. It is the responsibility of your UDRs or DataBlade® to manage concurrency issues on the named-memory block.

The greater the memory duration that is associated with the named memory, the more likely that you must manage concurrency of that memory. If the named memory is never updated (it is read-only), there are no concurrency problems. However, if any UDR updates the named memory that has a duration of PER_COMMAND or greater, there are concurrency issues just like there are for any global variable that gets updated.

For example, suppose the function myfunc() allocates a named-memory block named named_mem1. The memory duration of named_mem1 determines possible concurrency issues when myfunc() is called in the following query:
SELECT * FROM my_table
   WHERE myfunc(column1) = 1
   OR myfunc(column2) = 2

The following table shows possible concurrency issues of named_mem1.

Named-memory allocation Concurrency issues?
mi_named_alloc (2048, "named_mem1", PER_COMMAND, nmem1_ptr) Yes

Each invocation of myfunc() in the query gets it own private instance of named_mem1, which expires when the UDR completes, but there might be multiple threads running in a subquery that share the same PER_COMMAND pool.

If PER_COMMAND memory is cached in the MI_FPARAM user data, however, there are no concurrency issues because each thread has its own MI_FPARAM structure. Unless you need memory to be shared between threads, this is the preferable alternative for PER_COMMAND.

mi_named_alloc (2048, "named_mem1", PER_SESSION, nmem1_ptr) Yes

Each invocation of myfunc() in the same query accesses the same named_mem1. This memory does not get deallocated until the session closes.

mi_named_alloc(2048, "named_mem1", PER_SYSTEM, nmem1_ptr) Yes

Every invocation of myfunc() in every SQL statement accesses the same named_mem1. This memory does not get deallocated until the database server shuts down.

To handle concurrency problems, use the following DataBlade API memory-locking functions in UDRs that update named memory.
Memory-locking task DataBlade API memory-locking function
Request a lock on the specified named-memory block and wait for the lock to be obtained. mi_lock_memory()
Request a lock on the specified named-memory block and do not wait for the lock to be obtained. mi_try_lock_memory()
Unlock the specified named-memory block. mi_unlock_memory()
The following guidelines are recommended for handling concurrency problems:
  • The safest approach, even for threads that only read named memory, is to lock the named-memory block.

    After the named-memory block is locked, you can guarantee that all accesses will obtain a consistent read.

  • Keep the time that you lock a named-memory block as short as possible.
    The DataBlade API locking interface is intended to be used in a tightly coupled, fast section of code that protects a critical region during modification. Code must follow these steps:
    • Lock the named memory with mi_lock_memory() or mi_try_lock_memory().
    • Perform the modification or consistent read.
    • Immediately unlock the memory with mi_unlock_memory().
  • If you need to hold locks for a long time, do it with SQL in a client application.
Tip: Locking of named memory (with mi_lock_memory() and mi_try_lock_memory()) uses its own locking mechanism to keep track of named-memory locks. It does not consume database locks.
Suppose you have a user-defined structure named MyInfo with the following declaration:
typedef struct
{
   mi_integer is_initialized;
   ... other members here....
} MyInfo;