Select veraltet mit InnoDB

MisterMan

New Member
Guten Tag,

Derzeit arbeite ich an einem Advert Script für Gameserver (CSS). Dazu setze ich SQL zur Datenhaltung ein.
Nun habe ich das Problem, das ich bei InnoDB falsche Select ausgaben bekomme.

Tabellen:

Code:
CREATE TABLE IF NOT EXISTS `msg` (
  `idmsg` int(11) NOT NULL AUTO_INCREMENT,
  `msg` varchar(100) NOT NULL,
  PRIMARY KEY (`idmsg`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
 
CREATE TABLE IF NOT EXISTS `server` (
  `idserver` int(11) NOT NULL AUTO_INCREMENT,
  `ip` varchar(15) NOT NULL,
  `port` varchar(5) NOT NULL,
  `name` varchar(45) NOT NULL,
  PRIMARY KEY (`idserver`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
 
CREATE TABLE IF NOT EXISTS `server_msg` (
  `fk_idserver` int(11) NOT NULL,
  `fk_idmsg` int(11) NOT NULL,
  PRIMARY KEY (`fk_idserver`,`fk_idmsg`),
  FOREIGN KEY (fk_idserver) REFERENCES server(idserver),
  FOREIGN KEY (fk_idmsg) REFERENCES msg(idmsg)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Nun zu meinem Problem, wenn mein Advert script startet, liest es alle NachrichtenID's aus der server_msg. Sortiert natürlich die aus, welche für andere Server sind.
Wird nun über das Webinterface eine Nachricht entfernt, ignoriert es der Gameserver. Er bekommt fröhlich ein Ergebniss aus dem Cache, denke ich.

Der script ist in Python und es wird pymysql eingesetzt. Das Webinterface ist in PHP.

Danke !
 
Das Problem sollte eher an deinen Skripten zu suchen sein, gib mal die entsprechenden SQL-Kommandos.
 
Code:
	def getMsgcount(self,sid):
		sql = "Select count(fk_idmsg) FROM server_msg WHERE fk_idserver=%s" %(sid)
		self.__sql.cursor.execute(str(sql))
		return self.__sql.cursor.fetchone()

Bei Programmstart waren es 3, während der Laufzeit 2, es werden trotzdem 3 Ausgegeben bei Funktionsaufruf. Bis zum Neustart.

Code:
	def getMsg(self,idmsg):
		sql = "Select msg FROM msg WHERE idmsg=%s" %(idmsg)
		self.__sql.cursor.execute(str(sql))
		return self.__sql.cursor.fetchone()

Liefert mir die dritte gelöschte nachricht...

Deswegen vermute ich den Cache. Der Fehler tritt auch nur bei InnoDB auf !
 
Du startest zum Löschen des Datensatzes aber nicht zufällig eine Transaktion und vergisst das Commit danach?
Mit persistenten MySQL Verbindungen bleibt die Transaktion erstmal offen und deine Webanwendung sieht die Änderung. Dein Gameserver natürlich nicht.
 
Das interface kommt nicht von mir, da ich kein PHP kann. Was genau geschieht?

Code:
$loeschen = "DELETE FROM `server_msg` WHERE `fk_idserver` = $sid AND `fk_idmsg` = $mid";

Allerdings sieht der server wie beschrieben nix davon...
PHPmyAdmin sieht aber die Änderung. Bei MyISAM tritt der Fehler nicht auf....
Aber da feht mit die Referenzielle Datenintigrität !
 
sql.cursor riecht mal stark nach Transaktionen.
Das Rundherum ist vermutlich interessanter _wie_ es commited wird.

Da geschieht nix ausser dass eine Variable mit einem SQL-Kommando befuellt wird. Das Kommando selbst solltest du ja verstehen.

Aber ich bin wirklich muede oder du arbeitest einmal in der einen und einmal in der anderen Tabelle ohne dass die jeweils andere Tabelle aktualisiert wird.
 
Sorry erstmal für die Späte Antwort, aber Deutschland hat bekanntlich gerade gespielt !

der Code war aus dem Zusammenhang.
Das sind sind Funktionen aus meiner Datenbankklasse.

Aber wie gesagt, ich denke nicht das es am Code liegt, denn es geht mit ner MyISAM tabelle zu 100%.
Sobald es eine InnoDB ist, tritt der Fehler auf!

d4f said:
Aber ich bin wirklich muede oder du arbeitest einmal in der einen und einmal in der anderen Tabelle ohne dass die jeweils andere Tabelle aktualisiert wird.

Ich lese ja nur aus der tabelle, geschrieben wird nur von PHP. Das geht auch fehlerfrei !

Befehl an SQL:
Code:
self.__sql.cursor.execute(str(sql))

Auslesen des Ergebnisses und Rückgabe an Programm:
Code:
return self.__sql.cursor.fetchone()

Na ja und diese Rückgabe ist eben falsch. Da wird etwas zurück gegeben, was nicht mehr i der Tabelle steht. Nach Neustart des Scriptes, wird das richtige Zurückgegeben. Bis zur nächsten Modifikation eben.....
Deswegen denke ich, das schreit nach einem Caching fehler.

Kann bei bedarf auch den gesamten Code senden.
 
Ich glaube dein Problem verstanden zu haben.
Du fuehrst den execute am Anfang des Skriptes aus und liest dann mit fetchone gerne nach und nach durch wenn neue Eintraege kommen, korrekt?
So funktioniert das leider nicht; das Resultat ist beim execute bereits fest definiert, erstellt und gepuffert und die Inhalte bleiben so stehen.
Du musst immer ein neues execute ausfuehren.
 
Okay, ich poste nun mal alles an relevanten Code. Vill ist es dann verständlicher.

Datenbank:
Code:
class mysql(object):
	def __init__(self , host , port , user , passwd , database):
		self.__host = host
		self.__port = port
		self.__user = user
		self.__passwd = passwd
		self.__database = database
		self.__sql = self.__connect()
			
	def __connect(self):
		try:
			return smartDB.mysql(self.__host , self.__port , self.__user , self.__passwd , self.__database)
		except:
			es.unload(info.basename)
					
	def isServer(self,sid):
		sql = "Select count(idserver) FROM server WHERE idserver=%s" %(sid)
		self.__sql.cursor.execute(str(sql))
		rowcount = self.__sql.cursor.fetchone()
		rowcount = int(rowcount[0])
		if rowcount != 0:
			return True
		else:
			return False

	def getServer(self,sid):
		sql = "Select idserver,ip,port,name FROM server WHERE idserver=%s" %(sid)
		self.__sql.cursor.execute(str(sql))
		return self.__sql.cursor.fetchone()
		
	def getMsgindex(self,sid):
		sql = "Select fk_idmsg FROM server_msg WHERE fk_idserver=%s" %(sid)
		self.__sql.cursor.execute(str(sql))
		return self.__sql.cursor.fetchall()
		
	def getMsgcount(self,sid):
		sql = "Select count(fk_idmsg) FROM server_msg WHERE fk_idserver=%s" %(sid)
		self.__sql.cursor.execute(str(sql))
		return self.__sql.cursor.fetchone()
		
	def getMsg(self,idmsg):
		sql = "Select msg FROM msg WHERE idmsg=%s" %(idmsg)
		self.__sql.cursor.execute(str(sql))
		return self.__sql.cursor.fetchone()
		
	def disconnect(self):
		self.__sql.disconnect()


sql = mysql(host , port , user , passwd , database)

Die schleife, welche die Nachrichten sendet:
Code:
def msg_loop():
	global last
	try:
		if sql.isServer(sid):
			# Server gefunden
			msgc = sql.getMsgcount(sid)
			msgc = int(msgc[0])
			if msgc > 0:
				# Nachrichten vorhanden
				if rnd == 1:
					# Waehle zufaellige
					last = int(random.randrange(1 , msgc + 1 , 1))
				else:
					# Waehle naechste
					last += 1
					if last > msgc:
						last = 1
				msgi = sql.getMsgindex(sid)
				idmsg = msgi[last-1][0]
				msg = sql.getMsg(idmsg)
				msg = msg[0]
				
				if center_msg == 1:
					smsg.center(msg)
				if chat_msg == 1:
					smsg.chat(msg)
				if hud_msg == 1:
					smsg.hud(msg)
				if top_msg == 1:
					smsg.top(msg)
				if menu_msg == 1:
					smsg.menu(msg)
		else:
			smsg.log('Server deleted, sid unknow')
	except:
		pass
	gamethread.delayedname(time, 'loop', msg_loop)

Also wie gesagt, ich führe jedes mal neu aus.
Habe leider mit InnoDB nicht viel gearbeitet, denke es liegt an diesem Commit, das ich nen altensnapshot lese.
Lese es gerade nach, bin aber für Hilfe offen !

Edit 1:
Ich glaube dies beschreibt mein Problem und die Lösung dazu.
Code:
          User A                 User B

           SET AUTOCOMMIT=0;      SET AUTOCOMMIT=0;
time
|          SELECT * FROM t;
|          empty set
|                                 INSERT INTO t VALUES (1, 2);
|
v          SELECT * FROM t;
           empty set
                                  COMMIT;

           SELECT * FROM t;
           empty set

           COMMIT;

           SELECT * FROM t;
           ---------------------
           |    1    |    2    |
           ---------------------

Kann ich auch einfach beim verbinden "SET AUTOCOMMIT=1" setzen? oder muss ich nach jedeer änderung und vor jedem lesen einmal "COMMIT" machen?

Muss das "COMMIT" einfach durch den cursor gesetzt werden? Sprich:

Code:
sql = "COMMIT"
		self.__sql.cursor.execute(str(sql))
 
Last edited by a moderator:
Gesagt getan, nun geht es...

Code:
	def commit(self):
		sql = "COMMIT;"
		self.__sql.cursor.execute(str(sql))

Neue Funktion, welche bei jeden Durchlauf ein Commit macht. Nun geht es.

Danke an alle...
 
Back
Top