Forum BigDB Cannot create circular references inside database objects

Discussion and help relating to the PlayerIO database solution, BigDB.

Cannot create circular references inside database objects

Postby Tenko » May 2nd, 2011, 1:25 am

Found the solution to my problem myself, just by guessing what needed to be done. I will post the solution for anyone else who encounters this problem with DatabaseArrays.

I had defined this databasearray variable in the scope of my class:
Code: Select all
DatabaseArray LibraryIndexArray = new DatabaseArray();


I then used BigDB to load the values from the databaseobject so that the player could see what was in their library:
Code: Select all
LibraryIndexArray = result.GetArray("LibraryIndex");


Then, LibraryIndexArray was modified according to which cards the player wanted to add or remove.

After that, when the player was ready to save the final changes to their library, this code executed inside a BigDB load statement:
Code: Select all
result.Set("LibraryIndex", LibraryIndexArray);  // ERROR HERE


The error generated was:
Cannot create circular references inside database objects


I had no idea why it was claiming to be a circular reference, but I had a feeling that if I just created a new databasearray variable and loaded the values into that new object, it would save successfully. And it did.
Code: Select all
DatabaseArray NewLibraryIndexArray = new DatabaseArray();
for (i = 1; i <= LibraryIndexArray.Count; i++) {
      NewLibraryIndexArray.Add(LibraryIndexArray.GetUInt(i - 1));
}
result.Set("LibraryIndex", NewLibraryIndexArray); // No Error Thrown


I tested the save function afterwards, and it correctly saved the changes to LibraryIndexArray.

So if anyone has this error show up again, here is your solution. :D
Tenko
 
Posts: 8
Joined: November 25th, 2010, 5:26 pm

Re: Cannot create circular references inside database objects

Postby cjcenizal » May 2nd, 2011, 5:06 pm

Nice work! Thanks for sharing your solution.
cjcenizal
Paid Member
 
Posts: 115
Joined: March 29th, 2011, 12:31 am

Re: Cannot create circular references inside database objects

Postby cjcenizal » May 4th, 2011, 4:59 pm

Hey Tenko, I just realized what your problem is. First of all you don't need to do this:

Code: Select all
DatabaseArray LibraryIndexArray = new DatabaseArray();
LibraryIndexArray = result.GetArray("LibraryIndex");


The above code creates an instance of a DatabaseArray, and then overwrites it with another instance of DatabaseArray (result.GetArray() returns the second instance). So the above code should just be:

Code: Select all
DatabaseArray LibraryIndexArray = result.GetArray("LibraryIndex");


Also, your error from the database, "Cannot create circular reference", is pretty enlightening. I believe that result.GetArray("LibraryIndex") is returning a reference to its "LibraryIndex" property of type DatabaseArray. So when you pass it back in as a parameter of the Set() method, you're basically saying, "Replace your property with your property". Which is a circular reference.

You can test this by making a change to LibraryIndexArray, and then calling result.Save(). If the changes are reflected in BigDB, then that shows LibraryIndexArray is a reference to result's property, and not a new instance.
cjcenizal
Paid Member
 
Posts: 115
Joined: March 29th, 2011, 12:31 am

Re: Cannot create circular references inside database objects

Postby Tenko » May 4th, 2011, 8:33 pm

Yeah, It figures that
Code: Select all
result.GetArray("LibraryIndex");

would be returning a reference instead of a value. For some odd reason I just don't want to deal with it as a reference though. Even though it would be less code, too many things could still go wrong. Like, if I made changes to LibraryIndexArray, and opened the player object in a BigDB.Load statement to make changes to only something else, then used Save(), it would also perform the unintended save-change to the DatabaseArray.

But I find it very interesting that the two separate things (game server and BigDB server) could have their values shared by reference. Looks like BigDB is so closely tied to the game server that it should be able to react to commands incredibly fast. I also know that there's more than one computer handling the game server code, possibly more than one handling the BigDB server. So references pointing to memory addresses on computers hundreds of miles away is also impressive.
Tenko
 
Posts: 8
Joined: November 25th, 2010, 5:26 pm

Re: Cannot create circular references inside database objects

Postby cjcenizal » May 4th, 2011, 8:53 pm

I don't think DatabaseObject properties are references to memory on the server... it's just that their Get("propertName") methods return a reference to the property, as opposed to the value of the property. Also, btw, it's been said in previous threads that calling Save() only sends the data that's been changed... interesting to note.
cjcenizal
Paid Member
 
Posts: 115
Joined: March 29th, 2011, 12:31 am

Re: Cannot create circular references inside database objects

Postby Henrik » May 5th, 2011, 11:42 am

So I went digging a bit in the code, and it turns out that setting an array property of a databaseobject to itself triggers the circular reference error, even though it obviously doesn't create a circular reference.

The reason for this is that it makes the circular check faster, and as you found out, setting an array property or object property back to itself doesn't really do anything, since arrays and objects are reference types, not value types.

Tenko wrote:For some odd reason I just don't want to deal with it as a reference though. Even though it would be less code, too many things could still go wrong. Like, if I made changes to LibraryIndexArray, and opened the player object in a BigDB.Load statement to make changes to only something else, then used Save(), it would also perform the unintended save-change to the DatabaseArray.

No it won't, but having two references to the same DatabaseObject and doing different changes to them is going to cause troubles. However, BigDB supports optimistic locking which prevents you from accidentally overwriting your object with a stale reference. Like this:

Code: Select all
var obj = Client.BigDB.LoadOrCreate("Table", "obj");
obj.Set("arr", new DatabaseArray()); //obj is now {arr:[]}
obj.Save();
var arr = obj.GetArray("arr");
var obj2 = Client.BigDB.Load("Table", "obj"); //Get a second reference
arr.Add("foo"); //This modifies obj to {arr:['foo']}, obj2 is unchanged.
obj2.Set("bar", true); //This modifies obj2 to {arr:[], bar:true}, obj and obj2 are now different
obj2.Save(); //Save obj2, obj is now out of sync with the object in storage.
obj.Save(true); //This will fail because the optimistic locking will detect that obj is outdated.
obj.Save(); //This will work, but it will overwrite any changes made to obj2. Don't do this. :-)
Henrik
.IO
 
Posts: 1880
Joined: January 4th, 2010, 1:53 pm


Return to BigDB