Não é suficiente ter uma transação serializável, você precisa sugerir o bloqueio para que isso funcione.
O nível de isolamento serializável ainda geralmente adquirirá o tipo de bloqueio "mais fraco" possível, o que garante que as condições serializáveis sejam atendidas (leituras repetíveis, sem linhas fantasmas, etc.)
Então, você está pegando um bloqueio compartilhado em sua tabela que você está mais tarde (em sua transação serializável) tentando atualizar para um bloqueio de atualização. A atualização falhará se outro thread estiver mantendo o bloqueio compartilhado (funcionará se nenhum outro corpo estiver mantendo um bloqueio compartilhado).
Você provavelmente deseja alterá-lo para o seguinte:
SELECT * FROM SessionTest with (updlock) WHERE SessionId = @SessionId
Isso garantirá que um bloqueio de atualização seja adquirido quando o SELECT for executado (portanto, você não precisará atualizar o bloqueio).