Problema de simultaneidade: solução criativa explicada

Apr 03 2023
Na semana passada, notei um bug oculto em nosso código legado: quando os usuários tentavam atualizar o mesmo recurso ao mesmo tempo, tecnicamente falando, alguns milissegundos antes ou depois, todos conseguiam operar sem nenhum erro ou exceção. Isso foi uma merda! A simultaneidade era o caso Quando vários usuários enviavam solicitações uma após uma, o método assíncrono que estava lidando com chamadas e trabalhando no recurso, estava atualizando mais de uma vez o mesmo recurso enquanto um processo já havia iniciado antes (aqui o processo é o primeiro chamador do usuário requisição) e não finalizada até o momento da requisição do próximo usuário.

Na semana passada, notei um bug oculto em nosso código legado: quando os usuários tentavam atualizar o mesmo recurso ao mesmo tempo, tecnicamente falando, alguns milissegundos antes ou depois, todos conseguiam operar sem nenhum erro ou exceção. Isso foi uma merda!

A simultaneidade foi o caso

Quando vários usuários enviaram solicitações uma após uma, o método assíncrono que estava lidando com chamadas e trabalhando no recurso, estava atualizando mais de uma vez o mesmo recurso enquanto um processo já havia iniciado antes (aqui o processo é a solicitação do primeiro usuário chamador) e não terminou até o momento da solicitação do próximo usuário.

Na verdade, deveria ter reagido como uma transação .

Você já ouviu falar do Redis?

O Redis é um armazenamento de estrutura de dados na memória de código aberto (licenciado pelo BSD) usado como banco de dados, cache, agente de mensagens e mecanismo de streaming. Ele suporta tipos de dados primários, até mesmo objetos e assim por diante. Usei seu armazenamento de chaves para resolver o problema de simultaneidade.

Como resolver o dilema de simultaneidade com o Redis?

E se o recurso tiver algo idêntico, como chave primária (id) que possa diferenciá-lo? Essa foi uma época em que a luz começou a piscar em meu cérebro.

A solução é bastante fácil:

Adicionei chaves primárias de fonte compartilhada a uma matriz que foi armazenada no Redis. Quando um processo terminava em um recurso, a chave primária era excluída do array e armazenada novamente no Redis. Se um usuário tentasse atualizar o recurso que já estava sendo processado por outro usuário, o código verificaria os elementos do array: se houvesse uma chave primária igual à próxima solicitação, lançaria o erro 409 (conflito).

Que tal travar?

O bloqueio é um procedimento orientado à CPU que impede o trabalho em recursos compartilhados. Se o número do usuário no mesmo recurso for menor que 20, o tempo de espera deve ser aceitável. Mas e 1000?

Claro, você não quer que seus usuários esperem por horas. Aqui o Redis pode te dar uma mãozinha.

Conclusão

Assim, usar a tecnologia in-memory, chamada Redis, é uma maneira mais eficaz do que bloquear recursos com Semaphore ou Mutex. Como desenvolvedor e tomador de decisões, você deve escolher um que se encaixe no seu dilema .

Espero que ajude os leitores!