Persistence bug :(

BlitzMax Forums/Brucey's Modules/Persistence bug :(

Oddball(Posted 2011) [#1]
I'm experiencing a very frustrating intermittent bug with the bah.persistence module. It happens extremely rarely, but when it does happen it causes an unrecoverable error. From what I can tell by looking at the created XML file occasionally an object is serialised by reference as though it had already been serialised earlier in the file when in fact it is the first time the object has been serialised. This creates a reference to an object that does not exist and when the the XML file is deserialised it crashes the app. It has happened to me three times in about 6 months of using the module, and I also identified this as the cause of crashes some of my beta testers were having. Heres a dump of one of the offending XML files. See the TItemDef object on line 33.


I also have an older XML file from when it happened a previous time if that will help. It's in a different format from the one above, and is from one of my beta testers linux machine(mine is from the Mac version). The artifacts array at the end is causing the crash


And on a related note could I request that if the serialiser/deserialiser fails then it fails silently and just return a Null object that way I can attempt to recover from a backup copy.

Last edited 2011

Last edited 2011


Oddball(Posted 2011) [#2]
Aargh! it happened again. This time it failed to serialise the entire artifacts TList.


I may have to give up on bah.persistence. I'm too close to release to risk such a temperamental bug.

Has anyone else who uses bah.persistence experienced the same bug?


Brucey(Posted 2011) [#3]
The only way it can be thinking there is a reference already in the map is if map.Contains() returns True for the ref value.

The other way to do the check, would be to do a ValueForKey(ref) and compare the actual object returned with the one that you are trying to serialise. If those are different then perhaps it should raise an error (exception), and you can try to serialise it again from scratch?

Which function do you call to kick off the serialisation?


Oddball(Posted 2011) [#4]
I use SerialzeToFile
pers.SerializeToFile settings,"settings.xml"

I did try tracing through the code but as it happens so infrequently I couldn't determine much. I thought it might be a rogue reference in the object map, like you say, but from what I can tell it is always cleared before serialisation. Although I do find it hard to follow other peoples code so I may have missed something.


Brucey(Posted 2011) [#5]
Hi. I've changed it slightly so it does more verification before believing an object exists in the map.

If there is a "collision" (i.e. the same ref appears to be of two different objects), it will now throw a TPersistCollisionException.
You can wrap your serialize call in a Try/Catch block. If you run in debug, you'll get an idea of what is wrong.

If you want, rather, I can make the exception optional, and have it just write over the previous entry with the new data - but I'd be wary of that if there is some real underlying problem somewhere with brl.reflection...


Oddball(Posted 2011) [#6]
Thanks for the update. I was worried it was going to be a long wait before I got the error again, but I got a run of it happening 5-6 times on the run. I was able to catch the error and trace it back to the objects involved.

First I should explain a little about the structure of my game. When the game starts I deSeriealise the settings and saved game, the game is played, then on exit I Serialise the settings and the saved game.

When the error was thrown I examined the objects and found that one was from the settings and the other was from the saved game that was DeSerialised when the game started. So I examined the contents of objectMap and it was chock full of objects from the DeSerialised saved game. It seems if you DeSerialise and then Serialise objectMap is not cleared before Serialisation.

Still, this shouldn't be a problem as both objects would have different addresses as the both still existed. So I dug a little deeper. The first objects address was "$008967B0". The second objects address was "$00962F68". They are indeed different so I ran them through your Base36 code to see if that was the problem. The first address came out as "05D0AG", and this was also the ref coughed up by the exception, but the second address encoded as "05UYJS" which is totally different. So I have no idea why objectMap.Contains() returns true when the two objects have different reference keys.

So in conclusion objectMap doesn't get cleared after a DeSerialisation. objectMap.Contains() returns true even when the keys are different. I'm more stumped now than when I started.

Last edited 2011


Oddball(Posted 2011) [#7]
I've had chance to sleep on this(I did all the above at 4am), and have realised that when the DeSerialised object was initialised it would have had a different object address, because it was created the previous time the game was run. So it's just an amazing coincidence that a new object this time around is given the same address.

The solution to this is to clear the objectMap after DeSerialisation. It's wasted memory anyway as the data will never be used again.

Something like
	Method DeSerializeFromFile:Object(filename:String)
	
		xmlParserMaxDepth = maxDepth

		doc = TxmlDoc.parseFile(filename)

		If doc Then
			Local root:TxmlNode = doc.GetRootElement()
			fileVersion = root.GetAttribute("ver").ToInt() ' get the format version
			Local obj:Object = DeSerializeObject("", root)
			Free()
			Return obj
		End If
	End Method


Last edited 2011