Multi-Version Concurrent Data Structures�
PANAGIOTA FATOUROU
University of Crete, Department of Computer Science
Foundation for Research and Technology - Hellas
School on the Practice and Theory of Distributed Computing, November 2023
Multiversion Objects
SPTDC 2023 PANAGIOTA FATOUROU
5
10
Current version
Write(10)
5
Current version
Write(8)
8
10
Current version
5
2
Multiversioning
SPTDC 2023 PANAGIOTA FATOUROU
Database systems
3
SPTDC 2023 PANAGIOTA FATOUROU
Many applications require querying large portions or multiple parts of the data structure.
Big-data applications use shared in-memory tree-based data indices
Why Multiversioning?
4
Why multivesion Concurrent Data Structures?
SPTDC 2023 PANAGIOTA FATOUROU
Concurrent Data Structure built out of CAS objects
Take a snapshot of the data structure
Answer multi-point Queries using snapshot
The vCAS technique
Snapshot: Saves a read-only version of the state of the data structure at a single point in time. [An atomic view of the state of the data structure.]
5
Background Knowledge
SPTDC 2023 PANAGIOTA FATOUROU
6
Model
SPTDC 2023 PANAGIOTA FATOUROU
Thread 1
Thread 2
Thread n
…
shared variables
ATOMIC boolean Compare&Swap(� Variable V, Value vold, Value vnew) { � � if (V == vold) { V = vnew; return TRUE; } � return FALSE; �}
7
Correctness [Herlihy & Wing]
Linearizability
SPTDC 2023 PANAGIOTA FATOUROU
8
Linearizability: Queue supporting ReadAll()
SPTDC 2023 PANAGIOTA FATOUROU
ReadAll()
*
*
time
?*
?*
?*
Deq()
Enq(3)
res= 1
ok
ok() -> {1,2}
Enq(1)
Enq(2)
ok
*
*
{1}
Queue:
{1, 2}
{2}
{2, 3}
*
9
SPTDC 2023 PANAGIOTA FATOUROU
ReadAll()
*
*
time
?*
?*
?*
X
Example of non-linearizable execution
Deq()
Enq(3)
res= 1
ok
{1,2,3}
Enq(1)
Enq(2)
ok
ok
*
*
Linearizability: Queue supporting ReadAll()
X
X
1
Head
2
Tail
res = {1,2}
{1}
Queue:
{1, 2}
{2}
{2, 3}
Set ReadAll() { // sequential alg
Node *q = Head;
Set res;
while (q != NULL) {
res = res ∪ {q->data};
q = q-> next; }
return res;
}
10
SPTDC 2023 PANAGIOTA FATOUROU
ReadAll()
*
*
time
X
Example of non-linearizable execution
Deq()
Enq(4)
res= 1
ok
{1,2,3}
Enq(1)
Enq(2)
ok
ok
*
*
Linearizability: Queue supporting ReadAll()
Head
2
3
Tail
Set ReadAll() {
Node *q = Head;
while (q != NULL) {
res = res ∪ {q->data};
q = q-> next; }
return res;
}
X
res = {1,2,3}
X
X
1
X
11
Progress
Non-blocking Algorithms
Wait-Freedom
Lock-Freedom
SPTDC 2023 PANAGIOTA FATOUROU
12
An Example of a Concurrent �Queue Implementation
SPTDC 2023 PANAGIOTA FATOUROU
13
Michael & Scott Queue as an Example
SPTDC 2023 PANAGIOTA FATOUROU
Dummy
Tail
Head
struct node {
T value ; // unmutable
CAS Object next : struct node *;
}
CAS objects Head, Tail: struct node *; // initially, both point
to a dummy node
14
SPTDC 2023 PANAGIOTA FATOUROU
Dummy
Tail
Head
struct node {
T value ; // unmutable
CAS Object next : struct node *;
}
CAS objects Head, Tail: struct node *; // initially, both point
to a dummy node
Thread 1
Enqueue(1)
Read
Read
Michael & Scott Queue as an Example
15
Michael & Scott Queue as an Example
SPTDC 2023 PANAGIOTA FATOUROU
Dummy
Tail
Head
1
struct node {
T value ; // unmutable
CAS Object next : struct node *;
}
CAS objects Head, Tail: struct node *; // initially, both point
to a dummy node
Thread 1
Enqueue(1)
CAS √
16
Michael & Scott Queue as an Example
SPTDC 2023 PANAGIOTA FATOUROU
Dummy
Tail
Head
1
struct node {
T value ; // unmutable
CAS Object next : struct node *;
}
CAS objects Head, Tail: struct node *; // initially, both point
to a dummy node
Thread 1
Enqueue(1)
17
SPTDC 2023 PANAGIOTA FATOUROU
Dummy
Tail
Head
1
struct node {
T value ; // unmutable
CAS Object next : struct node *;
}
CAS objects Head, Tail: struct node *; // initially, both point
to a dummy node
Thread 1
Enqueue(1)
CAS √
Michael & Scott Queue as an Example
18
SPTDC 2023 PANAGIOTA FATOUROU
Dummy
Tail
Head
1
struct node {
T value ; // unmutable
CAS Object next : struct node *;
}
CAS objects Head, Tail: struct node *; // initially, both point
to a dummy node
Michael & Scott Queue as an Example
19
SPTDC 2023 PANAGIOTA FATOUROU
Dummy
Tail
Head
1
2
Thread 2�Enqueue(2)
Thread 1
Enqueue(1)
CAS √
CAS X
Michael & Scott Queue as an Example
20
Michael & Scott Queue as an Example
SPTDC 2023 PANAGIOTA FATOUROU
Dummy
Head
1
2
Thread 2
Tail
Thread 1
CAS √
21
Michael & Scott Queue as an Example
SPTDC 2023 PANAGIOTA FATOUROU
Dummy
Head
1
2
Thread 2
Tail
22
Michael & Scott Queue as an Example
SPTDC 2023 PANAGIOTA FATOUROU
Dummy
Head
1
2
Thread 2
Tail
CAS √
23
Michael & Scott Queue as an Example
SPTDC 2023 PANAGIOTA FATOUROU
Dummy
Head
1
2
Thread 1
Tail
CAS √
24
Michael & Scott Queue as an Example
SPTDC 2023 PANAGIOTA FATOUROU
Dummy
Head
1
2
Tail
25
Michael & Scott Queue as an Example
SPTDC 2023 PANAGIOTA FATOUROU
Dummy
Head
1
2
Thread 2
Tail
Thread 1 �is slow
Read
Read
26
Michael & Scott Queue as an Example
SPTDC 2023 PANAGIOTA FATOUROU
Dummy
Head
1
2
Thread 2
Tail
Thread 1 �is slow
CAS √
27
Michael & Scott Queue as an Example
SPTDC 2023 PANAGIOTA FATOUROU
Dummy
Head
1
2
Thread 2
Tail
Thread 1�is slow
CAS √
28
Michael & Scott Queue as an Example
SPTDC 2023 PANAGIOTA FATOUROU
Dummy
Head
1
2
Thread 2
Tail
Thread 1�is slow
CAS √
29
Michael & Scott Queue as an Example
SPTDC 2023 PANAGIOTA FATOUROU
Dummy
Head
1
2
Tail
30
SPTDC 2023 PANAGIOTA FATOUROU
Head
Thread 1
Deq()
Read
Michael & Scott Queue as an Example
Dummy
1
2
Tail
stores 1 as its return value
31
Read
Read
SPTDC 2023 PANAGIOTA FATOUROU
Head
Thread 1
Deq()
Michael & Scott Queue as an Example
Dummy
1
2
Tail
CAS √
32
SPTDC 2023 PANAGIOTA FATOUROU
Head
Michael & Scott Queue as an Example
Dummy
Dummy
1
2
Tail
33
vCAS Technique
SPTDC 2023 PANAGIOTA FATOUROU
Lock-free Queue [Michael & Scott’96]
Enqueue, Dequeue
Lock-free Snapshottable Queue
Enqueue �Dequeue
Snapshot
Range Query
i-th element
All elements
Simple, General, Efficient!
Preserves parallelism and time bounds
O(1) time, a single CAS
Wait-free,
Linearizable
Works with many lock-free data structures, including:
Lock-free Concurrent Data Structure
Take a snapshot of the Data Structure
Answer multi-point Queries using snapshot
34
Overview of the VCAS Approach
SPTDC 2023 PANAGIOTA FATOUROU
CAS Object
Versioned CAS (vCAS) Object
Camera Object
Supports:
Supports:
Supports:
TakeSnapshot
Time Complexity:
O(1) time, small constant
wait-free
35
Makes it possible for a thread to later read only the memory locations it needs from shared memory, knowning that all such reads will be atomic.
Supporting Multi-Point Queries
SPTDC 2023 PANAGIOTA FATOUROU
ts
Reads 0
0
1
2
Reads 1
Reads 1
Query thread
2
36
Versioned CAS Implementation
SPTDC 2023 PANAGIOTA FATOUROU
VCAS Object
next
37
nextv
8
val
nextv
4
val
nextv
0
val
Current value of next: Pointer to next node of queue
VNode
VNode
VNode
List ordered by timestamps, most recent first.
Versioned CAS Implementation
SPTDC 2023 PANAGIOTA FATOUROU
VCAS Object
next
38
nextv
8
val
nextv
4
val
nextv
0
val
previous values of next field of node
Versioned CAS Implementation
SPTDC 2023 PANAGIOTA FATOUROU
ts
vCAS(X, old, new)
vRead(X)
takeSnapshot()
readVersion(X, t)
VCAS Object
39
8
next
nextv
8
val
nextv
4
val
nextv
0
val
Overview of the VCAS Approach: �Michael & Scott Queue as an Example
SPTDC 2023 PANAGIOTA FATOUROU
Dummy
Tail
nextv
0
val
Thread 1�Enqueue(1)
nextv
0
val
vRead
vRead
struct node {
T value ;
CAS Object next : struct node *;
}
CAS objects Head, Tail: struct node *;
struct node {
T value ;
vCAS Object next : struct node *;
}
vCAS objects Head, Tail: struct node *;
Head
nextv
0
val
0
ts
40
vRead(X)
Overview of the VCAS Approach: �Michael & Scott Queue as an Example
SPTDC 2023 PANAGIOTA FATOUROU
Dummy
Tail
nextv
0
val
1
Thread 1�Enqueue(1)
nextv
0
val
nextv
0
val
struct node {
T value ;
CAS Object next : struct node *;
}
CAS objects Head, Tail: struct node *;
struct node {
T value ;
vCAS Object next : struct node *;
}
vCAS objects Head, Tail: struct node *;
Head
nextv
0
val
0
ts
41
Overview of the VCAS Approach: �Michael & Scott Queue as an Example
SPTDC 2023 PANAGIOTA FATOUROU
Dummy
Tail
nextv
0
val
1
Thread 1�Enqueue(1)
vCAS √
nextv
0
val
nextv
0
val
nextv
-1
val
struct node {
T value ;
CAS Object next : struct node *;
}
CAS objects Head, Tail: struct node *;
struct node {
T value ;
vCAS Object next : struct node *;
}
vCAS objects Head, Tail: struct node *;
Head
nextv
0
val
0
ts
42
vCAS(X, old, new)
Overview of the VCAS Approach: �Michael & Scott Queue as an Example
SPTDC 2023 PANAGIOTA FATOUROU
Dummy
Tail
struct node {
T value ;
CAS Object next : struct node *;
}
CAS objects Head, Tail: struct node *;
struct node {
T value ;
vCAS Object next : struct node *;
}
vCAS objects Head, Tail: struct node *;
nextv
0
val
1
Thread 1�Enqueue(1)
nextv
0
val
nextv
0
val
nextv
0
val
Head
nextv
0
val
0
ts
43
vCAS(X, old, new)
Overview of the VCAS Approach: �Michael & Scott Queue as an Example
SPTDC 2023 PANAGIOTA FATOUROU
Dummy
Tail
nextv
0
val
1
Thread 1
nextv
0
val
nextv
0
val
nextv
0
val
vCAS √
nextv
0
val
Head
nextv
0
val
ts
0
44
Overview of the VCAS Approach: �Michael & Scott Queue as an Example
SPTDC 2023 PANAGIOTA FATOUROU
Dummy
Tail
nextv
val
1
Thread 1
nextv
0
val
nextv
0
val
nextv
0
val
Head
nextv
ts
val
ts
0
45
nextv
0
val
nextv
0
val
Overview of the VCAS Approach: �Michael & Scott Queue as an Example
SPTDC 2023 PANAGIOTA FATOUROU
Dummy
Tail
nextv
0
val
1
nextv
0
val
nextv
0
val
nextv
0
val
nextv
0
val
2
Thread 2
Enqueue(2)
nextv
ts
val
3
Thread 3
Enqueue(3)
nextv
ts
val
Thread 4
Head
nextv
ts
val
deq()
ts
0
46
Overview of the VCAS Approach: �Michael & Scott Queue as an Example
SPTDC 2023 PANAGIOTA FATOUROU
Version list of 1->next
Version list of Dummy-> next
Dummy
Tail
nextv
ts
val
0
0
1
nextv
ts
val
0
0
2
0
0
3
0
0
nextv
ts
val
nextv
ts
val
Head
nextv
ts
val
0
0
ts
0
47
nextv
ts
0
0
0
0
val
Version list of Tail
Version list �of Head
Version list of 2->next
Version list of 3->next
Multi-Point Queries:�Michael & Scott Queue as an Example
SPTDC 2023 PANAGIOTA FATOUROU
3
Thread 3
Enqueue(3)
nextv
ts
val
Thread 4
ts
ReadAll()
Dummy
Tail
nextv
ts
val
0
0
1
nextv
ts
val
0
0
2
0
nextv
ts
val
Head
nextv
ts
val
0
deq()
0
48
nextv
ts
val
0
0
0
Takesnapshot → 0
1
Multi-Point Queries: �Michael & Scott Queue as an Example
SPTDC 2023 PANAGIOTA FATOUROU
Dummy
nextv
ts
val
0
0
Dummy
nextv
ts
val
0
0
2
0
1
3
1
nextv
ts
val
nextv
ts
val
Head
nextv
ts
val
0
1
ts
1
49
Tail
0
0
0
1
Multi-Point Queries: �Michael & Scott Queue as an Example
SPTDC 2023 PANAGIOTA FATOUROU
ReadAll()
Dummy
Tail
nextv
ts
val
0
0
1
nextv
ts
val
0
0
2
0
1
3
1
nextv
ts
val
nextv
ts
val
Head
nextv
ts
val
0
1
ts
1
50
0
0
0
1
Set ReadAll() {
Node *q = Head;
while (q != NULL) {
res = res ∪ {q->data};
q = q-> next; }
return res;
}
SPTDC 2023 PANAGIOTA FATOUROU
ReadAll()
*
*
time
X
Example of non-linearizable execution
Deq()
Enq(4)
res= 1
ok
{1,2,3}
Enq(1)
Enq(2)
ok
ok
*
*
Linearizability: Queue supporting ReadAll()
Head
2
3
Tail
Set ReadAll() {
Node *q = Head;
while (q != NULL) {
res = res ∪ {q->data};
q = q-> next; }
return res;
}
X
res = {1,2,3}
X
X
1
X
51
Multi-Point Queries: �Michael & Scott Queue as an Example
SPTDC 2023 PANAGIOTA FATOUROU
ReadAll()
It returns {1,2}
Dummy
Tail
nextv
0
val
nextv
ts
val
nextv
0
val
nextv
0
val
nextv
1
val
0
0
1
nextv
ts
val
0
0
2
0
1
3
1
nextv
ts
val
nextv
ts
val
Head
nextv
ts
val
0
1
ts
1
52
readVersion(X, t)
Overview of the VCAS Approach: �Michael & Scott Queue as an Example
SPTDC 2023 PANAGIOTA FATOUROU
void enq(T value ) {
NODE *next , *last ;
1. NODE *p = newcell(NODE) ;
// p->value = value ; p->next = NULL;
4. while (TRUE) {
5. last = Tail ;
7. if (last != Tail) continue;
12. if (CAS( , NULL , p)) break ;
13. }
14. CAS( , last, p );
}
Tail
last->next
Tail
void enq(T value ) {
NODE *next , *last ;
1. NODE *p = new(NODE, value, NULL) ;
4. while (TRUE) {
5. last = vRead(Tail) ;
7. if (last != vRead(Tail)) continue;
12. if (vCAS( , NULL , p)) break ;
13. }
14. vCAS( , last, p );
}
Tail
last->next
Tail
53
Versioned CAS on BSTs
SPTDC 2023 PANAGIOTA FATOUROU
A
B
C
D
E
A
5
5
4
B’
B
3
C
C’
5
D
5
E
root
root
2
1
A’
Snapshottable BST
Expanding out VCAS objects
Timestamps
54
Examples of queries
Multi-point Queries
SPTDC 2023 PANAGIOTA FATOUROU
55
Comparison with Existing Techniques
SPTDC 2023 PANAGIOTA FATOUROU
Generality
Efficiency
LFCA [Winbland et al., SPAA’18]
KiWi [Basil et al., PPoPP’17]
PNB-BST [Fatourou et al., SPPA’19]
SnapTree [Bronson et al., PPoPP’10]
Epoch RQs [Arbel, Raviv et al., PPoPP’18]
SnapCollector [Petrank et al., PPoPP’13]
STM [Fernandez et al., PPoPP’11]
The VCAS Approach, Wei et al.
56
Practical Optimizations
SPTDC 2023 PANAGIOTA FATOUROU
57
Avoiding Indirection
SPTDC 2023 PANAGIOTA FATOUROU
A
B
C
D
E
A
5
5
4
B’
B
3
C
C’
5
D
5
E
root
root
2
1
A’
Snapshottable BST
Expanding out VCAS objects
Without indirection
A, 2
B, 5
C, 5
D, 5
E, 5
root
A’, 1
B’, 4
C’, 3
Merge version list and
data structure nodes
58
Experimental Evaluation
SPTDC 2023 PANAGIOTA FATOUROU
59
Summary of vCAS Technique
SPTDC 2023 PANAGIOTA FATOUROU
60
Multi-Version Garbage Collection�
ANY SYSTEM THAT MAINTAINS MULTIPLE VERSIONS OF EACH OBJECT NEEDS A WAY OF EFFICIENTLY RECLAIMING THEM!
SPTDC 2023 PANAGIOTA FATOUROU
61
Research Question
SPTDC 2023 PANAGIOTA FATOUROU
How do we garbage collect, efficiently, for multiversion data structures?
62
Ben-David, Blelloch, Fatourou, Ruppert, Wei, DISC 2021
A general Multiversion Garbage Collection (GC) scheme with the following properties:
SPTDC 2023 PANAGIOTA FATOUROU
Previous solutions either use:
63
Multiversion Garbage Collection (MVGC)
SPTDC 2023 PANAGIOTA FATOUROU
X
Y
Objects
Versions
Maintaining all old versions ⇒ high memory usage
64
time
Which Versions are Needed?
SPTDC 2023 PANAGIOTA FATOUROU
Time
X
Y
Objects
Versions
Timestamps of multipoint queries
Most recent versions needed
Versions needed by read-only operations
65
Related Work – Epoch-Based Solutions
SPTDC 2023 PANAGIOTA FATOUROU
X
Operations started before this point have completed
Safe to collect
66
Related Work – Epoch-Based Solutions
Pros: Fast, easy to implement
SPTDC 2023 PANAGIOTA FATOUROU
X
67
Related Work – Other Solutions
SPTDC 2023 PANAGIOTA FATOUROU
68
What is the problem to solve?
Step 1: Identify obsolete versions
Step 2: Unlink from version list
Step 3: Reclaim memory of unlinked versions
SPTDC 2023 PANAGIOTA FATOUROU
69
Step 1: Identify obsolete versions
SPTDC 2023 PANAGIOTA FATOUROU
X
Y
70
Range Tracker
O(1) amortized time
O(needed) space
Unneeded Versions
Active multi-point queries
Obsolete Versions
Step 2: Unlink from version list
SPTDC 2023 PANAGIOTA FATOUROU
X
Y
n
unneeded
Need parent to unlink
We present a wait-free, amortized O(1) algorithm for remove()
71
Step 3: Reclaim memory of unlinked versions
SPTDC 2023 PANAGIOTA FATOUROU
X
n
P1
72
Step 1: Identifying Obsolete Versions
SPTDC 2023 PANAGIOTA FATOUROU
Range tracker
X
Y
x1
x4
x6
y2
y4
x7
y8
x5
y3
x2
y1
x3
y7
x6
Triplet [version, beginTS, endTS]
73
| 3 | | | 7 | |
Announcement Array
deprecate(z5, startTS, endTS)
z5
Add new triplet & return obsolete versions
Amortized O(1) time
Observation: Triplets added by the same process have increasing endTS.
Step 1: Identifying Obsolete Versions
Question: Given a triplet T and a sorted announcement array, how long does it take to check if T is obsolete?
SPTDC 2023 PANAGIOTA FATOUROU
0 | 3 | 4 | 5 | 9 | 20 |
Sorted Announcement Array
z5
length = P (i.e. number of processes)
Triplet
Z5, 7, 12 |
=
Binary search for endTS
Compare with startTS
O(log P) time
74
Step 1: Identifying Obsolete Versions
Question: What if you were given a batch of O(P) triplets sorted by end timestamp?
SPTDC 2023 PANAGIOTA FATOUROU
1 | 3 | 4 | 5 | 9 | 20 |
Sorted Announcement Array
length = P (i.e. number of processes)
Batch of O(P) Triplets
Z5, 2, 8 | X3, 7, 12 | … | Y7, 10, 18 |
Merge()
O(P) time
Takes O(P) time
O(P log P) time
Takes O(P log P) time
Batch of O(P log P) Triplets
75
Range Tracker: Implementation
SPTDC 2023 PANAGIOTA FATOUROU
P1
Local list of triplets
Shared Queue
7 | 3 | 20 | | 9 | 5 |
Announcement Array
| | | | |
| | | | |
| | | | |
…
| | | | |
Push()
Size at most O(P log P)
Pop() x2
| | | | |
| | | | |
Merge
Scan & Sort
| | |
| | | | |
Needed
Obsolete
Returned by deprecate
76
Step 2: Unlink from version list
SPTDC 2023 PANAGIOTA FATOUROU
X
Y
n
Obsolete
Concurrent removes
77
Concurrent Removes
SPTDC 2023 PANAGIOTA FATOUROU
A
B
C
D
A
B
C
D
Remove(B)
Remove(C)
Linked list structure corrupted
78
Concurrent Doubly Linked List, BBF+
SPTDC 2023 PANAGIOTA FATOUROU
79
BBF+: Linked List Remove
SPTDC 2023 PANAGIOTA FATOUROU
4
3
4
2
4
3
4
4
1
3
4
2
4
3
Implicitly defined tree
Version list
4
1
2
2
3
3
3
3
4
4
4
4
4
4
4
4
x
y
z
4
Marked for deletion
3
4
4
3
Amortized O(1) time
Worst case O(log L) time
L = # of nodes appended to version list
80
Space Overhead
SPTDC 2023 PANAGIOTA FATOUROU
4
3
4
2
4
1
3
4
2
4
3
Implicitly defined tree
Version list
4
1
2
2
3
3
3
4
4
4
4
4
4
3
2
1
2
3
Marked for deletion
O(log L) factor space overhead!
L = # of nodes appended to version list
81
Splicing Out Internal Nodes
SpliceUnmarkedLeft(Y): requires X > Y > Z and X unmarked
SPTDC 2023 PANAGIOTA FATOUROU
SpliceUnmarkedRight(Y): requires X < Y < Z and Z unmarked
X
Y
Z
May or may not be marked
X
Y
Z
X
Y
Z
X
Y
Z
82
BBF+: Space and Time
SPTDC 2023 PANAGIOTA FATOUROU
| TryAppend (worst-case) | Remove (amortized) | Remove (worst-case) | Space |
BBF+ | O(1) | O(1) | O(log L), Wait-free | O(S + c log L) |
L = # of successful TryAppends
S = # of nodes appended but not removed
c = # of ongoing remove operations
83
Overall Results
Full version (with proof of correctness) available on arxiv:
https://arxiv.org/abs/2108.02775
SPTDC 2023 PANAGIOTA FATOUROU
84
New MVGC Schemes �[Wei, Blelloch, Fatourou, Ruppert, PPoPP 2023]
SPTDC 2023 PANAGIOTA FATOUROU
Concurrent remove()s
85
Results
SPTDC 2023 PANAGIOTA FATOUROU
86
Conclusions
SPTDC 2023 PANAGIOTA FATOUROU
87
Conclusions
SPTDC 2023 PANAGIOTA FATOUROU
88
Thank You!
SPTDC 2023 PANAGIOTA FATOUROU
89
This work was supported by the Hellenic Foundation for Research and Innovation (HFRI) under the “Second Call for HFRI Research Projects to support Faculty Members and Researchers” (project number: 3684, project acronym: PERSIST).
We are recruiting!