Swift: How to avoid deadlock occurring inside for loop of asynchronous functions?

I just asked a question on stackoverflow regarding this issue. The question:
I have a for loop - running 6 times - containing an async function that uploads an object to a database. When called the function only uploads 3 - 4 objects and for the remaining 2 - 3 the console prints:
fServer reported an error: FAULT = ‘Server.Processing’ [java.lang.RuntimeException: java.lang.RuntimeException: java.lang.RuntimeException: com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction]
I’ve read other threads regarding this issue and I followed the advise to use dispatch groups. Unfortunately it’s still not working for me and I do not understand why. How can I avoid these deadlocks from happening?
func uploadObjects() {
let myGroup = dispatch_group_create()
self.backendless.initApp(self.APP_ID, secret: self.SECRET_KEY, version: self.VERSION_NUM)
for object in objects {
dispatch_group_enter(myGroup)
let dataStore = backendless.data.of(Game.ofClass())
// save object asynchronously
dataStore.save(
game,
response: { (result: AnyObject!) -> Void in
dispatch_group_leave(myGroup)
let obj = result as! Game
print(“Game saved: (obj.objectId)”)
},
error: { (fault: Fault!) -> Void in
print(“fServer reported an error: (fault)”)
})
}
dispatch_group_notify(myGroup, dispatch_get_main_queue(), {
print(“Finished all requests.”)
})
}
The respondents on SO tell me it’s a server side issue. Any thoughts on how to solve this?
Stijn

Please see this thread.

And you should call backendless.initApp(…) only once, in AppDelegate, for example:

 func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
 
 backendless.initApp(APP_ID, secret:SECRET_KEY, version:VERSION_NUM)
 return true
 }

Thanks Vyacheslav,

The deadlocks are gone if I upload the variables with a 1 to many relationship via a separate function . The problem now is that this function creates a new row AND updates the current object. I only want it to do the latter. Should I call saveTeamAsync somwhere else than from inside the completion handler of uploadGames?

func uploadGames() {

for manager in ManagersInGame.sharedInstance.managersArray {

let game = Game()

game.gameID = ManagersInGame.sharedInstance.gameID
game.name = 1

let dataStore = backendless.data.of(Game.ofClass())

dataStore.save(
game,
response: { (result: AnyObject!) -> Void in

for player in 0…5 {
game.team.append(manager.team[player])
}

self.saveTeamAsync(game.team, game: game)

print(“Game has been saved: ((result as! Game).objectId)”)
},
error: { (fault: Fault!) -> Void in
print(“Server reported an error: (fault)”)
})

}

}

func saveTeamAsync(data: [Player], game: Game) {

let dataStore = backendless.data.of(Player.ofClass())

game.team = data

dataStore.save(
game,
response: { (result: AnyObject!) -> Void in
print(“Team has been saved: ((result as! Game).objectId)”)
},
error: { (fault: Fault!) -> Void in
print(“Server reported an error: (fault)”)
})
}

The second “save” creates a new row because you should update the Gate object, which was returned by server (its ‘objectId’ is set), not an original object (its ‘objectId’ is not set).

So, fix this and try again:

dataStore.save(
game,
response: { (result: AnyObject!) -> Void in
game = result as! Game
for player in 0...5 {
game.team.append(manager.team[player])
}
self.saveTeamAsync(game.team, game: game)
print("Game has been saved: \((result as! Game).objectId)")
},
error: { (fault: Fault!) -> Void in
print("Server reported an error: \(fault)")
})

Sure, you could call saveTeamAsync somewhere else than from inside the completion handler of uploadGames, but in this case you need to keep the returned result in a class property, for example, in line 4:

self.savedGame = result as! Game

Thanks for the help, it works now :slight_smile: