0% found this document useful (0 votes)
2 views70 pages

Concurrency Control

Uploaded by

yohan vishvajith
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views70 pages

Concurrency Control

Uploaded by

yohan vishvajith
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 70

Advanced Database

Management Systems
ICT3212
Concurrency Control Techniques

ICT3212 Advanced Database Management Systems


Concurrency Control

Purpose of Concurrency Control


• To enforce Isolation (through mutual exclusion) among conflicting
transactions.
• To preserve database consistency through consistency preserving
execution of transactions.
• To resolve read-write and write-write conflicts.

9/24/2
ICT3212 Advanced Database Management Systems 2020
Concurrency Control

• Concurrency-control protocols are defined to allow concurrent


schedules and at the same time to make sure that the schedules are
conflict/view serializable, and are recoverable and maybe even
cascadeless.
• These protocols enforce a mechanism that avoids non-seralizable
schedules instead of testing a schedule for Serializability after it has
executed through examining the precedence graph.

9/24/3
ICT3212 Advanced Database Management Systems 2020
Concurrency Control Protocol Types
• Lock Based Protocol
• Time-Stamp Ordering Protocol
• Multi-version Protocol
• Graph Based Protocol
• Multiple Granularity Protocol

9/24/4
ICT3212 Advanced Database Management Systems 2020
Concurrency Control

LOCKS
• To control concurrent execution of transactions locking of the data
items technique is used.
• A lock is a variable associated with a data item that describes the
status of the item with respect to possible operation can be applied
to it.
• Generally, there is one lock for each data item in the database.
• Locks are used as a means of synchronizing the access by concurrent
transactions to the database items.

9/24/5
ICT3212 Advanced Database Management Systems 2020
Database Concurrency Control

• Lock Manager:
• Managing locks on data items.
• Lock table:
• Lock manager uses it to store the identify of transaction locking a
data item, the data item, lock mode and pointer to the next data
item locked. One simple way to implement a lock table is through
linked list.
Transaction ID Data item id lock mode Ptr to next data item
T1 X1 Read Next

9/24/6
ICT3212 Advanced Database Management Systems 2020
Lock Types

Different Types are available


• Binary
• Shared/exclusive

9/24/7
ICT3212 Advanced Database Management Systems 2020
Binary Locks
• A binary lock can have two states: locked and unlocked.
• A distinct lock is associated with each database item X.
• If
₋ Lock(X)= 1 item X cannot be accessed by a database operation that
requests the item.
₋ Lock(X)=0 Can access and change the value to 1
₋ Unlock_item(X) will release the lock
• Two operations, lock_item and unlock_item, are used with binary
locking.
• Enforces Mutual Exclusion on the data item.
9/24/8
ICT3212 Advanced Database Management Systems 2020
Binary Lock Operations
Lock_item(X)
if LOCK (X) = 0 (*item is unlocked*)
then LOCK (X)  1 (*lock the item*)
else begin
wait (until lock (X) = 0) and
the lock manager wakes up the transaction);
goto B
end;

9/24/9
ICT3212 Advanced Database Management Systems 2020
Binary Lock Operations
Unlock_item(X)
LOCK (X)  0 (*unlock the item*)
if any transactions are waiting then
wake up one of the waiting the transactions;

9/24/10
ICT3212 Advanced Database Management Systems 2020
Rules for Binary Locks
1. A transaction T must issue the operation lock_item(X) before any
read_item(X) or write_item(X) operations are performed in T.

2. A transaction T must issue the operation unlock_item(X) after all


read_item(X) and write_item(X) operations are completed in T.

3. A transaction T will not issue a lock_item(X) operation if it already holds


the lock on item X.

4.A transaction T will not issue an unlock_item(X) operation unless it


already holds the lock on item X.
9/24/11
ICT3212 Advanced Database Management Systems 2020
Shared/Exclusive (or Read/Write) Locks
• Binary locking scheme is too restrictive because at most one transaction
can hold a lock on a given item.
• Should allow several transactions to access the same item X if they all
access X for reading purposes only.
• Multiple-mode lock is used and there are two locks modes:
(a) shared (read)
(b) exclusive (write)

9/24/12
ICT3212 Advanced Database Management Systems 2020
Shared/Exclusive (or Read/Write) Locks
• Shared mode: shared lock (X)
More than one transaction can apply share lock on X for reading
its value, but no write lock can be applied on X by any other
transaction.
• Exclusive mode: Write lock (X)
Only one write lock on X can exist at any time and no shared lock
can be applied by any other transaction on X.

9/24/13
ICT3212 Advanced Database Management Systems 2020
Shared/Exclusive (or Read/Write) Lock Operations
• The following code performs the read operation:
B: if LOCK (X) = “unlocked” then
begin LOCK (X)  “read-locked”;
no_of_reads (X)  1;
end
else if LOCK (X)  “read-locked” then
no_of_reads (X)  no_of_reads (X) +1
else
begin
wait (until LOCK (X) = “unlocked” and
the lock manager wakes up the transaction);
go to B
end;
9/24/14
ICT3212 Advanced Database Management Systems 2020
Shared/Exclusive (or Read/Write) locks
The following code performs the write lock operation:
B: if LOCK (X) = “unlocked”
then LOCK (X)  “write-locked”;
else begin
wait (until LOCK (X) = “unlocked” and
the lock manager wakes up the transaction);
go to B
end;

9/24/15
ICT3212 Advanced Database Management Systems 2020
Shared/Exclusive (or Read/Write) Lock Operations
The following code performs the unlock operation:

if LOCK (X) = “write-locked” then


begin LOCK (X)  “unlocked”;
wakes up one of the transactions, if any
end
else if LOCK (X)  “read-locked” then
begin
no_of_reads (X)  no_of_reads (X) -1
if no_of_reads (X) = 0 then
begin
LOCK (X) = “unlocked”;
wake up one of the transactions, if any
end
end;

9/24/16
ICT3212 Advanced Database Management Systems 2020
Shared/Exclusive Lock Rules

• A transaction T must issue the operation read_lock(X) or write_lock(X)


before any read_item(X) operation is performed in T.
• A transaction T must issue the operation write_lock(X) before any
write_item(X) operation is performed in T.
• A transaction T must issue the operation unlock(X) after all
read_item(X) and write_item(X) operations are completed in T.
• A transaction T will not issue a read_lock(X) operation if it already
holds a read (shared) lock or a write (exclusive) lock on item X.

9/24/17
ICT3212 Advanced Database Management Systems 2020
Shared/Exclusive Lock Rules
• A transaction T will not issue a write_lock(X) operation if it already
holds a read (shared) lock or write (exclusive) lock on item X. This rule
may be relaxed.
• A transaction T will not issue an unlock(X) operation unless it already
holds a read (shared) lock or a write (exclusive) lock on item X.

9/24/18
ICT3212 Advanced Database Management Systems 2020
Example Problem

• If they are executed as two serial schedules T1, T2 or T2, T1 then serializability is
guaranteed.

9/24/19
ICT3212 Advanced Database Management Systems 2020
Example Problem

9/24/20
ICT3212 Advanced Database Management Systems 2020
Concurrency Control Protocols

• Using binary locks or read/write locks do not guarantee serializability.


• To guarantee serializability, it is necessary to follow an additional
protocol concerning the positioning of locking and unlocking
operations in every transaction.

• Two-Phase Locking – 2PL

10/17/1
ICT3212 Advanced Database Management Systems 2020
Two Phase Locking
• A transaction is said to follow the two-phase locking protocol if all locking
operations (read_lock, write_lock) precede the first unlock operation in the
transaction.
• Such a transaction can be divided into two phases:
• Expanding or growing (first) phase during which new locks on items
can be acquired but none can be released.
• Shrinking (second) phase  during which existing locks can be released
but no new locks can be acquired.

10/17/2
ICT3212 Advanced Database Management Systems 2020
Two Phase Locking (2PL)

• Lock conversion
• upgrading of locks (from read-locked to write-locked) must be done
during the expanding phase
• downgrading of locks (from write-locked to read-locked) must be
done in the shrinking phase

10/17/3
ICT3212 Advanced Database Management Systems 2020
Two Phase Locking

The highlighted parts are the last locks


taken, i. e. the serial schedule could
be:
T2 -> T3 -> T1

• If every transaction in a schedule follows the two-phase locking


protocol, the schedule is guaranteed to be serializable, obviating the
need to test for serializability of schedules.
• Guaranteeing serializability paid by reduce concurrency.
10/17/4
ICT3212 Advanced Database Management Systems 2020
Limitations in 2PL

Using 2PL may cause the following problems.


• Cascading Rollback
• Deadlocks

10/17/5
ICT3212 Advanced Database Management Systems 2020
2PL – Limitations
Cascading Rollback : Recursively abort the transactions that read data
written by aborted transactions.

If transaction T1 rollbacks then transaction T2 and T3 has to rollback.


10/17/6
ICT3212 Advanced Database Management Systems 2020
Cascading Rollback

Solutions to avoid Cascading Rollbacks :


• Strict Two Phase Locking Protocol
• Rigorous Two Phase Locking Protocol

10/17/7
ICT3212 Advanced Database Management Systems 2020
2PL – Limitations
• Deadlocks : occurs when each transaction T in a set of two or more
transactions is waiting for some item that is locked by some other
transaction T’ in the set.
• Waiting queue is maintained.
• Both T and T’ are waiting until the other release the lock.

10/17/8
ICT3212 Advanced Database Management Systems 2020
2PL – Limitations

T1 T2
Read_lock(Y)
Read_item(Y)
Read_lock(X)
Read_item(X)
T1 is waiting until the T2 lock on X Write_lock(X)
is released. T2 is waiting until the T1 lock on Y
Write_lock(Y) is released.
10/17/9
ICT3212 Advanced Database Management Systems 2020
2PL Types

• Due to the issues with 2-PL such as Cascading rollbacks and


Deadlocks some enhancements/modifications have been made
on 2-PL to improve it. There are three categories:
• ₋ Strict 2-PL
• ₋ Rigorous 2-PL
• ₋ Conservative 2-PL

10/17/
10
ICT3212 Advanced Database Management Systems 2020
2PL Types

Strict 2PL (most popular)


• Does not release exclusive (write) locks until after it commits or
aborts.
• Hence, no other transaction can read or write an item that is written
by T unless T has committed, leading to a strict schedule.
• Strict 2-PL ensures that the schedule is:
₋ Recoverable
₋ Cascadeless
• Not deadlock free.

10/17/
11
ICT3212 Advanced Database Management Systems 2020
2PL Types

10/17/
12
ICT3212 Advanced Database Management Systems 2020
2PL Types
Conservative 2PL/Static 2PL
• Requires a transaction to lock all the items it accesses before the
transaction begins execution, by pre declaring its read-set and write-
set. Hence this protocol does not have a Growing Phase.
• If any item can not be locked, does not lock any.
• Deadlock free; difficult to use.
• Releasing locks has no restrictions.
• Still face drawbacks like Cascading Rollbacks.

10/17/
13
ICT3212 Advanced Database Management Systems 2020
2PL
Types T1 T1
T1
Lock-s(A) Lock-s(A) Lock-s(A)
Read(A) Read(A) Lock-x(B)
Lock-x(B) Lock-x(B) Read(B)
Unlock(A) Read(B) Write(B)
Read(B) Write(B) Read(A)
Write(B) commit Unlock(A)
commit Unlock(B) commit
Unlock(B) Unlock(A) Unlock(B)
Strict 2PL: Exclusive locks are Rigorous: unlock all the locks Conservative: obtain all the
unlocked after commit. after commit. locks before starting the
transaction.

10/17/
14
ICT3212 Advanced Database Management Systems 2020
Deadlocks

10/17/
15
ICT3212 Advanced Database Management Systems 2020
Handling Deadlocks

Two main methods


• Deadlock prevention
• Deadlock detection & recovery

10/17/
16
ICT3212 Advanced Database Management Systems 2020
Deadlock Detection

• Can be used to detect any deadlock situation in advance.


• Wait for Graphs
• Track if any deadlock situation may arise.
• For each transaction entering into the system, a node is created.
• When a transaction Ti requests for a lock on an item X, which is held
by some other transaction Tj, a directed edge is created from Ti to Tj.
• If Tj releases item X, the edge between them is dropped and Ti locks
the data item.
• The system keeps checking if there's any cycle in the graph.

10/17/
17
ICT3212 Advanced Database Management Systems 2020
1
0
/
1

Deadlock Detection
7
/
2
0
2
0

Precedence graph Wait-for Graph


• Each transaction is a vertex  Each transaction is a vertex
• Arcs from T1 to T2 if  Arcs from T2 to T1 if
• T1 reads X before T2 writes X  T1 read-locks X then T2 tries to write-lock it
• T1 writes X before T2 reads X  T1 write-locks X then T2 tries to read-lock it
• T1 writes X before T2 writes X  T1 write-locks X then T2 tries to write-lock it

18
ICT3212 Advanced Database Management Systems
Deadlock Detection

10/17/
19
ICT3212 Advanced Database Management Systems 2020
Example

T1 Read(X)
T2 Read(Y)
T1 Write(X)
T2 Read(X)
T3 Read(Z)
X X
T3 Write(Z)
T1 Read(Y) Y

T3 Read(X)
T1 Write(Y)

10/17/
20
ICT3212 Advanced Database Management Systems 2020
Wait-for Graph

10/17/
21
ICT3212 Advanced Database Management Systems 2020
Wait-for Graph

10/17/
22
ICT3212 Advanced Database Management Systems 2020
Wait-for Graph

10/17/
23
ICT3212 Advanced Database Management Systems 2020
Deadlock Detection

Timeouts
• If a transaction waits for a period longer than a system-defined timeout
period, the system assumes that the transaction may be deadlocked and
aborts it.
• Do not check whether a deadlock actually exists or not.

10/17/
24
ICT3212 Advanced Database Management Systems 2020
Issues in Deadlock Recovery
I. Issue of choosing a victim – determine which transaction(s) among a
set of deadlocked transactions to roll back to break the deadlock.
II. Issue of rollback operation - determine how far the chosen victim
transaction should be rolled backed (total or partial).
III. Issue of starvation - avoid a situation where some transaction may
always be chosen as the victim due to selections based on cost
factors. This may prevent the transaction from ever completing its job.

10/17/
25
ICT3212 Advanced Database Management Systems 2020
Starvation
• Occurs when a transaction cannot proceed for an indefinite period of
time while other transactions in the system continue normally.
• Reasons
- if the waiting scheme for locked items is unfair, giving priority to some
transactions over others.
• a first-come-first-served queue
• allows some transactions to have priority over others but increases
the priority of a transaction the longer it waits, until it eventually
gets the highest priority and proceeds.

10/17/
26
ICT3212 Advanced Database Management Systems 2020
Starvation
- The algorithm selects the same transaction as victim repeatedly, thus
causing it to abort and never finish execution.
• The algorithm can use higher priorities for transactions that have
been aborted multiple times to avoid this problem.
• The wait-die and wound-wait schemes (under timestamp ordering)
avoid starvation, because they restart a transaction that has been
aborted with its same original timestamp, so the possibility that the
same transaction is aborted repeatedly is less.

10/17/
27
ICT3212 Advanced Database Management Systems 2020
Deadlock Prevention Protocols
• Conservative two-phase locking
₋ Requires that every transaction lock all the items it needs in
advance (generally not a practical assumption)
₋ If any of the items cannot be obtained, none of the items are
locked. The transaction waits and then tries again to lock all the
items it needs.
• Timestamp ordering mechanism
• No waiting (NW) algorithm
• Cautious waiting (CW) algorithm

10/17/
28
ICT3212 Advanced Database Management Systems 2020
Timestamp ordering

• These techniques use the concept of transaction timestamp TS(T),which


is a unique identifier assigned to each transaction.
• The timestamps are typically based on the order in which transactions
are started; hence, if transaction T1 starts before transaction T2, then
TS(T1) < TS(T2).
• Schemes that prevent deadlock are
- wait-die
- wound-wait

10/17/
29
ICT3212 Advanced Database Management Systems 2020
Wait-Die Scheme

• Check if TS(Ti) < TS(Tj) - If Ti is the older transaction and Tj has held
some resource, then Ti is allowed to wait until the data-item is
available for execution. That means if the older transaction is waiting
for a resource which is locked by the younger transaction, then the
older transaction is allowed to wait for resource until it is available.

• Check if TS(Ti) < TS(Tj) - If Ti is older transaction and has held some
resource and if Tj is waiting for it, then Tj is killed and restarted later
with the random delay but with the same timestamp.

10/17/
30
ICT3212 Advanced Database Management Systems 2020
Wound-Wait Scheme
In wound wait scheme, if the older transaction requests for a resource
which is held by the younger transaction, then older transaction forces
younger one to kill the transaction and release the resource. After the
minute delay, the younger transaction is restarted but with the same
timestamp.

If the older transaction has held a resource which is requested by the


Younger transaction, then the younger transaction is asked to wait until
older releases it.

10/17/
31
ICT3212 Advanced Database Management Systems 2020
Timestamp ordering

Both schemes end up aborting the younger of the two transactions that
may be involved in a deadlock.
The two techniques are deadlock-free since,
- in wait-die, transactions only wait on younger transactions so no
cycle is created.
- in wound-wait, transactions only wait on older transactions so no
cycle is created.
However, both techniques may cause some transactions to be aborted
and restarted needlessly, even though those transactions may never
actually cause a deadlock.

11/2/1
ICT3212 Advanced Database Management Systems 2020
No waiting (NW) Algorithm
• If a transaction is unable to obtain a lock, it is immediately aborted
and then restarted after a certain time delay without checking
whether a deadlock will actually occur or not.
• No transaction ever waits, so no deadlock will occur.
• This scheme can cause transactions to abort and restart needlessly.

11/2/2
ICT3212 Advanced Database Management Systems 2020
Cautious waiting (CW) Algorithm
• Objective is to reduce the number of needless aborts/restarts.
• Search and Learn more about this algorithm

11/2/3
ICT3212 Advanced Database Management Systems 2020
Timestamp Ordering
• Using transaction timestamps to order transaction execution for an
equivalent serial schedule.
• Timestamp is a unique identifier to identify a transaction.
• Equivalent to transaction start.
• Timestamp of transaction T as TS(T).
• Do not use locks therefore no deadlocks.

11/2/4
ICT3212 Advanced Database Management Systems 2020
Timestamp Ordering

• Generating Timestamps
• Counter
- use a counter that is incremented each time its value is assigned to
a transaction.
• System Clock
- use the current date/time value of the system clock and ensure
that no two timestamp values are generated during the same tick
of the clock.

11/2/5
ICT3212 Advanced Database Management Systems 2020
Timestamp Ordering Algorithm

• Idea for this scheme is to order the transactions based on their


timestamps.
• A schedule in which the transactions participate is then serializable, and
the only equivalent serial schedule permitted has the transactions in
order of their timestamp values.
• This is called timestamp ordering (TO).
• The algorithm must ensure that, for each item accessed by conflicting
operations in the schedule, the order in which the item is accessed does
not violate the timestamp order.

11/2/6
ICT3212 Advanced Database Management Systems 2020
Timestamp Ordering Algorithm
• Transaction Ti is assigned a unique timestamp TS(Ti) before starting
executing the transaction
• Next Transaction Tj is assigned TS(Tj) where TS(Ti) < TS(Tj)
• To do the above, the algorithm associates with each database item X
two timestamp (TS) values.

11/2/7
ICT3212 Advanced Database Management Systems 2020
Basic Timestamp Ordering

• Whenever some transaction T tries to issue a read_item(X) or a


write_item(X) operation, the basic TO algorithm compares the timestamp
of T with read_TS(X) and write_TS(X) to ensure that the timestamp order
of transaction execution is not violated.
• If this order is violated, then transaction T is aborted and resubmitted to
the system as a new transaction with a new timestamp.
• If T is aborted and rolled back, any transaction T1 that may have used a
value written by T must also be rolled back.
• Similarly, any transaction T2 that may have used a value written by T1
must also be rolled back (cascading rollback).
11/2/8
ICT3212 Advanced Database Management Systems 2020
Basic Timestamp Ordering
• Advantages of Basic TO Algorithm
– Schedules are serializable (like 2PL protocols)
– No waiting for transaction, thus, no deadlocks.
• Disadvantages
– Schedule may not be cascade free, and may not even be recoverable
(read uncommit data)

Solution: Utilize Strict TO


Algorithm
– Starvation is possible (if the same transaction is continually aborted and
restarted.)

11/2/9
ICT3212 Advanced Database Management Systems 2020
Strict Timestamp Ordering

• Ensures that the schedules are both strict (for easy recoverability) and
(conflict) serializable.
• A transaction T that issues a read_item(X) or write_item(X) such that
TS(T) > write_TS(X) has its read or write operation delayed until the
transaction T’ that wrote the value of X (hence TS(T’) = write_TS(X)) has
committed or aborted.
• This algorithm does not cause deadlock, since T waits for T’ only if TS(T)
> TS(T’).

11/2/10
ICT3212 Advanced Database Management Systems 2020
Thomas’s Write Rule
• Modification of the Basic TO
• Rejects fewer write operations by modifying the checks for the
write_item(X) operation as follows:
i) If read_TS(X) > TS(T) then abort and roll-back T and reject the
operation.
ii) If write_TS(X) > TS(T), then just ignore the write operation and
• continue execution. This is because the most recent writes counts
in case of two consecutive writes.
iii) If the conditions given in (i) and (ii) above do not occur, then
execute write_item(X) of T and set write_TS(X) to TS(T).
11/2/11
ICT3212 Advanced Database Management Systems 2020
Multiversion technique based on timestamp
ordering
• This approach maintains a number of versions of a data item and
allocates the right version to a read operation of a transaction.
• Thus unlike other mechanisms a read operation in this mechanism is
never rejected.
Disadvantage:
– Significantly more storage is required to maintain multiple versions.
– To check unlimited growth of versions, a garbage collection is run
periodically.

11/2/12
ICT3212 Advanced Database Management Systems 2020
Multiversion Technique Based on Timestamp
Ordering
• Assume X1, X2, …, Xn are the version of a data item X created by a write
operation of transactions.
- Note: New version of Xi is created only by a write operation.
- With each Xi a Read_TS(read timestamp) and a Write_TS(write
timestamp) are associated.

11/2/13
ICT3212 Advanced Database Management Systems 2020
Multiversion Technique Based on Timestamp
Ordering
• Read_TS(Xi): The read timestamp of Xi is the largest of all the
timestamps of transactions that have successfully read version Xi.
• Write_TS(Xi): The write timestamp of Xi is the largest of all the
timestamps of transactions that have successfully written the value of
version Xi.
• Basic Idea: Works much like Basic TO with the difference that instead of
Write_TS(X) and Read_TS(X), the highest Write_TS(Xi) and highest
Read_TS(Xi) are utilised respectively.

11/2/14
ICT3212 Advanced Database Management Systems 2020
SQL Support for Locking

Concurrency control is enforced using locking:


• Database level
• Table level
• Page level
• Row level
• Key level

11/2/15
ICT3212 Advanced Database Management Systems 2020
SQL Support for Locking
Database Level Locking:
• Other users cannot access database. Database stores exclusive.
• It can be used when executing a large number of updates involving
many tables;
• archiving the database files for backups;
• altering the structure of the database.

11/2/16
ICT3212 Advanced Database Management Systems 2020
SQL Support for Locking
Table Level Locking
• Other users cannot modify the table.
• It can be used to:
• avoid conflict with other users during batch operations that
affects most or all of the rows of a table;
• prevent users from updating a table for a period of time;
• prevent access to a table while altering its structure or creating
indexes

11/2/17
ICT3212 Advanced Database Management Systems 2020
Table Level Locking
• Table Level Locking in Share Mode: Others may SELECT from the table.
SQL Syntax:
• LOCK TABLE table_name IN SHARE MODE
• Table Level Locking in Exclusive Mode: Others may not SELECT from the
table.
SQL Syntax:
• LOCK TABLE table_name IN EXCLUSIVE MODE

11/2/18
ICT3212 Advanced Database Management Systems 2020
Unlocking / Locking
Unlocking a Table
SQL Syntax:
• UNLOCK TABLE table_name
Setting the Lock Mode:
• Wait forever for the lock to be released.
SQL Syntax:
• SET LOCK MODE TO WAIT
• Do not wait for lock to be released.
SQL Syntax:
• SET LOCK MODE TO NOT WAIT
• Wait 20 seconds for lock to be released.
SQL Syntax:
• SET LOCK MODE TO WAIT 20

11/2/19
ICT3212 Advanced Database Management Systems 2020

You might also like