Skip to content

Commit 93d899a

Browse files
authored
Entry cache pinned entries feature (#20)
* Pinned entries in entry cache Add design document for Entry cache pinned entries feature
1 parent a480977 commit 93d899a

2 files changed

Lines changed: 181 additions & 0 deletions

File tree

docs/389ds/design/design.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ If you are adding a new design document, use the [template](design-template.html
4141

4242
- [Session Tracking Control client - replication](session-identifier-clients.html)
4343
- [MemberOf Plugin Specific Group Scoping](memberof-specific-group-scoping-design.html)
44+
- [Entry cache pinned entries - limit large groups eviction](entrycache-pinned.html)
4445

4546
## 389 Directory Server 3.0
4647

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
# Improving the entry cache eviction
2+
3+
## Why changing things ?
4+
5+
Computing the acl involving large groups is costly. So we had better keep them in the acl cache. Some of the reason are:
6+
7+
- creating acl cache for large group is costly a group is evicted out of the acl if its entry pointer changed. (i.e that the group was removed from the entry cache then reloaded)
8+
9+
- If values are not already sorted, the time spent to sort the membership values is significant.
10+
11+
A way to improvie thing is to try to keep the large group entries in the entry cache as long as possible.
12+
13+
## Changes
14+
15+
* The main idea is to add a list holding a configured number of entries having the highest weight (based upon load time and group size)
16+
that pin the entries instead of storing them in the LRU where they could be evicted.
17+
* Handling a new flag in the entries for PINNED entries to avoid looking in pinned entries list for not pinned entries
18+
* Refactor the monitoring data to use a struct instead of lots of paramaters
19+
* Expose average entry load time (in microseconds) and pinned-entry metrics in cache monitoring output.
20+
21+
## New config parameters
22+
23+
New parameters in the backend config entry
24+
25+
| Parameter | Type | Default value | Description | Comments |
26+
|:------------------------------- |:------------ |:------------- |:------------------------------------------------------------------------------------------------------------------------- | ------- |
27+
| nsslapd-cache-preserved-entries | Integer >= 0 | 10 | Number of entries that are preserved from eviction | Should stay reasonably small because pinned entries consume memory that cannot be used for something else |
28+
| nsslapd-cache-debug-pattern | String | NULL | Debug feature allowing to log INFO message when an entry whose dn matches the value is added/removed from the entry cache | Intended for the CI tests and not really usefull for the users |
29+
30+
31+
## Internal Data changes
32+
33+
| Struct | field name | Description |
34+
|:-------------- |:------------------------- |:----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
35+
| backentry | ep\_weight | Entry weight (load time in microseconds) |
36+
| cache | c\_stats(\*) | cache\_stats statistics |
37+
| cache | c\_lrus[2] | LRU queues anchor |
38+
| cache | c\_inst | ldbm\_instance struct (to access the config parameters) |
39+
| ldbm\_instance | cache\_preserved\_entries | number of entries to preserve |
40+
| ldbm\_instance | cache\_debug\_pattern | nsslapd-cache-debug-pattern value (may be NULL): Regular expression used to log INFO messages in error log when adding/removing entries in cache (if their dn matches) |
41+
| ldbm\_instance | cache\_debug\_re | Compiled version of cache\_debug\_pattern |
42+
43+
(\*) All statistics fields of struct cache are moved in new cache\_stats struct
44+
45+
(\*\*) Using the generic word **weight** instead of loadingtime because its meaning may change
46+
47+
#### Entry Cache statistics
48+
49+
struct cache_stats
50+
51+
| Field | Description |
52+
| ------------------- | ----------------------------------------- |
53+
| uint64\_t hits | for analysis of hits/misses |
54+
| uint64\_t tries | for analysis of hits/misses |
55+
| uint64\_t nentries | current \# entries in cache |
56+
| int64\_t maxentries | max entries allowed (-1: no limit) |
57+
| uint64\_t size | current size in bytes |
58+
| uint64\_t maxsize | max size in bytes |
59+
| uint64\_t weight | Total weight of all entries |
60+
| uint64\_t nehw | current \# entries having weight in cache |
61+
62+
## Test
63+
dirsrvtests/tests/suites/features/entrycache_eviction_test.py verifying pinned-entry caching and eviction thresholds.
64+
65+
## Alternatives
66+
67+
### Alternatives rejected after testing them
68+
69+
These alternatives have been implemented and rejected because of the tests results:
70+
71+
#### LRU eviction algorithm version 1
72+
73+
##### Implementation
74+
75+
Entry weight is the elapsed time (in microseconds) while loading the entry in the cache.
76+
Have a configurable factor
77+
While evicting the entries from the cache keep all entry whose loading time is higher than the configurable factor multiplied by the average loading time. (The idea behind that is that if the entry is long to load we want to try to keep it)
78+
We can store (in the backentry) the time needed to compute an entry when reading it if not in the cache (and probably also when adding it in the cache too)
79+
(Simply by getting current time when getting the original backentry then just before replacing it.) and compute statistics like the average time (by keeping the number of entry (which is already done) and the total time of all the entries that are in the cache) and determine a threshold (tunable ?) about when we keep an entry in the cache
80+
IMHO a 100 or 1000 factor will do .
81+
Comparing average time to entry time has an advantage: big group entries get naturally evicted if too many of them are in the cache (because in such case, the average building time will then increase until some of the protected entry are no more protected from eviction)
82+
83+
##### Rejection cause:
84+
85+
But after the first tests I discovered that it is pretty hard to determine the right value for the threshold to get reliable results. The value is between 1.2 and 1.5
86+
87+
#### LRU eviction algorithm version 2
88+
89+
##### Implementation
90+
91+
While evicting the entries:
92+
93+
- build an array of preserved entries.
94+
95+
- Taking the first n entries in LRU queue walking the LRU queue from its head.
96+
- Sorting them
97+
98+
- while walking the entry to evict (stating from the queue tail):
99+
100+
- insert the costly entry in the array if neeeded.
101+
At the end put back the remainding entries in the head of LRU (which tend to sort the LRU queue by putting the hight weight around the head (and avoid trying to evict them)
102+
103+
##### Rejection reason
104+
105+
Behavior is more predictable than first version but moving the entries has some drawback:
106+
107+
- some entries that should be eveicted are no more evicted (i.e in practice more than n entries get preserved
108+
109+
#### Weight computation version 1
110+
111+
##### Implementation
112+
113+
My first trials were done using the elapsed time while loading the entry in micro seconds as weight
114+
115+
##### Rejection reason
116+
117+
Noise caused by the cpu context switch makes too many regular entry having higher load time than the large groups
118+
119+
### Alternatives considered but rejected
120+
121+
Some other alternatives has been rejected without being impmlemented
122+
123+
##### Use a flag instead of a weight
124+
125+
##### Implementation
126+
127+
Having a flag for large groups in the backentry and simply skip them.
128+
129+
##### Rejection reason
130+
131+
Cannot prioritize which group will be preserved (between two large groups) and could get in trouble if there are two many large groups. (no more spaces for regular entries)
132+
133+
#### Using getrusage instead of clock_gettime
134+
135+
##### Implementation
136+
137+
Using getrusage(RUSAGE\_THREAD, \&usage) instead of clock\_gettime
138+
139+
##### Rejection reason
140+
141+
Although it provides more accurate data and limit the cpu context switch noise, getrusage is 10 times more costly than clock\_gettime.
142+
143+
#### Using weight variance to decide if evicting the entry
144+
145+
##### Implementation
146+
147+
checking if w \> a+y\*v would allow to preserve x% of the entries
148+
149+
##### Rejection reason
150+
151+
it seems an overhead especially since we need to compute a square root to do that. maybe checking w2 \> a2\+y\*v2 will do something but I am not convinced
152+
153+
#### Using number of members as weight
154+
155+
##### Implementation
156+
157+
Use the number of members as weight instead of the loading time.
158+
159+
##### Rejection reason
160+
161+
We may perhaps also preserve some other complex entries
162+
163+
#### Pinning group entries by their dn
164+
165+
##### Implementation
166+
167+
Have a parameter holding a list of DN and never put the entries having these dn
168+
in the LRU (or skip these DN while evicting entries)
169+
170+
##### Rejection reason
171+
172+
- Comparing DNs may slow things especially if the list contains more than a few dns
173+
- Admin have to explicitly specifies the groups list
174+
175+
#### Handling pinned entries within the LRU
176+
##### Implementation
177+
Embedding the handling of the pinned entries within the LRU code as originally intented
178+
179+
##### Rejection reason
180+
Would make more difficult the replacement of LRU by something more efficient like ARC

0 commit comments

Comments
 (0)