-
Notifications
You must be signed in to change notification settings - Fork 115
/
Copy pathlocking.txt
executable file
·106 lines (79 loc) · 4.9 KB
/
locking.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
*** This may be out of date
In most parts of phpDiplomacy locking isn't necessary. In the forum records are
only added, not changed. If a new message is added by one user while the forum
is being loaded by another it won't corrupt anything.
However, weird results can occur if changes to the database can be made while
half way through a page. If the first half of the map is rendered for one phase,
and the second half is rendered for the next phase, there will be weird results.
In pages where locking isn't necessary it's only important that things don't
*appear* to change half way through, though if they actually change that's
fine because the information is only being displayed, not acted on.
Because of this all page requests are wrapped in a REPEATABLE READ transaction.
This means that if a page is being rendered and the database changes the changes
will not appear until the end of the transaction. The whole page operates on a
single, static, transaction, which doesn't change because of the effects of other
transactions.
However if one transaction UPDATEs a row, another transaction will have to wait
for the first transaction to finish until it can update that row. (Though it can
still view the row as if it hadn't been updated)
This means care should be taken when UPDATEing. If user A updates row A, user B
updates row B, then user A asks to update row B and user B asks to update row A
there will be a deadlock.
There are some sections where the data which gets selected must not be modified
as well as not appearing to be modified, because decisions will be made based on
the selected data.
For example in the board many decisions are made based on the game's mode, and
corruption could occur if the game gets altered.
This is different from the map in that data isn't just being presented based
on the selected data, data gets modified based on the selected data.
For this SELECT .. LOCK IN SHARE MODE is used. While a transaction has a row
locked in share mode it can be read by other users, but it cannot be altered.
LOCK IN SHARE MODE isn't good if you intend to update the database though. If
two users both access a row in SHARE MODE, and then either tries to update the
row, they won't be able to because they will both have locked the other out of
changing the row.
SELECT .. FOR UPDATE gets around this problem. Only one user can have a lock on
a row FOR UPDATE (it isn't shared), and while it is locked FOR UPDATE no-one
can lock it in SHARE MODE.
For example in the game join code block, it checks that there are less than 7
users before joining. It must make sure that no other users can add themselves
to the number of users after having checked that there are less than 7 but
before we get added to complete those 7. If that happened there would be 8
users, and there would be corruption. (It has happened)
Note that locking FOR UPDATE stops it being locked in SHARE MODE, but it
doesn't stop the row being viewed without locking.
Finally there are some code sections which are called less often, and it is
more convenient to simply lock the code section rather than try to use fine
grained locking.
eg Adding a user. If two users were added with the same name there would be
problems, but instead of indexing the username and locking a large section
of the users table while adding a user, thus blocking out other users from
viewing phpDiplomacy, it is simpler to lock the user creation code. (Making
the username row unique would also solve the problem, but it would mean an
database uniqueness error would be presented instead of a friendly "username
taken" error message.)
GET_LOCK is used to get a named lock; only one thread can get the lock at a
time, and if the lock can't be retrieved the code will not run. Because all
phpDiplomacy's access runs in a transaction results aren't committed until
the end of the page, so all locks requested with GET_LOCK must be kept until
the end of the page, after the data has been committed.
LOCK IN SHARE MODE:
board.php and ajax.php locks the game row being viewed in share mode
In ajax.php the membership being used is locked in share mode
FOR UPDATE:
The user being used is locked FOR UPDATE, unless they are a guest user.
In board.php the membership being used is locked FOR UPDATE
Orders are always locked FOR UPDATE
Gamemaster locks the game being processed FOR UPDATE, which prevents it
being processed while being viewed in board.php
GET_LOCK:
User creation
Game creation
User control panel
Gamemaster
To prevent deadlocks it is vital that this order is followed when locking:
USER -> GAME -> MEMBERSHIP -> ORDER
These are defined, and passed to the objects above to dictate how they are to
be locked: NOLOCK, SHARE, UPDATE
Careful consideration should be given to locking to prevent very hard to
reproduce and potentially very damaging bugs.