inacreditÁvel

Galerinha,

Estou aqui quase saindo do trabalho, há 1 dia da entrega do sistema para o cliente, e a aplicação para de funcionar completamente. :shock:

Como perdigueiro sai desesperadamente atrás do problema, quando me deparo com a seguinte situação:

Tenho uma maldita tabela no BD rodando em ORACLE onde existe uma chave primáira composta, acontece que um dos campos dessa chave é sequêncial. EX.

Chave Composta
campo1 campo2
10 1
10 2
10 3
11 1
11 2
11 3

Essa tabela é a principal tabela do sistema.

Acontece que para o sequênciamento estava sendo feito um SELECT MAX no campo e somando 1 :evil:

Agora imaginem, 3000 acessos silmultâneos fazendo este select + 1, e retornado o mesmo valor, visto que isso não garante um lock na tabela.

Oque ocorre, aplicação condenada :roll:

Surgiram algumas idéias, mas nada que resultasse numa solução viável ao momento, então resolvi recorrer a esse poço de talentos para que alguma alma me de alguma sugestão ou pelo um ombro amigo pra chorar :frowning:

E para os que pensaram em trigger, e storeProcedures, definitivamente fora de questão por exigências do cliente. :roll:

O sistema incrivelmente já está em produção na sua versão 1(não imagino como) e este é uma versão dois deste mesmo sistema(meu pai eterno), portanto, sem mudanças radicais ou vai mais um boooom tempo de refactoring.

idéias? :idea: :idea: :idea:

Mudar o segundo valor pra ser sempre sequencial, independente do primeiro? Algo como:

10 1
10 2
10 3
11 4
11 5
11 6

select max? Isso é coisa de quem nunca viu “SEQUENCEs” na vida e resolveu usar um banco de dados Oracle. Aproveitando o que o Mister__M lhe disse, daria para alimentar o segundo campo com uma sequence, ou então usar uma outra sequence para gerar um número sequencial, e calcular o primeiro e o segundo campo a partir desse número gerado?

[quote=mister__m]Mudar o segundo valor pra ser sempre sequencial, independente do primeiro? Algo como:

10 1 10 2 10 3 11 4 11 5 11 6 [/quote]

solução boa, mas acontece que são Pai e Filhos, nem semrpe vai ter essa sequência, por ex.

se eu pegar o 10 2, no seu exemplo ai, eu teria que gerar 10 7, 10 8, enfim alguns registros e não somente 1.

Outra coisa, é que esse registro só é gravado nesta tabela após ser feitas outras 3 operações no banco, e um comit no final de tudo isso.

Acontece que se der um rollback, é pau na coisa, se mais de um usuário acessar ao mesmo tempo, é pau na app, não posso dar lock na tabela, pois isso impediria os outros 2999 usuários de fazer operações nela :slight_smile:

Uma sugestão foi ter uma segunda tabela auxiliar onde se repetissem estes dois valores e esta sim fosse lockada, mas mas isso implicaria em mudanças estruturais e boas partes da aplicação :frowning:

Sem contar que a app, é totalmente estruturada, oo passo longe disso aqui, e não foi me permitido refazer tudo ou refatorar.

Acontee que a SEQUENCE não funciona em chaves compostas :frowning:

Tentamos simular um sequence, com uma tabela auxiliar, e usando um “for update” após o select, mas isso não ficou nada viável :frowning:

Pelo que estou imaginando, você poderia tentar fazer uma solução mista - obter o primeiro campo com uma SEQUENCE, e o segundo com SELECT MAX. Qual seria o problema nesse caso?

[quote=thingol]Pelo que estou imaginando, você poderia tentar fazer uma solução mista - obter o primeiro campo com uma SEQUENCE, e o segundo com SELECT MAX. Qual seria o problema nesse caso?
[/quote]

O primeiro campo é imutável a cada gerão de “Filhos”, o que muda seria o segundo campo que é no caso o sequencial.

O problema do MAX, é que ele não me garante um valor exclusivo, e se mais uma pessoa tentar usar este mesmo valor, o ORACLE trava geral, e esse valor em questão fia inutilizável enquanto o oracle não comitar as operações pendentes, só que se alguém vai lá e faz MAX novamente me retorna o mesmo valor, porque não foi inserido nada lá entende? a da sinuca de bico, porque o MAX sempre me trás o cara que não ta comitado e inutilizável, deixando travado banco e aplicação.

Eu nunca disse que ia ficar “bonitinho” assim :slight_smile:

Mas, por que não pode ser 10 1, 11 2, 10 3? Pelo que eu entendi, o importante é não coincidir e manter a ordem, não?

Como é que vocês fazem, quando ocorre algo deste tipo, uma chave composta que um dos campos é sequencial?

Criam um novo campo que distinguirá cada registro tornado este chave primária, passando a composta para chave única e continuam fazendo as buscas normais por esta chave única?

Como é feito num banco onde exitem grandes massas de dados?

Deve ter um modo, isso é rotineiro de se acontecer, ou não?

Explique seu caso de uso para isso :slight_smile:

Normalmente isso é coisa de usuário que acha “bonito” ser assim. Por que 10 1, 11 2, 10 3 não atende?

Hmm, você não poderia travar os registros primeiro.

Isso vai te garantir o sequênciamento correto e paralelismo entre seqüências diferentes.
O problema é quando inserir o primeiro, para isso você pode travar um tabela secundária.

Fora isso, recomento pegarem algum expert oracle/j2ee para ajudar nessa situação.

v se isso ajuda…


:oops:

fôia

Inacreditavel nesse projeto são 2 coisas.

:arrow: A porta que usou select max/count para gerar chave primaria, isso é uma das primeiras coisas que você aprende que não escala para mais de UM usuario simultâneo.

:arrow: Um projeto que vai ter toda essa carga só receber testes de stress um dia antes.

Sinceramente? Sejam honestos com o cliente ou criem diversão suficiênte para corrigir o problema por baixo dos panos.

[quote=mister__m]Eu nunca disse que ia ficar “bonitinho” assim :slight_smile:

Mas, por que não pode ser 10 1, 11 2, 10 3? Pelo que eu entendi, o importante é não coincidir e manter a ordem, não?[/quote]

pode ser assim sim mister.

Acontece o seguinte, para que eu possa inserir nas outras tabelas e nessa eu só preciso ter o próximo número que estará no segundo campo, sem usar MAX.

± isso: select numero maior do campo2 onde campo = algum valor

Se for assim, usa uma sequence! :slight_smile:

[quote=louds]Hmm, você não poderia travar os registros primeiro.

Isso vai te garantir o sequênciamento correto e paralelismo entre seqüências diferentes.
O problema é quando inserir o primeiro, para isso você pode travar um tabela secundária.

Fora isso, recomento pegarem algum expert oracle/j2ee para ajudar nessa situação.

[/quote]

Surgiu esta idéia aqui e até agora é a melhor, más só posso travar uma tabela auxiliar mesmo, a tabela em questão nunca, ela se relaciona com todo mundo o que me travaria o resto todo :slight_smile:

Um dúvida quanto a esta solução, é que eu tenho 3 operações no banco antes de comitar tudo, e a tabela ficaria lockada nesse período, o que poderia me acarretar um certo transtorno, concorda?

[quote=louds]Inacreditavel nesse projeto são 2 coisas.

:arrow: A porta que usou select max/count para gerar chave primaria, isso é uma das primeiras coisas que você aprende que não escala para mais de UM usuario simultâneo.

:arrow: Um projeto que vai ter toda essa carga só receber testes de stress um dia antes.

Sinceramente? Sejam honestos com o cliente ou criem diversão suficiênte para corrigir o problema por baixo dos panos.[/quote]

tu acha que coloquei o inacreditável ai por que? hehe
Tu não mensionou o fato de o sistema estar na versão 2, como a 1 está rodando? hehehe

Acontece também que os testes aqui não bem “testes” hehe
não existe programas para teste de stress, o teste é feito visualmente(essa é pra acabar), só começaram a testar as consultas dos desenvolvedores(é não temos um DBA, pasmem novamente) por que insisti muito neste sentido.

Mas quem acabou encontrando esse problemão foi o sortudo aqui ehehe. E isso aconteceu justamente no último caso de uso, que é o mais importante(outro erro).

E nem irei mencionar que isso está e foi utilizado em mais uns 5 sistemas aqui dos quais não participei, ou seja, estado de alerta geral com luz vermelha girando em cima kkkkk

Enfim, usando uma frase de uma música que não me lembro de quem é, “É pra rir ou pra chorar?”

Se este for o problema, faz isso num contexto transacional separado do primeiro. Deve resolver.

mister, mas o sequence não funcionará para o segundo campo, somente funciona para um campo só e quando este for a chave primária da tabela, ou to falando merda? :roll:

Pensei o segunte,

isolo o select max com for update num método, neste método eu abro uma conexão nova sem interferir na conexão que já está funcionando, e nesta conexão nova, eu já faço um insert atualizando o valor da tabela auxiliar, desta maneira, eu faria rapidamente um selec/insert/comit, isso já desalocaria a tabela para outros usuários, que já poderia pegar um valor novo, e assim não repetiria, e evitaria ao máximo o paralelismo.

será que é boa ou má solução?