Comment être sur que les données sont insérés [Résolu]

Signaler
Messages postés
13
Date d'inscription
vendredi 20 janvier 2006
Statut
Membre
Dernière intervention
17 février 2010
-
Messages postés
13
Date d'inscription
vendredi 20 janvier 2006
Statut
Membre
Dernière intervention
17 février 2010
-
Bonjour

Je travaille actuellement sur un petit logiciel permettant d'envoyer via internet des données dans une base mysql.

Toutes les minutes, le client se connecte au serveur, et fait un insert sur la table.
La connexion internet utilisée côtée client est assez capricieuse, c'est de la 3G.

Or j'ai remarqué que régulièrement, le nombre de valeur reçue sur une heure est inférieur à 60, des données se perdraient donc en route...

Au niveau de la connexion à la base et de l'execution de la requete d'insertion, j'ai des try catch qui sont censés me remplir un fichier de log. Mais les données manquantes n'apparaissent pas dans ce fichier log.
J'ai également essayé de tester l'entier en retour de la méthode ExecuteNonQuery, RAS à signaler de ce côté là non plus.

Comment cela peut il s'expliquer ? Et sinon, comment puis je m'assurer que les données ont bel et bien été insérées dans la base (à part en faisant un SELECT juste après) ?

Merci d'avance !

11 réponses

Messages postés
834
Date d'inscription
samedi 15 novembre 2008
Statut
Membre
Dernière intervention
14 janvier 2017
28
Bonsoir,

non pas nécéssairement,
le timeout d'execution du moteur ne tient pas compte du transport.
par exemple pour MS-SQL server c'est 30 secondes, mais seulement entre le moment ou le serveur à reçu la requette et le moment ou il envoie les données.
Mais si la communication entre le serveur et le client en lente, tu peux obtenir un total au-dela de la minute, et cela sans aucune exception.

As-tu mis en place des traces detaillées ? Notament avec la valeur de la requette ?

C# is amazing, enjoy it!
Messages postés
834
Date d'inscription
samedi 15 novembre 2008
Statut
Membre
Dernière intervention
14 janvier 2017
28
Bonjour

tout cela me laisse perplexe.
ça fait longtemps que je travaille avec les bases SQL et jusqu'à maintenant toutes les fois ou j'ai executé une commande "INSERT" sans erreurs les données ont été enregistrées.

Donc à priori si les commandes ne sont pas executées, c'est qu'elles ne sont pas lancées ou qu'il y a un bug dans ton composant "3G" d'interface.

peux-tu mettre en place un log coté client ?
genre un fichier à plat dans lequel tu traces tes executions de commande ? tu pourras alors comparer les commandes envoyées et celles enregistrées.
pour être certain de la séquence de tes commandes, peux-tu ajouter un compteur séquenciel, alimenté côté client, et stocké dans la base ?
cela permetrait d'etre certain que les commandes arrivent bien.

Je suppose que côté client tu utilises un timer, qui se reveille toutes les minutes. peux-tu vérifier s'il n'y aurait pas un dérive ?
car si le timer est désactivé pendant l'éxécution d'une commande et reprend lorsque la commande est effective, alors il y aura une petite dérive à chaque fois équivalente au temps de connexion + temps d'insertion.


C# is amazing, enjoy it!
Messages postés
13
Date d'inscription
vendredi 20 janvier 2006
Statut
Membre
Dernière intervention
17 février 2010

Bonjour et merci pour ton retour.

Au niveau 3G, je n'ai pas de composant spécifique, j'utilise la connexion internet du PC telle qu'elle est, 3G ou wifi, c'est transparent.

Côté client j'ai rendu le log un peu plus verbeux, et tout semble bien se passer.

Pour mieux comprendre comment mon programme marche : le timer appelle 4 fonctions :

Polling : envoi d'une trame sur le port série du PC et récupération de la valeur
Calculate : traitement des données - calculs entre valeurs
Update DB : insertion dans la base
Ecriture Sync : écriture des données en cas de pb de connexion sur la base (en cas de retour de Update DB == False)

Voici un exemple des données stockées dans la base et du fichier log du client associé :

id_site Date_time id_grandeur Valeur
3 2010-02-01 06:56:54 1 0.144
3 2010-02-01 06:57:54 1 0.127
3 2010-02-01 06:59:54 1 0.114
3 2010-02-01 07:00:54 1 0.137

Il manque les données de 2010-02-01 06:58:54

Au niveau du fichier log :
2010-02-01 06:56:54.093 - Polling
2010-02-01 06:57:54.093 - Polling
2010-02-01 06:58:54.093 - Polling
2010-02-01 06:59:54.093 - Polling
2010-02-01 07:00:54.093 - Polling

S'il y avait eu un souci, toute exception y aurait été inscrite. Le timer a bien fonctionnait à 06:58:54.

De plus, je suis sûr que certaines données insérées à 06:59:54 ont bien été calculées à partir de données collectées à 06:58:54 (certaines valeurs sont calculées en faisant la différence entre T0 et T0+1).

Le timer n'est jamais arrêté pour éviter une dérive. Il y a parfois une légère dérive des milisecondes mais rien de méchant.

Je pense donc que le pb est

Tout cela m'a permis de découvrir un autre souci bien plus bizarre : le PC change d'heure tout seul, avec un décallage de quelques secondes :
Ex :

2010-02-01 04:57:04.984 - Polling
2010-02-01 04:58:04.984 - Polling
2010-02-01 04:59:04.984 - Polling
2010-02-01 05:00:04.984 - Polling
2010-02-01 04:59:56.984 - Polling
2010-02-01 05:00:56.984 - Polling
2010-02-01 05:01:56.984 - Polling
2010-02-01 05:02:56.984 - Polling

Après 05:00:04, le l'heure du PC est revenue en arrière, le timer qui aurait du se relancer à 05:01:04 l'a en fait été à 04:59:56, puis à 05:00:56, ...

La mise à l'heure automatique du PC via time.windows.com ou autre ntp est désactivée, je ne vois vraiment pas d'où cela peut venir.

Merci d'avance pour toute idée !
Messages postés
13
Date d'inscription
vendredi 20 janvier 2006
Statut
Membre
Dernière intervention
17 février 2010

Autre point : j'ai fait une petite modif : après l'insertion, je fais un SELECT COUNT(*) FROM VALEUR WHERE Date_time = heure de mon dernier polling et je vérifie que le nb d'enregistrement est conforme. Ce test a toujours été confirmé.

Est ce possible que les données soit bien insérées mais ensuite perdues ?

Voici le code de Update DB :
public bool UpdateDB(){
connStr = "DRIVER={MySQL ODBC 5.1 Driver};SERVER="+parametres[1]+";UID="+parametres[3]+";PASSWORD="+parametres[4]+";DATABASE=eem";

oConn =  new System.Data.Odbc.OdbcConnection(connStr);
try {
oConn.Open();
OdbcCommand oComm = new OdbcCommand();
oComm.Connection = oConn;
oComm.CommandText = "insert into valeur  (id_site,Date_time,id_grandeur,Valeur) VALUES ";
int i = 0;
while (i<taille){
oComm.CommandText = oComm.CommandText + "('"+parametres[0]+"','"+Date_Heure+"','"+tag_esc[1,i]+"','"+tag_esc[10,i]+"')";
if (i < taille - 1) {
oComm.CommandText = oComm.CommandText + ",";
}
i++;
}

int records = oComm.ExecuteNonQuery();
oComm.CommandText = "SELECT COUNT(*) FROM VALEUR WHERE id_site="+parametres[0]+" AND Date_time = '"+Date_Heure+"'";
OdbcDataReader oReader = oComm.ExecuteReader();
oReader.Read();
int records2 = 0;
records2 = Convert.ToInt16(oReader.GetValue(0));
oReader.Close();
oConn.Close();
if (records2 != taille){
FichierLog = LogFile.AppendText();
FichierLog.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")+" - Erreur UPDATE DB:"+records2.ToString()+"/"+records.ToString()+"insérés / "+taille.ToString()+"prévus");
FichierLog.Flush();
FichierLog.Close();
 return(false);
}
}

catch (Exception e) {
FichierLog = LogFile.AppendText();		
FichierLog.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")+" - Erreur UPDATE DB");
FichierLog.Flush();
FichierLog.Close();
return(false);
}
return(true);        		
}

Messages postés
834
Date d'inscription
samedi 15 novembre 2008
Statut
Membre
Dernière intervention
14 janvier 2017
28
Bonjour

Je sèche un peu...
Les données inserées ne peuvent être perdues que si la base a été restaurée depuis une sauvegarde. Si non ce serait un bug majeur donc à priori impossible.

Dans ton code je ne vois pas la patie ou tu log les informations, le fais-tu en amont de cette fonction ?
peux-tu ajouter dans ton log les requetes SQL (oComm.CommandText)?
j'aimerais voir la valeur du parametre Date_Heure qui n'étant pas défini localement doit être global.

Autre piste, comme tu n'arretes pas le timer pendant l'execution, es-tu certain que la précédente insertion est bien terminée ? Auquel cas, l'utilisation de variables globales est dangereux.
je sais bien qu'une minute semble suffisant, et que par défaut le timeout des commandes doit être inferieur, mais vu que tu lances plusieurs commandes au travers d'un Wan on ne sait jamais.

Ajoute un boolean statique global, que tu bascules en début et en fin d'execution, cela permet de tester à l'entrée du timer si une execution est en cours.

une derniere chose, ta variable de connexion oConn n'est pas non plus locale, change le, et mets l'instanciation, l'open et le close dans le try/catch/finally
cela te permetra d'être certain de ne pas avoir de conflit avec une connexion qui ne serait pas fermée.

System.Data.Odbc.OdbcConnection oConn =  null;
try {
oConn =  new System.Data.Odbc.OdbcConnection(connStr);
oConn.Open();
...
}
catch 
{
...
}
finally
{
if (oConn != null)
  if (oConn.State == ConnectionState.Open) 
    oConn.Close();
}


Concernant la dérive due à la mise à jour de l'heure, je n'ai pas d'idée, le timer se déclanche sur un nombre de millisecondes et est indifferent au changement d'heur de la machine.

Bon courage!!


C# is amazing, enjoy it!
Messages postés
13
Date d'inscription
vendredi 20 janvier 2006
Statut
Membre
Dernière intervention
17 février 2010

Merci beaucoup pour ces éléments.

Je vais implémenter le try/catch/finally

J'ai mis plein de point d'arrêt sous forme de log qui se mette dans un fichier txt pour bien comprendre ce qui se passe. En fait l'éxecution de la fonction s'arrête parfois au niveau de oConn.Open() et parfois au niveau de oComm.ExecuteNonQuery(). Les autres appels de traitement prévus dans le timer ne se font pas, et j'ai tout de suite après le départ du timer (1 minute plus tard)...

Ce qui m'étonne beaucoup, c'est que ceci ne génère pas d'exception. Même si le temps de traitement est long sur le Wan, les timeouts par défaut sont forcément dépassés. Sur oConn.open(), c'est 15 secondes par défaut, et sur oComm.ExecuteNonQuery() je n'ai pas su trouver l'info mais ça doit être du même ordre de grandeur.

Une petite question de "débutant" :

Que se passe t'il si un timer qui doit lancer A() et B() prends trop de temps à traiter A() et que ce temps est supérieur à l'intervalle du timer.
B() sera executé à la fin de A() ou bien ce sera à nouveau A() ?
Messages postés
13
Date d'inscription
vendredi 20 janvier 2006
Statut
Membre
Dernière intervention
17 février 2010

peut être le driver odbc mysql qui déconne ? Je l'ai en version 5.1
Messages postés
13
Date d'inscription
vendredi 20 janvier 2006
Statut
Membre
Dernière intervention
17 février 2010

Pour le timer, j'ai trouvé la réponse en faisant un petit programme de test; chaque lancement se fait en parallèle jusqu'à la fin, sans interaction sauf en cas de variable public.
Messages postés
834
Date d'inscription
samedi 15 novembre 2008
Statut
Membre
Dernière intervention
14 janvier 2017
28
Bonjour

oui le timer étant un évenement il va s'executer en parrallele,
d'ou les problemes en cas d'utilisation de variables globales.
il faut absolument que toutes les variables soient définies localement, ou recu en parametres, comme pour un code réentrant.

je ne pense pas que le driver odbc soit en cause, la lecture de la base apres l'insertion montre que les données y sont.
je penche reelement pour un probleme de temps d'execution.

C# is amazing, enjoy it!
Messages postés
13
Date d'inscription
vendredi 20 janvier 2006
Statut
Membre
Dernière intervention
17 février 2010

Effectivement, moi aussi je penche pour un pb de temps d'execution, mais une exception de type timeout devrait être levée dans ce cas, non ??
Messages postés
13
Date d'inscription
vendredi 20 janvier 2006
Statut
Membre
Dernière intervention
17 février 2010

salut
Merci pour cette info sur le timeout.

On a décidé de passer sur SQL server et de revoir un peu l'architecture.
Le framework gérera surement mieux sur la nouvelle plateforme.

merci encore