ERROR CONNECTING: TIMEOUT EXPIRED

ALVAROVB2009 29/02/2024 09:46:52
#503133
Alterado em 29/02/2024 10:30:20 Bom dia pessoal, estou tratando um arquivo com mais de 9 milhões de registros e depois de processar mais de 7 milhões, começou a dar o erro de conexão ao banco de dados

Em um Módulo faço a conexão do banco, que é onde começou a dar o problema ao abrir a conexão
MySql.Data.MySqlClient.MySqlException: 'error connecting: Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.'

public static MySqlConnection ConectaBanco()
{
Conexao = new MySqlConnection($"server =localhost;user id=root;database=meubanco;password=123456; Allow Zero Datetime = True");
Conexao.Open();
return Conexao;
}



E aqui em outro módulo onde acesso a conexão com o banco de dados, abro a conexão, consulto / incluo o que precisa e fecho a conexão

public static DataTable MostraDados(string Sql = "")
{
DataTable Dt = new DataTable();
var Cmd = ConectaBanco().CreateCommand();
Cmd.CommandText = Sql;
Da = new MySqlDataAdapter(Cmd.CommandText, ConectaBanco());
Da.Fill(Dt);
Conexao.Close();
return Dt;
}

public static int Gravar(string Sql = "", string NomeTabela = "")
{
int CodGerado = 0;
var Cmd = ConectaBanco().CreateCommand();
Cmd.CommandText = Sql;
Cmd.ExecuteNonQuery();
Conexao.Close();
}



Estava buscando algumas soluções e encontrei que colocando Pooling=false , na string de conexão poderia resolver o problema, más com esse comando me da o erro
MySql.Data.MySqlClient.MySqlException: 'Too many connections'

Não tem tratamento desse sistema na memória e não tem como eu recomeçar o tratamento, pois foram mais de 9 horas de tratamento , estou tentando resolver esse problema ainda com o projeto aberto
Eu poderia até recomeçar o tratamento, desde que não tivesse o risco de acontecer o mesmo problema


O que consegui foi colocar um Application.DoEvents(); em alguns pontos e a importação rodou bem por um tempo

Más lembrando que todo o problema começou após ter mais de 7 milhões de registros tratados


Agradeço a todos pela ajuda
ALVAROVB2009 29/02/2024 09:54:41
#503134
Esqueci de colocar , uma outra forma de abrir a conexão com o banco que usei também e não deu certo
using(MySqlConnection Conexao = new MySqlConnection($"server =localhost;user id=root;database=meubanco;password=123456; Allow Zero Datetime = True"))

Deu a mesma mensagem de erro
WEBMASTER 29/02/2024 10:33:50
#503135
Bom, tem como você configurar o maximo de conexões mas sinceramente não acho que vai resolver...
Que tal fazer isso (conecta/desconecta) a cada X linhas ?
Eu tive esse tipo de situação uns bons anos atrás, a solução foi meio que criar um valor de alterações que faria e depois submeter todas.
Assim, pode ser chover no molhado, e sei que dá mais trabalho...mas sinceramente tenta fazer DIRETO no banco via stored procedure lendo o arquivo para uma tabela temporária, fatiando e lendo, pois assim é ele com ele mesmo.
Sempre que precisei (depois que aprendi isso) nunca mais usei intermediários, leio e gravo o que preciso diretamente via banco (mais saudável, garantido e performático).
KERPLUNK 29/02/2024 10:41:05
#503136
Verifique seu código. É provável que haja conexões não sendo fechadas corretamente, daí elas ficam penduradas e com o tempo isso que você está tendo pode acontecer. Essa é uma das causas de uso dos blocos "using".
OCELOT 29/02/2024 10:50:39
#503137
public static DataTable MostraDados(string Sql = "")
{
DataTable Dt = new DataTable();
var Cmd = ConectaBanco().CreateCommand();
Cmd.CommandText = Sql;
Da = new MySqlDataAdapter(Cmd.CommandText, ConectaBanco());
Da.Fill(Dt);
Conexao.Close();
return Dt;
}

Nesse código seu você abre duas conexões e só fecha uma delas

A primeira você abre na linha "var Cmd = ConectaBanco().CreateCommand()"
A segunda na linha Da = new MySqlDataAdapter(Cmd.CommandText, ConectaBanco());

Cada vez que você chama o ConectaBanco() ele abre uma nova conexão e aparentemente guarda ela na variável Conexao que parece ser uma variável global, eu diria pra você se livrar dessa variável, ela facilita muito de ter problemas, eu diria pra você simplesmente retornar a nova conexão no seu método ConectaBanco(), algo do tipo

public static MySqlConnection ConectaBanco()
{
var cnn = new MySqlConnection($"server =localhost;user id=root;database=meubanco;password=123456; Allow Zero Datetime = True");
cnn.Open();
return cnn;
}


E você precisa garantir que qualquer lugar que use ele feche todas as conexões que abrir, idealmente abra apenas uma conexão de cada vez se for possível

Então mudaria o código do MostraDados para usar a conexão assim:
public static DataTable MostraDados(string Sql = "")
{
using (var cnn = ConectaBanco())
{
DataTable Dt = new DataTable();
Da = new MySqlDataAdapter(sql, cnn);
Da.Fill(Dt);
cnn.Close();
}
return Dt;
}


Outro erro
 var Cmd = ConectaBanco().CreateCommand();
Cmd.CommandText = Sql;
Da = new MySqlDataAdapter(Cmd.CommandText, ConectaBanco());

Aqui você cria um Command mas não usa ele, você simplesmente coloca a SQL no CommandText dele e depois pega de volta esse valor pra passar pro DataAdapter, é o mesmo que passar direto o valor da variável Sql
KERPLUNK 29/02/2024 11:32:30
#503138
Concordo com o OCELOT e digo mais:
Objetos de banco, MySqlConnection, DataTable, MySqlDataAdapter, comando... todos eles são "disposable". Portanto além de fechar a conexão, o objeto dela precisa ser destruído para ser coletado pelo GAC(Garbage collector). Caso isso não seja feito, consiste em um memory leak e vai "comendo" a memória RAM da máquina.
Mais uma razão para usar blocos using e mais uma razão para usar OOP.
ALVARO2009, não fique bravo com minha crítica, mas um código assim, não passaria por review nem mesmo para um dev junior. Recomendo fortemente que reveja seus conhecimentos de desenvolvimento e implemente boas práticas. Isso vai te evitar MUITAS dores de cabeça no futuro. Se quiser, podemos ajudar nessa jornada de aprimoramento. Você vai gastar um tempo aprendendo essas coisas, mas com certeza vai ganhar muito tempo no futuro. Na prática, gasta umas horas agora, para ganhar várias semanas e até meses em tempo no futuro. Fora que vai abrir MUITO os horizontes pra você.
KERPLUNK 29/02/2024 11:38:02
#503139
Quantas tabelas tem no seu banco? Pode colocar a estrutura dele aqui? Você pode rodar essa query para mostrar isso:

SELECT *
FROM INFORMATION_SCHEMA.COLUMNS
ALVAROVB2009 29/02/2024 12:03:49
#503140
Agradeço o retorno de todos

Uma coisa que eu não faço, é colocar uma questão aqui e ficar esperando a ajuda, eu procuro ir atrás das soluções e tentar resolver(mesmo porque como ainda tenho pouca experiência com a linguagem, preciso aprender muita coisa) então as vezes acontece de eu conseguir resolver, antes de ter a resposta de vcs ou mesmo ver o retorno, que foi esse o caso que acabou acontecendo.
Eu vim aqui para encerrar o tópico e colocar o erro que acabei achando, para que futuramente caso alguém tenha o mesmo problema, possa achar uma possível solução

Más fiquei feliz por ter encontrado o problema e a solução que achei foi proposta por vcs ( e bem melhorada diga-se de passagem ) e tem uma coisa que não tinha pensado, que foi a solução do Webmaster


Webmaster - vou procurar ver como que faz essa leitura e jogar diretamente no banco de dados o arquivo, acredito que possa ficar mais rápido, porque o que estou fazendo é abrindo ele com o StreamReader e lendo linha a linha, e importando apenas o que me interessa
Será que vc poderia me passar o esquema de como fazer esse tratamento diretamente no banco de dados ?
Se vc puder me mandar no meu email ( alvaro.bernardi@bol.com.br) , fico muito agradecido, más de qqer forma irei atrás dessa solução


Kerplunk - Estive mesmo olhando o código e vendo se tinha alguma coisa que deixei passar, e acabei vendo que tinha sim, a conexão estava sendo aberta 2x, foi o problema que o Ocelot passou

Ocelot - Analisando linha a linha com o F11 , achei exatamente o problema que vc colocou, abri 2x o banco no meu mostradados, deixando ele assim
DataTable Dt = new DataTable();
Da = new MySqlDataAdapter( Sql, ConectaBanco());
Da.Fill(Dt);
Conexao.Close();
return Dt;

Porém não tinha me atendado para o tratamento para a variável Global que eu tinha feito, e estou usando a sua solução que vc passou, más primeiramente entendi perfeitamente o tratamento que vc deu, e tirei até mesmo os Application.DoEvents() , deixando a importação mais rápida, mesmo que trave de momento a aplicação, como ela é feita nesse momento só para a importação não tem problema

Mais uma vez agradeço a todos, por enqto deixarei aberto para poder receber qqer comentário
KERPLUNK 29/02/2024 12:22:57
#503141
Alterado em 29/02/2024 12:24:08 É que o problema na minha forma de ver, não é só o código, mas sim de entendimento do funcionamento da plataforma .NET. Mesmo que você tenha resolvido o problema momentaneamente, seu código está bem aquém de bom, em relação à design. Se você continuar desenvolvendo dessa maneira, com certeza vai ter mais problemas como esse e até piores no futuro.
Um dos problemas que vejo, é a falta de entendimento da herança. Por exemplo, se você observa a documentação da classe MySqlDataAdapter que você usa. Na sessão "implements", você vai observar que tem a interface IDisposable. Isso significa que instâncias dessa classe(sempre que você usar ela em código), você precisa ou chamar o método Dispose() assim que terminar de usar a instância do objeto ou colocar o código em que você usa essa instância dentro de um bloco using, mais ou menos assim:
Esse código, poderia ser um método:

Da = new MySqlDataAdapter( Sql, ConectaBanco());
Da.Fill(Dt);
Conexao.Close();
return Dt;


O método ficaria mais ou menos assim:

public DataTable GetDataTableFromQuery(string connectionString, string query)
{
DataTable dataTable = new DataTable();
using (MySqlConnection connection = new MySqlConnection(connectionString))
{
using (MySqlDataAdapter adapter = new MySqlDataAdapter(query, connection))
{
connection.Open();
adapter.Fill(dataTable);
}
}
return dataTable;
}


Isso garantiria que tanto a conexão quando o DataAdapter seriam abertos, fechados e dispostos corretamente.
ALVAROVB2009 29/02/2024 15:19:35
#503142

Citação:

Concordo com o OCELOT e digo mais:Objetos de banco, MySqlConnection, DataTable, MySqlDataAdapter, comando... todos eles são "disposable". Portanto além de fechar a conexão, o objeto dela precisa ser destruído para ser coletado pelo GAC(Garbage collector). Caso isso não seja feito, consiste em um memory leak e vai "comendo" a memória RAM da máquina.Mais uma razão para usar blocos using e mais uma razão para usar OOP.ALVARO2009, não fique bravo com minha crítica, mas um código assim, não passaria por review nem mesmo para um dev junior. Recomendo fortemente que reveja seus conhecimentos de desenvolvimento e implemente boas práticas. Isso vai te evitar MUITAS dores de cabeça no futuro. Se quiser, podemos ajudar nessa jornada de aprimoramento. Você vai gastar um tempo aprendendo essas coisas, mas com certeza vai ganhar muito tempo no futuro. Na prática, gasta umas horas agora, para ganhar várias semanas e até meses em tempo no futuro. Fora que vai abrir MUITO os horizontes pra você.


Quando respondi a primeira vez, não vi que tinha um comentário seu, acho que eu estava com a questão aberta e vi somente agora

Quanto a eu ficar bravo, NUNCA, sei que é uma critica construtiva, e isso para mim é importante. Realmente no C# estou engatinhando e esse erro que postei aqui, é uma das dores de cabeça que poderiam ter sido evitadas.
Os cursos que andei fazendo para conhecer a ferramenta, me deram uma base e é nessa base que estou desenvolvendo, ainda não 100%, pois tenho muitos projetos no vb6, más novos projetos estou procurando já iniciar no C#, e com isso quero aprender boas práticas para poder melhorar o meu desenvolvimento




Quanto a sua pergunta
Citação:

Quantas tabelas tem no seu banco? Pode colocar a estrutura dele aqui? Você pode rodar essa query para mostrar isso:SELECT *FROM INFORMATION_SCHEMA.COLUMNS



Nesse projeto tem apenas 10 tabelas, más o script que vc pediu para rodar, pegou todas as bases que tenho, e deu
/* Affected rows: 0 Registros encontrados: 16.105 Avisos: 0 Duração de 1 query: 1,641 sec. (+ 0,062 sec. network) */
KERPLUNK 29/02/2024 15:46:04
#503143
Se são apenas 10 tabelas, ainda mais fácil de implementar um ORM nele. Vai ter um pouco de escrita de código, mas garanto que vai valer à pena.
Crie classes que sejam "correspondentes" à cada tabela. Tipo, a tabela Cliente tem campos "Id integer" e "Nome Varchar", crie uma classe assim:

public class Cliente{
public int Id { get; set; }
public Nome { get; set; }
}


Coloque sempre os tipos de dados correspondentes em cada propriedade. DateTime para Date, string para varchar, int para integer e por aí vai.
Pode criar todas essas classes num arquivo só e de preferência dentro de um mesmo namespace.

Quando tiver isso pronto, me chama que podemos implementar o mini ORM que subi aqui no VBMANIA, a gente faz uma sessão "fora de hora" e te ajudo nisso. Vamos fazer teu código ficar muito bom. :)
Página 1 de 2 [16 registro(s)]
Faça seu login para responder