Skip to content

Commit 2572baf

Browse files
skorotkovalex-plekhanov
authored andcommitted
IGNITE-24992 Fix hang in put with RANDOM_LRU and RANDOM_2_LRU eviction policy - Fixes #12026.
Signed-off-by: Aleksey Plekhanov <[email protected]>
1 parent df0bffa commit 2572baf

19 files changed

+476
-31
lines changed

modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/evict/FairFifoPageEvictionTracker.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,14 @@ public FairFifoPageEvictionTracker(
7373
@Override protected synchronized boolean checkTouch(long pageId) {
7474
return true;
7575
}
76+
77+
/** {@inheritDoc} */
78+
@Override protected void linkFragmentPages(int pageTrackingIdx, int nextPageTrackingIdx) {
79+
// No-op.
80+
}
81+
82+
/** {@inheritDoc} */
83+
@Override protected int getFragmentLink(int pageTrackingIdx) {
84+
return 0;
85+
}
7686
}

modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/evict/NoOpPageEvictionTracker.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,9 @@ public class NoOpPageEvictionTracker implements PageEvictionTracker {
5252
@Override public boolean evictionRequired() {
5353
return false;
5454
}
55+
56+
/** {@inheritDoc} */
57+
@Override public void trackFragmentPage(long pageId, long prevPageId, boolean isHeadPage) throws IgniteCheckedException {
58+
// No-op.
59+
}
5560
}

modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/evict/PageAbstractEvictionTracker.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,4 +183,55 @@ int trackingIdx(int pageIdx) {
183183
int pageIdx(int trackingIdx) {
184184
return pageMem.pageIndex(trackingIdx);
185185
}
186+
187+
/** {@inheritDoc} */
188+
@Override public void trackFragmentPage(long pageId, long prevPageId, boolean isHeadPage) throws IgniteCheckedException {
189+
// Do nothing if called for tail page.
190+
if (prevPageId == 0)
191+
return;
192+
193+
if (isHeadPage) {
194+
// Store link to head fragment page in tail fragment page.
195+
linkFragmentPages(tailPageTrackingIdx(prevPageId), trackingIdx(PageIdUtils.pageIndex(pageId)));
196+
}
197+
else {
198+
// Store link to tail fragment page in each fragment page.
199+
linkFragmentPages(trackingIdx(PageIdUtils.pageIndex(pageId)), tailPageTrackingIdx(prevPageId));
200+
}
201+
}
202+
203+
/**
204+
* Determine tail page tracking index given page id of previously written fragment.
205+
*
206+
* @param prevPageId Page id of previously written fragment.
207+
* @return tail page tracking index.
208+
*/
209+
private int tailPageTrackingIdx(long prevPageId) {
210+
int prevPageTrackingIdx = trackingIdx(PageIdUtils.pageIndex(prevPageId));
211+
212+
int link = getFragmentLink(prevPageTrackingIdx);
213+
214+
if (link == 0) {
215+
// The previous page is just the tail one.
216+
return prevPageTrackingIdx;
217+
}
218+
else
219+
return link;
220+
}
221+
222+
/**
223+
* Link two pages containing fragments of row.
224+
*
225+
* @param pageTrackingIdx Page tracking index.
226+
* @param nextPageTrackingIdx Tracking index of page to link to.
227+
*/
228+
protected abstract void linkFragmentPages(int pageTrackingIdx, int nextPageTrackingIdx);
229+
230+
/**
231+
* Get tracking index of page linked to this one.
232+
*
233+
* @param pageTrackingIdx Page tracking index.
234+
* @return Tracking index of linked page, or {@code 0} if this page is not linked to any.
235+
*/
236+
protected abstract int getFragmentLink(int pageTrackingIdx);
186237
}

modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/evict/PageEvictionTracker.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,14 @@ public interface PageEvictionTracker extends LifecycleAware {
5656
* @throws IgniteCheckedException In case of page memory error.
5757
*/
5858
public void forgetPage(long pageId) throws IgniteCheckedException;
59+
60+
/**
61+
* Call this method when data page containing fragment of row is written.
62+
*
63+
* @param pageId Page id.
64+
* @param prevPageId Page id of previous fragment. 0 if called for the tail fragment (written first).
65+
* @param isHeadPage True if head fragment (written last) of row is written, False otherwise.
66+
* @throws IgniteCheckedException In case of page memory error.
67+
*/
68+
public void trackFragmentPage(long pageId, long prevPageId, boolean isHeadPage) throws IgniteCheckedException;
5969
}

modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/evict/Random2LruPageEvictionTracker.java

Lines changed: 77 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -92,16 +92,22 @@ public Random2LruPageEvictionTracker(
9292
do {
9393
int trackingIdx = trackingIdx(pageIdx);
9494

95-
int firstTs = GridUnsafe.getIntVolatile(null, trackingArrPtr + trackingIdx * 8L);
95+
long trackingData = GridUnsafe.getLongVolatile(null, trackingArrPtr + trackingIdx * 8L);
9696

97-
int secondTs = GridUnsafe.getIntVolatile(null, trackingArrPtr + trackingIdx * 8L + 4);
97+
int firstTs = first(trackingData);
98+
99+
assert firstTs >= 0 : "[firstTs=" + firstTs + ", trackingData=" + trackingData + "]";
100+
101+
int secondTs = second(trackingData);
102+
103+
long newTrackingData;
98104

99105
if (firstTs <= secondTs)
100-
success = GridUnsafe.compareAndSwapInt(null, trackingArrPtr + trackingIdx * 8L, firstTs, (int)latestTs);
101-
else {
102-
success = GridUnsafe.compareAndSwapInt(
103-
null, trackingArrPtr + trackingIdx * 8L + 4, secondTs, (int)latestTs);
104-
}
106+
newTrackingData = U.toLong((int)latestTs, secondTs);
107+
else
108+
newTrackingData = U.toLong(firstTs, (int)latestTs);
109+
110+
success = GridUnsafe.compareAndSwapLong(null, trackingArrPtr + trackingIdx * 8L, trackingData, newTrackingData);
105111
} while (!success);
106112
}
107113

@@ -123,9 +129,32 @@ public Random2LruPageEvictionTracker(
123129
while (dataPagesCnt < SAMPLE_SIZE) {
124130
int trackingIdx = rnd.nextInt(trackingSize);
125131

126-
int firstTs = GridUnsafe.getIntVolatile(null, trackingArrPtr + trackingIdx * 8L);
132+
long trackingData = GridUnsafe.getLongVolatile(null, trackingArrPtr + trackingIdx * 8L);
133+
134+
int firstTs = first(trackingData);
135+
136+
if (firstTs < 0) {
137+
// For page containing fragmented row data timestamps stored in the row's head page are used.
138+
// Fragment page (other than the tail one) contains link to tail page. Tail page contains link to
139+
// head page. So head page is found no more than in two hops.
140+
trackingIdx = -firstTs;
141+
142+
trackingData = GridUnsafe.getLongVolatile(null, trackingArrPtr + trackingIdx * 8L);
127143

128-
int secondTs = GridUnsafe.getIntVolatile(null, trackingArrPtr + trackingIdx * 8L + 4);
144+
firstTs = first(trackingData);
145+
146+
if (firstTs < 0) {
147+
trackingIdx = -firstTs;
148+
149+
trackingData = GridUnsafe.getLongVolatile(null, trackingArrPtr + trackingIdx * 8L);
150+
151+
firstTs = first(trackingData);
152+
153+
assert firstTs >= 0 : "[firstTs=" + firstTs + ", secondTs=" + second(trackingData) + "]";
154+
}
155+
}
156+
157+
int secondTs = second(trackingData);
129158

130159
int minTs = Math.min(firstTs, secondTs);
131160

@@ -164,9 +193,9 @@ public Random2LruPageEvictionTracker(
164193
@Override protected boolean checkTouch(long pageId) {
165194
int trackingIdx = trackingIdx(PageIdUtils.pageIndex(pageId));
166195

167-
int firstTs = GridUnsafe.getIntVolatile(null, trackingArrPtr + trackingIdx * 8L);
196+
int firstTs = first(GridUnsafe.getLongVolatile(null, trackingArrPtr + trackingIdx * 8L));
168197

169-
return firstTs != 0;
198+
return firstTs > 0;
170199
}
171200

172201
/** {@inheritDoc} */
@@ -177,4 +206,41 @@ public Random2LruPageEvictionTracker(
177206

178207
GridUnsafe.putLongVolatile(null, trackingArrPtr + trackingIdx * 8L, 0L);
179208
}
209+
210+
/**
211+
* Link two pages containing fragments of row.
212+
* <p>
213+
* Link is stored as a negated page tracking index in the first integer in the tracking data.
214+
* Second integer in the tracking data is set to 0.
215+
*
216+
* @param pageTrackingIdx Page tracking index.
217+
* @param nextPageTrackingIdx Tracking index of page to link to.
218+
*/
219+
@Override protected void linkFragmentPages(int pageTrackingIdx, int nextPageTrackingIdx) {
220+
GridUnsafe.putLongVolatile(null, trackingArrPtr + pageTrackingIdx * 8L,
221+
U.toLong(-nextPageTrackingIdx, 0));
222+
}
223+
224+
/** {@inheritDoc} */
225+
@Override protected int getFragmentLink(int pageTrackingIdx) {
226+
int link = first(GridUnsafe.getLongVolatile(null, trackingArrPtr + pageTrackingIdx * 8L));
227+
228+
assert link <= 0 : "[link=" + link + "]";
229+
230+
return -link;
231+
}
232+
233+
/**
234+
* Helper to extract first integer value.
235+
*/
236+
private int first(long l) {
237+
return (int)(l >> Integer.SIZE);
238+
}
239+
240+
/**
241+
* Helper to extract second integer value.
242+
*/
243+
private int second(long l) {
244+
return (int)l;
245+
}
180246
}

modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/evict/RandomLruPageEvictionTracker.java

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,23 @@ public RandomLruPageEvictionTracker(
112112

113113
int compactTs = GridUnsafe.getIntVolatile(null, trackingArrPtr + sampleTrackingIdx * 4L);
114114

115+
if (compactTs < 0) {
116+
// For page containing fragmented row data timestamps stored in the row's head page are used.
117+
// Fragment page (other than the tail one) contains link to tail page. Tail page contains link to
118+
// head page. So head page is found no more than in two hops.
119+
sampleTrackingIdx = -compactTs;
120+
121+
compactTs = GridUnsafe.getIntVolatile(null, trackingArrPtr + sampleTrackingIdx * 4L);
122+
123+
if (compactTs < 0) {
124+
sampleTrackingIdx = -compactTs;
125+
126+
compactTs = GridUnsafe.getIntVolatile(null, trackingArrPtr + sampleTrackingIdx * 4L);
127+
128+
assert compactTs >= 0 : "[compactTs=" + compactTs + "]";
129+
}
130+
}
131+
115132
if (compactTs != 0) {
116133
// We chose data page with at least one touch.
117134
if (compactTs < lruCompactTs) {
@@ -147,7 +164,7 @@ public RandomLruPageEvictionTracker(
147164

148165
int ts = GridUnsafe.getIntVolatile(null, trackingArrPtr + trackingIdx * 4L);
149166

150-
return ts != 0;
167+
return ts > 0;
151168
}
152169

153170
/** {@inheritDoc} */
@@ -156,4 +173,25 @@ public RandomLruPageEvictionTracker(
156173

157174
GridUnsafe.putIntVolatile(null, trackingArrPtr + trackingIdx(pageIdx) * 4L, 0);
158175
}
176+
177+
/**
178+
* Link two pages containing fragments of row.
179+
* <p>
180+
* Link is stored as a negated page tracking index.
181+
*
182+
* @param pageTrackingIdx Page tracking index.
183+
* @param nextPageTrackingIdx Tracking index of page to link to.
184+
*/
185+
@Override protected void linkFragmentPages(int pageTrackingIdx, int nextPageTrackingIdx) {
186+
GridUnsafe.putIntVolatile(null, trackingArrPtr + pageTrackingIdx * 4L, -nextPageTrackingIdx);
187+
}
188+
189+
/** {@inheritDoc} */
190+
@Override protected int getFragmentLink(int pageTrackingIdx) {
191+
int link = GridUnsafe.getIntVolatile(null, trackingArrPtr + pageTrackingIdx * 4L);
192+
193+
assert link <= 0 : "[link=" + link + "]";
194+
195+
return -link;
196+
}
159197
}

modules/core/src/main/java/org/apache/ignite/internal/processors/cache/persistence/freelist/AbstractFreeList.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,8 @@ protected Integer addRow(
193193
throws IgniteCheckedException {
194194
AbstractDataPageIO<T> io = (AbstractDataPageIO<T>)iox;
195195

196+
long lastLink = row.link();
197+
196198
int rowSize = row.size();
197199
int oldFreeSpace = io.getFreeSpace(pageAddr);
198200

@@ -205,6 +207,8 @@ protected Integer addRow(
205207
if (written == rowSize)
206208
evictionTracker.touchPage(pageId);
207209

210+
evictionTracker.trackFragmentPage(pageId, lastLink, written == rowSize);
211+
208212
// Avoid boxing with garbage generation for usual case.
209213
return written == rowSize ? COMPLETE : written;
210214
}
@@ -340,8 +344,6 @@ private final class WriteRowsHandler extends PageHandler<GridCursor<T>, Integer>
340344
written = writeRowHnd.addRow(pageId, page, pageAddr, io, row, written);
341345

342346
assert written == COMPLETE;
343-
344-
evictionTracker.touchPage(pageId);
345347
}
346348

347349
writeRowHnd.putPage(io.getFreeSpace(pageAddr), pageId, page, pageAddr, statHolder);
Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,11 @@
2121
import org.apache.ignite.IgniteDataStreamer;
2222
import org.apache.ignite.cache.CachePeekMode;
2323
import org.apache.ignite.configuration.CacheConfiguration;
24-
import org.apache.ignite.configuration.DataPageEvictionMode;
25-
import org.apache.ignite.configuration.IgniteConfiguration;
2624

2725
/**
2826
*
2927
*/
30-
public class PageEvictionDataStreamerTest extends PageEvictionMultinodeAbstractTest {
31-
/** {@inheritDoc} */
32-
@Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
33-
return setEvictionMode(DataPageEvictionMode.RANDOM_LRU, super.getConfiguration(gridName));
34-
}
35-
28+
public abstract class PageEvictionDataStreamerAbstractTest extends PageEvictionMultinodeAbstractTest {
3629
/** {@inheritDoc} */
3730
@Override protected void createCacheAndTestEviction(CacheConfiguration<Object, Object> cfg) throws Exception {
3831
IgniteCache<Object, Object> cache = clientGrid().getOrCreateCache(cfg);

modules/core/src/test/java/org/apache/ignite/internal/processors/cache/eviction/paged/PageEvictionMultinodeAbstractTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ else if (r.nextInt() % 13 == 0)
110110
System.out.println(">>> Resulting size: " + resultingSize);
111111

112112
// Eviction started, no OutOfMemory occurred, success.
113-
assertTrue(resultingSize < ENTRIES * 10 / 11);
113+
assertTrue(resultingSize < ENTRIES * 71 / 77);
114114

115115
clientGrid().destroyCache(cfg.getName());
116116
}
Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,17 @@
1818
package org.apache.ignite.internal.processors.cache.eviction.paged;
1919

2020
import org.apache.ignite.cluster.ClusterState;
21-
import org.apache.ignite.configuration.DataPageEvictionMode;
2221
import org.apache.ignite.configuration.DataRegionConfiguration;
2322
import org.apache.ignite.configuration.IgniteConfiguration;
2423

2524
/**
2625
* Enables but not touches persistent region, checks page eviction and PDS+no PDS mode.
2726
*/
28-
public class PageEvictionMultinodeMixedRegionsTest extends PageEvictionMultinodeAbstractTest {
27+
public abstract class PageEvictionMultinodeMixedRegionsAbstractTest extends PageEvictionMultinodeAbstractTest {
2928
/** {@inheritDoc} */
3029
@Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception {
3130
IgniteConfiguration cfg = super.getConfiguration(gridName);
3231

33-
setEvictionMode(DataPageEvictionMode.RANDOM_2_LRU, cfg);
34-
3532
DataRegionConfiguration persReg = new DataRegionConfiguration()
3633
.setName("persisted")
3734
.setPersistenceEnabled(true)

0 commit comments

Comments
 (0)