No es suficiente tener una transacción serializable, necesita insinuar el bloqueo para que esto funcione.
El nivel de aislamiento serializable seguirá adquiriendo el tipo de bloqueo "más débil" posible, lo que garantiza que se cumplan las condiciones serializables (lecturas repetibles, sin filas fantasma, etc.)
Por lo tanto, está tomando un bloqueo compartido en su tabla que luego (en su transacción serializable) intenta actualizar a un bloqueo de actualización. La actualización fallará si otro subproceso tiene el bloqueo compartido (funcionará si nadie más tiene un bloqueo compartido).
Probablemente desee cambiarlo a lo siguiente:
SELECT * FROM SessionTest with (updlock) WHERE SessionId = @SessionId
Eso asegurará que se adquiera un bloqueo de actualización cuando se realice SELECCIONAR (por lo que no necesitará actualizar el bloqueo).