poggit / libasynql

Asynchronous MySQL access library for PocketMine plugins.
https://poggit.github.io/libasynql
Apache License 2.0
134 stars 44 forks source link

More than two queries can not access the same data. #1

Closed matcracker closed 7 years ago

matcracker commented 7 years ago

Hi! After a lot of tests in my plugins, I can assert that more than two queries can't access the same data. I'll explain.

Code Structure:

public function getData(Player $player) : array{ $this->provider->getPlayerData($player); }

- **Provider class**

//This method allow to set player data to database. public function setPlayerData(Player $player, array $data){ $data1 = $data["Foo"]; $data2 = $data["Bar"]; $query = "INSERT INTO tblName (username, data1, data2) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE data1=VALUES(data1), data2 = VALUES(data2)"; $task = new DirectQueryMysqlTask($this->credentials, $query, [["s", $player->getName()], ["s", $data1], ["s", $data2]]); $this->plugin->getServer()->getScheduler()->scheduleAsyncTask($task); }

public function getPlayerData(Player $player){ $data = array(); //Requesting data from database, etc... return $data; }

- **Main class**

public function onPlayerJoin(PlayerJoinEvent $event){ //It's only an example but it can be any method or event $p = $event->getPlayer(); $pd = new PlayerData(); $arr_data1 = $pd->getData($p); $arr_data1["Foo"] = "Hello"; $pd->setData($p, $arr_data1); $arr_data2 = $pd->getData($p); $arr_data2["Bar"] = "world!"; $pd->setData($p, $arr_data2); }


## **Table structure**
- **Table with already data inserted**

| Player | Foo | Bar |
| --- | --- | --- |
| matcracker | foo | bar |

- **What should happen**

| Player | Foo | Bar |
| --- | --- | --- |
| matcracker | Hello | world! |
- **What really happens**

| Player | Foo | Bar |
| --- | --- | --- |
| matcracker | foo | world! |

## **Issue**
This isn't the real plugin code but I can confirm you that is what really happen.

So, how you can see from the tables, $arr_data2 isn't immediatly updated from $arr_data1. But using prepared statement as this library do, shouldn't the queries join an execution queue?
So, until $arr_data1 isn't executed, $arr_data2 will wait. But this isn't happening, why?

Thank you for you help, Matt.
PEMapModder commented 7 years ago

You should schedule the tasks to the same worker to ensure they are executed in the correct order. You may also write your own subclass of QueryMysqlTask that calls MysqlResult::executeQuery() twice.

SOF3 commented 7 years ago

Please understand the word "asynchronous". By definition, the data may not be updated or returned immediately after you schedule the AsyncTask. Therefore, you cannot return any data in getData(), nor can you assume the queries will be executed in the order you schedule the tasks.

Also as a result, you cannot handle the PlayerJoinEvent in time. You may only handle it slightly after the player joined, which will be after the event execution.

matcracker commented 7 years ago

Yes, sorry I do a mistake into the example. In the original code, the setData() set data into an array an then in the database and the getData() return the data array of set method. By the way there is an "update method" that update the array contents.

About the event, it's just only an example, in reality I don't use these methods inside it.

Anyway, I don't understand the way to create a multiple queries in sequence. I try to create an array containing queries and arguments but when I execute it, the server gives an error about a duplicate asynctask. Can someone explain me or give an example, if I don't disturb you. :)

Thank you again!

SOF3 commented 7 years ago

Make a class like this class except that it has two calls on executeQyery.